This is an automated email from the ASF dual-hosted git repository.
pitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new 5f6ee02985 GH-50251: [C++] Add GetSpan to ArrayData (#50366)
5f6ee02985 is described below
commit 5f6ee02985d2c7c653aea2ce3d6a78ee5791f6a6
Author: Daneel Patel <[email protected]>
AuthorDate: Tue Jul 7 16:26:55 2026 +0100
GH-50251: [C++] Add GetSpan to ArrayData (#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: #50251
Lead-authored-by: dkp116 <[email protected]>
Co-authored-by: Antoine Pitrou <[email protected]>
Signed-off-by: Antoine Pitrou <[email protected]>
---
cpp/src/arrow/array/data.h | 39 ++++++++++++++++++++++++
cpp/src/arrow/array/data_test.cc | 65 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 103 insertions(+), 1 deletion(-)
diff --git a/cpp/src/arrow/array/data.h b/cpp/src/arrow/array/data.h
index 92308e8a01..70ac850c01 100644
--- a/cpp/src/arrow/array/data.h
+++ b/cpp/src/arrow/array/data.h
@@ -256,6 +256,45 @@ struct ARROW_EXPORT ArrayData {
return NULLPTR;
}
}
+ /// \brief Access a buffer's data as a span
+ ///
+ /// \param i The buffer index
+ /// \param length The required length (in number of typed values) of the
requested span
+ /// \pre i > 0
+ /// \pre length <= the length of the buffer (in number of values) that's
expected for
+ /// this array type
+ /// \return A span<const T> of the requested length
+ template <typename T>
+ std::span<const T> GetSpan(int i, int64_t length) const {
+ if (!buffers[i]) {
+ assert(length == 0);
+ return {};
+ }
+ const int64_t buffer_length = buffers[i]->size() /
static_cast<int64_t>(sizeof(T));
+ assert(i > 0 && length + offset <= buffer_length);
+ ARROW_UNUSED(buffer_length);
+ return std::span<const T>(buffers[i]->data_as<T>() + this->offset, length);
+ }
+
+ /// \brief Access a buffer's data as a span
+ ///
+ /// \param i The buffer index
+ /// \param length The required length (in number of typed values) of the
requested span
+ /// \pre i > 0
+ /// \pre length <= the length of the buffer (in number of values) that's
expected for
+ /// this array type
+ /// \return A span<T> of the requested length
+ template <typename T>
+ std::span<T> GetMutableSpan(int i, int64_t length) {
+ if (!buffers[i]) {
+ assert(length == 0);
+ return {};
+ }
+ const int64_t buffer_length = buffers[i]->size() /
static_cast<int64_t>(sizeof(T));
+ assert(i > 0 && length + offset <= buffer_length);
+ ARROW_UNUSED(buffer_length);
+ return std::span<T>(buffers[i]->mutable_data_as<T>() + this->offset,
length);
+ }
/// \brief Access a buffer's data as a typed C pointer
///
diff --git a/cpp/src/arrow/array/data_test.cc b/cpp/src/arrow/array/data_test.cc
index 011249c54e..9e103207d0 100644
--- a/cpp/src/arrow/array/data_test.cc
+++ b/cpp/src/arrow/array/data_test.cc
@@ -15,9 +15,11 @@
// specific language governing permissions and limitations
// under the License.
+#include <type_traits>
+
#include <gtest/gtest.h>
-#include "arrow/array.h"
+#include "arrow/array/array_base.h"
#include "arrow/array/data.h"
#include "arrow/testing/gtest_util.h"
@@ -43,4 +45,65 @@ TEST(ArraySpan, SetSlice) {
ASSERT_EQ(span.GetNullCount(), 1);
}
+TEST(ArrayData, GetSpanRespectsOffset) {
+ std::vector<uint16_t> values = {1, 2, 3, 4, 5};
+
+ auto buffer = Buffer::FromVector(std::move(values));
+ auto data = ArrayData::Make(uint16(), /*length=*/3, {nullptr, buffer},
/*null_count=*/0,
+ /*offset=*/1);
+ auto span = data->GetSpan<uint16_t>(1, 3);
+
+ const bool is_const_pointer = std::is_same_v<decltype(span)::pointer, const
uint16_t*>;
+ ASSERT_TRUE(is_const_pointer);
+
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_EQ(span[0], 2);
+ EXPECT_EQ(span[1], 3);
+ EXPECT_EQ(span[2], 4);
+}
+
+TEST(ArrayData, GetMutableSpanRespectsOffset) {
+ std::vector<uint16_t> values = {10, 20, 30, 40, 50};
+
+ auto buffer =
std::make_shared<MutableBuffer>(reinterpret_cast<uint8_t*>(values.data()),
+ values.size() *
sizeof(uint16_t));
+ std::vector<std::shared_ptr<Buffer>> buffers = {nullptr, buffer};
+
+ auto data =
+ ArrayData::Make(uint16(), /*length=*/3, buffers, /*null_count=*/0,
/*offset=*/1);
+ auto span = data->GetMutableSpan<uint16_t>(1, 3);
+
+ const bool is_mut_pointer = std::is_same_v<decltype(span)::pointer,
uint16_t*>;
+ ASSERT_TRUE(is_mut_pointer);
+
+ EXPECT_EQ(span.size(), 3);
+ EXPECT_EQ(span[0], 20);
+ EXPECT_EQ(span[1], 30);
+ EXPECT_EQ(span[2], 40);
+
+ span[0] = 200;
+ span[1] = 300;
+ span[2] = 400;
+
+ auto raw = reinterpret_cast<uint16_t*>(buffer->mutable_data());
+
+ EXPECT_EQ(raw[1], 200);
+ EXPECT_EQ(raw[2], 300);
+ EXPECT_EQ(raw[3], 400);
+}
+
+TEST(ArrayData, GetSpanNullBuffer) {
+ auto data = ArrayData::Make(uint16(), /*length=*/0, /*buffers=*/{nullptr,
nullptr},
+ /*null_count=*/0, /*offset=*/0);
+ auto span = data->GetSpan<uint16_t>(1, 0);
+ EXPECT_EQ(span.size(), 0);
+}
+
+TEST(ArrayData, GetMutableSpanNullBuffer) {
+ auto data = ArrayData::Make(uint16(), /*length=*/0, /*buffers=*/{nullptr,
nullptr},
+ /*null_count=*/0, /*offset=*/0);
+ auto span = data->GetMutableSpan<uint16_t>(1, 0);
+ EXPECT_EQ(span.size(), 0);
+}
+
} // namespace arrow