This is an automated email from the ASF dual-hosted git repository.
paleolimbot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-nanoarrow.git
The following commit(s) were added to refs/heads/main by this push:
new 33af79ab feat: append array views to arrays (#930)
33af79ab is described below
commit 33af79abbfeacce23427c580966b24eb1694c4a9
Author: Rusty Conover <[email protected]>
AuthorDate: Thu Sep 10 16:20:38 2026 -0400
feat: append array views to arrays (#930)
## Summary
- add `ArrowArrayAppendArrayView()` as a core array-building API
- append primitive, nested, union, list-view, and run-end encoded values
from an `ArrowArrayView`
- preserve source slicing semantics and report unsupported sliced
run-end encoded inputs
- add direct native coverage for primitive, nested/sliced, union, and
run-end encoded arrays
## Context
This extracts the generic array-view appender from #928 as requested in
review. It is independently useful and will be the prerequisite for a
separate dictionary-delta decoding PR; #928 no longer contains the
appender or decoder changes.
## Validation
- native CMake build passed
- native CTest suite: 334/334 passed (4 optional codec tests skipped)
---
src/nanoarrow/common/array.c | 562 +++++++++++++++++++++
src/nanoarrow/common/array_test.cc | 913 +++++++++++++++++++++++++++++++++++
src/nanoarrow/common/buffer_test.cc | 20 +
src/nanoarrow/common/inline_buffer.h | 8 +
src/nanoarrow/nanoarrow.h | 18 +
5 files changed, 1521 insertions(+)
diff --git a/src/nanoarrow/common/array.c b/src/nanoarrow/common/array.c
index 68c42d0d..25b02c20 100644
--- a/src/nanoarrow/common/array.c
+++ b/src/nanoarrow/common/array.c
@@ -77,6 +77,568 @@ int ArrowArrayIsInternal(struct ArrowArray* array) {
return array->release == &ArrowArrayReleaseInternal;
}
+static int ArrowTypeIsSignedInteger(enum ArrowType type) {
+ switch (type) {
+ case NANOARROW_TYPE_BOOL:
+ case NANOARROW_TYPE_INT8:
+ case NANOARROW_TYPE_INT16:
+ case NANOARROW_TYPE_INT32:
+ case NANOARROW_TYPE_INT64:
+ case NANOARROW_TYPE_DATE32:
+ case NANOARROW_TYPE_DATE64:
+ case NANOARROW_TYPE_TIMESTAMP:
+ case NANOARROW_TYPE_TIME32:
+ case NANOARROW_TYPE_TIME64:
+ case NANOARROW_TYPE_DURATION:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int ArrowTypeIsUnsignedInteger(enum ArrowType type) {
+ switch (type) {
+ case NANOARROW_TYPE_UINT8:
+ case NANOARROW_TYPE_UINT16:
+ case NANOARROW_TYPE_UINT32:
+ case NANOARROW_TYPE_UINT64:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int ArrowTypeIsFloatingPoint(enum ArrowType type) {
+ return type == NANOARROW_TYPE_HALF_FLOAT || type == NANOARROW_TYPE_FLOAT ||
+ type == NANOARROW_TYPE_DOUBLE;
+}
+
+static int ArrowTypeIsBinaryLike(enum ArrowType type) {
+ switch (type) {
+ case NANOARROW_TYPE_STRING:
+ case NANOARROW_TYPE_BINARY:
+ case NANOARROW_TYPE_FIXED_SIZE_BINARY:
+ case NANOARROW_TYPE_LARGE_STRING:
+ case NANOARROW_TYPE_LARGE_BINARY:
+ case NANOARROW_TYPE_BINARY_VIEW:
+ case NANOARROW_TYPE_STRING_VIEW:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int ArrowTypeIsListLike(enum ArrowType type) {
+ switch (type) {
+ case NANOARROW_TYPE_LIST:
+ case NANOARROW_TYPE_LARGE_LIST:
+ case NANOARROW_TYPE_MAP:
+ case NANOARROW_TYPE_LIST_VIEW:
+ case NANOARROW_TYPE_LARGE_LIST_VIEW:
+ case NANOARROW_TYPE_FIXED_SIZE_LIST:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int ArrowArrayCanAppendStorageType(enum ArrowType dst_type,
+ enum ArrowType src_type) {
+ if (src_type == NANOARROW_TYPE_NA) {
+ return 1;
+ }
+
+ if (ArrowTypeIsSignedInteger(src_type) ||
ArrowTypeIsUnsignedInteger(src_type)) {
+ return ArrowTypeIsSignedInteger(dst_type) ||
ArrowTypeIsUnsignedInteger(dst_type) ||
+ ArrowTypeIsFloatingPoint(dst_type);
+ }
+
+ if (ArrowTypeIsFloatingPoint(src_type)) {
+ return ArrowTypeIsFloatingPoint(dst_type);
+ }
+
+ if (ArrowTypeIsBinaryLike(src_type)) {
+ return ArrowTypeIsBinaryLike(dst_type);
+ }
+
+ if (ArrowTypeIsListLike(src_type)) {
+ return ArrowTypeIsListLike(dst_type);
+ }
+
+ return dst_type == src_type;
+}
+
+static int ArrowArrayAppendNullMayReachRunEndEncoded(struct ArrowArray* dst) {
+ struct ArrowArrayPrivateData* private_data =
+ (struct ArrowArrayPrivateData*)dst->private_data;
+
+ switch (private_data->storage_type) {
+ case NANOARROW_TYPE_RUN_END_ENCODED:
+ return 1;
+ case NANOARROW_TYPE_FIXED_SIZE_LIST:
+ return private_data->layout.child_size_elements > 0 &&
+ ArrowArrayAppendNullMayReachRunEndEncoded(dst->children[0]);
+ case NANOARROW_TYPE_STRUCT:
+ case NANOARROW_TYPE_SPARSE_UNION:
+ for (int64_t i = 0; i < dst->n_children; i++) {
+ if (ArrowArrayAppendNullMayReachRunEndEncoded(dst->children[i])) {
+ return 1;
+ }
+ }
+ return 0;
+ case NANOARROW_TYPE_DENSE_UNION:
+ return ArrowArrayAppendNullMayReachRunEndEncoded(dst->children[0]);
+ default:
+ return 0;
+ }
+}
+
+static int ArrowArrayViewHasNulls(const struct ArrowArrayView* src) {
+ if (src->length == 0) {
+ return 0;
+ }
+
+ if (src->storage_type == NANOARROW_TYPE_NA) {
+ return 1;
+ }
+
+ if (src->layout.buffer_type[0] != NANOARROW_BUFFER_TYPE_VALIDITY) {
+ return 0;
+ }
+
+ const uint8_t* validity = src->buffer_views[0].data.as_uint8;
+ return validity != NULL &&
+ ArrowBitCountSet(validity, src->offset, src->length) != src->length;
+}
+
+static ArrowErrorCode ArrowArrayCheckCanAppendStorageFromArrayView(
+ struct ArrowArray* dst, const struct ArrowArrayView* src, struct
ArrowError* error) {
+ if (src->offset < 0 || src->length < 0 || src->offset > INT64_MAX -
src->length) {
+ ArrowErrorSet(error,
+ "Expected source offset and length to describe a valid int64
"
+ "range but found %" PRId64 " and %" PRId64,
+ src->offset, src->length);
+ return EINVAL;
+ }
+
+ if (!ArrowArrayIsInternal(dst)) {
+ ArrowErrorSet(error, "Expected destination to be an internal ArrowArray");
+ return EINVAL;
+ }
+
+ struct ArrowArrayPrivateData* private_data =
+ (struct ArrowArrayPrivateData*)dst->private_data;
+ enum ArrowType dst_type = private_data->storage_type;
+
+ if (src->storage_type == NANOARROW_TYPE_DENSE_UNION ||
+ src->storage_type == NANOARROW_TYPE_SPARSE_UNION) {
+ ArrowErrorSet(error, "Appending array views is not supported for %s",
+ ArrowTypeString(src->storage_type));
+ return ENOTSUP;
+ }
+
+ if (!ArrowArrayCanAppendStorageType(dst_type, src->storage_type)) {
+ ArrowErrorSet(error, "Can't append %s storage to an array with %s storage",
+ ArrowTypeString(src->storage_type),
ArrowTypeString(dst_type));
+ return EINVAL;
+ }
+
+ if ((src->dictionary == NULL) != (dst->dictionary == NULL)) {
+ ArrowErrorSet(error,
+ "Can't append storage when exactly one of source and
destination "
+ "is dictionary-encoded");
+ return EINVAL;
+ }
+
+ if (src->storage_type == NANOARROW_TYPE_NA) {
+ return NANOARROW_OK;
+ }
+
+ if (src->n_children != dst->n_children) {
+ ArrowErrorSet(error,
+ "Expected source and destination to have the same number of "
+ "children but found %" PRId64 " and %" PRId64,
+ src->n_children, dst->n_children);
+ return EINVAL;
+ }
+
+ if (src->storage_type == NANOARROW_TYPE_FIXED_SIZE_LIST &&
+ dst_type == NANOARROW_TYPE_FIXED_SIZE_LIST &&
+ src->layout.child_size_elements !=
private_data->layout.child_size_elements) {
+ ArrowErrorSet(error,
+ "Expected source and destination fixed-size list child sizes
to "
+ "match but found %" PRId64 " and %" PRId64,
+ src->layout.child_size_elements,
+ private_data->layout.child_size_elements);
+ return EINVAL;
+ }
+
+ if (src->storage_type == NANOARROW_TYPE_FIXED_SIZE_BINARY &&
+ dst_type == NANOARROW_TYPE_FIXED_SIZE_BINARY &&
+ src->layout.element_size_bits[1] !=
private_data->layout.element_size_bits[1]) {
+ ArrowErrorSet(error,
+ "Expected source and destination fixed-size binary widths to
"
+ "match but found %" PRId64 " and %" PRId64 " bits",
+ src->layout.element_size_bits[1],
+ private_data->layout.element_size_bits[1]);
+ return EINVAL;
+ }
+
+ for (int64_t i = 0; i < src->n_children; i++) {
+ NANOARROW_RETURN_NOT_OK(ArrowArrayCheckCanAppendStorageFromArrayView(
+ dst->children[i], src->children[i], error));
+ }
+
+ return NANOARROW_OK;
+}
+
+static ArrowErrorCode ArrowArrayAppendStorageFromArrayViewRange(
+ struct ArrowArray* dst, const struct ArrowArrayView* src, int64_t offset,
+ int64_t length, struct ArrowError* error);
+
+static ArrowErrorCode ArrowArrayAppendStorageFromArrayViewElement(
+ struct ArrowArray* dst, const struct ArrowArrayView* src, int64_t i,
+ struct ArrowError* error) {
+ if (ArrowArrayViewIsNull(src, i)) {
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowArrayAppendNull(dst, 1), error);
+ return NANOARROW_OK;
+ }
+
+ switch (src->storage_type) {
+ case NANOARROW_TYPE_NA:
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowArrayAppendNull(dst, 1), error);
+ return NANOARROW_OK;
+ case NANOARROW_TYPE_BOOL:
+ case NANOARROW_TYPE_INT8:
+ case NANOARROW_TYPE_INT16:
+ case NANOARROW_TYPE_INT32:
+ case NANOARROW_TYPE_INT64:
+ case NANOARROW_TYPE_DATE32:
+ case NANOARROW_TYPE_DATE64:
+ case NANOARROW_TYPE_TIMESTAMP:
+ case NANOARROW_TYPE_TIME32:
+ case NANOARROW_TYPE_TIME64:
+ case NANOARROW_TYPE_DURATION:
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(
+ ArrowArrayAppendInt(dst, ArrowArrayViewGetIntUnsafe(src, i)), error);
+ return NANOARROW_OK;
+ case NANOARROW_TYPE_UINT8:
+ case NANOARROW_TYPE_UINT16:
+ case NANOARROW_TYPE_UINT32:
+ case NANOARROW_TYPE_UINT64:
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(
+ ArrowArrayAppendUInt(dst, ArrowArrayViewGetUIntUnsafe(src, i)),
error);
+ return NANOARROW_OK;
+ case NANOARROW_TYPE_HALF_FLOAT:
+ case NANOARROW_TYPE_FLOAT:
+ case NANOARROW_TYPE_DOUBLE:
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(
+ ArrowArrayAppendDouble(dst, ArrowArrayViewGetDoubleUnsafe(src, i)),
error);
+ return NANOARROW_OK;
+ case NANOARROW_TYPE_STRING:
+ case NANOARROW_TYPE_BINARY:
+ case NANOARROW_TYPE_FIXED_SIZE_BINARY:
+ case NANOARROW_TYPE_LARGE_STRING:
+ case NANOARROW_TYPE_LARGE_BINARY:
+ case NANOARROW_TYPE_BINARY_VIEW:
+ case NANOARROW_TYPE_STRING_VIEW:
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(
+ ArrowArrayAppendBytes(dst, ArrowArrayViewGetBytesUnsafe(src, i)),
error);
+ return NANOARROW_OK;
+ case NANOARROW_TYPE_INTERVAL_MONTHS:
+ case NANOARROW_TYPE_INTERVAL_DAY_TIME:
+ case NANOARROW_TYPE_INTERVAL_MONTH_DAY_NANO: {
+ struct ArrowInterval interval;
+ ArrowIntervalInit(&interval, src->storage_type);
+ ArrowArrayViewGetIntervalUnsafe(src, i, &interval);
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowArrayAppendInterval(dst,
&interval), error);
+ return NANOARROW_OK;
+ }
+ case NANOARROW_TYPE_STRUCT:
+ for (int64_t child_i = 0; child_i < src->n_children; child_i++) {
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendStorageFromArrayViewRange(
+ dst->children[child_i], src->children[child_i], src->offset + i,
1, error));
+ }
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowArrayFinishElement(dst), error);
+ return NANOARROW_OK;
+ case NANOARROW_TYPE_LIST:
+ case NANOARROW_TYPE_LARGE_LIST:
+ case NANOARROW_TYPE_MAP:
+ case NANOARROW_TYPE_LIST_VIEW:
+ case NANOARROW_TYPE_LARGE_LIST_VIEW: {
+ int64_t logical_i = src->offset + i;
+ int64_t child_offset = ArrowArrayViewListChildOffset(src, logical_i);
+ int64_t child_length;
+ if (src->storage_type == NANOARROW_TYPE_LIST_VIEW) {
+ child_length = src->buffer_views[2].data.as_int32[logical_i];
+ } else if (src->storage_type == NANOARROW_TYPE_LARGE_LIST_VIEW) {
+ child_length = src->buffer_views[2].data.as_int64[logical_i];
+ } else {
+ child_length = ArrowArrayViewListChildOffset(src, logical_i + 1) -
child_offset;
+ }
+
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendStorageFromArrayViewRange(
+ dst->children[0], src->children[0], child_offset, child_length,
error));
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowArrayFinishElement(dst), error);
+ return NANOARROW_OK;
+ }
+ case NANOARROW_TYPE_FIXED_SIZE_LIST: {
+ int64_t child_offset = (src->offset + i) *
src->layout.child_size_elements;
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendStorageFromArrayViewRange(
+ dst->children[0], src->children[0], child_offset,
+ src->layout.child_size_elements, error));
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowArrayFinishElement(dst), error);
+ return NANOARROW_OK;
+ }
+ case NANOARROW_TYPE_DECIMAL32:
+ case NANOARROW_TYPE_DECIMAL64:
+ case NANOARROW_TYPE_DECIMAL128:
+ case NANOARROW_TYPE_DECIMAL256:
+ case NANOARROW_TYPE_DENSE_UNION:
+ case NANOARROW_TYPE_SPARSE_UNION:
+ case NANOARROW_TYPE_RUN_END_ENCODED:
+ case NANOARROW_TYPE_UNINITIALIZED:
+ default:
+ ArrowErrorSet(error, "Appending array views is not supported for %s",
+ ArrowTypeString(src->storage_type));
+ return ENOTSUP;
+ }
+}
+
+static int ArrowArrayCanAppendFixedWidthStorage(struct ArrowArray* dst,
+ const struct ArrowArrayView*
src) {
+ struct ArrowArrayPrivateData* private_data =
+ (struct ArrowArrayPrivateData*)dst->private_data;
+ return private_data->storage_type == src->storage_type && dst->n_buffers ==
2 &&
+ src->n_children == 0 &&
+ src->layout.buffer_type[1] == NANOARROW_BUFFER_TYPE_DATA &&
+ src->layout.element_size_bits[1] > 0 &&
+ src->layout.element_size_bits[1] % 8 == 0;
+}
+
+static int ArrowArrayCanAppendStructStorage(struct ArrowArray* dst,
+ const struct ArrowArrayView* src) {
+ struct ArrowArrayPrivateData* private_data =
+ (struct ArrowArrayPrivateData*)dst->private_data;
+ return private_data->storage_type == NANOARROW_TYPE_STRUCT &&
+ src->storage_type == NANOARROW_TYPE_STRUCT;
+}
+
+static ArrowErrorCode ArrowArrayAppendValidityFromArrayView(
+ struct ArrowArray* dst, const struct ArrowArrayView* src, struct
ArrowError* error) {
+ struct ArrowBitmap* dst_validity = ArrowArrayValidityBitmap(dst);
+ const uint8_t* src_validity = src->buffer_views[0].data.as_uint8;
+ if (src_validity == NULL && dst_validity->buffer.data == NULL) {
+ return NANOARROW_OK;
+ }
+
+ if (dst_validity->buffer.data == NULL) {
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowBitmapAppend(dst_validity, 1,
dst->length),
+ error);
+ }
+
+ if (src_validity == NULL) {
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowBitmapAppend(dst_validity, 1,
src->length),
+ error);
+ return NANOARROW_OK;
+ }
+
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(ArrowBitmapReserve(dst_validity,
src->length),
+ error);
+ int8_t validity[1024] = {0};
+ for (int64_t offset = 0; offset < src->length;) {
+ int64_t remaining = src->length - offset;
+ int64_t chunk_size =
+ remaining < (int64_t)sizeof(validity) ? remaining :
(int64_t)sizeof(validity);
+ ArrowBitsUnpackInt8(src_validity, src->offset + offset, chunk_size,
validity);
+ ArrowBitmapAppendInt8Unsafe(dst_validity, validity, chunk_size);
+ offset += chunk_size;
+ }
+
+ return NANOARROW_OK;
+}
+
+static ArrowErrorCode ArrowArrayAppendFixedWidthStorageFromArrayView(
+ struct ArrowArray* dst, const struct ArrowArrayView* src, struct
ArrowError* error) {
+ if (src->length == 0) {
+ return NANOARROW_OK;
+ }
+
+ int64_t element_size_bytes = src->layout.element_size_bits[1] / 8;
+ if (src->offset > INT64_MAX / element_size_bytes ||
+ src->length > INT64_MAX / element_size_bytes) {
+ ArrowErrorSet(error,
+ "Expected fixed-width append size to fit in int64 but found "
+ "element size %" PRId64 ", offset %" PRId64 ", and length %"
PRId64,
+ element_size_bytes, src->offset, src->length);
+ return EOVERFLOW;
+ }
+
+ int64_t src_offset_bytes = src->offset * element_size_bytes;
+ int64_t src_size_bytes = src->length * element_size_bytes;
+ if (src->buffer_views[1].data.as_uint8 == NULL ||
+ src_offset_bytes > src->buffer_views[1].size_bytes ||
+ src_size_bytes > src->buffer_views[1].size_bytes - src_offset_bytes) {
+ ArrowErrorSet(error,
+ "Expected fixed-width source buffer to contain %" PRId64
+ " bytes at offset %" PRId64 " but its size is %" PRId64,
+ src_size_bytes, src_offset_bytes,
src->buffer_views[1].size_bytes);
+ return EINVAL;
+ }
+
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendValidityFromArrayView(dst, src,
error));
+
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(
+ ArrowBufferAppend(ArrowArrayBuffer(dst, 1),
+ src->buffer_views[1].data.as_uint8 + src_offset_bytes,
+ src_size_bytes),
+ error);
+
+ if (src->buffer_views[0].data.as_uint8 != NULL) {
+ dst->null_count += src->length -
ArrowBitCountSet(src->buffer_views[0].data.as_uint8,
+ src->offset,
src->length);
+ }
+ dst->length += src->length;
+ return NANOARROW_OK;
+}
+
+static ArrowErrorCode ArrowArrayAppendStructStorageFromArrayView(
+ struct ArrowArray* dst, const struct ArrowArrayView* src, struct
ArrowError* error) {
+ for (int64_t i = 0; i < src->n_children; i++) {
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendStorageFromArrayViewRange(
+ dst->children[i], src->children[i], src->offset, src->length, error));
+ }
+
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendValidityFromArrayView(dst, src,
error));
+ if (src->buffer_views[0].data.as_uint8 != NULL) {
+ dst->null_count += src->length -
ArrowBitCountSet(src->buffer_views[0].data.as_uint8,
+ src->offset,
src->length);
+ }
+ dst->length += src->length;
+ return NANOARROW_OK;
+}
+
+static int64_t ArrowArrayViewResolveRun(const struct ArrowArrayView* run_ends,
+ int64_t logical_offset) {
+ if (run_ends->length <= 1) {
+ return 0;
+ }
+
+ switch (run_ends->storage_type) {
+ case NANOARROW_TYPE_INT32:
+ return ArrowResolveChunk32(
+ (int32_t)(logical_offset + 1),
+ run_ends->buffer_views[1].data.as_int32 + run_ends->offset, 0,
+ (int32_t)run_ends->length);
+ case NANOARROW_TYPE_INT64:
+ return ArrowResolveChunk64(
+ logical_offset + 1, run_ends->buffer_views[1].data.as_int64 +
run_ends->offset,
+ 0, run_ends->length);
+ case NANOARROW_TYPE_INT16:
+ return ArrowResolveChunk16(
+ (int16_t)(logical_offset + 1),
+ run_ends->buffer_views[1].data.as_int16 + run_ends->offset, 0,
+ (int16_t)run_ends->length);
+ default:
+ return 0;
+ }
+}
+
+static ArrowErrorCode ArrowArrayAppendStorageFromArrayViewRange(
+ struct ArrowArray* dst, const struct ArrowArrayView* src, int64_t offset,
+ int64_t length, struct ArrowError* error) {
+ // Note: src_slice must not be freed; it is a convenience to avoid repeatedly
+ // composing offset and length on top of src in internal calls.
+ struct ArrowArrayView src_slice = *src;
+ if (offset < 0 || src->offset < 0 || offset > INT64_MAX - src->offset) {
+ ArrowErrorSet(error,
+ "Expected source offset and range offset to describe a valid
"
+ "int64 offset but found %" PRId64 " and %" PRId64,
+ src->offset, offset);
+ return EINVAL;
+ }
+
+ src_slice.offset = src->offset + offset;
+ src_slice.length = length;
+ src = &src_slice;
+
+ NANOARROW_RETURN_NOT_OK(ArrowArrayCheckCanAppendStorageFromArrayView(dst,
src, error));
+
+ struct ArrowArrayPrivateData* private_data =
+ (struct ArrowArrayPrivateData*)dst->private_data;
+ // Exact struct-to-struct appends copy child storage in bulk, including
storage
+ // beneath null parent slots. Other null appends use ArrowArrayAppendNull(),
whose
+ // empty child values cannot represent run-end encoded storage.
+ if (!(src->storage_type == NANOARROW_TYPE_STRUCT &&
+ private_data->storage_type == NANOARROW_TYPE_STRUCT) &&
+ ArrowArrayAppendNullMayReachRunEndEncoded(dst) &&
ArrowArrayViewHasNulls(src)) {
+ ArrowErrorSet(error,
+ "Can't append null storage to an array whose null
representation "
+ "requires appending to run-end encoded storage");
+ return EINVAL;
+ }
+
+ if (ArrowArrayCanAppendFixedWidthStorage(dst, src)) {
+ return ArrowArrayAppendFixedWidthStorageFromArrayView(dst, src, error);
+ }
+
+ if (ArrowArrayCanAppendStructStorage(dst, src)) {
+ return ArrowArrayAppendStructStorageFromArrayView(dst, src, error);
+ }
+
+ if (src->storage_type == NANOARROW_TYPE_RUN_END_ENCODED) {
+ if (src->length == 0) {
+ return NANOARROW_OK;
+ }
+
+ const struct ArrowArrayView* run_ends = src->children[0];
+ if (run_ends->length == 0) {
+ ArrowErrorSet(error,
+ "Expected a non-empty run ends array for a non-empty "
+ "run-end encoded array");
+ return EINVAL;
+ }
+
+ if (dst->length > INT64_MAX - src->length) {
+ ArrowErrorSet(error,
+ "Expected run-end encoded destination length plus source
length "
+ "to fit in int64 but found %" PRId64 " and %" PRId64,
+ dst->length, src->length);
+ return EOVERFLOW;
+ }
+
+ int64_t first_run = ArrowArrayViewResolveRun(run_ends, src->offset);
+ int64_t slice_end = src->offset + src->length;
+ int64_t run_end_offset = dst->length;
+ for (int64_t i = first_run; i < run_ends->length; i++) {
+ int64_t src_run_end = ArrowArrayViewGetIntUnsafe(run_ends, i);
+ int64_t clipped_run_end = src_run_end < slice_end ? src_run_end :
slice_end;
+ int64_t dst_run_end = run_end_offset + clipped_run_end - src->offset;
+ NANOARROW_RETURN_NOT_OK_WITH_ERROR(
+ ArrowArrayAppendInt(dst->children[0], dst_run_end), error);
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendStorageFromArrayViewRange(
+ dst->children[1], src->children[1], i, 1, error));
+ if (src_run_end >= slice_end) {
+ break;
+ }
+ }
+ dst->length += src->length;
+ return NANOARROW_OK;
+ }
+
+ for (int64_t i = 0; i < src->length; i++) {
+ NANOARROW_RETURN_NOT_OK(
+ ArrowArrayAppendStorageFromArrayViewElement(dst, src, i, error));
+ }
+ return NANOARROW_OK;
+}
+
+ArrowErrorCode ArrowArrayAppendStorageFromArrayView(struct ArrowArray* dst,
+ const struct
ArrowArrayView* src,
+ struct ArrowError* error) {
+ return ArrowArrayAppendStorageFromArrayViewRange(dst, src, 0, src->length,
error);
+}
+
static ArrowErrorCode ArrowArraySetStorageType(struct ArrowArray* array,
enum ArrowType storage_type) {
switch (storage_type) {
diff --git a/src/nanoarrow/common/array_test.cc
b/src/nanoarrow/common/array_test.cc
index bc4686c2..4bb3cd95 100644
--- a/src/nanoarrow/common/array_test.cc
+++ b/src/nanoarrow/common/array_test.cc
@@ -33,6 +33,7 @@
#include <arrow/c/bridge.h>
#include <arrow/compare.h>
#include <arrow/config.h>
+#include <arrow/scalar.h>
#include <arrow/util/decimal.h>
#if defined(NANOARROW_BUILD_TESTS_WITH_ARROW) && defined(ARROW_VERSION_MAJOR)
&& \
@@ -5087,3 +5088,915 @@ TEST(ArrayMoveSharedTest, ArrayWithNullBuffers) {
ArrowArrayRelease(&shared);
}
+
+static ArrowErrorCode AppendStorageFromArrayViewForTest(const struct
ArrowArrayView* src,
+ struct ArrowArray* dst,
+ struct ArrowError*
error) {
+ NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromArrayView(dst, src, error));
+ NANOARROW_RETURN_NOT_OK(ArrowArrayStartAppending(dst));
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendStorageFromArrayView(dst, src,
error));
+ return ArrowArrayFinishBuildingDefault(dst, error);
+}
+
+static void ExpectPrimitiveStorageAppendIdentical(struct ArrowArray* src,
+ enum ArrowType type) {
+ struct ArrowError error;
+ struct ArrowArrayView src_view;
+ ArrowArrayViewInitFromType(&src_view, type);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, src, &error), NANOARROW_OK)
+ << error.message;
+
+ struct ArrowArray dst;
+ ASSERT_EQ(AppendStorageFromArrayViewForTest(&src_view, &dst, &error),
NANOARROW_OK)
+ << error.message;
+ struct ArrowArrayView dst_view;
+ ArrowArrayViewInitFromType(&dst_view, type);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK)
+ << error.message;
+
+ int identical = 0;
+ ASSERT_EQ(ArrowArrayViewCompare(&src_view, &dst_view,
NANOARROW_COMPARE_IDENTICAL,
+ &identical, &error),
+ NANOARROW_OK);
+ EXPECT_EQ(identical, 1) << error.message;
+
+ ArrowArrayViewReset(&dst_view);
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewPrimitiveTypes) {
+ struct ArrowArray array;
+
+ ASSERT_EQ(ArrowArrayInitFromType(&array, NANOARROW_TYPE_INT64),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&array), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&array, -42), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendNull(&array, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&array, nullptr), NANOARROW_OK);
+ ExpectPrimitiveStorageAppendIdentical(&array, NANOARROW_TYPE_INT64);
+ ArrowArrayRelease(&array);
+
+ ASSERT_EQ(ArrowArrayInitFromType(&array, NANOARROW_TYPE_UINT64),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&array), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendUInt(&array, UINT64_MAX), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&array, nullptr), NANOARROW_OK);
+ ExpectPrimitiveStorageAppendIdentical(&array, NANOARROW_TYPE_UINT64);
+ ArrowArrayRelease(&array);
+
+ ASSERT_EQ(ArrowArrayInitFromType(&array, NANOARROW_TYPE_DOUBLE),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&array), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendDouble(&array, 1.25), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&array, nullptr), NANOARROW_OK);
+ ExpectPrimitiveStorageAppendIdentical(&array, NANOARROW_TYPE_DOUBLE);
+ ArrowArrayRelease(&array);
+
+ ASSERT_EQ(ArrowArrayInitFromType(&array, NANOARROW_TYPE_STRING),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&array), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(&array, "some value"_asv), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&array, nullptr), NANOARROW_OK);
+ ExpectPrimitiveStorageAppendIdentical(&array, NANOARROW_TYPE_STRING);
+ ArrowArrayRelease(&array);
+
+ ASSERT_EQ(ArrowArrayInitFromType(&array, NANOARROW_TYPE_INTERVAL_MONTHS),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&array), NANOARROW_OK);
+ struct ArrowInterval interval;
+ ArrowIntervalInit(&interval, NANOARROW_TYPE_INTERVAL_MONTHS);
+ interval.months = 42;
+ ASSERT_EQ(ArrowArrayAppendInterval(&array, &interval), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&array, nullptr), NANOARROW_OK);
+ ExpectPrimitiveStorageAppendIdentical(&array,
NANOARROW_TYPE_INTERVAL_MONTHS);
+ ArrowArrayRelease(&array);
+
+ ASSERT_EQ(ArrowArrayInitFromType(&array, NANOARROW_TYPE_DECIMAL128),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&array), NANOARROW_OK);
+ struct ArrowDecimal decimal;
+ ArrowDecimalInit(&decimal, 128, 10, 2);
+ ArrowDecimalSetInt(&decimal, 1234);
+ ASSERT_EQ(ArrowArrayAppendDecimal(&array, &decimal), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&array, nullptr), NANOARROW_OK);
+ ExpectPrimitiveStorageAppendIdentical(&array, NANOARROW_TYPE_DECIMAL128);
+ ArrowArrayRelease(&array);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewNestedAndSliced) {
+ struct ArrowError error;
+ struct ArrowSchema schema;
+ ArrowSchemaInit(&schema);
+ ASSERT_EQ(ArrowSchemaSetTypeStruct(&schema, 2), NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[0], NANOARROW_TYPE_LIST),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[0]->children[0],
NANOARROW_TYPE_INT32),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[1], NANOARROW_TYPE_STRING),
NANOARROW_OK);
+
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&src, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(src.children[0]->children[0], 1),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(src.children[0]->children[0], 2),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishElement(src.children[0]), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(src.children[1], "first"_asv),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishElement(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendNull(&src, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishElement(src.children[0]), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(src.children[1], "third"_asv),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishElement(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK) <<
error.message;
+
+ struct ArrowArrayView src_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+ src_view.offset = 1;
+ src_view.length = 2;
+ src_view.null_count = 1;
+
+ struct ArrowArray dst;
+ ASSERT_EQ(AppendStorageFromArrayViewForTest(&src_view, &dst, &error),
NANOARROW_OK)
+ << error.message;
+ EXPECT_EQ(dst.length, 2);
+ EXPECT_EQ(dst.children[0]->length, 2);
+ EXPECT_EQ(dst.children[0]->children[0]->length, 0);
+ EXPECT_EQ(dst.children[1]->length, 2);
+
+ struct ArrowArrayView dst_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&dst_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK);
+ EXPECT_TRUE(ArrowArrayViewIsNull(&dst_view, 0));
+ EXPECT_EQ(ArrowArrayViewGetStringUnsafe(dst_view.children[1], 1),
"third"_asv);
+
+ ArrowArrayViewReset(&dst_view);
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+ ArrowSchemaRelease(&schema);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewNestedDecimal) {
+ struct ArrowError error;
+ struct ArrowSchema schema;
+ ArrowSchemaInit(&schema);
+ ASSERT_EQ(ArrowSchemaSetTypeStruct(&schema, 2), NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetName(schema.children[0], "scalar"), NANOARROW_OK);
+ ASSERT_EQ(
+ ArrowSchemaSetTypeDecimal(schema.children[0], NANOARROW_TYPE_DECIMAL128,
10, 2),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetName(schema.children[1], "values"), NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[1], NANOARROW_TYPE_LIST),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetTypeDecimal(schema.children[1]->children[0],
+ NANOARROW_TYPE_DECIMAL128, 10, 2),
+ NANOARROW_OK);
+
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&src, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ struct ArrowDecimal decimal;
+ ArrowDecimalInit(&decimal, 128, 10, 2);
+ ArrowDecimalSetInt(&decimal, 1234);
+ ASSERT_EQ(ArrowArrayAppendDecimal(src.children[0], &decimal), NANOARROW_OK);
+ ArrowDecimalSetInt(&decimal, 5678);
+ ASSERT_EQ(ArrowArrayAppendDecimal(src.children[1]->children[0], &decimal),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishElement(src.children[1]), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishElement(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK) <<
error.message;
+
+ struct ArrowArrayView src_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+ struct ArrowArray dst;
+ ASSERT_EQ(AppendStorageFromArrayViewForTest(&src_view, &dst, &error),
NANOARROW_OK)
+ << error.message;
+
+ struct ArrowArrayView dst_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&dst_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK);
+ int identical = 0;
+ ASSERT_EQ(ArrowArrayViewCompare(&src_view, &dst_view,
NANOARROW_COMPARE_IDENTICAL,
+ &identical, &error),
+ NANOARROW_OK);
+ EXPECT_EQ(identical, 1) << error.message;
+
+ ArrowArrayViewReset(&dst_view);
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+ ArrowSchemaRelease(&schema);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewRejectsUnions) {
+ for (enum ArrowType union_type :
+ {NANOARROW_TYPE_DENSE_UNION, NANOARROW_TYPE_SPARSE_UNION}) {
+ struct ArrowError error;
+ struct ArrowSchema schema;
+ ArrowSchemaInit(&schema);
+ ASSERT_EQ(ArrowSchemaSetTypeUnion(&schema, union_type, 2), NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[0], NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[1], NANOARROW_TYPE_STRING),
+ NANOARROW_OK);
+
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&src, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(src.children[0], 42), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishUnionElement(&src, 0), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(src.children[1], "value"_asv),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishUnionElement(&src, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK);
+
+ struct ArrowArrayView src_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+ struct ArrowArray dst;
+ ASSERT_EQ(AppendStorageFromArrayViewForTest(&src_view, &dst, &error),
ENOTSUP);
+ EXPECT_EQ(dst.length, 0);
+ EXPECT_EQ(std::string(error.message), "Appending array views is not
supported for " +
+
std::string(ArrowTypeString(union_type)));
+
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+ ArrowSchemaRelease(&schema);
+ }
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewRunEndEncoded) {
+ for (enum ArrowType run_end_type :
+ {NANOARROW_TYPE_INT16, NANOARROW_TYPE_INT32, NANOARROW_TYPE_INT64}) {
+ SCOPED_TRACE(ArrowTypeString(run_end_type));
+ struct ArrowError error;
+ struct ArrowSchema schema;
+ ArrowSchemaInit(&schema);
+ ASSERT_EQ(ArrowSchemaSetTypeRunEndEncoded(&schema, run_end_type),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[1], NANOARROW_TYPE_STRING),
+ NANOARROW_OK);
+
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&src, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(src.children[0], 2), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(src.children[0], 3), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(src.children[1], "a"_asv), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(src.children[1], "b"_asv), NANOARROW_OK);
+ src.length = 3;
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK);
+
+ struct ArrowArrayView src_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+ struct ArrowArray dst;
+ ASSERT_EQ(ArrowArrayInitFromArrayView(&dst, &src_view, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&dst, &error), NANOARROW_OK)
+ << error.message;
+ EXPECT_EQ(dst.length, 6);
+
+ struct ArrowArrayView dst_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&dst_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(dst_view.children[0], 2), 5);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(dst_view.children[0], 3), 6);
+ ArrowArrayViewReset(&dst_view);
+
+ src_view.offset = 1;
+ src_view.length = 1;
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
NANOARROW_OK)
+ << error.message;
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&dst, &error), NANOARROW_OK)
+ << error.message;
+ EXPECT_EQ(dst.length, 7);
+ EXPECT_EQ(dst.children[0]->length, 5);
+ EXPECT_EQ(dst.children[1]->length, 5);
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&dst_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(dst_view.children[0], 4), 7);
+ EXPECT_EQ(ArrowArrayViewGetStringUnsafe(dst_view.children[1], 4), "a"_asv);
+
+ ArrowArrayViewReset(&dst_view);
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+ ArrowSchemaRelease(&schema);
+ }
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewRunEndEncodedLengthOverflow) {
+ struct ArrowError error;
+ struct ArrowSchema schema;
+ ArrowSchemaInit(&schema);
+ ASSERT_EQ(ArrowSchemaSetTypeRunEndEncoded(&schema, NANOARROW_TYPE_INT64),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[1], NANOARROW_TYPE_INT32),
NANOARROW_OK);
+
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&src, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(src.children[0], 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(src.children[1], 42), NANOARROW_OK);
+ src.length = 1;
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK);
+
+ struct ArrowArrayView src_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+
+ struct ArrowArray dst;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&dst, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+ dst.length = INT64_MAX;
+
+ EXPECT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
EOVERFLOW);
+ EXPECT_STREQ(error.message,
+ "Expected run-end encoded destination length plus source length
to fit "
+ "in int64 but found 9223372036854775807 and 1");
+ EXPECT_EQ(dst.children[0]->length, 0);
+ EXPECT_EQ(dst.children[1]->length, 0);
+
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+ ArrowSchemaRelease(&schema);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewStructWithRunEndEncodedChild) {
+ struct ArrowError error;
+ struct ArrowSchema schema;
+ ArrowSchemaInit(&schema);
+ ASSERT_EQ(ArrowSchemaSetTypeStruct(&schema, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetTypeRunEndEncoded(schema.children[0],
NANOARROW_TYPE_INT32),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[0]->children[1],
NANOARROW_TYPE_STRING),
+ NANOARROW_OK);
+
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&src, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendNull(&src, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(src.children[0]->children[0], 1),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(src.children[0]->children[1], "value"_asv),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK) <<
error.message;
+
+ struct ArrowArrayView src_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+
+ struct ArrowArray dst;
+ ASSERT_EQ(AppendStorageFromArrayViewForTest(&src_view, &dst, &error),
NANOARROW_OK)
+ << error.message;
+
+ struct ArrowArrayView dst_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&dst_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK);
+ EXPECT_TRUE(ArrowArrayViewIsNull(&dst_view, 0));
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(dst_view.children[0]->children[0], 0),
1);
+ EXPECT_EQ(ArrowArrayViewGetStringUnsafe(dst_view.children[0]->children[1],
0),
+ "value"_asv);
+
+ ArrowArrayViewReset(&dst_view);
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+ ArrowSchemaRelease(&schema);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewRejectsNullToRunEndEncoded) {
+ struct ArrowError error;
+ struct ArrowArrayView src_view;
+ ArrowArrayViewInitFromType(&src_view, NANOARROW_TYPE_NA);
+ src_view.length = 1;
+
+ struct ArrowSchema schema;
+ ArrowSchemaInit(&schema);
+ ASSERT_EQ(ArrowSchemaSetTypeRunEndEncoded(&schema, NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[1], NANOARROW_TYPE_STRING),
NANOARROW_OK);
+ struct ArrowArray dst;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&dst, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+
+ EXPECT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
EINVAL);
+ EXPECT_STREQ(error.message,
+ "Can't append null storage to an array whose null
representation "
+ "requires appending to run-end encoded storage");
+ EXPECT_EQ(dst.length, 0);
+ EXPECT_EQ(dst.children[0]->length, 0);
+ EXPECT_EQ(dst.children[1]->length, 0);
+
+ ArrowArrayRelease(&dst);
+ ArrowSchemaRelease(&schema);
+ ArrowArrayViewReset(&src_view);
+}
+
+TEST(ArrayTest,
ArrayAppendStorageFromArrayViewRejectsNullToNestedRunEndEncoded) {
+ struct ArrowError error;
+ struct ArrowArrayView src_view;
+ ArrowArrayViewInitFromType(&src_view, NANOARROW_TYPE_NA);
+ src_view.length = 1;
+
+ struct ArrowSchema schema;
+ ArrowSchemaInit(&schema);
+ ASSERT_EQ(ArrowSchemaSetTypeStruct(&schema, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetTypeRunEndEncoded(schema.children[0],
NANOARROW_TYPE_INT32),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(schema.children[0]->children[1],
NANOARROW_TYPE_STRING),
+ NANOARROW_OK);
+
+ struct ArrowArray dst;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&dst, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+
+ EXPECT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
EINVAL);
+ EXPECT_STREQ(error.message,
+ "Can't append null storage to an array whose null
representation "
+ "requires appending to run-end encoded storage");
+ EXPECT_EQ(dst.length, 0);
+ EXPECT_EQ(dst.children[0]->length, 0);
+ EXPECT_EQ(dst.children[0]->children[0]->length, 0);
+ EXPECT_EQ(dst.children[0]->children[1]->length, 0);
+
+ ArrowArrayRelease(&dst);
+ ArrowSchemaRelease(&schema);
+ ArrowArrayViewReset(&src_view);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewFixedWidthSlicedValidity) {
+ struct ArrowError error;
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromType(&src, NANOARROW_TYPE_INT32), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendNull(&src, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&src, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendNull(&src, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&src, 3), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendNull(&src, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK);
+
+ struct ArrowArrayView src_view;
+ ArrowArrayViewInitFromType(&src_view, NANOARROW_TYPE_INT32);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+ src_view.offset = 1;
+ src_view.length = 3;
+ src_view.null_count = 1;
+
+ struct ArrowArray dst;
+ ASSERT_EQ(ArrowArrayInitFromType(&dst, NANOARROW_TYPE_INT32), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&dst, 10), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
NANOARROW_OK)
+ << error.message;
+
+ struct ArrowArray all_valid;
+ ASSERT_EQ(ArrowArrayInitFromType(&all_valid, NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&all_valid), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&all_valid, 4), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&all_valid, 5), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&all_valid, &error), NANOARROW_OK);
+ struct ArrowArrayView all_valid_view;
+ ArrowArrayViewInitFromType(&all_valid_view, NANOARROW_TYPE_INT32);
+ ASSERT_EQ(ArrowArrayViewSetArray(&all_valid_view, &all_valid, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &all_valid_view,
&error),
+ NANOARROW_OK)
+ << error.message;
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
NANOARROW_OK)
+ << error.message;
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&dst, &error), NANOARROW_OK) <<
error.message;
+
+ struct ArrowArrayView dst_view;
+ ArrowArrayViewInitFromType(&dst_view, NANOARROW_TYPE_INT32);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK);
+ EXPECT_EQ(dst.length, 9);
+ EXPECT_EQ(dst.null_count, 2);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(&dst_view, 0), 10);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(&dst_view, 1), 1);
+ EXPECT_TRUE(ArrowArrayViewIsNull(&dst_view, 2));
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(&dst_view, 3), 3);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(&dst_view, 4), 4);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(&dst_view, 5), 5);
+ EXPECT_TRUE(ArrowArrayViewIsNull(&dst_view, 7));
+
+ struct ArrowArray dst_all_valid;
+ ASSERT_EQ(ArrowArrayInitFromType(&dst_all_valid, NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst_all_valid), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst_all_valid,
&all_valid_view, &error),
+ NANOARROW_OK);
+ EXPECT_EQ(dst_all_valid.buffers[0], nullptr);
+
+ ArrowArrayRelease(&dst_all_valid);
+ ArrowArrayViewReset(&dst_view);
+ ArrowArrayViewReset(&all_valid_view);
+ ArrowArrayRelease(&all_valid);
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewFixedWidthChunkedValidity) {
+ constexpr int64_t kLength = 2050;
+ struct ArrowError error;
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromType(&src, NANOARROW_TYPE_INT32), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ for (int64_t i = 0; i < kLength; i++) {
+ if (i % 3 == 0) {
+ ASSERT_EQ(ArrowArrayAppendNull(&src, 1), NANOARROW_OK);
+ } else {
+ ASSERT_EQ(ArrowArrayAppendInt(&src, i), NANOARROW_OK);
+ }
+ }
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK);
+
+ struct ArrowArrayView src_view;
+ ArrowArrayViewInitFromType(&src_view, NANOARROW_TYPE_INT32);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+
+ struct ArrowArray dst;
+ ASSERT_EQ(AppendStorageFromArrayViewForTest(&src_view, &dst, &error),
NANOARROW_OK)
+ << error.message;
+ EXPECT_EQ(dst.length, kLength);
+ EXPECT_EQ(dst.null_count, 684);
+
+ struct ArrowArrayView dst_view;
+ ArrowArrayViewInitFromType(&dst_view, NANOARROW_TYPE_INT32);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK);
+ EXPECT_TRUE(ArrowArrayViewIsNull(&dst_view, 1023));
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(&dst_view, 1024), 1024);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(&dst_view, 2048), 2048);
+ EXPECT_TRUE(ArrowArrayViewIsNull(&dst_view, 2049));
+
+ ArrowArrayViewReset(&dst_view);
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewFixedToVariableWidth) {
+ struct ArrowError error;
+
+ struct ArrowSchema fixed_binary_schema;
+ ArrowSchemaInit(&fixed_binary_schema);
+ ASSERT_EQ(ArrowSchemaSetTypeFixedSize(&fixed_binary_schema,
+ NANOARROW_TYPE_FIXED_SIZE_BINARY, 3),
+ NANOARROW_OK);
+ struct ArrowArray fixed_binary;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&fixed_binary, &fixed_binary_schema,
&error),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&fixed_binary), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendBytes(&fixed_binary, {{"abc"}, 3}), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&fixed_binary, &error),
NANOARROW_OK);
+ struct ArrowArrayView fixed_binary_view;
+ ASSERT_EQ(
+ ArrowArrayViewInitFromSchema(&fixed_binary_view, &fixed_binary_schema,
&error),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&fixed_binary_view, &fixed_binary, &error),
+ NANOARROW_OK);
+
+ struct ArrowArray binary;
+ ASSERT_EQ(ArrowArrayInitFromType(&binary, NANOARROW_TYPE_BINARY),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&binary), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&binary, &fixed_binary_view,
&error),
+ NANOARROW_OK)
+ << error.message;
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&binary, &error), NANOARROW_OK);
+ struct ArrowArrayView binary_view;
+ ArrowArrayViewInitFromType(&binary_view, NANOARROW_TYPE_BINARY);
+ ASSERT_EQ(ArrowArrayViewSetArray(&binary_view, &binary, &error),
NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayViewGetStringUnsafe(&binary_view, 0), "abc"_asv);
+
+ ArrowArrayViewReset(&binary_view);
+ ArrowArrayRelease(&binary);
+ ArrowArrayViewReset(&fixed_binary_view);
+ ArrowArrayRelease(&fixed_binary);
+ ArrowSchemaRelease(&fixed_binary_schema);
+
+ struct ArrowSchema fixed_list_schema;
+ ArrowSchemaInit(&fixed_list_schema);
+ ASSERT_EQ(
+ ArrowSchemaSetTypeFixedSize(&fixed_list_schema,
NANOARROW_TYPE_FIXED_SIZE_LIST, 2),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(fixed_list_schema.children[0],
NANOARROW_TYPE_INT32),
+ NANOARROW_OK);
+ struct ArrowArray fixed_list;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&fixed_list, &fixed_list_schema, &error),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&fixed_list), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(fixed_list.children[0], 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(fixed_list.children[0], 2), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishElement(&fixed_list), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&fixed_list, &error),
NANOARROW_OK);
+ struct ArrowArrayView fixed_list_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&fixed_list_view, &fixed_list_schema,
&error),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&fixed_list_view, &fixed_list, &error),
NANOARROW_OK);
+
+ struct ArrowSchema list_schema;
+ ASSERT_EQ(ArrowSchemaInitFromType(&list_schema, NANOARROW_TYPE_LIST),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(list_schema.children[0], NANOARROW_TYPE_INT32),
+ NANOARROW_OK);
+ struct ArrowArray list;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&list, &list_schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&list), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&list, &fixed_list_view,
&error),
+ NANOARROW_OK)
+ << error.message;
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&list, &error), NANOARROW_OK);
+ struct ArrowArrayView list_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&list_view, &list_schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&list_view, &list, &error), NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayViewListChildOffset(&list_view, 0), 0);
+ EXPECT_EQ(ArrowArrayViewListChildOffset(&list_view, 1), 2);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(list_view.children[0], 0), 1);
+ EXPECT_EQ(ArrowArrayViewGetIntUnsafe(list_view.children[0], 1), 2);
+
+ ArrowArrayViewReset(&list_view);
+ ArrowArrayRelease(&list);
+ ArrowSchemaRelease(&list_schema);
+ ArrowArrayViewReset(&fixed_list_view);
+ ArrowArrayRelease(&fixed_list);
+ ArrowSchemaRelease(&fixed_list_schema);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewErrors) {
+ struct ArrowError error;
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromType(&src, NANOARROW_TYPE_INT64), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&src, INT8_MAX + 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK);
+ struct ArrowArrayView src_view;
+ ArrowArrayViewInitFromType(&src_view, NANOARROW_TYPE_INT64);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+
+ struct ArrowArray dst;
+ ASSERT_EQ(ArrowArrayInitFromType(&dst, NANOARROW_TYPE_INT8), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
EINVAL);
+ EXPECT_NE(error.message[0], '\0');
+ EXPECT_EQ(dst.length, 0);
+
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+
+ struct ArrowSchema list_schema;
+ ASSERT_EQ(ArrowSchemaInitFromType(&list_schema, NANOARROW_TYPE_LIST),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaSetType(list_schema.children[0], NANOARROW_TYPE_NA),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayInitFromSchema(&src, &list_schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendNull(src.children[0], 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishElement(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &list_schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayInitFromSchema(&dst, &list_schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+ dst.children[0]->length = INT32_MAX;
+ EXPECT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
EOVERFLOW);
+ EXPECT_NE(error.message[0], '\0');
+
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+ ArrowSchemaRelease(&list_schema);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewDictionary) {
+ struct ArrowError error;
+ struct ArrowSchema schema;
+ ASSERT_EQ(ArrowSchemaInitFromType(&schema, NANOARROW_TYPE_INT8),
NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaAllocateDictionary(&schema), NANOARROW_OK);
+ ASSERT_EQ(ArrowSchemaInitFromType(schema.dictionary, NANOARROW_TYPE_STRING),
+ NANOARROW_OK);
+
+ struct ArrowArray src;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&src, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&src), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(src.dictionary, "a"_asv), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendString(src.dictionary, "b"_asv), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&src, 0), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&src, 1), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(&src, 0), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(&src, &error), NANOARROW_OK);
+ struct ArrowArrayView src_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src, &error), NANOARROW_OK);
+
+ struct ArrowArray dst;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&dst, &schema, &error), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+ ASSERT_EQ(
+ ArrowArrayAppendStorageFromArrayView(dst.dictionary,
src_view.dictionary, &error),
+ NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuilding(&dst, NANOARROW_VALIDATION_LEVEL_FULL,
&error),
+ NANOARROW_OK)
+ << error.message;
+
+ struct ArrowArrayView dst_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&dst_view, &schema, &error),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayViewSetArray(&dst_view, &dst, &error), NANOARROW_OK);
+ int identical = 0;
+ ASSERT_EQ(ArrowArrayViewCompare(&src_view, &dst_view,
NANOARROW_COMPARE_IDENTICAL,
+ &identical, &error),
+ NANOARROW_OK);
+ EXPECT_EQ(identical, 1) << error.message;
+
+ struct ArrowArray plain_dst;
+ ASSERT_EQ(ArrowArrayInitFromType(&plain_dst, NANOARROW_TYPE_INT8),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(&plain_dst), NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayAppendStorageFromArrayView(&plain_dst, &src_view,
&error), EINVAL);
+ EXPECT_STREQ(error.message,
+ "Can't append storage when exactly one of source and
destination is "
+ "dictionary-encoded");
+
+ struct ArrowArrayView plain_view;
+ ArrowArrayViewInitFromType(&plain_view, NANOARROW_TYPE_INT8);
+ ASSERT_EQ(ArrowArrayViewSetArray(&plain_view, &plain_dst, &error),
NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &plain_view, &error),
EINVAL);
+ EXPECT_STREQ(error.message,
+ "Can't append storage when exactly one of source and
destination is "
+ "dictionary-encoded");
+
+ ArrowArrayViewReset(&plain_view);
+ ArrowArrayRelease(&plain_dst);
+ ArrowArrayViewReset(&dst_view);
+ ArrowArrayRelease(&dst);
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src);
+ ArrowSchemaRelease(&schema);
+}
+
+#if defined(NANOARROW_BUILD_TESTS_WITH_ARROW)
+
+enum class ArrowCppAppendValueKind { kSigned, kUnsigned, kString };
+
+static std::shared_ptr<Array> BuildArrowCppAppendArray(
+ const std::shared_ptr<DataType>& type, ArrowCppAppendValueKind value_kind)
{
+ auto maybe_builder = MakeBuilder(type);
+ ARROW_EXPECT_OK(maybe_builder.status());
+ auto builder = std::move(maybe_builder).ValueUnsafe();
+
+ for (int i = 0; i < 3; i++) {
+ if (i == 1) {
+ ARROW_EXPECT_OK(builder->AppendNull());
+ continue;
+ }
+
+ std::shared_ptr<Scalar> scalar;
+ switch (value_kind) {
+ case ArrowCppAppendValueKind::kSigned: {
+ auto maybe_scalar = MakeScalar(type, (int64_t)(i == 0 ? -1 : 42));
+ ARROW_EXPECT_OK(maybe_scalar.status());
+ scalar = maybe_scalar.ValueUnsafe();
+ break;
+ }
+ case ArrowCppAppendValueKind::kUnsigned: {
+ auto maybe_scalar = MakeScalar(type, (uint64_t)(i == 0 ? 1 : 42));
+ ARROW_EXPECT_OK(maybe_scalar.status());
+ scalar = maybe_scalar.ValueUnsafe();
+ break;
+ }
+ case ArrowCppAppendValueKind::kString: {
+ auto maybe_scalar = MakeScalar(type, std::string(i == 0 ? "abc" :
"def"));
+ ARROW_EXPECT_OK(maybe_scalar.status());
+ scalar = maybe_scalar.ValueUnsafe();
+ break;
+ }
+ }
+
+ ARROW_EXPECT_OK(builder->AppendScalar(*scalar));
+ }
+
+ auto maybe_array = builder->Finish();
+ ARROW_EXPECT_OK(maybe_array.status());
+ return maybe_array.ValueUnsafe();
+}
+
+static std::shared_ptr<Array> BuildArrowCppAppendListArray(
+ const std::shared_ptr<DataType>& type) {
+ auto child_builder = std::make_shared<Int32Builder>();
+ if (type->id() == Type::LIST) {
+ ListBuilder builder(default_memory_pool(), child_builder, type);
+ ARROW_EXPECT_OK(builder.Append());
+ ARROW_EXPECT_OK(child_builder->Append(1));
+ ARROW_EXPECT_OK(child_builder->Append(2));
+ ARROW_EXPECT_OK(builder.AppendNull());
+ ARROW_EXPECT_OK(builder.Append());
+ ARROW_EXPECT_OK(child_builder->Append(3));
+ auto maybe_array = builder.Finish();
+ ARROW_EXPECT_OK(maybe_array.status());
+ return maybe_array.ValueUnsafe();
+ } else {
+ LargeListBuilder builder(default_memory_pool(), child_builder, type);
+ ARROW_EXPECT_OK(builder.Append());
+ ARROW_EXPECT_OK(child_builder->Append(1));
+ ARROW_EXPECT_OK(child_builder->Append(2));
+ ARROW_EXPECT_OK(builder.AppendNull());
+ ARROW_EXPECT_OK(builder.Append());
+ ARROW_EXPECT_OK(child_builder->Append(3));
+ auto maybe_array = builder.Finish();
+ ARROW_EXPECT_OK(maybe_array.status());
+ return maybe_array.ValueUnsafe();
+ }
+}
+
+static void ExpectArrowCppStorageAppendEquals(const std::shared_ptr<Array>&
src,
+ const std::shared_ptr<Array>&
expected) {
+ struct ArrowError error;
+ struct ArrowArray src_array;
+ struct ArrowSchema src_schema;
+ ARROW_EXPECT_OK(ExportArray(*src, &src_array, &src_schema));
+ struct ArrowArrayView src_view;
+ ASSERT_EQ(ArrowArrayViewInitFromSchema(&src_view, &src_schema, &error),
NANOARROW_OK)
+ << error.message;
+ ASSERT_EQ(ArrowArrayViewSetArray(&src_view, &src_array, &error),
NANOARROW_OK)
+ << error.message;
+
+ struct ArrowSchema dst_schema;
+ ARROW_EXPECT_OK(ExportType(*expected->type(), &dst_schema));
+ struct ArrowArray dst;
+ ASSERT_EQ(ArrowArrayInitFromSchema(&dst, &dst_schema, &error), NANOARROW_OK)
+ << error.message;
+ ASSERT_EQ(ArrowArrayStartAppending(&dst), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendStorageFromArrayView(&dst, &src_view, &error),
NANOARROW_OK)
+ << error.message;
+ ASSERT_EQ(ArrowArrayFinishBuilding(&dst, NANOARROW_VALIDATION_LEVEL_FULL,
&error),
+ NANOARROW_OK)
+ << error.message;
+
+ auto maybe_actual = ImportArray(&dst, &dst_schema);
+ ARROW_EXPECT_OK(maybe_actual.status());
+ EXPECT_TRUE(maybe_actual.ValueUnsafe()->Equals(expected))
+ << maybe_actual.ValueUnsafe()->ToString() << "\n!=\n"
+ << expected->ToString();
+
+ ArrowArrayViewReset(&src_view);
+ ArrowArrayRelease(&src_array);
+ ArrowSchemaRelease(&src_schema);
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewArrowCppSignedIntegers) {
+ std::vector<std::shared_ptr<DataType>> types = {int8(), int16(), int32(),
int64()};
+ for (const auto& src_type : types) {
+ auto src = BuildArrowCppAppendArray(src_type,
ArrowCppAppendValueKind::kSigned);
+ for (const auto& dst_type : types) {
+ SCOPED_TRACE(src_type->ToString() + " -> " + dst_type->ToString());
+ auto expected =
+ BuildArrowCppAppendArray(dst_type, ArrowCppAppendValueKind::kSigned);
+ ExpectArrowCppStorageAppendEquals(src, expected);
+ }
+ }
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewArrowCppUnsignedIntegers) {
+ std::vector<std::shared_ptr<DataType>> types = {uint8(), uint16(), uint32(),
uint64()};
+ for (const auto& src_type : types) {
+ auto src = BuildArrowCppAppendArray(src_type,
ArrowCppAppendValueKind::kUnsigned);
+ for (const auto& dst_type : types) {
+ SCOPED_TRACE(src_type->ToString() + " -> " + dst_type->ToString());
+ auto expected =
+ BuildArrowCppAppendArray(dst_type,
ArrowCppAppendValueKind::kUnsigned);
+ ExpectArrowCppStorageAppendEquals(src, expected);
+ }
+ }
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewArrowCppStrings) {
+ std::vector<std::shared_ptr<DataType>> types = {utf8(), large_utf8()};
+ for (const auto& src_type : types) {
+ auto src = BuildArrowCppAppendArray(src_type,
ArrowCppAppendValueKind::kString);
+ for (const auto& dst_type : types) {
+ SCOPED_TRACE(src_type->ToString() + " -> " + dst_type->ToString());
+ auto expected =
+ BuildArrowCppAppendArray(dst_type, ArrowCppAppendValueKind::kString);
+ ExpectArrowCppStorageAppendEquals(src, expected);
+ }
+ }
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewArrowCppBinary) {
+ std::vector<std::shared_ptr<DataType>> types = {binary(), large_binary()};
+ for (const auto& src_type : types) {
+ auto src = BuildArrowCppAppendArray(src_type,
ArrowCppAppendValueKind::kString);
+ for (const auto& dst_type : types) {
+ SCOPED_TRACE(src_type->ToString() + " -> " + dst_type->ToString());
+ auto expected =
+ BuildArrowCppAppendArray(dst_type, ArrowCppAppendValueKind::kString);
+ ExpectArrowCppStorageAppendEquals(src, expected);
+ }
+ }
+}
+
+TEST(ArrayTest, ArrayAppendStorageFromArrayViewArrowCppLists) {
+ std::vector<std::shared_ptr<DataType>> types = {list(int32()),
large_list(int32())};
+ for (const auto& src_type : types) {
+ auto src = BuildArrowCppAppendListArray(src_type);
+ for (const auto& dst_type : types) {
+ SCOPED_TRACE(src_type->ToString() + " -> " + dst_type->ToString());
+ auto expected = BuildArrowCppAppendListArray(dst_type);
+ ExpectArrowCppStorageAppendEquals(src, expected);
+ }
+ }
+}
+
+#endif
diff --git a/src/nanoarrow/common/buffer_test.cc
b/src/nanoarrow/common/buffer_test.cc
index 3b3cc8af..51c0964e 100644
--- a/src/nanoarrow/common/buffer_test.cc
+++ b/src/nanoarrow/common/buffer_test.cc
@@ -647,6 +647,16 @@ TEST(BitmapTest, BitmapTestAppendInt8Unsafe) {
EXPECT_EQ(ArrowBitGet(bitmap.buffer.data, i), test_values[i - 136]);
}
+ // Append fewer values than are needed to complete the current byte
+ int8_t short_values[] = {1, 0, 1};
+ ASSERT_EQ(ArrowBitmapReserve(&bitmap, 3), NANOARROW_OK);
+ ArrowBitmapAppendInt8Unsafe(&bitmap, short_values, 3);
+ EXPECT_EQ(bitmap.size_bits, 207);
+ EXPECT_EQ(bitmap.buffer.size_bytes, 26);
+ for (int i = 0; i < 3; i++) {
+ EXPECT_EQ(ArrowBitGet(bitmap.buffer.data, 204 + i), short_values[i]);
+ }
+
ArrowBitmapReset(&bitmap);
}
@@ -702,6 +712,16 @@ TEST(BitmapTest, BitmapTestAppendInt32Unsafe) {
EXPECT_EQ(ArrowBitGet(bitmap.buffer.data, i), test_values[i - 136]);
}
+ // Append fewer values than are needed to complete the current byte
+ int32_t short_values[] = {1, 0, 1};
+ ASSERT_EQ(ArrowBitmapReserve(&bitmap, 3), NANOARROW_OK);
+ ArrowBitmapAppendInt32Unsafe(&bitmap, short_values, 3);
+ EXPECT_EQ(bitmap.size_bits, 207);
+ EXPECT_EQ(bitmap.buffer.size_bytes, 26);
+ for (int i = 0; i < 3; i++) {
+ EXPECT_EQ(ArrowBitGet(bitmap.buffer.data, 204 + i), short_values[i]);
+ }
+
ArrowBitmapReset(&bitmap);
}
diff --git a/src/nanoarrow/common/inline_buffer.h
b/src/nanoarrow/common/inline_buffer.h
index e3d48547..929fe815 100644
--- a/src/nanoarrow/common/inline_buffer.h
+++ b/src/nanoarrow/common/inline_buffer.h
@@ -656,6 +656,10 @@ static inline void ArrowBitmapAppendInt8Unsafe(struct
ArrowBitmap* bitmap,
// First byte
if ((out_i_cursor % 8) != 0) {
int64_t n_partial_bits = _ArrowRoundUpToMultipleOf8(out_i_cursor) -
out_i_cursor;
+ if (n_partial_bits > n_remaining) {
+ n_partial_bits = n_remaining;
+ }
+
for (int i = 0; i < n_partial_bits; i++) {
ArrowBitSetTo(bitmap->buffer.data, out_i_cursor++, values[i]);
}
@@ -706,6 +710,10 @@ static inline void ArrowBitmapAppendInt32Unsafe(struct
ArrowBitmap* bitmap,
// First byte
if ((out_i_cursor % 8) != 0) {
int64_t n_partial_bits = _ArrowRoundUpToMultipleOf8(out_i_cursor) -
out_i_cursor;
+ if (n_partial_bits > n_remaining) {
+ n_partial_bits = n_remaining;
+ }
+
for (int i = 0; i < n_partial_bits; i++) {
ArrowBitSetTo(bitmap->buffer.data, out_i_cursor++, (uint8_t)values[i]);
}
diff --git a/src/nanoarrow/nanoarrow.h b/src/nanoarrow/nanoarrow.h
index 564db40a..d31d67a8 100644
--- a/src/nanoarrow/nanoarrow.h
+++ b/src/nanoarrow/nanoarrow.h
@@ -122,6 +122,8 @@
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArraySetValidityBitmap)
#define ArrowArraySetBuffer NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowArraySetBuffer)
#define ArrowArrayReserve NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowArrayReserve)
+#define ArrowArrayAppendStorageFromArrayView \
+ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayAppendStorageFromArrayView)
#define ArrowArrayFinishBuilding \
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayFinishBuilding)
#define ArrowArrayFinishBuildingDefault \
@@ -1032,6 +1034,22 @@ static inline ArrowErrorCode
ArrowArrayStartAppending(struct ArrowArray* array);
NANOARROW_DLL ArrowErrorCode ArrowArrayReserve(struct ArrowArray* array,
int64_t
additional_size_elements);
+/// \brief Append storage from an ArrowArrayView to an ArrowArray
+///
+/// Appends each logical storage element of array_view to array. array must
have
+/// been initialized with compatible storage and prepared using
+/// ArrowArrayStartAppending(). Dictionary values referenced by array_view are
+/// not copied; dictionary-encoded inputs require a dictionary-encoded output.
+/// Struct children are matched by position. Logical type metadata not carried
by
+/// ArrowArrayView, including struct field names and decimal precision and
scale,
+/// must be checked by the caller.
+/// array_view must not reference storage owned by array or its children
because an
+/// append may reallocate and invalidate that storage.
+/// Returns EINVAL for incompatible storage and ENOTSUP for unsupported
storage.
+NANOARROW_DLL ArrowErrorCode ArrowArrayAppendStorageFromArrayView(
+ struct ArrowArray* array, const struct ArrowArrayView* array_view,
+ struct ArrowError* error);
+
/// \brief Append a null value to an array
static inline ArrowErrorCode ArrowArrayAppendNull(struct ArrowArray* array,
int64_t n);