| /* |
| * 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 |
| * |
| * https://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 avro_GenericDatum_hh__ |
| #define avro_GenericDatum_hh__ |
| |
| #include <stdint.h> |
| #include <vector> |
| #include <map> |
| #include <string> |
| |
| #if __cplusplus >= 201703L |
| #include <any> |
| #else |
| #include "boost/any.hpp" |
| #endif |
| |
| #include "LogicalType.hh" |
| #include "Node.hh" |
| #include "ValidSchema.hh" |
| |
| namespace avro { |
| |
| /** |
| * Generic datum which can hold any Avro type. The datum has a type |
| * and a value. The type is one of the Avro data types. The C++ type for |
| * value corresponds to the Avro type. |
| * \li An Avro <tt>null</tt> corresponds to no C++ type. It is illegal to |
| * to try to access values for <tt>null</tt>. |
| * \li Avro <tt>boolean</tt> maps to C++ <tt>bool</tt> |
| * \li Avro <tt>int</tt> maps to C++ <tt>int32_t</tt>. |
| * \li Avro <tt>long</tt> maps to C++ <tt>int64_t</tt>. |
| * \li Avro <tt>float</tt> maps to C++ <tt>float</tt>. |
| * \li Avro <tt>double</tt> maps to C++ <tt>double</tt>. |
| * \li Avro <tt>string</tt> maps to C++ <tt>std::string</tt>. |
| * \li Avro <tt>bytes</tt> maps to C++ <tt>std::vector<uint_t></tt>. |
| * \li Avro <tt>fixed</tt> maps to C++ class <tt>GenericFixed</tt>. |
| * \li Avro <tt>enum</tt> maps to C++ class <tt>GenericEnum</tt>. |
| * \li Avro <tt>array</tt> maps to C++ class <tt>GenericArray</tt>. |
| * \li Avro <tt>map</tt> maps to C++ class <tt>GenericMap</tt>. |
| * \li There is no C++ type corresponding to Avro <tt>union</tt>. The |
| * object should have the C++ type corresponing to one of the constituent |
| * types of the union. |
| * |
| */ |
| class AVRO_DECL GenericDatum { |
| protected: |
| Type type_; |
| LogicalType logicalType_; |
| #if __cplusplus >= 201703L |
| std::any value_; |
| #else |
| boost::any value_; |
| #endif |
| |
| GenericDatum(Type t) |
| : type_(t), logicalType_(LogicalType::NONE) { } |
| |
| GenericDatum(Type t, LogicalType logicalType) |
| : type_(t), logicalType_(logicalType) { } |
| |
| template <typename T> |
| GenericDatum(Type t, LogicalType logicalType, const T& v) |
| : type_(t), logicalType_(logicalType), value_(v) { } |
| |
| void init(const NodePtr& schema); |
| public: |
| /** |
| * The avro data type this datum holds. |
| */ |
| Type type() const; |
| |
| /** |
| * The avro logical type that augments the main data type this datum holds. |
| */ |
| LogicalType logicalType() const; |
| |
| /** |
| * Returns the value held by this datum. |
| * T The type for the value. This must correspond to the |
| * avro type returned by type(). |
| */ |
| template<typename T> const T& value() const; |
| |
| /** |
| * Returns the reference to the value held by this datum, which |
| * can be used to change the contents. Please note that only |
| * value can be changed, the data type of the value held cannot |
| * be changed. |
| * |
| * T The type for the value. This must correspond to the |
| * avro type returned by type(). |
| */ |
| template<typename T> T& value(); |
| |
| /** |
| * Returns true if and only if this datum is a union. |
| */ |
| bool isUnion() const { return type_ == AVRO_UNION; } |
| |
| /** |
| * Returns the index of the current branch, if this is a union. |
| * \sa isUnion(). |
| */ |
| size_t unionBranch() const; |
| |
| /** |
| * Selects a new branch in the union if this is a union. |
| * \sa isUnion(). |
| */ |
| void selectBranch(size_t branch); |
| |
| /// Makes a new AVRO_NULL datum. |
| GenericDatum() : type_(AVRO_NULL), logicalType_(LogicalType::NONE) { } |
| |
| /// Makes a new AVRO_BOOL datum whose value is of type bool. |
| GenericDatum(bool v) |
| : type_(AVRO_BOOL), logicalType_(LogicalType::NONE), value_(v) { } |
| |
| /// Makes a new AVRO_INT datum whose value is of type int32_t. |
| GenericDatum(int32_t v) |
| : type_(AVRO_INT), logicalType_(LogicalType::NONE), value_(v) { } |
| |
| /// Makes a new AVRO_LONG datum whose value is of type int64_t. |
| GenericDatum(int64_t v) |
| : type_(AVRO_LONG), logicalType_(LogicalType::NONE), value_(v) { } |
| |
| /// Makes a new AVRO_FLOAT datum whose value is of type float. |
| GenericDatum(float v) |
| : type_(AVRO_FLOAT), logicalType_(LogicalType::NONE), value_(v) { } |
| |
| /// Makes a new AVRO_DOUBLE datum whose value is of type double. |
| GenericDatum(double v) |
| : type_(AVRO_DOUBLE), logicalType_(LogicalType::NONE), value_(v) { } |
| |
| /// Makes a new AVRO_STRING datum whose value is of type std::string. |
| GenericDatum(const std::string& v) |
| : type_(AVRO_STRING), logicalType_(LogicalType::NONE), value_(v) { } |
| |
| /// Makes a new AVRO_BYTES datum whose value is of type |
| /// std::vector<uint8_t>. |
| GenericDatum(const std::vector<uint8_t>& v) : |
| type_(AVRO_BYTES), logicalType_(LogicalType::NONE), value_(v) { } |
| |
| /** |
| * Constructs a datum corresponding to the given avro type. |
| * The value will the appropraite default corresponding to the |
| * data type. |
| * \param schema The schema that defines the avro type. |
| */ |
| GenericDatum(const NodePtr& schema); |
| |
| /** |
| * Constructs a datum corresponding to the given avro type and set |
| * the value. |
| * \param schema The schema that defines the avro type. |
| * \param v The value for this type. |
| */ |
| template<typename T> |
| GenericDatum(const NodePtr& schema, const T& v) : |
| type_(schema->type()), logicalType_(schema->logicalType()) { |
| init(schema); |
| #if __cplusplus >= 201703L |
| *std::any_cast<T>(&value_) = v; |
| #else |
| *boost::any_cast<T>(&value_) = v; |
| #endif |
| } |
| |
| /** |
| * Constructs a datum corresponding to the given avro type. |
| * The value will the appropraite default corresponding to the |
| * data type. |
| * \param schema The schema that defines the avro type. |
| */ |
| GenericDatum(const ValidSchema& schema); |
| }; |
| |
| /** |
| * The base class for all generic type for containers. |
| */ |
| class AVRO_DECL GenericContainer { |
| NodePtr schema_; |
| static void assertType(const NodePtr& schema, Type type); |
| protected: |
| /** |
| * Constructs a container corresponding to the given schema. |
| */ |
| GenericContainer(Type type, const NodePtr& s) : schema_(s) { |
| assertType(s, type); |
| } |
| |
| public: |
| /// Returns the schema for this object |
| const NodePtr& schema() const { |
| return schema_; |
| } |
| }; |
| |
| /** |
| * Generic container for unions. |
| */ |
| class AVRO_DECL GenericUnion : public GenericContainer { |
| size_t curBranch_; |
| GenericDatum datum_; |
| |
| public: |
| /** |
| * Constructs a generic union corresponding to the given schema \p schema, |
| * and the given value. The schema should be of Avro type union |
| * and the value should correspond to one of the branches of the union. |
| */ |
| GenericUnion(const NodePtr& schema) : |
| GenericContainer(AVRO_UNION, schema), curBranch_(schema->leaves()) { |
| selectBranch(0); |
| } |
| |
| /** |
| * Returns the index of the current branch. |
| */ |
| size_t currentBranch() const { return curBranch_; } |
| |
| /** |
| * Selects a new branch. The type for the value is changed accordingly. |
| * \param branch The index for the selected branch. |
| */ |
| void selectBranch(size_t branch) { |
| if (curBranch_ != branch) { |
| datum_ = GenericDatum(schema()->leafAt(branch)); |
| curBranch_ = branch; |
| } |
| } |
| |
| /** |
| * Returns the datum corresponding to the currently selected branch |
| * in this union. |
| */ |
| GenericDatum& datum() { |
| return datum_; |
| } |
| |
| /** |
| * Returns the datum corresponding to the currently selected branch |
| * in this union. |
| */ |
| const GenericDatum& datum() const { |
| return datum_; |
| } |
| }; |
| |
| /** |
| * The generic container for Avro records. |
| */ |
| class AVRO_DECL GenericRecord : public GenericContainer { |
| std::vector<GenericDatum> fields_; |
| public: |
| /** |
| * Constructs a generic record corresponding to the given schema \p schema, |
| * which should be of Avro type record. |
| */ |
| GenericRecord(const NodePtr& schema); |
| |
| /** |
| * Returns the number of fields in the current record. |
| */ |
| size_t fieldCount() const { |
| return fields_.size(); |
| } |
| |
| /** |
| * Returns index of the field with the given name \p name |
| */ |
| size_t fieldIndex(const std::string& name) const { |
| size_t index = 0; |
| if (!schema()->nameIndex(name, index)) { |
| throw Exception("Invalid field name: " + name); |
| } |
| return index; |
| } |
| |
| /** |
| * Returns true if a field with the given name \p name is located in this r |
| * false otherwise |
| */ |
| bool hasField(const std::string& name) const { |
| size_t index = 0; |
| return schema()->nameIndex(name, index); |
| } |
| |
| /** |
| * Returns the field with the given name \p name. |
| */ |
| const GenericDatum& field(const std::string& name) const { |
| return fieldAt(fieldIndex(name)); |
| } |
| |
| /** |
| * Returns the reference to the field with the given name \p name, |
| * which can be used to change the contents. |
| */ |
| GenericDatum& field(const std::string& name) { |
| return fieldAt(fieldIndex(name)); |
| } |
| |
| /** |
| * Returns the field at the given position \p pos. |
| */ |
| const GenericDatum& fieldAt(size_t pos) const { |
| return fields_[pos]; |
| } |
| |
| /** |
| * Returns the reference to the field at the given position \p pos, |
| * which can be used to change the contents. |
| */ |
| GenericDatum& fieldAt(size_t pos) { |
| return fields_[pos]; |
| } |
| |
| /** |
| * Replaces the field at the given position \p pos with \p v. |
| */ |
| void setFieldAt(size_t pos, const GenericDatum& v) { |
| // assertSameType(v, schema()->leafAt(pos)); |
| fields_[pos] = v; |
| } |
| }; |
| |
| /** |
| * The generic container for Avro arrays. |
| */ |
| class AVRO_DECL GenericArray : public GenericContainer { |
| public: |
| /** |
| * The contents type for the array. |
| */ |
| typedef std::vector<GenericDatum> Value; |
| |
| /** |
| * Constructs a generic array corresponding to the given schema \p schema, |
| * which should be of Avro type array. |
| */ |
| GenericArray(const NodePtr& schema) : GenericContainer(AVRO_ARRAY, schema) { |
| } |
| |
| /** |
| * Returns the contents of this array. |
| */ |
| const Value& value() const { |
| return value_; |
| } |
| |
| /** |
| * Returns the reference to the contents of this array. |
| */ |
| Value& value() { |
| return value_; |
| } |
| private: |
| Value value_; |
| }; |
| |
| /** |
| * The generic container for Avro maps. |
| */ |
| class AVRO_DECL GenericMap : public GenericContainer { |
| public: |
| /** |
| * The contents type for the map. |
| */ |
| typedef std::vector<std::pair<std::string, GenericDatum> > Value; |
| |
| /** |
| * Constructs a generic map corresponding to the given schema \p schema, |
| * which should be of Avro type map. |
| */ |
| GenericMap(const NodePtr& schema) : GenericContainer(AVRO_MAP, schema) { |
| } |
| |
| /** |
| * Returns the contents of this map. |
| */ |
| const Value& value() const { |
| return value_; |
| } |
| |
| /** |
| * Returns the reference to the contents of this map. |
| */ |
| Value& value() { |
| return value_; |
| } |
| private: |
| Value value_; |
| }; |
| |
| /** |
| * Generic container for Avro enum. |
| */ |
| class AVRO_DECL GenericEnum : public GenericContainer { |
| size_t value_; |
| |
| static size_t index(const NodePtr& schema, const std::string& symbol) { |
| size_t result; |
| if (schema->nameIndex(symbol, result)) { |
| return result; |
| } |
| throw Exception("No such symbol"); |
| } |
| |
| public: |
| /** |
| * Constructs a generic enum corresponding to the given schema \p schema, |
| * which should be of Avro type enum. |
| */ |
| GenericEnum(const NodePtr& schema) : |
| GenericContainer(AVRO_ENUM, schema), value_(0) { |
| } |
| |
| GenericEnum(const NodePtr& schema, const std::string& symbol) : |
| GenericContainer(AVRO_ENUM, schema), value_(index(schema, symbol)) { |
| } |
| |
| /** |
| * Returns the symbol corresponding to the cardinal \p n. If the |
| * value for \p n is not within the limits an exception is thrown. |
| */ |
| const std::string& symbol(size_t n) { |
| if (n < schema()->names()) { |
| return schema()->nameAt(n); |
| } |
| throw Exception("Not as many symbols"); |
| } |
| |
| /** |
| * Returns the cardinal for the given symbol \c symbol. If the symbol |
| * is not defined for this enum and exception is thrown. |
| */ |
| size_t index(const std::string& symbol) const { |
| return index(schema(), symbol); |
| } |
| |
| /** |
| * Set the value for this enum corresponding to the given symbol \c symbol. |
| */ |
| size_t set(const std::string& symbol) { |
| return value_ = index(symbol); |
| } |
| |
| /** |
| * Set the value for this enum corresponding to the given cardinal \c n. |
| */ |
| void set(size_t n) { |
| if (n < schema()->names()) { |
| value_ = n; |
| return; |
| } |
| throw Exception("Not as many symbols"); |
| } |
| |
| /** |
| * Returns the cardinal for the current value of this enum. |
| */ |
| size_t value() const { |
| return value_; |
| } |
| |
| /** |
| * Returns the symbol for the current value of this enum. |
| */ |
| const std::string& symbol() const { |
| return schema()->nameAt(value_); |
| } |
| }; |
| |
| /** |
| * Generic container for Avro fixed. |
| */ |
| class AVRO_DECL GenericFixed : public GenericContainer { |
| std::vector<uint8_t> value_; |
| public: |
| /** |
| * Constructs a generic enum corresponding to the given schema \p schema, |
| * which should be of Avro type fixed. |
| */ |
| GenericFixed(const NodePtr& schema) : GenericContainer(AVRO_FIXED, schema) { |
| value_.resize(schema->fixedSize()); |
| } |
| |
| GenericFixed(const NodePtr& schema, const std::vector<uint8_t>& v) : |
| GenericContainer(AVRO_FIXED, schema), value_(v) { } |
| |
| /** |
| * Returns the contents of this fixed. |
| */ |
| const std::vector<uint8_t>& value() const { |
| return value_; |
| } |
| |
| /** |
| * Returns the reference to the contents of this fixed. |
| */ |
| std::vector<uint8_t>& value() { |
| return value_; |
| } |
| }; |
| |
| inline Type GenericDatum::type() const { |
| return (type_ == AVRO_UNION) ? |
| #if __cplusplus >= 201703L |
| std::any_cast<GenericUnion>(&value_)->datum().type() : |
| #else |
| boost::any_cast<GenericUnion>(&value_)->datum().type() : |
| #endif |
| type_; |
| } |
| |
| inline LogicalType GenericDatum::logicalType() const { |
| return logicalType_; |
| } |
| |
| template<typename T> T& GenericDatum::value() { |
| return (type_ == AVRO_UNION) ? |
| #if __cplusplus >= 201703L |
| std::any_cast<GenericUnion>(&value_)->datum().value<T>() : |
| *std::any_cast<T>(&value_); |
| #else |
| boost::any_cast<GenericUnion>(&value_)->datum().value<T>() : |
| *boost::any_cast<T>(&value_); |
| #endif |
| } |
| |
| template<typename T> const T& GenericDatum::value() const { |
| return (type_ == AVRO_UNION) ? |
| #if __cplusplus >= 201703L |
| std::any_cast<GenericUnion>(&value_)->datum().value<T>() : |
| *std::any_cast<T>(&value_); |
| #else |
| boost::any_cast<GenericUnion>(&value_)->datum().value<T>() : |
| *boost::any_cast<T>(&value_); |
| #endif |
| } |
| |
| inline size_t GenericDatum::unionBranch() const { |
| #if __cplusplus >= 201703L |
| return std::any_cast<GenericUnion>(&value_)->currentBranch(); |
| #else |
| return boost::any_cast<GenericUnion>(&value_)->currentBranch(); |
| #endif |
| } |
| |
| inline void GenericDatum::selectBranch(size_t branch) { |
| #if __cplusplus >= 201703L |
| std::any_cast<GenericUnion>(&value_)->selectBranch(branch); |
| #else |
| boost::any_cast<GenericUnion>(&value_)->selectBranch(branch); |
| #endif |
| } |
| |
| } // namespace avro |
| #endif // avro_GenericDatum_hh__ |