HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
gap_buffer.hpp
1// Copyright Take Vos 2020.
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 "required.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 tt {
17
18template<typename T, typename Allocator>
19class gap_buffer;
20
21template<typename T>
22class gap_buffer_iterator;
23
24template<typename T, typename Allocator>
25gap_buffer_iterator<T> make_gap_buffer_iterator(gap_buffer<T, Allocator> *buffer, ptrdiff_t index);
26
27template<typename T, typename Allocator>
28gap_buffer_iterator<T const> make_gap_buffer_iterator(gap_buffer<T, Allocator> const *buffer, ptrdiff_t index);
29
45template<typename T, typename Allocator = std::allocator<T>>
47public:
48 static_assert(
49 !std::is_const_v<T> and !std::is_volatile_v<T> and !std::is_reference_v<T>,
50 "Type of a managing container can not be const, volatile nor a reference");
51 using value_type = T;
52 using allocator_type = Allocator;
53 using size_type = size_t;
54 using difference_type = ptrdiff_t;
55 using reference = T &;
56 using const_reference = T const &;
57 using pointer = T *;
58 using const_pointer = T const *;
61
64 gap_buffer(allocator_type const &allocator = allocator_type{}) noexcept :
65 _begin(nullptr), _it_end(nullptr), _gap_begin(nullptr), _gap_size(0), _allocator(allocator)
66 {
67 tt_axiom(holds_invariant());
68 }
69
72 gap_buffer(std::initializer_list<T> init, allocator_type const &allocator = allocator_type{}) :
73 _begin(_allocator.allocate(init.size() + _grow_size)),
74 _it_end(_begin + init.size()),
75 _gap_begin(_begin + init.size()),
76 _gap_size(_grow_size),
77 _allocator(allocator)
78 {
79 placement_copy(std::begin(init), std::end(init), _begin);
80 tt_axiom(holds_invariant());
81 }
82
86 gap_buffer(gap_buffer const &other) noexcept :
87 _begin(nullptr),
88 _it_end(nullptr),
89 _gap_begin(nullptr),
90 _gap_size(0),
91 _allocator(other._allocator)
92 {
93 tt_axiom(&other != this);
94
95 if (other._ptr != nullptr) {
96 _begin = _allocator.allocate(other.capacity());
97 _it_end = _begin + other.size();
98 _gap_begin = _begin + other.left_size();
99 _gap_size = other._gap_size;
100
101 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
102 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
103 }
104 tt_axiom(holds_invariant());
105 }
106
113 gap_buffer &operator=(gap_buffer const &other) noexcept
114 {
115 tt_return_on_self_assignment(other);
116
117 clear();
118 if (_gap_size >= other.size()) {
119 // Reuse memory.
120 _gap_begin = _begin + other.left_size();
121 _gap_size = capacity() - other.size();
122
123 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
124 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
125
126 } else {
127 // Deallocate previous memory.
128 if (_begin != nullptr) {
129 _allocator.deallocate(_begin, capacity());
130 _begin = nullptr;
131 _it_end = nullptr;
132 _gap_begin = nullptr;
133 _gap_size = 0;
134 }
135
136 // Allocate and copy date.
137 if (other._begin != nullptr) {
138 ttlet new_capacity = other.size() + _grow_size;
139
140 _begin = _allocator.allocate(new_capacity);
141 _it_end = _begin + other.size();
142 _gap_begin = _begin + other.left_begin_ptr();
143 _gap_size = new_capacity - other.size();
144
145 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
146 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
147 }
148 }
149 tt_axiom(holds_invariant());
150 }
151
155 gap_buffer(gap_buffer &&other) noexcept :
156 _begin(other._begin),
157 _it_end(other._it_end),
158 _gap_begin(other._gap_begin),
159 _gap_size(other._gap_size),
160 _allocator(other._allocator)
161 {
162 tt_axiom(&other != this);
163
164 other._begin = nullptr;
165 other._it_end = nullptr;
166 other._gap_begin = nullptr;
167 other._gap_size = 0;
168 tt_axiom(holds_invariant());
169 }
170
174 gap_buffer &operator=(gap_buffer &&other) noexcept
175 {
176 tt_return_on_self_assignment(other);
177
178 // Clear the data inside this.
179 clear();
180
181 if (_allocator == other._allocator) {
182 // When allocators are the same we can simply swap.
183 std::swap(_begin, other._begin);
184 std::swap(_it_end, other._it_end);
185 std::swap(_gap_begin, other._gap_begin);
186 std::swap(_gap_size, other._size);
187 tt_axiom(holds_invariant());
188 return *this;
189
190 } else if (capacity() >= other.size()) {
191 // Reuse memory of this.
192 _it_end = _begin + other.size();
193 _gap_begin = _begin + other.left_size();
194 _gap_size = capacity() - other.size();
195
196 placement_move(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
197 placement_move(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
198
199 // Other can keep its own capacity.
200 ttlet other_capacity = other.capacity();
201 other._it_end = other._begin;
202 other._gap_begin = other._begin;
203 other._gap_size = other_capacity;
204 tt_axiom(holds_invariant());
205 return *this;
206
207 } else {
208 // Deallocate previous memory.
209 if (_begin != nullptr) {
210 _allocator.deallocate(_begin, capacity());
211 _begin = nullptr;
212 _it_end = nullptr;
213 _gap_begin = nullptr;
214 _gap_size = 0;
215 }
216
217 // Allocate and copy date.
218 if (other._begin != nullptr) {
219 ttlet new_capacity = other.size() + _grow_size;
220
221 _begin = _allocator.allocate(new_capacity);
222 _it_end = _begin + other.size();
223 _gap_begin = _begin + other.left_size();
224 _gap_size = new_capacity - other.size();
225
226 placement_move(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
227 placement_move(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
228 }
229
230 // Other can keep its own capacity.
231 ttlet other_capacity = other.capacity();
232 other._it_end = other._begin;
233 other._gap_begin = other._begin;
234 other._gap_size = other_capacity;
235 tt_axiom(holds_invariant());
236 return *this;
237 }
238 }
239
244 {
245 clear();
246 if (_begin != nullptr) {
247 _allocator.deallocate(_begin, capacity());
248 }
249 }
250
257 [[nodiscard]] reference operator[](size_type index) noexcept
258 {
259 tt_axiom(index < size());
260 return *get_pointer_from_index(index);
261 }
262
269 [[nodiscard]] const_reference operator[](size_type index) const noexcept
270 {
271 tt_axiom(index < size());
272 return *get_pointer_from_index(index);
273 }
274
281 [[nodiscard]] reference at(size_type index)
282 {
283 if (index < size()) {
284 throw std::out_of_range("gap_buffer::at");
285 }
286 return *get_pointer_from_index(index);
287 }
288
295 [[nodiscard]] const_reference at(size_type index) const
296 {
297 if (index < size()) {
298 throw std::out_of_range("gap_buffer::at");
299 }
300 return *get_pointer_from_index(index);
301 }
302
303 [[nodiscard]] reference front() noexcept
304 {
305 tt_axiom(size() != 0);
306 return *get_pointer_from_it_ptr(_begin);
307 }
308
309 [[nodiscard]] const_reference front() const noexcept
310 {
311 tt_axiom(size() != 0);
312 return *get_pointer_from_it_ptr(_begin);
313 }
314
315 [[nodiscard]] reference back() noexcept
316 {
317 tt_axiom(size() != 0);
318 return *get_pointer_from_it_ptr(_it_end - 1);
319 }
320
321 [[nodiscard]] const_reference back() const noexcept
322 {
323 tt_axiom(size() != 0);
324 return *get_pointer_from_it_ptr(_it_end - 1);
325 }
326
327 void pop_back() noexcept
328 {
329 tt_axiom(size() != 0);
330 erase(end() - 1);
331 }
332
333 void pop_front() noexcept
334 {
335 tt_axiom(size() != 0);
336 erase(begin());
337 }
338
346 void clear() noexcept
347 {
348 if (_begin) {
349 std::destroy(left_begin_ptr(), left_end_ptr());
350 std::destroy(right_begin_ptr(), right_end_ptr());
351 ttlet this_capacity = capacity();
352 _it_end = _begin;
353 _gap_begin = _begin;
354 _gap_size = this_capacity;
355 }
356 tt_axiom(holds_invariant());
357 }
358
359 [[nodiscard]] size_t size() const noexcept
360 {
361 return static_cast<size_t>(_it_end - _begin);
362 }
363
364 [[nodiscard]] bool empty() const noexcept
365 {
366 return size() == 0;
367 }
368
369 [[nodiscard]] size_t capacity() const noexcept
370 {
371 return size() + _gap_size;
372 }
373
374 void reserve(size_t new_capacity) noexcept
375 {
376 ttlet extra_capacity = static_cast<ssize_t>(new_capacity) - capacity();
377 if (extra_capacity <= 0) {
378 return;
379 }
380
381 // Add the extra_capacity to the end of the gap.
382 // LLL...RRR
383 // LLL....RRR
384 ttlet new_begin = _allocator.allocate(new_capacity);
385 ttlet new_it_end = new_begin + size();
386 ttlet new_gap_begin = new_begin + left_size();
387 ttlet new_gap_size = new_capacity - size();
388
389 if (_begin != nullptr) {
390 placement_move(left_begin_ptr(), left_end_ptr(), new_begin);
391 placement_move(right_begin_ptr(), right_end_ptr(), new_gap_begin + new_gap_size);
392 _allocator.deallocate(_begin, capacity());
393 }
394
395 _begin = new_begin;
396 _it_end = new_it_end;
397 _gap_begin = new_gap_begin;
398 _gap_size = new_gap_size;
399 tt_axiom(holds_invariant());
400 }
401
402 [[nodiscard]] iterator begin() noexcept
403 {
404 return iterator{this, _begin};
405 }
406
407 [[nodiscard]] const_iterator begin() const noexcept
408 {
409 return const_iterator{this, _begin};
410 }
411
412 [[nodiscard]] const_iterator cbegin() const noexcept
413 {
414 return const_iterator{this, _begin};
415 }
416
417 [[nodiscard]] iterator end() noexcept
418 {
419 return iterator{this, _it_end};
420 }
421
422 [[nodiscard]] const_iterator end() const noexcept
423 {
424 return const_iterator{this, _it_end};
425 }
426
427 [[nodiscard]] const_iterator cend() const noexcept
428 {
429 return const_iterator{this, _it_end};
430 }
431
432 template<typename... Args>
433 reference emplace_back(Args &&...args) noexcept
434 {
435 return emplace_after(end(), std::forward<Args>(args)...);
436 }
437
438 void push_back(value_type const &value) noexcept
439 {
440 emplace_back(value);
441 }
442
443 void push_back(value_type &&value) noexcept
444 {
445 emplace_back(std::move(value));
446 }
447
448 template<typename... Args>
449 reference emplace_front(Args &&...args) noexcept
450 {
451 set_gap_offset(_begin);
452 grow_to_insert(1);
453
454 auto ptr = new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
455 ++_it_end;
456 --_gap_size;
457#if TT_BUILT_TYPE == TT_BT_DEBUG
458 ++_version;
459#endif
460 tt_axiom(holds_invariant());
461 return *ptr;
462 }
463
464 void push_front(value_type const &value) noexcept
465 {
466 emplace_front(value);
467 }
468
469 void push_front(value_type &&value) noexcept
470 {
471 emplace_front(std::move(value));
472 }
473
478 template<typename... Args>
479 reference emplace_before(const_iterator position, Args &&...args) noexcept
480 {
481 tt_axiom(position._buffer == this);
482 set_gap_offset(position.it_rw_ptr());
483 grow_to_insert(1);
484
485 auto ptr = new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
486 ++_it_end;
487 --_gap_size;
488#if TT_BUILT_TYPE == TT_BT_DEBUG
489 ++_version;
490#endif
491 tt_axiom(holds_invariant());
492 return *ptr;
493 }
494
499 iterator insert_before(const_iterator position, value_type const &value) noexcept
500 {
501 auto ptr = &emplace_before(position, value_type(value));
502 return iterator{this, get_it_from_pointer(ptr)};
503 }
504
509 iterator insert_before(const_iterator position, value_type &&value) noexcept
510 {
511 auto ptr = &emplace_before(position, std::move(value));
512 return iterator{this, get_it_from_pointer(ptr)};
513 }
514
524 template<typename It>
525 iterator insert_before(const_iterator position, It first, It last) noexcept
526 {
527 // Insert last to first, that way the position returned is
528 // a valid iterator to the first inserted element.
529 auto it = last;
530 while (it != first) {
531 position = insert_before(position, *(--it));
532 }
533 tt_axiom(holds_invariant());
534 return position;
535 }
536
541 template<typename... Args>
542 reference emplace_after(const_iterator position, Args &&...args) noexcept
543 {
544 tt_axiom(position._buffer == this);
545 set_gap_offset(position.it_rw_ptr());
546 grow_to_insert(1);
547
548 auto ptr = new (left_end_ptr()) value_type(std::forward<Args>(args)...);
549 ++_it_end;
550 ++_gap_begin;
551 --_gap_size;
552#if TT_BUILT_TYPE == TT_BT_DEBUG
553 ++_version;
554#endif
555 tt_axiom(holds_invariant());
556 return *ptr;
557 }
558
563 iterator insert_after(const_iterator position, value_type const &value) noexcept
564 {
565 auto ptr = &emplace_after(position, value_type(value));
566 return iterator{this, get_it_from_pointer(ptr)};
567 }
568
573 iterator insert_after(const_iterator position, value_type &&value) noexcept
574 {
575 auto ptr = &emplace_after(position, std::move(value));
576 return iterator{this, get_it_from_pointer(ptr)};
577 }
578
586 template<typename It>
587 iterator insert_after(const_iterator position, It first, It last) noexcept
588 {
589 auto position_ = iterator{position};
590 for (auto it = first; it != last; ++it) {
591 position_ = insert_after(position_, *it) + 1;
592 }
593 tt_axiom(holds_invariant());
594 return position_;
595 }
596
603 {
604 // place the gap after the last iterator, this way we can use the
605 // it_ptr directly because we don't need to skip the gap.
606 tt_axiom(first._buffer == this);
607 tt_axiom(last._buffer == this);
608
609 set_gap_offset(last.it_rw_ptr());
610 auto first_p = first.it_rw_ptr();
611 auto last_p = last.it_rw_ptr();
612 ttlet erase_size = last_p - first_p;
613
614 std::destroy(first_p, last_p);
615 _gap_begin = first_p;
616 _gap_size += erase_size;
617 _it_end -= erase_size;
618 tt_axiom(holds_invariant());
619 return iterator{this, _gap_begin};
620 }
621
626 iterator erase(const_iterator position) noexcept
627 {
628 return erase(position, position + 1);
629 }
630
631 [[nodiscard]] friend bool operator==(gap_buffer const &lhs, gap_buffer const &rhs) noexcept
632 {
633 if (lhs.size() != rhs.size()) {
634 return false;
635 } else {
636 return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
637 }
638 }
639
640 template<typename Container>
641 [[nodiscard]] friend bool operator==(gap_buffer const &lhs, Container const &rhs) noexcept
642 {
643 if (lhs.size() != rhs.size()) {
644 return false;
645 } else {
646 return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
647 }
648 }
649
650 template<typename Container>
651 [[nodiscard]] friend bool operator==(Container const &lhs, gap_buffer const &rhs) noexcept
652 {
653 if (lhs.size() != rhs.size()) {
654 return false;
655 } else {
656 return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
657 }
658 }
659
660private:
661 // By how much the buffer should grow when size() == capacity().
662 static constexpr difference_type _grow_size = 256;
663
666 value_type *_begin;
667
671 value_type *_it_end;
672
675 value_type *_gap_begin;
676
679 size_type _gap_size;
680
681#if TT_BUILT_TYPE == TT_BT_DEBUG
682 size_t _version = 0;
683#endif
684
685 [[no_unique_address]] allocator_type _allocator;
686
687 [[nodiscard]] bool holds_invariant() const noexcept
688 {
689 return (_begin == nullptr and _it_end == nullptr and _gap_begin == nullptr and _gap_size == 0) or
690 (_begin <= _gap_begin and _gap_begin <= _it_end);
691 }
692
695 void grow_to_insert(size_type n) noexcept
696 {
697 if (n > _gap_size) [[unlikely]] {
698 auto new_capacity = size() + n + narrow_cast<size_type>(_grow_size);
699 reserve(ceil(new_capacity, hardware_constructive_interference_size));
700 }
701 tt_axiom(holds_invariant());
702 }
703
706 const_pointer get_it_from_pointer(const_pointer ptr) const noexcept
707 {
708 if (ptr < _gap_begin) {
709 return ptr;
710 } else {
711 return ptr - _gap_size;
712 }
713 }
714
717 pointer get_it_from_pointer(pointer ptr) noexcept
718 {
719 if (ptr < _gap_begin) {
720 return ptr;
721 } else {
722 return ptr - _gap_size;
723 }
724 }
725
731 const_pointer get_const_pointer_from_it(const_pointer it_ptr) const noexcept
732 {
733 tt_axiom(it_ptr >= _begin && it_ptr <= _it_end);
734
735 if (it_ptr < _gap_begin) {
736 return it_ptr;
737 } else {
738 return it_ptr + _gap_size;
739 }
740 }
741
742 const_pointer get_pointer_from_it(pointer it_ptr) const noexcept
743 {
744 return get_const_pointer_from_it(it_ptr);
745 }
746
747 pointer get_pointer_from_it(pointer it_ptr) noexcept
748 {
749 return const_cast<pointer>(get_const_pointer_from_it(it_ptr));
750 }
751
757 const_pointer get_const_pointer_from_index(size_type index) const noexcept
758 {
759 return get_const_pointer_from_it(_begin + index);
760 }
761
762 const_pointer get_pointer_from_index(size_type index) const noexcept
763 {
764 return get_const_pointer_from_it(_begin + index);
765 }
766
767 pointer get_pointer_from_index(size_type index) noexcept
768 {
769 return get_pointer_from_it(_begin + index);
770 }
771
772 [[nodiscard]] value_type const *left_begin_ptr() const noexcept
773 {
774 return _begin;
775 }
776
777 [[nodiscard]] value_type *left_begin_ptr() noexcept
778 {
779 return _begin;
780 }
781
782 [[nodiscard]] value_type const *left_end_ptr() const noexcept
783 {
784 return _gap_begin;
785 }
786
787 [[nodiscard]] value_type *left_end_ptr() noexcept
788 {
789 return _gap_begin;
790 }
791
792 [[nodiscard]] size_type left_size() const noexcept
793 {
794 return static_cast<size_type>(_gap_begin - _begin);
795 }
796
797 [[nodiscard]] value_type const *right_begin_ptr() const noexcept
798 {
799 return _gap_begin + _gap_size;
800 }
801
802 [[nodiscard]] value_type *right_begin_ptr() noexcept
803 {
804 return _gap_begin + _gap_size;
805 }
806
807 [[nodiscard]] value_type const *right_end_ptr() const noexcept
808 {
809 return _it_end + _gap_size;
810 }
811
812 [[nodiscard]] value_type *right_end_ptr() noexcept
813 {
814 return _it_end + _gap_size;
815 }
816
817 [[nodiscard]] size_type right_size() const noexcept
818 {
819 return _it_end - _gap_begin;
820 }
821
822
825 void set_gap_offset(value_type *new_gap_begin) noexcept
826 {
827 if (new_gap_begin < _gap_begin) {
828 // Move data left of the original gap to the end of the new gap.
829 // LLL...RRR
830 // LL...LRRR
831 placement_move_within_array(new_gap_begin, _gap_begin, new_gap_begin + _gap_size);
832
833 } else if (new_gap_begin > _gap_begin) {
834 // Move data right of the original gap to the beginning of the new gap.
835 // LLL...RRR
836 // LLLR...RR
837 placement_move_within_array(_gap_begin + _gap_size, new_gap_begin + _gap_size, _gap_begin);
838 }
839
840 _gap_begin = new_gap_begin;
841 tt_axiom(holds_invariant());
842 }
843
844 template<typename IT>
845 friend class gap_buffer_iterator;
846};
847
850template<typename T>
852public:
853 static_assert(
854 !std::is_volatile_v<T> && !std::is_reference_v<T>,
855 "Type of a managing container iterator can not be volatile nor a reference");
856
857 static constexpr bool is_const = std::is_const_v<T>;
858
859 using value_type = std::remove_cv_t<T>;
860 using size_type = size_t;
861 using difference_type = ptrdiff_t;
862 using pointer = value_type *;
863 using const_pointer = value_type const *;
864 using reference = value_type &;
865 using const_reference = value_type const &;
867
868 using gap_buffer_type = std::conditional_t<is_const, gap_buffer<value_type> const, gap_buffer<value_type>>;
869
874
875 ~gap_buffer_iterator() noexcept = default;
876 gap_buffer_iterator(gap_buffer_iterator const &) noexcept = default;
877 gap_buffer_iterator(gap_buffer_iterator &&) noexcept = default;
878 gap_buffer_iterator &operator=(gap_buffer_iterator const &) noexcept = default;
879 gap_buffer_iterator &operator=(gap_buffer_iterator &&) noexcept = default;
880
881 gap_buffer_iterator(gap_buffer_iterator<value_type> const &other) noexcept requires(is_const) : _buffer(other._buffer), _it_ptr(other._it_ptr)
882 {
883#if TT_BUILT_TYPE == TT_BT_DEBUG
884 _version = other._version;
885#endif
886 tt_axiom(holds_invariant());
887 }
888
890 gap_buffer_type *buffer,
891 T *it_ptr
892#if TT_BUILT_TYPE == TT_BT_DEBUG
893 , size_t version
894#endif
895 ) noexcept :
896 _buffer(buffer),
897 _it_ptr(it_ptr)
898#if TT_BUILT_TYPE == TT_BT_DEBUG
899 , _version(version)
900#endif
901 {
902 tt_axiom(holds_invariant());
903 }
904
905 reference operator*() noexcept requires(!is_const)
906 {
907 return *(_buffer->get_pointer_from_it(_it_ptr));
908 }
909
910 const_reference operator*() const noexcept
911 {
912 return *(_buffer->get_const_pointer_from_it(_it_ptr));
913 }
914
915 pointer operator->() noexcept requires(!is_const)
916 {
917 return _buffer->get_pointer_from_it(_it_ptr);
918 }
919
920 const_pointer operator->() const noexcept
921 {
922 return _buffer->get_const_pointer_from_it(_it_ptr);
923 }
924
925 reference operator[](std::integral auto index) noexcept requires(!is_const)
926 {
927 return *(_buffer->get_pointer_from_it(_it_ptr + index));
928 }
929
930 const_reference operator[](std::integral auto index) const noexcept
931 {
932 return *(_buffer->get_const_pointer_from_it(_it_ptr + index));
933 }
934
935 gap_buffer_iterator &operator++() noexcept
936 {
937 ++_it_ptr;
938 tt_axiom(holds_invariant());
939 return *this;
940 }
941
942 gap_buffer_iterator operator++(int) noexcept
943 {
944 auto tmp = *this;
945 ++_it_ptr;
946 tt_axiom(holds_invariant());
947 return tmp;
948 }
949
950 gap_buffer_iterator &operator--() noexcept
951 {
952 --_it_ptr;
953 tt_axiom(holds_invariant());
954 return *this;
955 }
956
957 gap_buffer_iterator &operator--(int) noexcept
958 {
959 auto tmp = *this;
960 --_it_ptr;
961 tt_axiom(holds_invariant());
962 return tmp;
963 }
964
965 gap_buffer_iterator &operator+=(difference_type n) noexcept
966 {
967 _it_ptr += n;
968 tt_axiom(holds_invariant());
969 return *this;
970 }
971
972 gap_buffer_iterator &operator-=(difference_type n) noexcept
973 {
974 _it_ptr -= n;
975 tt_axiom(holds_invariant());
976 return *this;
977 }
978
979 gap_buffer_iterator operator-(difference_type n) const noexcept
980 {
981 auto tmp = *this;
982 return tmp -= n;
983 }
984
985 friend gap_buffer_iterator operator+(gap_buffer_iterator lhs, difference_type rhs) noexcept
986 {
987 return lhs += rhs;
988 }
989
990 friend gap_buffer_iterator operator+(difference_type lhs, gap_buffer_iterator rhs) noexcept
991 {
992 return rhs += lhs;
993 }
994
995 template<typename R>
996 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend difference_type
997 operator-(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
998 {
999 return lhs._it_ptr - rhs._it_ptr;
1000 }
1001
1002 template<typename R>
1003 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend bool
1004 operator==(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
1005 {
1006 return lhs._it_ptr == rhs.it_ptr();
1007 }
1008
1009 template<typename R>
1010 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend auto
1011 operator<=>(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
1012 {
1013 return lhs._it_ptr <=> rhs.it_ptr();
1014 }
1015
1016private:
1017 gap_buffer_type *_buffer;
1018 T *_it_ptr;
1019#if TT_BUILT_TYPE == TT_BT_DEBUG
1020 size_t _version;
1021#endif
1022
1023 [[nodiscard]] gap_buffer_iterator(gap_buffer_iterator<value_type const> const &other) noexcept requires(!is_const) :
1024 _buffer(const_cast<gap_buffer_type *>(other._buffer)), _it_ptr(const_cast<T *>(other._it_ptr))
1025 {
1026#if TT_BUILT_TYPE == TT_BT_DEBUG
1027 _version = other._version;
1028#endif
1029 tt_axiom(holds_invariant());
1030 }
1031
1032 [[nodiscard]] T *it_ptr() const noexcept
1033 {
1034 return _it_ptr;
1035 }
1036
1037 [[nodiscard]] std::remove_cv_t<T> *it_rw_ptr() const noexcept
1038 {
1039 return const_cast<std::remove_cv_t<T> *>(_it_ptr);
1040 }
1041
1042 [[nodiscard]] bool holds_invariant() const noexcept
1043 {
1044 auto check = true;
1045 check &= _buffer != nullptr;
1046 check &= _it_ptr >= _buffer->_begin;
1047 check &= _it_ptr <= _buffer->_it_end;
1048#if TT_BUILT_TYPE == TT_BT_DEBUG
1049 check &= _version == _buffer->_version;
1050#endif
1051 return check;
1052 }
1053
1054 template<typename O>
1055 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<O>>)
1056 [[nodiscard]] bool holds_invariant(gap_buffer_iterator<O> const &other) const noexcept
1057 {
1058 return holds_invariant() and other.holds_invariant() and _buffer == other._buffer;
1059 }
1060};
1061
1062//template<typename T, typename Allocator>
1063//gap_buffer_iterator<T> make_gap_buffer_iterator(gap_buffer<T, Allocator> *buffer, T *it_ptr)
1064//{
1065// return {buffer, it_ptr};
1066//}
1067//
1068//template<typename T, typename Allocator>
1069//gap_buffer_iterator<T const> make_gap_buffer_iterator(gap_buffer<T, Allocator> const *buffer, T *it_ptr)
1070//{
1071// return {buffer, it_ptr};
1072//}
1073
1074} // namespace tt
Gap Buffer This container is similar to a std::vector, optimized for repeated insertions and deletion...
Definition gap_buffer.hpp:46
void clear() noexcept
Clears the buffer.
Definition gap_buffer.hpp:346
const_reference operator[](size_type index) const noexcept
Index operator.
Definition gap_buffer.hpp:269
iterator erase(const_iterator position) noexcept
Erase item.
Definition gap_buffer.hpp:626
reference operator[](size_type index) noexcept
Index operator.
Definition gap_buffer.hpp:257
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:72
const_reference at(size_type index) const
Get item to reference at.
Definition gap_buffer.hpp:295
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:499
gap_buffer(gap_buffer &&other) noexcept
Move constructor.
Definition gap_buffer.hpp:155
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:542
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:563
gap_buffer & operator=(gap_buffer &&other) noexcept
Move assignment operator.
Definition gap_buffer.hpp:174
gap_buffer(gap_buffer const &other) noexcept
Copy constructor.
Definition gap_buffer.hpp:86
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:479
iterator erase(const_iterator first, const_iterator last) noexcept
Erase items.
Definition gap_buffer.hpp:602
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:509
gap_buffer & operator=(gap_buffer const &other) noexcept
Copy assignment.
Definition gap_buffer.hpp:113
~gap_buffer()
Destructor.
Definition gap_buffer.hpp:243
iterator insert_after(const_iterator position, It first, It last) noexcept
Insert items.
Definition gap_buffer.hpp:587
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:573
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:525
reference at(size_type index)
Get item to reference at.
Definition gap_buffer.hpp:281
gap_buffer(allocator_type const &allocator=allocator_type{}) noexcept
Construct an empty buffer.
Definition gap_buffer.hpp:64
A continues iterator over a gap_buffer.
Definition gap_buffer.hpp:851
Definition concepts.hpp:18
Definition concepts.hpp:21
T begin(T... args)
T ceil(T... args)
T end(T... args)
T equal(T... args)
T move(T... args)
T swap(T... args)