This is an automated email from the ASF dual-hosted git repository.

github-bot 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 14dfcaa  Update dist/ for commit 
248b498c505aaba88f5593fb34f7c2e21e66c95d
14dfcaa is described below

commit 14dfcaaf9745a83af5b265ae14b9075e749e2d82
Author: GitHub Actions <[email protected]>
AuthorDate: Wed Dec 20 01:18:51 2023 +0000

    Update dist/ for commit 248b498c505aaba88f5593fb34f7c2e21e66c95d
---
 dist/nanoarrow.c           |  56 ++++-----
 dist/nanoarrow.h           | 243 +++++++++++++++++++++++++++++++--------
 dist/nanoarrow.hpp         | 278 ++++++++++++++++++++++++++++++++++-----------
 dist/nanoarrow_device.c    |  10 +-
 dist/nanoarrow_ipc.c       |  20 ++--
 dist/nanoarrow_testing.hpp |  66 ++++++++---
 6 files changed, 498 insertions(+), 175 deletions(-)

diff --git a/dist/nanoarrow.c b/dist/nanoarrow.c
index 31e0b86..ff67036 100644
--- a/dist/nanoarrow.c
+++ b/dist/nanoarrow.c
@@ -28,7 +28,7 @@ const char* ArrowNanoarrowVersion(void) { return 
NANOARROW_VERSION; }
 
 int ArrowNanoarrowVersionInt(void) { return NANOARROW_VERSION_INT; }
 
