Skip to content

Commit bc1141a

Browse files
committed
flat_jagged vector alignment
1 parent 3ae7c50 commit bc1141a

3 files changed

Lines changed: 66 additions & 27 deletions

File tree

include/gl/types/flat_jagged_vector.hpp

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
#include <stdexcept>
1414
#include <vector>
1515

16-
// TODO:
17-
// - use std::ranges::subrange instead of std::span
18-
// - define container_type
19-
// - use std::ptrdiff_t for offsets
20-
2116
namespace gl {
2217

2318
/// @brief A flattened 2D vector (jagged array) providing efficient storage for variable-length segments.
@@ -34,21 +29,24 @@ namespace gl {
3429
/// @todo Implement assign, and swap methods.
3530
/// @todo Implement iterator-based insert, emplace and erase methods.
3631
/// @todo Add `operator<<` overload for `std::ostream` and specialize `std::formatter`.
32+
/// @todo Use std::ptrdiff_t instead of std::size_t for offset values
3733
template <std::semiregular T>
3834
class flat_jagged_vector {
3935
public:
4036
/// @brief Type of elements stored in segments
4137
using value_type = T;
4238
/// @brief Unsigned integral type used for sizes and indices
4339
using size_type = std::size_t;
40+
/// @brief The underlying contiguous storage container
41+
using container_type = std::vector<value_type>;
4442
/// @brief Reference to an element
45-
using reference = value_type&;
43+
using reference = typename container_type::reference;
4644
/// @brief Const reference to an element
47-
using const_reference = const value_type&;
48-
/// @brief Span type representing a non-owning segment of elements
49-
using segment_type = std::span<value_type>;
50-
/// @brief Const span type representing a non-owning const segment of elements
51-
using const_segment_type = std::span<const value_type>;
45+
using const_reference = typename container_type::const_reference;
46+
/// @brief Subrange type representing a non-owning segment of elements
47+
using segment_type = std::ranges::subrange<typename container_type::iterator>;
48+
/// @brief Const subrange type representing a non-owning const segment of elements
49+
using const_segment_type = std::ranges::subrange<typename container_type::const_iterator>;
5250

5351
// --- iterators ---
5452

@@ -63,7 +61,10 @@ class flat_jagged_vector {
6361
/// @warning Invalidated when the `flat_jagged_vector` is modified (structure changes or element insertions/deletions).
6462
template <bool Const>
6563
class segment_iterator {
66-
using data_ptr_type = std::conditional_t<Const, const T*, T*>;
64+
using data_iter_type = std::conditional_t<
65+
Const,
66+
typename container_type::const_iterator,
67+
typename container_type::iterator>;
6768
using offset_ptr_type = const size_type*;
6869

6970
public:
@@ -84,25 +85,25 @@ class flat_jagged_vector {
8485
segment_iterator() = default;
8586

8687
/// @brief Constructs an iterator pointing to a specific segment.
87-
/// @param data_ptr Pointer to the underlying element data (may be null for null iterator)
88+
/// @param data_iter Iterator to the underlying element data (may be null for null iterator)
8889
/// @param offset_ptr Pointer to the offsets array at the position of this segment
89-
segment_iterator(data_ptr_type data_ptr, offset_ptr_type offset_ptr) noexcept
90-
: _data_ptr(data_ptr), _offset_ptr(offset_ptr) {}
90+
segment_iterator(data_iter_type data_iter, offset_ptr_type offset_ptr) noexcept
91+
: _data_iter(data_iter), _offset_ptr(offset_ptr) {}
9192

9293
/// @brief Implicit conversion from mutable to const iterator
9394
/// @return A const iterator pointing to the same segment
9495
operator segment_iterator<true>() const noexcept
9596
requires(not Const)
9697
{
97-
return segment_iterator<true>(this->_data_ptr, this->_offset_ptr);
98+
return segment_iterator<true>(this->_data_iter, this->_offset_ptr);
9899
}
99100

100101
/// @brief Dereferences the iterator to the current segment.
101102
/// @return A span representing the segment at the current position
102103
[[nodiscard]] reference operator*() const noexcept {
103104
const auto beg = *this->_offset_ptr;
104105
const auto end = *(this->_offset_ptr + 1uz);
105-
return reference(this->_data_ptr + beg, end - beg);
106+
return reference(this->_data_iter + beg, this->_data_iter + end);
106107
}
107108

108109
/// @brief Random access to a segment at offset from current position.
@@ -220,7 +221,7 @@ class flat_jagged_vector {
220221
}
221222

222223
private:
223-
data_ptr_type _data_ptr{nullptr};
224+
data_iter_type _data_iter;
224225
offset_ptr_type _offset_ptr{nullptr};
225226
};
226227

@@ -476,7 +477,7 @@ class flat_jagged_vector {
476477
[[nodiscard]] segment_type operator[](size_type i) {
477478
const auto beg = this->_offsets[i];
478479
const auto end = this->_offsets[i + 1uz];
479-
return segment_type(this->_data.data() + beg, end - beg);
480+
return segment_type(this->_data.begin() + beg, this->_data.begin() + end);
480481
}
481482

482483
/// @brief Returns a const segment at the given index without bounds checking.
@@ -488,7 +489,7 @@ class flat_jagged_vector {
488489
[[nodiscard]] const_segment_type operator[](size_type i) const {
489490
const auto beg = this->_offsets[i];
490491
const auto end = this->_offsets[i + 1uz];
491-
return const_segment_type(this->_data.data() + beg, end - beg);
492+
return const_segment_type(this->_data.begin() + beg, this->_data.begin() + end);
492493
}
493494

494495
/// @brief Returns a reference to an element within a segment without bounds checking.
@@ -696,14 +697,20 @@ class flat_jagged_vector {
696697

697698
/// @brief Returns a raw pointer to the underlying flat data array.
698699
/// @return A raw pointer to the first element in the `_data` array.
699-
/// @warning No bounds checking is performed. Structural modifications (like resizing) cannot be done via this pointer; use `data_storage()` instead.
700-
[[nodiscard]] value_type* data_ptr() noexcept {
700+
/// @warning Structural modifications (like resizing) cannot be done via this pointer; use `data_storage()` instead.
701+
/// @warning Not available for boolean vectors.
702+
[[nodiscard]] value_type* data_ptr() noexcept
703+
requires(not std::same_as<value_type, bool>)
704+
{
701705
return this->_data.data();
702706
}
703707

704708
/// @brief Returns a const raw pointer to the underlying flat data array.
705709
/// @return A const raw pointer to the first element in the `_data` array.
706-
[[nodiscard]] const value_type* data_ptr() const noexcept {
710+
/// @warning Not available for boolean vectors.
711+
[[nodiscard]] const value_type* data_ptr() const noexcept
712+
requires(not std::same_as<value_type, bool>)
713+
{
707714
return this->_data.data();
708715
}
709716

@@ -752,28 +759,28 @@ class flat_jagged_vector {
752759
/// @return Iterator to the first segment
753760
/// @note Iterator invalidated by structural modifications
754761
[[nodiscard]] iterator begin() noexcept {
755-
return iterator(this->_data.data(), this->_offsets.data());
762+
return iterator(this->_data.begin(), this->_offsets.data());
756763
}
757764

758765
/// @brief Returns a mutable iterator past the last segment (end sentinel).
759766
/// @return Iterator one position past the last segment
760767
/// @note Iterator invalidated by structural modifications
761768
[[nodiscard]] iterator end() noexcept {
762-
return iterator(this->_data.data(), this->_offsets.data() + this->size());
769+
return iterator(this->_data.begin(), this->_offsets.data() + this->size());
763770
}
764771

765772
/// @brief Returns a const iterator to the first segment.
766773
/// @return Const iterator to the first segment
767774
/// @note Iterator invalidated by structural modifications
768775
[[nodiscard]] const_iterator begin() const noexcept {
769-
return const_iterator(this->_data.data(), this->_offsets.data());
776+
return const_iterator(this->_data.begin(), this->_offsets.data());
770777
}
771778

772779
/// @brief Returns a const iterator past the last segment (end sentinel).
773780
/// @return Const iterator one position past the last segment
774781
/// @note Iterator invalidated by structural modifications
775782
[[nodiscard]] const_iterator end() const noexcept {
776-
return const_iterator(this->_data.data(), this->_offsets.data() + this->size());
783+
return const_iterator(this->_data.begin(), this->_offsets.data() + this->size());
777784
}
778785

779786
/// @brief Returns a const iterator to the first segment (explicit const form).

tests/source/gl/test_flat_jagged_vector.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,22 @@ TEST_CASE_FIXTURE(
15501550
}
15511551
}
15521552

1553+
TEST_CASE("flat_jagged_vector should work properly for boolean value type") {
1554+
using sut_type = gl::flat_jagged_vector<bool>;
1555+
1556+
std::vector<std::vector<bool>> data{
1557+
{true, false},
1558+
{false, true, false}
1559+
};
1560+
sut_type sut{data};
1561+
1562+
CHECK_EQ(sut.size(), data.size());
1563+
CHECK(std::ranges::all_of(std::views::zip(sut, data), [](const auto& entries) {
1564+
const auto& [segment, inner_vec] = entries;
1565+
return std::ranges::equal(segment, inner_vec);
1566+
}));
1567+
}
1568+
15531569
TEST_SUITE_END(); // test_flat_jagged_vector
15541570

15551571
} // namespace gl_testing

tests/source/gl/test_flat_matrix.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,22 @@ TEST_CASE_FIXTURE(
16501650
CHECK_EQ(transposed.data_size(), 0uz);
16511651
}
16521652

1653+
TEST_CASE("flat_matrix should work properly for boolean value type") {
1654+
using sut_type = gl::flat_matrix<bool>;
1655+
1656+
std::vector<std::vector<bool>> data{
1657+
{ true, false},
1658+
{false, true}
1659+
};
1660+
sut_type sut{data};
1661+
1662+
CHECK_EQ(sut.size(), data.size());
1663+
CHECK(std::ranges::all_of(std::views::zip(sut, data), [](const auto& entries) {
1664+
const auto& [row, inner_vec] = entries;
1665+
return std::ranges::equal(row, inner_vec);
1666+
}));
1667+
}
1668+
16531669
TEST_SUITE_END(); // test_flat_matrix
16541670

16551671
} // namespace gl_testing

0 commit comments

Comments
 (0)