15#ifndef RPNX_SEGMENTED_DYNAR_HPP
16#define RPNX_SEGMENTED_DYNAR_HPP
43 template <
typename T,
typename Alloc = std::allocator< T > >
48 std::size_t m_size = 0;
49 std::size_t m_capacity = 0;
50 T** m_segments =
nullptr;
51 [[no_unique_address]] Alloc m_alloc;
53 static constexpr std::size_t index_segment(std::size_t index)
noexcept
55 if (index == 0 || index == 1)
60 return std::bit_width(index) - 1;
63 static constexpr std::size_t index_subindex(std::size_t index)
noexcept
65 if (index == 0 || index == 1)
70 std::size_t segment = index_segment(index);
71 std::size_t base_index = std::size_t{1} << segment;
72 return index - base_index;
75 static constexpr std::size_t capacity_segment_count(std::size_t
capacity)
noexcept
88 static_assert(capacity_segment_count(1) == 1);
90 static constexpr std::size_t segment_count_total_capacity(std::size_t segment_count)
noexcept
92 if (segment_count == 0)
96 return (std::size_t{1} << segment_count);
99 static constexpr std::size_t segment_size(std::size_t segment_index)
101 if (segment_index == 0 || segment_index == 1)
105 return (std::size_t{1} << segment_index);
108 static_assert(segment_size(0) == 2);
109 static_assert(segment_size(1) == 2);
110 static_assert(segment_size(2) == 4);
112 static_assert(capacity_segment_count(0) == 0);
113 static_assert(segment_count_total_capacity(0) == 0);
114 static_assert(segment_size(0) == 2);
116 static_assert(capacity_segment_count(2) == 1);
117 static_assert(segment_count_total_capacity(1) == 2);
118 static_assert(segment_size(1) == 2);
120 static_assert(capacity_segment_count(4) == 2);
121 static_assert(segment_count_total_capacity(2) == 4);
122 static_assert(segment_size(2) == 4);
124 static_assert(capacity_segment_count(8) == 3);
125 static_assert(segment_count_total_capacity(3) == 8);
126 static_assert(segment_size(3) == 8);
128 static_assert(capacity_segment_count(16) == 4);
129 static_assert(segment_count_total_capacity(4) == 16);
130 static_assert(segment_size(4) == 16);
132 static_assert(index_segment(0) == 0);
133 static_assert(index_segment(1) == 0);
134 static_assert(index_segment(2) == 1);
135 static_assert(index_segment(3) == 1);
136 static_assert(index_segment(4) == 2);
137 static_assert(index_segment(5) == 2);
138 static_assert(index_segment(6) == 2);
139 static_assert(index_segment(7) == 2);
140 static_assert(index_segment(8) == 3);
141 static_assert(index_segment(9) == 3);
142 static_assert(index_segment(15) == 3);
143 static_assert(index_segment(16) == 4);
145 static_assert(index_subindex(0) == 0);
146 static_assert(index_subindex(1) == 1);
147 static_assert(index_subindex(2) == 0);
148 static_assert(index_subindex(3) == 1);
149 static_assert(index_subindex(4) == 0);
150 static_assert(index_subindex(5) == 1);
188 using segment_allocator_type =
typename std::allocator_traits< Alloc >::template rebind_alloc< T* >;
189 using segment_alloc_traits = std::allocator_traits< segment_allocator_type >;
190 if (new_capacity <= m_capacity)
194 if (new_capacity > (std::size_t{1} << (std::numeric_limits< std::size_t >::digits - 1)))
196 throw std::length_error(
"segmented_dynar::reserve: requested capacity is too large");
198 std::size_t old_segment_count = capacity_segment_count(m_capacity);
199 std::size_t new_segment_count = capacity_segment_count(new_capacity);
200 segment_allocator_type typed_allocator(m_alloc);
201 T** new_segments = segment_alloc_traits::allocate(typed_allocator, new_segment_count);
203 using element_allocator_type =
typename std::allocator_traits< Alloc >::template rebind_alloc< T >;
204 using element_alloc_traits = std::allocator_traits< element_allocator_type >;
205 element_allocator_type elem_alloc(m_alloc);
208 for (std::size_t i = 0; i < old_segment_count; ++i)
210 new_segments[i] = m_segments[i];
212 for (std::size_t i = old_segment_count; i < new_segment_count; ++i)
214 new_segments[i] =
nullptr;
216 for (std::size_t i = old_segment_count; i < new_segment_count; ++i)
218 new_segments[i] = element_alloc_traits::allocate(elem_alloc, segment_size(i));
220 if (m_segments !=
nullptr)
222 segment_alloc_traits::deallocate(typed_allocator, m_segments, old_segment_count);
227 for (std::size_t i = old_segment_count; i < new_segment_count; ++i)
229 if (new_segments[i] !=
nullptr)
231 element_alloc_traits::deallocate(elem_alloc, new_segments[i], segment_size(i));
234 segment_alloc_traits::deallocate(typed_allocator, new_segments, new_segment_count);
237 m_segments = new_segments;
238 m_capacity = segment_count_total_capacity(new_segment_count);
252 std::size_t insertion_index = m_size;
253 assert(
capacity() > insertion_index);
254 std::size_t segment_index = index_segment(insertion_index);
255 std::size_t sub_index = index_subindex(insertion_index);
257 using element_allocator_type =
typename std::allocator_traits< Alloc >::template rebind_alloc< T >;
258 using element_alloc_traits = std::allocator_traits< element_allocator_type >;
259 element_allocator_type elem_alloc(m_alloc);
260 T*& segment = m_segments[segment_index];
261 assert(sub_index < segment_size(segment_index));
262 assert(segment !=
nullptr);
263 element_alloc_traits::construct(elem_alloc, &segment[sub_index], std::move(value));
270 std::size_t segment_index = index_segment(index);
271 std::size_t sub_index = index_subindex(index);
272 return m_segments[segment_index][sub_index];
278 std::size_t segment_index = index_segment(index);
279 std::size_t sub_index = index_subindex(index);
280 return m_segments[segment_index][sub_index];
284 T&
at(std::size_t index)
288 throw std::out_of_range(
"segmented_dynar::at: index out of range");
290 return (*
this)[index];
294 T
const&
at(std::size_t index)
const
298 throw std::out_of_range(
"segmented_dynar::at: index out of range");
300 return (*
this)[index];
318 return (*
this)[m_size - 1];
324 return (*
this)[m_size - 1];
328 void assign(std::size_t count,
const T& value)
332 for (std::size_t i = 0; i < count; ++i)
345 template <
typename InputIt,
typename = std::enable_if_t< !std::is_
integral_v< InputIt > > >
349 if constexpr (std::is_base_of_v< std::forward_iterator_tag, typename std::iterator_traits< InputIt >::iterator_category >)
351 reserve(std::distance(first, last));
353 for (; first != last; ++first)
360 void assign(std::initializer_list< T > ilist)
362 assign(ilist.begin(), ilist.end());
369 std::size_t segment_index = index_segment(m_size);
370 std::size_t sub_index = index_subindex(m_size);
371 using element_allocator_type =
typename std::allocator_traits< Alloc >::template rebind_alloc< T >;
372 using element_alloc_traits = std::allocator_traits< element_allocator_type >;
373 element_allocator_type elem_alloc(m_alloc);
374 T*& segment = m_segments[segment_index];
375 element_alloc_traits::destroy(elem_alloc, &segment[sub_index]);
384 std::size_t required_segment_count = capacity_segment_count(m_size);
385 if (required_segment_count < capacity_segment_count(m_capacity))
387 using segment_allocator_type =
typename std::allocator_traits< Alloc >::template rebind_alloc< T* >;
388 using segment_alloc_traits = std::allocator_traits< segment_allocator_type >;
389 segment_allocator_type segment_allocator(m_alloc);
391 using element_allocator_type =
typename std::allocator_traits< Alloc >::template rebind_alloc< T >;
392 using element_alloc_traits = std::allocator_traits< element_allocator_type >;
393 element_allocator_type elem_alloc(m_alloc);
395 if (required_segment_count == 0)
397 for (std::size_t i = 0; i < capacity_segment_count(m_capacity); ++i)
399 elem_alloc.deallocate(m_segments[i], segment_size(i));
401 segment_allocator.deallocate(m_segments, capacity_segment_count(m_capacity));
402 m_segments =
nullptr;
406 T** new_segments = segment_alloc_traits::allocate(segment_allocator, required_segment_count);
407 for (std::size_t i = 0; i < required_segment_count; ++i)
409 new_segments[i] = m_segments[i];
411 for (std::size_t i = required_segment_count; i < capacity_segment_count(m_capacity); ++i)
413 elem_alloc.deallocate(m_segments[i], segment_size(i));
415 segment_allocator.deallocate(m_segments, capacity_segment_count(m_capacity));
416 m_segments = new_segments;
417 m_capacity = segment_count_total_capacity(required_segment_count);
450 template <
typename... Args >
457 std::size_t insertion_index = m_size;
458 assert(
capacity() > insertion_index);
459 std::size_t segment_index = index_segment(insertion_index);
460 std::size_t sub_index = index_subindex(insertion_index);
461 using element_allocator_type =
typename std::allocator_traits< Alloc >::template rebind_alloc< T >;
462 using element_alloc_traits = std::allocator_traits< element_allocator_type >;
463 element_allocator_type elem_alloc(m_alloc);
464 T*& segment = m_segments[segment_index];
465 assert(sub_index < segment_size(segment_index));
466 assert(segment !=
nullptr);
467 element_alloc_traits::construct(elem_alloc, &segment[sub_index], std::forward< Args >(args)...);
469 return segment[sub_index];
473 segmented_dynar(
segmented_dynar&& other) noexcept : m_size(other.m_size), m_capacity(other.m_capacity), m_segments(other.m_segments), m_alloc(std::move(other.m_alloc))
476 other.m_capacity = 0;
477 other.m_segments =
nullptr;
486 m_size = other.m_size;
487 m_capacity = other.m_capacity;
488 m_segments = other.m_segments;
489 m_alloc = std::move(other.m_alloc);
492 other.m_capacity = 0;
493 other.m_segments =
nullptr;
502 for (std::size_t i = 0; i < other.m_size; ++i)
513 if constexpr (std::allocator_traits< Alloc >::propagate_on_container_copy_assignment::value)
515 if (m_alloc != other.m_alloc)
519 m_alloc = other.m_alloc;
523 for (std::size_t i = 0; i < other.m_size; ++i)
538 template <
bool Const >
549 using pointer = std::conditional_t< Const, T const*, T* >;
551 using reference = std::conditional_t< Const, T const&, T& >;
553 using container_ptr = std::conditional_t< Const, const segmented_dynar*, segmented_dynar* >;
557 std::size_t m_global_index = 0;
562 void load_segment_cache()
564 if (m_global_index == m_container->size())
570 std::size_t seg_idx = index_segment(m_global_index);
571 std::size_t sub_idx = index_subindex(m_global_index);
573 pointer segment_base = m_container->m_segments[seg_idx];
574 std::size_t seg_sz = segment_size(seg_idx);
576 m_ptr = segment_base + sub_idx;
577 m_seg_begin = segment_base;
578 m_seg_end = segment_base + seg_sz;
588 if (container && index < container->
size())
590 load_segment_cache();
595 template <
bool Const2,
typename = std::enable_if_t< Const && !Const2 > >
596 iterator_impl(
const iterator_impl< Const2 >& other) : m_container(other.m_container), m_global_index(other.m_global_index), m_ptr(other.m_ptr), m_seg_begin(other.m_seg_begin), m_seg_end(other.m_seg_end)
616 if (m_ptr == m_seg_end)
618 load_segment_cache();
634 if (m_ptr == m_seg_begin || m_global_index == m_container->size())
637 load_segment_cache();
663 if (n > 0 && (m_ptr + n < m_seg_end))
669 else if (n < 0 && (m_ptr + n >= m_seg_begin))
678 load_segment_cache();
685 return *
this += (-n);
725 return m_global_index == other.m_global_index;
731 return m_global_index == other.m_global_index;
737 return !(*
this == other);
742 return !(*
this == other);
747 return m_global_index < other.m_global_index;
752 return m_global_index < other.m_global_index;
757 return m_global_index > other.m_global_index;
762 return m_global_index > other.m_global_index;
767 return m_global_index <= other.m_global_index;
772 return m_global_index <= other.m_global_index;
777 return m_global_index >= other.m_global_index;
782 return m_global_index >= other.m_global_index;
pointer operator->() const
Accesses the current element.
Definition segmented_dynar.hpp:606
iterator_impl(container_ptr container, std::size_t index)
Constructs an iterator at a logical index.
Definition segmented_dynar.hpp:586
std::conditional_t< Const, T const *, T * > pointer
Mutable or immutable element pointer type.
Definition segmented_dynar.hpp:549
iterator_impl & operator++()
Advances one element.
Definition segmented_dynar.hpp:612
bool operator>=(const iterator_impl< false > &other) const
Tests whether this index does not precede a mutable iterator.
Definition segmented_dynar.hpp:780
bool operator<=(const iterator_impl< true > &other) const
Tests whether this index does not follow an immutable iterator.
Definition segmented_dynar.hpp:765
iterator_impl & operator--()
Retreats one element.
Definition segmented_dynar.hpp:632
iterator_impl operator+(difference_type n) const
Returns an iterator moved by an offset.
Definition segmented_dynar.hpp:689
bool operator>=(const iterator_impl< true > &other) const
Tests whether this index does not precede an immutable iterator.
Definition segmented_dynar.hpp:775
iterator_impl operator--(int)
Retreats one element.
Definition segmented_dynar.hpp:648
iterator_impl(const iterator_impl< Const2 > &other)
Converts a mutable iterator to an immutable iterator.
Definition segmented_dynar.hpp:596
T value_type
Iterated element type.
Definition segmented_dynar.hpp:547
bool operator!=(const iterator_impl< false > &other) const
Compares with a mutable iterator for inequality.
Definition segmented_dynar.hpp:740
bool operator<(const iterator_impl< true > &other) const
Tests whether this index precedes an immutable iterator.
Definition segmented_dynar.hpp:745
std::conditional_t< Const, T const &, T & > reference
Mutable or immutable element reference type.
Definition segmented_dynar.hpp:551
bool operator==(const iterator_impl< false > &other) const
Compares with a mutable iterator for equality.
Definition segmented_dynar.hpp:729
bool operator<=(const iterator_impl< false > &other) const
Tests whether this index does not follow a mutable iterator.
Definition segmented_dynar.hpp:770
bool operator<(const iterator_impl< false > &other) const
Tests whether this index precedes a mutable iterator.
Definition segmented_dynar.hpp:750
reference operator*() const
Dereferences the current position.
Definition segmented_dynar.hpp:601
iterator_impl & operator+=(difference_type n)
Moves by a signed offset.
Definition segmented_dynar.hpp:656
std::conditional_t< Const, const segmented_dynar *, segmented_dynar * > container_ptr
Pointer to the mutable or immutable owning container.
Definition segmented_dynar.hpp:553
iterator_impl operator++(int)
Advances one element.
Definition segmented_dynar.hpp:624
bool operator>(const iterator_impl< false > &other) const
Tests whether this index follows a mutable iterator.
Definition segmented_dynar.hpp:760
bool operator!=(const iterator_impl< true > &other) const
Compares with an immutable iterator for inequality.
Definition segmented_dynar.hpp:735
std::ptrdiff_t difference_type
Signed iterator-distance type.
Definition segmented_dynar.hpp:545
iterator_impl()=default
Constructs a singular iterator.
bool operator>(const iterator_impl< true > &other) const
Tests whether this index follows an immutable iterator.
Definition segmented_dynar.hpp:755
difference_type operator-(const iterator_impl< false > &other) const
Computes distance from a mutable iterator.
Definition segmented_dynar.hpp:711
iterator_impl & operator-=(difference_type n)
Moves backward by a signed offset.
Definition segmented_dynar.hpp:683
bool operator==(const iterator_impl< true > &other) const
Compares with an immutable iterator for equality.
Definition segmented_dynar.hpp:723
reference operator[](difference_type n) const
Dereferences an offset position.
Definition segmented_dynar.hpp:717
iterator_impl operator-(difference_type n) const
Returns an iterator moved backward by an offset.
Definition segmented_dynar.hpp:697
difference_type operator-(const iterator_impl< true > &other) const
Computes distance from an immutable iterator.
Definition segmented_dynar.hpp:705
std::random_access_iterator_tag iterator_category
Iterator category for legacy algorithms.
Definition segmented_dynar.hpp:543
iterator_impl< true > const_iterator
Immutable random-access iterator type.
Definition segmented_dynar.hpp:792
void reserve(std::size_t new_capacity)
Ensures capacity for at least a requested number of elements.
Definition segmented_dynar.hpp:186
~segmented_dynar()
Destroys all elements and releases all segments.
Definition segmented_dynar.hpp:438
const_iterator cend() const
Returns an immutable iterator one past the final element.
Definition segmented_dynar.hpp:820
void clear()
Destroys all elements while retaining allocated segments.
Definition segmented_dynar.hpp:422
iterator begin()
Returns an iterator to the first element.
Definition segmented_dynar.hpp:795
const_iterator end() const
Returns an immutable iterator one past the final element.
Definition segmented_dynar.hpp:810
segmented_dynar & operator=(segmented_dynar &&other) noexcept
Move-assigns by taking ownership of all segments.
Definition segmented_dynar.hpp:481
T const & back() const
Returns the final element.
Definition segmented_dynar.hpp:322
T & front()
Returns the first element.
Definition segmented_dynar.hpp:304
iterator end()
Returns an iterator one past the final element.
Definition segmented_dynar.hpp:800
iterator_impl< false > iterator
Mutable random-access iterator type.
Definition segmented_dynar.hpp:790
void assign(std::initializer_list< T > ilist)
Replaces the contents from an initializer list.
Definition segmented_dynar.hpp:360
T & back()
Returns the final element.
Definition segmented_dynar.hpp:316
void reset()
Destroys all elements and releases all allocated storage.
Definition segmented_dynar.hpp:431
std::size_t size() const
Returns the number of constructed elements.
Definition segmented_dynar.hpp:174
T const & front() const
Returns the first element.
Definition segmented_dynar.hpp:310
segmented_dynar(segmented_dynar &&other) noexcept
Move-constructs by taking ownership of all segments.
Definition segmented_dynar.hpp:473
segmented_dynar & operator=(const segmented_dynar &other)
Copy-assigns every element subject to allocator propagation rules.
Definition segmented_dynar.hpp:509
T & operator[](std::size_t index)
Accesses an element without bounds checking.
Definition segmented_dynar.hpp:268
void pop_back()
Destroys the final element.
Definition segmented_dynar.hpp:366
segmented_dynar()=default
Constructs an empty container with a default-constructed allocator.
T const & at(std::size_t index) const
Accesses an element with bounds checking.
Definition segmented_dynar.hpp:294
segmented_dynar(const Alloc &a) noexcept
Constructs an empty container with an allocator.
Definition segmented_dynar.hpp:157
void assign(std::size_t count, const T &value)
Replaces the contents with repeated copies.
Definition segmented_dynar.hpp:328
std::size_t capacity() const
Returns the number of elements that fit in allocated segments.
Definition segmented_dynar.hpp:168
void push_back(T value)
Appends an element by value.
Definition segmented_dynar.hpp:246
segmented_dynar(const segmented_dynar &other)
Copy-constructs every element.
Definition segmented_dynar.hpp:499
T const & operator[](std::size_t index) const
Accesses an element without bounds checking.
Definition segmented_dynar.hpp:276
T & emplace_back(Args &&... args)
Constructs an element at the end of the container.
Definition segmented_dynar.hpp:451
void assign(InputIt first, InputIt last)
Replaces the contents with an iterator range.
Definition segmented_dynar.hpp:346
const_iterator cbegin() const
Returns an immutable iterator to the first element.
Definition segmented_dynar.hpp:815
Alloc get_allocator() const noexcept
Returns the allocator associated with the container.
Definition segmented_dynar.hpp:162
void shrink_to_fit()
Releases segments that are not needed for the current size.
Definition segmented_dynar.hpp:382
const_iterator begin() const
Returns an immutable iterator to the first element.
Definition segmented_dynar.hpp:805
T & at(std::size_t index)
Accesses an element with bounds checking.
Definition segmented_dynar.hpp:284
Containers, iterator adapters, callable wrappers, and value utilities.
Definition annex.hpp:14