mirror of
https://github.com/google/flatbuffers.git
synced 2026-07-03 09:22:26 +00:00
Reworked reflection.h to be more general.
e.g. support generic reading/writing from structs/vectors etc. Change-Id: I2eb6e24db088a72da444d5c8df7e506e53d5bc2d Tested: on Linux. Bug: 22660837
This commit is contained in:
@@ -25,6 +25,7 @@ set(FlatBuffers_Library_SRCS
|
|||||||
include/flatbuffers/reflection_generated.h
|
include/flatbuffers/reflection_generated.h
|
||||||
src/idl_parser.cpp
|
src/idl_parser.cpp
|
||||||
src/idl_gen_text.cpp
|
src/idl_gen_text.cpp
|
||||||
|
src/reflection.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(FlatBuffers_Compiler_SRCS
|
set(FlatBuffers_Compiler_SRCS
|
||||||
@@ -43,14 +44,9 @@ set(FlatHash_SRCS
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(FlatBuffers_Tests_SRCS
|
set(FlatBuffers_Tests_SRCS
|
||||||
include/flatbuffers/flatbuffers.h
|
${FlatBuffers_Library_SRCS}
|
||||||
include/flatbuffers/hash.h
|
|
||||||
include/flatbuffers/idl.h
|
|
||||||
include/flatbuffers/util.h
|
|
||||||
src/idl_parser.cpp
|
|
||||||
src/idl_gen_general.cpp
|
|
||||||
src/idl_gen_text.cpp
|
|
||||||
src/idl_gen_fbs.cpp
|
src/idl_gen_fbs.cpp
|
||||||
|
src/idl_gen_general.cpp
|
||||||
tests/test.cpp
|
tests/test.cpp
|
||||||
# file generate by running compiler on tests/monster_test.fbs
|
# file generate by running compiler on tests/monster_test.fbs
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h
|
${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ LOCAL_SRC_FILES := main.cpp \
|
|||||||
../../tests/test.cpp \
|
../../tests/test.cpp \
|
||||||
../../src/idl_parser.cpp \
|
../../src/idl_parser.cpp \
|
||||||
../../src/idl_gen_text.cpp \
|
../../src/idl_gen_text.cpp \
|
||||||
../../src/idl_gen_fbs.cpp
|
../../src/idl_gen_fbs.cpp \
|
||||||
|
../../src/idl_gen_general.cpp \
|
||||||
|
../../src/reflection.cpp
|
||||||
LOCAL_LDLIBS := -llog -landroid
|
LOCAL_LDLIBS := -llog -landroid
|
||||||
LOCAL_STATIC_LIBRARIES := android_native_app_glue flatbuffers
|
LOCAL_STATIC_LIBRARIES := android_native_app_glue flatbuffers
|
||||||
LOCAL_ARM_MODE := arm
|
LOCAL_ARM_MODE := arm
|
||||||
|
|||||||
@@ -353,6 +353,24 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Represent a vector much like the template above, but in this case we
|
||||||
|
// don't know what the element types are (used with reflection.h).
|
||||||
|
class VectorOfAny {
|
||||||
|
public:
|
||||||
|
uoffset_t size() const { return EndianScalar(length_); }
|
||||||
|
|
||||||
|
const uint8_t *Data() const {
|
||||||
|
return reinterpret_cast<const uint8_t *>(&length_ + 1);
|
||||||
|
}
|
||||||
|
uint8_t *Data() {
|
||||||
|
return reinterpret_cast<uint8_t *>(&length_ + 1);
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
VectorOfAny();
|
||||||
|
|
||||||
|
uoffset_t length_;
|
||||||
|
};
|
||||||
|
|
||||||
// Convenient helper function to get the length of any vector, regardless
|
// Convenient helper function to get the length of any vector, regardless
|
||||||
// of wether it is null or not (the field is not set).
|
// of wether it is null or not (the field is not set).
|
||||||
template<typename T> static inline size_t VectorLength(const Vector<T> *v) {
|
template<typename T> static inline size_t VectorLength(const Vector<T> *v) {
|
||||||
@@ -995,6 +1013,9 @@ class Struct FLATBUFFERS_FINAL_CLASS {
|
|||||||
return reinterpret_cast<T>(&data_[o]);
|
return reinterpret_cast<T>(&data_[o]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint8_t *GetAddressOf(uoffset_t o) const { return &data_[o]; }
|
||||||
|
uint8_t *GetAddressOf(uoffset_t o) { return &data_[o]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t data_[1];
|
uint8_t data_[1];
|
||||||
};
|
};
|
||||||
@@ -1027,7 +1048,6 @@ class Table {
|
|||||||
? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p))
|
? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p))
|
||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename P> P GetPointer(voffset_t field) const {
|
template<typename P> P GetPointer(voffset_t field) const {
|
||||||
return const_cast<Table *>(this)->GetPointer<P>(field);
|
return const_cast<Table *>(this)->GetPointer<P>(field);
|
||||||
}
|
}
|
||||||
@@ -1052,6 +1072,14 @@ class Table {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t *GetAddressOf(voffset_t field) {
|
||||||
|
auto field_offset = GetOptionalFieldOffset(field);
|
||||||
|
return field_offset ? data_ + field_offset : nullptr;
|
||||||
|
}
|
||||||
|
const uint8_t *GetAddressOf(voffset_t field) const {
|
||||||
|
return const_cast<Table *>(this)->GetAddressOf(field);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t *GetVTable() { return data_ - ReadScalar<soffset_t>(data_); }
|
uint8_t *GetVTable() { return data_ - ReadScalar<soffset_t>(data_); }
|
||||||
|
|
||||||
bool CheckField(voffset_t field) const {
|
bool CheckField(voffset_t field) const {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
#ifndef FLATBUFFERS_REFLECTION_H_
|
#ifndef FLATBUFFERS_REFLECTION_H_
|
||||||
#define FLATBUFFERS_REFLECTION_H_
|
#define FLATBUFFERS_REFLECTION_H_
|
||||||
|
|
||||||
#include "flatbuffers/util.h"
|
|
||||||
|
|
||||||
// This is somewhat of a circular dependency because flatc (and thus this
|
// This is somewhat of a circular dependency because flatc (and thus this
|
||||||
// file) is needed to generate this header in the first place.
|
// file) is needed to generate this header in the first place.
|
||||||
// Should normally not be a problem since it can be generated by the
|
// Should normally not be a problem since it can be generated by the
|
||||||
@@ -30,12 +28,28 @@
|
|||||||
|
|
||||||
namespace flatbuffers {
|
namespace flatbuffers {
|
||||||
|
|
||||||
|
// ------------------------- GETTERS -------------------------
|
||||||
|
|
||||||
|
// Size of a basic type, don't use with structs.
|
||||||
inline size_t GetTypeSize(reflection::BaseType base_type) {
|
inline size_t GetTypeSize(reflection::BaseType base_type) {
|
||||||
// This needs to correspond to the BaseType enum.
|
// 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 };
|
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];
|
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.
|
// Get the root, regardless of what type it is.
|
||||||
inline Table *GetAnyRoot(uint8_t *flatbuf) {
|
inline Table *GetAnyRoot(uint8_t *flatbuf) {
|
||||||
return GetMutableRoot<Table>(flatbuf);
|
return GetMutableRoot<Table>(flatbuf);
|
||||||
@@ -75,6 +89,14 @@ template<typename T> Vector<T> *GetFieldV(const Table &table,
|
|||||||
return table.GetPointer<Vector<T> *>(field.offset());
|
return table.GetPointer<Vector<T> *>(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<VectorOfAny *>(field.offset());
|
||||||
|
}
|
||||||
|
|
||||||
// Get a field, if you know it's a table.
|
// Get a field, if you know it's a table.
|
||||||
inline Table *GetFieldT(const Table &table,
|
inline Table *GetFieldT(const Table &table,
|
||||||
const reflection::Field &field) {
|
const reflection::Field &field) {
|
||||||
@@ -83,93 +105,124 @@ inline Table *GetFieldT(const Table &table,
|
|||||||
return table.GetPointer<Table *>(field.offset());
|
return table.GetPointer<Table *>(field.offset());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get any field as a 64bit int, regardless of what it is (bool/int/float/str).
|
// 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,
|
inline int64_t GetAnyFieldI(const Table &table,
|
||||||
const reflection::Field &field) {
|
const reflection::Field &field) {
|
||||||
# define FLATBUFFERS_GET(C, T) \
|
auto field_ptr = table.GetAddressOf(field.offset());
|
||||||
static_cast<int64_t>(GetField##C<T>(table, field))
|
return field_ptr ? GetAnyValueI(field.type()->base_type(), field_ptr)
|
||||||
switch (field.type()->base_type()) {
|
: field.default_integer();
|
||||||
case reflection::UType:
|
|
||||||
case reflection::Bool:
|
|
||||||
case reflection::UByte: return FLATBUFFERS_GET(I, uint8_t);
|
|
||||||
case reflection::Byte: return FLATBUFFERS_GET(I, int8_t);
|
|
||||||
case reflection::Short: return FLATBUFFERS_GET(I, int16_t);
|
|
||||||
case reflection::UShort: return FLATBUFFERS_GET(I, uint16_t);
|
|
||||||
case reflection::Int: return FLATBUFFERS_GET(I, int32_t);
|
|
||||||
case reflection::UInt: return FLATBUFFERS_GET(I, uint32_t);
|
|
||||||
case reflection::Long: return FLATBUFFERS_GET(I, int64_t);
|
|
||||||
case reflection::ULong: return FLATBUFFERS_GET(I, uint64_t);
|
|
||||||
case reflection::Float: return FLATBUFFERS_GET(F, float);
|
|
||||||
case reflection::Double: return FLATBUFFERS_GET(F, double);
|
|
||||||
case reflection::String: {
|
|
||||||
auto s = GetFieldS(table, field);
|
|
||||||
return s ? StringToInt(s->c_str()) : 0;
|
|
||||||
}
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
# undef FLATBUFFERS_GET
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get any field as a double, regardless of what it is (bool/int/float/str).
|
// Get any table field as a double, regardless of what type it is.
|
||||||
inline double GetAnyFieldF(const Table &table,
|
inline double GetAnyFieldF(const Table &table,
|
||||||
const reflection::Field &field) {
|
const reflection::Field &field) {
|
||||||
switch (field.type()->base_type()) {
|
auto field_ptr = table.GetAddressOf(field.offset());
|
||||||
case reflection::Float: return GetFieldF<float>(table, field);
|
return field_ptr ? GetAnyValueF(field.type()->base_type(), field_ptr)
|
||||||
case reflection::Double: return GetFieldF<double>(table, field);
|
: field.default_real();
|
||||||
case reflection::String: {
|
|
||||||
auto s = GetFieldS(table, field);
|
|
||||||
return s ? strtod(s->c_str(), nullptr) : 0.0;
|
|
||||||
}
|
|
||||||
default: return static_cast<double>(GetAnyFieldI(table, field));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get any field as a string, regardless of what it is (bool/int/float/str).
|
|
||||||
|
// 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,
|
inline std::string GetAnyFieldS(const Table &table,
|
||||||
const reflection::Field &field,
|
const reflection::Field &field,
|
||||||
const reflection::Schema &schema) {
|
const reflection::Schema *schema) {
|
||||||
switch (field.type()->base_type()) {
|
auto field_ptr = table.GetAddressOf(field.offset());
|
||||||
case reflection::Float:
|
return field_ptr ? GetAnyValueS(field.type()->base_type(), field_ptr, schema,
|
||||||
case reflection::Double: return NumToString(GetAnyFieldF(table, field));
|
field.type()->index())
|
||||||
case reflection::String: {
|
: "";
|
||||||
auto s = GetFieldS(table, field);
|
|
||||||
return s ? s->c_str() : "";
|
|
||||||
}
|
|
||||||
case reflection::Obj: {
|
|
||||||
// Convert the table to a string. This is mostly for debugging purposes,
|
|
||||||
// and does NOT promise to be JSON compliant.
|
|
||||||
// Also prefixes the type.
|
|
||||||
auto &objectdef = *schema.objects()->Get(field.type()->index());
|
|
||||||
auto s = objectdef.name()->str();
|
|
||||||
if (objectdef.is_struct()) {
|
|
||||||
s += "(struct)"; // TODO: implement this as well.
|
|
||||||
} else {
|
|
||||||
auto table_field = GetFieldT(table, field);
|
|
||||||
s += " { ";
|
|
||||||
auto fielddefs = objectdef.fields();
|
|
||||||
for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
|
|
||||||
auto &fielddef = **it;
|
|
||||||
if (!table.CheckField(fielddef.offset())) continue;
|
|
||||||
auto val = GetAnyFieldS(*table_field, fielddef, schema);
|
|
||||||
if (fielddef.type()->base_type() == reflection::String)
|
|
||||||
val = "\"" + val + "\""; // Doesn't deal with escape codes etc.
|
|
||||||
s += fielddef.name()->str();
|
|
||||||
s += ": ";
|
|
||||||
s += val;
|
|
||||||
s += ", ";
|
|
||||||
}
|
|
||||||
s += "}";
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
case reflection::Vector:
|
|
||||||
return "[(elements)]"; // TODO: implement this as well.
|
|
||||||
case reflection::Union:
|
|
||||||
return "(union)"; // TODO: implement this as well.
|
|
||||||
default: return NumToString(GetAnyFieldI(table, field));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<typename T> T *GetAnyVectorElemPointer(const VectorOfAny *vec,
|
||||||
|
size_t i) {
|
||||||
|
auto elem_ptr = vec->Data() + sizeof(uoffset_t) * i;
|
||||||
|
return (T *)(elem_ptr + ReadScalar<uoffset_t>(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<typename T> 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<typename T> T *GetAnyFieldAddressOf(const Table &table,
|
||||||
|
const reflection::Field &field) {
|
||||||
|
return (T *)table.GetAddressOf(field.offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similarly, for elements of structs.
|
||||||
|
template<typename T> 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.
|
// Set any scalar field, if you know its exact type.
|
||||||
template<typename T> bool SetField(Table *table, const reflection::Field &field,
|
template<typename T> bool SetField(Table *table, const reflection::Field &field,
|
||||||
T val) {
|
T val) {
|
||||||
@@ -177,52 +230,83 @@ template<typename T> bool SetField(Table *table, const reflection::Field &field,
|
|||||||
return table->SetField(field.offset(), val);
|
return table->SetField(field.offset(), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set any field as a 64bit int, regardless of what it is (bool/int/float/str).
|
// Raw helper functions used below: set any value in memory as a 64bit int, a
|
||||||
inline void SetAnyFieldI(Table *table, const reflection::Field &field,
|
// 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) {
|
int64_t val) {
|
||||||
# define FLATBUFFERS_SET(T) SetField<T>(table, field, static_cast<T>(val))
|
auto field_ptr = table->GetAddressOf(field.offset());
|
||||||
switch (field.type()->base_type()) {
|
if (!field_ptr) return false;
|
||||||
case reflection::UType:
|
SetAnyValueI(field.type()->base_type(), field_ptr, val);
|
||||||
case reflection::Bool:
|
return true;
|
||||||
case reflection::UByte: FLATBUFFERS_SET(uint8_t ); break;
|
|
||||||
case reflection::Byte: FLATBUFFERS_SET(int8_t ); break;
|
|
||||||
case reflection::Short: FLATBUFFERS_SET(int16_t ); break;
|
|
||||||
case reflection::UShort: FLATBUFFERS_SET(uint16_t ); break;
|
|
||||||
case reflection::Int: FLATBUFFERS_SET(int32_t ); break;
|
|
||||||
case reflection::UInt: FLATBUFFERS_SET(uint32_t ); break;
|
|
||||||
case reflection::Long: FLATBUFFERS_SET(int64_t ); break;
|
|
||||||
case reflection::ULong: FLATBUFFERS_SET(uint64_t ); break;
|
|
||||||
case reflection::Float: FLATBUFFERS_SET(float ); break;
|
|
||||||
case reflection::Double: FLATBUFFERS_SET(double ); break;
|
|
||||||
// TODO: support strings
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
# undef FLATBUFFERS_SET
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set any field as a double, regardless of what it is (bool/int/float/str).
|
// Set any table field as a double, regardless of what type it is.
|
||||||
inline void SetAnyFieldF(Table *table, const reflection::Field &field,
|
inline bool SetAnyFieldF(Table *table, const reflection::Field &field,
|
||||||
double val) {
|
double val) {
|
||||||
switch (field.type()->base_type()) {
|
auto field_ptr = table->GetAddressOf(field.offset());
|
||||||
case reflection::Float: SetField<float> (table, field,
|
if (!field_ptr) return false;
|
||||||
static_cast<float>(val)); break;
|
SetAnyValueF(field.type()->base_type(), field_ptr, val);
|
||||||
case reflection::Double: SetField<double>(table, field, val); break;
|
return true;
|
||||||
// TODO: support strings.
|
|
||||||
default: SetAnyFieldI(table, field, static_cast<int64_t>(val)); break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set any field as a string, regardless of what it is (bool/int/float/str).
|
// Set any table field as a string, regardless of what type it is.
|
||||||
inline void SetAnyFieldS(Table *table, const reflection::Field &field,
|
inline bool SetAnyFieldS(Table *table, const reflection::Field &field,
|
||||||
const char *val) {
|
const char *val) {
|
||||||
switch (field.type()->base_type()) {
|
auto field_ptr = table->GetAddressOf(field.offset());
|
||||||
case reflection::Float:
|
if (!field_ptr) return false;
|
||||||
case reflection::Double: SetAnyFieldF(table, field, strtod(val, nullptr));
|
SetAnyValueS(field.type()->base_type(), field_ptr, val);
|
||||||
// TODO: support strings.
|
return true;
|
||||||
default: SetAnyFieldI(table, field, StringToInt(val)); break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// "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.
|
// a vector into a relative offset, such that it is not affected by resizes.
|
||||||
template<typename T, typename U> class pointer_inside_vector {
|
template<typename T, typename U> class pointer_inside_vector {
|
||||||
@@ -265,194 +349,43 @@ inline const reflection::Object &GetUnionType(
|
|||||||
return *enumval->object();
|
return *enumval->object();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize a FlatBuffer in-place by iterating through all offsets in the buffer
|
|
||||||
// and adjusting them by "delta" if they straddle the start offset.
|
|
||||||
// Once that is done, bytes can now be inserted/deleted safely.
|
|
||||||
// "delta" may be negative (shrinking).
|
|
||||||
// Unless "delta" is a multiple of the largest alignment, you'll create a small
|
|
||||||
// amount of garbage space in the buffer (usually 0..7 bytes).
|
|
||||||
// If your FlatBuffer's root table is not the schema's root table, you should
|
|
||||||
// pass in your root_table type as well.
|
|
||||||
class ResizeContext {
|
|
||||||
public:
|
|
||||||
ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta,
|
|
||||||
std::vector<uint8_t> *flatbuf,
|
|
||||||
const reflection::Object *root_table = nullptr)
|
|
||||||
: schema_(schema), startptr_(flatbuf->data() + start),
|
|
||||||
delta_(delta), buf_(*flatbuf),
|
|
||||||
dag_check_(flatbuf->size() / sizeof(uoffset_t), false) {
|
|
||||||
auto mask = static_cast<int>(sizeof(largest_scalar_t) - 1);
|
|
||||||
delta_ = (delta_ + mask) & ~mask;
|
|
||||||
if (!delta_) return; // We can't shrink by less than largest_scalar_t.
|
|
||||||
// Now change all the offsets by delta_.
|
|
||||||
auto root = GetAnyRoot(buf_.data());
|
|
||||||
Straddle<uoffset_t, 1>(buf_.data(), root, buf_.data());
|
|
||||||
ResizeTable(root_table ? *root_table : *schema.root_table(), root);
|
|
||||||
// We can now add or remove bytes at start.
|
|
||||||
if (delta_ > 0) buf_.insert(buf_.begin() + start, delta_, 0);
|
|
||||||
else buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the range between first (lower address) and second straddles
|
|
||||||
// the insertion point. If it does, change the offset at offsetloc (of
|
|
||||||
// type T, with direction D).
|
|
||||||
template<typename T, int D> void Straddle(void *first, void *second,
|
|
||||||
void *offsetloc) {
|
|
||||||
if (first <= startptr_ && second >= startptr_) {
|
|
||||||
WriteScalar<T>(offsetloc, ReadScalar<T>(offsetloc) + delta_ * D);
|
|
||||||
DagCheck(offsetloc) = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This returns a boolean that records if the corresponding offset location
|
|
||||||
// has been modified already. If so, we can't even read the corresponding
|
|
||||||
// offset, since it is pointing to a location that is illegal until the
|
|
||||||
// resize actually happens.
|
|
||||||
// This must be checked for every offset, since we can't know which offsets
|
|
||||||
// will straddle and which won't.
|
|
||||||
uint8_t &DagCheck(void *offsetloc) {
|
|
||||||
auto dag_idx = reinterpret_cast<uoffset_t *>(offsetloc) -
|
|
||||||
reinterpret_cast<uoffset_t *>(buf_.data());
|
|
||||||
return dag_check_[dag_idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResizeTable(const reflection::Object &objectdef, Table *table) {
|
|
||||||
if (DagCheck(table))
|
|
||||||
return; // Table already visited.
|
|
||||||
auto vtable = table->GetVTable();
|
|
||||||
// Check if the vtable offset points beyond the insertion point.
|
|
||||||
Straddle<soffset_t, -1>(table, vtable, table);
|
|
||||||
// This direction shouldn't happen because vtables that sit before tables
|
|
||||||
// are always directly adjacent, but check just in case we ever change the
|
|
||||||
// way flatbuffers are built.
|
|
||||||
Straddle<soffset_t, -1>(vtable, table, table);
|
|
||||||
// Early out: since all fields inside the table must point forwards in
|
|
||||||
// memory, if the insertion point is before the table we can stop here.
|
|
||||||
auto tableloc = reinterpret_cast<uint8_t *>(table);
|
|
||||||
if (startptr_ <= tableloc) return;
|
|
||||||
// Check each field.
|
|
||||||
auto fielddefs = objectdef.fields();
|
|
||||||
for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
|
|
||||||
auto &fielddef = **it;
|
|
||||||
auto base_type = fielddef.type()->base_type();
|
|
||||||
// Ignore scalars.
|
|
||||||
if (base_type <= reflection::Double) continue;
|
|
||||||
// Ignore fields that are not stored.
|
|
||||||
auto offset = table->GetOptionalFieldOffset(fielddef.offset());
|
|
||||||
if (!offset) continue;
|
|
||||||
// Ignore structs.
|
|
||||||
auto subobjectdef = base_type == reflection::Obj ?
|
|
||||||
schema_.objects()->Get(fielddef.type()->index()) : nullptr;
|
|
||||||
if (subobjectdef && subobjectdef->is_struct()) continue;
|
|
||||||
// Get this fields' offset, and read it if safe.
|
|
||||||
auto offsetloc = tableloc + offset;
|
|
||||||
if (DagCheck(offsetloc))
|
|
||||||
continue; // This offset already visited.
|
|
||||||
auto ref = offsetloc + ReadScalar<uoffset_t>(offsetloc);
|
|
||||||
Straddle<uoffset_t, 1>(offsetloc, ref, offsetloc);
|
|
||||||
// Recurse.
|
|
||||||
switch (base_type) {
|
|
||||||
case reflection::Obj: {
|
|
||||||
ResizeTable(*subobjectdef, reinterpret_cast<Table *>(ref));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case reflection::Vector: {
|
|
||||||
auto elem_type = fielddef.type()->element();
|
|
||||||
if (elem_type != reflection::Obj && elem_type != reflection::String)
|
|
||||||
break;
|
|
||||||
auto vec = reinterpret_cast<Vector<uoffset_t> *>(ref);
|
|
||||||
auto elemobjectdef = elem_type == reflection::Obj
|
|
||||||
? schema_.objects()->Get(fielddef.type()->index())
|
|
||||||
: nullptr;
|
|
||||||
if (elemobjectdef && elemobjectdef->is_struct()) break;
|
|
||||||
for (uoffset_t i = 0; i < vec->size(); i++) {
|
|
||||||
auto loc = vec->Data() + i * sizeof(uoffset_t);
|
|
||||||
if (DagCheck(loc))
|
|
||||||
continue; // This offset already visited.
|
|
||||||
auto dest = loc + vec->Get(i);
|
|
||||||
Straddle<uoffset_t, 1>(loc, dest ,loc);
|
|
||||||
if (elemobjectdef)
|
|
||||||
ResizeTable(*elemobjectdef, reinterpret_cast<Table *>(dest));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case reflection::Union: {
|
|
||||||
ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table),
|
|
||||||
reinterpret_cast<Table *>(ref));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case reflection::String:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator=(const ResizeContext &rc);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const reflection::Schema &schema_;
|
|
||||||
uint8_t *startptr_;
|
|
||||||
int delta_;
|
|
||||||
std::vector<uint8_t> &buf_;
|
|
||||||
std::vector<uint8_t> dag_check_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Changes the contents of a string inside a FlatBuffer. FlatBuffer must
|
// Changes the contents of a string inside a FlatBuffer. FlatBuffer must
|
||||||
// live inside a std::vector so we can resize the buffer if needed.
|
// 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.
|
// "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
|
// If your FlatBuffer's root table is not the schema's root table, you should
|
||||||
// pass in your root_table type as well.
|
// pass in your root_table type as well.
|
||||||
inline void SetString(const reflection::Schema &schema, const std::string &val,
|
void SetString(const reflection::Schema &schema, const std::string &val,
|
||||||
const String *str, std::vector<uint8_t> *flatbuf,
|
const String *str, std::vector<uint8_t> *flatbuf,
|
||||||
const reflection::Object *root_table = nullptr) {
|
const reflection::Object *root_table = nullptr);
|
||||||
auto delta = static_cast<int>(val.size()) - static_cast<int>(str->Length());
|
|
||||||
auto start = static_cast<uoffset_t>(reinterpret_cast<const uint8_t *>(str) -
|
|
||||||
flatbuf->data() +
|
|
||||||
sizeof(uoffset_t));
|
|
||||||
if (delta) {
|
|
||||||
// Clear the old string, since we don't want parts of it remaining.
|
|
||||||
memset(flatbuf->data() + start, 0, str->Length());
|
|
||||||
// Different size, we must expand (or contract).
|
|
||||||
ResizeContext(schema, start, delta, flatbuf, root_table);
|
|
||||||
}
|
|
||||||
// Copy new data. Safe because we created the right amount of space.
|
|
||||||
memcpy(flatbuf->data() + start, val.c_str(), val.size() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resizes a flatbuffers::Vector inside a FlatBuffer. FlatBuffer must
|
// Resizes a flatbuffers::Vector inside a FlatBuffer. FlatBuffer must
|
||||||
// live inside a std::vector so we can resize the buffer if needed.
|
// 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.
|
// "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
|
// If your FlatBuffer's root table is not the schema's root table, you should
|
||||||
// pass in your root_table type as well.
|
// 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<uint8_t> *flatbuf,
|
||||||
|
const reflection::Object *root_table = nullptr);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
|
void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
|
||||||
const Vector<T> *vec, std::vector<uint8_t> *flatbuf,
|
const Vector<T> *vec, std::vector<uint8_t> *flatbuf,
|
||||||
const reflection::Object *root_table = nullptr) {
|
const reflection::Object *root_table = nullptr) {
|
||||||
auto delta_elem = static_cast<int>(newsize) - static_cast<int>(vec->size());
|
auto delta_elem = static_cast<int>(newsize) - static_cast<int>(vec->size());
|
||||||
auto delta_bytes = delta_elem * static_cast<int>(sizeof(T));
|
auto newelems = ResizeAnyVector(schema, newsize,
|
||||||
auto vec_start = reinterpret_cast<const uint8_t *>(vec) - flatbuf->data();
|
reinterpret_cast<const VectorOfAny *>(vec),
|
||||||
auto start = static_cast<uoffset_t>(vec_start + sizeof(uoffset_t) +
|
vec->size(),
|
||||||
sizeof(T) * vec->size());
|
static_cast<uoffset_t>(sizeof(T)), flatbuf,
|
||||||
if (delta_bytes) {
|
root_table);
|
||||||
if (delta_elem < 0) {
|
// Set new elements to "val".
|
||||||
// Clear elements we're throwing away, since some might remain in the
|
for (int i = 0; i < delta_elem; i++) {
|
||||||
// buffer.
|
auto loc = newelems + i * sizeof(T);
|
||||||
memset(flatbuf->data() + start + delta_elem * sizeof(T), 0,
|
auto is_scalar = std::is_scalar<T>::value;
|
||||||
-delta_elem * sizeof(T));
|
if (is_scalar) {
|
||||||
}
|
WriteScalar(loc, val);
|
||||||
ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
|
} else { // struct
|
||||||
WriteScalar(flatbuf->data() + vec_start, newsize); // Length field.
|
*reinterpret_cast<T *>(loc) = val;
|
||||||
// Set new elements to "val".
|
|
||||||
for (int i = 0; i < delta_elem; i++) {
|
|
||||||
auto loc = flatbuf->data() + start + i * sizeof(T);
|
|
||||||
auto is_scalar = std::is_scalar<T>::value;
|
|
||||||
if (is_scalar) {
|
|
||||||
WriteScalar(loc, val);
|
|
||||||
} else { // struct
|
|
||||||
*reinterpret_cast<T *>(loc) = val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,21 +398,8 @@ void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
|
|||||||
// existing one.
|
// existing one.
|
||||||
// The return value can now be set using Vector::MutateOffset or SetFieldT
|
// The return value can now be set using Vector::MutateOffset or SetFieldT
|
||||||
// below.
|
// below.
|
||||||
inline const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
|
const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
|
||||||
const uint8_t *newbuf, size_t newlen) {
|
const uint8_t *newbuf, size_t newlen);
|
||||||
// Align to sizeof(uoffset_t) past sizeof(largest_scalar_t) since we're
|
|
||||||
// going to chop off the root offset.
|
|
||||||
while ((flatbuf.size() & (sizeof(uoffset_t) - 1)) ||
|
|
||||||
!(flatbuf.size() & (sizeof(largest_scalar_t) - 1))) {
|
|
||||||
flatbuf.push_back(0);
|
|
||||||
}
|
|
||||||
auto insertion_point = static_cast<uoffset_t>(flatbuf.size());
|
|
||||||
// Insert the entire FlatBuffer minus the root pointer.
|
|
||||||
flatbuf.insert(flatbuf.end(), newbuf + sizeof(uoffset_t),
|
|
||||||
newbuf + newlen - sizeof(uoffset_t));
|
|
||||||
auto root_offset = ReadScalar<uoffset_t>(newbuf) - sizeof(uoffset_t);
|
|
||||||
return flatbuf.data() + insertion_point + root_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool SetFieldT(Table *table, const reflection::Field &field,
|
inline bool SetFieldT(Table *table, const reflection::Field &field,
|
||||||
const uint8_t *val) {
|
const uint8_t *val) {
|
||||||
@@ -487,6 +407,8 @@ inline bool SetFieldT(Table *table, const reflection::Field &field,
|
|||||||
return table->SetPointer(field.offset(), val);
|
return table->SetPointer(field.offset(), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------- COPYING -------------------------
|
||||||
|
|
||||||
// Generic copying of tables from a FlatBuffer into a FlatBuffer builder.
|
// 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
|
// 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
|
// of existing buffers. Also useful to reconstruct a whole buffer if the
|
||||||
@@ -495,134 +417,10 @@ inline bool SetFieldT(Table *table, const reflection::Field &field,
|
|||||||
// Note: this does not deal with DAGs correctly. If the table passed forms a
|
// Note: this does not deal with DAGs correctly. If the table passed forms a
|
||||||
// DAG, the copy will be a tree instead (with duplicates).
|
// DAG, the copy will be a tree instead (with duplicates).
|
||||||
|
|
||||||
inline void CopyInline(FlatBufferBuilder &fbb,
|
Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
|
||||||
const reflection::Field &fielddef,
|
const reflection::Schema &schema,
|
||||||
const Table &table,
|
const reflection::Object &objectdef,
|
||||||
size_t align, size_t size) {
|
const Table &table);
|
||||||
fbb.Align(align);
|
|
||||||
fbb.PushBytes(table.GetStruct<const uint8_t *>(fielddef.offset()), size);
|
|
||||||
fbb.TrackField(fielddef.offset(), fbb.GetSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
|
|
||||||
const reflection::Schema &schema,
|
|
||||||
const reflection::Object &objectdef,
|
|
||||||
const Table &table) {
|
|
||||||
// Before we can construct the table, we have to first generate any
|
|
||||||
// subobjects, and collect their offsets.
|
|
||||||
std::vector<uoffset_t> offsets;
|
|
||||||
auto fielddefs = objectdef.fields();
|
|
||||||
for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
|
|
||||||
auto &fielddef = **it;
|
|
||||||
// Skip if field is not present in the source.
|
|
||||||
if (!table.CheckField(fielddef.offset())) continue;
|
|
||||||
uoffset_t offset = 0;
|
|
||||||
switch (fielddef.type()->base_type()) {
|
|
||||||
case reflection::String: {
|
|
||||||
offset = fbb.CreateString(GetFieldS(table, fielddef)).o;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case reflection::Obj: {
|
|
||||||
auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
|
|
||||||
if (!subobjectdef.is_struct()) {
|
|
||||||
offset = CopyTable(fbb, schema, subobjectdef,
|
|
||||||
*GetFieldT(table, fielddef)).o;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case reflection::Union: {
|
|
||||||
auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table);
|
|
||||||
offset = CopyTable(fbb, schema, subobjectdef,
|
|
||||||
*GetFieldT(table, fielddef)).o;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case reflection::Vector: {
|
|
||||||
auto vec = table.GetPointer<const Vector<Offset<Table>> *>(
|
|
||||||
fielddef.offset());
|
|
||||||
auto element_base_type = fielddef.type()->element();
|
|
||||||
auto elemobjectdef = element_base_type == reflection::Obj
|
|
||||||
? schema.objects()->Get(fielddef.type()->index())
|
|
||||||
: nullptr;
|
|
||||||
switch (element_base_type) {
|
|
||||||
case reflection::String: {
|
|
||||||
std::vector<Offset<const String *>> elements(vec->size());
|
|
||||||
auto vec_s = reinterpret_cast<const Vector<Offset<String>> *>(vec);
|
|
||||||
for (uoffset_t i = 0; i < vec_s->size(); i++) {
|
|
||||||
elements[i] = fbb.CreateString(vec_s->Get(i)).o;
|
|
||||||
}
|
|
||||||
offset = fbb.CreateVector(elements).o;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case reflection::Obj: {
|
|
||||||
if (!elemobjectdef->is_struct()) {
|
|
||||||
std::vector<Offset<const Table *>> elements(vec->size());
|
|
||||||
for (uoffset_t i = 0; i < vec->size(); i++) {
|
|
||||||
elements[i] =
|
|
||||||
CopyTable(fbb, schema, *elemobjectdef, *vec->Get(i));
|
|
||||||
}
|
|
||||||
offset = fbb.CreateVector(elements).o;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// FALL-THRU:
|
|
||||||
}
|
|
||||||
default: { // Scalars and structs.
|
|
||||||
auto element_size = GetTypeSize(element_base_type);
|
|
||||||
if (elemobjectdef && elemobjectdef->is_struct())
|
|
||||||
element_size = elemobjectdef->bytesize();
|
|
||||||
fbb.StartVector(element_size, vec->size());
|
|
||||||
fbb.PushBytes(vec->Data(), element_size * vec->size());
|
|
||||||
offset = fbb.EndVector(vec->size());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: // Scalars.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (offset) {
|
|
||||||
offsets.push_back(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Now we can build the actual table from either offsets or scalar data.
|
|
||||||
auto start = objectdef.is_struct()
|
|
||||||
? fbb.StartStruct(objectdef.minalign())
|
|
||||||
: fbb.StartTable();
|
|
||||||
size_t offset_idx = 0;
|
|
||||||
for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
|
|
||||||
auto &fielddef = **it;
|
|
||||||
if (!table.CheckField(fielddef.offset())) continue;
|
|
||||||
auto base_type = fielddef.type()->base_type();
|
|
||||||
switch (base_type) {
|
|
||||||
case reflection::Obj: {
|
|
||||||
auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
|
|
||||||
if (subobjectdef.is_struct()) {
|
|
||||||
CopyInline(fbb, fielddef, table, subobjectdef.minalign(),
|
|
||||||
subobjectdef.bytesize());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// else: FALL-THRU:
|
|
||||||
}
|
|
||||||
case reflection::Union:
|
|
||||||
case reflection::String:
|
|
||||||
case reflection::Vector:
|
|
||||||
fbb.AddOffset(fielddef.offset(), Offset<void>(offsets[offset_idx++]));
|
|
||||||
break;
|
|
||||||
default: { // Scalars.
|
|
||||||
auto size = GetTypeSize(base_type);
|
|
||||||
CopyInline(fbb, fielddef, table, size, size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(offset_idx == offsets.size());
|
|
||||||
if (objectdef.is_struct()) {
|
|
||||||
fbb.ClearOffsets();
|
|
||||||
return fbb.EndStruct();
|
|
||||||
} else {
|
|
||||||
return fbb.EndTable(start, static_cast<voffset_t>(fielddefs->size()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace flatbuffers
|
} // namespace flatbuffers
|
||||||
|
|
||||||
|
|||||||
470
src/reflection.cpp
Normal file
470
src/reflection.cpp
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "flatbuffers/reflection.h"
|
||||||
|
#include "flatbuffers/util.h"
|
||||||
|
|
||||||
|
// Helper functionality for reflection.
|
||||||
|
|
||||||
|
namespace flatbuffers {
|
||||||
|
|
||||||
|
int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) {
|
||||||
|
# define FLATBUFFERS_GET(T) static_cast<int64_t>(ReadScalar<T>(data))
|
||||||
|
switch (type) {
|
||||||
|
case reflection::UType:
|
||||||
|
case reflection::Bool:
|
||||||
|
case reflection::UByte: return FLATBUFFERS_GET(uint8_t);
|
||||||
|
case reflection::Byte: return FLATBUFFERS_GET(int8_t);
|
||||||
|
case reflection::Short: return FLATBUFFERS_GET(int16_t);
|
||||||
|
case reflection::UShort: return FLATBUFFERS_GET(uint16_t);
|
||||||
|
case reflection::Int: return FLATBUFFERS_GET(int32_t);
|
||||||
|
case reflection::UInt: return FLATBUFFERS_GET(uint32_t);
|
||||||
|
case reflection::Long: return FLATBUFFERS_GET(int64_t);
|
||||||
|
case reflection::ULong: return FLATBUFFERS_GET(uint64_t);
|
||||||
|
case reflection::Float: return FLATBUFFERS_GET(float);
|
||||||
|
case reflection::Double: return FLATBUFFERS_GET(double);
|
||||||
|
case reflection::String: {
|
||||||
|
auto s = reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) +
|
||||||
|
data);
|
||||||
|
return s ? StringToInt(s->c_str()) : 0;
|
||||||
|
}
|
||||||
|
default: return 0; // Tables & vectors do not make sense.
|
||||||
|
}
|
||||||
|
# undef FLATBUFFERS_GET
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetAnyValueF(reflection::BaseType type, const uint8_t *data) {
|
||||||
|
switch (type) {
|
||||||
|
case reflection::Float: return static_cast<double>(ReadScalar<float>(data));
|
||||||
|
case reflection::Double: return ReadScalar<double>(data);
|
||||||
|
case reflection::String: {
|
||||||
|
auto s = reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) +
|
||||||
|
data);
|
||||||
|
return s ? strtod(s->c_str(), nullptr) : 0.0;
|
||||||
|
}
|
||||||
|
default: return static_cast<double>(GetAnyValueI(type, data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data,
|
||||||
|
const reflection::Schema *schema, int type_index) {
|
||||||
|
switch (type) {
|
||||||
|
case reflection::Float:
|
||||||
|
case reflection::Double: return NumToString(GetAnyValueF(type, data));
|
||||||
|
case reflection::String: {
|
||||||
|
auto s = reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) +
|
||||||
|
data);
|
||||||
|
return s ? s->c_str() : "";
|
||||||
|
}
|
||||||
|
case reflection::Obj:
|
||||||
|
if (schema) {
|
||||||
|
// Convert the table to a string. This is mostly for debugging purposes,
|
||||||
|
// and does NOT promise to be JSON compliant.
|
||||||
|
// Also prefixes the type.
|
||||||
|
auto &objectdef = *schema->objects()->Get(type_index);
|
||||||
|
auto s = objectdef.name()->str();
|
||||||
|
if (objectdef.is_struct()) {
|
||||||
|
s += "(struct)"; // TODO: implement this as well.
|
||||||
|
} else {
|
||||||
|
auto table_field = reinterpret_cast<const Table *>(
|
||||||
|
ReadScalar<uoffset_t>(data) + data);
|
||||||
|
s += " { ";
|
||||||
|
auto fielddefs = objectdef.fields();
|
||||||
|
for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
|
||||||
|
auto &fielddef = **it;
|
||||||
|
if (!table_field->CheckField(fielddef.offset())) continue;
|
||||||
|
auto val = GetAnyFieldS(*table_field, fielddef, schema);
|
||||||
|
if (fielddef.type()->base_type() == reflection::String)
|
||||||
|
val = "\"" + val + "\""; // Doesn't deal with escape codes etc.
|
||||||
|
s += fielddef.name()->str();
|
||||||
|
s += ": ";
|
||||||
|
s += val;
|
||||||
|
s += ", ";
|
||||||
|
}
|
||||||
|
s += "}";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
} else {
|
||||||
|
return "(table)";
|
||||||
|
}
|
||||||
|
case reflection::Vector:
|
||||||
|
return "[(elements)]"; // TODO: implement this as well.
|
||||||
|
case reflection::Union:
|
||||||
|
return "(union)"; // TODO: implement this as well.
|
||||||
|
default: return NumToString(GetAnyValueI(type, data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) {
|
||||||
|
# define FLATBUFFERS_SET(T) WriteScalar(data, static_cast<T>(val))
|
||||||
|
switch (type) {
|
||||||
|
case reflection::UType:
|
||||||
|
case reflection::Bool:
|
||||||
|
case reflection::UByte: FLATBUFFERS_SET(uint8_t ); break;
|
||||||
|
case reflection::Byte: FLATBUFFERS_SET(int8_t ); break;
|
||||||
|
case reflection::Short: FLATBUFFERS_SET(int16_t ); break;
|
||||||
|
case reflection::UShort: FLATBUFFERS_SET(uint16_t); break;
|
||||||
|
case reflection::Int: FLATBUFFERS_SET(int32_t ); break;
|
||||||
|
case reflection::UInt: FLATBUFFERS_SET(uint32_t); break;
|
||||||
|
case reflection::Long: FLATBUFFERS_SET(int64_t ); break;
|
||||||
|
case reflection::ULong: FLATBUFFERS_SET(uint64_t); break;
|
||||||
|
case reflection::Float: FLATBUFFERS_SET(float ); break;
|
||||||
|
case reflection::Double: FLATBUFFERS_SET(double ); break;
|
||||||
|
// TODO: support strings
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
# undef FLATBUFFERS_SET
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val) {
|
||||||
|
switch (type) {
|
||||||
|
case reflection::Float: WriteScalar(data, static_cast<float>(val)); break;
|
||||||
|
case reflection::Double: WriteScalar(data, val); break;
|
||||||
|
// TODO: support strings.
|
||||||
|
default: SetAnyValueI(type, data, static_cast<int64_t>(val)); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val) {
|
||||||
|
switch (type) {
|
||||||
|
case reflection::Float:
|
||||||
|
case reflection::Double: SetAnyValueF(type, data, strtod(val, nullptr));
|
||||||
|
// TODO: support strings.
|
||||||
|
default: SetAnyValueI(type, data, StringToInt(val)); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize a FlatBuffer in-place by iterating through all offsets in the buffer
|
||||||
|
// and adjusting them by "delta" if they straddle the start offset.
|
||||||
|
// Once that is done, bytes can now be inserted/deleted safely.
|
||||||
|
// "delta" may be negative (shrinking).
|
||||||
|
// Unless "delta" is a multiple of the largest alignment, you'll create a small
|
||||||
|
// amount of garbage space in the buffer (usually 0..7 bytes).
|
||||||
|
// If your FlatBuffer's root table is not the schema's root table, you should
|
||||||
|
// pass in your root_table type as well.
|
||||||
|
class ResizeContext {
|
||||||
|
public:
|
||||||
|
ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta,
|
||||||
|
std::vector<uint8_t> *flatbuf,
|
||||||
|
const reflection::Object *root_table = nullptr)
|
||||||
|
: schema_(schema), startptr_(flatbuf->data() + start),
|
||||||
|
delta_(delta), buf_(*flatbuf),
|
||||||
|
dag_check_(flatbuf->size() / sizeof(uoffset_t), false) {
|
||||||
|
auto mask = static_cast<int>(sizeof(largest_scalar_t) - 1);
|
||||||
|
delta_ = (delta_ + mask) & ~mask;
|
||||||
|
if (!delta_) return; // We can't shrink by less than largest_scalar_t.
|
||||||
|
// Now change all the offsets by delta_.
|
||||||
|
auto root = GetAnyRoot(buf_.data());
|
||||||
|
Straddle<uoffset_t, 1>(buf_.data(), root, buf_.data());
|
||||||
|
ResizeTable(root_table ? *root_table : *schema.root_table(), root);
|
||||||
|
// We can now add or remove bytes at start.
|
||||||
|
if (delta_ > 0) buf_.insert(buf_.begin() + start, delta_, 0);
|
||||||
|
else buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the range between first (lower address) and second straddles
|
||||||
|
// the insertion point. If it does, change the offset at offsetloc (of
|
||||||
|
// type T, with direction D).
|
||||||
|
template<typename T, int D> void Straddle(void *first, void *second,
|
||||||
|
void *offsetloc) {
|
||||||
|
if (first <= startptr_ && second >= startptr_) {
|
||||||
|
WriteScalar<T>(offsetloc, ReadScalar<T>(offsetloc) + delta_ * D);
|
||||||
|
DagCheck(offsetloc) = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This returns a boolean that records if the corresponding offset location
|
||||||
|
// has been modified already. If so, we can't even read the corresponding
|
||||||
|
// offset, since it is pointing to a location that is illegal until the
|
||||||
|
// resize actually happens.
|
||||||
|
// This must be checked for every offset, since we can't know which offsets
|
||||||
|
// will straddle and which won't.
|
||||||
|
uint8_t &DagCheck(void *offsetloc) {
|
||||||
|
auto dag_idx = reinterpret_cast<uoffset_t *>(offsetloc) -
|
||||||
|
reinterpret_cast<uoffset_t *>(buf_.data());
|
||||||
|
return dag_check_[dag_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResizeTable(const reflection::Object &objectdef, Table *table) {
|
||||||
|
if (DagCheck(table))
|
||||||
|
return; // Table already visited.
|
||||||
|
auto vtable = table->GetVTable();
|
||||||
|
// Check if the vtable offset points beyond the insertion point.
|
||||||
|
Straddle<soffset_t, -1>(table, vtable, table);
|
||||||
|
// This direction shouldn't happen because vtables that sit before tables
|
||||||
|
// are always directly adjacent, but check just in case we ever change the
|
||||||
|
// way flatbuffers are built.
|
||||||
|
Straddle<soffset_t, -1>(vtable, table, table);
|
||||||
|
// Early out: since all fields inside the table must point forwards in
|
||||||
|
// memory, if the insertion point is before the table we can stop here.
|
||||||
|
auto tableloc = reinterpret_cast<uint8_t *>(table);
|
||||||
|
if (startptr_ <= tableloc) return;
|
||||||
|
// Check each field.
|
||||||
|
auto fielddefs = objectdef.fields();
|
||||||
|
for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
|
||||||
|
auto &fielddef = **it;
|
||||||
|
auto base_type = fielddef.type()->base_type();
|
||||||
|
// Ignore scalars.
|
||||||
|
if (base_type <= reflection::Double) continue;
|
||||||
|
// Ignore fields that are not stored.
|
||||||
|
auto offset = table->GetOptionalFieldOffset(fielddef.offset());
|
||||||
|
if (!offset) continue;
|
||||||
|
// Ignore structs.
|
||||||
|
auto subobjectdef = base_type == reflection::Obj ?
|
||||||
|
schema_.objects()->Get(fielddef.type()->index()) : nullptr;
|
||||||
|
if (subobjectdef && subobjectdef->is_struct()) continue;
|
||||||
|
// Get this fields' offset, and read it if safe.
|
||||||
|
auto offsetloc = tableloc + offset;
|
||||||
|
if (DagCheck(offsetloc))
|
||||||
|
continue; // This offset already visited.
|
||||||
|
auto ref = offsetloc + ReadScalar<uoffset_t>(offsetloc);
|
||||||
|
Straddle<uoffset_t, 1>(offsetloc, ref, offsetloc);
|
||||||
|
// Recurse.
|
||||||
|
switch (base_type) {
|
||||||
|
case reflection::Obj: {
|
||||||
|
ResizeTable(*subobjectdef, reinterpret_cast<Table *>(ref));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case reflection::Vector: {
|
||||||
|
auto elem_type = fielddef.type()->element();
|
||||||
|
if (elem_type != reflection::Obj && elem_type != reflection::String)
|
||||||
|
break;
|
||||||
|
auto vec = reinterpret_cast<Vector<uoffset_t> *>(ref);
|
||||||
|
auto elemobjectdef = elem_type == reflection::Obj
|
||||||
|
? schema_.objects()->Get(fielddef.type()->index())
|
||||||
|
: nullptr;
|
||||||
|
if (elemobjectdef && elemobjectdef->is_struct()) break;
|
||||||
|
for (uoffset_t i = 0; i < vec->size(); i++) {
|
||||||
|
auto loc = vec->Data() + i * sizeof(uoffset_t);
|
||||||
|
if (DagCheck(loc))
|
||||||
|
continue; // This offset already visited.
|
||||||
|
auto dest = loc + vec->Get(i);
|
||||||
|
Straddle<uoffset_t, 1>(loc, dest ,loc);
|
||||||
|
if (elemobjectdef)
|
||||||
|
ResizeTable(*elemobjectdef, reinterpret_cast<Table *>(dest));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case reflection::Union: {
|
||||||
|
ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table),
|
||||||
|
reinterpret_cast<Table *>(ref));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case reflection::String:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(const ResizeContext &rc);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const reflection::Schema &schema_;
|
||||||
|
uint8_t *startptr_;
|
||||||
|
int delta_;
|
||||||
|
std::vector<uint8_t> &buf_;
|
||||||
|
std::vector<uint8_t> dag_check_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetString(const reflection::Schema &schema, const std::string &val,
|
||||||
|
const String *str, std::vector<uint8_t> *flatbuf,
|
||||||
|
const reflection::Object *root_table) {
|
||||||
|
auto delta = static_cast<int>(val.size()) - static_cast<int>(str->Length());
|
||||||
|
auto start = static_cast<uoffset_t>(reinterpret_cast<const uint8_t *>(str) -
|
||||||
|
flatbuf->data() +
|
||||||
|
sizeof(uoffset_t));
|
||||||
|
if (delta) {
|
||||||
|
// Clear the old string, since we don't want parts of it remaining.
|
||||||
|
memset(flatbuf->data() + start, 0, str->Length());
|
||||||
|
// Different size, we must expand (or contract).
|
||||||
|
ResizeContext(schema, start, delta, flatbuf, root_table);
|
||||||
|
}
|
||||||
|
// Copy new data. Safe because we created the right amount of space.
|
||||||
|
memcpy(flatbuf->data() + start, val.c_str(), val.size() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize,
|
||||||
|
const VectorOfAny *vec, uoffset_t num_elems,
|
||||||
|
uoffset_t elem_size, std::vector<uint8_t> *flatbuf,
|
||||||
|
const reflection::Object *root_table) {
|
||||||
|
auto delta_elem = static_cast<int>(newsize) - static_cast<int>(num_elems);
|
||||||
|
auto delta_bytes = delta_elem * static_cast<int>(elem_size);
|
||||||
|
auto vec_start = reinterpret_cast<const uint8_t *>(vec) - flatbuf->data();
|
||||||
|
auto start = static_cast<uoffset_t>(vec_start + sizeof(uoffset_t) +
|
||||||
|
elem_size * num_elems);
|
||||||
|
if (delta_bytes) {
|
||||||
|
if (delta_elem < 0) {
|
||||||
|
// Clear elements we're throwing away, since some might remain in the
|
||||||
|
// buffer.
|
||||||
|
auto size_clear = -delta_elem * elem_size;
|
||||||
|
memset(flatbuf->data() + start - size_clear, 0, size_clear);
|
||||||
|
}
|
||||||
|
ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
|
||||||
|
WriteScalar(flatbuf->data() + vec_start, newsize); // Length field.
|
||||||
|
// Set new elements to 0.. this can be overwritten by the caller.
|
||||||
|
if (delta_elem > 0) {
|
||||||
|
memset(flatbuf->data() + start, 0, delta_elem * elem_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flatbuf->data() + start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
|
||||||
|
const uint8_t *newbuf, size_t newlen) {
|
||||||
|
// Align to sizeof(uoffset_t) past sizeof(largest_scalar_t) since we're
|
||||||
|
// going to chop off the root offset.
|
||||||
|
while ((flatbuf.size() & (sizeof(uoffset_t) - 1)) ||
|
||||||
|
!(flatbuf.size() & (sizeof(largest_scalar_t) - 1))) {
|
||||||
|
flatbuf.push_back(0);
|
||||||
|
}
|
||||||
|
auto insertion_point = static_cast<uoffset_t>(flatbuf.size());
|
||||||
|
// Insert the entire FlatBuffer minus the root pointer.
|
||||||
|
flatbuf.insert(flatbuf.end(), newbuf + sizeof(uoffset_t),
|
||||||
|
newbuf + newlen - sizeof(uoffset_t));
|
||||||
|
auto root_offset = ReadScalar<uoffset_t>(newbuf) - sizeof(uoffset_t);
|
||||||
|
return flatbuf.data() + insertion_point + root_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyInline(FlatBufferBuilder &fbb, const reflection::Field &fielddef,
|
||||||
|
const Table &table, size_t align, size_t size) {
|
||||||
|
fbb.Align(align);
|
||||||
|
fbb.PushBytes(table.GetStruct<const uint8_t *>(fielddef.offset()), size);
|
||||||
|
fbb.TrackField(fielddef.offset(), fbb.GetSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
|
||||||
|
const reflection::Schema &schema,
|
||||||
|
const reflection::Object &objectdef,
|
||||||
|
const Table &table) {
|
||||||
|
// Before we can construct the table, we have to first generate any
|
||||||
|
// subobjects, and collect their offsets.
|
||||||
|
std::vector<uoffset_t> offsets;
|
||||||
|
auto fielddefs = objectdef.fields();
|
||||||
|
for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
|
||||||
|
auto &fielddef = **it;
|
||||||
|
// Skip if field is not present in the source.
|
||||||
|
if (!table.CheckField(fielddef.offset())) continue;
|
||||||
|
uoffset_t offset = 0;
|
||||||
|
switch (fielddef.type()->base_type()) {
|
||||||
|
case reflection::String: {
|
||||||
|
offset = fbb.CreateString(GetFieldS(table, fielddef)).o;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case reflection::Obj: {
|
||||||
|
auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
|
||||||
|
if (!subobjectdef.is_struct()) {
|
||||||
|
offset = CopyTable(fbb, schema, subobjectdef,
|
||||||
|
*GetFieldT(table, fielddef)).o;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case reflection::Union: {
|
||||||
|
auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table);
|
||||||
|
offset = CopyTable(fbb, schema, subobjectdef,
|
||||||
|
*GetFieldT(table, fielddef)).o;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case reflection::Vector: {
|
||||||
|
auto vec = table.GetPointer<const Vector<Offset<Table>> *>(
|
||||||
|
fielddef.offset());
|
||||||
|
auto element_base_type = fielddef.type()->element();
|
||||||
|
auto elemobjectdef = element_base_type == reflection::Obj
|
||||||
|
? schema.objects()->Get(fielddef.type()->index())
|
||||||
|
: nullptr;
|
||||||
|
switch (element_base_type) {
|
||||||
|
case reflection::String: {
|
||||||
|
std::vector<Offset<const String *>> elements(vec->size());
|
||||||
|
auto vec_s = reinterpret_cast<const Vector<Offset<String>> *>(vec);
|
||||||
|
for (uoffset_t i = 0; i < vec_s->size(); i++) {
|
||||||
|
elements[i] = fbb.CreateString(vec_s->Get(i)).o;
|
||||||
|
}
|
||||||
|
offset = fbb.CreateVector(elements).o;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case reflection::Obj: {
|
||||||
|
if (!elemobjectdef->is_struct()) {
|
||||||
|
std::vector<Offset<const Table *>> elements(vec->size());
|
||||||
|
for (uoffset_t i = 0; i < vec->size(); i++) {
|
||||||
|
elements[i] =
|
||||||
|
CopyTable(fbb, schema, *elemobjectdef, *vec->Get(i));
|
||||||
|
}
|
||||||
|
offset = fbb.CreateVector(elements).o;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// FALL-THRU:
|
||||||
|
}
|
||||||
|
default: { // Scalars and structs.
|
||||||
|
auto element_size = GetTypeSize(element_base_type);
|
||||||
|
if (elemobjectdef && elemobjectdef->is_struct())
|
||||||
|
element_size = elemobjectdef->bytesize();
|
||||||
|
fbb.StartVector(element_size, vec->size());
|
||||||
|
fbb.PushBytes(vec->Data(), element_size * vec->size());
|
||||||
|
offset = fbb.EndVector(vec->size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // Scalars.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (offset) {
|
||||||
|
offsets.push_back(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now we can build the actual table from either offsets or scalar data.
|
||||||
|
auto start = objectdef.is_struct()
|
||||||
|
? fbb.StartStruct(objectdef.minalign())
|
||||||
|
: fbb.StartTable();
|
||||||
|
size_t offset_idx = 0;
|
||||||
|
for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
|
||||||
|
auto &fielddef = **it;
|
||||||
|
if (!table.CheckField(fielddef.offset())) continue;
|
||||||
|
auto base_type = fielddef.type()->base_type();
|
||||||
|
switch (base_type) {
|
||||||
|
case reflection::Obj: {
|
||||||
|
auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
|
||||||
|
if (subobjectdef.is_struct()) {
|
||||||
|
CopyInline(fbb, fielddef, table, subobjectdef.minalign(),
|
||||||
|
subobjectdef.bytesize());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// else: FALL-THRU:
|
||||||
|
}
|
||||||
|
case reflection::Union:
|
||||||
|
case reflection::String:
|
||||||
|
case reflection::Vector:
|
||||||
|
fbb.AddOffset(fielddef.offset(), Offset<void>(offsets[offset_idx++]));
|
||||||
|
break;
|
||||||
|
default: { // Scalars.
|
||||||
|
auto size = GetTypeSize(base_type);
|
||||||
|
CopyInline(fbb, fielddef, table, size, size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(offset_idx == offsets.size());
|
||||||
|
if (objectdef.is_struct()) {
|
||||||
|
fbb.ClearOffsets();
|
||||||
|
return fbb.EndStruct();
|
||||||
|
} else {
|
||||||
|
return fbb.EndTable(start, static_cast<voffset_t>(fielddefs->size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace flatbuffers
|
||||||
@@ -324,7 +324,7 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) {
|
|||||||
TEST_EQ(hp_int64, 80);
|
TEST_EQ(hp_int64, 80);
|
||||||
auto hp_double = flatbuffers::GetAnyFieldF(root, hp_field);
|
auto hp_double = flatbuffers::GetAnyFieldF(root, hp_field);
|
||||||
TEST_EQ(hp_double, 80.0);
|
TEST_EQ(hp_double, 80.0);
|
||||||
auto hp_string = flatbuffers::GetAnyFieldS(root, hp_field, schema);
|
auto hp_string = flatbuffers::GetAnyFieldS(root, hp_field, &schema);
|
||||||
TEST_EQ_STR(hp_string.c_str(), "80");
|
TEST_EQ_STR(hp_string.c_str(), "80");
|
||||||
|
|
||||||
// We can also modify it.
|
// We can also modify it.
|
||||||
|
|||||||
Reference in New Issue
Block a user