HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
gap_buffer.hpp
1// Copyright Take Vos 2020-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "utility.hpp"
8#include "assert.hpp"
9#include "memory.hpp"
10#include "cast.hpp"
11#include <memory>
12#include <algorithm>
13#include <string>
14#include <type_traits>
15
16namespace hi::inline v1 {
17
33template<typename T, typename Allocator = std::allocator<T>>
35public:
36 static_assert(
37 !std::is_const_v<T> and !std::is_volatile_v<T> and !std::is_reference_v<T>,
38 "Type of a managing container can not be const, volatile nor a reference");
39 using value_type = T;
40 using allocator_type = Allocator;
41 using size_type = std::size_t;
42 using difference_type = ptrdiff_t;
43 using reference = T&;
44 using const_reference = T const&;
45 using pointer = T *;
46 using const_pointer = T const *;
47
48 class iterator {
49 public:
50 static_assert(
51 !std::is_volatile_v<T> && !std::is_reference_v<T>,
52 "Type of a managing container iterator can not be volatile nor a reference");
53
54 using value_type = gap_buffer::value_type;
55 using size_type = std::size_t;
56 using difference_type = ptrdiff_t;
57 using pointer = value_type *;
58 using const_pointer = value_type const *;
59 using reference = value_type&;
60 using const_reference = value_type const&;
62
63 constexpr ~iterator() noexcept = default;
64 constexpr iterator(iterator const&) noexcept = default;
65 constexpr iterator(iterator&&) noexcept = default;
66 constexpr iterator& operator=(iterator const&) noexcept = default;
67 constexpr iterator& operator=(iterator&&) noexcept = default;
68
69 constexpr iterator(gap_buffer *buffer, T *it_ptr) noexcept : _buffer(buffer), _it_ptr(it_ptr)
70 {
71#ifndef NDEBUG
72 _version = buffer->_version;
73#endif
74 hi_axiom(holds_invariant());
75 }
76
77 [[nodiscard]] constexpr reference operator*() noexcept
78 {
79 return *(_buffer->get_pointer_from_it(_it_ptr));
80 }
81
82 [[nodiscard]] constexpr const_reference operator*() const noexcept
83 {
84 return *(_buffer->get_const_pointer_from_it(_it_ptr));
85 }
86
87 [[nodiscard]] constexpr pointer operator->() noexcept
88 {
89 return _buffer->get_pointer_from_it(_it_ptr);
90 }
91
92 [[nodiscard]] constexpr const_pointer operator->() const noexcept
93 {
94 return _buffer->get_const_pointer_from_it(_it_ptr);
95 }
96
97 [[nodiscard]] constexpr reference operator[](std::integral auto index) noexcept
98 {
99 return *(_buffer->get_pointer_from_it(_it_ptr + index));
100 }
101
102 [[nodiscard]] constexpr const_reference operator[](std::integral auto index) const noexcept
103 {
104 return *(_buffer->get_const_pointer_from_it(_it_ptr + index));
105 }
106
107 constexpr iterator& operator++() noexcept
108 {
109 ++_it_ptr;
110 hi_axiom(holds_invariant());
111 return *this;
112 }
113
114 constexpr iterator operator++(int) noexcept
115 {
116 auto tmp = *this;
117 ++_it_ptr;
118 hi_axiom(holds_invariant());
119 return tmp;
120 }
121
122 constexpr iterator& operator--() noexcept
123 {
124 --_it_ptr;
125 hi_axiom(holds_invariant());
126 return *this;
127 }
128
129 constexpr iterator& operator--(int) noexcept
130 {
131 auto tmp = *this;
132 --_it_ptr;
133 hi_axiom(holds_invariant());
134 return tmp;
135 }
136
137 constexpr iterator& operator+=(difference_type n) noexcept
138 {
139 _it_ptr += n;
140 hi_axiom(holds_invariant());
141 return *this;
142 }
143
144 constexpr iterator& operator-=(difference_type n) noexcept
145 {
146 _it_ptr -= n;
147 hi_axiom(holds_invariant());
148 return *this;
149 }
150
151 [[nodiscard]] constexpr friend iterator operator+(iterator lhs, difference_type rhs) noexcept
152 {
153 return lhs += rhs;
154 }
155
156 [[nodiscard]] constexpr friend iterator operator-(iterator lhs, difference_type rhs) noexcept
157 {
158 return lhs -= rhs;
159 }
160
161 [[nodiscard]] constexpr friend difference_type operator-(iterator const& lhs, iterator const& rhs) noexcept
162 {
163 return lhs._it_ptr - rhs._it_ptr;
164 }
165
166 [[nodiscard]] constexpr friend bool operator==(iterator const& lhs, iterator const& rhs) noexcept
167 {
168 return lhs._it_ptr == rhs._it_ptr;
169 }
170
171 [[nodiscard]] constexpr friend auto operator<=>(iterator const& lhs, iterator const& rhs) noexcept
172 {
173 return lhs._it_ptr <=> rhs._it_ptr;
174 }
175
176 private:
177 gap_buffer *_buffer;
178 value_type *_it_ptr;
179#ifndef NDEBUG
180 std::size_t _version = 0;
181#endif
182
183 [[nodiscard]] constexpr bool holds_invariant() const noexcept
184 {
185 return _buffer and _it_ptr >= _buffer->_begin and _it_ptr <= _buffer->_it_end
186#ifndef NDEBUG
187 and _version == _buffer->_version
188#endif
189 ;
190 }
191
192 [[nodiscard]] constexpr bool holds_invariant(iterator const& other) const noexcept
193 {
194 return holds_invariant() and other.holds_invariant() and _buffer == other._buffer;
195 }
196
197 friend class gap_buffer;
198 friend class gap_buffer::const_iterator;
199 };
200
202 public:
203 static_assert(
204 !std::is_volatile_v<T> && !std::is_reference_v<T>,
205 "Type of a managing container iterator can not be volatile nor a reference");
206
207 using value_type = gap_buffer::value_type;
208 using size_type = std::size_t;
209 using difference_type = ptrdiff_t;
210 using pointer = value_type *;
211 using const_pointer = value_type const *;
212 using reference = value_type&;
213 using const_reference = value_type const&;
215
216 constexpr ~const_iterator() noexcept = default;
217 constexpr const_iterator(const_iterator const&) noexcept = default;
218 constexpr const_iterator(const_iterator&&) noexcept = default;
219 constexpr const_iterator& operator=(const_iterator const&) noexcept = default;
220 constexpr const_iterator& operator=(const_iterator&&) noexcept = default;
221
222 constexpr const_iterator(iterator const& other) noexcept : _buffer(other._buffer), _it_ptr(other._it_ptr)
223 {
224#ifndef NDEBUG
225 _version = other._version;
226#endif
227 hi_axiom(holds_invariant());
228 }
229
230 constexpr const_iterator& operator=(iterator const& other) noexcept
231 {
232 _buffer = other._buffer;
233 _it_ptr = other._it_ptr;
234#ifndef NDEBUG
235 _version = other._version;
236#endif
237 hi_axiom(holds_invariant());
238 return *this;
239 }
240
241 constexpr const_iterator(gap_buffer const *buffer, value_type const *it_ptr) noexcept : _buffer(buffer), _it_ptr(it_ptr)
242 {
243#ifndef NDEBUG
244 _version = buffer->_version;
245#endif
246 hi_axiom(holds_invariant());
247 }
248
249 [[nodiscard]] constexpr const_reference operator*() const noexcept
250 {
251 return *(_buffer->get_const_pointer_from_it(_it_ptr));
252 }
253
254 [[nodiscard]] constexpr const_pointer operator->() const noexcept
255 {
256 return _buffer->get_const_pointer_from_it(_it_ptr);
257 }
258
259 [[nodiscard]] constexpr const_reference operator[](std::integral auto index) const noexcept
260 {
261 return *(_buffer->get_const_pointer_from_it(_it_ptr + index));
262 }
263
264 constexpr const_iterator& operator++() noexcept
265 {
266 ++_it_ptr;
267 hi_axiom(holds_invariant());
268 return *this;
269 }
270
271 constexpr const_iterator operator++(int) noexcept
272 {
273 auto tmp = *this;
274 ++_it_ptr;
275 hi_axiom(holds_invariant());
276 return tmp;
277 }
278
279 constexpr const_iterator& operator--() noexcept
280 {
281 --_it_ptr;
282 hi_axiom(holds_invariant());
283 return *this;
284 }
285
286 constexpr const_iterator& operator--(int) noexcept
287 {
288 auto tmp = *this;
289 --_it_ptr;
290 hi_axiom(holds_invariant());
291 return tmp;
292 }
293
294 constexpr const_iterator& operator+=(difference_type n) noexcept
295 {
296 _it_ptr += n;
297 hi_axiom(holds_invariant());
298 return *this;
299 }
300
301 constexpr const_iterator& operator-=(difference_type n) noexcept
302 {
303 _it_ptr -= n;
304 hi_axiom(holds_invariant());
305 return *this;
306 }
307
308 [[nodiscard]] constexpr friend const_iterator operator+(const_iterator lhs, difference_type rhs) noexcept
309 {
310 return lhs += rhs;
311 }
312
313 [[nodiscard]] constexpr friend const_iterator operator-(const_iterator lhs, difference_type rhs) noexcept
314 {
315 return lhs -= rhs;
316 }
317
318 [[nodiscard]] constexpr friend difference_type operator-(const_iterator const& lhs, const_iterator const& rhs) noexcept
319 {
320 return lhs._it_ptr - rhs._it_ptr;
321 }
322
323 [[nodiscard]] constexpr friend bool operator==(const_iterator const& lhs, const_iterator const& rhs) noexcept
324 {
325 return lhs._it_ptr == rhs._it_ptr;
326 }
327
328 [[nodiscard]] constexpr friend auto operator<=>(const_iterator const& lhs, const_iterator const& rhs) noexcept
329 {
330 return lhs._it_ptr <=> rhs._it_ptr;
331 }
332
333 private:
334 gap_buffer const *_buffer;
335 T const *_it_ptr;
336#ifndef NDEBUG
337 std::size_t _version = 0;
338#endif
339
340 [[nodiscard]] constexpr bool holds_invariant() const noexcept
341 {
342 return _buffer and _it_ptr >= _buffer->_begin and _it_ptr <= _buffer->_it_end
343#ifndef NDEBUG
344 and _version == _buffer->_version
345#endif
346 ;
347 }
348
349 [[nodiscard]] constexpr bool holds_invariant(const_iterator const& other) const noexcept
350 {
351 return holds_invariant() and other.holds_invariant() and _buffer == other._buffer;
352 }
353
354 friend class gap_buffer;
355 };
356
359 constexpr gap_buffer(allocator_type const& allocator = allocator_type{}) noexcept :
360 _begin(nullptr), _it_end(nullptr), _gap_begin(nullptr), _gap_size(0), _allocator(allocator)
361 {
362 hi_axiom(holds_invariant());
363 }
364
367 constexpr gap_buffer(std::initializer_list<T> init, allocator_type const& allocator = allocator_type{}) :
368 _begin(_allocator.allocate(init.size() + _grow_size)),
369 _it_end(_begin + init.size()),
370 _gap_begin(_begin + init.size()),
371 _gap_size(_grow_size),
372 _allocator(allocator)
373 {
374 placement_copy(init.begin(), init.end(), _begin);
375 hi_axiom(holds_invariant());
376 }
377
381 constexpr gap_buffer(gap_buffer const& other) noexcept :
382 _begin(nullptr), _it_end(nullptr), _gap_begin(nullptr), _gap_size(0), _allocator(other._allocator)
383 {
384 hi_axiom(&other != this);
385
386 if (other._ptr != nullptr) {
387 _begin = _allocator.allocate(other.capacity());
388 _it_end = _begin + other.size();
389 _gap_begin = _begin + other.left_size();
390 _gap_size = other._gap_size;
391
392 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
393 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
394 }
395 hi_axiom(holds_invariant());
396 }
397
404 constexpr gap_buffer& operator=(gap_buffer const& other) noexcept
405 {
406 hi_return_on_self_assignment(other);
407
408 clear();
409 if (_gap_size >= other.size()) {
410 // Reuse memory.
411 _gap_begin = _begin + other.left_size();
412 _gap_size = capacity() - other.size();
413
414 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
415 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
416
417 } else {
418 // Deallocate previous memory.
419 if (_begin != nullptr) {
420 _allocator.deallocate(_begin, capacity());
421 _begin = nullptr;
422 _it_end = nullptr;
423 _gap_begin = nullptr;
424 _gap_size = 0;
425 }
426
427 // Allocate and copy date.
428 if (other._begin != nullptr) {
429 hilet new_capacity = other.size() + _grow_size;
430
431 _begin = _allocator.allocate(new_capacity);
432 _it_end = _begin + other.size();
433 _gap_begin = _begin + other.left_begin_ptr();
434 _gap_size = new_capacity - other.size();
435
436 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
437 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
438 }
439 }
440 hi_axiom(holds_invariant());
441 }
442
446 constexpr gap_buffer(gap_buffer&& other) noexcept :
447 _begin(other._begin),
448 _it_end(other._it_end),
449 _gap_begin(other._gap_begin),
450 _gap_size(other._gap_size),
451 _allocator(other._allocator)
452 {
453 hi_axiom(&other != this);
454
455 other._begin = nullptr;
456 other._it_end = nullptr;
457 other._gap_begin = nullptr;
458 other._gap_size = 0;
459 hi_axiom(holds_invariant());
460 }
461
465 constexpr gap_buffer& operator=(gap_buffer&& other) noexcept
466 {
467 hi_return_on_self_assignment(other);
468
469 // Clear the data inside this.
470 clear();
471
472 if (_allocator == other._allocator) {
473 // When allocators are the same we can simply swap.
474 std::swap(_begin, other._begin);
475 std::swap(_it_end, other._it_end);
476 std::swap(_gap_begin, other._gap_begin);
477 std::swap(_gap_size, other._size);
478 hi_axiom(holds_invariant());
479 return *this;
480
481 } else if (capacity() >= other.size()) {
482 // Reuse memory of this.
483 _it_end = _begin + other.size();
484 _gap_begin = _begin + other.left_size();
485 _gap_size = capacity() - other.size();
486
487 placement_move(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
488 placement_move(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
489
490 // Other can keep its own capacity.
491 hilet other_capacity = other.capacity();
492 other._it_end = other._begin;
493 other._gap_begin = other._begin;
494 other._gap_size = other_capacity;
495 hi_axiom(holds_invariant());
496 return *this;
497
498 } else {
499 // Deallocate previous memory.
500 if (_begin != nullptr) {
501 _allocator.deallocate(_begin, capacity());
502 _begin = nullptr;
503 _it_end = nullptr;
504 _gap_begin = nullptr;
505 _gap_size = 0;
506 }
507
508 // Allocate and copy date.
509 if (other._begin != nullptr) {
510 hilet new_capacity = other.size() + _grow_size;
511
512 _begin = _allocator.allocate(new_capacity);
513 _it_end = _begin + other.size();
514 _gap_begin = _begin + other.left_size();
515 _gap_size = new_capacity - other.size();
516
517 placement_move(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
518 placement_move(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
519 }
520
521 // Other can keep its own capacity.
522 hilet other_capacity = other.capacity();
523 other._it_end = other._begin;
524 other._gap_begin = other._begin;
525 other._gap_size = other_capacity;
526 hi_axiom(holds_invariant());
527 return *this;
528 }
529 }
530
534 constexpr ~gap_buffer()
535 {
536 clear();
537 if (_begin != nullptr) {
538 _allocator.deallocate(_begin, capacity());
539 }
540 }
541
548 [[nodiscard]] constexpr reference operator[](size_type index) noexcept
549 {
550 hi_axiom(index < size());
551 return *get_pointer_from_index(index);
552 }
553
560 [[nodiscard]] constexpr const_reference operator[](size_type index) const noexcept
561 {
562 hi_axiom(index < size());
563 return *get_pointer_from_index(index);
564 }
565
572 [[nodiscard]] constexpr reference at(size_type index)
573 {
574 if (index < size()) {
575 throw std::out_of_range("gap_buffer::at");
576 }
577 return *get_pointer_from_index(index);
578 }
579
586 [[nodiscard]] constexpr const_reference at(size_type index) const
587 {
588 if (index < size()) {
589 throw std::out_of_range("gap_buffer::at");
590 }
591 return *get_pointer_from_index(index);
592 }
593
594 [[nodiscard]] constexpr reference front() noexcept
595 {
596 hi_axiom(size() != 0);
597 return *get_pointer_from_it_ptr(_begin);
598 }
599
600 [[nodiscard]] constexpr const_reference front() const noexcept
601 {
602 hi_axiom(size() != 0);
603 return *get_pointer_from_it_ptr(_begin);
604 }
605
606 [[nodiscard]] constexpr reference back() noexcept
607 {
608 hi_axiom(size() != 0);
609 return *get_pointer_from_it_ptr(_it_end - 1);
610 }
611
612 [[nodiscard]] constexpr const_reference back() const noexcept
613 {
614 hi_axiom(size() != 0);
615 return *get_pointer_from_it_ptr(_it_end - 1);
616 }
617
618 constexpr void pop_back() noexcept
619 {
620 hi_axiom(size() != 0);
621 erase(end() - 1);
622 }
623
624 constexpr void pop_front() noexcept
625 {
626 hi_axiom(size() != 0);
627 erase(begin());
628 }
629
637 constexpr void clear() noexcept
638 {
639 if (_begin) {
640 std::destroy(left_begin_ptr(), left_end_ptr());
641 std::destroy(right_begin_ptr(), right_end_ptr());
642 hilet this_capacity = capacity();
643 _it_end = _begin;
644 _gap_begin = _begin;
645 _gap_size = this_capacity;
646 }
647 hi_axiom(holds_invariant());
648 }
649
650 [[nodiscard]] constexpr std::size_t size() const noexcept
651 {
652 return static_cast<std::size_t>(_it_end - _begin);
653 }
654
655 [[nodiscard]] constexpr bool empty() const noexcept
656 {
657 return size() == 0;
658 }
659
660 [[nodiscard]] constexpr std::size_t capacity() const noexcept
661 {
662 return size() + _gap_size;
663 }
664
665 constexpr void reserve(std::size_t new_capacity) noexcept
666 {
667 hilet extra_capacity = static_cast<ssize_t>(new_capacity) - capacity();
668 if (extra_capacity <= 0) {
669 return;
670 }
671
672 // Add the extra_capacity to the end of the gap.
673 // LLL...RRR
674 // LLL....RRR
675 hilet new_begin = _allocator.allocate(new_capacity);
676 hilet new_it_end = new_begin + size();
677 hilet new_gap_begin = new_begin + left_size();
678 hilet new_gap_size = new_capacity - size();
679
680 if (_begin != nullptr) {
681 placement_move(left_begin_ptr(), left_end_ptr(), new_begin);
682 placement_move(right_begin_ptr(), right_end_ptr(), new_gap_begin + new_gap_size);
683 _allocator.deallocate(_begin, capacity());
684 }
685
686 _begin = new_begin;
687 _it_end = new_it_end;
688 _gap_begin = new_gap_begin;
689 _gap_size = new_gap_size;
690 hi_axiom(holds_invariant());
691 }
692
693 [[nodiscard]] constexpr iterator begin() noexcept
694 {
695 return iterator{this, _begin};
696 }
697
698 [[nodiscard]] constexpr const_iterator begin() const noexcept
699 {
700 return const_iterator{this, _begin};
701 }
702
703 [[nodiscard]] constexpr const_iterator cbegin() const noexcept
704 {
705 return const_iterator{this, _begin};
706 }
707
708 [[nodiscard]] constexpr iterator end() noexcept
709 {
710 return iterator{this, _it_end};
711 }
712
713 [[nodiscard]] constexpr const_iterator end() const noexcept
714 {
715 return const_iterator{this, _it_end};
716 }
717
718 [[nodiscard]] constexpr const_iterator cend() const noexcept
719 {
720 return const_iterator{this, _it_end};
721 }
722
723 [[nodiscard]] constexpr friend iterator begin(gap_buffer& rhs) noexcept
724 {
725 return rhs.begin();
726 }
727
728 [[nodiscard]] constexpr friend const_iterator begin(gap_buffer const& rhs) noexcept
729 {
730 return rhs.begin();
731 }
732
733 [[nodiscard]] constexpr friend const_iterator cbegin(gap_buffer const& rhs) noexcept
734 {
735 return rhs.begin();
736 }
737
738 [[nodiscard]] constexpr friend iterator end(gap_buffer& rhs) noexcept
739 {
740 return rhs.end();
741 }
742
743 [[nodiscard]] constexpr friend const_iterator end(gap_buffer const& rhs) noexcept
744 {
745 return rhs.end();
746 }
747
748 [[nodiscard]] constexpr friend const_iterator cend(gap_buffer const& rhs) noexcept
749 {
750 return rhs.end();
751 }
752
753 template<typename... Args>
754 constexpr reference emplace_back(Args&&...args) noexcept
755 {
756 return emplace_after(end(), std::forward<Args>(args)...);
757 }
758
759 constexpr void push_back(value_type const& value) noexcept
760 {
761 emplace_back(value);
762 }
763
764 constexpr void push_back(value_type&& value) noexcept
765 {
766 emplace_back(std::move(value));
767 }
768
769 template<typename... Args>
770 constexpr reference emplace_front(Args&&...args) noexcept
771 {
772 set_gap_offset(_begin);
773 grow_to_insert(1);
774
775 auto ptr = new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
776 ++_it_end;
777 --_gap_size;
778#ifndef NDEBUG
779 ++_version;
780#endif
781 hi_axiom(holds_invariant());
782 return *ptr;
783 }
784
785 constexpr void push_front(value_type const& value) noexcept
786 {
787 emplace_front(value);
788 }
789
790 constexpr void push_front(value_type&& value) noexcept
791 {
792 emplace_front(std::move(value));
793 }
794
799 template<typename... Args>
800 constexpr reference emplace_before(const_iterator position, Args&&...args) noexcept
801 {
802 hi_axiom(position._buffer == this);
803 set_gap_offset(const_cast<value_type *>(position._it_ptr));
804 grow_to_insert(1);
805
806 auto ptr = new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
807 ++_it_end;
808 --_gap_size;
809#ifndef NDEBUG
810 ++_version;
811#endif
812 hi_axiom(holds_invariant());
813 return *ptr;
814 }
815
820 constexpr iterator insert_before(const_iterator position, value_type const& value) noexcept
821 {
822 auto ptr = &emplace_before(position, value_type(value));
823 return iterator{this, get_it_from_pointer(ptr)};
824 }
825
830 constexpr iterator insert_before(const_iterator position, value_type&& value) noexcept
831 {
832 auto ptr = &emplace_before(position, std::move(value));
833 return iterator{this, get_it_from_pointer(ptr)};
834 }
835
845 template<typename It>
846 constexpr iterator insert_before(const_iterator position, It first, It last) noexcept
847 {
848 // Insert last to first, that way the position returned is
849 // a valid iterator to the first inserted element.
850 auto it = last;
851 while (it != first) {
852 position = insert_before(position, *(--it));
853 }
854 hi_axiom(holds_invariant());
855 return position;
856 }
857
862 template<typename... Args>
863 constexpr reference emplace_after(const_iterator position, Args&&...args) noexcept
864 {
865 hi_axiom(position._buffer == this);
866 set_gap_offset(const_cast<value_type *>(position._it_ptr));
867 grow_to_insert(1);
868
869 auto ptr = new (left_end_ptr()) value_type(std::forward<Args>(args)...);
870 ++_it_end;
871 ++_gap_begin;
872 --_gap_size;
873#ifndef NDEBUG
874 ++_version;
875#endif
876 hi_axiom(holds_invariant());
877 return *ptr;
878 }
879
884 constexpr iterator insert_after(const_iterator position, value_type const& value) noexcept
885 {
886 auto ptr = &emplace_after(position, value_type(value));
887 return iterator{this, get_it_from_pointer(ptr)};
888 }
889
894 constexpr iterator insert_after(const_iterator position, value_type&& value) noexcept
895 {
896 auto ptr = &emplace_after(position, std::move(value));
897 return iterator{this, get_it_from_pointer(ptr)};
898 }
899
907 template<typename It>
908 constexpr iterator insert_after(const_iterator position, It first, It last) noexcept
909 {
910 auto position_ = iterator{this, const_cast<value_type *>(position._it_ptr)};
911 for (auto it = first; it != last; ++it) {
912 position_ = insert_after(position_, *it) + 1;
913 }
914 hi_axiom(holds_invariant());
915 return position_;
916 }
917
923 constexpr iterator erase(const_iterator first, const_iterator last) noexcept
924 {
925 // place the gap after the last iterator, this way we can use the
926 // it_ptr directly because we don't need to skip the gap.
927 hi_axiom(first._buffer == this);
928 hi_axiom(last._buffer == this);
929
930 set_gap_offset(const_cast<value_type *>(last._it_ptr));
931 auto first_p = const_cast<value_type *>(first._it_ptr);
932 auto last_p = const_cast<value_type *>(last._it_ptr);
933 hilet erase_size = last_p - first_p;
934
935 std::destroy(first_p, last_p);
936 _gap_begin = first_p;
937 _gap_size += erase_size;
938 _it_end -= erase_size;
939 hi_axiom(holds_invariant());
940 return iterator{this, _gap_begin};
941 }
942
947 constexpr iterator erase(const_iterator position) noexcept
948 {
949 return erase(position, position + 1);
950 }
951
952 [[nodiscard]] constexpr friend bool operator==(gap_buffer const& lhs, gap_buffer const& rhs) noexcept
953 {
954 if (lhs.size() != rhs.size()) {
955 return false;
956 } else {
957 return std::equal(lhs.begin(), lhs.end(), rhs.begin());
958 }
959 }
960
961 template<typename Container>
962 [[nodiscard]] constexpr friend bool operator==(gap_buffer const& lhs, Container const& rhs) noexcept
963 {
964 using std::size;
965 using std::begin;
966
967 if (lhs.size() != size(rhs)) {
968 return false;
969 } else {
970 return std::equal(lhs.begin(), lhs.end(), begin(rhs));
971 }
972 }
973
974 template<typename Container>
975 [[nodiscard]] constexpr friend bool operator==(Container const& lhs, gap_buffer const& rhs) noexcept
976 {
977 return rhs == lhs;
978 }
979
980private:
981 // By how much the buffer should grow when size() == capacity().
982 static constexpr difference_type _grow_size = 256;
983
986 value_type *_begin;
987
991 value_type *_it_end;
992
995 value_type *_gap_begin;
996
999 size_type _gap_size;
1000
1001#ifndef NDEBUG
1002 std::size_t _version = 0;
1003#endif
1004
1005 [[no_unique_address]] allocator_type _allocator;
1006
1007 [[nodiscard]] constexpr bool holds_invariant() const noexcept
1008 {
1009 return (_begin == nullptr and _it_end == nullptr and _gap_begin == nullptr and _gap_size == 0) or
1010 (_begin <= _gap_begin and _gap_begin <= _it_end);
1011 }
1012
1015 constexpr void grow_to_insert(size_type n) noexcept
1016 {
1017 if (n > _gap_size) [[unlikely]] {
1018 auto new_capacity = size() + n + narrow_cast<size_type>(_grow_size);
1019 reserve(ceil(new_capacity, hardware_constructive_interference_size));
1020 }
1021 hi_axiom(holds_invariant());
1022 }
1023
1026 [[nodiscard]] constexpr const_pointer get_it_from_pointer(const_pointer ptr) const noexcept
1027 {
1028 if (ptr < _gap_begin) {
1029 return ptr;
1030 } else {
1031 return ptr - _gap_size;
1032 }
1033 }
1034
1037 [[nodiscard]] constexpr pointer get_it_from_pointer(pointer ptr) noexcept
1038 {
1039 if (ptr < _gap_begin) {
1040 return ptr;
1041 } else {
1042 return ptr - _gap_size;
1043 }
1044 }
1045
1051 [[nodiscard]] constexpr const_pointer get_const_pointer_from_it(const_pointer it_ptr) const noexcept
1052 {
1053 hi_axiom(it_ptr >= _begin && it_ptr <= _it_end);
1054
1055 if (it_ptr < _gap_begin) {
1056 return it_ptr;
1057 } else {
1058 return it_ptr + _gap_size;
1059 }
1060 }
1061
1062 [[nodiscard]] constexpr const_pointer get_pointer_from_it(pointer it_ptr) const noexcept
1063 {
1064 return get_const_pointer_from_it(it_ptr);
1065 }
1066
1067 [[nodiscard]] constexpr pointer get_pointer_from_it(pointer it_ptr) noexcept
1068 {
1069 return const_cast<pointer>(get_const_pointer_from_it(it_ptr));
1070 }
1071
1077 [[nodiscard]] constexpr const_pointer get_const_pointer_from_index(size_type index) const noexcept
1078 {
1079 return get_const_pointer_from_it(_begin + index);
1080 }
1081
1082 [[nodiscard]] constexpr const_pointer get_pointer_from_index(size_type index) const noexcept
1083 {
1084 return get_const_pointer_from_it(_begin + index);
1085 }
1086
1087 [[nodiscard]] constexpr pointer get_pointer_from_index(size_type index) noexcept
1088 {
1089 return get_pointer_from_it(_begin + index);
1090 }
1091
1092 [[nodiscard]] constexpr value_type const *left_begin_ptr() const noexcept
1093 {
1094 return _begin;
1095 }
1096
1097 [[nodiscard]] constexpr value_type *left_begin_ptr() noexcept
1098 {
1099 return _begin;
1100 }
1101
1102 [[nodiscard]] constexpr value_type const *left_end_ptr() const noexcept
1103 {
1104 return _gap_begin;
1105 }
1106
1107 [[nodiscard]] constexpr value_type *left_end_ptr() noexcept
1108 {
1109 return _gap_begin;
1110 }
1111
1112 [[nodiscard]] constexpr size_type left_size() const noexcept
1113 {
1114 return static_cast<size_type>(_gap_begin - _begin);
1115 }
1116
1117 [[nodiscard]] constexpr value_type const *right_begin_ptr() const noexcept
1118 {
1119 return _gap_begin + _gap_size;
1120 }
1121
1122 [[nodiscard]] constexpr value_type *right_begin_ptr() noexcept
1123 {
1124 return _gap_begin + _gap_size;
1125 }
1126
1127 [[nodiscard]] constexpr value_type const *right_end_ptr() const noexcept
1128 {
1129 return _it_end + _gap_size;
1130 }
1131
1132 [[nodiscard]] constexpr value_type *right_end_ptr() noexcept
1133 {
1134 return _it_end + _gap_size;
1135 }
1136
1137 [[nodiscard]] constexpr size_type right_size() const noexcept
1138 {
1139 return _it_end - _gap_begin;
1140 }
1141
1144 constexpr void set_gap_offset(value_type *new_gap_begin) noexcept
1145 {
1146 if (new_gap_begin < _gap_begin) {
1147 // Move data left of the original gap to the end of the new gap.
1148 // LLL...RRR
1149 // LL...LRRR
1150 placement_move_within_array(new_gap_begin, _gap_begin, new_gap_begin + _gap_size);
1151
1152 } else if (new_gap_begin > _gap_begin) {
1153 // Move data right of the original gap to the beginning of the new gap.
1154 // LLL...RRR
1155 // LLLR...RR
1156 placement_move_within_array(_gap_begin + _gap_size, new_gap_begin + _gap_size, _gap_begin);
1157 }
1158
1159 _gap_begin = new_gap_begin;
1160 hi_axiom(holds_invariant());
1161 }
1162};
1163
1164} // namespace hi::inline v1
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
T * placement_copy(InputIt src, T *dst)
Copy an object to another memory locations.
Definition memory.hpp:50
void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
Move an objects between two memory locations.
Definition memory.hpp:107
T * placement_move(T *src, T *dst)
Move an object between two memory locations.
Definition memory.hpp:87
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition utility.hpp:162
Gap Buffer This container is similar to a std::vector, optimized for repeated insertions and deletion...
Definition gap_buffer.hpp:34
constexpr gap_buffer & operator=(gap_buffer const &other) noexcept
Copy assignment.
Definition gap_buffer.hpp:404
constexpr iterator insert_before(const_iterator position, value_type &&value) noexcept
Place the gap at the position and emplace at the end of the gap.
Definition gap_buffer.hpp:830
constexpr gap_buffer & operator=(gap_buffer &&other) noexcept
Move assignment operator.
Definition gap_buffer.hpp:465
constexpr reference emplace_after(const_iterator position, Args &&...args) noexcept
Place the gap at the position and emplace at the beginning of the gap.
Definition gap_buffer.hpp:863
constexpr iterator insert_after(const_iterator position, value_type const &value) noexcept
Place the gap at the position and emplace at the beginning of the gap.
Definition gap_buffer.hpp:884
constexpr reference emplace_before(const_iterator position, Args &&...args) noexcept
Place the gap at the position and emplace at the end of the gap.
Definition gap_buffer.hpp:800
constexpr reference at(size_type index)
Get item to reference at.
Definition gap_buffer.hpp:572
constexpr iterator erase(const_iterator position) noexcept
Erase item.
Definition gap_buffer.hpp:947
constexpr const_reference at(size_type index) const
Get item to reference at.
Definition gap_buffer.hpp:586
constexpr gap_buffer(gap_buffer const &other) noexcept
Copy constructor.
Definition gap_buffer.hpp:381
constexpr void clear() noexcept
Clears the buffer.
Definition gap_buffer.hpp:637
constexpr iterator insert_before(const_iterator position, value_type const &value) noexcept
Place the gap at the position and emplace at the end of the gap.
Definition gap_buffer.hpp:820
constexpr iterator insert_after(const_iterator position, It first, It last) noexcept
Insert items.
Definition gap_buffer.hpp:908
constexpr const_reference operator[](size_type index) const noexcept
Index operator.
Definition gap_buffer.hpp:560
constexpr gap_buffer(std::initializer_list< T > init, allocator_type const &allocator=allocator_type{})
Construct a buffer with the given initializer list.
Definition gap_buffer.hpp:367
constexpr iterator erase(const_iterator first, const_iterator last) noexcept
Erase items.
Definition gap_buffer.hpp:923
constexpr iterator insert_after(const_iterator position, value_type &&value) noexcept
Place the gap at the position and emplace at the beginning of the gap.
Definition gap_buffer.hpp:894
constexpr gap_buffer(gap_buffer &&other) noexcept
Move constructor.
Definition gap_buffer.hpp:446
constexpr ~gap_buffer()
Destructor.
Definition gap_buffer.hpp:534
constexpr iterator insert_before(const_iterator position, It first, It last) noexcept
Insert items If an insert requires a reallocation then all current iterators become invalid.
Definition gap_buffer.hpp:846
constexpr reference operator[](size_type index) noexcept
Index operator.
Definition gap_buffer.hpp:548
constexpr gap_buffer(allocator_type const &allocator=allocator_type{}) noexcept
Construct an empty buffer.
Definition gap_buffer.hpp:359
Definition gap_buffer.hpp:48
Definition gap_buffer.hpp:201
Definition concepts.hpp:36
Definition concepts.hpp:39
T ceil(T... args)
T equal(T... args)
T move(T... args)
T swap(T... args)