blob: 26387691d3a3496439a82d5d4477012a9abd7ff4 [file] [log] [blame]
// 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 <stdexcept>
#include <string>
#include <vector>
#include "nanoarrow.h"
#ifndef NANOARROW_HPP_INCLUDED
#define NANOARROW_HPP_INCLUDED
/// \defgroup nanoarrow_hpp Nanoarrow C++ Helpers
///
/// The utilities provided in this file are intended to support C++ users
/// of the nanoarrow C library such that C++-style resource allocation
/// 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 internal {
/// \defgroup nanoarrow_hpp-unique_base Base classes for Unique wrappers
///
/// @{
static inline void init_pointer(struct ArrowSchema* data) { data->release = nullptr; }
static inline void move_pointer(struct ArrowSchema* src, struct ArrowSchema* dst) {
ArrowSchemaMove(src, dst);
}
static inline void release_pointer(struct ArrowSchema* data) {
if (data->release != nullptr) {
data->release(data);
}
}
static inline void init_pointer(struct ArrowArray* data) { data->release = nullptr; }
static inline void move_pointer(struct ArrowArray* src, struct ArrowArray* dst) {
ArrowArrayMove(src, dst);
}
static inline void release_pointer(struct ArrowArray* data) {
if (data->release != nullptr) {
data->release(data);
}
}
static inline void init_pointer(struct ArrowArrayStream* data) {
data->release = nullptr;
}
static inline void move_pointer(struct ArrowArrayStream* src,
struct ArrowArrayStream* dst) {
ArrowArrayStreamMove(src, dst);
}
static inline void release_pointer(ArrowArrayStream* data) {
if (data->release != nullptr) {
data->release(data);
}
}
static inline void init_pointer(struct ArrowBuffer* data) { ArrowBufferInit(data); }
static inline void move_pointer(struct ArrowBuffer* src, struct ArrowBuffer* dst) {
ArrowBufferMove(src, dst);
}
static inline void release_pointer(struct ArrowBuffer* data) { ArrowBufferReset(data); }
static inline void init_pointer(struct ArrowBitmap* data) { ArrowBitmapInit(data); }
static inline void move_pointer(struct ArrowBitmap* src, struct ArrowBitmap* dst) {
ArrowBitmapMove(src, dst);
}
static inline void release_pointer(struct ArrowBitmap* data) { ArrowBitmapReset(data); }
static inline void init_pointer(struct ArrowArrayView* data) {
ArrowArrayViewInitFromType(data, NANOARROW_TYPE_UNINITIALIZED);
}
static inline void move_pointer(struct ArrowArrayView* src, struct ArrowArrayView* dst) {
ArrowArrayViewMove(src, dst);
}
static 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() { init_pointer(&data_); }
/// \brief Move and take ownership of data
Unique(T* data) { move_pointer(data, &data_); }
/// \brief Move and take ownership of data wrapped by rhs
Unique(Unique&& rhs) : Unique(rhs.get()) {}
// These objects are not copyable
Unique(Unique& rhs) = delete;
/// \brief Get a pointer to the data owned by this object
T* get() noexcept { return &data_; }
/// \brief Use the pointer operator to access fields of this object
T* operator->() { 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>;
/// @}
/// \defgroup nanoarrow_hpp-array-stream ArrayStream helpers
///
/// These classes provide simple struct ArrowArrayStream implementations that
/// can be extended to help simplify the process of creating a valid
/// ArrowArrayStream implementation or used as-is for testing.
///
/// @{
/// \brief An empty array stream
///
/// This class can be constructed from an enum ArrowType or
/// struct ArrowSchema and implements a default get_next() method that
/// always marks the output ArrowArray as released. This class can
/// be extended with an implementation of get_next() for a custom
/// source.
class EmptyArrayStream {
public:
/// \brief Create an empty UniqueArrayStream from a struct ArrowSchema
///
/// This object takes ownership of the schema and marks the source schema
/// as released.
static UniqueArrayStream MakeUnique(struct ArrowSchema* schema) {
UniqueArrayStream stream;
(new EmptyArrayStream(schema))->MakeStream(stream.get());
return stream;
}
virtual ~EmptyArrayStream() {}
protected:
UniqueSchema schema_;
struct ArrowError error_;
EmptyArrayStream(struct ArrowSchema* schema) : schema_(schema) {
error_.message[0] = '\0';
}
void MakeStream(struct ArrowArrayStream* stream) {
stream->get_schema = &get_schema_wrapper;
stream->get_next = &get_next_wrapper;
stream->get_last_error = &get_last_error_wrapper;
stream->release = &release_wrapper;
stream->private_data = this;
}
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:
static int get_schema_wrapper(struct ArrowArrayStream* stream,
struct ArrowSchema* schema) {
return reinterpret_cast<EmptyArrayStream*>(stream->private_data)->get_schema(schema);
}
static int get_next_wrapper(struct ArrowArrayStream* stream, struct ArrowArray* array) {
return reinterpret_cast<EmptyArrayStream*>(stream->private_data)->get_next(array);
}
static const char* get_last_error_wrapper(struct ArrowArrayStream* stream) {
return reinterpret_cast<EmptyArrayStream*>(stream->private_data)->get_last_error();
}
static void release_wrapper(struct ArrowArrayStream* stream) {
delete reinterpret_cast<EmptyArrayStream*>(stream->private_data);
stream->release = nullptr;
stream->private_data = nullptr;
}
};
/// \brief Implementation of an ArrowArrayStream backed by a vector of ArrowArray objects
class VectorArrayStream : public EmptyArrayStream {
public:
/// \brief Create a UniqueArrowArrayStream from an existing array
///
/// Takes ownership of the schema and the array.
static UniqueArrayStream MakeUnique(struct ArrowSchema* schema,
struct ArrowArray* array) {
std::vector<UniqueArray> arrays;
arrays.emplace_back(array);
return MakeUnique(schema, std::move(arrays));
}
/// \brief Create a UniqueArrowArrayStream from existing arrays
///
/// This object takes ownership of the schema and arrays.
static UniqueArrayStream MakeUnique(struct ArrowSchema* schema,
std::vector<UniqueArray> arrays) {
UniqueArrayStream stream;
(new VectorArrayStream(schema, std::move(arrays)))->MakeStream(stream.get());
return stream;
}
protected:
VectorArrayStream(struct ArrowSchema* schema, std::vector<UniqueArray> arrays)
: EmptyArrayStream(schema), arrays_(std::move(arrays)), offset_(0) {}
int get_next(struct ArrowArray* array) {
if (offset_ < static_cast<int64_t>(arrays_.size())) {
arrays_[offset_++].move(array);
} else {
array->release = nullptr;
}
return NANOARROW_OK;
}
private:
std::vector<UniqueArray> arrays_;
int64_t offset_;
};
/// @}
} // namespace nanoarrow
#endif