diff --git a/src/nanoarrow/common/array.c b/src/nanoarrow/common/array.c index 20daed5ce..c6fb1cd9c 100644 --- a/src/nanoarrow/common/array.c +++ b/src/nanoarrow/common/array.c @@ -1820,14 +1820,16 @@ static int ArrowArrayViewValidateDefault(struct ArrowArrayView* array_view, case NANOARROW_TYPE_STRING: case NANOARROW_TYPE_BINARY: if (array_view->buffer_views[1].size_bytes != 0) { - first_offset = array_view->buffer_views[1].data.as_int32[array_view->offset]; + first_offset = ArrowBufferViewGetInt32Unsafe(&array_view->buffer_views[1], + array_view->offset); if (first_offset < 0) { ArrowErrorSet(error, "Expected first offset >= 0 but found %" PRId64, first_offset); return EINVAL; } - last_offset = array_view->buffer_views[1].data.as_int32[offset_plus_length]; + last_offset = ArrowBufferViewGetInt32Unsafe(&array_view->buffer_views[1], + offset_plus_length); if (last_offset < 0) { ArrowErrorSet(error, "Expected last offset >= 0 but found %" PRId64, last_offset); @@ -1856,14 +1858,16 @@ static int ArrowArrayViewValidateDefault(struct ArrowArrayView* array_view, case NANOARROW_TYPE_LARGE_STRING: case NANOARROW_TYPE_LARGE_BINARY: if (array_view->buffer_views[1].size_bytes != 0) { - first_offset = array_view->buffer_views[1].data.as_int64[array_view->offset]; + first_offset = ArrowBufferViewGetInt64Unsafe(&array_view->buffer_views[1], + array_view->offset); if (first_offset < 0) { ArrowErrorSet(error, "Expected first offset >= 0 but found %" PRId64, first_offset); return EINVAL; } - last_offset = array_view->buffer_views[1].data.as_int64[offset_plus_length]; + last_offset = ArrowBufferViewGetInt64Unsafe(&array_view->buffer_views[1], + offset_plus_length); if (last_offset < 0) { ArrowErrorSet(error, "Expected last offset >= 0 but found %" PRId64, last_offset); @@ -1905,14 +1909,16 @@ static int ArrowArrayViewValidateDefault(struct ArrowArrayView* array_view, case NANOARROW_TYPE_LIST: case NANOARROW_TYPE_MAP: if (array_view->buffer_views[1].size_bytes != 0) { - first_offset = array_view->buffer_views[1].data.as_int32[array_view->offset]; + first_offset = ArrowBufferViewGetInt32Unsafe(&array_view->buffer_views[1], + array_view->offset); if (first_offset < 0) { ArrowErrorSet(error, "Expected first offset >= 0 but found %" PRId64, first_offset); return EINVAL; } - last_offset = array_view->buffer_views[1].data.as_int32[offset_plus_length]; + last_offset = ArrowBufferViewGetInt32Unsafe(&array_view->buffer_views[1], + offset_plus_length); if (last_offset < 0) { ArrowErrorSet(error, "Expected last offset >= 0 but found %" PRId64, last_offset); @@ -1933,14 +1939,16 @@ static int ArrowArrayViewValidateDefault(struct ArrowArrayView* array_view, case NANOARROW_TYPE_LARGE_LIST: if (array_view->buffer_views[1].size_bytes != 0) { - first_offset = array_view->buffer_views[1].data.as_int64[array_view->offset]; + first_offset = ArrowBufferViewGetInt64Unsafe(&array_view->buffer_views[1], + array_view->offset); if (first_offset < 0) { ArrowErrorSet(error, "Expected first offset >= 0 but found %" PRId64, first_offset); return EINVAL; } - last_offset = array_view->buffer_views[1].data.as_int64[offset_plus_length]; + last_offset = ArrowBufferViewGetInt64Unsafe(&array_view->buffer_views[1], + offset_plus_length); if (last_offset < 0) { ArrowErrorSet(error, "Expected last offset >= 0 but found %" PRId64, last_offset); @@ -2037,7 +2045,8 @@ static int ArrowAssertIncreasingInt32(struct ArrowBufferView view, } for (int64_t i = 1; i < view.size_bytes / (int64_t)sizeof(int32_t); i++) { - if (view.data.as_int32[i] < view.data.as_int32[i - 1]) { + if (ArrowBufferViewGetInt32Unsafe(&view, i) < + ArrowBufferViewGetInt32Unsafe(&view, i - 1)) { ArrowErrorSet(error, "[%" PRId64 "] Expected element size >= 0", i); return EINVAL; } @@ -2053,7 +2062,8 @@ static int ArrowAssertIncreasingInt64(struct ArrowBufferView view, } for (int64_t i = 1; i < view.size_bytes / (int64_t)sizeof(int64_t); i++) { - if (view.data.as_int64[i] < view.data.as_int64[i - 1]) { + if (ArrowBufferViewGetInt64Unsafe(&view, i) < + ArrowBufferViewGetInt64Unsafe(&view, i - 1)) { ArrowErrorSet(error, "[%" PRId64 "] Expected element size >= 0", i); return EINVAL; } @@ -2110,14 +2120,14 @@ static int ArrowArrayViewValidateFull(struct ArrowArrayView* array_view, } if (array_view->layout.element_size_bits[i] == 32) { struct ArrowBufferView sliced_offsets; - sliced_offsets.data.as_int32 = - array_view->buffer_views[i].data.as_int32 + array_view->offset; + sliced_offsets.data.as_uint8 = array_view->buffer_views[i].data.as_uint8 + + array_view->offset * sizeof(int32_t); sliced_offsets.size_bytes = (array_view->length + 1) * sizeof(int32_t); NANOARROW_RETURN_NOT_OK(ArrowAssertIncreasingInt32(sliced_offsets, error)); } else { struct ArrowBufferView sliced_offsets; - sliced_offsets.data.as_int64 = - array_view->buffer_views[i].data.as_int64 + array_view->offset; + sliced_offsets.data.as_uint8 = array_view->buffer_views[i].data.as_uint8 + + array_view->offset * sizeof(int64_t); sliced_offsets.size_bytes = (array_view->length + 1) * sizeof(int64_t); NANOARROW_RETURN_NOT_OK(ArrowAssertIncreasingInt64(sliced_offsets, error)); } diff --git a/src/nanoarrow/common/array_test.cc b/src/nanoarrow/common/array_test.cc index 520f6df9d..02550a7ef 100644 --- a/src/nanoarrow/common/array_test.cc +++ b/src/nanoarrow/common/array_test.cc @@ -2907,6 +2907,48 @@ TEST(ArrayTest, ArrayViewTestString) { ArrowArrayViewReset(&array_view); } +TEST(ArrayTest, ArrayViewTestValidateUnalignedOffsets) { + struct ArrowError error; + + { + struct ArrowArrayView array_view; + uint8_t offsets[1 + 3 * sizeof(int32_t)] = {}; + const int32_t expected[] = {0, 1, 3}; + memcpy(offsets + 1, expected, sizeof(expected)); + + ArrowArrayViewInitFromType(&array_view, NANOARROW_TYPE_STRING); + ArrowArrayViewSetLength(&array_view, 2); + array_view.buffer_views[1].data.as_uint8 = offsets + 1; + array_view.buffer_views[1].size_bytes = sizeof(expected); + array_view.buffer_views[2].data.as_uint8 = offsets; + array_view.buffer_views[2].size_bytes = 3; + + EXPECT_EQ( + ArrowArrayViewValidate(&array_view, NANOARROW_VALIDATION_LEVEL_FULL, &error), + NANOARROW_OK); + ArrowArrayViewReset(&array_view); + } + + { + struct ArrowArrayView array_view; + uint8_t offsets[1 + 3 * sizeof(int64_t)] = {}; + const int64_t expected[] = {0, 1, 3}; + memcpy(offsets + 1, expected, sizeof(expected)); + + ArrowArrayViewInitFromType(&array_view, NANOARROW_TYPE_LARGE_STRING); + ArrowArrayViewSetLength(&array_view, 2); + array_view.buffer_views[1].data.as_uint8 = offsets + 1; + array_view.buffer_views[1].size_bytes = sizeof(expected); + array_view.buffer_views[2].data.as_uint8 = offsets; + array_view.buffer_views[2].size_bytes = 3; + + EXPECT_EQ( + ArrowArrayViewValidate(&array_view, NANOARROW_VALIDATION_LEVEL_FULL, &error), + NANOARROW_OK); + ArrowArrayViewReset(&array_view); + } +} + TEST(ArrayTest, ArrayViewTestLargeString) { struct ArrowArrayView array_view; struct ArrowError error; diff --git a/src/nanoarrow/common/inline_types.h b/src/nanoarrow/common/inline_types.h index 9c2033f06..1c1cd0bbc 100644 --- a/src/nanoarrow/common/inline_types.h +++ b/src/nanoarrow/common/inline_types.h @@ -714,6 +714,20 @@ struct ArrowBufferView { int64_t size_bytes; }; +static inline int32_t ArrowBufferViewGetInt32Unsafe(const struct ArrowBufferView* view, + int64_t i) { + int32_t value; + memcpy(&value, view->data.as_uint8 + i * sizeof(int32_t), sizeof(value)); + return value; +} + +static inline int64_t ArrowBufferViewGetInt64Unsafe(const struct ArrowBufferView* view, + int64_t i) { + int64_t value; + memcpy(&value, view->data.as_uint8 + i * sizeof(int64_t), sizeof(value)); + return value; +} + /// \brief Array buffer allocation and deallocation /// \ingroup nanoarrow-buffer ///