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 53202371 refactor: Split up nanoarrow.hpp into multiple .hpp files
(#668)
53202371 is described below
commit 5320237165c2a88853525020f4776952c665211e
Author: Dewey Dunnington <[email protected]>
AuthorDate: Fri Nov 1 20:54:14 2024 +0000
refactor: Split up nanoarrow.hpp into multiple .hpp files (#668)
This PR splits `nanoarrow.hpp`, which was getting large, into multiple
files (bundling them, of course, when creating the bundled
distribution). This is intended to make space for new helpers that are
being proposed in #599 although I think it's also just a good idea in
general since the utilities to create streams and buffers using
templating are in a pretty different category from the things that keep
you from leaking memory and many users will only need part of the API.
Because it's header-only on top of the C runtime, I think this is
probably a good pattern to enable.
This doesn't quite solve `nanoarrow_device.hpp` and
`nanoarrow_ipc.hpp`...they currently still just include all of
`nanoarrow.hpp`. I think we can probably solve that in the bundler but I
might punt on that since it's not actually hurting anything at the
moment.
---
CMakeLists.txt | 48 +-
ci/scripts/bundle.py | 23 +-
meson.build | 27 +-
src/nanoarrow/common/nanoarrow_hpp_test.cc | 415 ----------
src/nanoarrow/hpp/array_stream.hpp | 204 +++++
src/nanoarrow/hpp/array_stream_test.cc | 64 ++
src/nanoarrow/hpp/buffer.hpp | 85 ++
src/nanoarrow/hpp/buffer_test.cc | 86 ++
src/nanoarrow/hpp/exception.hpp | 79 ++
.../exception_test.cc} | 24 +-
src/nanoarrow/hpp/operators.hpp | 62 ++
src/nanoarrow/hpp/unique.hpp | 226 +++++
src/nanoarrow/hpp/unique_test.cc | 182 ++++
src/nanoarrow/hpp/view.hpp | 377 +++++++++
src/nanoarrow/hpp/view_test.cc | 137 +++
src/nanoarrow/nanoarrow.hpp | 921 +--------------------
src/nanoarrow/nanoarrow_config.h.in | 11 +-
17 files changed, 1611 insertions(+), 1360 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 04419839..4cefb8fb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -223,6 +223,13 @@ if(NANOARROW_IPC)
install(TARGETS nanoarrow_ipc DESTINATION lib)
install(FILES src/nanoarrow/nanoarrow_ipc.h src/nanoarrow/nanoarrow_ipc.hpp
src/nanoarrow/ipc/flatcc_generated.h DESTINATION
include/nanoarrow)
+ install(FILES src/nanoarrow/hpp/array_stream.hpp
+ src/nanoarrow/hpp/buffer.hpp
+ src/nanoarrow/hpp/exception.hpp
+ src/nanoarrow/hpp/operators.hpp
+ src/nanoarrow/hpp/unique.hpp
+ src/nanoarrow/hpp/view.hpp
+ DESTINATION include/nanoarrow/hpp)
endif()
if(NANOARROW_IPC AND (NANOARROW_BUILD_INTEGRATION_TESTS OR
NANOARROW_BUILD_TESTS))
@@ -444,11 +451,16 @@ if(NANOARROW_BUILD_TESTS)
add_executable(array_test src/nanoarrow/common/array_test.cc)
add_executable(schema_test src/nanoarrow/common/schema_test.cc)
add_executable(array_stream_test src/nanoarrow/common/array_stream_test.cc)
- add_executable(nanoarrow_hpp_test src/nanoarrow/common/nanoarrow_hpp_test.cc)
add_executable(nanoarrow_testing_test src/nanoarrow/testing/testing_test.cc)
add_executable(c_data_integration_test
src/nanoarrow/integration/c_data_integration_test.cc)
+ add_executable(hpp_array_stream src/nanoarrow/hpp/array_stream_test.cc)
+ add_executable(hpp_buffer src/nanoarrow/hpp/buffer_test.cc)
+ add_executable(hpp_exception src/nanoarrow/hpp/exception_test.cc)
+ add_executable(hpp_unique src/nanoarrow/hpp/unique_test.cc)
+ add_executable(hpp_view src/nanoarrow/hpp/view_test.cc)
+
target_link_libraries(utils_test
nanoarrow_testing
gtest_main
@@ -472,17 +484,37 @@ if(NANOARROW_BUILD_TESTS)
gtest_main
gmock_main
nanoarrow_coverage_config)
- target_link_libraries(nanoarrow_hpp_test
+ target_link_libraries(nanoarrow_testing_test nanoarrow_testing gtest_main
+ nanoarrow_coverage_config)
+ target_link_libraries(c_data_integration_test
+ nanoarrow
+ nanoarrow_c_data_integration
+ gtest_main
+ nanoarrow_coverage_config)
+ target_link_libraries(hpp_array_stream
nanoarrow
gtest_main
gmock_main
nanoarrow_coverage_config)
- target_link_libraries(nanoarrow_testing_test nanoarrow_testing gtest_main
+ target_link_libraries(hpp_buffer
+ nanoarrow
+ gtest_main
+ gmock_main
nanoarrow_coverage_config)
- target_link_libraries(c_data_integration_test
+ target_link_libraries(hpp_exception
+ nanoarrow
+ gtest_main
+ gmock_main
+ nanoarrow_coverage_config)
+ target_link_libraries(hpp_unique
nanoarrow
- nanoarrow_c_data_integration
gtest_main
+ gmock_main
+ nanoarrow_coverage_config)
+ target_link_libraries(hpp_view
+ nanoarrow
+ gtest_main
+ gmock_main
nanoarrow_coverage_config)
include(GoogleTest)
@@ -494,9 +526,13 @@ if(NANOARROW_BUILD_TESTS)
gtest_discover_tests(array_test DISCOVERY_TIMEOUT 10)
gtest_discover_tests(schema_test DISCOVERY_TIMEOUT 10)
gtest_discover_tests(array_stream_test DISCOVERY_TIMEOUT 10)
- gtest_discover_tests(nanoarrow_hpp_test DISCOVERY_TIMEOUT 10)
gtest_discover_tests(nanoarrow_testing_test DISCOVERY_TIMEOUT 10)
gtest_discover_tests(c_data_integration_test DISCOVERY_TIMEOUT 10)
+ gtest_discover_tests(hpp_array_stream)
+ gtest_discover_tests(hpp_buffer)
+ gtest_discover_tests(hpp_exception)
+ gtest_discover_tests(hpp_unique)
+ gtest_discover_tests(hpp_view)
if(NANOARROW_IPC)
diff --git a/ci/scripts/bundle.py b/ci/scripts/bundle.py
index 542d9ea7..746fcd37 100644
--- a/ci/scripts/bundle.py
+++ b/ci/scripts/bundle.py
@@ -125,13 +125,22 @@ def bundle_nanoarrow(
nanoarrow_h = re.sub(r'#include "(nanoarrow/)?[a-z_./]+"', "", nanoarrow_h)
yield f"{output_include_dir}/nanoarrow.h", nanoarrow_h
- # Generate files that don't need special handling
- for filename in [
- "nanoarrow.hpp",
- ]:
- content = read_content(src_dir / filename)
- content = namespace_nanoarrow_includes(content, header_namespace)
- yield f"{output_include_dir}/{filename}", content
+ # Generate nanoarrow/nanoarrow.hpp
+ nanoarrow_hpp = concatenate_content(
+ [
+ src_dir / "nanoarrow.hpp",
+ src_dir / "hpp" / "exception.hpp",
+ src_dir / "hpp" / "operators.hpp",
+ src_dir / "hpp" / "unique.hpp",
+ src_dir / "hpp" / "array_stream.hpp",
+ src_dir / "hpp" / "buffer.hpp",
+ src_dir / "hpp" / "view.hpp",
+ ]
+ )
+
+ nanoarrow_hpp = re.sub(r'#include "(nanoarrow/)?hpp/[a-z_./]+"', "",
nanoarrow_hpp)
+ nanoarrow_hpp = namespace_nanoarrow_includes(nanoarrow_hpp,
header_namespace)
+ yield f"{output_include_dir}/nanoarrow.hpp", nanoarrow_hpp
# Generate nanoarrow/nanoarrow.c
nanoarrow_c = concatenate_content(
diff --git a/meson.build b/meson.build
index 144319c7..3a9aa8a2 100644
--- a/meson.build
+++ b/meson.build
@@ -62,6 +62,16 @@ install_headers(
subdir: 'nanoarrow',
)
+install_headers(
+ 'src/nanoarrow/hpp/array_stream.hpp',
+ 'src/nanoarrow/hpp/buffer.hpp',
+ 'src/nanoarrow/hpp/exception.hpp',
+ 'src/nanoarrow/hpp/operators.hpp',
+ 'src/nanoarrow/hpp/unique.hpp',
+ 'src/nanoarrow/hpp/view.hpp',
+ subdir: 'nanoarrow/hpp',
+)
+
install_headers(
'src/nanoarrow/common/inline_array.h',
'src/nanoarrow/common/inline_buffer.h',
@@ -181,7 +191,7 @@ if get_option('tests')
gtest_dep = dependency('gtest_main')
gmock_dep = dependency('gmock')
- nanoarrow_tests = ['utils', 'buffer', 'array', 'schema', 'array-stream',
'nanoarrow-hpp']
+ nanoarrow_tests = ['utils', 'buffer', 'array', 'schema', 'array-stream']
foreach name : nanoarrow_tests
exc = executable(
@@ -193,6 +203,18 @@ if get_option('tests')
test(name, exc)
endforeach
+ nanoarrow_hpp_tests = ['array_stream', 'buffer', 'exception', 'unique',
'view']
+
+ foreach name : nanoarrow_hpp_tests
+ exc = executable(
+ 'hpp-' + name + '-test',
+ sources: 'src/nanoarrow/hpp/' + name.replace('-', '_') + '_test.cc',
+ include_directories: incdir,
+ dependencies: [nanoarrow_testing_dep, gtest_dep, gmock_dep],
+ )
+ test(name, exc)
+ endforeach
+
testing_test = executable('nanoarrow-testing-test',
'src/nanoarrow/testing/testing_test.cc',
include_directories: incdir,
@@ -210,7 +232,7 @@ if get_option('tests')
ipc_test_files = {
'ipc-decoder': {
'src': 'decoder',
- 'deps': [nanoarrow_ipc_dep, arrow_dep, gtest_dep, gmock_dep],
+ 'deps': [nanoarrow_ipc_dep, flatcc_dep, arrow_dep, gtest_dep,
gmock_dep],
'timeout': 30,
},
'ipc-reader': {
@@ -226,6 +248,7 @@ if get_option('tests')
'deps': [
nanoarrow_testing_dep,
nanoarrow_ipc_dep,
+ flatcc_dep,
zlib_dep,
arrow_dep,
gtest_dep,
diff --git a/src/nanoarrow/common/nanoarrow_hpp_test.cc
b/src/nanoarrow/common/nanoarrow_hpp_test.cc
deleted file mode 100644
index b76aa4cf..00000000
--- a/src/nanoarrow/common/nanoarrow_hpp_test.cc
+++ /dev/null
@@ -1,415 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-#include <array>
-#include <cerrno>
-
-#include <gmock/gmock-matchers.h>
-#include <gtest/gtest.h>
-
-#include "nanoarrow/nanoarrow_gtest_util.hpp"
-#include "nanoarrow/nanoarrow_testing.hpp"
-
-using testing::ElementsAre;
-
-TEST(NanoarrowHppTest, NanoarrowHppExceptionTest) {
- ASSERT_THROW(NANOARROW_THROW_NOT_OK(EINVAL), nanoarrow::Exception);
- ASSERT_NO_THROW(NANOARROW_THROW_NOT_OK(NANOARROW_OK));
- try {
- NANOARROW_THROW_NOT_OK(EINVAL);
- } catch (const nanoarrow::Exception& e) {
- EXPECT_EQ(std::string(e.what()).substr(0, 24), "EINVAL failed with errno");
- }
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppUniqueArrayTest) {
- nanoarrow::UniqueArray array;
- EXPECT_EQ(array->release, nullptr);
-
- ASSERT_EQ(ArrowArrayInitFromType(array.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
- ASSERT_EQ(ArrowArrayStartAppending(array.get()), NANOARROW_OK);
- ASSERT_EQ(ArrowArrayAppendInt(array.get(), 123), NANOARROW_OK);
- ASSERT_EQ(ArrowArrayFinishBuildingDefault(array.get(), nullptr),
NANOARROW_OK);
-
- EXPECT_NE(array->release, nullptr);
- EXPECT_EQ(array->length, 1);
-
- // move constructor
- nanoarrow::UniqueArray array2 = std::move(array);
- EXPECT_EQ(array->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
- EXPECT_NE(array2->release, nullptr);
- EXPECT_EQ(array2->length, 1);
-
- // pointer constructor
- nanoarrow::UniqueArray array3(array2.get());
- EXPECT_EQ(array2->release, nullptr);
- EXPECT_NE(array3->release, nullptr);
- EXPECT_EQ(array3->length, 1);
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppUniqueSchemaTest) {
- nanoarrow::UniqueSchema schema;
- EXPECT_EQ(schema->release, nullptr);
-
- ASSERT_EQ(ArrowSchemaInitFromType(schema.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
- EXPECT_NE(schema->release, nullptr);
- EXPECT_STREQ(schema->format, "i");
-
- // move constructor
- nanoarrow::UniqueSchema schema2 = std::move(schema);
- EXPECT_EQ(schema->release, nullptr); //
NOLINT(clang-analyzer-cplusplus.Move)
- EXPECT_NE(schema2->release, nullptr);
- EXPECT_STREQ(schema2->format, "i");
-
- // pointer constructor
- nanoarrow::UniqueSchema schema3(schema2.get());
- EXPECT_EQ(schema2->release, nullptr);
- EXPECT_NE(schema3->release, nullptr);
- EXPECT_STREQ(schema3->format, "i");
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppUniqueArrayStreamTest) {
- nanoarrow::UniqueSchema schema;
- schema->format = NULL;
-
- nanoarrow::UniqueArrayStream array_stream_default;
- EXPECT_EQ(array_stream_default->release, nullptr);
-
- nanoarrow::UniqueSchema schema_in;
- EXPECT_EQ(ArrowSchemaInitFromType(schema_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
- auto array_stream = nanoarrow::EmptyArrayStream::MakeUnique(schema_in.get());
- EXPECT_NE(array_stream->release, nullptr);
- EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream.get(), schema.get(),
nullptr),
- NANOARROW_OK);
- EXPECT_STREQ(schema->format, "i");
- schema.reset();
- schema->format = NULL;
-
- // move constructor
- nanoarrow::UniqueArrayStream array_stream2 = std::move(array_stream);
- EXPECT_EQ(array_stream->release, nullptr); //
NOLINT(clang-analyzer-cplusplus.Move)
- EXPECT_NE(array_stream2->release, nullptr);
- EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream2.get(), schema.get(),
nullptr),
- NANOARROW_OK);
- EXPECT_STREQ(schema->format, "i");
- schema.reset();
- schema->format = NULL;
-
- // pointer constructor
- nanoarrow::UniqueArrayStream array_stream3(array_stream2.get());
- EXPECT_EQ(array_stream2->release, nullptr);
- EXPECT_NE(array_stream3->release, nullptr);
- EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream2.get(), schema.get(),
nullptr),
- NANOARROW_OK);
- EXPECT_STREQ(schema->format, "i");
-
- // releasing should clear the release callback
- EXPECT_EQ(ArrowSchemaInitFromType(schema_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
- auto array_stream4 =
nanoarrow::EmptyArrayStream::MakeUnique(schema_in.get());
- EXPECT_NE(array_stream4->release, nullptr);
- array_stream4->release(array_stream4.get());
- EXPECT_EQ(array_stream4->private_data, nullptr);
- EXPECT_EQ(array_stream4->release, nullptr);
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppUniqueBufferTest) {
- nanoarrow::UniqueBuffer buffer;
- EXPECT_EQ(buffer->data, nullptr);
- EXPECT_EQ(buffer->size_bytes, 0);
-
- ASSERT_EQ(ArrowBufferAppendFill(buffer.get(), 0xff, 123), NANOARROW_OK);
- EXPECT_NE(buffer->data, nullptr);
- EXPECT_EQ(buffer->size_bytes, 123);
-
- // move constructor
- nanoarrow::UniqueBuffer buffer2 = std::move(buffer);
- EXPECT_EQ(buffer->data, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
- EXPECT_EQ(buffer->size_bytes, 0); // NOLINT(clang-analyzer-cplusplus.Move)
- EXPECT_NE(buffer2->data, nullptr);
- EXPECT_EQ(buffer2->size_bytes, 123);
-
- // pointer constructor
- nanoarrow::UniqueBuffer buffer3(buffer2.get());
- EXPECT_EQ(buffer2->data, nullptr);
- EXPECT_EQ(buffer2->size_bytes, 0);
- EXPECT_NE(buffer3->data, nullptr);
- EXPECT_EQ(buffer3->size_bytes, 123);
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppUniqueBitmapTest) {
- nanoarrow::UniqueBitmap bitmap;
- EXPECT_EQ(bitmap->buffer.data, nullptr);
- EXPECT_EQ(bitmap->size_bits, 0);
-
- ASSERT_EQ(ArrowBitmapAppend(bitmap.get(), true, 123), NANOARROW_OK);
- EXPECT_NE(bitmap->buffer.data, nullptr);
- EXPECT_EQ(bitmap->size_bits, 123);
-
- // move constructor
- nanoarrow::UniqueBitmap bitmap2 = std::move(bitmap);
- EXPECT_EQ(bitmap->buffer.data, nullptr); //
NOLINT(clang-analyzer-cplusplus.Move)
- EXPECT_EQ(bitmap->size_bits, 0); //
NOLINT(clang-analyzer-cplusplus.Move)
- EXPECT_NE(bitmap2->buffer.data, nullptr);
- EXPECT_EQ(bitmap2->size_bits, 123);
-
- // pointer constructor
- nanoarrow::UniqueBitmap bitmap3(bitmap2.get());
- EXPECT_EQ(bitmap2->buffer.data, nullptr);
- EXPECT_EQ(bitmap2->size_bits, 0);
- EXPECT_NE(bitmap3->buffer.data, nullptr);
- EXPECT_EQ(bitmap3->size_bits, 123);
-}
-
-struct TestWrappedObj {
- int64_t* num_frees;
-
- TestWrappedObj(int64_t* addr) { num_frees = addr; }
-
- TestWrappedObj(TestWrappedObj&& obj) {
- num_frees = obj.num_frees;
- obj.num_frees = nullptr;
- }
-
- ~TestWrappedObj() {
- if (num_frees != nullptr) {
- *num_frees = *num_frees + 1;
- }
- }
-};
-
-TEST(NanoarrowHppTest, NanoarrowHppBufferInitWrappedTest) {
- nanoarrow::UniqueBuffer buffer;
- int64_t num_frees = 0;
-
- TestWrappedObj obj(&num_frees);
- nanoarrow::BufferInitWrapped(buffer.get(), std::move(obj), nullptr, 0);
- EXPECT_EQ(obj.num_frees, nullptr);
- EXPECT_EQ(num_frees, 0);
- buffer.reset();
- EXPECT_EQ(num_frees, 1);
-
- // Ensure the destructor won't get called again when ArrowBufferReset is
- // called on the empty buffer.
- buffer.reset();
- EXPECT_EQ(num_frees, 1);
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppBufferInitSequenceTest) {
- nanoarrow::UniqueBuffer buffer;
-
- // Check templating magic with std::string
- nanoarrow::BufferInitSequence(buffer.get(), std::string("1234"));
- EXPECT_EQ(buffer->size_bytes, 4);
- EXPECT_EQ(buffer->capacity_bytes, 0);
- EXPECT_EQ(memcmp(buffer->data, "1234", 4), 0);
-
- // Check templating magic with std::vector
- buffer.reset();
- nanoarrow::BufferInitSequence(buffer.get(), std::vector<uint8_t>({1, 2, 3,
4}));
- EXPECT_EQ(buffer->size_bytes, 4);
- EXPECT_EQ(buffer->capacity_bytes, 0);
- EXPECT_EQ(buffer->data[0], 1);
- EXPECT_EQ(buffer->data[1], 2);
- EXPECT_EQ(buffer->data[2], 3);
- EXPECT_EQ(buffer->data[3], 4);
-
- // Check templating magic with std::array
- buffer.reset();
- nanoarrow::BufferInitSequence(buffer.get(), std::array<uint8_t, 4>({1, 2, 3,
4}));
- EXPECT_EQ(buffer->size_bytes, 4);
- EXPECT_EQ(buffer->capacity_bytes, 0);
- EXPECT_EQ(buffer->data[0], 1);
- EXPECT_EQ(buffer->data[1], 2);
- EXPECT_EQ(buffer->data[2], 3);
- EXPECT_EQ(buffer->data[3], 4);
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppUniqueArrayViewTest) {
- nanoarrow::UniqueArrayView array_view;
- EXPECT_EQ(array_view->storage_type, NANOARROW_TYPE_UNINITIALIZED);
-
- // Use an ArrayView with children, since an ArrayView with no children
- // doesn't hold any resources
- ArrowArrayViewInitFromType(array_view.get(), NANOARROW_TYPE_STRUCT);
- ASSERT_EQ(ArrowArrayViewAllocateChildren(array_view.get(), 2), NANOARROW_OK);
- EXPECT_EQ(array_view->storage_type, NANOARROW_TYPE_STRUCT);
-
- // move constructor
- nanoarrow::UniqueArrayView array_view2 = std::move(array_view);
- EXPECT_EQ(array_view->storage_type, // NOLINT(clang-analyzer-cplusplus.Move)
- NANOARROW_TYPE_UNINITIALIZED);
- EXPECT_EQ(array_view2->storage_type, NANOARROW_TYPE_STRUCT);
-
- // pointer constructor
- nanoarrow::UniqueArrayView array_view3(array_view2.get());
- EXPECT_EQ(array_view2->storage_type, NANOARROW_TYPE_UNINITIALIZED);
- EXPECT_EQ(array_view3->storage_type, NANOARROW_TYPE_STRUCT);
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppViewArrayAsTest) {
- nanoarrow::UniqueBuffer is_valid, floats;
- nanoarrow::BufferInitSequence(is_valid.get(), std::vector<uint8_t>{0xFF});
- ArrowBitClear(is_valid->data, 2);
- ArrowBitClear(is_valid->data, 5);
- nanoarrow::BufferInitSequence(floats.get(),
- std::vector<float>{8, 4, 2, 1, .5, .25, .125});
-
- const void* buffers[] = {is_valid->data, floats->data};
- struct ArrowArray array {};
- array.length = 7;
- array.null_count = 2;
- array.n_buffers = 2;
- array.buffers = buffers;
-
- int i = 0;
- float f = 8;
- for (auto slot : nanoarrow::ViewArrayAs<float>(&array)) {
- if (i == 2 || i == 5) {
- EXPECT_EQ(slot, nanoarrow::NA);
- } else {
- EXPECT_EQ(slot, f);
- }
- ++i;
- f /= 2;
- }
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppViewArrayAsBytesTest) {
- using namespace nanoarrow::literals;
-
- nanoarrow::UniqueBuffer is_valid, offsets, data;
- nanoarrow::BufferInitSequence(is_valid.get(), std::vector<uint8_t>{0xFF});
- ArrowBitClear(is_valid->data, 2);
- ArrowBitClear(is_valid->data, 5);
- nanoarrow::BufferInitSequence(offsets.get(),
- std::vector<int32_t>{0, 1, 2, 3, 4, 5, 6, 7});
- nanoarrow::BufferInitSequence(data.get(), std::string{"abcdefghi"});
-
- const void* buffers[] = {is_valid->data, offsets->data, data->data};
- struct ArrowArray array {};
- array.length = 7;
- array.null_count = 2;
- array.n_buffers = 2;
- array.buffers = buffers;
-
- int i = 0;
- ArrowStringView expected[] = {"a"_asv, "b"_asv, "c"_asv, "d"_asv,
- "e"_asv, "f"_asv, "g"_asv};
- for (auto slot : nanoarrow::ViewArrayAsBytes<32>(&array)) {
- if (i == 2 || i == 5) {
- EXPECT_EQ(slot, nanoarrow::NA);
- } else {
- EXPECT_EQ(slot, expected[i]);
- }
- ++i;
- }
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppViewArrayAsFixedSizeBytesTest) {
- using namespace nanoarrow::literals;
-
- nanoarrow::UniqueBuffer is_valid, data;
- nanoarrow::BufferInitSequence(is_valid.get(), std::vector<uint8_t>{0xFF});
- ArrowBitClear(is_valid->data, 2);
- ArrowBitClear(is_valid->data, 5);
- nanoarrow::BufferInitSequence(
- data.get(), std::string{"foo"} + "bar" + "foo" + "bar" + "foo" + "bar" +
"foo");
-
- const void* buffers[] = {is_valid->data, data->data};
- struct ArrowArray array {};
- array.length = 7;
- array.null_count = 2;
- array.n_buffers = 2;
- array.buffers = buffers;
-
- int i = 0;
- for (auto slot : nanoarrow::ViewArrayAsFixedSizeBytes(&array, 3)) {
- if (i == 2 || i == 5) {
- EXPECT_EQ(slot, nanoarrow::NA);
- } else {
- EXPECT_EQ(slot, i % 2 == 0 ? "foo"_asv : "bar"_asv);
- }
- ++i;
- }
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppViewArrayStreamTest) {
- static int32_t slot = 1;
-
- struct ArrowArrayStream stream {};
- stream.get_schema = [](struct ArrowArrayStream*, struct ArrowSchema* out) {
- return ArrowSchemaInitFromType(out, NANOARROW_TYPE_INT32);
- };
- stream.get_next = [](struct ArrowArrayStream*, struct ArrowArray* out) {
- if (slot >= 16) return ENOMEM;
- NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromType(out, NANOARROW_TYPE_INT32));
- NANOARROW_RETURN_NOT_OK(ArrowArrayStartAppending(out));
- NANOARROW_RETURN_NOT_OK(ArrowArrayAppendInt(out, slot *= 2));
- return ArrowArrayFinishBuildingDefault(out, nullptr);
- };
- stream.get_last_error = [](struct ArrowArrayStream*) { return "foo bar"; };
- stream.release = [](struct ArrowArrayStream*) {};
-
- nanoarrow::ViewArrayStream stream_view(&stream);
- for (ArrowArray& array : stream_view) {
- EXPECT_THAT(nanoarrow::ViewArrayAs<int32_t>(&array), ElementsAre(slot));
- }
- EXPECT_EQ(stream_view.count(), 4);
- EXPECT_EQ(stream_view.code(), ENOMEM);
- EXPECT_STREQ(stream_view.error()->message, "foo bar");
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppEmptyArrayStreamTest) {
- nanoarrow::UniqueSchema schema;
- struct ArrowArray array;
-
- nanoarrow::UniqueSchema schema_in;
- EXPECT_EQ(ArrowSchemaInitFromType(schema_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
-
- nanoarrow::UniqueArrayStream array_stream;
-
nanoarrow::EmptyArrayStream(schema_in.get()).ToArrayStream(array_stream.get());
-
- EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream.get(), schema.get(),
nullptr),
- NANOARROW_OK);
- EXPECT_STREQ(schema->format, "i");
- EXPECT_EQ(ArrowArrayStreamGetNext(array_stream.get(), &array, nullptr),
NANOARROW_OK);
- EXPECT_EQ(array.release, nullptr);
- EXPECT_STREQ(ArrowArrayStreamGetLastError(array_stream.get()), "");
-}
-
-TEST(NanoarrowHppTest, NanoarrowHppVectorArrayStreamTest) {
- nanoarrow::UniqueArray array_in;
- EXPECT_EQ(ArrowArrayInitFromType(array_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
- EXPECT_EQ(ArrowArrayStartAppending(array_in.get()), NANOARROW_OK);
- EXPECT_EQ(ArrowArrayAppendInt(array_in.get(), 1234), NANOARROW_OK);
- EXPECT_EQ(ArrowArrayFinishBuildingDefault(array_in.get(), nullptr),
NANOARROW_OK);
-
- nanoarrow::UniqueSchema schema_in;
- EXPECT_EQ(ArrowSchemaInitFromType(schema_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
-
- nanoarrow::UniqueArrayStream array_stream;
- nanoarrow::VectorArrayStream(schema_in.get(), array_in.get())
- .ToArrayStream(array_stream.get());
-
- nanoarrow::ViewArrayStream array_stream_view(array_stream.get());
- for (ArrowArray& array : array_stream_view) {
- EXPECT_THAT(nanoarrow::ViewArrayAs<int32_t>(&array), ElementsAre(1234));
- }
- EXPECT_EQ(array_stream_view.count(), 1);
- EXPECT_EQ(array_stream_view.code(), NANOARROW_OK);
- EXPECT_STREQ(array_stream_view.error()->message, "");
-}
diff --git a/src/nanoarrow/hpp/array_stream.hpp
b/src/nanoarrow/hpp/array_stream.hpp
new file mode 100644
index 00000000..eb398925
--- /dev/null
+++ b/src/nanoarrow/hpp/array_stream.hpp
@@ -0,0 +1,204 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#ifndef NANOARROW_HPP_ARRAY_STREAM_HPP_INCLUDED
+#define NANOARROW_HPP_ARRAY_STREAM_HPP_INCLUDED
+
+#include <vector>
+
+#include "nanoarrow/hpp/unique.hpp"
+
+NANOARROW_CXX_NAMESPACE_BEGIN
+
+/// \defgroup nanoarrow_hpp-array-stream ArrayStream helpers
+///
+/// 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 struct ArrowSchema and implements a
default
+/// get_next() method that always marks the output ArrowArray as released.
+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);
+ }
+
+ private:
+ UniqueSchema schema_;
+ struct ArrowError error_;
+
+ friend class ArrayStreamFactory<EmptyArrayStream>;
+
+ int GetSchema(struct ArrowSchema* schema) {
+ return ArrowSchemaDeepCopy(schema_.get(), schema);
+ }
+
+ int GetNext(struct ArrowArray* array) {
+ array->release = nullptr;
+ return NANOARROW_OK;
+ }
+
+ const char* GetLastError() { return error_.message; }
+};
+
+/// \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);
+ }
+
+ private:
+ int64_t offset_;
+ UniqueSchema schema_;
+ std::vector<UniqueArray> arrays_;
+
+ friend class ArrayStreamFactory<VectorArrayStream>;
+
+ 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 {
+ array->release = nullptr;
+ }
+
+ return NANOARROW_OK;
+ }
+
+ const char* GetLastError() { return ""; }
+};
+
+/// @}
+
+NANOARROW_CXX_NAMESPACE_END
+
+#endif
diff --git a/src/nanoarrow/hpp/array_stream_test.cc
b/src/nanoarrow/hpp/array_stream_test.cc
new file mode 100644
index 00000000..5e178827
--- /dev/null
+++ b/src/nanoarrow/hpp/array_stream_test.cc
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+
+#include "nanoarrow/nanoarrow.hpp"
+
+using testing::ElementsAre;
+
+TEST(HppArrayStream, EmptyArrayStream) {
+ nanoarrow::UniqueSchema schema;
+ struct ArrowArray array;
+
+ nanoarrow::UniqueSchema schema_in;
+ EXPECT_EQ(ArrowSchemaInitFromType(schema_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
+
+ nanoarrow::UniqueArrayStream array_stream;
+
nanoarrow::EmptyArrayStream(schema_in.get()).ToArrayStream(array_stream.get());
+
+ EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream.get(), schema.get(),
nullptr),
+ NANOARROW_OK);
+ EXPECT_STREQ(schema->format, "i");
+ EXPECT_EQ(ArrowArrayStreamGetNext(array_stream.get(), &array, nullptr),
NANOARROW_OK);
+ EXPECT_EQ(array.release, nullptr);
+ EXPECT_STREQ(ArrowArrayStreamGetLastError(array_stream.get()), "");
+}
+
+TEST(HppArrayStream, VectorArrayStream) {
+ nanoarrow::UniqueArray array_in;
+ EXPECT_EQ(ArrowArrayInitFromType(array_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayStartAppending(array_in.get()), NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayAppendInt(array_in.get(), 1234), NANOARROW_OK);
+ EXPECT_EQ(ArrowArrayFinishBuildingDefault(array_in.get(), nullptr),
NANOARROW_OK);
+
+ nanoarrow::UniqueSchema schema_in;
+ EXPECT_EQ(ArrowSchemaInitFromType(schema_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
+
+ nanoarrow::UniqueArrayStream array_stream;
+ nanoarrow::VectorArrayStream(schema_in.get(), array_in.get())
+ .ToArrayStream(array_stream.get());
+
+ nanoarrow::ViewArrayStream array_stream_view(array_stream.get());
+ for (ArrowArray& array : array_stream_view) {
+ EXPECT_THAT(nanoarrow::ViewArrayAs<int32_t>(&array), ElementsAre(1234));
+ }
+ EXPECT_EQ(array_stream_view.count(), 1);
+ EXPECT_EQ(array_stream_view.code(), NANOARROW_OK);
+ EXPECT_STREQ(array_stream_view.error()->message, "");
+}
diff --git a/src/nanoarrow/hpp/buffer.hpp b/src/nanoarrow/hpp/buffer.hpp
new file mode 100644
index 00000000..b42ec0d0
--- /dev/null
+++ b/src/nanoarrow/hpp/buffer.hpp
@@ -0,0 +1,85 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#ifndef NANOARROW_HPP_BUFFER_HPP_INCLUDED
+#define NANOARROW_HPP_BUFFER_HPP_INCLUDED
+
+#include <stdint.h>
+#include <utility>
+#include "nanoarrow/nanoarrow.h"
+
+NANOARROW_CXX_NAMESPACE_BEGIN
+
+namespace internal {
+template <typename T>
+static inline void DeallocateWrappedBuffer(struct ArrowBufferAllocator*
allocator,
+ uint8_t* ptr, int64_t size) {
+ NANOARROW_UNUSED(ptr);
+ NANOARROW_UNUSED(size);
+ auto obj = reinterpret_cast<T*>(allocator->private_data);
+ delete obj;
+}
+} // namespace internal
+
+/// \defgroup nanoarrow_hpp-buffer Buffer helpers
+///
+/// Helpers to wrap buffer-like C++ objects as ArrowBuffer objects that can
+/// be used to build ArrowArray objects.
+///
+/// @{
+
+/// \brief Initialize a buffer wrapping an arbitrary C++ object
+///
+/// Initializes a buffer with a release callback that deletes the moved obj
+/// when ArrowBufferReset is called. This version is useful for wrapping
+/// an object whose .data() member is missing or unrelated to the buffer
+/// value that is destined for a the buffer of an ArrowArray. T must be
movable.
+template <typename T>
+static inline void BufferInitWrapped(struct ArrowBuffer* buffer, T obj,
+ const uint8_t* data, int64_t size_bytes) {
+ T* obj_moved = new T(std::move(obj));
+ buffer->data = const_cast<uint8_t*>(data);
+ buffer->size_bytes = size_bytes;
+ buffer->capacity_bytes = 0;
+ buffer->allocator =
+ ArrowBufferDeallocator(&internal::DeallocateWrappedBuffer<T>, obj_moved);
+}
+
+/// \brief Initialize a buffer wrapping a C++ sequence
+///
+/// Specifically, this uses obj.data() to set the buffer address and
+/// obj.size() * sizeof(T::value_type) to set the buffer size. This works
+/// for STL containers like std::vector, std::array, and std::string.
+/// This function moves obj and ensures it is deleted when ArrowBufferReset
+/// is called.
+template <typename T>
+void BufferInitSequence(struct ArrowBuffer* buffer, T obj) {
+ // Move before calling .data() (matters sometimes).
+ T* obj_moved = new T(std::move(obj));
+ buffer->data =
+ const_cast<uint8_t*>(reinterpret_cast<const
uint8_t*>(obj_moved->data()));
+ buffer->size_bytes = obj_moved->size() * sizeof(typename T::value_type);
+ buffer->capacity_bytes = 0;
+ buffer->allocator =
+ ArrowBufferDeallocator(&internal::DeallocateWrappedBuffer<T>, obj_moved);
+}
+
+/// @}
+
+NANOARROW_CXX_NAMESPACE_END
+
+#endif
diff --git a/src/nanoarrow/hpp/buffer_test.cc b/src/nanoarrow/hpp/buffer_test.cc
new file mode 100644
index 00000000..19e5bedf
--- /dev/null
+++ b/src/nanoarrow/hpp/buffer_test.cc
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <array>
+
+#include <gtest/gtest.h>
+
+#include "nanoarrow/nanoarrow.hpp"
+
+struct TestWrappedObj {
+ int64_t* num_frees;
+
+ TestWrappedObj(int64_t* addr) { num_frees = addr; }
+
+ TestWrappedObj(TestWrappedObj&& obj) {
+ num_frees = obj.num_frees;
+ obj.num_frees = nullptr;
+ }
+
+ ~TestWrappedObj() {
+ if (num_frees != nullptr) {
+ *num_frees = *num_frees + 1;
+ }
+ }
+};
+
+TEST(HppBuffer, BufferInitWrapped) {
+ nanoarrow::UniqueBuffer buffer;
+ int64_t num_frees = 0;
+
+ TestWrappedObj obj(&num_frees);
+ nanoarrow::BufferInitWrapped(buffer.get(), std::move(obj), nullptr, 0);
+ EXPECT_EQ(obj.num_frees, nullptr);
+ EXPECT_EQ(num_frees, 0);
+ buffer.reset();
+ EXPECT_EQ(num_frees, 1);
+
+ // Ensure the destructor won't get called again when ArrowBufferReset is
+ // called on the empty buffer.
+ buffer.reset();
+ EXPECT_EQ(num_frees, 1);
+}
+
+TEST(HppBuffer, BufferInitSequence) {
+ nanoarrow::UniqueBuffer buffer;
+
+ // Check templating magic with std::string
+ nanoarrow::BufferInitSequence(buffer.get(), std::string("1234"));
+ EXPECT_EQ(buffer->size_bytes, 4);
+ EXPECT_EQ(buffer->capacity_bytes, 0);
+ EXPECT_EQ(memcmp(buffer->data, "1234", 4), 0);
+
+ // Check templating magic with std::vector
+ buffer.reset();
+ nanoarrow::BufferInitSequence(buffer.get(), std::vector<uint8_t>({1, 2, 3,
4}));
+ EXPECT_EQ(buffer->size_bytes, 4);
+ EXPECT_EQ(buffer->capacity_bytes, 0);
+ EXPECT_EQ(buffer->data[0], 1);
+ EXPECT_EQ(buffer->data[1], 2);
+ EXPECT_EQ(buffer->data[2], 3);
+ EXPECT_EQ(buffer->data[3], 4);
+
+ // Check templating magic with std::array
+ buffer.reset();
+ nanoarrow::BufferInitSequence(buffer.get(), std::array<uint8_t, 4>({1, 2, 3,
4}));
+ EXPECT_EQ(buffer->size_bytes, 4);
+ EXPECT_EQ(buffer->capacity_bytes, 0);
+ EXPECT_EQ(buffer->data[0], 1);
+ EXPECT_EQ(buffer->data[1], 2);
+ EXPECT_EQ(buffer->data[2], 3);
+ EXPECT_EQ(buffer->data[3], 4);
+}
diff --git a/src/nanoarrow/hpp/exception.hpp b/src/nanoarrow/hpp/exception.hpp
new file mode 100644
index 00000000..6ff4a01b
--- /dev/null
+++ b/src/nanoarrow/hpp/exception.hpp
@@ -0,0 +1,79 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#ifndef NANOARROW_HPP_EXCEPTION_HPP_INCLUDED
+#define NANOARROW_HPP_EXCEPTION_HPP_INCLUDED
+
+#include <exception>
+#include <string>
+
+#include "nanoarrow/nanoarrow.h"
+
+NANOARROW_CXX_NAMESPACE_BEGIN
+
+/// \defgroup nanoarrow_hpp-errors Error handling helpers
+///
+/// Most functions in the C API return an ArrowErrorCode to communicate
+/// possible failure. Except where documented, it is usually not safe to
+/// continue after a non-zero value has been returned. While the
+/// nanoarrow C++ helpers do not throw any exceptions of their own,
+/// these helpers are provided to facilitate using the nanoarrow C++ helpers
+/// in frameworks where this is a useful error handling idiom.
+///
+/// @{
+
+class Exception : public std::exception {
+ public:
+ Exception(const std::string& msg) : msg_(msg) {}
+ const char* what() const noexcept { return msg_.c_str(); }
+
+ private:
+ std::string msg_;
+};
+
+#if defined(NANOARROW_DEBUG)
+#define _NANOARROW_THROW_NOT_OK_IMPL(NAME, EXPR, EXPR_STR)
\
+ do {
\
+ const int NAME = (EXPR);
\
+ if (NAME) {
\
+ throw nanoarrow::Exception(
\
+ std::string(EXPR_STR) + std::string(" failed with errno ") +
\
+ std::to_string(NAME) + std::string("\n * ") + std::string(__FILE__)
+ \
+ std::string(":") + std::to_string(__LINE__) + std::string("\n"));
\
+ }
\
+ } while (0)
+#else
+#define _NANOARROW_THROW_NOT_OK_IMPL(NAME, EXPR, EXPR_STR) \
+ do { \
+ const int NAME = (EXPR); \
+ if (NAME) { \
+ throw nanoarrow::Exception(std::string(EXPR_STR) + \
+ std::string(" failed with errno ") + \
+ std::to_string(NAME)); \
+ } \
+ } while (0)
+#endif
+
+#define NANOARROW_THROW_NOT_OK(EXPR)
\
+ _NANOARROW_THROW_NOT_OK_IMPL(_NANOARROW_MAKE_NAME(errno_status_,
__COUNTER__), EXPR, \
+ #EXPR)
+
+/// @}
+
+NANOARROW_CXX_NAMESPACE_END
+
+#endif
diff --git a/src/nanoarrow/nanoarrow_config.h.in
b/src/nanoarrow/hpp/exception_test.cc
similarity index 61%
copy from src/nanoarrow/nanoarrow_config.h.in
copy to src/nanoarrow/hpp/exception_test.cc
index b29709a9..388afede 100644
--- a/src/nanoarrow/nanoarrow_config.h.in
+++ b/src/nanoarrow/hpp/exception_test.cc
@@ -15,18 +15,16 @@
// specific language governing permissions and limitations
// under the License.
-#ifndef NANOARROW_BUILD_ID_H_INCLUDED
-#define NANOARROW_BUILD_ID_H_INCLUDED
+#include <gtest/gtest.h>
-#define NANOARROW_VERSION_MAJOR @NANOARROW_VERSION_MAJOR@
-#define NANOARROW_VERSION_MINOR @NANOARROW_VERSION_MINOR@
-#define NANOARROW_VERSION_PATCH @NANOARROW_VERSION_PATCH@
-#define NANOARROW_VERSION "@NANOARROW_VERSION@"
+#include "nanoarrow/nanoarrow.hpp"
-#define NANOARROW_VERSION_INT \
- (NANOARROW_VERSION_MAJOR * 10000 + NANOARROW_VERSION_MINOR * 100 + \
- NANOARROW_VERSION_PATCH)
-
-@NANOARROW_NAMESPACE_DEFINE@
-
-#endif
+TEST(HppException, Exception) {
+ ASSERT_THROW(NANOARROW_THROW_NOT_OK(EINVAL), nanoarrow::Exception);
+ ASSERT_NO_THROW(NANOARROW_THROW_NOT_OK(NANOARROW_OK));
+ try {
+ NANOARROW_THROW_NOT_OK(EINVAL);
+ } catch (const nanoarrow::Exception& e) {
+ EXPECT_EQ(std::string(e.what()).substr(0, 24), "EINVAL failed with errno");
+ }
+}
diff --git a/src/nanoarrow/hpp/operators.hpp b/src/nanoarrow/hpp/operators.hpp
new file mode 100644
index 00000000..23b854bf
--- /dev/null
+++ b/src/nanoarrow/hpp/operators.hpp
@@ -0,0 +1,62 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#ifndef NANOARROW_HPP_OPERATORS_HPP_INCLUDED
+#define NANOARROW_HPP_OPERATORS_HPP_INCLUDED
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "nanoarrow/nanoarrow.h"
+
+NANOARROW_CXX_NAMESPACE_BEGIN
+
+namespace literals {
+
+/// \defgroup nanoarrow_hpp-string_view_helpers ArrowStringView helpers
+///
+/// Factories and equality comparison for ArrowStringView.
+///
+/// @{
+
+/// \brief User literal operator allowing ArrowStringView construction like
"str"_asv
+#if !defined(__clang__) && (defined(__GNUC__) && __GNUC__ < 6)
+inline ArrowStringView operator"" _asv(const char* data, size_t size_bytes) {
+ return {data, static_cast<int64_t>(size_bytes)};
+}
+#else
+inline ArrowStringView operator""_asv(const char* data, size_t size_bytes) {
+ return {data, static_cast<int64_t>(size_bytes)};
+}
+#endif
+// N.B. older GCC requires the space above, newer Clang forbids the space
+
+// @}
+
+} // namespace literals
+
+NANOARROW_CXX_NAMESPACE_END
+
+/// \brief Equality comparison operator between ArrowStringView
+/// \ingroup nanoarrow_hpp-string_view_helpers
+inline bool operator==(ArrowStringView l, ArrowStringView r) {
+ if (l.size_bytes != r.size_bytes) return false;
+ return memcmp(l.data, r.data, l.size_bytes) == 0;
+}
+
+#endif
diff --git a/src/nanoarrow/hpp/unique.hpp b/src/nanoarrow/hpp/unique.hpp
new file mode 100644
index 00000000..a67f4fcd
--- /dev/null
+++ b/src/nanoarrow/hpp/unique.hpp
@@ -0,0 +1,226 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#ifndef NANOARROW_HPP_UNIQUE_HPP_INCLUDED
+#define NANOARROW_HPP_UNIQUE_HPP_INCLUDED
+
+#include <string.h>
+
+#include "nanoarrow/nanoarrow.h"
+
+NANOARROW_CXX_NAMESPACE_BEGIN
+
+namespace internal {
+
+/// \defgroup nanoarrow_hpp-unique_base Base classes for Unique wrappers
+///
+/// @{
+
+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;
+}
+
+template <>
+inline void move_pointer(struct ArrowSchema* src, struct ArrowSchema* dst) {
+ ArrowSchemaMove(src, dst);
+}
+
+template <>
+inline void release_pointer(struct ArrowSchema* data) {
+ if (data->release != nullptr) {
+ data->release(data);
+ }
+}
+
+template <>
+inline void init_pointer(struct ArrowArray* data) {
+ data->release = nullptr;
+}
+
+template <>
+inline void move_pointer(struct ArrowArray* src, struct ArrowArray* dst) {
+ ArrowArrayMove(src, dst);
+}
+
+template <>
+inline void release_pointer(struct ArrowArray* data) {
+ if (data->release != nullptr) {
+ data->release(data);
+ }
+}
+
+template <>
+inline void init_pointer(struct ArrowArrayStream* data) {
+ data->release = nullptr;
+}
+
+template <>
+inline void move_pointer(struct ArrowArrayStream* src, struct
ArrowArrayStream* dst) {
+ ArrowArrayStreamMove(src, dst);
+}
+
+template <>
+inline void release_pointer(ArrowArrayStream* data) {
+ if (data->release != nullptr) {
+ data->release(data);
+ }
+}
+
+template <>
+inline void init_pointer(struct ArrowBuffer* data) {
+ ArrowBufferInit(data);
+}
+
+template <>
+inline void move_pointer(struct ArrowBuffer* src, struct ArrowBuffer* dst) {
+ ArrowBufferMove(src, dst);
+}
+
+template <>
+inline void release_pointer(struct ArrowBuffer* data) {
+ ArrowBufferReset(data);
+}
+
+template <>
+inline void init_pointer(struct ArrowBitmap* data) {
+ ArrowBitmapInit(data);
+}
+
+template <>
+inline void move_pointer(struct ArrowBitmap* src, struct ArrowBitmap* dst) {
+ ArrowBitmapMove(src, dst);
+}
+
+template <>
+inline void release_pointer(struct ArrowBitmap* data) {
+ ArrowBitmapReset(data);
+}
+
+template <>
+inline void init_pointer(struct ArrowArrayView* data) {
+ ArrowArrayViewInitFromType(data, NANOARROW_TYPE_UNINITIALIZED);
+}
+
+template <>
+inline void move_pointer(struct ArrowArrayView* src, struct ArrowArrayView*
dst) {
+ ArrowArrayViewMove(src, dst);
+}
+
+template <>
+inline void release_pointer(struct ArrowArrayView* data) {
+ ArrowArrayViewReset(data);
+}
+
+/// \brief A unique_ptr-like base class for stack-allocatable objects
+/// \tparam T The object type
+template <typename T>
+class Unique {
+ public:
+ /// \brief Construct an invalid instance of T holding no resources
+ Unique() {
+ memset(&data_, 0, sizeof(data_));
+ init_pointer(&data_);
+ }
+
+ /// \brief Move and take ownership of data
+ Unique(T* data) {
+ memset(&data_, 0, sizeof(data_));
+ move_pointer(data, &data_);
+ }
+
+ /// \brief Move and take ownership of data wrapped by rhs
+ Unique(Unique&& rhs) : Unique(rhs.get()) {}
+ Unique& operator=(Unique&& rhs) {
+ reset(rhs.get());
+ return *this;
+ }
+
+ // These objects are not copyable
+ Unique(const Unique& rhs) = delete;
+
+ /// \brief Get a pointer to the data owned by this object
+ T* get() noexcept { return &data_; }
+ const T* get() const noexcept { return &data_; }
+
+ /// \brief Use the pointer operator to access fields of this object
+ T* operator->() noexcept { return &data_; }
+ const T* operator->() const noexcept { return &data_; }
+
+ /// \brief Call data's release callback if valid
+ void reset() { release_pointer(&data_); }
+
+ /// \brief Call data's release callback if valid and move ownership of the
data
+ /// pointed to by data
+ void reset(T* data) {
+ reset();
+ move_pointer(data, &data_);
+ }
+
+ /// \brief Move ownership of this object to the data pointed to by out
+ void move(T* out) { move_pointer(&data_, out); }
+
+ ~Unique() { reset(); }
+
+ protected:
+ T data_;
+};
+
+/// @}
+
+} // namespace internal
+
+/// \defgroup nanoarrow_hpp-unique Unique object wrappers
+///
+/// The Arrow C Data interface, the Arrow C Stream interface, and the
+/// nanoarrow C library use stack-allocatable objects, some of which
+/// require initialization or cleanup.
+///
+/// @{
+
+/// \brief Class wrapping a unique struct ArrowSchema
+using UniqueSchema = internal::Unique<struct ArrowSchema>;
+
+/// \brief Class wrapping a unique struct ArrowArray
+using UniqueArray = internal::Unique<struct ArrowArray>;
+
+/// \brief Class wrapping a unique struct ArrowArrayStream
+using UniqueArrayStream = internal::Unique<struct ArrowArrayStream>;
+
+/// \brief Class wrapping a unique struct ArrowBuffer
+using UniqueBuffer = internal::Unique<struct ArrowBuffer>;
+
+/// \brief Class wrapping a unique struct ArrowBitmap
+using UniqueBitmap = internal::Unique<struct ArrowBitmap>;
+
+/// \brief Class wrapping a unique struct ArrowArrayView
+using UniqueArrayView = internal::Unique<struct ArrowArrayView>;
+
+/// @}
+
+NANOARROW_CXX_NAMESPACE_END
+
+#endif
diff --git a/src/nanoarrow/hpp/unique_test.cc b/src/nanoarrow/hpp/unique_test.cc
new file mode 100644
index 00000000..d9d25cac
--- /dev/null
+++ b/src/nanoarrow/hpp/unique_test.cc
@@ -0,0 +1,182 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <gtest/gtest.h>
+
+#include "nanoarrow/nanoarrow.hpp"
+
+TEST(HppUnique, UniqueArray) {
+ nanoarrow::UniqueArray array;
+ EXPECT_EQ(array->release, nullptr);
+
+ ASSERT_EQ(ArrowArrayInitFromType(array.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayStartAppending(array.get()), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayAppendInt(array.get(), 123), NANOARROW_OK);
+ ASSERT_EQ(ArrowArrayFinishBuildingDefault(array.get(), nullptr),
NANOARROW_OK);
+
+ EXPECT_NE(array->release, nullptr);
+ EXPECT_EQ(array->length, 1);
+
+ // move constructor
+ nanoarrow::UniqueArray array2 = std::move(array);
+ EXPECT_EQ(array->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
+ EXPECT_NE(array2->release, nullptr);
+ EXPECT_EQ(array2->length, 1);
+
+ // pointer constructor
+ nanoarrow::UniqueArray array3(array2.get());
+ EXPECT_EQ(array2->release, nullptr);
+ EXPECT_NE(array3->release, nullptr);
+ EXPECT_EQ(array3->length, 1);
+}
+
+TEST(HppUnique, UniqueSchema) {
+ nanoarrow::UniqueSchema schema;
+ EXPECT_EQ(schema->release, nullptr);
+
+ ASSERT_EQ(ArrowSchemaInitFromType(schema.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ EXPECT_NE(schema->release, nullptr);
+ EXPECT_STREQ(schema->format, "i");
+
+ // move constructor
+ nanoarrow::UniqueSchema schema2 = std::move(schema);
+ EXPECT_EQ(schema->release, nullptr); //
NOLINT(clang-analyzer-cplusplus.Move)
+ EXPECT_NE(schema2->release, nullptr);
+ EXPECT_STREQ(schema2->format, "i");
+
+ // pointer constructor
+ nanoarrow::UniqueSchema schema3(schema2.get());
+ EXPECT_EQ(schema2->release, nullptr);
+ EXPECT_NE(schema3->release, nullptr);
+ EXPECT_STREQ(schema3->format, "i");
+}
+
+TEST(HppUnique, UniqueArrayStream) {
+ nanoarrow::UniqueSchema schema;
+ schema->format = NULL;
+
+ nanoarrow::UniqueArrayStream array_stream_default;
+ EXPECT_EQ(array_stream_default->release, nullptr);
+
+ nanoarrow::UniqueSchema schema_in;
+ EXPECT_EQ(ArrowSchemaInitFromType(schema_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ nanoarrow::UniqueArrayStream array_stream;
+
nanoarrow::EmptyArrayStream(schema_in.get()).ToArrayStream(array_stream.get());
+ EXPECT_NE(array_stream->release, nullptr);
+ EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream.get(), schema.get(),
nullptr),
+ NANOARROW_OK);
+ EXPECT_STREQ(schema->format, "i");
+ schema.reset();
+ schema->format = NULL;
+
+ // move constructor
+ nanoarrow::UniqueArrayStream array_stream2 = std::move(array_stream);
+ EXPECT_EQ(array_stream->release, nullptr); //
NOLINT(clang-analyzer-cplusplus.Move)
+ EXPECT_NE(array_stream2->release, nullptr);
+ EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream2.get(), schema.get(),
nullptr),
+ NANOARROW_OK);
+ EXPECT_STREQ(schema->format, "i");
+ schema.reset();
+ schema->format = NULL;
+
+ // pointer constructor
+ nanoarrow::UniqueArrayStream array_stream3(array_stream2.get());
+ EXPECT_EQ(array_stream2->release, nullptr);
+ EXPECT_NE(array_stream3->release, nullptr);
+ EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream2.get(), schema.get(),
nullptr),
+ NANOARROW_OK);
+ EXPECT_STREQ(schema->format, "i");
+
+ // releasing should clear the release callback
+ EXPECT_EQ(ArrowSchemaInitFromType(schema_in.get(), NANOARROW_TYPE_INT32),
NANOARROW_OK);
+ nanoarrow::UniqueArrayStream array_stream4;
+
nanoarrow::EmptyArrayStream(schema_in.get()).ToArrayStream(array_stream4.get());
+ EXPECT_NE(array_stream4->release, nullptr);
+ array_stream4->release(array_stream4.get());
+ EXPECT_EQ(array_stream4->private_data, nullptr);
+ EXPECT_EQ(array_stream4->release, nullptr);
+}
+
+TEST(HppUnique, UniqueBuffer) {
+ nanoarrow::UniqueBuffer buffer;
+ EXPECT_EQ(buffer->data, nullptr);
+ EXPECT_EQ(buffer->size_bytes, 0);
+
+ ASSERT_EQ(ArrowBufferAppendFill(buffer.get(), 0xff, 123), NANOARROW_OK);
+ EXPECT_NE(buffer->data, nullptr);
+ EXPECT_EQ(buffer->size_bytes, 123);
+
+ // move constructor
+ nanoarrow::UniqueBuffer buffer2 = std::move(buffer);
+ EXPECT_EQ(buffer->data, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
+ EXPECT_EQ(buffer->size_bytes, 0); // NOLINT(clang-analyzer-cplusplus.Move)
+ EXPECT_NE(buffer2->data, nullptr);
+ EXPECT_EQ(buffer2->size_bytes, 123);
+
+ // pointer constructor
+ nanoarrow::UniqueBuffer buffer3(buffer2.get());
+ EXPECT_EQ(buffer2->data, nullptr);
+ EXPECT_EQ(buffer2->size_bytes, 0);
+ EXPECT_NE(buffer3->data, nullptr);
+ EXPECT_EQ(buffer3->size_bytes, 123);
+}
+
+TEST(HppUnique, UniqueBitmap) {
+ nanoarrow::UniqueBitmap bitmap;
+ EXPECT_EQ(bitmap->buffer.data, nullptr);
+ EXPECT_EQ(bitmap->size_bits, 0);
+
+ ASSERT_EQ(ArrowBitmapAppend(bitmap.get(), true, 123), NANOARROW_OK);
+ EXPECT_NE(bitmap->buffer.data, nullptr);
+ EXPECT_EQ(bitmap->size_bits, 123);
+
+ // move constructor
+ nanoarrow::UniqueBitmap bitmap2 = std::move(bitmap);
+ EXPECT_EQ(bitmap->buffer.data, nullptr); //
NOLINT(clang-analyzer-cplusplus.Move)
+ EXPECT_EQ(bitmap->size_bits, 0); //
NOLINT(clang-analyzer-cplusplus.Move)
+ EXPECT_NE(bitmap2->buffer.data, nullptr);
+ EXPECT_EQ(bitmap2->size_bits, 123);
+
+ // pointer constructor
+ nanoarrow::UniqueBitmap bitmap3(bitmap2.get());
+ EXPECT_EQ(bitmap2->buffer.data, nullptr);
+ EXPECT_EQ(bitmap2->size_bits, 0);
+ EXPECT_NE(bitmap3->buffer.data, nullptr);
+ EXPECT_EQ(bitmap3->size_bits, 123);
+}
+
+TEST(HppUnique, UniqueArrayView) {
+ nanoarrow::UniqueArrayView array_view;
+ EXPECT_EQ(array_view->storage_type, NANOARROW_TYPE_UNINITIALIZED);
+
+ // Use an ArrayView with children, since an ArrayView with no children
+ // doesn't hold any resources
+ ArrowArrayViewInitFromType(array_view.get(), NANOARROW_TYPE_STRUCT);
+ ASSERT_EQ(ArrowArrayViewAllocateChildren(array_view.get(), 2), NANOARROW_OK);
+ EXPECT_EQ(array_view->storage_type, NANOARROW_TYPE_STRUCT);
+
+ // move constructor
+ nanoarrow::UniqueArrayView array_view2 = std::move(array_view);
+ EXPECT_EQ(array_view->storage_type, // NOLINT(clang-analyzer-cplusplus.Move)
+ NANOARROW_TYPE_UNINITIALIZED);
+ EXPECT_EQ(array_view2->storage_type, NANOARROW_TYPE_STRUCT);
+
+ // pointer constructor
+ nanoarrow::UniqueArrayView array_view3(array_view2.get());
+ EXPECT_EQ(array_view2->storage_type, NANOARROW_TYPE_UNINITIALIZED);
+ EXPECT_EQ(array_view3->storage_type, NANOARROW_TYPE_STRUCT);
+}
diff --git a/src/nanoarrow/hpp/view.hpp b/src/nanoarrow/hpp/view.hpp
new file mode 100644
index 00000000..f0e267ff
--- /dev/null
+++ b/src/nanoarrow/hpp/view.hpp
@@ -0,0 +1,377 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#ifndef NANOARROW_HPP_VIEW_HPP_INCLUDED
+#define NANOARROW_HPP_VIEW_HPP_INCLUDED
+
+#include <stdint.h>
+#include <type_traits>
+
+#include "nanoarrow/hpp/unique.hpp"
+#include "nanoarrow/nanoarrow.h"
+
+NANOARROW_CXX_NAMESPACE_BEGIN
+
+namespace internal {
+struct Nothing {};
+
+template <typename T>
+class Maybe {
+ public:
+ Maybe() : nothing_(Nothing()), is_something_(false) {}
+ Maybe(Nothing) : Maybe() {}
+
+ Maybe(T something) // NOLINT(google-explicit-constructor)
+ : something_(something), is_something_(true) {}
+
+ explicit constexpr operator bool() const { return is_something_; }
+
+ const T& operator*() const { return something_; }
+
+ friend inline bool operator==(Maybe l, Maybe r) {
+ if (l.is_something_ != r.is_something_) return false;
+ return l.is_something_ ? l.something_ == r.something_ : true;
+ }
+ friend inline bool operator!=(Maybe l, Maybe r) { return !(l == r); }
+
+ T value_or(T val) const { return is_something_ ? something_ : val; }
+
+ private:
+ // When support for gcc 4.8 is dropped, we should also assert
+ // is_trivially_copyable<T>::value
+ static_assert(std::is_trivially_destructible<T>::value, "");
+
+ union {
+ Nothing nothing_;
+ T something_;
+ };
+ bool is_something_;
+};
+
+template <typename Get>
+struct RandomAccessRange {
+ Get get;
+ int64_t size;
+
+ using value_type = decltype(std::declval<Get>()(0));
+
+ struct const_iterator {
+ int64_t i;
+ const RandomAccessRange* range;
+ bool operator==(const_iterator other) const { return i == other.i; }
+ bool operator!=(const_iterator other) const { return i != other.i; }
+ const_iterator& operator++() { return ++i, *this; }
+ value_type operator*() const { return range->get(i); }
+ };
+
+ const_iterator begin() const { return {0, this}; }
+ const_iterator end() const { return {size, this}; }
+};
+
+template <typename Next>
+struct InputRange {
+ Next next;
+ using ValueOrFalsy = decltype(std::declval<Next>()());
+
+ static_assert(std::is_constructible<bool, ValueOrFalsy>::value, "");
+ static_assert(std::is_default_constructible<ValueOrFalsy>::value, "");
+ using value_type = decltype(*std::declval<ValueOrFalsy>());
+
+ struct iterator {
+ InputRange* range;
+ ValueOrFalsy stashed;
+
+ bool operator==(iterator other) const {
+ return static_cast<bool>(stashed) == static_cast<bool>(other.stashed);
+ }
+ bool operator!=(iterator other) const { return !(*this == other); }
+
+ iterator& operator++() {
+ stashed = range->next();
+ return *this;
+ }
+ value_type operator*() const { return *stashed; }
+ };
+
+ iterator begin() { return {this, next()}; }
+ iterator end() { return {this, ValueOrFalsy()}; }
+};
+} // namespace internal
+
+/// \defgroup nanoarrow_hpp-range_for Range-for helpers
+///
+/// The Arrow C Data interface and the Arrow C Stream interface represent
+/// data which can be iterated through using C++'s range-for statement.
+///
+/// @{
+
+/// \brief An object convertible to any empty optional
+constexpr internal::Nothing NA{};
+
+/// \brief A range-for compatible wrapper for ArrowArray of fixed size type
+///
+/// Provides a sequence of optional<T> copied from each non-null
+/// slot of the wrapped array (null slots result in empty optionals).
+template <typename T>
+class ViewArrayAs {
+ private:
+ struct Get {
+ const uint8_t* validity;
+ const void* values;
+ int64_t offset;
+
+ internal::Maybe<T> operator()(int64_t i) const {
+ i += offset;
+ if (validity == nullptr || ArrowBitGet(validity, i)) {
+ if (std::is_same<T, bool>::value) {
+ return ArrowBitGet(static_cast<const uint8_t*>(values), i);
+ } else {
+ return static_cast<const T*>(values)[i];
+ }
+ }
+ return NA;
+ }
+ };
+
+ internal::RandomAccessRange<Get> range_;
+
+ public:
+ ViewArrayAs(const ArrowArrayView* array_view)
+ : range_{
+ Get{
+ array_view->buffer_views[0].data.as_uint8,
+ array_view->buffer_views[1].data.data,
+ array_view->offset,
+ },
+ array_view->length,
+ } {}
+
+ ViewArrayAs(const ArrowArray* array)
+ : range_{
+ Get{
+ static_cast<const uint8_t*>(array->buffers[0]),
+ array->buffers[1],
+ /*offset=*/0,
+ },
+ array->length,
+ } {}
+
+ using value_type = typename internal::RandomAccessRange<Get>::value_type;
+ 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); }
+};
+
+/// \brief A range-for compatible wrapper for ArrowArray of binary or utf8
+///
+/// Provides a sequence of optional<ArrowStringView> referencing each non-null
+/// slot of the wrapped array (null slots result in empty optionals). Large
+/// binary and utf8 arrays can be wrapped by specifying 64 instead of 32 for
+/// the template argument.
+template <int OffsetSize>
+class ViewArrayAsBytes {
+ private:
+ static_assert(OffsetSize == 32 || OffsetSize == 64, "");
+ using OffsetType = typename std::conditional<OffsetSize == 32, int32_t,
int64_t>::type;
+
+ struct Get {
+ const uint8_t* validity;
+ const void* offsets;
+ const char* data;
+ int64_t offset;
+
+ internal::Maybe<ArrowStringView> operator()(int64_t i) const {
+ i += offset;
+ auto* offsets = static_cast<const OffsetType*>(this->offsets);
+ if (validity == nullptr || ArrowBitGet(validity, i)) {
+ return ArrowStringView{data + offsets[i], offsets[i + 1] - offsets[i]};
+ }
+ return NA;
+ }
+ };
+
+ internal::RandomAccessRange<Get> range_;
+
+ public:
+ ViewArrayAsBytes(const ArrowArrayView* array_view)
+ : range_{
+ Get{
+ array_view->buffer_views[0].data.as_uint8,
+ array_view->buffer_views[1].data.data,
+ array_view->buffer_views[2].data.as_char,
+ array_view->offset,
+ },
+ array_view->length,
+ } {}
+
+ ViewArrayAsBytes(const ArrowArray* array)
+ : range_{
+ Get{
+ static_cast<const uint8_t*>(array->buffers[0]),
+ array->buffers[1],
+ static_cast<const char*>(array->buffers[2]),
+ /*offset=*/0,
+ },
+ array->length,
+ } {}
+
+ using value_type = typename internal::RandomAccessRange<Get>::value_type;
+ 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); }
+};
+
+/// \brief A range-for compatible wrapper for ArrowArray of fixed size binary
+///
+/// Provides a sequence of optional<ArrowStringView> referencing each non-null
+/// slot of the wrapped array (null slots result in empty optionals).
+class ViewArrayAsFixedSizeBytes {
+ private:
+ struct Get {
+ const uint8_t* validity;
+ const char* data;
+ int64_t offset;
+ int fixed_size;
+
+ internal::Maybe<ArrowStringView> operator()(int64_t i) const {
+ i += offset;
+ if (validity == nullptr || ArrowBitGet(validity, i)) {
+ return ArrowStringView{data + i * fixed_size, fixed_size};
+ }
+ return NA;
+ }
+ };
+
+ internal::RandomAccessRange<Get> range_;
+
+ public:
+ ViewArrayAsFixedSizeBytes(const ArrowArrayView* array_view, int fixed_size)
+ : range_{
+ Get{
+ array_view->buffer_views[0].data.as_uint8,
+ array_view->buffer_views[1].data.as_char,
+ array_view->offset,
+ fixed_size,
+ },
+ array_view->length,
+ } {}
+
+ ViewArrayAsFixedSizeBytes(const ArrowArray* array, int fixed_size)
+ : range_{
+ Get{
+ static_cast<const uint8_t*>(array->buffers[0]),
+ static_cast<const char*>(array->buffers[1]),
+ /*offset=*/0,
+ fixed_size,
+ },
+ array->length,
+ } {}
+
+ using value_type = typename internal::RandomAccessRange<Get>::value_type;
+ 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); }
+};
+
+/// \brief A range-for compatible wrapper for ArrowArrayStream
+///
+/// Provides a sequence of ArrowArray& referencing the most recent array drawn
+/// from the wrapped stream. (Each array may be moved from if necessary.)
+/// When streams terminate due to an error, the error code and message are
+/// available for inspection through the code() and error() member functions
+/// respectively. Failure to inspect the error code will result in
+/// an assertion failure. The number of arrays drawn from the stream is also
+/// available through the count() member function.
+class ViewArrayStream {
+ public:
+ ViewArrayStream(ArrowArrayStream* stream, ArrowErrorCode* code, ArrowError*
error)
+ : code_{code}, error_{error} {
+ // Using a slightly more verbose constructor to silence a warning that
occurs
+ // on some versions of MSVC.
+ range_.next.self = this;
+ range_.next.stream = stream;
+ }
+
+ ViewArrayStream(ArrowArrayStream* stream, ArrowError* error)
+ : ViewArrayStream{stream, &internal_code_, error} {}
+
+ ViewArrayStream(ArrowArrayStream* stream)
+ : ViewArrayStream{stream, &internal_code_, &internal_error_} {}
+
+ // disable copy/move of this view, since its error references may point into
itself
+ ViewArrayStream(ViewArrayStream&&) = delete;
+ ViewArrayStream& operator=(ViewArrayStream&&) = delete;
+ ViewArrayStream(const ViewArrayStream&) = delete;
+ ViewArrayStream& operator=(const ViewArrayStream&) = delete;
+
+ // ensure the error code of this stream was accessed at least once
+ ~ViewArrayStream() { NANOARROW_DCHECK(code_was_accessed_); }
+
+ private:
+ struct Next {
+ ViewArrayStream* self;
+ ArrowArrayStream* stream;
+ UniqueArray array;
+
+ ArrowArray* operator()() {
+ array.reset();
+ *self->code_ = ArrowArrayStreamGetNext(stream, array.get(),
self->error_);
+
+ if (array->release != nullptr) {
+ NANOARROW_DCHECK(*self->code_ == NANOARROW_OK);
+ ++self->count_;
+ return array.get();
+ }
+
+ return nullptr;
+ }
+ };
+
+ internal::InputRange<Next> range_;
+ ArrowErrorCode* code_;
+ ArrowError* error_;
+ ArrowError internal_error_ = {};
+ ArrowErrorCode internal_code_;
+ bool code_was_accessed_ = false;
+ int count_ = 0;
+
+ public:
+ using value_type = typename internal::InputRange<Next>::value_type;
+ using iterator = typename internal::InputRange<Next>::iterator;
+ iterator begin() { return range_.begin(); }
+ iterator end() { return range_.end(); }
+
+ /// The error code which caused this stream to terminate, if any.
+ ArrowErrorCode code() {
+ code_was_accessed_ = true;
+ return *code_;
+ }
+ /// The error message which caused this stream to terminate, if any.
+ ArrowError* error() { return error_; }
+
+ /// The number of arrays streamed so far.
+ int count() const { return count_; }
+};
+
+/// @}
+
+NANOARROW_CXX_NAMESPACE_END
+
+#endif
diff --git a/src/nanoarrow/hpp/view_test.cc b/src/nanoarrow/hpp/view_test.cc
new file mode 100644
index 00000000..e18c7c63
--- /dev/null
+++ b/src/nanoarrow/hpp/view_test.cc
@@ -0,0 +1,137 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+
+#include "nanoarrow/nanoarrow.hpp"
+#include "nanoarrow/nanoarrow_gtest_util.hpp"
+
+using testing::ElementsAre;
+
+TEST(NanoarrowHppTest, NanoarrowHppViewArrayAsTest) {
+ nanoarrow::UniqueBuffer is_valid, floats;
+ nanoarrow::BufferInitSequence(is_valid.get(), std::vector<uint8_t>{0xFF});
+ ArrowBitClear(is_valid->data, 2);
+ ArrowBitClear(is_valid->data, 5);
+ nanoarrow::BufferInitSequence(floats.get(),
+ std::vector<float>{8, 4, 2, 1, .5, .25, .125});
+
+ const void* buffers[] = {is_valid->data, floats->data};
+ struct ArrowArray array {};
+ array.length = 7;
+ array.null_count = 2;
+ array.n_buffers = 2;
+ array.buffers = buffers;
+
+ int i = 0;
+ float f = 8;
+ for (auto slot : nanoarrow::ViewArrayAs<float>(&array)) {
+ if (i == 2 || i == 5) {
+ EXPECT_EQ(slot, nanoarrow::NA);
+ } else {
+ EXPECT_EQ(slot, f);
+ }
+ ++i;
+ f /= 2;
+ }
+}
+
+TEST(NanoarrowHppTest, NanoarrowHppViewArrayAsBytesTest) {
+ using namespace nanoarrow::literals;
+
+ nanoarrow::UniqueBuffer is_valid, offsets, data;
+ nanoarrow::BufferInitSequence(is_valid.get(), std::vector<uint8_t>{0xFF});
+ ArrowBitClear(is_valid->data, 2);
+ ArrowBitClear(is_valid->data, 5);
+ nanoarrow::BufferInitSequence(offsets.get(),
+ std::vector<int32_t>{0, 1, 2, 3, 4, 5, 6, 7});
+ nanoarrow::BufferInitSequence(data.get(), std::string{"abcdefghi"});
+
+ const void* buffers[] = {is_valid->data, offsets->data, data->data};
+ struct ArrowArray array {};
+ array.length = 7;
+ array.null_count = 2;
+ array.n_buffers = 2;
+ array.buffers = buffers;
+
+ int i = 0;
+ ArrowStringView expected[] = {"a"_asv, "b"_asv, "c"_asv, "d"_asv,
+ "e"_asv, "f"_asv, "g"_asv};
+ for (auto slot : nanoarrow::ViewArrayAsBytes<32>(&array)) {
+ if (i == 2 || i == 5) {
+ EXPECT_EQ(slot, nanoarrow::NA);
+ } else {
+ EXPECT_EQ(slot, expected[i]);
+ }
+ ++i;
+ }
+}
+
+TEST(NanoarrowHppTest, NanoarrowHppViewArrayAsFixedSizeBytesTest) {
+ using namespace nanoarrow::literals;
+
+ nanoarrow::UniqueBuffer is_valid, data;
+ nanoarrow::BufferInitSequence(is_valid.get(), std::vector<uint8_t>{0xFF});
+ ArrowBitClear(is_valid->data, 2);
+ ArrowBitClear(is_valid->data, 5);
+ nanoarrow::BufferInitSequence(
+ data.get(), std::string{"foo"} + "bar" + "foo" + "bar" + "foo" + "bar" +
"foo");
+
+ const void* buffers[] = {is_valid->data, data->data};
+ struct ArrowArray array {};
+ array.length = 7;
+ array.null_count = 2;
+ array.n_buffers = 2;
+ array.buffers = buffers;
+
+ int i = 0;
+ for (auto slot : nanoarrow::ViewArrayAsFixedSizeBytes(&array, 3)) {
+ if (i == 2 || i == 5) {
+ EXPECT_EQ(slot, nanoarrow::NA);
+ } else {
+ EXPECT_EQ(slot, i % 2 == 0 ? "foo"_asv : "bar"_asv);
+ }
+ ++i;
+ }
+}
+
+TEST(NanoarrowHppTest, NanoarrowHppViewArrayStreamTest) {
+ static int32_t slot = 1;
+
+ struct ArrowArrayStream stream {};
+ stream.get_schema = [](struct ArrowArrayStream*, struct ArrowSchema* out) {
+ return ArrowSchemaInitFromType(out, NANOARROW_TYPE_INT32);
+ };
+ stream.get_next = [](struct ArrowArrayStream*, struct ArrowArray* out) {
+ if (slot >= 16) return ENOMEM;
+ NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromType(out, NANOARROW_TYPE_INT32));
+ NANOARROW_RETURN_NOT_OK(ArrowArrayStartAppending(out));
+ NANOARROW_RETURN_NOT_OK(ArrowArrayAppendInt(out, slot *= 2));
+ return ArrowArrayFinishBuildingDefault(out, nullptr);
+ };
+ stream.get_last_error = [](struct ArrowArrayStream*) { return "foo bar"; };
+ stream.release = [](struct ArrowArrayStream*) {};
+
+ nanoarrow::ViewArrayStream stream_view(&stream);
+ for (ArrowArray& array : stream_view) {
+ EXPECT_THAT(nanoarrow::ViewArrayAs<int32_t>(&array), ElementsAre(slot));
+ }
+ EXPECT_EQ(stream_view.count(), 4);
+ EXPECT_EQ(stream_view.code(), ENOMEM);
+ EXPECT_STREQ(stream_view.error()->message, "foo bar");
+}
diff --git a/src/nanoarrow/nanoarrow.hpp b/src/nanoarrow/nanoarrow.hpp
index 84125ada..855e1eab 100644
--- a/src/nanoarrow/nanoarrow.hpp
+++ b/src/nanoarrow/nanoarrow.hpp
@@ -15,16 +15,6 @@
// specific language governing permissions and limitations
// under the License.
-#ifndef NANOARROW_HPP_INCLUDED
-#define NANOARROW_HPP_INCLUDED
-
-#include <cstring>
-#include <exception>
-#include <string>
-#include <vector>
-
-#include "nanoarrow/nanoarrow.h"
-
/// \defgroup nanoarrow_hpp Nanoarrow C++ Helpers
///
/// The utilities provided in this file are intended to support C++ users
@@ -32,908 +22,9 @@
/// and error handling can be used with nanoarrow data structures.
/// These utilities are not intended to mirror the nanoarrow C API.
-namespace nanoarrow {
-
-/// \defgroup nanoarrow_hpp-errors Error handling helpers
-///
-/// Most functions in the C API return an ArrowErrorCode to communicate
-/// possible failure. Except where documented, it is usually not safe to
-/// continue after a non-zero value has been returned. While the
-/// nanoarrow C++ helpers do not throw any exceptions of their own,
-/// these helpers are provided to facilitate using the nanoarrow C++ helpers
-/// in frameworks where this is a useful error handling idiom.
-///
-/// @{
-
-class Exception : public std::exception {
- public:
- Exception(const std::string& msg) : msg_(msg) {}
- const char* what() const noexcept { return msg_.c_str(); }
-
- private:
- std::string msg_;
-};
-
-#if defined(NANOARROW_DEBUG)
-#define _NANOARROW_THROW_NOT_OK_IMPL(NAME, EXPR, EXPR_STR)
\
- do {
\
- const int NAME = (EXPR);
\
- if (NAME) {
\
- throw nanoarrow::Exception(
\
- std::string(EXPR_STR) + std::string(" failed with errno ") +
\
- std::to_string(NAME) + std::string("\n * ") + std::string(__FILE__)
+ \
- std::string(":") + std::to_string(__LINE__) + std::string("\n"));
\
- }
\
- } while (0)
-#else
-#define _NANOARROW_THROW_NOT_OK_IMPL(NAME, EXPR, EXPR_STR) \
- do { \
- const int NAME = (EXPR); \
- if (NAME) { \
- throw nanoarrow::Exception(std::string(EXPR_STR) + \
- std::string(" failed with errno ") + \
- std::to_string(NAME)); \
- } \
- } while (0)
-#endif
-
-#define NANOARROW_THROW_NOT_OK(EXPR)
\
- _NANOARROW_THROW_NOT_OK_IMPL(_NANOARROW_MAKE_NAME(errno_status_,
__COUNTER__), EXPR, \
- #EXPR)
-
-/// @}
-
-namespace literals {
-
-/// \defgroup nanoarrow_hpp-string_view_helpers ArrowStringView helpers
-///
-/// Factories and equality comparison for ArrowStringView.
-///
-/// @{
-
-/// \brief User literal operator allowing ArrowStringView construction like
"str"_asv
-#if !defined(__clang__) && (defined(__GNUC__) && __GNUC__ < 6)
-inline ArrowStringView operator"" _asv(const char* data, std::size_t
size_bytes) {
- return {data, static_cast<int64_t>(size_bytes)};
-}
-#else
-inline ArrowStringView operator""_asv(const char* data, std::size_t
size_bytes) {
- return {data, static_cast<int64_t>(size_bytes)};
-}
-#endif
-// N.B. older GCC requires the space above, newer Clang forbids the space
-
-// @}
-
-} // namespace literals
-
-namespace internal {
-
-/// \defgroup nanoarrow_hpp-unique_base Base classes for Unique wrappers
-///
-/// @{
-
-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;
-}
-
-template <>
-inline void move_pointer(struct ArrowSchema* src, struct ArrowSchema* dst) {
- ArrowSchemaMove(src, dst);
-}
-
-template <>
-inline void release_pointer(struct ArrowSchema* data) {
- if (data->release != nullptr) {
- data->release(data);
- }
-}
-
-template <>
-inline void init_pointer(struct ArrowArray* data) {
- data->release = nullptr;
-}
-
-template <>
-inline void move_pointer(struct ArrowArray* src, struct ArrowArray* dst) {
- ArrowArrayMove(src, dst);
-}
-
-template <>
-inline void release_pointer(struct ArrowArray* data) {
- if (data->release != nullptr) {
- data->release(data);
- }
-}
-
-template <>
-inline void init_pointer(struct ArrowArrayStream* data) {
- data->release = nullptr;
-}
-
-template <>
-inline void move_pointer(struct ArrowArrayStream* src, struct
ArrowArrayStream* dst) {
- ArrowArrayStreamMove(src, dst);
-}
-
-template <>
-inline void release_pointer(ArrowArrayStream* data) {
- if (data->release != nullptr) {
- data->release(data);
- }
-}
-
-template <>
-inline void init_pointer(struct ArrowBuffer* data) {
- ArrowBufferInit(data);
-}
-
-template <>
-inline void move_pointer(struct ArrowBuffer* src, struct ArrowBuffer* dst) {
- ArrowBufferMove(src, dst);
-}
-
-template <>
-inline void release_pointer(struct ArrowBuffer* data) {
- ArrowBufferReset(data);
-}
-
-template <>
-inline void init_pointer(struct ArrowBitmap* data) {
- ArrowBitmapInit(data);
-}
-
-template <>
-inline void move_pointer(struct ArrowBitmap* src, struct ArrowBitmap* dst) {
- ArrowBitmapMove(src, dst);
-}
-
-template <>
-inline void release_pointer(struct ArrowBitmap* data) {
- ArrowBitmapReset(data);
-}
-
-template <>
-inline void init_pointer(struct ArrowArrayView* data) {
- ArrowArrayViewInitFromType(data, NANOARROW_TYPE_UNINITIALIZED);
-}
-
-template <>
-inline void move_pointer(struct ArrowArrayView* src, struct ArrowArrayView*
dst) {
- ArrowArrayViewMove(src, dst);
-}
-
-template <>
-inline void release_pointer(struct ArrowArrayView* data) {
- ArrowArrayViewReset(data);
-}
-
-/// \brief A unique_ptr-like base class for stack-allocatable objects
-/// \tparam T The object type
-template <typename T>
-class Unique {
- public:
- /// \brief Construct an invalid instance of T holding no resources
- Unique() {
- std::memset(&data_, 0, sizeof(data_));
- init_pointer(&data_);
- }
-
- /// \brief Move and take ownership of data
- Unique(T* data) {
- std::memset(&data_, 0, sizeof(data_));
- move_pointer(data, &data_);
- }
-
- /// \brief Move and take ownership of data wrapped by rhs
- Unique(Unique&& rhs) : Unique(rhs.get()) {}
- Unique& operator=(Unique&& rhs) {
- reset(rhs.get());
- return *this;
- }
-
- // These objects are not copyable
- Unique(const Unique& rhs) = delete;
-
- /// \brief Get a pointer to the data owned by this object
- T* get() noexcept { return &data_; }
- const T* get() const noexcept { return &data_; }
-
- /// \brief Use the pointer operator to access fields of this object
- T* operator->() noexcept { return &data_; }
- const T* operator->() const noexcept { return &data_; }
-
- /// \brief Call data's release callback if valid
- void reset() { release_pointer(&data_); }
-
- /// \brief Call data's release callback if valid and move ownership of the
data
- /// pointed to by data
- void reset(T* data) {
- reset();
- move_pointer(data, &data_);
- }
-
- /// \brief Move ownership of this object to the data pointed to by out
- void move(T* out) { move_pointer(&data_, out); }
-
- ~Unique() { reset(); }
-
- protected:
- T data_;
-};
-
-template <typename T>
-static inline void DeallocateWrappedBuffer(struct ArrowBufferAllocator*
allocator,
- uint8_t* ptr, int64_t size) {
- NANOARROW_UNUSED(ptr);
- NANOARROW_UNUSED(size);
- auto obj = reinterpret_cast<T*>(allocator->private_data);
- delete obj;
-}
-
-/// @}
-
-} // namespace internal
-
-/// \defgroup nanoarrow_hpp-unique Unique object wrappers
-///
-/// The Arrow C Data interface, the Arrow C Stream interface, and the
-/// nanoarrow C library use stack-allocatable objects, some of which
-/// require initialization or cleanup.
-///
-/// @{
-
-/// \brief Class wrapping a unique struct ArrowSchema
-using UniqueSchema = internal::Unique<struct ArrowSchema>;
-
-/// \brief Class wrapping a unique struct ArrowArray
-using UniqueArray = internal::Unique<struct ArrowArray>;
-
-/// \brief Class wrapping a unique struct ArrowArrayStream
-using UniqueArrayStream = internal::Unique<struct ArrowArrayStream>;
-
-/// \brief Class wrapping a unique struct ArrowBuffer
-using UniqueBuffer = internal::Unique<struct ArrowBuffer>;
-
-/// \brief Class wrapping a unique struct ArrowBitmap
-using UniqueBitmap = internal::Unique<struct ArrowBitmap>;
-
-/// \brief Class wrapping a unique struct ArrowArrayView
-using UniqueArrayView = internal::Unique<struct ArrowArrayView>;
-
-/// @}
-
-/// \defgroup nanoarrow_hpp-buffer Buffer helpers
-///
-/// Helpers to wrap buffer-like C++ objects as ArrowBuffer objects that can
-/// be used to build ArrowArray objects.
-///
-/// @{
-
-/// \brief Initialize a buffer wrapping an arbitrary C++ object
-///
-/// Initializes a buffer with a release callback that deletes the moved obj
-/// when ArrowBufferReset is called. This version is useful for wrapping
-/// an object whose .data() member is missing or unrelated to the buffer
-/// value that is destined for a the buffer of an ArrowArray. T must be
movable.
-template <typename T>
-static inline void BufferInitWrapped(struct ArrowBuffer* buffer, T obj,
- const uint8_t* data, int64_t size_bytes) {
- T* obj_moved = new T(std::move(obj));
- buffer->data = const_cast<uint8_t*>(data);
- buffer->size_bytes = size_bytes;
- buffer->capacity_bytes = 0;
- buffer->allocator =
- ArrowBufferDeallocator(&internal::DeallocateWrappedBuffer<T>, obj_moved);
-}
-
-/// \brief Initialize a buffer wrapping a C++ sequence
-///
-/// Specifically, this uses obj.data() to set the buffer address and
-/// obj.size() * sizeof(T::value_type) to set the buffer size. This works
-/// for STL containers like std::vector, std::array, and std::string.
-/// This function moves obj and ensures it is deleted when ArrowBufferReset
-/// is called.
-template <typename T>
-void BufferInitSequence(struct ArrowBuffer* buffer, T obj) {
- // Move before calling .data() (matters sometimes).
- T* obj_moved = new T(std::move(obj));
- buffer->data =
- const_cast<uint8_t*>(reinterpret_cast<const
uint8_t*>(obj_moved->data()));
- buffer->size_bytes = obj_moved->size() * sizeof(typename T::value_type);
- buffer->capacity_bytes = 0;
- buffer->allocator =
- ArrowBufferDeallocator(&internal::DeallocateWrappedBuffer<T>, obj_moved);
-}
-
-/// @}
-
-/// \defgroup nanoarrow_hpp-array-stream ArrayStream helpers
-///
-/// 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 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
- ///
- /// DEPRECATED (0.4.0): Use the constructor + ToArrayStream() to export an
- /// EmptyArrayStream to an ArrowArrayStream consumer.
- static UniqueArrayStream MakeUnique(struct ArrowSchema* schema) {
- UniqueArrayStream stream;
- EmptyArrayStream(schema).ToArrayStream(stream.get());
- return stream;
- }
-
- virtual ~EmptyArrayStream() {}
-
- protected:
- UniqueSchema schema_;
- struct ArrowError error_;
-
- void MakeStream(struct ArrowArrayStream* stream) { ToArrayStream(stream); }
-
- virtual int get_schema(struct ArrowSchema* schema) {
- return ArrowSchemaDeepCopy(schema_.get(), schema);
- }
-
- virtual int get_next(struct ArrowArray* array) {
- array->release = nullptr;
- return NANOARROW_OK;
- }
-
- virtual const char* get_last_error() { return error_.message; }
-
- private:
- friend class ArrayStreamFactory<EmptyArrayStream>;
-
- int GetSchema(struct ArrowSchema* schema) { return get_schema(schema); }
-
- int GetNext(struct ArrowArray* array) { return get_next(array); }
-
- const char* GetLastError() { return get_last_error(); }
-};
-
-/// \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
- ///
- /// 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) {
- UniqueArrayStream stream;
- VectorArrayStream(schema, array).ToArrayStream(stream.get());
- return stream;
- }
-
- /// \brief Create a UniqueArrowArrayStream from existing 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;
- VectorArrayStream(schema, std::move(arrays)).ToArrayStream(stream.get());
- return stream;
- }
-
- private:
- int64_t offset_;
- UniqueSchema schema_;
- std::vector<UniqueArray> arrays_;
-
- friend class ArrayStreamFactory<VectorArrayStream>;
-
- 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 {
- array->release = nullptr;
- }
-
- return NANOARROW_OK;
- }
-
- const char* GetLastError() { return ""; }
-};
-
-/// @}
-
-namespace internal {
-struct Nothing {};
-
-template <typename T>
-class Maybe {
- public:
- Maybe() : nothing_(Nothing()), is_something_(false) {}
- Maybe(Nothing) : Maybe() {}
-
- Maybe(T something) // NOLINT(google-explicit-constructor)
- : something_(something), is_something_(true) {}
-
- explicit constexpr operator bool() const { return is_something_; }
-
- const T& operator*() const { return something_; }
-
- friend inline bool operator==(Maybe l, Maybe r) {
- if (l.is_something_ != r.is_something_) return false;
- return l.is_something_ ? l.something_ == r.something_ : true;
- }
- friend inline bool operator!=(Maybe l, Maybe r) { return !(l == r); }
-
- T value_or(T val) const { return is_something_ ? something_ : val; }
-
- private:
- // When support for gcc 4.8 is dropped, we should also assert
- // is_trivially_copyable<T>::value
- static_assert(std::is_trivially_destructible<T>::value, "");
-
- union {
- Nothing nothing_;
- T something_;
- };
- bool is_something_;
-};
-
-template <typename Get>
-struct RandomAccessRange {
- Get get;
- int64_t size;
-
- using value_type = decltype(std::declval<Get>()(0));
-
- struct const_iterator {
- int64_t i;
- const RandomAccessRange* range;
- bool operator==(const_iterator other) const { return i == other.i; }
- bool operator!=(const_iterator other) const { return i != other.i; }
- const_iterator& operator++() { return ++i, *this; }
- value_type operator*() const { return range->get(i); }
- };
-
- const_iterator begin() const { return {0, this}; }
- const_iterator end() const { return {size, this}; }
-};
-
-template <typename Next>
-struct InputRange {
- Next next;
- using ValueOrFalsy = decltype(std::declval<Next>()());
-
- static_assert(std::is_constructible<bool, ValueOrFalsy>::value, "");
- static_assert(std::is_default_constructible<ValueOrFalsy>::value, "");
- using value_type = decltype(*std::declval<ValueOrFalsy>());
-
- struct iterator {
- InputRange* range;
- ValueOrFalsy stashed;
-
- bool operator==(iterator other) const {
- return static_cast<bool>(stashed) == static_cast<bool>(other.stashed);
- }
- bool operator!=(iterator other) const { return !(*this == other); }
-
- iterator& operator++() {
- stashed = range->next();
- return *this;
- }
- value_type operator*() const { return *stashed; }
- };
-
- iterator begin() { return {this, next()}; }
- iterator end() { return {this, ValueOrFalsy()}; }
-};
-} // namespace internal
-
-/// \defgroup nanoarrow_hpp-range_for Range-for helpers
-///
-/// The Arrow C Data interface and the Arrow C Stream interface represent
-/// data which can be iterated through using C++'s range-for statement.
-///
-/// @{
-
-/// \brief An object convertible to any empty optional
-constexpr internal::Nothing NA{};
-
-/// \brief A range-for compatible wrapper for ArrowArray of fixed size type
-///
-/// Provides a sequence of optional<T> copied from each non-null
-/// slot of the wrapped array (null slots result in empty optionals).
-template <typename T>
-class ViewArrayAs {
- private:
- struct Get {
- const uint8_t* validity;
- const void* values;
- int64_t offset;
-
- internal::Maybe<T> operator()(int64_t i) const {
- i += offset;
- if (validity == nullptr || ArrowBitGet(validity, i)) {
- if (std::is_same<T, bool>::value) {
- return ArrowBitGet(static_cast<const uint8_t*>(values), i);
- } else {
- return static_cast<const T*>(values)[i];
- }
- }
- return NA;
- }
- };
-
- internal::RandomAccessRange<Get> range_;
-
- public:
- ViewArrayAs(const ArrowArrayView* array_view)
- : range_{
- Get{
- array_view->buffer_views[0].data.as_uint8,
- array_view->buffer_views[1].data.data,
- array_view->offset,
- },
- array_view->length,
- } {}
-
- ViewArrayAs(const ArrowArray* array)
- : range_{
- Get{
- static_cast<const uint8_t*>(array->buffers[0]),
- array->buffers[1],
- /*offset=*/0,
- },
- array->length,
- } {}
-
- using value_type = typename internal::RandomAccessRange<Get>::value_type;
- 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); }
-};
-
-/// \brief A range-for compatible wrapper for ArrowArray of binary or utf8
-///
-/// Provides a sequence of optional<ArrowStringView> referencing each non-null
-/// slot of the wrapped array (null slots result in empty optionals). Large
-/// binary and utf8 arrays can be wrapped by specifying 64 instead of 32 for
-/// the template argument.
-template <int OffsetSize>
-class ViewArrayAsBytes {
- private:
- static_assert(OffsetSize == 32 || OffsetSize == 64, "");
- using OffsetType = typename std::conditional<OffsetSize == 32, int32_t,
int64_t>::type;
-
- struct Get {
- const uint8_t* validity;
- const void* offsets;
- const char* data;
- int64_t offset;
-
- internal::Maybe<ArrowStringView> operator()(int64_t i) const {
- i += offset;
- auto* offsets = static_cast<const OffsetType*>(this->offsets);
- if (validity == nullptr || ArrowBitGet(validity, i)) {
- return ArrowStringView{data + offsets[i], offsets[i + 1] - offsets[i]};
- }
- return NA;
- }
- };
-
- internal::RandomAccessRange<Get> range_;
-
- public:
- ViewArrayAsBytes(const ArrowArrayView* array_view)
- : range_{
- Get{
- array_view->buffer_views[0].data.as_uint8,
- array_view->buffer_views[1].data.data,
- array_view->buffer_views[2].data.as_char,
- array_view->offset,
- },
- array_view->length,
- } {}
-
- ViewArrayAsBytes(const ArrowArray* array)
- : range_{
- Get{
- static_cast<const uint8_t*>(array->buffers[0]),
- array->buffers[1],
- static_cast<const char*>(array->buffers[2]),
- /*offset=*/0,
- },
- array->length,
- } {}
-
- using value_type = typename internal::RandomAccessRange<Get>::value_type;
- 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); }
-};
-
-/// \brief A range-for compatible wrapper for ArrowArray of fixed size binary
-///
-/// Provides a sequence of optional<ArrowStringView> referencing each non-null
-/// slot of the wrapped array (null slots result in empty optionals).
-class ViewArrayAsFixedSizeBytes {
- private:
- struct Get {
- const uint8_t* validity;
- const char* data;
- int64_t offset;
- int fixed_size;
-
- internal::Maybe<ArrowStringView> operator()(int64_t i) const {
- i += offset;
- if (validity == nullptr || ArrowBitGet(validity, i)) {
- return ArrowStringView{data + i * fixed_size, fixed_size};
- }
- return NA;
- }
- };
-
- internal::RandomAccessRange<Get> range_;
-
- public:
- ViewArrayAsFixedSizeBytes(const ArrowArrayView* array_view, int fixed_size)
- : range_{
- Get{
- array_view->buffer_views[0].data.as_uint8,
- array_view->buffer_views[1].data.as_char,
- array_view->offset,
- fixed_size,
- },
- array_view->length,
- } {}
-
- ViewArrayAsFixedSizeBytes(const ArrowArray* array, int fixed_size)
- : range_{
- Get{
- static_cast<const uint8_t*>(array->buffers[0]),
- static_cast<const char*>(array->buffers[1]),
- /*offset=*/0,
- fixed_size,
- },
- array->length,
- } {}
-
- using value_type = typename internal::RandomAccessRange<Get>::value_type;
- 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); }
-};
-
-/// \brief A range-for compatible wrapper for ArrowArrayStream
-///
-/// Provides a sequence of ArrowArray& referencing the most recent array drawn
-/// from the wrapped stream. (Each array may be moved from if necessary.)
-/// When streams terminate due to an error, the error code and message are
-/// available for inspection through the code() and error() member functions
-/// respectively. Failure to inspect the error code will result in
-/// an assertion failure. The number of arrays drawn from the stream is also
-/// available through the count() member function.
-class ViewArrayStream {
- public:
- ViewArrayStream(ArrowArrayStream* stream, ArrowErrorCode* code, ArrowError*
error)
- : code_{code}, error_{error} {
- // Using a slightly more verbose constructor to silence a warning that
occurs
- // on some versions of MSVC.
- range_.next.self = this;
- range_.next.stream = stream;
- }
-
- ViewArrayStream(ArrowArrayStream* stream, ArrowError* error)
- : ViewArrayStream{stream, &internal_code_, error} {}
-
- ViewArrayStream(ArrowArrayStream* stream)
- : ViewArrayStream{stream, &internal_code_, &internal_error_} {}
-
- // disable copy/move of this view, since its error references may point into
itself
- ViewArrayStream(ViewArrayStream&&) = delete;
- ViewArrayStream& operator=(ViewArrayStream&&) = delete;
- ViewArrayStream(const ViewArrayStream&) = delete;
- ViewArrayStream& operator=(const ViewArrayStream&) = delete;
-
- // ensure the error code of this stream was accessed at least once
- ~ViewArrayStream() { NANOARROW_DCHECK(code_was_accessed_); }
-
- private:
- struct Next {
- ViewArrayStream* self;
- ArrowArrayStream* stream;
- UniqueArray array;
-
- ArrowArray* operator()() {
- array.reset();
- *self->code_ = ArrowArrayStreamGetNext(stream, array.get(),
self->error_);
-
- if (array->release != nullptr) {
- NANOARROW_DCHECK(*self->code_ == NANOARROW_OK);
- ++self->count_;
- return array.get();
- }
-
- return nullptr;
- }
- };
-
- internal::InputRange<Next> range_;
- ArrowErrorCode* code_;
- ArrowError* error_;
- ArrowError internal_error_ = {};
- ArrowErrorCode internal_code_;
- bool code_was_accessed_ = false;
- int count_ = 0;
-
- public:
- using value_type = typename internal::InputRange<Next>::value_type;
- using iterator = typename internal::InputRange<Next>::iterator;
- iterator begin() { return range_.begin(); }
- iterator end() { return range_.end(); }
-
- /// The error code which caused this stream to terminate, if any.
- ArrowErrorCode code() {
- code_was_accessed_ = true;
- return *code_;
- }
- /// The error message which caused this stream to terminate, if any.
- ArrowError* error() { return error_; }
-
- /// The number of arrays streamed so far.
- int count() const { return count_; }
-};
-
-/// @}
-
-} // namespace nanoarrow
-
-/// \brief Equality comparison operator between ArrowStringView
-/// \ingroup nanoarrow_hpp-string_view_helpers
-inline bool operator==(ArrowStringView l, ArrowStringView r) {
- if (l.size_bytes != r.size_bytes) return false;
- return memcmp(l.data, r.data, l.size_bytes) == 0;
-}
-
-#endif
+#include "nanoarrow/hpp/array_stream.hpp"
+#include "nanoarrow/hpp/buffer.hpp"
+#include "nanoarrow/hpp/exception.hpp"
+#include "nanoarrow/hpp/operators.hpp"
+#include "nanoarrow/hpp/unique.hpp"
+#include "nanoarrow/hpp/view.hpp"
diff --git a/src/nanoarrow/nanoarrow_config.h.in
b/src/nanoarrow/nanoarrow_config.h.in
index b29709a9..695a986c 100644
--- a/src/nanoarrow/nanoarrow_config.h.in
+++ b/src/nanoarrow/nanoarrow_config.h.in
@@ -15,8 +15,8 @@
// specific language governing permissions and limitations
// under the License.
-#ifndef NANOARROW_BUILD_ID_H_INCLUDED
-#define NANOARROW_BUILD_ID_H_INCLUDED
+#ifndef NANOARROW_CONFIG_H_INCLUDED
+#define NANOARROW_CONFIG_H_INCLUDED
#define NANOARROW_VERSION_MAJOR @NANOARROW_VERSION_MAJOR@
#define NANOARROW_VERSION_MINOR @NANOARROW_VERSION_MINOR@
@@ -29,4 +29,11 @@
@NANOARROW_NAMESPACE_DEFINE@
+#if !defined(NANOARROW_CXX_NAMESPACE)
+#define NANOARROW_CXX_NAMESPACE nanoarrow
+#endif
+
+#define NANOARROW_CXX_NAMESPACE_BEGIN namespace NANOARROW_CXX_NAMESPACE {
+#define NANOARROW_CXX_NAMESPACE_END }
+
#endif