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