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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd0f86b..6e8cdee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,7 @@
include/flatbuffers/reflection_generated.h
src/idl_parser.cpp
src/idl_gen_text.cpp
+ src/reflection.cpp
)
set(FlatBuffers_Compiler_SRCS
@@ -43,14 +44,9 @@
)
set(FlatBuffers_Tests_SRCS
- include/flatbuffers/flatbuffers.h
- 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
+ ${FlatBuffers_Library_SRCS}
src/idl_gen_fbs.cpp
+ src/idl_gen_general.cpp
tests/test.cpp
# file generate by running compiler on tests/monster_test.fbs
${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 01e1141..e007135 100755
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -31,7 +31,9 @@
../../tests/test.cpp \
../../src/idl_parser.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_STATIC_LIBRARIES := android_native_app_glue flatbuffers
LOCAL_ARM_MODE := arm
diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h
index c1e75bd..3bf1f0a 100644
--- a/include/flatbuffers/flatbuffers.h
+++ b/include/flatbuffers/flatbuffers.h
@@ -353,6 +353,24 @@
}
};
+// 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
// of wether it is null or not (the field is not set).
template<typename T> static inline size_t VectorLength(const Vector<T> *v) {
@@ -995,6 +1013,9 @@
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:
uint8_t data_[1];
};
@@ -1027,7 +1048,6 @@
? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p))
: nullptr;
}
-
template<typename P> P GetPointer(voffset_t field) const {
return const_cast<Table *>(this)->GetPointer<P>(field);
}
@@ -1052,6 +1072,14 @@
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_); }
bool CheckField(voffset_t field) const {
diff --git a/include/flatbuffers/reflection.h b/include/flatbuffers/reflection.h
index 9dd5ef9..cb18b14 100644
--- a/include/flatbuffers/reflection.h
+++ b/include/flatbuffers/reflection.h
@@ -17,8 +17,6 @@
#ifndef FLATBUFFERS_REFLECTION_H_
#define FLATBUFFERS_REFLECTION_H_
-#include "flatbuffers/util.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
@@ -30,12 +28,28 @@
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<Table>(flatbuf);
@@ -75,6 +89,14 @@
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.
inline Table *GetFieldT(const Table &table,
const reflection::Field &field) {
@@ -83,93 +105,124 @@
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,
const reflection::Field &field) {
-# define FLATBUFFERS_GET(C, T) \
- static_cast<int64_t>(GetField##C<T>(table, field))
- switch (field.type()->base_type()) {
- 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
+ auto field_ptr = table.GetAddressOf(field.offset());
+ return field_ptr ? GetAnyValueI(field.type()->base_type(), field_ptr)
+ : field.default_integer();
}
-// 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,
const reflection::Field &field) {
- switch (field.type()->base_type()) {
- case reflection::Float: return GetFieldF<float>(table, field);
- case reflection::Double: return GetFieldF<double>(table, field);
- 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));
- }
+ auto field_ptr = table.GetAddressOf(field.offset());
+ return field_ptr ? GetAnyValueF(field.type()->base_type(), field_ptr)
+ : field.default_real();
}
-// 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,
const reflection::Field &field,
- const reflection::Schema &schema) {
- switch (field.type()->base_type()) {
- case reflection::Float:
- case reflection::Double: return NumToString(GetAnyFieldF(table, field));
- 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));
- }
+ 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<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.
template<typename T> bool SetField(Table *table, const reflection::Field &field,
T val) {
@@ -177,52 +230,83 @@
return table->SetField(field.offset(), val);
}
-// Set any field as a 64bit int, regardless of what it is (bool/int/float/str).
-inline void SetAnyFieldI(Table *table, const reflection::Field &field,
+// 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) {
-# define FLATBUFFERS_SET(T) SetField<T>(table, field, static_cast<T>(val))
- switch (field.type()->base_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
+ auto field_ptr = table->GetAddressOf(field.offset());
+ if (!field_ptr) return false;
+ SetAnyValueI(field.type()->base_type(), field_ptr, val);
+ return true;
}
-// Set any field as a double, regardless of what it is (bool/int/float/str).
-inline void SetAnyFieldF(Table *table, const reflection::Field &field,
+// Set any table field as a double, regardless of what type it is.
+inline bool SetAnyFieldF(Table *table, const reflection::Field &field,
double val) {
- switch (field.type()->base_type()) {
- case reflection::Float: SetField<float> (table, field,
- static_cast<float>(val)); break;
- case reflection::Double: SetField<double>(table, field, val); break;
- // TODO: support strings.
- default: SetAnyFieldI(table, field, static_cast<int64_t>(val)); break;
- }
+ auto field_ptr = table->GetAddressOf(field.offset());
+ if (!field_ptr) return false;
+ SetAnyValueF(field.type()->base_type(), field_ptr, val);
+ return true;
}
-// Set any field as a string, regardless of what it is (bool/int/float/str).
-inline void SetAnyFieldS(Table *table, const reflection::Field &field,
- const char *val) {
- switch (field.type()->base_type()) {
- case reflection::Float:
- case reflection::Double: SetAnyFieldF(table, field, strtod(val, nullptr));
- // TODO: support strings.
- default: SetAnyFieldI(table, field, StringToInt(val)); break;
- }
+// 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<typename T, typename U> class pointer_inside_vector {
@@ -265,194 +349,43 @@
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
// 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.
-inline void SetString(const reflection::Schema &schema, const std::string &val,
- const String *str, std::vector<uint8_t> *flatbuf,
- 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);
-}
+void SetString(const reflection::Schema &schema, const std::string &val,
+ const String *str, std::vector<uint8_t> *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<uint8_t> *flatbuf,
+ const reflection::Object *root_table = nullptr);
+
template <typename T>
void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
const Vector<T> *vec, std::vector<uint8_t> *flatbuf,
const reflection::Object *root_table = nullptr) {
auto delta_elem = static_cast<int>(newsize) - static_cast<int>(vec->size());
- auto delta_bytes = delta_elem * static_cast<int>(sizeof(T));
- auto vec_start = reinterpret_cast<const uint8_t *>(vec) - flatbuf->data();
- auto start = static_cast<uoffset_t>(vec_start + sizeof(uoffset_t) +
- sizeof(T) * vec->size());
- if (delta_bytes) {
- if (delta_elem < 0) {
- // Clear elements we're throwing away, since some might remain in the
- // buffer.
- memset(flatbuf->data() + start + delta_elem * sizeof(T), 0,
- -delta_elem * sizeof(T));
- }
- ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
- WriteScalar(flatbuf->data() + vec_start, newsize); // Length field.
- // 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;
- }
+ auto newelems = ResizeAnyVector(schema, newsize,
+ reinterpret_cast<const VectorOfAny *>(vec),
+ vec->size(),
+ static_cast<uoffset_t>(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<T>::value;
+ if (is_scalar) {
+ WriteScalar(loc, val);
+ } else { // struct
+ *reinterpret_cast<T *>(loc) = val;
}
}
}
@@ -465,21 +398,8 @@
// existing one.
// The return value can now be set using Vector::MutateOffset or SetFieldT
// below.
-inline 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;
-}
+const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
+ const uint8_t *newbuf, size_t newlen);
inline bool SetFieldT(Table *table, const reflection::Field &field,
const uint8_t *val) {
@@ -487,6 +407,8 @@
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
@@ -495,134 +417,10 @@
// Note: this does not deal with DAGs correctly. If the table passed forms a
// DAG, the copy will be a tree instead (with duplicates).
-inline 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());
-}
-
-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()));
- }
-}
+Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
+ const reflection::Schema &schema,
+ const reflection::Object &objectdef,
+ const Table &table);
} // namespace flatbuffers
diff --git a/src/reflection.cpp b/src/reflection.cpp
new file mode 100644
index 0000000..aa1ffaf
--- /dev/null
+++ b/src/reflection.cpp
@@ -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
diff --git a/tests/test.cpp b/tests/test.cpp
index eb7c487..984e1c6 100644
--- a/tests/test.cpp
+++ b/tests/test.cpp
@@ -324,7 +324,7 @@
TEST_EQ(hp_int64, 80);
auto hp_double = flatbuffers::GetAnyFieldF(root, hp_field);
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");
// We can also modify it.