Skip to content

Commit 5f6ee02

Browse files
dkp116pitrou
andauthored
apacheGH-50251: [C++] Add GetSpan to ArrayData (apache#50366)
### What changes are included in this PR? Add `GetSpan<T>(int i, int64_t length)` for read-only typed access to ArrayData. Add `GetMutableSpan<T>(int i, int64_t length)` for writable typed access to ArrayData. ### Are these changes tested? Yes, new unit tests were added in `array/data_test.cc` * GitHub Issue: apache#50251 Lead-authored-by: dkp116 <daneel668@gmail.com> Co-authored-by: Antoine Pitrou <antoine@python.org> Signed-off-by: Antoine Pitrou <antoine@python.org>
1 parent b19c476 commit 5f6ee02

2 files changed

Lines changed: 103 additions & 1 deletion

File tree

cpp/src/arrow/array/data.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,45 @@ struct ARROW_EXPORT ArrayData {
256256
return NULLPTR;
257257
}
258258
}
259+
/// \brief Access a buffer's data as a span
260+
///
261+
/// \param i The buffer index
262+
/// \param length The required length (in number of typed values) of the requested span
263+
/// \pre i > 0
264+
/// \pre length <= the length of the buffer (in number of values) that's expected for
265+
/// this array type
266+
/// \return A span<const T> of the requested length
267+
template <typename T>
268+
std::span<const T> GetSpan(int i, int64_t length) const {
269+
if (!buffers[i]) {
270+
assert(length == 0);
271+
return {};
272+
}
273+
const int64_t buffer_length = buffers[i]->size() / static_cast<int64_t>(sizeof(T));
274+
assert(i > 0 && length + offset <= buffer_length);
275+
ARROW_UNUSED(buffer_length);
276+
return std::span<const T>(buffers[i]->data_as<T>() + this->offset, length);
277+
}
278+
279+
/// \brief Access a buffer's data as a span
280+
///
281+
/// \param i The buffer index
282+
/// \param length The required length (in number of typed values) of the requested span
283+
/// \pre i > 0
284+
/// \pre length <= the length of the buffer (in number of values) that's expected for
285+
/// this array type
286+
/// \return A span<T> of the requested length
287+
template <typename T>
288+
std::span<T> GetMutableSpan(int i, int64_t length) {
289+
if (!buffers[i]) {
290+
assert(length == 0);
291+
return {};
292+
}
293+
const int64_t buffer_length = buffers[i]->size() / static_cast<int64_t>(sizeof(T));
294+
assert(i > 0 && length + offset <= buffer_length);
295+
ARROW_UNUSED(buffer_length);
296+
return std::span<T>(buffers[i]->mutable_data_as<T>() + this->offset, length);
297+
}
259298

260299
/// \brief Access a buffer's data as a typed C pointer
261300
///

cpp/src/arrow/array/data_test.cc

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
#include <type_traits>
19+
1820
#include <gtest/gtest.h>
1921

20-
#include "arrow/array.h"
22+
#include "arrow/array/array_base.h"
2123
#include "arrow/array/data.h"
2224
#include "arrow/testing/gtest_util.h"
2325

@@ -43,4 +45,65 @@ TEST(ArraySpan, SetSlice) {
4345
ASSERT_EQ(span.GetNullCount(), 1);
4446
}
4547

48+
TEST(ArrayData, GetSpanRespectsOffset) {
49+
std::vector<uint16_t> values = {1, 2, 3, 4, 5};
50+
51+
auto buffer = Buffer::FromVector(std::move(values));
52+
auto data = ArrayData::Make(uint16(), /*length=*/3, {nullptr, buffer}, /*null_count=*/0,
53+
/*offset=*/1);
54+
auto span = data->GetSpan<uint16_t>(1, 3);
55+
56+
const bool is_const_pointer = std::is_same_v<decltype(span)::pointer, const uint16_t*>;
57+
ASSERT_TRUE(is_const_pointer);
58+
59+
EXPECT_EQ(span.size(), 3);
60+
EXPECT_EQ(span[0], 2);
61+
EXPECT_EQ(span[1], 3);
62+
EXPECT_EQ(span[2], 4);
63+
}
64+
65+
TEST(ArrayData, GetMutableSpanRespectsOffset) {
66+
std::vector<uint16_t> values = {10, 20, 30, 40, 50};
67+
68+
auto buffer = std::make_shared<MutableBuffer>(reinterpret_cast<uint8_t*>(values.data()),
69+
values.size() * sizeof(uint16_t));
70+
std::vector<std::shared_ptr<Buffer>> buffers = {nullptr, buffer};
71+
72+
auto data =
73+
ArrayData::Make(uint16(), /*length=*/3, buffers, /*null_count=*/0, /*offset=*/1);
74+
auto span = data->GetMutableSpan<uint16_t>(1, 3);
75+
76+
const bool is_mut_pointer = std::is_same_v<decltype(span)::pointer, uint16_t*>;
77+
ASSERT_TRUE(is_mut_pointer);
78+
79+
EXPECT_EQ(span.size(), 3);
80+
EXPECT_EQ(span[0], 20);
81+
EXPECT_EQ(span[1], 30);
82+
EXPECT_EQ(span[2], 40);
83+
84+
span[0] = 200;
85+
span[1] = 300;
86+
span[2] = 400;
87+
88+
auto raw = reinterpret_cast<uint16_t*>(buffer->mutable_data());
89+
90+
EXPECT_EQ(raw[1], 200);
91+
EXPECT_EQ(raw[2], 300);
92+
EXPECT_EQ(raw[3], 400);
93+
}
94+
95+
TEST(ArrayData, GetSpanNullBuffer) {
96+
auto data = ArrayData::Make(uint16(), /*length=*/0, /*buffers=*/{nullptr, nullptr},
97+
/*null_count=*/0, /*offset=*/0);
98+
auto span = data->GetSpan<uint16_t>(1, 0);
99+
EXPECT_EQ(span.size(), 0);
100+
}
101+
102+
TEST(ArrayData, GetMutableSpanNullBuffer) {
103+
auto data = ArrayData::Make(uint16(), /*length=*/0, /*buffers=*/{nullptr, nullptr},
104+
/*null_count=*/0, /*offset=*/0);
105+
auto span = data->GetMutableSpan<uint16_t>(1, 0);
106+
EXPECT_EQ(span.size(), 0);
107+
}
108+
46109
} // namespace arrow

0 commit comments

Comments
 (0)