-int ArrowErrorSet(struct ArrowError* error, const char* fmt, ...) {
+ArrowErrorCode ArrowErrorSet(struct ArrowError* error, const char* fmt, ...) {
   if (error == NULL) {
     return NANOARROW_OK;
   }
@@ -49,14 +49,6 @@ int ArrowErrorSet(struct ArrowError* error, const char* fmt, 
...) {
   }
 }
 
-const char* ArrowErrorMessage(struct ArrowError* error) {
-  if (error == NULL) {
-    return "";
-  } else {
-    return error->message;
-  }
-}
-
 void ArrowLayoutInit(struct ArrowLayout* layout, enum ArrowType storage_type) {
   layout->buffer_type[0] = NANOARROW_BUFFER_TYPE_VALIDITY;
   layout->buffer_data_type[0] = NANOARROW_TYPE_BOOL;
@@ -255,7 +247,7 @@ struct ArrowBufferAllocator ArrowBufferDeallocator(
 
 #include "nanoarrow.h"
 
-static void ArrowSchemaRelease(struct ArrowSchema* schema) {
+static void ArrowSchemaReleaseInternal(struct ArrowSchema* schema) {
   if (schema->format != NULL) ArrowFree((void*)schema->format);
   if (schema->name != NULL) ArrowFree((void*)schema->name);
   if (schema->metadata != NULL) ArrowFree((void*)schema->metadata);
@@ -267,7 +259,7 @@ static void ArrowSchemaRelease(struct ArrowSchema* schema) {
     for (int64_t i = 0; i < schema->n_children; i++) {
       if (schema->children[i] != NULL) {
         if (schema->children[i]->release != NULL) {
-          schema->children[i]->release(schema->children[i]);
+          ArrowSchemaRelease(schema->children[i]);
         }
 
         ArrowFree(schema->children[i]);
@@ -282,7 +274,7 @@ static void ArrowSchemaRelease(struct ArrowSchema* schema) {
   // release() callback.
   if (schema->dictionary != NULL) {
     if (schema->dictionary->release != NULL) {
-      schema->dictionary->release(schema->dictionary);
+      ArrowSchemaRelease(schema->dictionary);
     }
 
     ArrowFree(schema->dictionary);
@@ -404,7 +396,7 @@ void ArrowSchemaInit(struct ArrowSchema* schema) {
   schema->children = NULL;
   schema->dictionary = NULL;
   schema->private_data = NULL;
-  schema->release = &ArrowSchemaRelease;
+  schema->release = &ArrowSchemaReleaseInternal;
 }
 
 ArrowErrorCode ArrowSchemaSetType(struct ArrowSchema* schema, enum ArrowType 
type) {
@@ -440,7 +432,7 @@ ArrowErrorCode ArrowSchemaInitFromType(struct ArrowSchema* 
schema, enum ArrowTyp
 
   int result = ArrowSchemaSetType(schema, type);
   if (result != NANOARROW_OK) {
-    schema->release(schema);
+    ArrowSchemaRelease(schema);
     return result;
   }
 
@@ -722,7 +714,7 @@ ArrowErrorCode ArrowSchemaDeepCopy(const struct 
ArrowSchema* schema,
 
   int result = ArrowSchemaSetFormat(schema_out, schema->format);
   if (result != NANOARROW_OK) {
-    schema_out->release(schema_out);
+    ArrowSchemaRelease(schema_out);
     return result;
   }
 
@@ -730,26 +722,26 @@ ArrowErrorCode ArrowSchemaDeepCopy(const struct 
ArrowSchema* schema,
 
   result = ArrowSchemaSetName(schema_out, schema->name);
   if (result != NANOARROW_OK) {
-    schema_out->release(schema_out);
+    ArrowSchemaRelease(schema_out);
     return result;
   }
 
   result = ArrowSchemaSetMetadata(schema_out, schema->metadata);
   if (result != NANOARROW_OK) {
-    schema_out->release(schema_out);
+    ArrowSchemaRelease(schema_out);
     return result;
   }
 
   result = ArrowSchemaAllocateChildren(schema_out, schema->n_children);
   if (result != NANOARROW_OK) {
-    schema_out->release(schema_out);
+    ArrowSchemaRelease(schema_out);
     return result;
   }
 
   for (int64_t i = 0; i < schema->n_children; i++) {
     result = ArrowSchemaDeepCopy(schema->children[i], schema_out->children[i]);
     if (result != NANOARROW_OK) {
-      schema_out->release(schema_out);
+      ArrowSchemaRelease(schema_out);
       return result;
     }
   }
@@ -757,13 +749,13 @@ ArrowErrorCode ArrowSchemaDeepCopy(const struct 
ArrowSchema* schema,
   if (schema->dictionary != NULL) {
     result = ArrowSchemaAllocateDictionary(schema_out);
     if (result != NANOARROW_OK) {
-      schema_out->release(schema_out);
+      ArrowSchemaRelease(schema_out);
       return result;
     }
 
     result = ArrowSchemaDeepCopy(schema->dictionary, schema_out->dictionary);
     if (result != NANOARROW_OK) {
-      schema_out->release(schema_out);
+      ArrowSchemaRelease(schema_out);
       return result;
     }
   }
@@ -1780,7 +1772,7 @@ ArrowErrorCode ArrowMetadataBuilderRemove(struct 
ArrowBuffer* buffer,
 
 #include "nanoarrow.h"
 
-static void ArrowArrayRelease(struct ArrowArray* array) {
+static void ArrowArrayReleaseInternal(struct ArrowArray* array) {
   // Release buffers held by this array
   struct ArrowArrayPrivateData* private_data =
       (struct ArrowArrayPrivateData*)array->private_data;
@@ -1798,7 +1790,7 @@ static void ArrowArrayRelease(struct ArrowArray* array) {
     for (int64_t i = 0; i < array->n_children; i++) {
       if (array->children[i] != NULL) {
         if (array->children[i]->release != NULL) {
-          array->children[i]->release(array->children[i]);
+          ArrowArrayRelease(array->children[i]);
         }
 
         ArrowFree(array->children[i]);
@@ -1813,7 +1805,7 @@ static void ArrowArrayRelease(struct ArrowArray* array) {
   // release() callback.
   if (array->dictionary != NULL) {
     if (array->dictionary->release != NULL) {
-      array->dictionary->release(array->dictionary);
+      ArrowArrayRelease(array->dictionary);
     }
 
     ArrowFree(array->dictionary);
@@ -1891,7 +1883,7 @@ ArrowErrorCode ArrowArrayInitFromType(struct ArrowArray* 
array,
   array->buffers = NULL;
   array->children = NULL;
   array->dictionary = NULL;
-  array->release = &ArrowArrayRelease;
+  array->release = &ArrowArrayReleaseInternal;
   array->private_data = NULL;
 
   struct ArrowArrayPrivateData* private_data =
@@ -1913,7 +1905,7 @@ ArrowErrorCode ArrowArrayInitFromType(struct ArrowArray* 
array,
 
   int result = ArrowArraySetStorageType(array, storage_type);
   if (result != NANOARROW_OK) {
-    array->release(array);
+    ArrowArrayRelease(array);
     return result;
   }
 
@@ -1938,7 +1930,7 @@ ArrowErrorCode ArrowArrayInitFromArrayView(struct 
ArrowArray* array,
   if (array_view->n_children > 0) {
     result = ArrowArrayAllocateChildren(array, array_view->n_children);
     if (result != NANOARROW_OK) {
-      array->release(array);
+      ArrowArrayRelease(array);
       return result;
     }
 
@@ -1946,7 +1938,7 @@ ArrowErrorCode ArrowArrayInitFromArrayView(struct 
ArrowArray* array,
       result =
           ArrowArrayInitFromArrayView(array->children[i], 
array_view->children[i], error);
       if (result != NANOARROW_OK) {
-        array->release(array);
+        ArrowArrayRelease(array);
         return result;
       }
     }
@@ -1955,14 +1947,14 @@ ArrowErrorCode ArrowArrayInitFromArrayView(struct 
ArrowArray* array,
   if (array_view->dictionary != NULL) {
     result = ArrowArrayAllocateDictionary(array);
     if (result != NANOARROW_OK) {
-      array->release(array);
+      ArrowArrayRelease(array);
       return result;
     }
 
     result =
         ArrowArrayInitFromArrayView(array->dictionary, array_view->dictionary, 
error);
     if (result != NANOARROW_OK) {
-      array->release(array);
+      ArrowArrayRelease(array);
       return result;
     }
   }
@@ -3019,12 +3011,12 @@ static void ArrowBasicArrayStreamRelease(struct 
ArrowArrayStream* array_stream)
       (struct BasicArrayStreamPrivate*)array_stream->private_data;
 
   if (private_data->schema.release != NULL) {
-    private_data->schema.release(&private_data->schema);
+    ArrowSchemaRelease(&private_data->schema);
   }
 
   for (int64_t i = 0; i < private_data->n_arrays; i++) {
     if (private_data->arrays[i].release != NULL) {
-      private_data->arrays[i].release(&private_data->arrays[i]);
+      ArrowArrayRelease(&private_data->arrays[i]);
     }
   }
 
diff --git a/dist/nanoarrow.h b/dist/nanoarrow.h
index d0fea4a..8fde828 100644
--- a/dist/nanoarrow.h
+++ b/dist/nanoarrow.h
@@ -162,25 +162,6 @@ struct ArrowArrayStream {
 #endif  // ARROW_C_STREAM_INTERFACE
 #endif  // ARROW_FLAG_DICTIONARY_ORDERED
 
-/// \brief Move the contents of src into dst and set src->release to NULL
-static inline void ArrowSchemaMove(struct ArrowSchema* src, struct 
ArrowSchema* dst) {
-  memcpy(dst, src, sizeof(struct ArrowSchema));
-  src->release = NULL;
-}
-
-/// \brief Move the contents of src into dst and set src->release to NULL
-static inline void ArrowArrayMove(struct ArrowArray* src, struct ArrowArray* 
dst) {
-  memcpy(dst, src, sizeof(struct ArrowArray));
-  src->release = NULL;
-}
-
-/// \brief Move the contents of src into dst and set src->release to NULL
-static inline void ArrowArrayStreamMove(struct ArrowArrayStream* src,
-                                        struct ArrowArrayStream* dst) {
-  memcpy(dst, src, sizeof(struct ArrowArrayStream));
-  src->release = NULL;
-}
-
 /// @}
 
 // Utility macros
@@ -228,6 +209,55 @@ static inline void ArrowArrayStreamMove(struct 
ArrowArrayStream* src,
 /// \ingroup nanoarrow-errors
 typedef int ArrowErrorCode;
 
+/// \brief Error type containing a UTF-8 encoded message.
+/// \ingroup nanoarrow-errors
+struct ArrowError {
+  /// \brief A character buffer with space for an error message.
+  char message[1024];
+};
+
+/// \brief Ensure an ArrowError is null-terminated by zeroing the first 
character.
+/// \ingroup nanoarrow-errors
+///
+/// If error is NULL, this function does nothing.
+static inline void ArrowErrorInit(struct ArrowError* error) {
+  if (error != NULL) {
+    error->message[0] = '\0';
+  }
+}
+
+/// \brief Get the contents of an error
+/// \ingroup nanoarrow-errors
+///
+/// If error is NULL, returns "", or returns the contents of the error message
+/// otherwise.
+static inline const char* ArrowErrorMessage(struct ArrowError* error) {
+  if (error == NULL) {
+    return "";
+  } else {
+    return error->message;
+  }
+}
+
+/// \brief Set the contents of an error from an existing null-terminated string
+/// \ingroup nanoarrow-errors
+///
+/// If error is NULL, this function does nothing.
+static inline void ArrowErrorSetString(struct ArrowError* error, const char* 
src) {
+  if (error == NULL) {
+    return;
+  }
+
+  int64_t src_len = strlen(src);
+  if (src_len >= ((int64_t)sizeof(error->message))) {
+    memcpy(error->message, src, sizeof(error->message) - 1);
+    error->message[sizeof(error->message) - 1] = '\0';
+  } else {
+    memcpy(error->message, src, src_len);
+    error->message[src_len] = '\0';
+  }
+}
+
 /// \brief Check the result of an expression and return it if not NANOARROW_OK
 /// \ingroup nanoarrow-errors
 #define NANOARROW_RETURN_NOT_OK(EXPR) \
@@ -245,11 +275,11 @@ typedef int ArrowErrorCode;
       _NANOARROW_MAKE_NAME(errno_status_, __COUNTER__), EXPR, ERROR_EXPR, 
#EXPR)
 
 #if defined(NANOARROW_DEBUG) && !defined(NANOARROW_PRINT_AND_DIE)
-#define NANOARROW_PRINT_AND_DIE(VALUE, EXPR_STR)                               
   \
-  do {                                                                         
   \
-    fprintf(stderr, "%s failed with errno %d\n* %s:%d\n", EXPR_STR, 
(int)(VALUE), \
-            __FILE__, (int)__LINE__);                                          
   \
-    abort();                                                                   
   \
+#define NANOARROW_PRINT_AND_DIE(VALUE, EXPR_STR)                               
  \
+  do {                                                                         
  \
+    fprintf(stderr, "%s failed with code %d\n* %s:%d\n", EXPR_STR, 
(int)(VALUE), \
+            __FILE__, (int)__LINE__);                                          
  \
+    abort();                                                                   
  \
   } while (0)
 #endif
 
@@ -270,10 +300,99 @@ typedef int ArrowErrorCode;
 /// This macro is provided as a convenience for users and is not used 
internally.
 #define NANOARROW_ASSERT_OK(EXPR) \
   _NANOARROW_ASSERT_OK_IMPL(_NANOARROW_MAKE_NAME(errno_status_, __COUNTER__), 
EXPR, #EXPR)
+
+#define _NANOARROW_DCHECK_IMPL(EXPR, EXPR_STR)          \
+  do {                                                  \
+    if (!(EXPR)) NANOARROW_PRINT_AND_DIE(-1, EXPR_STR); \
+  } while (0)
+
+#define NANOARROW_DCHECK(EXPR) _NANOARROW_DCHECK_IMPL(EXPR, #EXPR)
 #else
 #define NANOARROW_ASSERT_OK(EXPR) EXPR
+#define NANOARROW_DCHECK(EXPR)
 #endif
 
+static inline void ArrowSchemaMove(struct ArrowSchema* src, struct 
ArrowSchema* dst) {
+  NANOARROW_DCHECK(src != NULL);
+  NANOARROW_DCHECK(dst != NULL);
+
+  memcpy(dst, src, sizeof(struct ArrowSchema));
+  src->release = NULL;
+}
+
+static inline void ArrowSchemaRelease(struct ArrowSchema* schema) {
+  NANOARROW_DCHECK(schema != NULL);
+  schema->release(schema);
+  NANOARROW_DCHECK(schema->release == NULL);
+}
+
+static inline void ArrowArrayMove(struct ArrowArray* src, struct ArrowArray* 
dst) {
+  NANOARROW_DCHECK(src != NULL);
+  NANOARROW_DCHECK(dst != NULL);
+
+  memcpy(dst, src, sizeof(struct ArrowArray));
+  src->release = NULL;
+}
+
+static inline void ArrowArrayRelease(struct ArrowArray* array) {
+  NANOARROW_DCHECK(array != NULL);
+  array->release(array);
+  NANOARROW_DCHECK(array->release == NULL);
+}
+
+static inline void ArrowArrayStreamMove(struct ArrowArrayStream* src,
+                                        struct ArrowArrayStream* dst) {
+  NANOARROW_DCHECK(src != NULL);
+  NANOARROW_DCHECK(dst != NULL);
+
+  memcpy(dst, src, sizeof(struct ArrowArrayStream));
+  src->release = NULL;
+}
+
+static inline const char* ArrowArrayStreamGetLastError(
+    struct ArrowArrayStream* array_stream) {
+  NANOARROW_DCHECK(array_stream != NULL);
+
+  const char* value = array_stream->get_last_error(array_stream);
+  if (value == NULL) {
+    return "";
+  } else {
+    return value;
+  }
+}
+
+static inline ArrowErrorCode ArrowArrayStreamGetSchema(
+    struct ArrowArrayStream* array_stream, struct ArrowSchema* out,
+    struct ArrowError* error) {
+  NANOARROW_DCHECK(array_stream != NULL);
+
+  int result = array_stream->get_schema(array_stream, out);
+  if (result != NANOARROW_OK && error != NULL) {
+    ArrowErrorSetString(error, ArrowArrayStreamGetLastError(array_stream));
+  }
+
+  return result;
+}
+
+static inline ArrowErrorCode ArrowArrayStreamGetNext(
+    struct ArrowArrayStream* array_stream, struct ArrowArray* out,
+    struct ArrowError* error) {
+  NANOARROW_DCHECK(array_stream != NULL);
+
+  int result = array_stream->get_next(array_stream, out);
+  if (result != NANOARROW_OK && error != NULL) {
+    ArrowErrorSetString(error, ArrowArrayStreamGetLastError(array_stream));
+  }
+
+  return result;
+}
+
+static inline void ArrowArrayStreamRelease(struct ArrowArrayStream* 
array_stream) {
+  NANOARROW_DCHECK(array_stream != NULL);
+  array_stream->release(array_stream);
+  NANOARROW_DCHECK(array_stream->release == NULL);
+}
+
 static char _ArrowIsLittleEndian(void) {
   uint32_t check = 1;
   char first_byte;
@@ -849,7 +968,6 @@ static inline void ArrowDecimalSetBytes(struct 
ArrowDecimal* decimal,
 #define ArrowNanoarrowVersion NANOARROW_SYMBOL(NANOARROW_NAMESPACE, 
ArrowNanoarrowVersion)
 #define ArrowNanoarrowVersionInt \
   NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowNanoarrowVersionInt)
-#define ArrowErrorMessage NANOARROW_SYMBOL(NANOARROW_NAMESPACE, 
ArrowErrorMessage)
 #define ArrowMalloc NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowMalloc)
 #define ArrowRealloc NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowRealloc)
 #define ArrowFree NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowFree)
@@ -995,6 +1113,60 @@ struct ArrowBufferAllocator ArrowBufferDeallocator(
 
 /// @}
 
+/// \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);
+
+/// \brief Call the release callback of an ArrowSchema
+/// \ingroup nanoarrow-arrow-cdata
+static inline void ArrowSchemaRelease(struct ArrowSchema* schema);
+
+/// \brief Move the contents of an src ArrowArray into dst and set 
src->release to NULL
+/// \ingroup nanoarrow-arrow-cdata
+static inline void ArrowArrayMove(struct ArrowArray* src, struct ArrowArray* 
dst);
+
+/// \brief Call the release callback of an ArrowArray
+static inline void ArrowArrayRelease(struct ArrowArray* array);
+
+/// \brief Move the contents of an src ArrowArrayStream into dst and set 
src->release to
+/// NULL \ingroup nanoarrow-arrow-cdata
+static inline void ArrowArrayStreamMove(struct ArrowArrayStream* src,
+                                        struct ArrowArrayStream* dst);
+
+/// \brief Call the get_schema callback of an ArrowArrayStream
+/// \ingroup nanoarrow-arrow-cdata
+///
+/// Unlike the get_schema callback, this wrapper checks the return code
+/// and propagates the error reported by get_last_error into error. This
+/// makes it significantly less verbose to iterate over array streams
+/// using NANOARROW_RETURN_NOT_OK()-style error handling.
+static inline ArrowErrorCode ArrowArrayStreamGetSchema(
+    struct ArrowArrayStream* array_stream, struct ArrowSchema* out,
+    struct ArrowError* error);
+
+/// \brief Call the get_schema callback of an ArrowArrayStream
+/// \ingroup nanoarrow-arrow-cdata
+///
+/// Unlike the get_next callback, this wrapper checks the return code
+/// and propagates the error reported by get_last_error into error. This
+/// makes it significantly less verbose to iterate over array streams
+/// using NANOARROW_RETURN_NOT_OK()-style error handling.
+static inline ArrowErrorCode ArrowArrayStreamGetNext(
+    struct ArrowArrayStream* array_stream, struct ArrowArray* out,
+    struct ArrowError* error);
+
+/// \brief Call the get_next callback of an ArrowArrayStream
+/// \ingroup nanoarrow-arrow-cdata
+///
+/// Unlike the get_next callback, this function never returns NULL (i.e., its
+/// result is safe to use in printf-style error formatters). Null values from 
the
+/// original callback are reported as "<get_last_error() returned NULL>".
+static inline const char* ArrowArrayStreamGetLastError(
+    struct ArrowArrayStream* array_stream);
+
+/// \brief Call the release callback of an ArrowArrayStream
+static inline void ArrowArrayStreamRelease(struct ArrowArrayStream* 
array_stream);
+
 /// \defgroup nanoarrow-errors Error handling
 ///
 /// Functions generally return an errno-compatible error code; functions that
@@ -1014,32 +1186,11 @@ struct ArrowBufferAllocator ArrowBufferDeallocator(
 ///
 /// @{
 
-/// \brief Error type containing a UTF-8 encoded message.
-struct ArrowError {
-  /// \brief A character buffer with space for an error message.
-  char message[1024];
-};
-
-/// \brief Ensure an ArrowError is null-terminated by zeroing the first 
character.
-///
-/// If error is NULL, this function does nothing.
-static inline void ArrowErrorInit(struct ArrowError* error) {
-  if (error) {
-    error->message[0] = '\0';
-  }
-}
-
 /// \brief Set the contents of an error using printf syntax.
 ///
 /// If error is NULL, this function does nothing and returns NANOARROW_OK.
 ArrowErrorCode ArrowErrorSet(struct ArrowError* error, const char* fmt, ...);
 
-/// \brief Get the contents of an error
-///
-/// If error is NULL, returns "", or returns the contents of the error message
-/// otherwise.
-const char* ArrowErrorMessage(struct ArrowError* error);
-
 /// @}
 
 /// \defgroup nanoarrow-utils Utility data structures
diff --git a/dist/nanoarrow.hpp b/dist/nanoarrow.hpp
index 2638769..15914ce 100644
--- a/dist/nanoarrow.hpp
+++ b/dist/nanoarrow.hpp
@@ -88,70 +88,108 @@ namespace internal {
 ///
 /// @{
 
-static inline void init_pointer(struct ArrowSchema* data) { data->release = 
nullptr; }
+template <typename T>
+static inline void init_pointer(T* data);
+
+template <typename T>
+static inline void move_pointer(T* src, T* dst);
+
+template <typename T>
+static inline void release_pointer(T* data);
+
+template <>
+inline void init_pointer(struct ArrowSchema* data) {
+  data->release = nullptr;
+}
 
-static inline void move_pointer(struct ArrowSchema* src, struct ArrowSchema* 
dst) {
+template <>
+inline void move_pointer(struct ArrowSchema* src, struct ArrowSchema* dst) {
   ArrowSchemaMove(src, dst);
 }
 
-static inline void release_pointer(struct ArrowSchema* data) {
+template <>
+inline void release_pointer(struct ArrowSchema* data) {
   if (data->release != nullptr) {
     data->release(data);
   }
 }
 
-static inline void init_pointer(struct ArrowArray* data) { data->release = 
nullptr; }
+template <>
+inline void init_pointer(struct ArrowArray* data) {
+  data->release = nullptr;
+}
 
-static inline void move_pointer(struct ArrowArray* src, struct ArrowArray* 
dst) {
+template <>
+inline void move_pointer(struct ArrowArray* src, struct ArrowArray* dst) {
   ArrowArrayMove(src, dst);
 }
 
-static inline void release_pointer(struct ArrowArray* data) {
+template <>
+inline void release_pointer(struct ArrowArray* data) {
   if (data->release != nullptr) {
     data->release(data);
   }
 }
 
-static inline void init_pointer(struct ArrowArrayStream* data) {
+template <>
+inline void init_pointer(struct ArrowArrayStream* data) {
   data->release = nullptr;
 }
 
-static inline void move_pointer(struct ArrowArrayStream* src,
-                                struct ArrowArrayStream* dst) {
+template <>
+inline void move_pointer(struct ArrowArrayStream* src, struct 
ArrowArrayStream* dst) {
   ArrowArrayStreamMove(src, dst);
 }
 
-static inline void release_pointer(ArrowArrayStream* data) {
+template <>
+inline void release_pointer(ArrowArrayStream* data) {
   if (data->release != nullptr) {
     data->release(data);
   }
 }
 
-static inline void init_pointer(struct ArrowBuffer* data) { 
ArrowBufferInit(data); }
+template <>
+inline void init_pointer(struct ArrowBuffer* data) {
+  ArrowBufferInit(data);
+}
 
-static inline void move_pointer(struct ArrowBuffer* src, struct ArrowBuffer* 
dst) {
+template <>
+inline void move_pointer(struct ArrowBuffer* src, struct ArrowBuffer* dst) {
   ArrowBufferMove(src, dst);
 }
 
-static inline void release_pointer(struct ArrowBuffer* data) { 
ArrowBufferReset(data); }
+template <>
+inline void release_pointer(struct ArrowBuffer* data) {
+  ArrowBufferReset(data);
+}
 
-static inline void init_pointer(struct ArrowBitmap* data) { 
ArrowBitmapInit(data); }
+template <>
+inline void init_pointer(struct ArrowBitmap* data) {
+  ArrowBitmapInit(data);
+}
 
-static inline void move_pointer(struct ArrowBitmap* src, struct ArrowBitmap* 
dst) {
+template <>
+inline void move_pointer(struct ArrowBitmap* src, struct ArrowBitmap* dst) {
   ArrowBitmapMove(src, dst);
 }
 
-static inline void release_pointer(struct ArrowBitmap* data) { 
ArrowBitmapReset(data); }
+template <>
+inline void release_pointer(struct ArrowBitmap* data) {
+  ArrowBitmapReset(data);
+}
 
-static inline void init_pointer(struct ArrowArrayView* data) {
+template <>
+inline void init_pointer(struct ArrowArrayView* data) {
   ArrowArrayViewInitFromType(data, NANOARROW_TYPE_UNINITIALIZED);
 }
 
-static inline void move_pointer(struct ArrowArrayView* src, struct 
ArrowArrayView* dst) {
+template <>
+inline void move_pointer(struct ArrowArrayView* src, struct ArrowArrayView* 
dst) {
   ArrowArrayViewMove(src, dst);
 }
 
-static inline void release_pointer(struct ArrowArrayView* data) {
+template <>
+inline void release_pointer(struct ArrowArrayView* data) {
   ArrowArrayViewReset(data);
 }
 
@@ -231,28 +269,124 @@ using UniqueArrayView = internal::Unique<struct 
ArrowArrayView>;
 
 /// \defgroup nanoarrow_hpp-array-stream ArrayStream helpers
 ///
-/// These classes provide simple struct ArrowArrayStream implementations that
+/// These classes provide simple ArrowArrayStream implementations that
 /// can be extended to help simplify the process of creating a valid
 /// ArrowArrayStream implementation or used as-is for testing.
 ///
 /// @{
 
+/// @brief Export an ArrowArrayStream from a standard C++ class
+/// @tparam T A class with methods `int GetSchema(ArrowSchema*)`, `int
+/// GetNext(ArrowArray*)`, and `const char* GetLastError()`
+///
+/// This class allows a standard C++ class to be exported to a generic 
ArrowArrayStream
+/// consumer by mapping C callback invocations to method calls on an instance 
of the
+/// object whose lifecycle is owned by the ArrowArrayStream. See 
VectorArrayStream for
+/// minimal useful example of this pattern.
+///
+/// The methods must be accessible to the ArrayStreamFactory, either as public 
methods or
+/// by declaring ArrayStreamFactory<ImplClass> a friend. Implementors are 
encouraged (but
+/// not required) to implement a ToArrayStream(ArrowArrayStream*) that creates 
a new
+/// instance owned by the ArrowArrayStream and moves the relevant data to that 
instance.
+///
+/// An example implementation might be:
+///
+/// \code
+/// class StreamImpl {
+///  public:
+///   // Public methods (e.g., constructor) used from C++ to initialize 
relevant data
+///
+///   // Idiomatic exporter to move data + lifecycle responsibility to an 
instance
+///   // managed by the ArrowArrayStream callbacks
+///   void ToArrayStream(struct ArrowArrayStream* out) {
+///     ArrayStreamFactory<StreamImpl>::InitArrayStream(new StreamImpl(...), 
out);
+///   }
+///
+///  private:
+///   // Make relevant methods available to the ArrayStreamFactory
+///   friend class ArrayStreamFactory<StreamImpl>;
+///
+///   // Method implementations (called from C, not normally interacted with 
from C++)
+///   int GetSchema(struct ArrowSchema* schema) { return ENOTSUP; }
+///   int GetNext(struct ArrowArray* array) { return ENOTSUP; }
+///   const char* GetLastError() { nullptr; }
+/// };
+/// \endcode
+///
+/// An example usage might be:
+///
+/// \code
+/// // Call constructor and/or public methods to initialize relevant data
+/// StreamImpl impl;
+///
+/// // Export to ArrowArrayStream after data are finalized
+/// UniqueArrayStream stream;
+/// impl.ToArrayStream(stream.get());
+/// \endcode
+template <typename T>
+class ArrayStreamFactory {
+ public:
+  /// \brief Take ownership of instance and populate callbacks of out
+  static void InitArrayStream(T* instance, struct ArrowArrayStream* out) {
+    out->get_schema = &get_schema_wrapper;
+    out->get_next = &get_next_wrapper;
+    out->get_last_error = &get_last_error_wrapper;
+    out->release = &release_wrapper;
+    out->private_data = instance;
+  }
+
+ private:
+  static int get_schema_wrapper(struct ArrowArrayStream* stream,
+                                struct ArrowSchema* schema) {
+    return reinterpret_cast<T*>(stream->private_data)->GetSchema(schema);
+  }
+
+  static int get_next_wrapper(struct ArrowArrayStream* stream, struct 
ArrowArray* array) {
+    return reinterpret_cast<T*>(stream->private_data)->GetNext(array);
+  }
+
+  static const char* get_last_error_wrapper(struct ArrowArrayStream* stream) {
+    return reinterpret_cast<T*>(stream->private_data)->GetLastError();
+  }
+
+  static void release_wrapper(struct ArrowArrayStream* stream) {
+    delete reinterpret_cast<T*>(stream->private_data);
+    stream->release = nullptr;
+    stream->private_data = nullptr;
+  }
+};
+
 /// \brief An empty array stream
 ///
-/// This class can be constructed from an enum ArrowType or
-/// struct ArrowSchema and implements a default get_next() method that
-/// always marks the output ArrowArray as released. This class can
-/// be extended with an implementation of get_next() for a custom
-/// source.
+/// This class can be constructed from an struct ArrowSchema and implements a 
default
+/// get_next() method that always marks the output ArrowArray as released.
+///
+/// DEPRECATED (0.4.0): Early versions of nanoarrow allowed subclasses to 
override
+/// get_schema(), get_next(), and get_last_error(). This functionality will be 
removed
+/// in a future release: use the pattern documented in ArrayStreamFactory to 
create
+/// custom ArrowArrayStream implementations.
 class EmptyArrayStream {
  public:
+  /// \brief Create an EmptyArrayStream from an ArrowSchema
+  ///
+  /// Takes ownership of schema.
+  EmptyArrayStream(struct ArrowSchema* schema) : schema_(schema) {
+    ArrowErrorInit(&error_);
+  }
+
+  /// \brief Export to ArrowArrayStream
+  void ToArrayStream(struct ArrowArrayStream* out) {
+    EmptyArrayStream* impl = new EmptyArrayStream(schema_.get());
+    ArrayStreamFactory<EmptyArrayStream>::InitArrayStream(impl, out);
+  }
+
   /// \brief Create an empty UniqueArrayStream from a struct ArrowSchema
   ///
-  /// This object takes ownership of the schema and marks the source schema
-  /// as released.
+  /// DEPRECATED (0.4.0): Use the constructor + ToArrayStream() to export an
+  /// EmptyArrayStream to an ArrowArrayStream consumer.
   static UniqueArrayStream MakeUnique(struct ArrowSchema* schema) {
     UniqueArrayStream stream;
-    (new EmptyArrayStream(schema))->MakeStream(stream.get());
+    EmptyArrayStream(schema).ToArrayStream(stream.get());
     return stream;
   }
 
@@ -262,17 +396,7 @@ class EmptyArrayStream {
   UniqueSchema schema_;
   struct ArrowError error_;
 
-  EmptyArrayStream(struct ArrowSchema* schema) : schema_(schema) {
-    error_.message[0] = '\0';
-  }
-
-  void MakeStream(struct ArrowArrayStream* stream) {
-    stream->get_schema = &get_schema_wrapper;
-    stream->get_next = &get_next_wrapper;
-    stream->get_last_error = &get_last_error_wrapper;
-    stream->release = &release_wrapper;
-    stream->private_data = this;
-  }
+  void MakeStream(struct ArrowArrayStream* stream) { ToArrayStream(stream); }
 
   virtual int get_schema(struct ArrowSchema* schema) {
     return ArrowSchemaDeepCopy(schema_.get(), schema);
@@ -286,54 +410,72 @@ class EmptyArrayStream {
   virtual const char* get_last_error() { return error_.message; }
 
  private:
-  static int get_schema_wrapper(struct ArrowArrayStream* stream,
-                                struct ArrowSchema* schema) {
-    return 
reinterpret_cast<EmptyArrayStream*>(stream->private_data)->get_schema(schema);
-  }
+  friend class ArrayStreamFactory<EmptyArrayStream>;
 
-  static int get_next_wrapper(struct ArrowArrayStream* stream, struct 
ArrowArray* array) {
-    return 
reinterpret_cast<EmptyArrayStream*>(stream->private_data)->get_next(array);
-  }
+  int GetSchema(struct ArrowSchema* schema) { return get_schema(schema); }
 
-  static const char* get_last_error_wrapper(struct ArrowArrayStream* stream) {
-    return 
reinterpret_cast<EmptyArrayStream*>(stream->private_data)->get_last_error();
-  }
+  int GetNext(struct ArrowArray* array) { return get_next(array); }
 
-  static void release_wrapper(struct ArrowArrayStream* stream) {
-    delete reinterpret_cast<EmptyArrayStream*>(stream->private_data);
-    stream->release = nullptr;
-    stream->private_data = nullptr;
-  }
+  const char* GetLastError() { return get_last_error(); }
 };
 
-/// \brief Implementation of an ArrowArrayStream backed by a vector of 
ArrowArray objects
-class VectorArrayStream : public EmptyArrayStream {
+/// \brief Implementation of an ArrowArrayStream backed by a vector of 
UniqueArray objects
+class VectorArrayStream {
  public:
+  /// \brief Create a VectorArrayStream from an ArrowSchema + vector of 
UniqueArray
+  ///
+  /// Takes ownership of schema and moves arrays if possible.
+  VectorArrayStream(struct ArrowSchema* schema, std::vector<UniqueArray> 
arrays)
+      : offset_(0), schema_(schema), arrays_(std::move(arrays)) {}
+
+  /// \brief Create a one-shot VectorArrayStream from an ArrowSchema + 
ArrowArray
+  ///
+  /// Takes ownership of schema and array.
+  VectorArrayStream(struct ArrowSchema* schema, struct ArrowArray* array)
+      : offset_(0), schema_(schema) {
+    arrays_.emplace_back(array);
+  }
+
+  /// \brief Export to ArrowArrayStream
+  void ToArrayStream(struct ArrowArrayStream* out) {
+    VectorArrayStream* impl = new VectorArrayStream(schema_.get(), 
std::move(arrays_));
+    ArrayStreamFactory<VectorArrayStream>::InitArrayStream(impl, out);
+  }
+
   /// \brief Create a UniqueArrowArrayStream from an existing array
   ///
-  /// Takes ownership of the schema and the array.
+  /// DEPRECATED (0.4.0): Use the constructors + ToArrayStream() to export a
+  /// VectorArrayStream to an ArrowArrayStream consumer.
   static UniqueArrayStream MakeUnique(struct ArrowSchema* schema,
                                       struct ArrowArray* array) {
-    std::vector<UniqueArray> arrays;
-    arrays.emplace_back(array);
-    return MakeUnique(schema, std::move(arrays));
+    UniqueArrayStream stream;
+    VectorArrayStream(schema, array).ToArrayStream(stream.get());
+    return stream;
   }
 
   /// \brief Create a UniqueArrowArrayStream from existing arrays
   ///
-  /// This object takes ownership of the schema and arrays.
+  /// DEPRECATED (0.4.0): Use the constructor + ToArrayStream() to export a
+  /// VectorArrayStream to an ArrowArrayStream consumer.
   static UniqueArrayStream MakeUnique(struct ArrowSchema* schema,
                                       std::vector<UniqueArray> arrays) {
     UniqueArrayStream stream;
-    (new VectorArrayStream(schema, 
std::move(arrays)))->MakeStream(stream.get());
+    VectorArrayStream(schema, std::move(arrays)).ToArrayStream(stream.get());
     return stream;
   }
 
- protected:
-  VectorArrayStream(struct ArrowSchema* schema, std::vector<UniqueArray> 
arrays)
-      : EmptyArrayStream(schema), arrays_(std::move(arrays)), offset_(0) {}
+ private:
+  int64_t offset_;
+  UniqueSchema schema_;
+  std::vector<UniqueArray> arrays_;
+
+  friend class ArrayStreamFactory<VectorArrayStream>;
 
-  int get_next(struct ArrowArray* array) {
+  int GetSchema(struct ArrowSchema* schema) {
+    return ArrowSchemaDeepCopy(schema_.get(), schema);
+  }
+
+  int GetNext(struct ArrowArray* array) {
     if (offset_ < static_cast<int64_t>(arrays_.size())) {
       arrays_[offset_++].move(array);
     } else {
@@ -343,9 +485,7 @@ class VectorArrayStream : public EmptyArrayStream {
     return NANOARROW_OK;
   }
 
- private:
-  std::vector<UniqueArray> arrays_;
-  int64_t offset_;
+  const char* GetLastError() { return ""; }
 };
 
 /// @}
diff --git a/dist/nanoarrow_device.c b/dist/nanoarrow_device.c
index c4df3d1..0c76d96 100644
--- a/dist/nanoarrow_device.c
+++ b/dist/nanoarrow_device.c
@@ -226,7 +226,7 @@ static int ArrowDeviceBasicArrayStreamGetNext(struct 
ArrowDeviceArrayStream* arr
       private_data->naive_stream.get_next(&private_data->naive_stream, &tmp));
   int result = ArrowDeviceArrayInit(private_data->device, device_array, &tmp);
   if (result != NANOARROW_OK) {
-    tmp.release(&tmp);
+    ArrowArrayRelease(&tmp);
     return result;
   }
 
@@ -244,7 +244,7 @@ static void ArrowDeviceBasicArrayStreamRelease(
     struct ArrowDeviceArrayStream* array_stream) {
   struct ArrowBasicDeviceArrayStreamPrivate* private_data =
       (struct ArrowBasicDeviceArrayStreamPrivate*)array_stream->private_data;
-  private_data->naive_stream.release(&private_data->naive_stream);
+  ArrowArrayStreamRelease(&private_data->naive_stream);
   ArrowFree(private_data);
   array_stream->release = NULL;
 }
@@ -439,19 +439,19 @@ ArrowErrorCode ArrowDeviceArrayViewCopy(struct 
ArrowDeviceArrayView* src,
   int result =
       ArrowDeviceArrayViewCopyInternal(src->device, &src->array_view, 
device_dst, &tmp);
   if (result != NANOARROW_OK) {
-    tmp.release(&tmp);
+    ArrowArrayRelease(&tmp);
     return result;
   }
 
   result = ArrowArrayFinishBuilding(&tmp, NANOARROW_VALIDATION_LEVEL_MINIMAL, 
NULL);
   if (result != NANOARROW_OK) {
-    tmp.release(&tmp);
+    ArrowArrayRelease(&tmp);
     return result;
   }
 
   result = ArrowDeviceArrayInit(device_dst, dst, &tmp);
   if (result != NANOARROW_OK) {
-    tmp.release(&tmp);
+    ArrowArrayRelease(&tmp);
     return result;
   }
 
diff --git a/dist/nanoarrow_ipc.c b/dist/nanoarrow_ipc.c
index a37b910..54e7468 100644
--- a/dist/nanoarrow_ipc.c
+++ b/dist/nanoarrow_ipc.c
@@ -20517,7 +20517,7 @@ void ArrowIpcDecoderReset(struct ArrowIpcDecoder* 
decoder) {
     ArrowArrayViewReset(&private_data->array_view);
 
     if (private_data->array.release != NULL) {
-      private_data->array.release(&private_data->array);
+      ArrowArrayRelease(&private_data->array);
     }
 
     if (private_data->fields != NULL) {
@@ -21388,7 +21388,7 @@ ArrowErrorCode ArrowIpcDecoderDecodeSchema(struct 
ArrowIpcDecoder* decoder,
   ArrowSchemaInit(&tmp);
   int result = ArrowSchemaSetTypeStruct(&tmp, n_fields);
   if (result != NANOARROW_OK) {
-    tmp.release(&tmp);
+    ArrowSchemaRelease(&tmp);
     ArrowErrorSet(error, "Failed to allocate struct schema with %ld children",
                   (long)n_fields);
     return result;
@@ -21396,13 +21396,13 @@ ArrowErrorCode ArrowIpcDecoderDecodeSchema(struct 
ArrowIpcDecoder* decoder,
 
   result = ArrowIpcDecoderSetChildren(&tmp, fields, error);
   if (result != NANOARROW_OK) {
-    tmp.release(&tmp);
+    ArrowSchemaRelease(&tmp);
     return result;
   }
 
   result = ArrowIpcDecoderSetMetadata(&tmp, 
ns(Schema_custom_metadata(schema)), error);
   if (result != NANOARROW_OK) {
-    tmp.release(&tmp);
+    ArrowSchemaRelease(&tmp);
     return result;
   }
 
@@ -21449,7 +21449,7 @@ ArrowErrorCode ArrowIpcDecoderSetSchema(struct 
ArrowIpcDecoder* decoder,
   private_data->n_fields = 0;
   ArrowArrayViewReset(&private_data->array_view);
   if (private_data->array.release != NULL) {
-    private_data->array.release(&private_data->array);
+    ArrowArrayRelease(&private_data->array);
   }
   if (private_data->fields != NULL) {
     ArrowFree(private_data->fields);
@@ -21946,7 +21946,7 @@ ArrowErrorCode ArrowIpcDecoderDecodeArray(struct 
ArrowIpcDecoder* decoder,
   int result =
       ArrowIpcDecoderDecodeArrayInternal(decoder, i, &temp, validation_level, 
error);
   if (result != NANOARROW_OK && temp.release != NULL) {
-    temp.release(&temp);
+    ArrowArrayRelease(&temp);
   } else if (result != NANOARROW_OK) {
     return result;
   }
@@ -21970,7 +21970,7 @@ ArrowErrorCode ArrowIpcDecoderDecodeArrayFromShared(
   int result =
       ArrowIpcDecoderDecodeArrayInternal(decoder, i, &temp, validation_level, 
error);
   if (result != NANOARROW_OK && temp.release != NULL) {
-    temp.release(&temp);
+    ArrowArrayRelease(&temp);
   } else if (result != NANOARROW_OK) {
     return result;
   }
@@ -22169,7 +22169,7 @@ static void ArrowIpcArrayStreamReaderRelease(struct 
ArrowArrayStream* stream) {
   ArrowIpcDecoderReset(&private_data->decoder);
 
   if (private_data->out_schema.release != NULL) {
-    private_data->out_schema.release(&private_data->out_schema);
+    ArrowSchemaRelease(&private_data->out_schema);
   }
 
   ArrowBufferReset(&private_data->header);
@@ -22306,7 +22306,7 @@ static int ArrowIpcArrayStreamReaderReadSchemaIfNeeded(
 
   // Only support "read the whole thing" for now
   if (private_data->field_index != -1) {
-    tmp.release(&tmp);
+    ArrowSchemaRelease(&tmp);
     ArrowErrorSet(&private_data->error, "Field index != -1 is not yet 
supported");
     return ENOTSUP;
   }
@@ -22315,7 +22315,7 @@ static int ArrowIpcArrayStreamReaderReadSchemaIfNeeded(
   int result =
       ArrowIpcDecoderSetSchema(&private_data->decoder, &tmp, 
&private_data->error);
   if (result != NANOARROW_OK) {
-    tmp.release(&tmp);
+    ArrowSchemaRelease(&tmp);
     return result;
   }
 
diff --git a/dist/nanoarrow_testing.hpp b/dist/nanoarrow_testing.hpp
index c2502a1..dbcc4f4 100644
--- a/dist/nanoarrow_testing.hpp
+++ b/dist/nanoarrow_testing.hpp
@@ -67,7 +67,7 @@ class TestingJSONWriter {
     out << R"({"schema": )";
 
     nanoarrow::UniqueSchema schema;
-    NANOARROW_RETURN_NOT_OK(stream->get_schema(stream, schema.get()));
+    NANOARROW_RETURN_NOT_OK(ArrowArrayStreamGetSchema(stream, schema.get(), 
nullptr));
     NANOARROW_RETURN_NOT_OK(WriteSchema(out, schema.get()));
 
     nanoarrow::UniqueArrayView array_view;
@@ -79,7 +79,7 @@ class TestingJSONWriter {
     nanoarrow::UniqueArray array;
     std::string sep;
     do {
-      NANOARROW_RETURN_NOT_OK(stream->get_next(stream, array.get()));
+      NANOARROW_RETURN_NOT_OK(ArrowArrayStreamGetNext(stream, array.get(), 
nullptr));
       if (array->release == nullptr) {
         break;
       }
@@ -733,11 +733,18 @@ class TestingJSONReader {
   using json = nlohmann::json;
 
  public:
+  TestingJSONReader(ArrowBufferAllocator allocator) : allocator_(allocator) {}
+  TestingJSONReader() : TestingJSONReader(ArrowBufferAllocatorDefault()) {}
+
+  static const int kNumBatchOnlySchema = -2;
+  static const int kNumBatchReadAll = -1;
+
   /// \brief Read JSON representing a data file object
   ///
   /// Read a JSON object in the form `{"schema": {...}, "batches": [...], 
...}`,
   /// propagating `out` on success.
   ArrowErrorCode ReadDataFile(const std::string& data_file_json, 
ArrowArrayStream* out,
+                              int num_batch = kNumBatchReadAll,
                               ArrowError* error = nullptr) {
     try {
       auto obj = json::parse(data_file_json);
@@ -760,18 +767,34 @@ class TestingJSONReader {
       NANOARROW_RETURN_NOT_OK(
           ArrowArrayViewInitFromSchema(array_view.get(), schema.get(), error));
 
+      // Get a vector of batch ids to parse
+      std::vector<size_t> batch_ids;
+      if (num_batch == kNumBatchOnlySchema) {
+        batch_ids.resize(0);
+      } else if (num_batch == kNumBatchReadAll) {
+        batch_ids.resize(batches.size());
+        std::iota(batch_ids.begin(), batch_ids.end(), 0);
+      } else if (num_batch >= 0 && num_batch < batches.size()) {
+        batch_ids.push_back(num_batch);
+      } else {
+        ArrowErrorSet(error, "Expected num_batch between 0 and %d but got %d",
+                      static_cast<int>(batches.size() - 1), num_batch);
+        return EINVAL;
+      }
+
       // Initialize ArrayStream with required capacity
       nanoarrow::UniqueArrayStream stream;
       NANOARROW_RETURN_NOT_OK_WITH_ERROR(
-          ArrowBasicArrayStreamInit(stream.get(), schema.get(), 
batches.size()), error);
+          ArrowBasicArrayStreamInit(stream.get(), schema.get(), 
batch_ids.size()), error);
 
       // Populate ArrayStream batches
-      for (size_t i = 0; i < batches.size(); i++) {
+      for (size_t i = 0; i < batch_ids.size(); i++) {
         nanoarrow::UniqueArray array;
         NANOARROW_RETURN_NOT_OK(
             ArrowArrayInitFromArrayView(array.get(), array_view.get(), error));
+        SetArrayAllocatorRecursive(array.get());
         NANOARROW_RETURN_NOT_OK(
-            SetArrayBatch(batches[i], array_view.get(), array.get(), error));
+            SetArrayBatch(batches[batch_ids[i]], array_view.get(), 
array.get(), error));
         ArrowBasicArrayStreamSetArray(stream.get(), i, array.get());
       }
 
@@ -839,6 +862,7 @@ class TestingJSONReader {
       // ArrowArray to hold memory
       nanoarrow::UniqueArray array;
       NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromSchema(array.get(), schema, 
error));
+      SetArrayAllocatorRecursive(array.get());
 
       NANOARROW_RETURN_NOT_OK(SetArrayBatch(obj, array_view.get(), 
array.get(), error));
       ArrowArrayMove(array.get(), out);
@@ -867,6 +891,7 @@ class TestingJSONReader {
       // ArrowArray to hold memory
       nanoarrow::UniqueArray array;
       NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromSchema(array.get(), schema, 
error));
+      SetArrayAllocatorRecursive(array.get());
 
       // Parse the JSON into the array
       NANOARROW_RETURN_NOT_OK(SetArrayColumn(obj, array_view.get(), 
array.get(), error));
@@ -881,6 +906,8 @@ class TestingJSONReader {
   }
 
  private:
+  ArrowBufferAllocator allocator_;
+
   ArrowErrorCode SetSchema(ArrowSchema* schema, const json& value, ArrowError* 
error) {
     NANOARROW_RETURN_NOT_OK(
         Check(value.is_object(), error, "Expected Schema to be a JSON 
object"));
@@ -1713,6 +1740,20 @@ class TestingJSONReader {
     return NANOARROW_OK;
   }
 
+  void SetArrayAllocatorRecursive(ArrowArray* array) {
+    for (int i = 0; i < array->n_buffers; i++) {
+      ArrowArrayBuffer(array, i)->allocator = allocator_;
+    }
+
+    for (int64_t i = 0; i < array->n_children; i++) {
+      SetArrayAllocatorRecursive(array->children[i]);
+    }
+
+    if (array->dictionary != nullptr) {
+      SetArrayAllocatorRecursive(array->dictionary);
+    }
+  }
+
   ArrowErrorCode PrefixError(ArrowErrorCode value, ArrowError* error,
                              const std::string& prefix) {
     if (value != NANOARROW_OK && error != nullptr) {
@@ -1784,10 +1825,10 @@ class TestingJSONComparison {
     // Read both schemas
     nanoarrow::UniqueSchema actual_schema;
     nanoarrow::UniqueSchema expected_schema;
-    NANOARROW_RETURN_NOT_OK_WITH_ERROR(actual->get_schema(actual, 
actual_schema.get()),
-                                       error);
-    NANOARROW_RETURN_NOT_OK_WITH_ERROR(
-        expected->get_schema(expected, expected_schema.get()), error);
+    NANOARROW_RETURN_NOT_OK(
+        ArrowArrayStreamGetSchema(actual, actual_schema.get(), error));
+    NANOARROW_RETURN_NOT_OK(
+        ArrowArrayStreamGetSchema(expected, expected_schema.get(), error));
 
     // Compare them and return if they are not equal
     NANOARROW_RETURN_NOT_OK(
@@ -1809,10 +1850,9 @@ class TestingJSONComparison {
       // Read a batch from each stream
       actual_array.reset();
       expected_array.reset();
-      NANOARROW_RETURN_NOT_OK_WITH_ERROR(actual->get_next(actual, 
actual_array.get()),
-                                         error);
-      NANOARROW_RETURN_NOT_OK_WITH_ERROR(
-          expected->get_next(expected, expected_array.get()), error);
+      NANOARROW_RETURN_NOT_OK(ArrowArrayStreamGetNext(actual, 
actual_array.get(), error));
+      NANOARROW_RETURN_NOT_OK(
+          ArrowArrayStreamGetNext(expected, expected_array.get(), error));
 
       // Check the finished/unfinished status of both streams
       if (actual_array->release == nullptr && expected_array->release != 
nullptr) {


Reply via email to