44
55#pragma once
66
7+ #include " gl/types/core.hpp"
8+
79#include < concepts>
8- #include < cstddef>
910#include < cstdint>
1011#include < format>
1112#include < initializer_list>
@@ -30,22 +31,24 @@ namespace gl {
3031// / @todo Implement assign, and swap methods.
3132// / @todo Implement iterator-based insert, emplace and erase methods.
3233// / @todo Add `operator<<` overload for `std::ostream` and specialize `std::formatter`.
33- // / @todo Use ` std::ptrdiff_t` instead of ` std::size_t` for offset values.
34+ // / @todo Use std::ptrdiff_t instead of std::size_t for offset values
3435template <std::semiregular T>
3536class flat_jagged_vector {
3637public:
3738 // / @brief Type of elements stored in segments
3839 using value_type = T;
3940 // / @brief Unsigned integral type used for sizes and indices
4041 using size_type = std::size_t ;
42+ // / @brief The underlying contiguous storage container
43+ using container_type = std::vector<value_type>;
4144 // / @brief Reference to an element
42- using reference = value_type& ;
45+ using reference = typename container_type::reference ;
4346 // / @brief Const reference to an element
44- using const_reference = const value_type& ;
45- // / @brief Span type representing a non-owning segment of elements
46- using segment_type = std::span<value_type >;
47- // / @brief Const span type representing a non-owning const segment of elements
48- using const_segment_type = std::span< const value_type >;
47+ using const_reference = typename container_type::const_reference ;
48+ // / @brief Subrange type representing a non-owning segment of elements
49+ using segment_type = std::ranges::subrange< typename container_type::iterator >;
50+ // / @brief Const subrange type representing a non-owning const segment of elements
51+ using const_segment_type = std::ranges::subrange< typename container_type::const_iterator >;
4952
5053 // --- iterators ---
5154
@@ -60,7 +63,10 @@ class flat_jagged_vector {
6063 // / @warning Invalidated when the `flat_jagged_vector` is modified (structure changes or element insertions/deletions).
6164 template <bool Const>
6265 class segment_iterator {
63- using data_ptr_type = std::conditional_t <Const, const T*, T*>;
66+ using data_iter_type = std::conditional_t <
67+ Const,
68+ typename container_type::const_iterator,
69+ typename container_type::iterator>;
6470 using offset_ptr_type = const size_type*;
6571
6672 public:
@@ -81,25 +87,25 @@ class flat_jagged_vector {
8187 segment_iterator () = default ;
8288
8389 // / @brief Constructs an iterator pointing to a specific segment.
84- // / @param data_ptr Pointer to the underlying element data (may be null for null iterator)
90+ // / @param data_iter Iterator to the underlying element data (may be null for null iterator)
8591 // / @param offset_ptr Pointer to the offsets array at the position of this segment
86- segment_iterator (data_ptr_type data_ptr , offset_ptr_type offset_ptr) noexcept
87- : _data_ptr(data_ptr ), _offset_ptr(offset_ptr) {}
92+ segment_iterator (data_iter_type data_iter , offset_ptr_type offset_ptr) noexcept
93+ : _data_iter(data_iter ), _offset_ptr(offset_ptr) {}
8894
8995 // / @brief Implicit conversion from mutable to const iterator
9096 // / @return A const iterator pointing to the same segment
9197 operator segment_iterator<true >() const noexcept
9298 requires (not Const)
9399 {
94- return segment_iterator<true >(this ->_data_ptr , this ->_offset_ptr );
100+ return segment_iterator<true >(this ->_data_iter , this ->_offset_ptr );
95101 }
96102
97103 // / @brief Dereferences the iterator to the current segment.
98104 // / @return A span representing the segment at the current position
99105 [[nodiscard]] reference operator *() const noexcept {
100- const auto beg = *this ->_offset_ptr ;
101- const auto end = *(this ->_offset_ptr + 1uz);
102- return reference (this ->_data_ptr + beg, end - beg );
106+ const auto beg = to_diff ( *this ->_offset_ptr ) ;
107+ const auto end = to_diff ( *(this ->_offset_ptr + 1uz) );
108+ return reference (this ->_data_iter + beg, this -> _data_iter + end );
103109 }
104110
105111 // / @brief Random access to a segment at offset from current position.
@@ -217,7 +223,7 @@ class flat_jagged_vector {
217223 }
218224
219225 private:
220- data_ptr_type _data_ptr{ nullptr } ;
226+ data_iter_type _data_iter ;
221227 offset_ptr_type _offset_ptr{nullptr };
222228 };
223229
@@ -471,9 +477,9 @@ class flat_jagged_vector {
471477 // / @warning No bounds checking is performed for performance. Use `at()` for bounds-checked access.
472478 // / Calling on an out-of-bounds index results in Undefined Behavior.
473479 [[nodiscard]] segment_type operator [](size_type i) {
474- const auto beg = this ->_offsets [i];
475- const auto end = this ->_offsets [i + 1uz];
476- return segment_type (this ->_data .data () + beg, end - beg );
480+ const auto beg = to_diff ( this ->_offsets [i]) ;
481+ const auto end = to_diff ( this ->_offsets [i + 1uz]) ;
482+ return segment_type (this ->_data .begin () + beg, this -> _data . begin () + end );
477483 }
478484
479485 // / @brief Returns a const segment at the given index without bounds checking.
@@ -483,9 +489,9 @@ class flat_jagged_vector {
483489 // / @warning No bounds checking is performed for performance. Use `at()` for bounds-checked access.
484490 // / Calling on an out-of-bounds index results in Undefined Behavior.
485491 [[nodiscard]] const_segment_type operator [](size_type i) const {
486- const auto beg = this ->_offsets [i];
487- const auto end = this ->_offsets [i + 1uz];
488- return const_segment_type (this ->_data .data () + beg, end - beg );
492+ const auto beg = to_diff ( this ->_offsets [i]) ;
493+ const auto end = to_diff ( this ->_offsets [i + 1uz]) ;
494+ return const_segment_type (this ->_data .begin () + beg, this -> _data . begin () + end );
489495 }
490496
491497 // / @brief Returns a reference to an element within a segment without bounds checking.
@@ -693,14 +699,20 @@ class flat_jagged_vector {
693699
694700 // / @brief Returns a raw pointer to the underlying flat data array.
695701 // / @return A raw pointer to the first element in the `_data` array.
696- // / @warning No bounds checking is performed. Structural modifications (like resizing) cannot be done via this pointer; use `data_storage()` instead.
697- [[nodiscard]] value_type* data_ptr () noexcept {
702+ // / @warning Structural modifications (like resizing) cannot be done via this pointer; use `data_storage()` instead.
703+ // / @warning Not available for boolean vectors.
704+ [[nodiscard]] value_type* data_ptr () noexcept
705+ requires(not std::same_as<value_type, bool >)
706+ {
698707 return this ->_data .data ();
699708 }
700709
701710 // / @brief Returns a const raw pointer to the underlying flat data array.
702711 // / @return A const raw pointer to the first element in the `_data` array.
703- [[nodiscard]] const value_type* data_ptr () const noexcept {
712+ // / @warning Not available for boolean vectors.
713+ [[nodiscard]] const value_type* data_ptr () const noexcept
714+ requires(not std::same_as<value_type, bool >)
715+ {
704716 return this ->_data .data ();
705717 }
706718
@@ -749,28 +761,28 @@ class flat_jagged_vector {
749761 // / @return Iterator to the first segment
750762 // / @note Iterator invalidated by structural modifications
751763 [[nodiscard]] iterator begin () noexcept {
752- return iterator (this ->_data .data (), this ->_offsets .data ());
764+ return iterator (this ->_data .begin (), this ->_offsets .data ());
753765 }
754766
755767 // / @brief Returns a mutable iterator past the last segment (end sentinel).
756768 // / @return Iterator one position past the last segment
757769 // / @note Iterator invalidated by structural modifications
758770 [[nodiscard]] iterator end () noexcept {
759- return iterator (this ->_data .data (), this ->_offsets .data () + this ->size ());
771+ return iterator (this ->_data .begin (), this ->_offsets .data () + this ->size ());
760772 }
761773
762774 // / @brief Returns a const iterator to the first segment.
763775 // / @return Const iterator to the first segment
764776 // / @note Iterator invalidated by structural modifications
765777 [[nodiscard]] const_iterator begin () const noexcept {
766- return const_iterator (this ->_data .data (), this ->_offsets .data ());
778+ return const_iterator (this ->_data .begin (), this ->_offsets .data ());
767779 }
768780
769781 // / @brief Returns a const iterator past the last segment (end sentinel).
770782 // / @return Const iterator one position past the last segment
771783 // / @note Iterator invalidated by structural modifications
772784 [[nodiscard]] const_iterator end () const noexcept {
773- return const_iterator (this ->_data .data (), this ->_offsets .data () + this ->size ());
785+ return const_iterator (this ->_data .begin (), this ->_offsets .data () + this ->size ());
774786 }
775787
776788 // / @brief Returns a const iterator to the first segment (explicit const form).
@@ -905,7 +917,7 @@ class flat_jagged_vector {
905917 requires std::convertible_to<std::ranges::range_reference_t <R>, value_type>
906918 void insert (size_type pos, R&& r) {
907919 const auto beg = this ->_offsets [pos];
908- const auto beg_pos = static_cast <std:: ptrdiff_t > (beg);
920+ const auto beg_pos = to_diff (beg);
909921 const auto old_size = this ->_data .size ();
910922
911923 this ->_ensure_offset_capacity ();
@@ -922,7 +934,7 @@ class flat_jagged_vector {
922934 }
923935
924936 const auto inserted = this ->_data .size () - old_size;
925- this ->_offsets .insert (this ->_offsets .begin () + static_cast <std:: ptrdiff_t > (pos), beg);
937+ this ->_offsets .insert (this ->_offsets .begin () + to_diff (pos), beg);
926938 for (size_type i = pos + 1uz; i < this ->_offsets .size (); i++)
927939 this ->_offsets [i] += inserted;
928940 }
@@ -952,12 +964,12 @@ class flat_jagged_vector {
952964 // / the erased segment in the underlying vector, $S$ is the number of segments after `pos`,
953965 // / and $L$ is the size of the erased segment. Erasing the **last** segment is $O(L)$.
954966 void erase (size_type pos) {
955- const auto start = static_cast <std:: ptrdiff_t > (this ->_offsets [pos]);
956- const auto end = static_cast <std:: ptrdiff_t > (this ->_offsets [pos + 1uz]);
967+ const auto start = to_diff (this ->_offsets [pos]);
968+ const auto end = to_diff (this ->_offsets [pos + 1uz]);
957969 const auto len = static_cast <size_type>(end - start);
958970
959971 this ->_data .erase (this ->_data .begin () + start, this ->_data .begin () + end);
960- this ->_offsets .erase (this ->_offsets .begin () + static_cast <std:: ptrdiff_t > (pos));
972+ this ->_offsets .erase (this ->_offsets .begin () + to_diff (pos));
961973 for (size_type i = pos; i < this ->_offsets .size (); i++)
962974 this ->_offsets [i] -= len;
963975 }
@@ -1022,7 +1034,7 @@ class flat_jagged_vector {
10221034 // / @note **Time Complexity:** Amortized $O(E + S)$ where $E$ is the number of elements after
10231035 // / the insertion point in the underlying vector, and $S$ is the number of segments after `seg`.
10241036 void insert (size_type seg, size_type pos, const value_type& value) {
1025- const auto insert_pos = static_cast <std:: ptrdiff_t > (this ->_offsets [seg] + pos);
1037+ const auto insert_pos = to_diff (this ->_offsets [seg] + pos);
10261038 this ->_data .insert (this ->_data .begin () + insert_pos, value);
10271039 for (size_type i = seg + 1uz; i < this ->_offsets .size (); i++)
10281040 this ->_offsets [i]++;
@@ -1041,7 +1053,7 @@ class flat_jagged_vector {
10411053 // / the insertion point in the underlying vector, and $S$ is the number of segments after `seg`.
10421054 template <class ... Args>
10431055 void emplace (size_type seg, size_type pos, Args&&... args) {
1044- const auto insert_pos = static_cast <std:: ptrdiff_t > (this ->_offsets [seg] + pos);
1056+ const auto insert_pos = to_diff (this ->_offsets [seg] + pos);
10451057 this ->_data .emplace (this ->_data .begin () + insert_pos, std::forward<Args>(args)...);
10461058 for (size_type i = seg + 1uz; i < this ->_offsets .size (); i++)
10471059 this ->_offsets [i]++;
@@ -1055,7 +1067,7 @@ class flat_jagged_vector {
10551067 // / @note **Time Complexity:** $O(E + S)$ where $E$ is the number of elements after the erased
10561068 // / position in the underlying vector, and $S$ is the number of segments after `seg`.
10571069 void erase (size_type seg, size_type pos) {
1058- const auto erase_pos = static_cast <std:: ptrdiff_t > (this ->_offsets [seg] + pos);
1070+ const auto erase_pos = to_diff (this ->_offsets [seg] + pos);
10591071 this ->_data .erase (this ->_data .begin () + erase_pos);
10601072 for (size_type i = seg + 1uz; i < this ->_offsets .size (); i++)
10611073 this ->_offsets [i]--;
@@ -1080,16 +1092,16 @@ class flat_jagged_vector {
10801092 const auto curr_count = this ->segment_size (seg);
10811093 if (n < curr_count) {
10821094 const auto diff = curr_count - n;
1083- const auto start = static_cast <std:: ptrdiff_t > (this ->_offsets [seg] + n);
1084- const auto end = static_cast <std:: ptrdiff_t > (this ->_offsets [seg + 1uz]);
1095+ const auto start = to_diff (this ->_offsets [seg] + n);
1096+ const auto end = to_diff (this ->_offsets [seg + 1uz]);
10851097
10861098 this ->_data .erase (this ->_data .begin () + start, this ->_data .begin () + end);
10871099 for (size_type i = seg + 1uz; i < this ->_offsets .size (); i++)
10881100 this ->_offsets [i] -= diff;
10891101 }
10901102 else if (n > curr_count) {
10911103 const auto diff = n - curr_count;
1092- const auto pos = static_cast <std:: ptrdiff_t > (this ->_offsets [seg + 1uz]);
1104+ const auto pos = to_diff (this ->_offsets [seg + 1uz]);
10931105
10941106 this ->_data .insert (this ->_data .begin () + pos, diff, value_type ());
10951107 for (size_type i = seg + 1uz; i < this ->_offsets .size (); i++)
@@ -1117,16 +1129,16 @@ class flat_jagged_vector {
11171129 const auto curr_count = this ->segment_size (seg);
11181130 if (n < curr_count) {
11191131 const auto diff = curr_count - n;
1120- const auto start = static_cast <std:: ptrdiff_t > (this ->_offsets [seg] + n);
1121- const auto end = static_cast <std:: ptrdiff_t > (this ->_offsets [seg + 1uz]);
1132+ const auto start = to_diff (this ->_offsets [seg] + n);
1133+ const auto end = to_diff (this ->_offsets [seg + 1uz]);
11221134
11231135 this ->_data .erase (this ->_data .begin () + start, this ->_data .begin () + end);
11241136 for (size_type i = seg + 1uz; i < this ->_offsets .size (); i++)
11251137 this ->_offsets [i] -= diff;
11261138 }
11271139 else if (n > curr_count) {
11281140 const auto diff = n - curr_count;
1129- const auto pos = static_cast <std:: ptrdiff_t > (this ->_offsets [seg + 1uz]);
1141+ const auto pos = to_diff (this ->_offsets [seg + 1uz]);
11301142
11311143 this ->_data .insert (this ->_data .begin () + pos, diff, value);
11321144 for (size_type i = seg + 1uz; i < this ->_offsets .size (); i++)
0 commit comments