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 hi::inline v1 {
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 = std::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 hi_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(init.begin(), init.end(), _begin);
80 hi_axiom(holds_invariant());
81 }
82
86 gap_buffer(gap_buffer const &other) noexcept :
87 _begin(nullptr), _it_end(nullptr), _gap_begin(nullptr), _gap_size(0), _allocator(other._allocator)
88 {
89 hi_axiom(&other != this);
90
91 if (other._ptr != nullptr) {
92 _begin = _allocator.allocate(other.capacity());
93 _it_end = _begin + other.size();
94 _gap_begin = _begin + other.left_size();
95 _gap_size = other._gap_size;
96
97 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
98 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
99 }
100 hi_axiom(holds_invariant());
101 }
102
109 gap_buffer &operator=(gap_buffer const &other) noexcept
110 {
111 hi_return_on_self_assignment(other);
112
113 clear();
114 if (_gap_size >= other.size()) {
115 // Reuse memory.
116 _gap_begin = _begin + other.left_size();
117 _gap_size = capacity() - other.size();
118
119 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
120 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
121
122 } else {
123 // Deallocate previous memory.
124 if (_begin != nullptr) {
125 _allocator.deallocate(_begin, capacity());
126 _begin = nullptr;
127 _it_end = nullptr;
128 _gap_begin = nullptr;
129 _gap_size = 0;
130 }
131
132 // Allocate and copy date.
133 if (other._begin != nullptr) {
134 hilet new_capacity = other.size() + _grow_size;
135
136 _begin = _allocator.allocate(new_capacity);
137 _it_end = _begin + other.size();
138 _gap_begin = _begin + other.left_begin_ptr();
139 _gap_size = new_capacity - other.size();
140
141 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
142 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
143 }
144 }
145 hi_axiom(holds_invariant());
146 }
147
151 gap_buffer(gap_buffer &&other) noexcept :
152 _begin(other._begin),
153 _it_end(other._it_end),
154 _gap_begin(other._gap_begin),
155 _gap_size(other._gap_size),
156 _allocator(other._allocator)
157 {
158 hi_axiom(&other != this);
159
160 other._begin = nullptr;
161 other._it_end = nullptr;
162 other._gap_begin = nullptr;
163 other._gap_size = 0;
164 hi_axiom(holds_invariant());
165 }
166
170 gap_buffer &operator=(gap_buffer &&other) noexcept
171 {
172 hi_return_on_self_assignment(other);
173
174 // Clear the data inside this.
175 clear();
176
177 if (_allocator == other._allocator) {
178 // When allocators are the same we can simply swap.
179 std::swap(_begin, other._begin);
180 std::swap(_it_end, other._it_end);
181 std::swap(_gap_begin, other._gap_begin);
182 std::swap(_gap_size, other._size);
183 hi_axiom(holds_invariant());
184 return *this;
185
186 } else if (capacity() >= other.size()) {
187 // Reuse memory of this.
188 _it_end = _begin + other.size();
189 _gap_begin = _begin + other.left_size();
190 _gap_size = capacity() - other.size();
191
192 placement_move(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
193 placement_move(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
194
195 // Other can keep its own capacity.
196 hilet other_capacity = other.capacity();
197 other._it_end = other._begin;
198 other._gap_begin = other._begin;
199 other._gap_size = other_capacity;
200 hi_axiom(holds_invariant());
201 return *this;
202
203 } else {
204 // Deallocate previous memory.
205 if (_begin != nullptr) {
206 _allocator.deallocate(_begin, capacity());
207 _begin = nullptr;
208 _it_end = nullptr;
209 _gap_begin = nullptr;
210 _gap_size = 0;
211 }
212
213 // Allocate and copy date.
214 if (other._begin != nullptr) {
215 hilet new_capacity = other.size() + _grow_size;
216
217 _begin = _allocator.allocate(new_capacity);
218 _it_end = _begin + other.size();
219 _gap_begin = _begin + other.left_size();
220 _gap_size = new_capacity - other.size();
221
222 placement_move(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
223 placement_move(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
224 }
225
226 // Other can keep its own capacity.
227 hilet other_capacity = other.capacity();
228 other._it_end = other._begin;
229 other._gap_begin = other._begin;
230 other._gap_size = other_capacity;
231 hi_axiom(holds_invariant());
232 return *this;
233 }
234 }
235
240 {
241 clear();
242 if (_begin != nullptr) {
243 _allocator.deallocate(_begin, capacity());
244 }
245 }
246
253 [[nodiscard]] reference operator[](size_type index) noexcept
254 {
255 hi_axiom(index < size());
256 return *get_pointer_from_index(index);
257 }
258
265 [[nodiscard]] const_reference operator[](size_type index) const noexcept
266 {
267 hi_axiom(index < size());
268 return *get_pointer_from_index(index);
269 }
270
277 [[nodiscard]] reference at(size_type index)
278 {
279 if (index < size()) {
280 throw std::out_of_range("gap_buffer::at");
281 }
282 return *get_pointer_from_index(index);
283 }
284
291 [[nodiscard]] const_reference at(size_type index) const
292 {
293 if (index < size()) {
294 throw std::out_of_range("gap_buffer::at");
295 }
296 return *get_pointer_from_index(index);
297 }
298
299 [[nodiscard]] reference front() noexcept
300 {
301 hi_axiom(size() != 0);
302 return *get_pointer_from_it_ptr(_begin);
303 }
304
305 [[nodiscard]] const_reference front() const noexcept
306 {
307 hi_axiom(size() != 0);
308 return *get_pointer_from_it_ptr(_begin);
309 }
310
311 [[nodiscard]] reference back() noexcept
312 {
313 hi_axiom(size() != 0);
314 return *get_pointer_from_it_ptr(_it_end - 1);
315 }
316
317 [[nodiscard]] const_reference back() const noexcept
318 {
319 hi_axiom(size() != 0);
320 return *get_pointer_from_it_ptr(_it_end - 1);
321 }
322
323 void pop_back() noexcept
324 {
325 hi_axiom(size() != 0);
326 erase(end() - 1);
327 }
328
329 void pop_front() noexcept
330 {
331 hi_axiom(size() != 0);
332 erase(begin());
333 }
334
342 void clear() noexcept
343 {
344 if (_begin) {
345 std::destroy(left_begin_ptr(), left_end_ptr());
346 std::destroy(right_begin_ptr(), right_end_ptr());
347 hilet this_capacity = capacity();
348 _it_end = _begin;
349 _gap_begin = _begin;
350 _gap_size = this_capacity;
351 }
352 hi_axiom(holds_invariant());
353 }
354
355 [[nodiscard]] std::size_t size() const noexcept
356 {
357 return static_cast<std::size_t>(_it_end - _begin);
358 }
359
360 [[nodiscard]] bool empty() const noexcept
361 {
362 return size() == 0;
363 }
364
365 [[nodiscard]] std::size_t capacity() const noexcept
366 {
367 return size() + _gap_size;
368 }
369
370 void reserve(std::size_t new_capacity) noexcept
371 {
372 hilet extra_capacity = static_cast<ssize_t>(new_capacity) - capacity();
373 if (extra_capacity <= 0) {
374 return;
375 }
376
377 // Add the extra_capacity to the end of the gap.
378 // LLL...RRR
379 // LLL....RRR
380 hilet new_begin = _allocator.allocate(new_capacity);
381 hilet new_it_end = new_begin + size();
382 hilet new_gap_begin = new_begin + left_size();
383 hilet new_gap_size = new_capacity - size();
384
385 if (_begin != nullptr) {
386 placement_move(left_begin_ptr(), left_end_ptr(), new_begin);
387 placement_move(right_begin_ptr(), right_end_ptr(), new_gap_begin + new_gap_size);
388 _allocator.deallocate(_begin, capacity());
389 }
390
391 _begin = new_begin;
392 _it_end = new_it_end;
393 _gap_begin = new_gap_begin;
394 _gap_size = new_gap_size;
395 hi_axiom(holds_invariant());
396 }
397
398 [[nodiscard]] iterator begin() noexcept
399 {
400 return iterator{this, _begin};
401 }
402
403 [[nodiscard]] const_iterator begin() const noexcept
404 {
405 return const_iterator{this, _begin};
406 }
407
408 [[nodiscard]] const_iterator cbegin() const noexcept
409 {
410 return const_iterator{this, _begin};
411 }
412
413 [[nodiscard]] iterator end() noexcept
414 {
415 return iterator{this, _it_end};
416 }
417
418 [[nodiscard]] const_iterator end() const noexcept
419 {
420 return const_iterator{this, _it_end};
421 }
422
423 [[nodiscard]] const_iterator cend() const noexcept
424 {
425 return const_iterator{this, _it_end};
426 }
427
428 [[nodiscard]] friend iterator begin(gap_buffer &rhs) noexcept
429 {
430 return rhs.begin();
431 }
432
433 [[nodiscard]] friend const_iterator begin(gap_buffer const &rhs) noexcept
434 {
435 return rhs.begin();
436 }
437
438 [[nodiscard]] friend const_iterator cbegin(gap_buffer const &rhs) noexcept
439 {
440 return rhs.begin();
441 }
442
443 [[nodiscard]] friend iterator end(gap_buffer &rhs) noexcept
444 {
445 return rhs.end();
446 }
447
448 [[nodiscard]] friend const_iterator end(gap_buffer const &rhs) noexcept
449 {
450 return rhs.end();
451 }
452
453 [[nodiscard]] friend const_iterator cend(gap_buffer const &rhs) noexcept
454 {
455 return rhs.end();
456 }
457
458 template<typename... Args>
459 reference emplace_back(Args &&...args) noexcept
460 {
461 return emplace_after(end(), std::forward<Args>(args)...);
462 }
463
464 void push_back(value_type const &value) noexcept
465 {
466 emplace_back(value);
467 }
468
469 void push_back(value_type &&value) noexcept
470 {
471 emplace_back(std::move(value));
472 }
473
474 template<typename... Args>
475 reference emplace_front(Args &&...args) noexcept
476 {
477 set_gap_offset(_begin);
478 grow_to_insert(1);
479
480 auto ptr = new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
481 ++_it_end;
482 --_gap_size;
483#if HI_BUILT_TYPE == HI_BT_DEBUG
484 ++_version;
485#endif
486 hi_axiom(holds_invariant());
487 return *ptr;
488 }
489
490 void push_front(value_type const &value) noexcept
491 {
492 emplace_front(value);
493 }
494
495 void push_front(value_type &&value) noexcept
496 {
497 emplace_front(std::move(value));
498 }
499
504 template<typename... Args>
505 reference emplace_before(const_iterator position, Args &&...args) noexcept
506 {
507 hi_axiom(position._buffer == this);
508 set_gap_offset(position.it_rw_ptr());
509 grow_to_insert(1);
510
511 auto ptr = new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
512 ++_it_end;
513 --_gap_size;
514#if HI_BUILT_TYPE == HI_BT_DEBUG
515 ++_version;
516#endif
517 hi_axiom(holds_invariant());
518 return *ptr;
519 }
520
525 iterator insert_before(const_iterator position, value_type const &value) noexcept
526 {
527 auto ptr = &emplace_before(position, value_type(value));
528 return iterator{this, get_it_from_pointer(ptr)};
529 }
530
535 iterator insert_before(const_iterator position, value_type &&value) noexcept
536 {
537 auto ptr = &emplace_before(position, std::move(value));
538 return iterator{this, get_it_from_pointer(ptr)};
539 }
540
550 template<typename It>
551 iterator insert_before(const_iterator position, It first, It last) noexcept
552 {
553 // Insert last to first, that way the position returned is
554 // a valid iterator to the first inserted element.
555 auto it = last;
556 while (it != first) {
557 position = insert_before(position, *(--it));
558 }
559 hi_axiom(holds_invariant());
560 return position;
561 }
562
567 template<typename... Args>
568 reference emplace_after(const_iterator position, Args &&...args) noexcept
569 {
570 hi_axiom(position._buffer == this);
571 set_gap_offset(position.it_rw_ptr());
572 grow_to_insert(1);
573
574 auto ptr = new (left_end_ptr()) value_type(std::forward<Args>(args)...);
575 ++_it_end;
576 ++_gap_begin;
577 --_gap_size;
578#if HI_BUILT_TYPE == HI_BT_DEBUG
579 ++_version;
580#endif
581 hi_axiom(holds_invariant());
582 return *ptr;
583 }
584
589 iterator insert_after(const_iterator position, value_type const &value) noexcept
590 {
591 auto ptr = &emplace_after(position, value_type(value));
592 return iterator{this, get_it_from_pointer(ptr)};
593 }
594
599 iterator insert_after(const_iterator position, value_type &&value) noexcept
600 {
601 auto ptr = &emplace_after(position, std::move(value));
602 return iterator{this, get_it_from_pointer(ptr)};
603 }
604
612 template<typename It>
613 iterator insert_after(const_iterator position, It first, It last) noexcept
614 {
615 auto position_ = iterator{position};
616 for (auto it = first; it != last; ++it) {
617 position_ = insert_after(position_, *it) + 1;
618 }
619 hi_axiom(holds_invariant());
620 return position_;
621 }
622
629 {
630 // place the gap after the last iterator, this way we can use the
631 // it_ptr directly because we don't need to skip the gap.
632 hi_axiom(first._buffer == this);
633 hi_axiom(last._buffer == this);
634
635 set_gap_offset(last.it_rw_ptr());
636 auto first_p = first.it_rw_ptr();
637 auto last_p = last.it_rw_ptr();
638 hilet erase_size = last_p - first_p;
639
640 std::destroy(first_p, last_p);
641 _gap_begin = first_p;
642 _gap_size += erase_size;
643 _it_end -= erase_size;
644 hi_axiom(holds_invariant());
645 return iterator{this, _gap_begin};
646 }
647
652 iterator erase(const_iterator position) noexcept
653 {
654 return erase(position, position + 1);
655 }
656
657 [[nodiscard]] friend bool operator==(gap_buffer const &lhs, gap_buffer const &rhs) noexcept
658 {
659 if (lhs.size() != rhs.size()) {
660 return false;
661 } else {
662 return std::equal(lhs.begin(), lhs.end(), rhs.begin());
663 }
664 }
665
666 template<typename Container>
667 [[nodiscard]] friend bool operator==(gap_buffer const &lhs, Container const &rhs) noexcept
668 {
669 using std::size;
670 using std::begin;
671
672 if (lhs.size() != size(rhs)) {
673 return false;
674 } else {
675 return std::equal(lhs.begin(), lhs.end(), begin(rhs));
676 }
677 }
678
679 template<typename Container>
680 [[nodiscard]] friend bool operator==(Container const &lhs, gap_buffer const &rhs) noexcept
681 {
682 return rhs == lhs;
683 }
684
685private:
686 // By how much the buffer should grow when size() == capacity().
687 static constexpr difference_type _grow_size = 256;
688
691 value_type *_begin;
692
696 value_type *_it_end;
697
700 value_type *_gap_begin;
701
704 size_type _gap_size;
705
706#if HI_BUILT_TYPE == HI_BT_DEBUG
707 std::size_t _version = 0;
708#endif
709
710 [[no_unique_address]] allocator_type _allocator;
711
712 [[nodiscard]] bool holds_invariant() const noexcept
713 {
714 return (_begin == nullptr and _it_end == nullptr and _gap_begin == nullptr and _gap_size == 0) or
715 (_begin <= _gap_begin and _gap_begin <= _it_end);
716 }
717
720 void grow_to_insert(size_type n) noexcept
721 {
722 if (n > _gap_size) [[unlikely]] {
723 auto new_capacity = size() + n + narrow_cast<size_type>(_grow_size);
724 reserve(ceil(new_capacity, hardware_constructive_interference_size));
725 }
726 hi_axiom(holds_invariant());
727 }
728
731 const_pointer get_it_from_pointer(const_pointer ptr) const noexcept
732 {
733 if (ptr < _gap_begin) {
734 return ptr;
735 } else {
736 return ptr - _gap_size;
737 }
738 }
739
742 pointer get_it_from_pointer(pointer ptr) noexcept
743 {
744 if (ptr < _gap_begin) {
745 return ptr;
746 } else {
747 return ptr - _gap_size;
748 }
749 }
750
756 const_pointer get_const_pointer_from_it(const_pointer it_ptr) const noexcept
757 {
758 hi_axiom(it_ptr >= _begin && it_ptr <= _it_end);
759
760 if (it_ptr < _gap_begin) {
761 return it_ptr;
762 } else {
763 return it_ptr + _gap_size;
764 }
765 }
766
767 const_pointer get_pointer_from_it(pointer it_ptr) const noexcept
768 {
769 return get_const_pointer_from_it(it_ptr);
770 }
771
772 pointer get_pointer_from_it(pointer it_ptr) noexcept
773 {
774 return const_cast<pointer>(get_const_pointer_from_it(it_ptr));
775 }
776
782 const_pointer get_const_pointer_from_index(size_type index) const noexcept
783 {
784 return get_const_pointer_from_it(_begin + index);
785 }
786
787 const_pointer get_pointer_from_index(size_type index) const noexcept
788 {
789 return get_const_pointer_from_it(_begin + index);
790 }
791
792 pointer get_pointer_from_index(size_type index) noexcept
793 {
794 return get_pointer_from_it(_begin + index);
795 }
796
797 [[nodiscard]] value_type const *left_begin_ptr() const noexcept
798 {
799 return _begin;
800 }
801
802 [[nodiscard]] value_type *left_begin_ptr() noexcept
803 {
804 return _begin;
805 }
806
807 [[nodiscard]] value_type const *left_end_ptr() const noexcept
808 {
809 return _gap_begin;
810 }
811
812 [[nodiscard]] value_type *left_end_ptr() noexcept
813 {
814 return _gap_begin;
815 }
816
817 [[nodiscard]] size_type left_size() const noexcept
818 {
819 return static_cast<size_type>(_gap_begin - _begin);
820 }
821
822 [[nodiscard]] value_type const *right_begin_ptr() const noexcept
823 {
824 return _gap_begin + _gap_size;
825 }
826
827 [[nodiscard]] value_type *right_begin_ptr() noexcept
828 {
829 return _gap_begin + _gap_size;
830 }
831
832 [[nodiscard]] value_type const *right_end_ptr() const noexcept
833 {
834 return _it_end + _gap_size;
835 }
836
837 [[nodiscard]] value_type *right_end_ptr() noexcept
838 {
839 return _it_end + _gap_size;
840 }
841
842 [[nodiscard]] size_type right_size() const noexcept
843 {
844 return _it_end - _gap_begin;
845 }
846
849 void set_gap_offset(value_type *new_gap_begin) noexcept
850 {
851 if (new_gap_begin < _gap_begin) {
852 // Move data left of the original gap to the end of the new gap.
853 // LLL...RRR
854 // LL...LRRR
855 placement_move_within_array(new_gap_begin, _gap_begin, new_gap_begin + _gap_size);
856
857 } else if (new_gap_begin > _gap_begin) {
858 // Move data right of the original gap to the beginning of the new gap.
859 // LLL...RRR
860 // LLLR...RR
861 placement_move_within_array(_gap_begin + _gap_size, new_gap_begin + _gap_size, _gap_begin);
862 }
863
864 _gap_begin = new_gap_begin;
865 hi_axiom(holds_invariant());
866 }
867
868 template<typename IT>
869 friend class gap_buffer_iterator;
870};
871
874template<typename T>
876public:
877 static_assert(
878 !std::is_volatile_v<T> && !std::is_reference_v<T>,
879 "Type of a managing container iterator can not be volatile nor a reference");
880
881 static constexpr bool is_const = std::is_const_v<T>;
882
883 using value_type = std::remove_cv_t<T>;
884 using size_type = std::size_t;
885 using difference_type = ptrdiff_t;
886 using pointer = value_type *;
887 using const_pointer = value_type const *;
888 using reference = value_type &;
889 using const_reference = value_type const &;
891
892 using gap_buffer_type = std::conditional_t<is_const, gap_buffer<value_type> const, gap_buffer<value_type>>;
893
898
899 ~gap_buffer_iterator() noexcept = default;
900 gap_buffer_iterator(gap_buffer_iterator const &) noexcept = default;
901 gap_buffer_iterator(gap_buffer_iterator &&) noexcept = default;
902 gap_buffer_iterator &operator=(gap_buffer_iterator const &) noexcept = default;
903 gap_buffer_iterator &operator=(gap_buffer_iterator &&) noexcept = default;
904
905 gap_buffer_iterator(gap_buffer_iterator<value_type> const &other) noexcept requires(is_const) :
906 _buffer(other._buffer), _it_ptr(other._it_ptr)
907 {
908#if HI_BUILT_TYPE == HI_BT_DEBUG
909 _version = other._version;
910#endif
911 hi_axiom(holds_invariant());
912 }
913
915 gap_buffer_type *buffer,
916 T *it_ptr
917#if HI_BUILT_TYPE == HI_BT_DEBUG
918 ,
919 std::size_t version
920#endif
921 ) noexcept :
922 _buffer(buffer),
923 _it_ptr(it_ptr)
924#if HI_BUILT_TYPE == HI_BT_DEBUG
925 ,
926 _version(version)
927#endif
928 {
929 hi_axiom(holds_invariant());
930 }
931
932 reference operator*() noexcept requires(!is_const)
933 {
934 return *(_buffer->get_pointer_from_it(_it_ptr));
935 }
936
937 const_reference operator*() const noexcept
938 {
939 return *(_buffer->get_const_pointer_from_it(_it_ptr));
940 }
941
942 pointer operator->() noexcept requires(!is_const)
943 {
944 return _buffer->get_pointer_from_it(_it_ptr);
945 }
946
947 const_pointer operator->() const noexcept
948 {
949 return _buffer->get_const_pointer_from_it(_it_ptr);
950 }
951
952 reference operator[](std::integral auto index) noexcept requires(!is_const)
953 {
954 return *(_buffer->get_pointer_from_it(_it_ptr + index));
955 }
956
957 const_reference operator[](std::integral auto index) const noexcept
958 {
959 return *(_buffer->get_const_pointer_from_it(_it_ptr + index));
960 }
961
962 gap_buffer_iterator &operator++() noexcept
963 {
964 ++_it_ptr;
965 hi_axiom(holds_invariant());
966 return *this;
967 }
968
969 gap_buffer_iterator operator++(int) noexcept
970 {
971 auto tmp = *this;
972 ++_it_ptr;
973 hi_axiom(holds_invariant());
974 return tmp;
975 }
976
977 gap_buffer_iterator &operator--() noexcept
978 {
979 --_it_ptr;
980 hi_axiom(holds_invariant());
981 return *this;
982 }
983
984 gap_buffer_iterator &operator--(int) noexcept
985 {
986 auto tmp = *this;
987 --_it_ptr;
988 hi_axiom(holds_invariant());
989 return tmp;
990 }
991
992 gap_buffer_iterator &operator+=(difference_type n) noexcept
993 {
994 _it_ptr += n;
995 hi_axiom(holds_invariant());
996 return *this;
997 }
998
999 gap_buffer_iterator &operator-=(difference_type n) noexcept
1000 {
1001 _it_ptr -= n;
1002 hi_axiom(holds_invariant());
1003 return *this;
1004 }
1005
1006 gap_buffer_iterator operator-(difference_type n) const noexcept
1007 {
1008 auto tmp = *this;
1009 return tmp -= n;
1010 }
1011
1012 friend gap_buffer_iterator operator+(gap_buffer_iterator lhs, difference_type rhs) noexcept
1013 {
1014 return lhs += rhs;
1015 }
1016
1017 friend gap_buffer_iterator operator+(difference_type lhs, gap_buffer_iterator rhs) noexcept
1018 {
1019 return rhs += lhs;
1020 }
1021
1022 template<typename R>
1023 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend difference_type
1024 operator-(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
1025 {
1026 return lhs._it_ptr - rhs._it_ptr;
1027 }
1028
1029 template<typename R>
1030 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend bool
1031 operator==(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
1032 {
1033 return lhs._it_ptr == rhs.it_ptr();
1034 }
1035
1036 template<typename R>
1037 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<R>>) friend auto
1038 operator<=>(gap_buffer_iterator const &lhs, gap_buffer_iterator<R> const &rhs) noexcept
1039 {
1040 return lhs._it_ptr <=> rhs.it_ptr();
1041 }
1042
1043private:
1044 gap_buffer_type *_buffer;
1045 T *_it_ptr;
1046#if HI_BUILT_TYPE == HI_BT_DEBUG
1047 std::size_t _version;
1048#endif
1049
1050 [[nodiscard]] gap_buffer_iterator(gap_buffer_iterator<value_type const> const &other) noexcept requires(!is_const) :
1051 _buffer(const_cast<gap_buffer_type *>(other._buffer)), _it_ptr(const_cast<T *>(other._it_ptr))
1052 {
1053#if HI_BUILT_TYPE == HI_BT_DEBUG
1054 _version = other._version;
1055#endif
1056 hi_axiom(holds_invariant());
1057 }
1058
1059 [[nodiscard]] T *it_ptr() const noexcept
1060 {
1061 return _it_ptr;
1062 }
1063
1064 [[nodiscard]] std::remove_cv_t<T> *it_rw_ptr() const noexcept
1065 {
1066 return const_cast<std::remove_cv_t<T> *>(_it_ptr);
1067 }
1068
1069 [[nodiscard]] bool holds_invariant() const noexcept
1070 {
1071 return _buffer and _it_ptr >= _buffer->_begin and _it_ptr <= _buffer->_it_end
1072#if HI_BUILT_TYPE == HI_BT_DEBUG
1073 and _version == _buffer->_version
1074#endif
1075 ;
1076 }
1077
1078 template<typename O>
1079 requires(std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<O>>)
1080 [[nodiscard]] bool holds_invariant(gap_buffer_iterator<O> const &other) const noexcept
1081 {
1082 return holds_invariant() and other.holds_invariant() and _buffer == other._buffer;
1083 }
1084};
1085
1086} // namespace hi::inline v1
This file includes required definitions.
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition required.hpp:37
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
Gap Buffer This container is similar to a std::vector, optimized for repeated insertions and deletion...
Definition gap_buffer.hpp:46
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:535
gap_buffer(gap_buffer &&other) noexcept
Move constructor.
Definition gap_buffer.hpp:151
void clear() noexcept
Clears the buffer.
Definition gap_buffer.hpp:342
const_reference at(size_type index) const
Get item to reference at.
Definition gap_buffer.hpp:291
iterator erase(const_iterator first, const_iterator last) noexcept
Erase items.
Definition gap_buffer.hpp:628
gap_buffer(allocator_type const &allocator=allocator_type{}) noexcept
Construct an empty buffer.
Definition gap_buffer.hpp:64
gap_buffer(gap_buffer const &other) noexcept
Copy constructor.
Definition gap_buffer.hpp:86
iterator insert_after(const_iterator position, It first, It last) noexcept
Insert items.
Definition gap_buffer.hpp:613
reference at(size_type index)
Get item to reference at.
Definition gap_buffer.hpp:277
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:589
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:505
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:525
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:568
reference operator[](size_type index) noexcept
Index operator.
Definition gap_buffer.hpp:253
iterator erase(const_iterator position) noexcept
Erase item.
Definition gap_buffer.hpp:652
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
gap_buffer & operator=(gap_buffer const &other) noexcept
Copy assignment.
Definition gap_buffer.hpp:109
~gap_buffer()
Destructor.
Definition gap_buffer.hpp:239
gap_buffer & operator=(gap_buffer &&other) noexcept
Move assignment operator.
Definition gap_buffer.hpp:170
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:551
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:599
const_reference operator[](size_type index) const noexcept
Index operator.
Definition gap_buffer.hpp:265
A continues iterator over a gap_buffer.
Definition gap_buffer.hpp:875
Definition concepts.hpp:35
Definition concepts.hpp:38
T ceil(T... args)
T equal(T... args)
T move(T... args)
T swap(T... args)