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> && !std::is_volatile_v<T> && !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 _ptr(nullptr), _size(0), _gap_offset(0), _gap_size(0), _allocator(allocator)
66 {
67 }
68
71 gap_buffer(std::initializer_list<T> init, allocator_type const &allocator = allocator_type{}) :
72 _ptr(_allocator.allocate(init.size() + _grow_size)),
73 _size(init.size() + _grow_size),
74 _gap_offset(init.size()),
75 _gap_size(_grow_size),
76 _allocator(allocator)
77 {
78 placement_copy(std::begin(init), std::end(init), left_begin());
79 }
80
84 gap_buffer(gap_buffer const &other) noexcept :
85 _ptr(nullptr),
86 _size(other._size),
87 _gap_offset(other._gap_offset),
88 _gap_size(other._gap_size),
89 _allocator(other._allocator)
90 {
91 tt_axiom(&other != this);
92
93 if (other._ptr != nullptr) {
94 _ptr = _allocator.allocate(_size);
95 placement_copy(other.left_begin(), other.left_end(), left_begin());
96 placement_copy(other.right_begin(), other.right_end(), right_begin());
97 }
98 }
99
106 gap_buffer &operator=(gap_buffer const &other) noexcept
107 {
108 tt_axiom(&other != this);
109
110 clear();
111 if (_size >= other.size()) {
112 // Reuse memory.
113 _gap_offset = other._gap_offset;
114 _gap_size = other._size - other.right_size();
115
116 placement_copy(other.left_begin(), other.left_end(), left_begin());
117 placement_copy(other.right_begin(), other.right_end(), right_begin());
118
119 } else {
120 // Deallocate previous memory.
121 if (_ptr != nullptr) {
122 _allocator.deallocate(_ptr, _size);
123 _ptr = nullptr;
124 _size = 0;
125 _gap_offset = 0;
126 _gap_size = 0;
127 }
128
129 // Allocate and copy date.
130 if (other._ptr != nullptr) {
131 _size = other.size() + _grow_size;
132 _ptr = _allocator.allocate(_size);
133 _gap_offset = other._gap_offset;
134 _gap_size = other._gap_size + _grow_size;
135
136 placement_copy(other.left_begin(), other.left_end(), left_begin());
137 placement_copy(other.right_begin(), other.right_end(), right_begin());
138 }
139 }
140 }
141
145 gap_buffer(gap_buffer &&other) noexcept :
146 _ptr(other._ptr),
147 _size(other._size),
148 _gap_offset(other._gap_offset),
149 _gap_size(other._gap_size),
150 _allocator(other._allocator)
151 {
152 other._ptr = nullptr;
153 other._size = 0;
154 other._gap_offset = 0;
155 other._gap_size = 0;
156 }
157
161 gap_buffer &operator=(gap_buffer &&other) noexcept
162 {
163 tt_axiom(&other != this);
164
165 // Clear the data inside this.
166 clear();
167
168 if (_allocator == other._allocator) {
169 // When allocators are the same we can simply swap.
170 std::swap(_allocator, other._allocator);
171 std::swap(_size, other._size);
172 std::swap(_gap_offset, other._gap_pffset);
173 std::swap(_gap_size, other._size);
174 return *this;
175 }
176
177 if (_size >= other.size()) {
178 // Reuse memory.
179 _gap_offset = other._gap_offset;
180 _gap_size = other._size - other.right_size();
181
182 placement_move(other.left_begin(), other.left_end(), left_begin());
183 placement_move(other.right_begin(), other.right_end(), right_begin());
184
185 } else {
186 // Deallocate previous memory.
187 if (_ptr != nullptr) {
188 _allocator.deallocate(_ptr, _size);
189 _ptr = nullptr;
190 _size = 0;
191 _gap_offset = 0;
192 _gap_size = 0;
193 }
194
195 // Allocate and copy date.
196 if (other._ptr != nullptr) {
197 _size = other.size() + _grow_size;
198 _ptr = _allocator.allocate(_size);
199 _gap_offset = other._gap_offset;
200 _gap_size = other._gap_size + _grow_size;
201
202 placement_move(other.left_begin(), other.left_end(), left_begin());
203 placement_move(other.right_begin(), other.right_end(), right_begin());
204 }
205 }
206
207 // All items where moved, but keep the memory allocated in other, for potential reuse.
208 other._gap_offset = 0;
209 other._gap_size = other._size;
210 }
211
216 {
217 clear();
218 if (_ptr != nullptr) {
219 _allocator.deallocate(_ptr, _size);
220 }
221 }
222
229 [[nodiscard]] reference operator[](size_type index) noexcept
230 {
231 tt_axiom(index < size());
232 return *get_pointer(index);
233 }
234
241 [[nodiscard]] const_reference operator[](size_type index) const noexcept
242 {
243 tt_axiom(index < size());
244 return *get_pointer(index);
245 }
246
253 [[nodiscard]] reference at(size_type index)
254 {
255 if (index < size()) {
256 throw std::out_of_range("gap_buffer::at");
257 }
258 return (*this)[index];
259 }
260
267 [[nodiscard]] const_reference at(size_type index) const
268 {
269 if (index < size()) {
270 throw std::out_of_range("gap_buffer::at");
271 }
272 return (*this)[index];
273 }
274
275 [[nodiscard]] reference front() noexcept
276 {
277 tt_axiom(size() != 0);
278 return *(this)[0];
279 }
280
281 [[nodiscard]] const_reference front() const noexcept
282 {
283 tt_axiom(size() != 0);
284 return *(this)[0];
285 }
286
287 [[nodiscard]] reference back() noexcept
288 {
289 tt_axiom(size() != 0);
290 return *(this)[size() - 1];
291 }
292
293 [[nodiscard]] const_reference back() const noexcept
294 {
295 tt_axiom(size() != 0);
296 return *(this)[size() - 1];
297 }
298
299 void pop_back() noexcept
300 {
301 tt_axiom(size() != 0);
302 erase(end() - 1);
303 }
304
305 void pop_front() noexcept
306 {
307 tt_axiom(size() != 0);
308 erase(begin());
309 }
310
318 void clear() noexcept
319 {
320 if (_ptr) {
321 std::destroy(left_begin(), left_end());
322 std::destroy(right_begin(), right_end());
323 _gap_offset = 0;
324 _gap_size = _size;
325 }
326 }
327
328 [[nodiscard]] size_t size() const noexcept
329 {
330 return _size - _gap_size;
331 }
332
333 [[nodiscard]] bool empty() const noexcept
334 {
335 return size() == 0;
336 }
337
338 [[nodiscard]] size_t capacity() const noexcept
339 {
340 return _size;
341 }
342
343 void reserve(size_t new_capacity) noexcept
344 {
345 ttlet extra_capacity = static_cast<ssize_t>(new_capacity) - capacity();
346 if (extra_capacity <= 0) {
347 return;
348 }
349
350 // Add the extra_capacity to the end of the gap.
351 // LLL...RRR
352 // LLL....RRR
353 ttlet new_ptr = _allocator.allocate(new_capacity);
354 ttlet new_size = _size + extra_capacity;
355 ttlet new_gap_offset = _gap_offset;
356 ttlet new_gap_size = _gap_size + extra_capacity;
357
358 if (_ptr != nullptr) {
359 ttlet new_left_begin = new_ptr;
360 ttlet new_right_begin = new_ptr + new_gap_offset + new_gap_size;
361 placement_move(left_begin(), left_end(), new_left_begin);
362 placement_move(right_begin(), right_end(), new_right_begin);
363 _allocator.deallocate(_ptr, _size);
364 }
365
366 _ptr = new_ptr;
367 _gap_offset = new_gap_offset;
368 _gap_size = new_gap_size;
369 _size = new_size;
370 }
371
372 [[nodiscard]] iterator begin() noexcept
373 {
374 return make_gap_buffer_iterator(this, 0);
375 }
376
377 [[nodiscard]] const_iterator begin() const noexcept
378 {
379 return make_gap_buffer_iterator(this, 0);
380 }
381
382 [[nodiscard]] iterator cbegin() const noexcept
383 {
384 return make_gap_buffer_iterator(this, 0);
385 }
386
387 [[nodiscard]] iterator end() noexcept
388 {
389 return make_gap_buffer_iterator(this, narrow_cast<difference_type>(size()));
390 }
391
392 [[nodiscard]] const_iterator end() const noexcept
393 {
394 return make_gap_buffer_iterator(this, narrow_cast<difference_type>(size()));
395 }
396
397 [[nodiscard]] iterator cend() const noexcept
398 {
399 return make_gap_buffer_iterator(this, narrow_cast<difference_type>(size()));
400 }
401
402 template<typename... Args>
403 void emplace_back(Args &&...args) noexcept
404 {
405 set_gap_offset(size());
406 grow_to_insert(1);
407
408 auto p = _ptr + _gap_offset;
409 new (p) value_type(std::forward<Args>(args)...);
410 ++_gap_offset;
411 --_gap_size;
412 }
413
414 void push_back(value_type const &value) noexcept
415 {
416 return emplace_back(value);
417 }
418
419 void push_back(value_type &&value) noexcept
420 {
421 return emplace_back(std::move(value));
422 }
423
424 template<typename... Args>
425 void emplace_front(Args &&...args) noexcept
426 {
427 set_gap_offset(0);
428 grow_to_insert(1);
429
430 auto p = _ptr + _gap_offset + _gap_size - 1;
431 new (p) value_type(std::forward<Args>(args)...);
432 --_gap_size;
433 }
434
435 void push_front(value_type const &value) noexcept
436 {
437 return emplace_front(value);
438 }
439
440 void push_front(value_type &&value) noexcept
441 {
442 return emplace_front(std::move(value));
443 }
444
447 template<typename... Args>
448 iterator emplace_before(iterator position, Args &&...args) noexcept
449 {
450 tt_axiom(position.buffer() == this);
451 set_gap_offset(position.index());
452 grow_to_insert(1);
453
454 auto p = _ptr + _gap_offset + _gap_size - 1;
455 new (p) value_type(std::forward<Args>(args)...);
456 --_gap_size;
457 return gap_buffer_iterator<T>(this, _gap_offset + _gap_size);
458 }
459
460 iterator insert_before(iterator position, value_type const &value) noexcept
461 {
462 return emplace_before(position, value_type(value));
463 }
464
465 iterator insert_before(iterator position, value_type &&value) noexcept
466 {
467 return emplace_before(position, std::move(value));
468 }
469
477 template<typename It>
478 iterator insert_before(iterator position, It first, It last) noexcept
479 {
480 auto it = last;
481 while (it != first) {
482 position = insert_before(position, *(--it));
483 }
484 return position;
485 }
486
489 template<typename... Args>
490 iterator emplace_after(iterator position, Args &&...args) noexcept
491 {
492 tt_axiom(position.buffer() == this);
493 set_gap_offset(position.index() + 1);
494 grow_to_insert(1);
495
496 auto p = _ptr + _gap_offset;
497 new (p) value_type(std::forward<Args>(args)...);
498 ++_gap_offset;
499 --_gap_size;
500 return gap_buffer_iterator<T>(this, _gap_offset);
501 }
502
503 iterator insert_after(iterator position, value_type const &value) noexcept
504 {
505 return emplace_after(position, value_type(value));
506 }
507
508 iterator insert_after(iterator position, value_type &&value) noexcept
509 {
510 return emplace_after(position, std::move(value));
511 }
512
520 template<typename It>
521 iterator insert_after(iterator position, It first, It last) noexcept
522 {
523 for (auto it = first; it != last; ++it) {
524 position = insert_after(position, *it);
525 }
526 return position;
527 }
528
534 iterator erase(iterator first, iterator last) noexcept
535 {
536 // place the gap before the first iterator, so that we can extend it.
537 tt_axiom(first.buffer() == this);
538 tt_axiom(last.buffer() == this);
539
540 set_gap_offset(first.index());
541 ttlet first_p = get_pointer(first.index());
542 ttlet last_p = get_pointer(last.index());
543 std::destroy(first_p, last_p);
544 _gap_size += last_p - first_p;
545 return make_gap_buffer_iterator(this, _gap_offset);
546 }
547
552 iterator erase(iterator position) noexcept
553 {
554 return erase(position, position + 1);
555 }
556
557 [[nodiscard]] friend bool operator==(gap_buffer const &lhs, gap_buffer const &rhs) noexcept
558 {
559 if (lhs.size() != rhs.size()) {
560 return false;
561 } else {
562 return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
563 }
564 }
565
566 template<typename Container>
567 [[nodiscard]] friend bool operator==(gap_buffer const &lhs, Container const &rhs) noexcept
568 {
569 if (lhs.size() != rhs.size()) {
570 return false;
571 } else {
572 return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
573 }
574 }
575
576 template<typename Container>
577 [[nodiscard]] friend bool operator==(Container const &lhs, gap_buffer const &rhs) noexcept
578 {
579 if (lhs.size() != rhs.size()) {
580 return false;
581 } else {
582 return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
583 }
584 }
585
586private:
587 // By how much the buffer should grow when size() == capacity().
588 static constexpr difference_type _grow_size = 256;
589
590 value_type *_ptr;
591 difference_type _size;
592 difference_type _gap_offset;
593 difference_type _gap_size;
594 [[no_unique_address]] allocator_type _allocator;
595
596 [[nodiscard]] bool is_valid() const noexcept
597 {
598 return _size >= 0 && _gap_offset >= 0 && _gap_size >= 0 && _gap_offset + _gap_size <= _size &&
599 (_ptr != nullptr || (_size == 0 && _gap_offset == 0 && _gap_size == 0));
600 }
601
604 void grow_to_insert(size_type n) noexcept
605 {
606 tt_axiom(is_valid());
607 tt_axiom(n >= 0);
608 if (n > narrow_cast<size_type>(_gap_size)) [[unlikely]] {
609 auto new_capacity = size() + n + narrow_cast<size_type>(_grow_size);
610 reserve(ceil(new_capacity, hardware_constructive_interference_size));
611 }
612 }
613
619 [[nodiscard]] difference_type get_offset(difference_type index) const noexcept
620 {
621 tt_axiom(is_valid());
622 tt_axiom(index >= 0 && index <= std::ssize(*this));
623 if (index >= _gap_offset) {
624 index += _gap_size;
625 }
626 return index;
627 }
628
634 pointer get_pointer(difference_type index) noexcept
635 {
636 tt_axiom(is_valid());
637 tt_axiom(index >= 0);
638 return std::launder(_ptr + get_offset(index));
639 }
640
646 const_pointer get_pointer(difference_type index) const noexcept
647 {
648 tt_axiom(is_valid());
649 tt_axiom(index >= 0);
650 return std::launder(_ptr + get_offset(index));
651 }
652
653 [[nodiscard]] value_type const *left_begin() const noexcept
654 {
655 return _ptr;
656 }
657
658 [[nodiscard]] value_type *left_begin() noexcept
659 {
660 return _ptr;
661 }
662
663 [[nodiscard]] value_type const *left_end() const noexcept
664 {
665 tt_axiom(is_valid());
666 return _ptr + _gap_offset;
667 }
668
669 [[nodiscard]] value_type *left_end() noexcept
670 {
671 tt_axiom(is_valid());
672 return _ptr + _gap_offset;
673 }
674
675 [[nodiscard]] value_type const *right_begin() const noexcept
676 {
677 tt_axiom(is_valid());
678 return _ptr + _gap_offset + _gap_size;
679 }
680
681 [[nodiscard]] value_type *right_begin() noexcept
682 {
683 tt_axiom(is_valid());
684 return _ptr + _gap_offset + _gap_size;
685 }
686
687 [[nodiscard]] value_type const *right_end() const noexcept
688 {
689 tt_axiom(is_valid());
690 return _ptr + _size;
691 }
692
693 [[nodiscard]] value_type *right_end() noexcept
694 {
695 tt_axiom(is_valid());
696 return _ptr + _size;
697 }
698
699 [[nodiscard]] value_type const *gap_begin() const noexcept
700 {
701 tt_axiom(is_valid());
702 return _ptr + _gap_offset;
703 }
704
705 [[nodiscard]] value_type *gap_begin() noexcept
706 {
707 tt_axiom(is_valid());
708 return _ptr + _gap_offset;
709 }
710
711 [[nodiscard]] value_type const *gap_end() const noexcept
712 {
713 tt_axiom(is_valid());
714 return _ptr + _gap_offset + _gap_size;
715 }
716
717 [[nodiscard]] value_type *gap_end() noexcept
718 {
719 tt_axiom(is_valid());
720 return _ptr + _gap_offset + _gap_size;
721 }
722
725 void set_gap_offset(difference_type new_gap_offset) noexcept
726 {
727 ttlet new_gap_begin = _ptr + new_gap_offset;
728 ttlet new_gap_end = new_gap_begin + _gap_size;
729
730 if (new_gap_offset < _gap_offset) {
731 // Move data left of the original gap to the end of the new gap.
732 // LLL...RRR
733 // LL...LRRR
734 placement_move_within_array(new_gap_begin, gap_begin(), new_gap_end);
735
736 } else if (new_gap_offset > _gap_offset) {
737 // Move data right of the original gap to the beginning of the new gap.
738 // LLL...RRR
739 // LLLR...RR
740 placement_move_within_array(gap_end(), new_gap_end, gap_begin());
741 }
742
743 _gap_offset = new_gap_offset;
744 }
745
746 friend gap_buffer_iterator<T>;
747};
748
751template<typename T>
753public:
754 static_assert(
755 !std::is_volatile_v<T> && !std::is_reference_v<T>,
756 "Type of a managing container iterator can not be volatile nor a reference");
757
758 using value_type = std::remove_cv_t<T>;
759 using size_type = size_t;
760 using difference_type = ptrdiff_t;
761 using pointer = T *;
762 using const_pointer = T const *;
763 using reference = T &;
764 using const_reference = T const &;
766
767 using gap_buffer_type = std::conditional_t<std::is_const_v<T>, gap_buffer<value_type> const, gap_buffer<value_type>>;
768
769 ~gap_buffer_iterator() noexcept = default;
770 gap_buffer_iterator(gap_buffer_iterator const &) noexcept = default;
771 gap_buffer_iterator(gap_buffer_iterator &&) noexcept = default;
772 gap_buffer_iterator &operator=(gap_buffer_iterator const &) noexcept = default;
773 gap_buffer_iterator &operator=(gap_buffer_iterator &&) noexcept = default;
774
775 gap_buffer_iterator(gap_buffer_type *buffer, difference_type index) noexcept : _buffer(buffer), _index(index) {}
776
777 gap_buffer_type *buffer() const noexcept
778 {
779 return _buffer;
780 }
781
782 difference_type index() const noexcept
783 {
784 return _index;
785 }
786
787 reference operator*() noexcept
788 {
789 return (*_buffer)[narrow_cast<size_type>(_index)];
790 }
791
792 const_reference operator*() const noexcept
793 {
794 tt_axiom(is_valid());
795 return (*_buffer)[narrow_cast<size_type>(_index)];
796 }
797
798 reference operator[](std::integral auto index) noexcept
799 {
800 return (*_buffer)[narrow_cast<size_type>(_index + narrow_cast<difference_type>(index))];
801 }
802
803 const_reference operator[](std::integral auto index) const noexcept
804 {
805 return (*_buffer)[narrow_cast<size_type>(_index + narrow_cast<difference_type>(index))];
806 }
807
808 gap_buffer_iterator &operator++() noexcept
809 {
810 ++_index;
811 tt_axiom(is_valid());
812 return *this;
813 }
814
815 gap_buffer_iterator operator++(int) noexcept
816 {
817 auto tmp = *this;
818 ++_index;
819 tt_axiom(is_valid());
820 return tmp;
821 }
822
823 gap_buffer_iterator &operator--() noexcept
824 {
825 --_index;
826 tt_axiom(is_valid());
827 return *this;
828 }
829
830 gap_buffer_iterator &operator--(int) noexcept
831 {
832 auto tmp = *this;
833 --_index;
834 tt_axiom(is_valid());
835 return tmp;
836 }
837
838 gap_buffer_iterator &operator+=(std::integral auto n) noexcept
839 {
840 _index += narrow_cast<difference_type>(n);
841 tt_axiom(is_valid());
842 return *this;
843 }
844
845 gap_buffer_iterator &operator-=(std::integral auto n) noexcept
846 {
847 _index -= narrow_cast<difference_type>(n);
848 tt_axiom(is_valid());
849 return *this;
850 }
851
852 gap_buffer_iterator operator-(std::integral auto n) const noexcept
853 {
854 auto tmp = *this;
855 return tmp -= n;
856 }
857
858 friend gap_buffer_iterator operator+(gap_buffer_iterator lhs, std::integral auto rhs) noexcept
859 {
860 return lhs += rhs;
861 }
862
863 friend gap_buffer_iterator operator+(std::integral auto lhs, gap_buffer_iterator rhs) noexcept
864 {
865 return rhs += lhs;
866 }
867
868 template<typename R>
869 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend difference_type
870 operator-(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
871 {
872 tt_axiom(lhs.is_valid(rhs));
873 return lhs._index - rhs._index;
874 }
875
876 template<typename R>
877 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend bool
878 operator==(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
879 {
880 tt_axiom(lhs.is_valid(rhs));
881 return lhs._index == rhs._index;
882 }
883
884 template<typename R>
885 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend bool
886 operator!=(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
887 {
888 tt_axiom(lhs.is_valid(rhs));
889 return lhs._index != rhs._index;
890 }
891
892 template<typename R>
893 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend bool
894 operator<(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
895 {
896 tt_axiom(lhs.is_valid(rhs));
897 return lhs._index < rhs._index;
898 }
899
900 template<typename R>
901 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend bool
902 operator>(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
903 {
904 tt_axiom(lhs.is_valid(rhs));
905 return lhs._index < rhs._index;
906 }
907
908 template<typename R>
909 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend bool
910 operator<=(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
911 {
912 tt_axiom(lhs.is_valid(rhs));
913 return lhs._index <= rhs._index;
914 }
915
916 template<typename R>
917 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend bool
918 operator>=(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
919 {
920 tt_axiom(lhs.is_valid(rhs));
921 return lhs._index >= rhs._index;
922 }
923
924private:
925 gap_buffer_type *_buffer;
926 difference_type _index;
927
928 [[nodiscard]] bool is_valid() const noexcept
929 {
930 return _buffer != nullptr && _index >= 0 && _index <= std::ssize(*_buffer);
931 }
932
933 template<typename O>
934 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<O>>)
935 [[nodiscard]] bool is_valid(gap_buffer_iterator<O> const &other) const noexcept
936 {
937 return is_valid() && other.is_valid() && _buffer == other._buffer;
938 }
939};
940
941template<typename T, typename Allocator>
942gap_buffer_iterator<T> make_gap_buffer_iterator(gap_buffer<T, Allocator> *buffer, ptrdiff_t index)
943{
944 return {buffer, index};
945}
946
947template<typename T, typename Allocator>
948gap_buffer_iterator<T const> make_gap_buffer_iterator(gap_buffer<T, Allocator> const *buffer, ptrdiff_t index)
949{
950 return {buffer, index};
951}
952
953} // 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:318
const_reference operator[](size_type index) const noexcept
Index operator.
Definition gap_buffer.hpp:241
iterator erase(iterator first, iterator last) noexcept
Erase items.
Definition gap_buffer.hpp:534
reference operator[](size_type index) noexcept
Index operator.
Definition gap_buffer.hpp:229
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:71
const_reference at(size_type index) const
Get item to reference at.
Definition gap_buffer.hpp:267
iterator insert_after(iterator position, It first, It last) noexcept
Insert items.
Definition gap_buffer.hpp:521
gap_buffer(gap_buffer &&other) noexcept
Move constructor.
Definition gap_buffer.hpp:145
iterator emplace_before(iterator position, Args &&...args) noexcept
Place the gap before the position and emplace at the end of the gap.
Definition gap_buffer.hpp:448
gap_buffer & operator=(gap_buffer &&other) noexcept
Move assignment operator.
Definition gap_buffer.hpp:161
iterator emplace_after(iterator position, Args &&...args) noexcept
Place the gap after the position and emplace at the beginning of the gap.
Definition gap_buffer.hpp:490
iterator erase(iterator position) noexcept
Erase item.
Definition gap_buffer.hpp:552
gap_buffer(gap_buffer const &other) noexcept
Copy constructor.
Definition gap_buffer.hpp:84
gap_buffer & operator=(gap_buffer const &other) noexcept
Copy assignment.
Definition gap_buffer.hpp:106
~gap_buffer()
Destructor.
Definition gap_buffer.hpp:215
iterator insert_before(iterator position, It first, It last) noexcept
Insert items.
Definition gap_buffer.hpp:478
reference at(size_type index)
Get item to reference at.
Definition gap_buffer.hpp:253
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:752
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)