16namespace hi::inline
v1 {
33template<
typename T,
typename Allocator = std::allocator<T>>
37 !std::is_const_v<T> and !std::is_volatile_v<T> and !std::is_reference_v<T>,
38 "Type of a managing container can not be const, volatile nor a reference");
40 using allocator_type = Allocator;
42 using difference_type = ptrdiff_t;
44 using const_reference = T
const&;
46 using const_pointer = T
const *;
51 !std::is_volatile_v<T> && !std::is_reference_v<T>,
52 "Type of a managing container iterator can not be volatile nor a reference");
54 using value_type = gap_buffer::value_type;
56 using difference_type = ptrdiff_t;
58 using const_pointer = value_type
const *;
60 using const_reference = value_type
const&;
69 constexpr iterator(
gap_buffer *buffer, T *it_ptr) noexcept : _buffer(buffer), _it_ptr(it_ptr)
72 _version = buffer->_version;
77 [[nodiscard]]
constexpr reference operator*()
noexcept
79 return *(_buffer->get_pointer_from_it(_it_ptr));
82 [[nodiscard]]
constexpr const_reference operator*()
const noexcept
84 return *(_buffer->get_const_pointer_from_it(_it_ptr));
87 [[nodiscard]]
constexpr pointer operator->()
noexcept
89 return _buffer->get_pointer_from_it(_it_ptr);
92 [[nodiscard]]
constexpr const_pointer operator->()
const noexcept
94 return _buffer->get_const_pointer_from_it(_it_ptr);
97 [[nodiscard]]
constexpr reference operator[](std::integral
auto index)
noexcept
99 return *(_buffer->get_pointer_from_it(_it_ptr + index));
102 [[nodiscard]]
constexpr const_reference operator[](std::integral
auto index)
const noexcept
104 return *(_buffer->get_const_pointer_from_it(_it_ptr + index));
107 constexpr iterator& operator++()
noexcept
114 constexpr iterator operator++(
int)
noexcept
122 constexpr iterator& operator--()
noexcept
129 constexpr iterator& operator--(
int)
noexcept
137 constexpr iterator& operator+=(difference_type
n)
noexcept
144 constexpr iterator& operator-=(difference_type
n)
noexcept
151 [[nodiscard]]
constexpr friend iterator operator+(
iterator lhs, difference_type rhs)
noexcept
156 [[nodiscard]]
constexpr friend iterator operator-(
iterator lhs, difference_type rhs)
noexcept
161 [[nodiscard]]
constexpr friend difference_type operator-(
iterator const& lhs,
iterator const& rhs)
noexcept
163 return lhs._it_ptr - rhs._it_ptr;
166 [[nodiscard]]
constexpr friend bool operator==(
iterator const& lhs,
iterator const& rhs)
noexcept
168 return lhs._it_ptr == rhs._it_ptr;
171 [[nodiscard]]
constexpr friend auto operator<=>(
iterator const& lhs,
iterator const& rhs)
noexcept
173 return lhs._it_ptr <=> rhs._it_ptr;
183 [[nodiscard]]
constexpr bool holds_invariant()
const noexcept
185 return _buffer and _it_ptr >= _buffer->_begin and _it_ptr <= _buffer->_it_end
187 and _version == _buffer->_version
192 [[nodiscard]]
constexpr bool holds_invariant(
iterator const& other)
const noexcept
194 return holds_invariant() and other.holds_invariant() and _buffer == other._buffer;
204 !std::is_volatile_v<T> && !std::is_reference_v<T>,
205 "Type of a managing container iterator can not be volatile nor a reference");
207 using value_type = gap_buffer::value_type;
209 using difference_type = ptrdiff_t;
211 using const_pointer = value_type
const *;
213 using const_reference = value_type
const&;
225 _version = other._version;
232 _buffer = other._buffer;
233 _it_ptr = other._it_ptr;
235 _version = other._version;
241 constexpr const_iterator(
gap_buffer const *buffer, value_type
const *it_ptr) noexcept : _buffer(buffer), _it_ptr(it_ptr)
244 _version = buffer->_version;
249 [[nodiscard]]
constexpr const_reference operator*()
const noexcept
251 return *(_buffer->get_const_pointer_from_it(_it_ptr));
254 [[nodiscard]]
constexpr const_pointer operator->()
const noexcept
256 return _buffer->get_const_pointer_from_it(_it_ptr);
259 [[nodiscard]]
constexpr const_reference operator[](std::integral
auto index)
const noexcept
261 return *(_buffer->get_const_pointer_from_it(_it_ptr + index));
320 return lhs._it_ptr - rhs._it_ptr;
325 return lhs._it_ptr == rhs._it_ptr;
330 return lhs._it_ptr <=> rhs._it_ptr;
340 [[nodiscard]]
constexpr bool holds_invariant()
const noexcept
342 return _buffer and _it_ptr >= _buffer->_begin and _it_ptr <= _buffer->_it_end
344 and _version == _buffer->_version
349 [[nodiscard]]
constexpr bool holds_invariant(
const_iterator const& other)
const noexcept
351 return holds_invariant() and other.holds_invariant() and _buffer == other._buffer;
359 constexpr gap_buffer(allocator_type
const& allocator = allocator_type{})
noexcept :
360 _begin(nullptr), _it_end(nullptr), _gap_begin(nullptr), _gap_size(0), _allocator(allocator)
368 _begin(_allocator.allocate(init.size() + _grow_size)),
369 _it_end(_begin + init.size()),
370 _gap_begin(_begin + init.size()),
371 _gap_size(_grow_size),
372 _allocator(allocator)
382 _begin(
nullptr), _it_end(
nullptr), _gap_begin(
nullptr), _gap_size(0), _allocator(other._allocator)
386 if (other._ptr !=
nullptr) {
387 _begin = _allocator.allocate(other.capacity());
388 _it_end = _begin + other.size();
389 _gap_begin = _begin + other.left_size();
390 _gap_size = other._gap_size;
392 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
393 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
406 hi_return_on_self_assignment(other);
409 if (_gap_size >= other.size()) {
411 _gap_begin = _begin + other.left_size();
412 _gap_size = capacity() - other.size();
414 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
415 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
419 if (_begin !=
nullptr) {
420 _allocator.deallocate(_begin, capacity());
423 _gap_begin =
nullptr;
428 if (other._begin !=
nullptr) {
429 hilet new_capacity = other.size() + _grow_size;
431 _begin = _allocator.allocate(new_capacity);
432 _it_end = _begin + other.size();
433 _gap_begin = _begin + other.left_begin_ptr();
434 _gap_size = new_capacity - other.size();
436 placement_copy(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
437 placement_copy(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
447 _begin(other._begin),
448 _it_end(other._it_end),
449 _gap_begin(other._gap_begin),
450 _gap_size(other._gap_size),
451 _allocator(other._allocator)
455 other._begin =
nullptr;
456 other._it_end =
nullptr;
457 other._gap_begin =
nullptr;
467 hi_return_on_self_assignment(other);
472 if (_allocator == other._allocator) {
481 }
else if (capacity() >= other.size()) {
483 _it_end = _begin + other.size();
484 _gap_begin = _begin + other.left_size();
485 _gap_size = capacity() - other.size();
487 placement_move(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
488 placement_move(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
491 hilet other_capacity = other.capacity();
492 other._it_end = other._begin;
493 other._gap_begin = other._begin;
494 other._gap_size = other_capacity;
500 if (_begin !=
nullptr) {
501 _allocator.deallocate(_begin, capacity());
504 _gap_begin =
nullptr;
509 if (other._begin !=
nullptr) {
510 hilet new_capacity = other.size() + _grow_size;
512 _begin = _allocator.allocate(new_capacity);
513 _it_end = _begin + other.size();
514 _gap_begin = _begin + other.left_size();
515 _gap_size = new_capacity - other.size();
517 placement_move(other.left_begin_ptr(), other.left_end_ptr(), left_begin_ptr());
518 placement_move(other.right_begin_ptr(), other.right_end_ptr(), right_begin_ptr());
522 hilet other_capacity = other.capacity();
523 other._it_end = other._begin;
524 other._gap_begin = other._begin;
525 other._gap_size = other_capacity;
537 if (_begin !=
nullptr) {
538 _allocator.deallocate(_begin, capacity());
550 hi_assert_bounds(index, *
this);
551 return *get_pointer_from_index(index);
562 hi_assert_bounds(index, *
this);
563 return *get_pointer_from_index(index);
574 if (index < size()) {
577 return *get_pointer_from_index(index);
588 if (index < size()) {
591 return *get_pointer_from_index(index);
594 [[nodiscard]]
constexpr reference front() noexcept
597 return *get_pointer_from_it_ptr(_begin);
600 [[nodiscard]]
constexpr const_reference front() const noexcept
603 return *get_pointer_from_it_ptr(_begin);
606 [[nodiscard]]
constexpr reference back() noexcept
609 return *get_pointer_from_it_ptr(_it_end - 1);
612 [[nodiscard]]
constexpr const_reference back() const noexcept
615 return *get_pointer_from_it_ptr(_it_end - 1);
618 constexpr void pop_back() noexcept
624 constexpr void pop_front() noexcept
640 std::destroy(left_begin_ptr(), left_end_ptr());
641 std::destroy(right_begin_ptr(), right_end_ptr());
642 hilet this_capacity = capacity();
645 _gap_size = this_capacity;
650 [[nodiscard]]
constexpr std::size_t size() const noexcept
655 [[nodiscard]]
constexpr bool empty() const noexcept
660 [[nodiscard]]
constexpr std::size_t capacity() const noexcept
662 return size() + _gap_size;
665 constexpr void reserve(
std::size_t new_capacity)
noexcept
667 hilet extra_capacity =
static_cast<ssize_t>(new_capacity) - capacity();
668 if (extra_capacity <= 0) {
675 hilet new_begin = _allocator.allocate(new_capacity);
676 hilet new_it_end = new_begin + size();
677 hilet new_gap_begin = new_begin + left_size();
678 hilet new_gap_size = new_capacity - size();
680 if (_begin !=
nullptr) {
682 placement_move(right_begin_ptr(), right_end_ptr(), new_gap_begin + new_gap_size);
683 _allocator.deallocate(_begin, capacity());
687 _it_end = new_it_end;
688 _gap_begin = new_gap_begin;
689 _gap_size = new_gap_size;
693 [[nodiscard]]
constexpr iterator
begin() noexcept
695 return iterator{
this, _begin};
698 [[nodiscard]]
constexpr const_iterator
begin() const noexcept
700 return const_iterator{
this, _begin};
703 [[nodiscard]]
constexpr const_iterator cbegin() const noexcept
705 return const_iterator{
this, _begin};
708 [[nodiscard]]
constexpr iterator
end() noexcept
710 return iterator{
this, _it_end};
713 [[nodiscard]]
constexpr const_iterator
end() const noexcept
715 return const_iterator{
this, _it_end};
718 [[nodiscard]]
constexpr const_iterator cend() const noexcept
720 return const_iterator{
this, _it_end};
723 [[nodiscard]]
constexpr friend iterator
begin(gap_buffer& rhs)
noexcept
728 [[nodiscard]]
constexpr friend const_iterator
begin(gap_buffer
const& rhs)
noexcept
733 [[nodiscard]]
constexpr friend const_iterator cbegin(gap_buffer
const& rhs)
noexcept
738 [[nodiscard]]
constexpr friend iterator
end(gap_buffer& rhs)
noexcept
743 [[nodiscard]]
constexpr friend const_iterator
end(gap_buffer
const& rhs)
noexcept
748 [[nodiscard]]
constexpr friend const_iterator cend(gap_buffer
const& rhs)
noexcept
753 template<
typename... Args>
754 constexpr reference emplace_back(Args&&...args)
noexcept
756 return emplace_after(
end(), std::forward<Args>(args)...);
759 constexpr void push_back(value_type
const& value)
noexcept
764 constexpr void push_back(value_type&& value)
noexcept
769 template<
typename... Args>
770 constexpr reference emplace_front(Args&&...args)
noexcept
772 set_gap_offset(_begin);
775 auto ptr =
new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
785 constexpr void push_front(value_type
const& value)
noexcept
787 emplace_front(value);
790 constexpr void push_front(value_type&& value)
noexcept
799 template<
typename... Args>
803 set_gap_offset(
const_cast<value_type *
>(position._it_ptr));
806 auto ptr =
new (right_begin_ptr() - 1) value_type(std::forward<Args>(args)...);
822 auto ptr = &emplace_before(position, value_type(value));
823 return iterator{
this, get_it_from_pointer(ptr)};
832 auto ptr = &emplace_before(position,
std::move(value));
833 return iterator{
this, get_it_from_pointer(ptr)};
845 template<
typename It>
851 while (it != first) {
852 position = insert_before(position, *(--it));
862 template<
typename... Args>
866 set_gap_offset(
const_cast<value_type *
>(position._it_ptr));
869 auto ptr =
new (left_end_ptr()) value_type(std::forward<Args>(args)...);
886 auto ptr = &emplace_after(position, value_type(value));
887 return iterator{
this, get_it_from_pointer(ptr)};
896 auto ptr = &emplace_after(position,
std::move(value));
897 return iterator{
this, get_it_from_pointer(ptr)};
907 template<
typename It>
910 auto position_ =
iterator{
this,
const_cast<value_type *
>(position._it_ptr)};
911 for (
auto it = first; it != last; ++it) {
912 position_ = insert_after(position_, *it) + 1;
930 set_gap_offset(
const_cast<value_type *
>(last._it_ptr));
931 auto first_p =
const_cast<value_type *
>(first._it_ptr);
932 auto last_p =
const_cast<value_type *
>(last._it_ptr);
933 hilet erase_size = last_p - first_p;
935 std::destroy(first_p, last_p);
936 _gap_begin = first_p;
937 _gap_size += erase_size;
938 _it_end -= erase_size;
949 return erase(position, position + 1);
952 [[nodiscard]]
constexpr friend bool operator==(
gap_buffer const& lhs,
gap_buffer const& rhs)
noexcept
954 if (lhs.size() != rhs.size()) {
957 return std::equal(lhs.begin(), lhs.end(), rhs.begin());
961 template<
typename Container>
962 [[nodiscard]]
constexpr friend bool operator==(gap_buffer
const& lhs, Container
const& rhs)
noexcept
967 if (lhs.size() != size(rhs)) {
974 template<
typename Container>
975 [[nodiscard]]
constexpr friend bool operator==(Container
const& lhs, gap_buffer
const& rhs)
noexcept
982 static constexpr difference_type _grow_size = 256;
995 value_type *_gap_begin;
1005 [[no_unique_address]] allocator_type _allocator;
1007 [[nodiscard]]
constexpr bool holds_invariant() const noexcept
1009 return (_begin ==
nullptr and _it_end ==
nullptr and _gap_begin ==
nullptr and _gap_size == 0) or
1010 (_begin <= _gap_begin and _gap_begin <= _it_end);
1015 constexpr void grow_to_insert(size_type n)
noexcept
1017 if (n > _gap_size) [[unlikely]] {
1018 auto new_capacity = size() +
n + narrow_cast<size_type>(_grow_size);
1019 reserve(
ceil(new_capacity, hardware_constructive_interference_size));
1026 [[nodiscard]]
constexpr const_pointer get_it_from_pointer(const_pointer ptr)
const noexcept
1028 if (ptr < _gap_begin) {
1031 return ptr - _gap_size;
1037 [[nodiscard]]
constexpr pointer get_it_from_pointer(pointer ptr)
noexcept
1039 if (ptr < _gap_begin) {
1042 return ptr - _gap_size;
1051 [[nodiscard]]
constexpr const_pointer get_const_pointer_from_it(const_pointer it_ptr)
const noexcept
1053 hi_axiom(it_ptr >= _begin && it_ptr <= _it_end);
1055 if (it_ptr < _gap_begin) {
1058 return it_ptr + _gap_size;
1062 [[nodiscard]]
constexpr const_pointer get_pointer_from_it(pointer it_ptr)
const noexcept
1064 return get_const_pointer_from_it(it_ptr);
1067 [[nodiscard]]
constexpr pointer get_pointer_from_it(pointer it_ptr)
noexcept
1069 return const_cast<pointer
>(get_const_pointer_from_it(it_ptr));
1077 [[nodiscard]]
constexpr const_pointer get_const_pointer_from_index(size_type index)
const noexcept
1079 return get_const_pointer_from_it(_begin + index);
1082 [[nodiscard]]
constexpr const_pointer get_pointer_from_index(size_type index)
const noexcept
1084 return get_const_pointer_from_it(_begin + index);
1087 [[nodiscard]]
constexpr pointer get_pointer_from_index(size_type index)
noexcept
1089 return get_pointer_from_it(_begin + index);
1092 [[nodiscard]]
constexpr value_type
const *left_begin_ptr() const noexcept
1097 [[nodiscard]]
constexpr value_type *left_begin_ptr() noexcept
1102 [[nodiscard]]
constexpr value_type
const *left_end_ptr() const noexcept
1107 [[nodiscard]]
constexpr value_type *left_end_ptr() noexcept
1112 [[nodiscard]]
constexpr size_type left_size() const noexcept
1114 return static_cast<size_type
>(_gap_begin - _begin);
1117 [[nodiscard]]
constexpr value_type
const *right_begin_ptr() const noexcept
1119 return _gap_begin + _gap_size;
1122 [[nodiscard]]
constexpr value_type *right_begin_ptr() noexcept
1124 return _gap_begin + _gap_size;
1127 [[nodiscard]]
constexpr value_type
const *right_end_ptr() const noexcept
1129 return _it_end + _gap_size;
1132 [[nodiscard]]
constexpr value_type *right_end_ptr() noexcept
1134 return _it_end + _gap_size;
1137 [[nodiscard]]
constexpr size_type right_size() const noexcept
1139 return _it_end - _gap_begin;
1144 constexpr void set_gap_offset(value_type *new_gap_begin)
noexcept
1146 if (new_gap_begin < _gap_begin) {
1152 }
else if (new_gap_begin > _gap_begin) {
1159 _gap_begin = new_gap_begin;
Utilities to assert and bound check.
#define hi_axiom(expression)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hi_assert(expression)
Assert if expression is true.
Definition assert.hpp:86
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
T * placement_copy(InputIt src, T *dst)
Copy an object to another memory locations.
Definition memory.hpp:52
void placement_move_within_array(T *src_first, T *src_last, T *dst_first)
Move an objects between two memory locations.
Definition memory.hpp:109
T * placement_move(T *src, T *dst)
Move an object between two memory locations.
Definition memory.hpp:89
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition utility.hpp:173
Gap Buffer This container is similar to a std::vector, optimized for repeated insertions and deletion...
Definition gap_buffer.hpp:34
constexpr gap_buffer & operator=(gap_buffer const &other) noexcept
Copy assignment.
Definition gap_buffer.hpp:404
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:830
constexpr gap_buffer & operator=(gap_buffer &&other) noexcept
Move assignment operator.
Definition gap_buffer.hpp:465
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:863
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:884
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:800
constexpr reference at(size_type index)
Get item to reference at.
Definition gap_buffer.hpp:572
constexpr iterator erase(const_iterator position) noexcept
Erase item.
Definition gap_buffer.hpp:947
constexpr const_reference at(size_type index) const
Get item to reference at.
Definition gap_buffer.hpp:586
constexpr gap_buffer(gap_buffer const &other) noexcept
Copy constructor.
Definition gap_buffer.hpp:381
constexpr void clear() noexcept
Clears the buffer.
Definition gap_buffer.hpp:637
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:820
constexpr iterator insert_after(const_iterator position, It first, It last) noexcept
Insert items.
Definition gap_buffer.hpp:908
constexpr const_reference operator[](size_type index) const noexcept
Index operator.
Definition gap_buffer.hpp:560
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:367
constexpr iterator erase(const_iterator first, const_iterator last) noexcept
Erase items.
Definition gap_buffer.hpp:923
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:894
constexpr gap_buffer(gap_buffer &&other) noexcept
Move constructor.
Definition gap_buffer.hpp:446
constexpr ~gap_buffer()
Destructor.
Definition gap_buffer.hpp:534
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:846
constexpr reference operator[](size_type index) noexcept
Index operator.
Definition gap_buffer.hpp:548
constexpr gap_buffer(allocator_type const &allocator=allocator_type{}) noexcept
Construct an empty buffer.
Definition gap_buffer.hpp:359
Definition gap_buffer.hpp:48
Definition gap_buffer.hpp:201
Definition concepts.hpp:36
Definition concepts.hpp:39