Skip to content

Commit 7d2ca70

Browse files
committed
ENH: Add nx::core::Extent and unified readExtent API on AbstractDataStore
Adds nx::core::Extent (a strided N-dimensional range struct, moved from simplnx-ooc) and pure-virtual readExtent/writeExtent on AbstractDataStore<T>. This is the primary bulk-access API for visualization code: the concrete store (DataStore for in-memory, HDF5ChunkedStore for OOC) picks the optimal I/O primitive per call, which lets the viz layer make one virtual call instead of hand-rolling per-row copyIntoBuffer loops. DataStore<T> implements the 3D fast path via std::memcpy on contiguous X rows (the hot path for image geometries in ZYX tuple order). The memcpy branch is guarded behind !std::is_same_v<T, bool> because std::vector<bool> has no .data() accessor; bool falls through to a per-tuple copy. 1D extents use a flat strided walk. EmptyDataStore returns empty / no-ops as a placeholder. Also updates MockOocDataStore in IParallelAlgorithmTest to override the new virtuals (otherwise the mock is abstract and can't be instantiated) and adds ExtentTest with 82 assertions covering construction, contains/overlaps/intersect, equality, and strided semantics.
1 parent 404284c commit 7d2ca70

8 files changed

Lines changed: 753 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ set(SIMPLNX_HDRS
353353
${SIMPLNX_SOURCE_DIR}/Common/Constants.hpp
354354
${SIMPLNX_SOURCE_DIR}/Common/DataTypeUtilities.hpp
355355
${SIMPLNX_SOURCE_DIR}/Common/DataVector.hpp
356+
${SIMPLNX_SOURCE_DIR}/Common/Extent.hpp
356357
${SIMPLNX_SOURCE_DIR}/Common/EulerAngle.hpp
357358
${SIMPLNX_SOURCE_DIR}/Common/Filesystem.hpp
358359
${SIMPLNX_SOURCE_DIR}/Common/IteratorUtility.hpp

src/simplnx/Common/Extent.hpp

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#pragma once
2+
3+
#include "simplnx/Common/Types.hpp"
4+
5+
#include <algorithm>
6+
#include <limits>
7+
#include <numeric>
8+
#include <stdexcept>
9+
#include <vector>
10+
11+
namespace nx::core
12+
{
13+
struct Extent
14+
{
15+
std::vector<uint64> min; // N-dimensional lower bounds (inclusive)
16+
std::vector<uint64> max; // N-dimensional upper bounds (inclusive)
17+
std::vector<uint64> stride; // N-dimensional step sizes (default all-ones)
18+
19+
Extent() = default;
20+
21+
/**
22+
* @brief Constructs an Extent from min and max bound vectors with default stride (all ones).
23+
* Both vectors must have the same number of dimensions.
24+
* @param minVal N-dimensional lower bounds (inclusive)
25+
* @param maxVal N-dimensional upper bounds (inclusive)
26+
* @throw std::invalid_argument if minVal and maxVal have different sizes
27+
*/
28+
Extent(std::vector<uint64> minVal, std::vector<uint64> maxVal)
29+
: min(std::move(minVal))
30+
, max(std::move(maxVal))
31+
, stride(min.size(), 1)
32+
{
33+
if(min.size() != max.size())
34+
{
35+
throw std::invalid_argument("Extent min and max must have the same number of dimensions");
36+
}
37+
}
38+
39+
/**
40+
* @brief Constructs an Extent from min, max, and stride vectors.
41+
* All vectors must have the same number of dimensions. Each stride value
42+
* must be >= 1.
43+
* @param minVal N-dimensional lower bounds (inclusive)
44+
* @param maxVal N-dimensional upper bounds (inclusive)
45+
* @param strideVal N-dimensional step sizes (>= 1 per dimension)
46+
* @throw std::invalid_argument if sizes differ or any stride is zero
47+
*/
48+
Extent(std::vector<uint64> minVal, std::vector<uint64> maxVal, std::vector<uint64> strideVal)
49+
: min(std::move(minVal))
50+
, max(std::move(maxVal))
51+
, stride(std::move(strideVal))
52+
{
53+
if(min.size() != max.size() || min.size() != stride.size())
54+
{
55+
throw std::invalid_argument("Extent min, max, and stride must have the same number of dimensions");
56+
}
57+
for(uint64 d = 0; d < stride.size(); ++d)
58+
{
59+
if(stride[d] == 0)
60+
{
61+
throw std::invalid_argument("Extent stride must be >= 1 in every dimension");
62+
}
63+
}
64+
}
65+
66+
/**
67+
* @brief Returns the number of dimensions.
68+
*/
69+
uint64 dimensions() const
70+
{
71+
return min.size();
72+
}
73+
74+
/**
75+
* @brief Returns the number of strided elements along the given dimension.
76+
*
77+
* With stride s, the selected indices are min[d], min[d]+s, min[d]+2s, ...
78+
* up to max[d] (inclusive). The count is:
79+
* ceil((max[d] - min[d] + 1) / stride[d])
80+
* which equals (max[d] - min[d] + stride[d]) / stride[d] using integer
81+
* arithmetic (ceiling division).
82+
*/
83+
uint64 size(uint64 dim) const
84+
{
85+
uint64 span = max[dim] - min[dim] + 1;
86+
uint64 s = stride[dim];
87+
return (span + s - 1) / s;
88+
}
89+
90+
/**
91+
* @brief Returns the product of all dimension sizes (accounting for stride).
92+
*/
93+
uint64 totalElements() const
94+
{
95+
if(min.empty())
96+
{
97+
return 0;
98+
}
99+
uint64 total = 1;
100+
for(uint64 d = 0; d < dimensions(); ++d)
101+
{
102+
uint64 s = size(d);
103+
if(s > 0 && total > std::numeric_limits<uint64>::max() / s)
104+
{
105+
throw std::overflow_error("Extent::totalElements() overflow");
106+
}
107+
total *= s;
108+
}
109+
return total;
110+
}
111+
112+
/**
113+
* @brief Returns true if this extent fully contains the other extent.
114+
* Containment is determined by min/max bounds only (stride-agnostic).
115+
*/
116+
bool contains(const Extent& other) const
117+
{
118+
if(dimensions() != other.dimensions())
119+
{
120+
return false;
121+
}
122+
for(uint64 d = 0; d < dimensions(); ++d)
123+
{
124+
if(other.min[d] < min[d] || other.max[d] > max[d])
125+
{
126+
return false;
127+
}
128+
}
129+
return true;
130+
}
131+
132+
/**
133+
* @brief Returns true if this extent overlaps with the other extent.
134+
* Adjacent extents (touching but not sharing elements) do NOT overlap.
135+
* Overlap is determined by min/max bounds only (stride-agnostic).
136+
*/
137+
bool overlaps(const Extent& other) const
138+
{
139+
if(dimensions() != other.dimensions())
140+
{
141+
return false;
142+
}
143+
for(uint64 d = 0; d < dimensions(); ++d)
144+
{
145+
if(max[d] < other.min[d] || other.max[d] < min[d])
146+
{
147+
return false;
148+
}
149+
}
150+
return true;
151+
}
152+
153+
/**
154+
* @brief Returns the intersection of this extent with the other extent.
155+
* If the extents do not overlap, the result is an empty (0-dimensional) extent.
156+
* The resulting stride in each dimension is the element-wise maximum of the
157+
* two input strides.
158+
*/
159+
Extent intersect(const Extent& other) const
160+
{
161+
if(!overlaps(other))
162+
{
163+
return Extent{};
164+
}
165+
std::vector<uint64> newMin(dimensions());
166+
std::vector<uint64> newMax(dimensions());
167+
std::vector<uint64> newStride(dimensions());
168+
for(uint64 d = 0; d < dimensions(); ++d)
169+
{
170+
newMin[d] = std::max(min[d], other.min[d]);
171+
newMax[d] = std::min(max[d], other.max[d]);
172+
newStride[d] = std::max(stride[d], other.stride[d]);
173+
}
174+
return Extent{std::move(newMin), std::move(newMax), std::move(newStride)};
175+
}
176+
177+
bool operator==(const Extent& other) const = default;
178+
};
179+
} // namespace nx::core

src/simplnx/DataStructure/AbstractDataStore.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "simplnx/Common/Result.hpp"
4+
#include "simplnx/Common/Extent.hpp"
45
#include "simplnx/Common/StringLiteralFormatting.hpp"
56
#include "simplnx/Common/TypesUtility.hpp"
67
#include "simplnx/DataStructure/IDataStore.hpp"
@@ -468,6 +469,10 @@ class AbstractDataStore : public IDataStore
468469
*/
469470
virtual Result<> copyFromBuffer(usize startIndex, nonstd::span<const T> buffer) = 0;
470471

472+
virtual std::vector<T> readExtent(const Extent& extent) const = 0;
473+
474+
virtual void writeExtent(const Extent& extent, nonstd::span<const T> data) = 0;
475+
471476
/**
472477
* @brief Returns the value found at the specified index of the DataStore.
473478
* This cannot be used to edit the value found at the specified index.

0 commit comments

Comments
 (0)