7#include "utility/module.hpp"
13namespace hi::inline
v1 {
30template<
typename T,
typename Allocator = std::allocator<T>>
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");
37 using allocator_type = Allocator;
39 using difference_type = ptrdiff_t;
41 using const_reference = T
const&;
43 using const_pointer = T
const *;
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");
51 using value_type = gap_buffer::value_type;
53 using difference_type = ptrdiff_t;
55 using const_pointer = value_type
const *;
57 using const_reference = value_type
const&;
66 constexpr iterator(
gap_buffer *buffer, T *it_ptr) noexcept : _buffer(buffer), _it_ptr(it_ptr)
69 _version = buffer->_version;
74 [[nodiscard]]
constexpr reference operator*()
noexcept
76 return *(_buffer->get_pointer_from_it(_it_ptr));
79 [[nodiscard]]
constexpr const_reference operator*()
const noexcept
81 return *(_buffer->get_const_pointer_from_it(_it_ptr));
84 [[nodiscard]]
constexpr pointer operator->()
noexcept
86 return _buffer->get_pointer_from_it(_it_ptr);
89 [[nodiscard]]
constexpr const_pointer operator->()
const noexcept
91 return _buffer->get_const_pointer_from_it(_it_ptr);
94 [[nodiscard]]
constexpr reference operator[](std::integral
auto index)
noexcept
96 return *(_buffer->get_pointer_from_it(_it_ptr + index));
99 [[nodiscard]]
constexpr const_reference operator[](std::integral
auto index)
const noexcept
101 return *(_buffer->get_const_pointer_from_it(_it_ptr + index));
104 constexpr iterator& operator++()
noexcept
111 constexpr iterator operator++(
int)
noexcept
119 constexpr iterator& operator--()
noexcept
126 constexpr iterator& operator--(
int)
noexcept
134 constexpr iterator& operator+=(difference_type n)
noexcept
141 constexpr iterator& operator-=(difference_type n)
noexcept
148 [[nodiscard]]
constexpr friend iterator operator+(
iterator lhs, difference_type rhs)
noexcept
153 [[nodiscard]]
constexpr friend iterator operator-(
iterator lhs, difference_type rhs)
noexcept
158 [[nodiscard]]
constexpr friend difference_type operator-(
iterator const& lhs,
iterator const& rhs)
noexcept
160 return lhs._it_ptr - rhs._it_ptr;
163 [[nodiscard]]
constexpr friend bool operator==(
iterator const& lhs,
iterator const& rhs)
noexcept
165 return lhs._it_ptr == rhs._it_ptr;
168 [[nodiscard]]
constexpr friend auto operator<=>(
iterator const& lhs,
iterator const& rhs)
noexcept
170 return lhs._it_ptr <=> rhs._it_ptr;
180 [[nodiscard]]
constexpr bool holds_invariant()
const noexcept
182 return _buffer and _it_ptr >= _buffer->_begin and _it_ptr <= _buffer->_it_end
184 and _version == _buffer->_version
189 [[nodiscard]]
constexpr bool holds_invariant(
iterator const& other)
const noexcept
191 return holds_invariant() and other.holds_invariant() and _buffer == other._buffer;
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");
204 using value_type = gap_buffer::value_type;
206 using difference_type = ptrdiff_t;
208 using const_pointer = value_type
const *;
210 using const_reference = value_type
const&;
222 _version = other._version;
229 _buffer = other._buffer;
230 _it_ptr = other._it_ptr;
232 _version = other._version;
238 constexpr const_iterator(
gap_buffer const *buffer, value_type
const *it_ptr) noexcept : _buffer(buffer), _it_ptr(it_ptr)
241 _version = buffer->_version;
246 [[nodiscard]]
constexpr const_reference operator*()
const noexcept
248 return *(_buffer->get_const_pointer_from_it(_it_ptr));
251 [[nodiscard]]
constexpr const_pointer operator->()
const noexcept
253 return _buffer->get_const_pointer_from_it(_it_ptr);
256 [[nodiscard]]
constexpr const_reference operator[](std::integral
auto index)
const noexcept
258 return *(_buffer->get_const_pointer_from_it(_it_ptr + index));
317 return lhs._it_ptr - rhs._it_ptr;
322 return lhs._it_ptr == rhs._it_ptr;
327 return lhs._it_ptr <=> rhs._it_ptr;
337 [[nodiscard]]
constexpr bool holds_invariant()
const noexcept
339 return _buffer and _it_ptr >= _buffer->_begin and _it_ptr <= _buffer->_it_end
341 and _version == _buffer->_version
346 [[nodiscard]]
constexpr bool holds_invariant(
const_iterator const& other)
const noexcept
348 return holds_invariant() and other.holds_invariant() and _buffer == other._buffer;
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)
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)
379 _begin(
nullptr), _it_end(
nullptr), _gap_begin(
nullptr), _gap_size(0), _allocator(other._allocator)
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;
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());
403 hi_return_on_self_assignment(other);
406 if (_gap_size >= other.size()) {
408 _gap_begin = _begin + other.left_size();
409 _gap_size = capacity() - other.size();
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());
416 if (_begin !=
nullptr) {
417 _allocator.deallocate(_begin, capacity());
420 _gap_begin =
nullptr;
425 if (other._begin !=
nullptr) {
426 hilet new_capacity = other.size() + _grow_size;
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();
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());
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)
452 other._begin =
nullptr;
453 other._it_end =
nullptr;
454 other._gap_begin =
nullptr;
464 hi_return_on_self_assignment(other);
469 if (_allocator == other._allocator) {
478 }
else if (capacity() >= other.size()) {
480 _it_end = _begin + other.size();
481 _gap_begin = _begin + other.left_size();
482 _gap_size = capacity() - other.size();
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());
488 hilet other_capacity = other.capacity();
489 other._it_end = other._begin;
490 other._gap_begin = other._begin;
491 other._gap_size = other_capacity;
497 if (_begin !=
nullptr) {
498 _allocator.deallocate(_begin, capacity());
501 _gap_begin =
nullptr;
506 if (other._begin !=
nullptr) {
507 hilet new_capacity = other.size() + _grow_size;
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();
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());
519 hilet other_capacity = other.capacity();
520 other._it_end = other._begin;
521 other._gap_begin = other._begin;
522 other._gap_size = other_capacity;
534 if (_begin !=
nullptr) {
535 _allocator.deallocate(_begin, capacity());
548 return *get_pointer_from_index(index);
560 return *get_pointer_from_index(index);
571 if (index < size()) {
574 return *get_pointer_from_index(index);
585 if (index < size()) {
588 return *get_pointer_from_index(index);
591 [[nodiscard]]
constexpr reference front() noexcept
594 return *get_pointer_from_it_ptr(_begin);
597 [[nodiscard]]
constexpr const_reference front() const noexcept
600 return *get_pointer_from_it_ptr(_begin);
603 [[nodiscard]]
constexpr reference back() noexcept
606 return *get_pointer_from_it_ptr(_it_end - 1);
609 [[nodiscard]]
constexpr const_reference back() const noexcept
612 return *get_pointer_from_it_ptr(_it_end - 1);
615 constexpr void pop_back() noexcept
621 constexpr void pop_front() noexcept
637 std::destroy(left_begin_ptr(), left_end_ptr());
638 std::destroy(right_begin_ptr(), right_end_ptr());
639 hilet this_capacity = capacity();
642 _gap_size = this_capacity;
647 [[nodiscard]]
constexpr std::size_t size() const noexcept
649 return narrow_cast<std::size_t>(_it_end - _begin);
652 [[nodiscard]]
constexpr bool empty() const noexcept
657 [[nodiscard]]
constexpr std::size_t capacity() const noexcept
659 return size() + _gap_size;
662 constexpr void reserve(
std::size_t new_capacity)
noexcept
664 hilet extra_capacity = narrow_cast<ptrdiff_t>(new_capacity) - capacity();
665 if (extra_capacity <= 0) {
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();
677 if (_begin !=
nullptr) {
679 placement_move(right_begin_ptr(), right_end_ptr(), new_gap_begin + new_gap_size);
680 _allocator.deallocate(_begin, capacity());
684 _it_end = new_it_end;
685 _gap_begin = new_gap_begin;
686 _gap_size = new_gap_size;
690 [[nodiscard]]
constexpr iterator
begin() noexcept
692 return iterator{
this, _begin};
695 [[nodiscard]]
constexpr const_iterator
begin() const noexcept
697 return const_iterator{
this, _begin};
700 [[nodiscard]]
constexpr const_iterator cbegin() const noexcept
702 return const_iterator{
this, _begin};
705 [[nodiscard]]
constexpr iterator
end() noexcept
707 return iterator{
this, _it_end};
710 [[nodiscard]]
constexpr const_iterator
end() const noexcept
712 return const_iterator{
this, _it_end};
715 [[nodiscard]]
constexpr const_iterator cend() const noexcept
717 return const_iterator{
this, _it_end};
720 [[nodiscard]]
constexpr friend iterator
begin(gap_buffer& rhs)
noexcept
725 [[nodiscard]]
constexpr friend const_iterator
begin(gap_buffer
const& rhs)
noexcept
730 [[nodiscard]]
constexpr friend const_iterator cbegin(gap_buffer
const& rhs)
noexcept
735 [[nodiscard]]
constexpr friend iterator
end(gap_buffer& rhs)
noexcept
740 [[nodiscard]]
constexpr friend const_iterator
end(gap_buffer
const& rhs)
noexcept
745 [[nodiscard]]
constexpr friend const_iterator cend(gap_buffer
const& rhs)
noexcept
750 template<
typename... Args>
751 constexpr reference emplace_back(Args&&...args)
noexcept
753 return emplace_after(
end(), std::forward<Args>(args)...);
756 constexpr void push_back(value_type
const& value)
noexcept
761 constexpr void push_back(value_type&& value)
noexcept
766 template<
typename... Args>
767 constexpr reference emplace_front(Args&&...args)
noexcept
769 set_gap_offset(_begin);
772 auto ptr =
new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
782 constexpr void push_front(value_type
const& value)
noexcept
784 emplace_front(value);
787 constexpr void push_front(value_type&& value)
noexcept
796 template<
typename... Args>
800 set_gap_offset(
const_cast<value_type *
>(position._it_ptr));
803 auto ptr =
new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
819 auto ptr = &emplace_before(position, value_type(value));
820 return iterator{
this, get_it_from_pointer(ptr)};
829 auto ptr = &emplace_before(position,
std::move(value));
830 return iterator{
this, get_it_from_pointer(ptr)};
842 template<
typename It>
848 while (it != first) {
849 position = insert_before(position, *(--it));
859 template<
typename... Args>
863 set_gap_offset(
const_cast<value_type *
>(position._it_ptr));
866 auto ptr =
new (left_end_ptr()) value_type(std::forward<Args>(args)...);
883 auto ptr = &emplace_after(position, value_type(value));
884 return iterator{
this, get_it_from_pointer(ptr)};
893 auto ptr = &emplace_after(position,
std::move(value));
894 return iterator{
this, get_it_from_pointer(ptr)};
904 template<
typename It>
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;
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;
932 std::destroy(first_p, last_p);
933 _gap_begin = first_p;
934 _gap_size += erase_size;
935 _it_end -= erase_size;
946 return erase(position, position + 1);
949 [[nodiscard]]
constexpr friend bool operator==(
gap_buffer const& lhs,
gap_buffer const& rhs)
noexcept
951 if (lhs.size() != rhs.size()) {
954 return std::equal(lhs.begin(), lhs.end(), rhs.begin());
958 template<
typename Container>
959 [[nodiscard]]
constexpr friend bool operator==(gap_buffer
const& lhs, Container
const& rhs)
noexcept
964 if (lhs.size() != size(rhs)) {
971 template<
typename Container>
972 [[nodiscard]]
constexpr friend bool operator==(Container
const& lhs, gap_buffer
const& rhs)
noexcept
979 static constexpr difference_type _grow_size = 256;
992 value_type *_gap_begin;
1002 [[no_unique_address]] allocator_type _allocator;
1004 [[nodiscard]]
constexpr bool holds_invariant() const noexcept
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);
1012 constexpr void grow_to_insert(size_type n)
noexcept
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));
1023 [[nodiscard]]
constexpr const_pointer get_it_from_pointer(const_pointer ptr)
const noexcept
1025 if (ptr < _gap_begin) {
1028 return ptr - _gap_size;
1034 [[nodiscard]]
constexpr pointer get_it_from_pointer(pointer ptr)
noexcept
1036 if (ptr < _gap_begin) {
1039 return ptr - _gap_size;
1048 [[nodiscard]]
constexpr const_pointer get_const_pointer_from_it(const_pointer it_ptr)
const noexcept
1050 hi_axiom(it_ptr >= _begin && it_ptr <= _it_end);
1052 if (it_ptr < _gap_begin) {
1055 return it_ptr + _gap_size;
1059 [[nodiscard]]
constexpr const_pointer get_pointer_from_it(pointer it_ptr)
const noexcept
1061 return get_const_pointer_from_it(it_ptr);
1064 [[nodiscard]]
constexpr pointer get_pointer_from_it(pointer it_ptr)
noexcept
1066 return const_cast<pointer
>(get_const_pointer_from_it(it_ptr));
1074 [[nodiscard]]
constexpr const_pointer get_const_pointer_from_index(size_type index)
const noexcept
1076 return get_const_pointer_from_it(_begin + index);
1079 [[nodiscard]]
constexpr const_pointer get_pointer_from_index(size_type index)
const noexcept
1081 return get_const_pointer_from_it(_begin + index);
1084 [[nodiscard]]
constexpr pointer get_pointer_from_index(size_type index)
noexcept
1086 return get_pointer_from_it(_begin + index);
1089 [[nodiscard]]
constexpr value_type
const *left_begin_ptr() const noexcept
1094 [[nodiscard]]
constexpr value_type *left_begin_ptr() noexcept
1099 [[nodiscard]]
constexpr value_type
const *left_end_ptr() const noexcept
1104 [[nodiscard]]
constexpr value_type *left_end_ptr() noexcept
1109 [[nodiscard]]
constexpr size_type left_size() const noexcept
1111 return narrow_cast<size_type>(_gap_begin - _begin);
1114 [[nodiscard]]
constexpr value_type
const *right_begin_ptr() const noexcept
1116 return _gap_begin + _gap_size;
1119 [[nodiscard]]
constexpr value_type *right_begin_ptr() noexcept
1121 return _gap_begin + _gap_size;
1124 [[nodiscard]]
constexpr value_type
const *right_end_ptr() const noexcept
1126 return _it_end + _gap_size;
1129 [[nodiscard]]
constexpr value_type *right_end_ptr() noexcept
1131 return _it_end + _gap_size;
1134 [[nodiscard]]
constexpr size_type right_size() const noexcept
1136 return _it_end - _gap_begin;
1141 constexpr void set_gap_offset(value_type *new_gap_begin)
noexcept
1143 if (new_gap_begin < _gap_begin) {
1149 }
else if (new_gap_begin > _gap_begin) {
1156 _gap_begin = new_gap_begin;
#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