/* * Copyright 2015 Google Inc. All rights reserved. * * Licensed 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 FLATBUFFERS_REFLECTION_H_ #define FLATBUFFERS_REFLECTION_H_ // This is somewhat of a circular dependency because flatc (and thus this // file) is needed to generate this header in the first place. // Should normally not be a problem since it can be generated by the // previous version of flatc whenever this code needs to change. // See reflection/generate_code.sh #include "flatbuffers/reflection_generated.h" // Helper functionality for reflection. namespace flatbuffers { // ------------------------- GETTERS ------------------------- // Size of a basic type, don't use with structs. inline size_t GetTypeSize(reflection::BaseType base_type) { // This needs to correspond to the BaseType enum. static size_t sizes[] = { 0, 1, 1, 1, 1, 2, 2, 4, 4, 8, 8, 4, 8, 4, 4, 4, 4 }; return sizes[base_type]; } // Same as above, but now correctly returns the size of a struct if // the field (or vector element) is a struct. inline size_t GetTypeSizeInline(reflection::BaseType base_type, int type_index, const reflection::Schema &schema) { if (base_type == reflection::Obj && schema.objects()->Get(type_index)->is_struct()) { return schema.objects()->Get(type_index)->bytesize(); } else { return GetTypeSize(base_type); } } // Get the root, regardless of what type it is. inline Table *GetAnyRoot(uint8_t *flatbuf) { return GetMutableRoot(flatbuf); } inline const Table *GetAnyRoot(const uint8_t *flatbuf) { return GetRoot
(flatbuf); } // Get a field, if you know it's an integer, and its exact type. template T GetFieldI(const Table &table, const reflection::Field &field) { assert(sizeof(T) == GetTypeSize(field.type()->base_type())); return table.GetField(field.offset(), static_cast(field.default_integer())); } // Get a field, if you know it's floating point and its exact type. template T GetFieldF(const Table &table, const reflection::Field &field) { assert(sizeof(T) == GetTypeSize(field.type()->base_type())); return table.GetField(field.offset(), static_cast(field.default_real())); } // Get a field, if you know it's a string. inline const String *GetFieldS(const Table &table, const reflection::Field &field) { assert(field.type()->base_type() == reflection::String); return table.GetPointer(field.offset()); } // Get a field, if you know it's a vector. template Vector *GetFieldV(const Table &table, const reflection::Field &field) { assert(field.type()->base_type() == reflection::Vector && sizeof(T) == GetTypeSize(field.type()->element())); return table.GetPointer *>(field.offset()); } // Get a field, if you know it's a vector, generically. // To actually access elements, use the return value together with // field.type()->element() in any of GetAnyVectorElemI below etc. inline VectorOfAny *GetFieldAnyV(const Table &table, const reflection::Field &field) { return table.GetPointer(field.offset()); } // Get a field, if you know it's a table. inline Table *GetFieldT(const Table &table, const reflection::Field &field) { assert(field.type()->base_type() == reflection::Obj || field.type()->base_type() == reflection::Union); return table.GetPointer
(field.offset()); } // Get a field, if you know it's a struct. inline const Struct *GetFieldStruct(const Table &table, const reflection::Field &field) { // TODO: This does NOT check if the field is a table or struct, but we'd need // access to the schema to check the is_struct flag. assert(field.type()->base_type() == reflection::Obj); return table.GetStruct(field.offset()); } // Get a structure's field, if you know it's a struct. inline const Struct *GetFieldStruct(const Struct &structure, const reflection::Field &field) { assert(field.type()->base_type() == reflection::Obj); return structure.GetStruct(field.offset()); } // Raw helper functions used below: get any value in memory as a 64bit int, a // double or a string. // All scalars get static_cast to an int64_t, strings use strtoull, every other // data type returns 0. int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data); // All scalars static cast to double, strings use strtod, every other data // type is 0.0. double GetAnyValueF(reflection::BaseType type, const uint8_t *data); // All scalars converted using stringstream, strings as-is, and all other // data types provide some level of debug-pretty-printing. std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data, const reflection::Schema *schema, int type_index); // Get any table field as a 64bit int, regardless of what type it is. inline int64_t GetAnyFieldI(const Table &table, const reflection::Field &field) { auto field_ptr = table.GetAddressOf(field.offset()); return field_ptr ? GetAnyValueI(field.type()->base_type(), field_ptr) : field.default_integer(); } // Get any table field as a double, regardless of what type it is. inline double GetAnyFieldF(const Table &table, const reflection::Field &field) { auto field_ptr = table.GetAddressOf(field.offset()); return field_ptr ? GetAnyValueF(field.type()->base_type(), field_ptr) : field.default_real(); } // Get any table field as a string, regardless of what type it is. // You may pass nullptr for the schema if you don't care to have fields that // are of table type pretty-printed. inline std::string GetAnyFieldS(const Table &table, const reflection::Field &field, const reflection::Schema *schema) { auto field_ptr = table.GetAddressOf(field.offset()); return field_ptr ? GetAnyValueS(field.type()->base_type(), field_ptr, schema, field.type()->index()) : ""; } // Get any struct field as a 64bit int, regardless of what type it is. inline int64_t GetAnyFieldI(const Struct &st, const reflection::Field &field) { return GetAnyValueI(field.type()->base_type(), st.GetAddressOf(field.offset())); } // Get any struct field as a double, regardless of what type it is. inline double GetAnyFieldF(const Struct &st, const reflection::Field &field) { return GetAnyValueF(field.type()->base_type(), st.GetAddressOf(field.offset())); } // Get any struct field as a string, regardless of what type it is. inline std::string GetAnyFieldS(const Struct &st, const reflection::Field &field) { return GetAnyValueS(field.type()->base_type(), st.GetAddressOf(field.offset()), nullptr, -1); } // Get any vector element as a 64bit int, regardless of what type it is. inline int64_t GetAnyVectorElemI(const VectorOfAny *vec, reflection::BaseType elem_type, size_t i) { return GetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i); } // Get any vector element as a double, regardless of what type it is. inline double GetAnyVectorElemF(const VectorOfAny *vec, reflection::BaseType elem_type, size_t i) { return GetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i); } // Get any vector element as a string, regardless of what type it is. inline std::string GetAnyVectorElemS(const VectorOfAny *vec, reflection::BaseType elem_type, size_t i) { return GetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, nullptr, -1); } // Get a vector element that's a table/string/vector from a generic vector. // Pass Table/String/VectorOfAny as template parameter. // Warning: does no typechecking. template T *GetAnyVectorElemPointer(const VectorOfAny *vec, size_t i) { auto elem_ptr = vec->Data() + sizeof(uoffset_t) * i; return (T *)(elem_ptr + ReadScalar(elem_ptr)); } // Get the inline-address of a vector element. Useful for Structs (pass Struct // as template arg), or being able to address a range of scalars in-line. // Get elem_size from GetTypeSizeInline(). // Note: little-endian data on all platforms, use EndianScalar() instead of // raw pointer access with scalars). template T *GetAnyVectorElemAddressOf(const VectorOfAny *vec, size_t i, size_t elem_size) { // C-cast to allow const conversion. return (T *)(vec->Data() + elem_size * i); } // Similarly, for elements of tables. template T *GetAnyFieldAddressOf(const Table &table, const reflection::Field &field) { return (T *)table.GetAddressOf(field.offset()); } // Similarly, for elements of structs. template T *GetAnyFieldAddressOf(const Struct &st, const reflection::Field &field) { return (T *)st.GetAddressOf(field.offset()); } // ------------------------- SETTERS ------------------------- // Set any scalar field, if you know its exact type. template bool SetField(Table *table, const reflection::Field &field, T val) { assert(sizeof(T) == GetTypeSize(field.type()->base_type())); return table->SetField(field.offset(), val); } // Raw helper functions used below: set any value in memory as a 64bit int, a // double or a string. // These work for all scalar values, but do nothing for other data types. // To set a string, see SetString below. void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val); void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val); void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val); // Set any table field as a 64bit int, regardless of type what it is. inline bool SetAnyFieldI(Table *table, const reflection::Field &field, int64_t val) { auto field_ptr = table->GetAddressOf(field.offset()); if (!field_ptr) return false; SetAnyValueI(field.type()->base_type(), field_ptr, val); return true; } // Set any table field as a double, regardless of what type it is. inline bool SetAnyFieldF(Table *table, const reflection::Field &field, double val) { auto field_ptr = table->GetAddressOf(field.offset()); if (!field_ptr) return false; SetAnyValueF(field.type()->base_type(), field_ptr, val); return true; } // Set any table field as a string, regardless of what type it is. inline bool SetAnyFieldS(Table *table, const reflection::Field &field, const char *val) { auto field_ptr = table->GetAddressOf(field.offset()); if (!field_ptr) return false; SetAnyValueS(field.type()->base_type(), field_ptr, val); return true; } // Set any struct field as a 64bit int, regardless of type what it is. inline void SetAnyFieldI(Struct *st, const reflection::Field &field, int64_t val) { SetAnyValueI(field.type()->base_type(), st->GetAddressOf(field.offset()), val); } // Set any struct field as a double, regardless of type what it is. inline void SetAnyFieldF(Struct *st, const reflection::Field &field, double val) { SetAnyValueF(field.type()->base_type(), st->GetAddressOf(field.offset()), val); } // Set any struct field as a string, regardless of type what it is. inline void SetAnyFieldS(Struct *st, const reflection::Field &field, const char *val) { SetAnyValueS(field.type()->base_type(), st->GetAddressOf(field.offset()), val); } // Set any vector element as a 64bit int, regardless of type what it is. inline void SetAnyVectorElemI(VectorOfAny *vec, reflection::BaseType elem_type, size_t i, int64_t val) { SetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); } // Set any vector element as a double, regardless of type what it is. inline void SetAnyVectorElemF(VectorOfAny *vec, reflection::BaseType elem_type, size_t i, double val) { SetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); } // Set any vector element as a string, regardless of type what it is. inline void SetAnyVectorElemS(VectorOfAny *vec, reflection::BaseType elem_type, size_t i, const char *val) { SetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); } // ------------------------- RESIZING SETTERS ------------------------- // "smart" pointer for use with resizing vectors: turns a pointer inside // a vector into a relative offset, such that it is not affected by resizes. template class pointer_inside_vector { public: pointer_inside_vector(T *ptr, std::vector &vec) : offset_(reinterpret_cast(ptr) - reinterpret_cast(vec.data())), vec_(vec) {} T *operator*() const { return reinterpret_cast( reinterpret_cast(vec_.data()) + offset_); } T *operator->() const { return operator*(); } void operator=(const pointer_inside_vector &piv); private: size_t offset_; std::vector &vec_; }; // Helper to create the above easily without specifying template args. template pointer_inside_vector piv(T *ptr, std::vector &vec) { return pointer_inside_vector(ptr, vec); } inline const char *UnionTypeFieldSuffix() { return "_type"; } // Helper to figure out the actual table type a union refers to. inline const reflection::Object &GetUnionType( const reflection::Schema &schema, const reflection::Object &parent, const reflection::Field &unionfield, const Table &table) { auto enumdef = schema.enums()->Get(unionfield.type()->index()); // TODO: this is clumsy and slow, but no other way to find it? auto type_field = parent.fields()->LookupByKey( (unionfield.name()->str() + UnionTypeFieldSuffix()).c_str()); assert(type_field); auto union_type = GetFieldI(table, *type_field); auto enumval = enumdef->values()->LookupByKey(union_type); return *enumval->object(); } // Changes the contents of a string inside a FlatBuffer. FlatBuffer must // live inside a std::vector so we can resize the buffer if needed. // "str" must live inside "flatbuf" and may be invalidated after this call. // If your FlatBuffer's root table is not the schema's root table, you should // pass in your root_table type as well. void SetString(const reflection::Schema &schema, const std::string &val, const String *str, std::vector *flatbuf, const reflection::Object *root_table = nullptr); // Resizes a flatbuffers::Vector inside a FlatBuffer. FlatBuffer must // live inside a std::vector so we can resize the buffer if needed. // "vec" must live inside "flatbuf" and may be invalidated after this call. // If your FlatBuffer's root table is not the schema's root table, you should // pass in your root_table type as well. uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize, const VectorOfAny *vec, uoffset_t num_elems, uoffset_t elem_size, std::vector *flatbuf, const reflection::Object *root_table = nullptr); #ifndef FLATBUFFERS_CPP98_STL template void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val, const Vector *vec, std::vector *flatbuf, const reflection::Object *root_table = nullptr) { auto delta_elem = static_cast(newsize) - static_cast(vec->size()); auto newelems = ResizeAnyVector(schema, newsize, reinterpret_cast(vec), vec->size(), static_cast(sizeof(T)), flatbuf, root_table); // Set new elements to "val". for (int i = 0; i < delta_elem; i++) { auto loc = newelems + i * sizeof(T); auto is_scalar = std::is_scalar::value; if (is_scalar) { WriteScalar(loc, val); } else { // struct *reinterpret_cast(loc) = val; } } } #endif // Adds any new data (in the form of a new FlatBuffer) to an existing // FlatBuffer. This can be used when any of the above methods are not // sufficient, in particular for adding new tables and new fields. // This is potentially slightly less efficient than a FlatBuffer constructed // in one piece, since the new FlatBuffer doesn't share any vtables with the // existing one. // The return value can now be set using Vector::MutateOffset or SetFieldT // below. const uint8_t *AddFlatBuffer(std::vector &flatbuf, const uint8_t *newbuf, size_t newlen); inline bool SetFieldT(Table *table, const reflection::Field &field, const uint8_t *val) { assert(sizeof(uoffset_t) == GetTypeSize(field.type()->base_type())); return table->SetPointer(field.offset(), val); } // ------------------------- COPYING ------------------------- // Generic copying of tables from a FlatBuffer into a FlatBuffer builder. // Can be used to do any kind of merging/selecting you may want to do out // of existing buffers. Also useful to reconstruct a whole buffer if the // above resizing functionality has introduced garbage in a buffer you want // to remove. // Note: this does not deal with DAGs correctly. If the table passed forms a // DAG, the copy will be a tree instead (with duplicates). Strings can be // shared however, by passing true for use_string_pooling. Offset CopyTable(FlatBufferBuilder &fbb, const reflection::Schema &schema, const reflection::Object &objectdef, const Table &table, bool use_string_pooling = false); // Verifies the provided flatbuffer using reflection. // root should point to the root type for this flatbuffer. // buf should point to the start of flatbuffer data. // length specifies the size of the flatbuffer data. bool Verify(const reflection::Schema &schema, const reflection::Object &root, const uint8_t *buf, size_t length); } // namespace flatbuffers #endif // FLATBUFFERS_REFLECTION_H_