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-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new 12a1889f9 chore(c/vendor): Update vendored nanoarrow (#4636)
12a1889f9 is described below
commit 12a1889f9d681fab7b437c98b0dd50ea526820d4
Author: Dewey Dunnington <[email protected]>
AuthorDate: Sat Aug 15 21:48:53 2026 -0500
chore(c/vendor): Update vendored nanoarrow (#4636)
Updates the vendored nanoarrow to 0.9.0.
---
c/vendor/nanoarrow/nanoarrow.c | 601 ++++++++++++++++++++++++++++++++++-----
c/vendor/nanoarrow/nanoarrow.h | 127 ++++++++-
c/vendor/nanoarrow/nanoarrow.hpp | 2 +-
c/vendor/vendor_nanoarrow.sh | 2 +-
4 files changed, 653 insertions(+), 79 deletions(-)
diff --git a/c/vendor/nanoarrow/nanoarrow.c b/c/vendor/nanoarrow/nanoarrow.c
index 123e8e845..cfe9c113b 100644
--- a/c/vendor/nanoarrow/nanoarrow.c
+++ b/c/vendor/nanoarrow/nanoarrow.c
@@ -22,6 +22,29 @@
#include <stdlib.h>
#include <string.h>
+// For thread safe shared buffers we need C11 + stdatomic.h
+// Can compile with -DNANOARROW_USE_STDATOMIC=0 or 1 to override
+// automatic detection
+#if !defined(NANOARROW_USE_STDATOMIC)
+#define NANOARROW_USE_STDATOMIC 0
+
+// Check for C11
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+
+// Check for GCC 4.8, which doesn't include stdatomic.h but does
+// not define __STDC_NO_ATOMICS__
+#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 5
+
+#if !defined(__STDC_NO_ATOMICS__)
+#include <stdatomic.h>
+#undef NANOARROW_USE_STDATOMIC
+#define NANOARROW_USE_STDATOMIC 1
+#endif
+#endif
+#endif
+
+#endif
+
#include "nanoarrow.h"
const char* ArrowNanoarrowVersion(void) { return NANOARROW_VERSION; }
@@ -276,6 +299,225 @@ struct ArrowBufferAllocator ArrowBufferDeallocator(
return allocator;
}
+#if NANOARROW_USE_STDATOMIC
+struct ArrowSharedBufferPrivate {
+ struct ArrowBuffer src;
+ atomic_long reference_count;
+};
+
+static int64_t ArrowSharedBufferUpdate(struct ArrowSharedBufferPrivate*
private_data,
+ int delta) {
+ int64_t old_count = atomic_fetch_add(&private_data->reference_count, delta);
+ return old_count + delta;
+}
+
+static void ArrowSharedBufferSet(struct ArrowSharedBufferPrivate* private_data,
+ int64_t count) {
+ atomic_store(&private_data->reference_count, count);
+}
+
+int ArrowSharedBufferIsThreadSafe(void) { return 1; }
+
+struct ArrowSharedArrayPrivate {
+ struct ArrowArray src;
+ atomic_long reference_count;
+};
+
+static int64_t ArrowSharedArrayUpdate(struct ArrowSharedArrayPrivate*
private_data,
+ int delta) {
+ int64_t old_count = atomic_fetch_add(&private_data->reference_count, delta);
+ return old_count + delta;
+}
+
+static void ArrowSharedArraySet(struct ArrowSharedArrayPrivate* private_data,
+ int64_t count) {
+ atomic_store(&private_data->reference_count, count);
+}
+#else
+struct ArrowSharedBufferPrivate {
+ struct ArrowBuffer src;
+ int64_t reference_count;
+};
+
+static int64_t ArrowSharedBufferUpdate(struct ArrowSharedBufferPrivate*
private_data,
+ int delta) {
+ private_data->reference_count += delta;
+ return private_data->reference_count;
+}
+
+static void ArrowSharedBufferSet(struct ArrowSharedBufferPrivate* private_data,
+ int64_t count) {
+ private_data->reference_count = count;
+}
+
+int ArrowSharedBufferIsThreadSafe(void) { return 0; }
+
+struct ArrowSharedArrayPrivate {
+ struct ArrowArray src;
+ int64_t reference_count;
+};
+
+static int64_t ArrowSharedArrayUpdate(struct ArrowSharedArrayPrivate*
private_data,
+ int delta) {
+ private_data->reference_count += delta;
+ return private_data->reference_count;
+}
+
+static void ArrowSharedArraySet(struct ArrowSharedArrayPrivate* private_data,
+ int64_t count) {
+ private_data->reference_count = count;
+}
+#endif
+
+static void ArrowSharedBufferFree(struct ArrowBufferAllocator* allocator,
uint8_t* ptr,
+ int64_t size) {
+ NANOARROW_UNUSED(allocator);
+ NANOARROW_UNUSED(ptr);
+ NANOARROW_UNUSED(size);
+
+ struct ArrowSharedBufferPrivate* private_data =
+ (struct ArrowSharedBufferPrivate*)allocator->private_data;
+
+ if (ArrowSharedBufferUpdate(private_data, -1) == 0) {
+ ArrowBufferReset(&private_data->src);
+ ArrowFree(private_data);
+ }
+}
+
+static void ArrowSharedArrayBufferFree(struct ArrowBufferAllocator* allocator,
+ uint8_t* ptr, int64_t size) {
+ NANOARROW_UNUSED(ptr);
+ NANOARROW_UNUSED(size);
+
+ struct ArrowSharedArrayPrivate* private_data =
+ (struct ArrowSharedArrayPrivate*)allocator->private_data;
+
+ if (ArrowSharedArrayUpdate(private_data, -1) == 0) {
+ if (private_data->src.release != NULL) {
+ ArrowArrayRelease(&private_data->src);
+ }
+ ArrowFree(private_data);
+ }
+}
+
+ArrowErrorCode ArrowSharedBufferInit(struct ArrowBuffer* shared,
+ struct ArrowBuffer* src) {
+ if (src->data == NULL) {
+ ArrowBufferMove(src, shared);
+ return NANOARROW_OK;
+ }
+
+ struct ArrowSharedBufferPrivate* private_data =
+ (struct ArrowSharedBufferPrivate*)ArrowMalloc(
+ sizeof(struct ArrowSharedBufferPrivate));
+ if (private_data == NULL) {
+ return ENOMEM;
+ }
+
+ ArrowBufferMove(src, &private_data->src);
+ ArrowSharedBufferSet(private_data, 1);
+
+ ArrowBufferInit(shared);
+ shared->data = private_data->src.data;
+ shared->size_bytes = private_data->src.size_bytes;
+ // Don't expose any extra capcity from src so that any calls to
ArrowBufferAppend
+ // on this buffer will fail.
+ shared->capacity_bytes = private_data->src.size_bytes;
+ shared->allocator = ArrowBufferDeallocator(&ArrowSharedBufferFree,
private_data);
+ return NANOARROW_OK;
+}
+
+int ArrowIsSharedBuffer(struct ArrowBuffer* buffer) {
+ return buffer->allocator.free == &ArrowSharedBufferFree ||
+ buffer->allocator.free == &ArrowSharedArrayBufferFree;
+}
+
+ArrowErrorCode ArrowSharedBufferClone(struct ArrowBuffer* shared,
+ struct ArrowBuffer* shared_out) {
+ // If the buffer has no data, initialize an empty buffer
+ if (shared->data == NULL) {
+ ArrowBufferInit(shared_out);
+ return NANOARROW_OK;
+ }
+
+ if (shared->allocator.free == &ArrowSharedBufferFree) {
+ struct ArrowSharedBufferPrivate* private_data =
+ (struct ArrowSharedBufferPrivate*)shared->allocator.private_data;
+ ArrowSharedBufferUpdate(private_data, 1);
+ memcpy(shared_out, shared, sizeof(struct ArrowBuffer));
+ return NANOARROW_OK;
+ }
+
+ if (shared->allocator.free == &ArrowSharedArrayBufferFree) {
+ struct ArrowSharedArrayPrivate* private_data =
+ (struct ArrowSharedArrayPrivate*)shared->allocator.private_data;
+ ArrowSharedArrayUpdate(private_data, 1);
+ memcpy(shared_out, shared, sizeof(struct ArrowBuffer));
+ return NANOARROW_OK;
+ }
+
+ return EINVAL;
+}
+
+ArrowErrorCode ArrowSharedArrayInit(struct ArrowSharedArray* shared,
+ struct ArrowArray* src) {
+ struct ArrowSharedArrayPrivate* private_data =
+ (struct ArrowSharedArrayPrivate*)ArrowMalloc(
+ sizeof(struct ArrowSharedArrayPrivate));
+ if (private_data == NULL) {
+ return ENOMEM;
+ }
+
+ ArrowArrayMove(src, &private_data->src);
+ ArrowSharedArraySet(private_data, 1);
+ shared->private_data = private_data;
+ return NANOARROW_OK;
+}
+
+void ArrowSharedArrayRelease(struct ArrowSharedArray* shared) {
+ if (shared->private_data == NULL) {
+ return;
+ }
+
+ struct ArrowSharedArrayPrivate* private_data =
+ (struct ArrowSharedArrayPrivate*)shared->private_data;
+
+ if (ArrowSharedArrayUpdate(private_data, -1) == 0) {
+ if (private_data->src.release != NULL) {
+ ArrowArrayRelease(&private_data->src);
+ }
+ ArrowFree(private_data);
+ }
+
+ shared->private_data = NULL;
+}
+
+ArrowErrorCode ArrowSharedArrayBuffer(struct ArrowSharedArray* shared, int64_t
i,
+ struct ArrowBuffer* out) {
+ struct ArrowSharedArrayPrivate* private_data =
+ (struct ArrowSharedArrayPrivate*)shared->private_data;
+ NANOARROW_DCHECK(i >= 0 && i < private_data->src.n_buffers);
+
+ ArrowSharedArrayUpdate(private_data, 1);
+ ArrowBufferInit(out);
+
+ if (ArrowArrayIsInternal(&private_data->src)) {
+ // The source array was built with nanoarrow, so we can get buffer size
info
+ struct ArrowBuffer* src = ArrowArrayBuffer(&private_data->src, i);
+ out->data = src->data;
+ out->size_bytes = src->size_bytes;
+ out->capacity_bytes = src->size_bytes;
+ } else {
+ // Generic C Data Interface array: buffer sizes are not known
+ out->data = (uint8_t*)private_data->src.buffers[i];
+ out->size_bytes = 0;
+ out->capacity_bytes = 0;
+ }
+
+ out->allocator = ArrowBufferDeallocator(&ArrowSharedArrayBufferFree,
private_data);
+ return NANOARROW_OK;
+}
+
static const int kInt32DecimalDigits = 9;
static const uint64_t kUInt32PowersOfTen[] = {
@@ -578,6 +820,7 @@ ArrowErrorCode ArrowDecimalAppendStringToBuffer(const
struct ArrowDecimal* decim
#include <errno.h>
#include <inttypes.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -1931,47 +2174,34 @@ ArrowErrorCode ArrowSchemaViewInit(struct
ArrowSchemaView* schema_view,
return NANOARROW_OK;
}
-static int64_t ArrowSchemaTypeToStringInternal(struct ArrowSchemaView*
schema_view,
- char* out, int64_t n) {
- const char* type_string = ArrowTypeString(schema_view->type);
- switch (schema_view->type) {
- case NANOARROW_TYPE_DECIMAL32:
- case NANOARROW_TYPE_DECIMAL64:
- case NANOARROW_TYPE_DECIMAL128:
- case NANOARROW_TYPE_DECIMAL256:
- return snprintf(out, n, "%s(%" PRId32 ", %" PRId32 ")", type_string,
- schema_view->decimal_precision,
schema_view->decimal_scale);
- case NANOARROW_TYPE_TIMESTAMP:
- return snprintf(out, n, "%s('%s', '%s')", type_string,
- ArrowTimeUnitString(schema_view->time_unit),
schema_view->timezone);
- case NANOARROW_TYPE_TIME32:
- case NANOARROW_TYPE_TIME64:
- case NANOARROW_TYPE_DURATION:
- return snprintf(out, n, "%s('%s')", type_string,
- ArrowTimeUnitString(schema_view->time_unit));
- case NANOARROW_TYPE_FIXED_SIZE_BINARY:
- case NANOARROW_TYPE_FIXED_SIZE_LIST:
- return snprintf(out, n, "%s(%" PRId32 ")", type_string,
schema_view->fixed_size);
- case NANOARROW_TYPE_SPARSE_UNION:
- case NANOARROW_TYPE_DENSE_UNION:
- return snprintf(out, n, "%s([%s])", type_string,
schema_view->union_type_ids);
- default:
- return snprintf(out, n, "%s", type_string);
+static inline void ArrowSchemaToStringSNPrintF(char** out, int64_t*
n_remaining,
+ int64_t* n_chars, const char*
fmt, ...) {
+ // With _FORTIFY_SOURCE=3, vsnprintf on a null output warns, so ensure we
never pass
+ // a NULL to vsnprintf
+ char tmp_not_null[1];
+ char* out_not_null;
+ size_t out_not_null_size;
+ if (*out == NULL) {
+ out_not_null = tmp_not_null;
+ out_not_null_size = sizeof(tmp_not_null);
+ } else {
+ out_not_null = *out;
+ out_not_null_size = (size_t)*n_remaining;
}
-}
-// Helper for bookkeeping to emulate sprintf()-like behaviour spread
-// among multiple sprintf calls.
-static inline void ArrowToStringLogChars(char** out, int64_t n_chars_last,
- int64_t* n_remaining, int64_t*
n_chars) {
+ va_list args;
+ va_start(args, fmt);
+ int n_chars_last = vsnprintf(out_not_null, out_not_null_size, fmt, args);
+ va_end(args);
+
// In the unlikely snprintf() returning a negative value (encoding error),
// ensure the result won't cause an out-of-bounds access.
if (n_chars_last < 0) {
n_chars_last = 0;
}
- *n_chars += n_chars_last;
*n_remaining -= n_chars_last;
+ *n_chars += n_chars_last;
// n_remaining is never less than 0
if (*n_remaining < 0) {
@@ -1984,93 +2214,134 @@ static inline void ArrowToStringLogChars(char** out,
int64_t n_chars_last,
}
}
-int64_t ArrowSchemaToString(const struct ArrowSchema* schema, char* out,
int64_t n,
- char recursive) {
+static void ArrowSchemaTypeToStringInternal(struct ArrowSchemaView*
schema_view,
+ char** out, int64_t* n_remaining,
+ int64_t* n_chars) {
+ const char* type_string = ArrowTypeString(schema_view->type);
+ switch (schema_view->type) {
+ case NANOARROW_TYPE_DECIMAL32:
+ case NANOARROW_TYPE_DECIMAL64:
+ case NANOARROW_TYPE_DECIMAL128:
+ case NANOARROW_TYPE_DECIMAL256:
+ ArrowSchemaToStringSNPrintF(
+ out, n_remaining, n_chars, "%s(%" PRId32 ", %" PRId32 ")",
type_string,
+ schema_view->decimal_precision, schema_view->decimal_scale);
+ return;
+ case NANOARROW_TYPE_TIMESTAMP:
+ ArrowSchemaToStringSNPrintF(
+ out, n_remaining, n_chars, "%s('%s', '%s')", type_string,
+ ArrowTimeUnitString(schema_view->time_unit), schema_view->timezone);
+ return;
+ case NANOARROW_TYPE_TIME32:
+ case NANOARROW_TYPE_TIME64:
+ case NANOARROW_TYPE_DURATION:
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "%s('%s')",
type_string,
+ ArrowTimeUnitString(schema_view->time_unit));
+ return;
+ case NANOARROW_TYPE_FIXED_SIZE_BINARY:
+ case NANOARROW_TYPE_FIXED_SIZE_LIST:
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "%s(%" PRId32 ")",
+ type_string, schema_view->fixed_size);
+ return;
+ case NANOARROW_TYPE_SPARSE_UNION:
+ case NANOARROW_TYPE_DENSE_UNION:
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "%s([%s])",
type_string,
+ schema_view->union_type_ids);
+ return;
+ default:
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "%s",
type_string);
+ return;
+ }
+}
+
+static void ArrowSchemaToStringInternal(const struct ArrowSchema* schema,
char** out,
+ int64_t* n_remaining, int64_t* n_chars,
+ char recursive) {
if (schema == NULL) {
- return snprintf(out, n, "[invalid: pointer is null]");
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "[invalid: pointer
is null]");
+ return;
}
if (schema->release == NULL) {
- return snprintf(out, n, "[invalid: schema is released]");
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars,
+ "[invalid: schema is released]");
+ return;
}
struct ArrowSchemaView schema_view;
struct ArrowError error;
if (ArrowSchemaViewInit(&schema_view, schema, &error) != NANOARROW_OK) {
- return snprintf(out, n, "[invalid: %s]", ArrowErrorMessage(&error));
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "[invalid: %s]",
+ ArrowErrorMessage(&error));
+ return;
}
// Extension type and dictionary should include both the top-level type
// and the storage type.
int is_extension = schema_view.extension_name.size_bytes > 0;
int is_dictionary = schema->dictionary != NULL;
- int64_t n_chars = 0;
- int64_t n_chars_last = 0;
// Uncommon but not technically impossible that both are true
if (is_extension && is_dictionary) {
- n_chars_last = snprintf(
- out, n, "%.*s{dictionary(%s)<",
(int)schema_view.extension_name.size_bytes,
- schema_view.extension_name.data,
ArrowTypeString(schema_view.storage_type));
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars,
"%.*s{dictionary(%s)<",
+ (int)schema_view.extension_name.size_bytes,
+ schema_view.extension_name.data,
+ ArrowTypeString(schema_view.storage_type));
} else if (is_extension) {
- n_chars_last = snprintf(out, n, "%.*s{",
(int)schema_view.extension_name.size_bytes,
- schema_view.extension_name.data);
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "%.*s{",
+ (int)schema_view.extension_name.size_bytes,
+ schema_view.extension_name.data);
} else if (is_dictionary) {
- n_chars_last =
- snprintf(out, n, "dictionary(%s)<",
ArrowTypeString(schema_view.storage_type));
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "dictionary(%s)<",
+ ArrowTypeString(schema_view.storage_type));
}
- ArrowToStringLogChars(&out, n_chars_last, &n, &n_chars);
-
if (!is_dictionary) {
- n_chars_last = ArrowSchemaTypeToStringInternal(&schema_view, out, n);
+ ArrowSchemaTypeToStringInternal(&schema_view, out, n_remaining, n_chars);
} else {
- n_chars_last = ArrowSchemaToString(schema->dictionary, out, n, recursive);
+ ArrowSchemaToStringInternal(schema->dictionary, out, n_remaining, n_chars,
recursive);
}
- ArrowToStringLogChars(&out, n_chars_last, &n, &n_chars);
-
if (recursive && schema->format[0] == '+') {
- n_chars_last = snprintf(out, n, "<");
- ArrowToStringLogChars(&out, n_chars_last, &n, &n_chars);
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "<");
for (int64_t i = 0; i < schema->n_children; i++) {
if (i > 0) {
- n_chars_last = snprintf(out, n, ", ");
- ArrowToStringLogChars(&out, n_chars_last, &n, &n_chars);
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, ", ");
}
// ArrowSchemaToStringInternal() will validate the child and print the
error,
// but we need the name first
if (schema->children[i] != NULL && schema->children[i]->release != NULL
&&
schema->children[i]->name != NULL) {
- n_chars_last = snprintf(out, n, "%s: ", schema->children[i]->name);
- ArrowToStringLogChars(&out, n_chars_last, &n, &n_chars);
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars,
+ "%s: ", schema->children[i]->name);
}
- n_chars_last = ArrowSchemaToString(schema->children[i], out, n,
recursive);
- ArrowToStringLogChars(&out, n_chars_last, &n, &n_chars);
+ ArrowSchemaToStringInternal(schema->children[i], out, n_remaining,
n_chars,
+ recursive);
}
- n_chars_last = snprintf(out, n, ">");
- ArrowToStringLogChars(&out, n_chars_last, &n, &n_chars);
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, ">");
}
if (is_extension && is_dictionary) {
- n_chars += snprintf(out, n, ">}");
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, ">}");
} else if (is_extension) {
- n_chars += snprintf(out, n, "}");
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, "}");
} else if (is_dictionary) {
- n_chars += snprintf(out, n, ">");
+ ArrowSchemaToStringSNPrintF(out, n_remaining, n_chars, ">");
}
+}
- // Ensure that we always return a positive result
- if (n_chars > 0) {
- return n_chars;
- } else {
- return 0;
- }
+int64_t ArrowSchemaToString(const struct ArrowSchema* schema, char* out,
int64_t n,
+ char recursive) {
+ NANOARROW_DCHECK(n >= 0);
+ NANOARROW_DCHECK(out != NULL || n == 0);
+ int64_t n_chars = 0;
+ ArrowSchemaToStringInternal(schema, &out, &n, &n_chars, recursive);
+ return n_chars;
}
ArrowErrorCode ArrowMetadataReaderInit(struct ArrowMetadataReader* reader,
@@ -2363,7 +2634,7 @@ static void ArrowArrayReleaseInternal(struct ArrowArray*
array) {
array->release = NULL;
}
-static int ArrowArrayIsInternal(struct ArrowArray* array) {
+int ArrowArrayIsInternal(struct ArrowArray* array) {
return array->release == &ArrowArrayReleaseInternal;
}
@@ -2858,6 +3129,172 @@ ArrowErrorCode ArrowArrayFinishBuildingDefault(struct
ArrowArray* array,
return ArrowArrayFinishBuilding(array, NANOARROW_VALIDATION_LEVEL_DEFAULT,
error);
}
+static int ArrowArrayIsShared(struct ArrowArray* array) {
+ if (!ArrowArrayIsInternal(array)) {
+ return 0;
+ }
+
+ for (int64_t i = 0; i < array->n_buffers; i++) {
+ struct ArrowBuffer* buffer = ArrowArrayBuffer(array, i);
+ if (buffer->data != NULL && !ArrowIsSharedBuffer(buffer)) {
+ return 0;
+ }
+ }
+
+ for (int64_t i = 0; i < array->n_children; i++) {
+ if (!ArrowArrayIsShared(array->children[i])) {
+ return 0;
+ }
+ }
+
+ if (array->dictionary != NULL && !ArrowArrayIsShared(array->dictionary)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static ArrowErrorCode ArrowArrayMoveSharedInternal(struct ArrowArray* src,
+ struct ArrowArray* dst) {
+ if (ArrowArrayIsShared(src)) {
+ ArrowArrayMove(src, dst);
+ return NANOARROW_OK;
+ }
+
+ NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromType(dst,
NANOARROW_TYPE_UNINITIALIZED));
+
+ // Allocate children and move source children to dst children
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAllocateChildren(dst, src->n_children));
+ for (int64_t i = 0; i < src->n_children; i++) {
+ NANOARROW_RETURN_NOT_OK(
+ ArrowArrayMoveSharedInternal(src->children[i], dst->children[i]));
+ }
+
+ // Allocate dictionary if needed and move source dictionary to dst dictionary
+ if (src->dictionary != NULL) {
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAllocateDictionary(dst));
+ NANOARROW_RETURN_NOT_OK(
+ ArrowArrayMoveSharedInternal(src->dictionary, dst->dictionary));
+ }
+
+ // We might need some more buffers if we are shallowly copying a
string/binary view
+ if (src->n_buffers > 3) {
+ if (src->n_buffers > INT_MAX) {
+ return EINVAL;
+ }
+
+ NANOARROW_RETURN_NOT_OK(
+ ArrowArrayAddVariadicBuffers(dst, (int32_t)src->n_buffers - 3));
+ }
+
+ // Move src into a shared array and set dst's buffers using the ref-counted
version
+ struct ArrowSharedArray shared_array;
+ NANOARROW_RETURN_NOT_OK(ArrowSharedArrayInit(&shared_array, src));
+
+ for (int64_t i = 0; i < src->n_buffers; i++) {
+ struct ArrowBuffer* dst_buffer = ArrowArrayBuffer(dst, i);
+ ArrowErrorCode result = ArrowSharedArrayBuffer(&shared_array, i,
dst_buffer);
+ if (result != NANOARROW_OK) {
+ ArrowSharedArrayRelease(&shared_array);
+ return result;
+ }
+ }
+
+ ArrowSharedArrayRelease(&shared_array);
+
+ dst->n_buffers = src->n_buffers;
+ dst->length = src->length;
+ dst->null_count = src->null_count;
+ dst->offset = src->offset;
+
+ // Flush internal buffer pointers to array->buffers
+ NANOARROW_RETURN_NOT_OK(ArrowArrayFlushInternalPointers(dst));
+
+ return NANOARROW_OK;
+}
+
+static ArrowErrorCode ArrowArrayCloneSharedInternal(struct ArrowArray* src,
+ struct ArrowArray* dst) {
+ NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromType(dst,
NANOARROW_TYPE_UNINITIALIZED));
+
+ // Allocate children and clone source children to dst children
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAllocateChildren(dst, src->n_children));
+ for (int64_t i = 0; i < src->n_children; i++) {
+ NANOARROW_RETURN_NOT_OK(
+ ArrowArrayCloneSharedInternal(src->children[i], dst->children[i]));
+ }
+
+ // Allocate dictionary if needed and clone source dictionary to dst
dictionary
+ if (src->dictionary != NULL) {
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAllocateDictionary(dst));
+ NANOARROW_RETURN_NOT_OK(
+ ArrowArrayCloneSharedInternal(src->dictionary, dst->dictionary));
+ }
+
+ // We might need some more buffers if we are shallowly copying a
string/binary view
+ if (src->n_buffers > 3) {
+ if (src->n_buffers > INT_MAX) {
+ return EINVAL;
+ }
+
+ NANOARROW_RETURN_NOT_OK(
+ ArrowArrayAddVariadicBuffers(dst, (int32_t)src->n_buffers - 3));
+ }
+
+ for (int64_t i = 0; i < src->n_buffers; i++) {
+ struct ArrowBuffer* src_buffer = ArrowArrayBuffer(src, i);
+ struct ArrowBuffer* dst_buffer = ArrowArrayBuffer(dst, i);
+ NANOARROW_RETURN_NOT_OK(ArrowSharedBufferClone(src_buffer, dst_buffer));
+ }
+
+ dst->n_buffers = src->n_buffers;
+ dst->length = src->length;
+ dst->null_count = src->null_count;
+ dst->offset = src->offset;
+
+ // Flush internal buffer pointers to array->buffers
+ NANOARROW_RETURN_NOT_OK(ArrowArrayFlushInternalPointers(dst));
+
+ return NANOARROW_OK;
+}
+
+ArrowErrorCode ArrowArrayMoveShared(struct ArrowArray* array, struct
ArrowArray* shared) {
+ struct ArrowArray tmp;
+ tmp.release = NULL;
+ ArrowErrorCode result = ArrowArrayMoveSharedInternal(array, shared);
+ if (result != NANOARROW_OK) {
+ // On failure, release the temporary output
+ if (tmp.release != NULL) {
+ ArrowArrayRelease(&tmp);
+ }
+
+ // Because this operation may have partially moved the input array at this
point
+ // we also have to release it on failure to be predictable. These failures
are
+ // usually failed heap allocations and are difficult to trigger in
practice.
+ if (array->release != NULL) {
+ ArrowArrayRelease(array);
+ }
+ }
+
+ return result;
+}
+
+ArrowErrorCode ArrowArrayCloneShared(struct ArrowArray* shared,
+ struct ArrowArray* array) {
+ if (!ArrowArrayIsShared(shared)) {
+ return EINVAL;
+ }
+
+ struct ArrowArray tmp;
+ tmp.release = NULL;
+ ArrowErrorCode result = ArrowArrayCloneSharedInternal(shared, array);
+ if (result != NANOARROW_OK && tmp.release != NULL) {
+ ArrowArrayRelease(&tmp);
+ }
+
+ return result;
+}
+
void ArrowArrayViewInitFromType(struct ArrowArrayView* array_view,
enum ArrowType storage_type) {
memset(array_view, 0, sizeof(struct ArrowArrayView));
@@ -3803,10 +4240,26 @@ static int ArrowArrayViewValidateFull(struct
ArrowArrayView* array_view,
NANOARROW_RETURN_NOT_OK(ArrowArrayViewValidateFull(array_view->children[i],
error));
}
- // Dictionary validation not implemented
+ // Dictionary index validation
if (array_view->dictionary != NULL) {
NANOARROW_RETURN_NOT_OK(ArrowArrayViewValidateFull(array_view->dictionary,
error));
- // TODO: validate the indices
+
+ // Validate that all non-null indices are within the dictionary bounds
+ int64_t dictionary_length = array_view->dictionary->length;
+ for (int64_t i = 0; i < array_view->length; i++) {
+ if (ArrowArrayViewIsNull(array_view, i)) {
+ continue;
+ }
+
+ int64_t index = ArrowArrayViewGetIntUnsafe(array_view, i);
+ if (index < 0 || index >= dictionary_length) {
+ ArrowErrorSet(error,
+ "[%" PRId64 "] Expected dictionary index >= 0 and < %"
PRId64
+ " but found value %" PRId64,
+ i, dictionary_length, index);
+ return EINVAL;
+ }
+ }
}
return NANOARROW_OK;
diff --git a/c/vendor/nanoarrow/nanoarrow.h b/c/vendor/nanoarrow/nanoarrow.h
index 8a2e78cb7..4a7795326 100644
--- a/c/vendor/nanoarrow/nanoarrow.h
+++ b/c/vendor/nanoarrow/nanoarrow.h
@@ -21,7 +21,7 @@
#define NANOARROW_VERSION_MAJOR 0
#define NANOARROW_VERSION_MINOR 9
#define NANOARROW_VERSION_PATCH 0
-#define NANOARROW_VERSION "0.9.0-SNAPSHOT"
+#define NANOARROW_VERSION "0.9.0"
#define NANOARROW_VERSION_INT \
(NANOARROW_VERSION_MAJOR * 10000 + NANOARROW_VERSION_MINOR * 100 + \
@@ -512,7 +512,7 @@ enum ArrowType {
/// \brief Get a string value of an enum ArrowType value
/// \ingroup nanoarrow-utils
///
-/// Returns NULL for invalid values for type
+/// Returns "<unknown type identifier>" for invalid values for type
static inline const char* ArrowTypeString(enum ArrowType type);
static inline const char* ArrowTypeString(enum ArrowType type) {
@@ -608,7 +608,7 @@ static inline const char* ArrowTypeString(enum ArrowType
type) {
case NANOARROW_TYPE_LARGE_LIST_VIEW:
return "large_list_view";
default:
- return NULL;
+ return "<unknown type identifier>";
}
}
@@ -1140,6 +1140,17 @@ static inline void ArrowDecimalSetBytes(struct
ArrowDecimal* decimal,
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowBufferAllocatorDefault)
#define ArrowBufferDeallocator \
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowBufferDeallocator)
+#define ArrowSharedBufferInit NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowSharedBufferInit)
+#define ArrowSharedBufferIsThreadSafe \
+ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowSharedBufferIsThreadSafe)
+#define ArrowSharedBufferClone \
+ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowSharedBufferClone)
+#define ArrowSharedArrayInit NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowSharedArrayInit)
+#define ArrowSharedArrayRelease \
+ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowSharedArrayRelease)
+#define ArrowSharedArrayBuffer \
+ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowSharedArrayBuffer)
+#define ArrowIsSharedBuffer NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowIsSharedBuffer)
#define ArrowErrorSet NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowErrorSet)
#define ArrowLayoutInit NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowLayoutInit)
#define ArrowDecimalSetDigits NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowDecimalSetDigits)
@@ -1189,6 +1200,7 @@ static inline void ArrowDecimalSetBytes(struct
ArrowDecimal* decimal,
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowMetadataBuilderRemove)
#define ArrowSchemaViewInit NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowSchemaViewInit)
#define ArrowSchemaToString NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowSchemaToString)
+#define ArrowArrayIsInternal NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowArrayIsInternal)
#define ArrowArrayInitFromType \
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayInitFromType)
#define ArrowArrayInitFromSchema \
@@ -1209,6 +1221,8 @@ static inline void ArrowDecimalSetBytes(struct
ArrowDecimal* decimal,
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayFinishBuilding)
#define ArrowArrayFinishBuildingDefault \
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayFinishBuildingDefault)
+#define ArrowArrayMoveShared NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowArrayMoveShared)
+#define ArrowArrayCloneShared NANOARROW_SYMBOL(NANOARROW_NAMESPACE,
ArrowArrayCloneShared)
#define ArrowArrayViewInitFromType \
NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayViewInitFromType)
#define ArrowArrayViewInitFromSchema \
@@ -1298,6 +1312,85 @@ NANOARROW_DLL struct ArrowBufferAllocator
ArrowBufferDeallocator(
/// @}
+/// \defgroup nanoarrow-shared-buffer Shared buffers and arrays
+///
+/// The nanoarrow library implements two kinds of shared buffers: those backed
by
+/// a reference-counted ArrowBuffer and those backed by a reference-counted
ArrowArray.
+/// These are useful for building ArrowArrays using buffers without copying
the bytes
+/// of the source. For example the IPC implementation uses shared buffers
derived from an
+/// ArrowBuffer to avoid copying buffers from an IPC message, and the Shared
buffers
+/// derived from arrays are used to make a safe shallow clone of an array.
+///
+/// Reference counts are implemented using C11 atomics and not necessarily
thread safe
+/// on all platforms (notably, MSVC requires `/experimental:c11atomics`). Use
+/// ArrowSharedBufferIsThreadSafe() to check for thread safety if this is
needed.
+///
+/// @{
+
+/// \brief Initialize a shared buffer from an ArrowBuffer
+///
+/// If NANOARROW_OK is returned, shared is a reference-counted buffer that
+/// took ownership of src. The caller should release the shared buffer
+/// using ArrowBufferReset() when finished. The underlying data will persist
+/// until all clones have also been released.
+NANOARROW_DLL ArrowErrorCode ArrowSharedBufferInit(struct ArrowBuffer* shared,
+ struct ArrowBuffer* src);
+
+/// \brief Check for shared buffer thread safety
+///
+/// Thread-safe shared buffers require C11 and the stdatomic.h header.
+/// If either are unavailable, shared buffers are still possible but
+/// the resulting arrays must not be passed to other threads to be released.
+NANOARROW_DLL int ArrowSharedBufferIsThreadSafe(void);
+
+/// \brief Check if a buffer is a shared buffer
+///
+/// Returns non-zero if buffer was created by ArrowSharedBufferInit() or
+/// obtained from ArrowSharedArrayBuffer().
+NANOARROW_DLL int ArrowIsSharedBuffer(struct ArrowBuffer* buffer);
+
+/// \brief Clone a shared buffer, incrementing its reference count
+///
+/// This creates a new ArrowBuffer that shares the same underlying data with
the
+/// original shared buffer. The reference count is incremented. Returns EINVAL
+/// if shared is not a shared buffer (i.e. ArrowIsSharedBuffer() returns
false).
+NANOARROW_DLL ArrowErrorCode ArrowSharedBufferClone(struct ArrowBuffer* shared,
+ struct ArrowBuffer*
shared_out);
+
+/// \brief An opaque handle to a shared, reference-counted ArrowArray
+struct ArrowSharedArray {
+ void* private_data;
+};
+
+/// \brief Initialize a shared array from an ArrowArray
+///
+/// Takes ownership of src (sets src->release to NULL). The shared array
+/// should be released with ArrowSharedArrayRelease() when all buffers have
+/// been extracted from the source. The original array will be released
+/// when all borrowed buffers have been released.
+NANOARROW_DLL ArrowErrorCode ArrowSharedArrayInit(struct ArrowSharedArray*
shared,
+ struct ArrowArray* src);
+
+/// \brief Release the caller's reference to a shared array
+///
+/// Decrements the reference count. When no more references remain
+/// (including any buffers obtained via ArrowSharedArrayBuffer()),
+/// the underlying ArrowArray is released.
+NANOARROW_DLL void ArrowSharedArrayRelease(struct ArrowSharedArray* shared);
+
+/// \brief Obtain a buffer from a shared array
+///
+/// Returns an ArrowBuffer that views buffer i of the underlying ArrowArray.
+/// If the source array was built with nanoarrow (i.e. ArrowArrayIsInternal()
+/// returns true), buffer sizes are known and propagated to the output.
+/// Otherwise the output buffer's size_bytes will be 0. The shared array's
+/// reference count is incremented. The returned buffer is compatible with
+/// ArrowSharedBufferClone().
+NANOARROW_DLL ArrowErrorCode ArrowSharedArrayBuffer(struct ArrowSharedArray*
shared,
+ int64_t i, struct
ArrowBuffer* out);
+
+/// @}
+
/// \brief Move the contents of an src ArrowSchema into dst and set
src->release to NULL
/// \ingroup nanoarrow-arrow-cdata
static inline void ArrowSchemaMove(struct ArrowSchema* src, struct
ArrowSchema* dst);
@@ -2133,6 +2226,12 @@ static inline ArrowErrorCode
ArrowArrayShrinkToFit(struct ArrowArray* array);
NANOARROW_DLL ArrowErrorCode ArrowArrayFinishBuildingDefault(struct
ArrowArray* array,
struct
ArrowError* error);
+/// \brief Check if an ArrowArray was allocated by nanoarrow
+///
+/// Returns non-zero if array was allocated using ArrowArrayInitFromType(),
+/// ArrowArrayInitFromSchema(), or ArrowArrayInitFromArrayView().
+NANOARROW_DLL int ArrowArrayIsInternal(struct ArrowArray* array);
+
/// \brief Finish building an ArrowArray with explicit validation
///
/// Finish building with an explicit validation level. This could perform less
validation
@@ -2144,6 +2243,28 @@ NANOARROW_DLL ArrowErrorCode ArrowArrayFinishBuilding(
struct ArrowArray* array, enum ArrowValidationLevel validation_level,
struct ArrowError* error);
+/// \brief Create a shared copy of an ArrowArray
+///
+/// Recursively converts array into a version where all buffers are
+/// reference-counted. On success, shared is a new ArrowArray whose buffers
+/// are backed by ArrowSharedArray references, and array is consumed
+/// (release set to NULL). The resulting shared array can be safely moved
+/// or have its buffers cloned via ArrowSharedBufferClone(). On error,
+/// (e.g., failure to allocate a copy of a buffer), the input array is
+/// released as it may have been partially moved at the point the error
+/// occurs.
+NANOARROW_DLL ArrowErrorCode ArrowArrayMoveShared(struct ArrowArray* array,
+ struct ArrowArray* shared);
+
+/// \brief Clone a shared ArrowArray
+///
+/// Creates a new ArrowArray whose buffers share the same underlying data as
+/// the source shared array. The source must have been created by
+/// ArrowArrayMoveShared() (i.e. all buffers are reference-counted).
+/// Returns EINVAL if shared is not a shared array.
+NANOARROW_DLL ArrowErrorCode ArrowArrayCloneShared(struct ArrowArray* shared,
+ struct ArrowArray* array);
+
/// @}
/// \defgroup nanoarrow-array-view Reading arrays
diff --git a/c/vendor/nanoarrow/nanoarrow.hpp b/c/vendor/nanoarrow/nanoarrow.hpp
index 595971b77..5c39a22f3 100644
--- a/c/vendor/nanoarrow/nanoarrow.hpp
+++ b/c/vendor/nanoarrow/nanoarrow.hpp
@@ -861,7 +861,7 @@ class ViewArrayAs {
using const_iterator = typename
internal::RandomAccessRange<Get>::const_iterator;
const_iterator begin() const { return range_.begin(); }
const_iterator end() const { return range_.end(); }
- value_type operator[](int64_t i) const { return range_.get(i); }
+ value_type operator[](int64_t i) const { return range_.get(range_.offset +
i); }
};
/// \brief A range-for compatible wrapper for ArrowArray of binary or utf8
diff --git a/c/vendor/vendor_nanoarrow.sh b/c/vendor/vendor_nanoarrow.sh
index d11899230..64e29abe6 100755
--- a/c/vendor/vendor_nanoarrow.sh
+++ b/c/vendor/vendor_nanoarrow.sh
@@ -21,7 +21,7 @@
main() {
local -r repo_url="https://github.com/apache/arrow-nanoarrow"
# Check releases page: https://github.com/apache/arrow-nanoarrow/releases/
- local -r commit_sha=af347fa31d0e2dca6f6b6f818849a437244b6bf5
+ local -r commit_sha=9fad80292360d0f3978264c11ba865ccc94020d8
echo "Fetching $commit_sha from $repo_url"
SCRATCH=$(mktemp -d)