Skip to content

Commit 9329ff0

Browse files
committed
flat jagged vector alignment
1 parent 7703f49 commit 9329ff0

3 files changed

Lines changed: 57 additions & 46 deletions

File tree

include/gl/types/flat_jagged_vector.hpp

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
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
3435
template <std::semiregular T>
3536
class flat_jagged_vector {
3637
public:
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++)

include/gl/types/flat_matrix.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#include "gl/types/core.hpp"
88

99
#include <concepts>
10-
#include <cstddef>
1110
#include <cstdint>
1211
#include <format>
1312
#include <initializer_list>

include/hgl/impl/flat_incidence_matrix.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class flat_incidence_matrix<hgl::undirected_t, ImplTag> final {
177177
}
178178
else { // incident with minor
179179
return std::views::iota(initial_id_v<id_type>, this->_matrix.n_rows())
180-
| std::views::filter([this, minor_idx = to_diff(id)](const id_type major_id) {
180+
| std::views::filter([this, minor_idx = to_idx(id)](const id_type major_id) {
181181
return this->_matrix[to_idx(major_id), minor_idx];
182182
});
183183
}

0 commit comments

Comments
 (0)