FlatBuffers is an efficient cross platform serialization library for C++, with support for Java, C# and Go. It was created at Google specifically for game development and other performance-critical applications.
-
It is available as open source under the Apache license, v2 (see LICENSE.txt).
+
It is available as Open Source on GitHub under the Apache license, v2 (see LICENSE.txt).
Why use FlatBuffers?
Access to serialized data without parsing/unpacking - What sets FlatBuffers apart is that it represents hierarchical data in a flat binary buffer in such a way that it can still be accessed directly without parsing/unpacking, while also still supporting data structure evolution (forwards/backwards compatibility).
-o PATH : Output all generated files to PATH (either absolute, or relative to the current directory). If omitted, PATH will be the current directory. PATH should end in your systems path separator, e.g. / or \.
-I PATH : when encountering include statements, attempt to load the files from this path. Paths will be tried in the order given, and if all fail (or none are specified) it will try to load relative to the path of the schema file being parsed.
--strict-json : Require & generate strict JSON (field names are enclosed in quotes, no trailing commas in tables/vectors). By default, no quotes are required/generated, and trailing commas are allowed.
+
--defaults-json : Output fields whose value is equal to the default value when writing JSON text.
--no-prefix : Don't prefix enum values in generated C++ by their enum type.
--gen-includes : (deprecated), instead use:
--no-includes : Don't generate include statements for included schemas the generated file depends on (C++).
--gen-mutable : Generate additional non-const accessors for mutating FlatBuffers in-place.
--raw-binary : Allow binaries without a file_indentifier to be read. This may crash flatc given a mismatched schema.
-
--proto: Expect input files to be .proto files (protocol buffers). Output the corresponding .fbs file. Currently supports: package, message, enum. Does not support, but will skip without error: import, option. Does not support, will generate error: service, extend, extensions, oneof, group, custom options, nested declarations.
+
--proto: Expect input files to be .proto files (protocol buffers). Output the corresponding .fbs file. Currently supports: package, message, enum. Does not support, but will skip without error: import, option. Does not support, will generate error: service, extend, extensions, oneof, group, custom options, nested declarations.
+
--schema: Serialize schemas instead of JSON (use with -b). This will output a binary version of the specified schema that itself corresponds to the reflection/reflection.fbs schema. Loading this binary file is the basis for reflection functionality.
We use the somewhat verbose term mutate instead of set to indicate that this is a special use case, not to be confused with the default way of constructing FlatBuffer data.
After the above mutations, you can send on the FlatBuffer to a new recipient without any further work!
Note that any mutate_ functions on tables return a bool, which is false if the field we're trying to set isn't present in the buffer. Fields are not present if they weren't set, or even if they happen to be equal to the default value. For example, in the creation code above we set the mana field to 150, which is the default value, so it was never stored in the buffer. Trying to call mutate_mana() on such data will return false, and the value won't actually be modified!
-
There's two ways around this. First, you can call ForceDefaults() on a FlatBufferBuilder to force all fields you set to actually be written. This of course increases the size of the buffer somewhat, but this may be acceptable for a mutable buffer.
-
Alternatively, you can use mutation functions that are able to insert fields and change the size of things. These functions are expensive however, since they need to resize the buffer and create new data.
+
One way to solve this is to call ForceDefaults() on a FlatBufferBuilder to force all fields you set to actually be written. This of course increases the size of the buffer somewhat, but this may be acceptable for a mutable buffer.
+
Alternatively, you can use the more powerful reflection functionality:
+
Reflection (& Resizing)
+
If the above ways of accessing a buffer are still too static for you, there is experimental support for reflection in FlatBuffers, allowing you to read and write data even if you don't know the exact format of a buffer, and even allows you to change sizes of strings and vectors in-place.
+
The way this works is very elegant, there is actually a FlatBuffer schema that describes schemas (!) which you can find in reflection/reflection.fbs. The compiler flatc can write out any schemas it has just parsed as a binary FlatBuffer, corresponding to this meta-schema.
+
Loading in one of these binary schemas at runtime allows you traverse any FlatBuffer data that corresponds to it without knowing the exact format. You can query what fields are present, and then read/write them after.
+
For convenient field manipulation, you can include the header flatbuffers/reflection.h which includes both the generated code from the meta schema, as well as a lot of helper functions.
+
And example of usage for the moment you can find in test.cpp/ReflectionTest().
Storing maps / dictionaries in a FlatBuffer
FlatBuffers doesn't support maps natively, but there is support to emulate their behavior with vectors and binary search, which means you can have fast lookups directly from a FlatBuffer without having to unpack your data into a std::map or similar.
To use it:
diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md
index 31e2045f2..36995bb9f 100755
--- a/docs/source/Compiler.md
+++ b/docs/source/Compiler.md
@@ -67,3 +67,8 @@ be generated for each file processed:
Does not support, but will skip without error: `import`, `option`.
Does not support, will generate error: `service`, `extend`, `extensions`,
`oneof`, `group`, custom options, nested declarations.
+
+- `--schema`: Serialize schemas instead of JSON (use with -b). This will
+ output a binary version of the specified schema that itself corresponds
+ to the reflection/reflection.fbs schema. Loading this binary file is the
+ basis for reflection functionality.
diff --git a/docs/source/CppUsage.md b/docs/source/CppUsage.md
index 01576e20b..0786dcbf3 100755
--- a/docs/source/CppUsage.md
+++ b/docs/source/CppUsage.md
@@ -203,14 +203,34 @@ to `150`, which is the default value, so it was never stored in the buffer.
Trying to call mutate_mana() on such data will return false, and the value won't
actually be modified!
-There's two ways around this. First, you can call `ForceDefaults()` on a
+One way to solve this is to call `ForceDefaults()` on a
`FlatBufferBuilder` to force all fields you set to actually be written. This
of course increases the size of the buffer somewhat, but this may be
acceptable for a mutable buffer.
-Alternatively, you can use mutation functions that are able to insert fields
-and change the size of things. These functions are expensive however, since
-they need to resize the buffer and create new data.
+Alternatively, you can use the more powerful reflection functionality:
+
+### Reflection (& Resizing)
+
+If the above ways of accessing a buffer are still too static for you, there is
+experimental support for reflection in FlatBuffers, allowing you to read and
+write data even if you don't know the exact format of a buffer, and even allows
+you to change sizes of strings and vectors in-place.
+
+The way this works is very elegant, there is actually a FlatBuffer schema that
+describes schemas (!) which you can find in `reflection/reflection.fbs`.
+The compiler `flatc` can write out any schemas it has just parsed as a binary
+FlatBuffer, corresponding to this meta-schema.
+
+Loading in one of these binary schemas at runtime allows you traverse any
+FlatBuffer data that corresponds to it without knowing the exact format. You
+can query what fields are present, and then read/write them after.
+
+For convenient field manipulation, you can include the header
+`flatbuffers/reflection.h` which includes both the generated code from the meta
+schema, as well as a lot of helper functions.
+
+And example of usage for the moment you can find in `test.cpp/ReflectionTest()`.
### Storing maps / dictionaries in a FlatBuffer
diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h
index 9502464a7..25cbad7bf 100644
--- a/include/flatbuffers/flatbuffers.h
+++ b/include/flatbuffers/flatbuffers.h
@@ -754,7 +754,7 @@ class FlatBufferBuilder FLATBUFFERS_FINAL_CLASS {
}
template Offset>> CreateVectorOfSortedTables(
- std::vector *v) {
+ std::vector> *v) {
return CreateVectorOfSortedTables(v->data(), v->size());
}
@@ -1017,6 +1017,8 @@ class Table {
return true;
}
+ uint8_t *GetVTable() { return data_ - ReadScalar(data_); }
+
bool CheckField(voffset_t field) const {
return GetOptionalFieldOffset(field) != 0;
}
diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h
index 9de893ee6..74d702e4f 100644
--- a/include/flatbuffers/idl.h
+++ b/include/flatbuffers/idl.h
@@ -25,6 +25,7 @@
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/hash.h"
+#include "flatbuffers/reflection.h"
// This file defines the data types representing a parsed IDL (Interface
// Definition Language) / schema file.
@@ -126,6 +127,8 @@ struct Type {
Type VectorType() const { return Type(element, struct_def, enum_def); }
+ Offset Serialize(FlatBufferBuilder *builder) const;
+
BaseType base_type;
BaseType element; // only set if t == BASE_TYPE_VECTOR
StructDef *struct_def; // only set if t or element == BASE_TYPE_STRUCT
@@ -179,7 +182,8 @@ struct Namespace {
// Base class for all definition types (fields, structs_, enums_).
struct Definition {
- Definition() : generated(false), defined_namespace(nullptr) {}
+ Definition() : generated(false), defined_namespace(nullptr),
+ serialized_location(0), index(-1) {}
std::string name;
std::string file;
@@ -187,12 +191,19 @@ struct Definition {
SymbolTable attributes;
bool generated; // did we already output code for this definition?
Namespace *defined_namespace; // Where it was defined.
+
+ // For use with Serialize()
+ uoffset_t serialized_location;
+ int index; // Inside the vector it is stored.
};
struct FieldDef : public Definition {
FieldDef() : deprecated(false), required(false), key(false), padding(0),
used(false) {}
+ Offset Serialize(FlatBufferBuilder *builder, uint16_t id)
+ const;
+
Value value;
bool deprecated; // Field is allowed to be present in old data, but can't be
// written in new data nor accessed in new code.
@@ -218,6 +229,8 @@ struct StructDef : public Definition {
if (fields.vec.size()) fields.vec.back()->padding = padding;
}
+ Offset Serialize(FlatBufferBuilder *builder) const;
+
SymbolTable fields;
bool fixed; // If it's struct, not a table.
bool predecl; // If it's used before it was defined.
@@ -243,6 +256,8 @@ struct EnumVal {
EnumVal(const std::string &_name, int64_t _val)
: name(_name), value(_val), struct_def(nullptr) {}
+ Offset Serialize(FlatBufferBuilder *builder) const;
+
std::string name;
std::vector doc_comment;
int64_t value;
@@ -263,6 +278,8 @@ struct EnumDef : public Definition {
return nullptr;
}
+ Offset Serialize(FlatBufferBuilder *builder) const;
+
SymbolTable vals;
bool is_union;
Type underlying_type;
@@ -271,7 +288,7 @@ struct EnumDef : public Definition {
class Parser {
public:
Parser(bool strict_json = false, bool proto_mode = false)
- : root_struct_def(nullptr),
+ : root_struct_def_(nullptr),
source_(nullptr),
cursor_(nullptr),
line_(1),
@@ -325,6 +342,10 @@ class Parser {
std::set GetIncludedFilesRecursive(
const std::string &file_name) const;
+ // Fills builder_ with a binary version of the schema parsed.
+ // See reflection/reflection.fbs
+ void Serialize();
+
private:
int64_t ParseHexNum(int nibbles);
void Next();
@@ -363,7 +384,7 @@ class Parser {
std::string error_; // User readable error_ if Parse() == false
FlatBufferBuilder builder_; // any data contained in the file
- StructDef *root_struct_def;
+ StructDef *root_struct_def_;
std::string file_identifier_;
std::string file_extension_;
diff --git a/include/flatbuffers/reflection.h b/include/flatbuffers/reflection.h
new file mode 100644
index 000000000..0f77c40c8
--- /dev/null
+++ b/include/flatbuffers/reflection.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLATBUFFERS_REFLECTION_H_
+#define FLATBUFFERS_REFLECTION_H_
+
+#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
+// previous version of flatc whenever this code needs to change.
+// See reflection/generate_code.sh
+#include "flatbuffers/reflection_generated.h"
+
+// Helper functionality for reflection.
+
+namespace flatbuffers {
+
+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];
+}
+
+// Get the root, regardless of what type it is.
+inline Table *GetAnyRoot(uint8_t *flatbuf) {
+ return GetMutableRoot
(flatbuf);
+}
+
+// Get a field, if you know it's an integer, and its exact type.
+template T GetFieldI(const Table *table,
+ const reflection::Field *field) {
+ assert(sizeof(T) == GetTypeSize(field->type()->base_type()));
+ return table->GetField(field->offset(), field->default_integer());
+}
+
+// Get a field, if you know it's floating point and its exact type.
+template T GetFieldF(const Table *table,
+ const reflection::Field *field) {
+ assert(sizeof(T) == GetTypeSize(field->type()->base_type()));
+ return table->GetField(field->offset(), field->default_real());
+}
+
+// Get a field, if you know it's a string.
+inline const String *GetFieldS(const Table *table,
+ const reflection::Field *field) {
+ assert(field->type()->base_type() == reflection::String);
+ return table->GetPointer(field->offset());
+}
+
+// Get a field, if you know it's a vector.
+template const Vector *GetFieldV(const Table *table,
+ const reflection::Field *field) {
+ assert(field->type()->base_type() == reflection::Vector &&
+ sizeof(T) == GetTypeSize(field->type()->element()));
+ return table->GetPointer *>(field->offset());
+}
+
+// Get any field as a 64bit int, regardless of what it is (bool/int/float/str).
+inline int64_t GetAnyFieldI(const Table *table,
+ const reflection::Field *field) {
+# define FLATBUFFERS_GET(C, T) \
+ static_cast(GetField##C(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: return StringToInt(
+ GetFieldS(table, field)->c_str());
+ default: return 0;
+ }
+# undef FLATBUFFERS_GET
+}
+
+// Get any field as a double, regardless of what it is (bool/int/float/str).
+inline double GetAnyFieldF(const Table *table,
+ const reflection::Field *field) {
+ switch (field->type()->base_type()) {
+ case reflection::Float: return GetFieldF(table, field);
+ case reflection::Double: return GetFieldF(table, field);
+ case reflection::String: return strtod(GetFieldS(table, field)->c_str(),
+ nullptr);
+ default: return GetAnyFieldI(table, field);
+ }
+}
+
+// Get any field as a string, regardless of what it is (bool/int/float/str).
+inline std::string GetAnyFieldS(const Table *table,
+ const reflection::Field *field) {
+ switch (field->type()->base_type()) {
+ case reflection::Float:
+ case reflection::Double: return NumToString(GetAnyFieldF(table, field));
+ case reflection::String: return GetFieldS(table, field)->c_str();
+ // TODO: could return vector/table etc as JSON string.
+ default: return NumToString(GetAnyFieldI(table, field));
+ }
+}
+
+// Set any scalar field, if you know its exact type.
+template bool SetField(Table *table, const reflection::Field *field,
+ T val) {
+ assert(sizeof(T) == GetTypeSize(field->type()->base_type()));
+ return table->SetField(field->offset(), val);
+}
+
+// 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,
+ int64_t val) {
+# define FLATBUFFERS_SET(T) SetField(table, field, static_cast(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
+}
+
+// Set any field as a double, regardless of what it is (bool/int/float/str).
+inline void SetAnyFieldF(Table *table, const reflection::Field *field,
+ double val) {
+ switch (field->type()->base_type()) {
+ case reflection::Float: SetField (table, field,
+ static_cast(val)); break;
+ case reflection::Double: SetField(table, field, val); break;
+ // TODO: support strings.
+ default: SetAnyFieldI(table, field, static_cast(val)); break;
+ }
+}
+
+// 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;
+ }
+}
+
+// "smart" pointer for use with resizing vectors: turns a pointer inside
+// a vector into a relative offset, such that it is not affected by resizes.
+template class pointer_inside_vector {
+ public:
+ pointer_inside_vector(const T *ptr, const std::vector &vec)
+ : offset_(reinterpret_cast(ptr) -
+ reinterpret_cast(vec.data())),
+ vec_(vec) {}
+
+ const T *operator*() const {
+ return reinterpret_cast(
+ reinterpret_cast(vec_.data()) + offset_);
+ }
+ const T *operator->() const {
+ return operator*();
+ }
+ private:
+ size_t offset_;
+ const std::vector &vec_;
+};
+
+// Helper to create the above easily without specifying template args.
+template pointer_inside_vector piv(
+ const T *ptr, const std::vector &vec) {
+ return pointer_inside_vector(ptr, vec);
+}
+
+// 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.
+class ResizeContext {
+ public:
+ ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta,
+ std::vector *flatbuf)
+ : schema_(schema), start_(start), startptr_(flatbuf->data() + start),
+ delta_(delta), buf_(*flatbuf),
+ dag_check_(flatbuf->size() / sizeof(uoffset_t), false) {
+ auto mask = 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(buf_.data(), root, buf_.data());
+ ResizeTable(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 void Straddle(void *first, void *second,
+ void *offsetloc) {
+ if (first <= startptr_ && second >= startptr_) {
+ WriteScalar(offsetloc, ReadScalar(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(offsetloc) -
+ reinterpret_cast(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(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(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(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(offsetloc);
+ Straddle(offsetloc, ref, offsetloc);
+ // Recurse.
+ switch (base_type) {
+ case reflection::Obj: {
+ ResizeTable(subobjectdef, reinterpret_cast
(ref));
+ break;
+ }
+ case reflection::Vector: {
+ if (fielddef->type()->element() != reflection::Obj) break;
+ auto vec = reinterpret_cast *>(ref);
+ auto elemobjectdef =
+ schema_.objects()->Get(fielddef->type()->index());
+ if (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(loc, dest ,loc);
+ ResizeTable(elemobjectdef, reinterpret_cast
(dest));
+ }
+ break;
+ }
+ case reflection::Union: {
+ auto enumdef = schema_.enums()->Get(fielddef->type()->index());
+ // TODO: this is clumsy and slow, but no other way to find it?
+ auto type_field = fielddefs->LookupByKey(
+ (fielddef->name()->c_str() + std::string("_type")).c_str());
+ assert(type_field);
+ auto union_type = GetFieldI(table, type_field);
+ auto enumval = enumdef->values()->LookupByKey(union_type);
+ ResizeTable(enumval->object(), reinterpret_cast
(ref));
+ break;
+ }
+ case reflection::String:
+ break;
+ default:
+ assert(false);
+ }
+ }
+ }
+
+ private:
+ const reflection::Schema &schema_;
+ uoffset_t start_;
+ uint8_t *startptr_;
+ int delta_;
+ std::vector &buf_;
+ std::vector 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.
+inline void SetString(const reflection::Schema &schema, const std::string &val,
+ const String *str, std::vector *flatbuf) {
+ auto delta = static_cast(val.size()) - static_cast(str->Length());
+ auto start = static_cast(reinterpret_cast(str) -
+ flatbuf->data() +
+ sizeof(uoffset_t));
+ if (delta) {
+ // Different size, we must expand (or contract).
+ ResizeContext(schema, start, delta, flatbuf);
+ if (delta < 0) {
+ // Clear the old string, since we don't want parts of it remaining.
+ memset(flatbuf->data() + start, 0, str->Length());
+ }
+ }
+ // 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
+// 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.
+template void ResizeVector(const reflection::Schema &schema,
+ uoffset_t newsize, T val,
+ const Vector *vec,
+ std::vector *flatbuf) {
+ auto delta_elem = newsize - static_cast(vec->size());
+ auto delta_bytes = delta_elem * static_cast(sizeof(T));
+ auto vec_start = reinterpret_cast(vec) - flatbuf->data();
+ auto start = static_cast(vec_start + sizeof(uoffset_t) +
+ sizeof(T) * vec->size());
+ if (delta_bytes) {
+ ResizeContext(schema, start, delta_bytes, flatbuf);
+ 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);
+ if (std::is_scalar::value) {
+ WriteScalar(loc, val);
+ } else { // struct
+ *reinterpret_cast(loc) = val;
+ }
+ }
+ }
+}
+
+
+} // namespace flatbuffers
+
+#endif // FLATBUFFERS_REFLECTION_H_
diff --git a/include/flatbuffers/reflection_generated.h b/include/flatbuffers/reflection_generated.h
new file mode 100644
index 000000000..dfcf3e166
--- /dev/null
+++ b/include/flatbuffers/reflection_generated.h
@@ -0,0 +1,368 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+#ifndef FLATBUFFERS_GENERATED_REFLECTION_REFLECTION_H_
+#define FLATBUFFERS_GENERATED_REFLECTION_REFLECTION_H_
+
+#include "flatbuffers/flatbuffers.h"
+
+
+namespace reflection {
+
+struct Type;
+struct EnumVal;
+struct Enum;
+struct Field;
+struct Object;
+struct Schema;
+
+enum BaseType {
+ None = 0,
+ UType = 1,
+ Bool = 2,
+ Byte = 3,
+ UByte = 4,
+ Short = 5,
+ UShort = 6,
+ Int = 7,
+ UInt = 8,
+ Long = 9,
+ ULong = 10,
+ Float = 11,
+ Double = 12,
+ String = 13,
+ Vector = 14,
+ Obj = 15,
+ Union = 16
+};
+
+inline const char **EnumNamesBaseType() {
+ static const char *names[] = { "None", "UType", "Bool", "Byte", "UByte", "Short", "UShort", "Int", "UInt", "Long", "ULong", "Float", "Double", "String", "Vector", "Obj", "Union", nullptr };
+ return names;
+}
+
+inline const char *EnumNameBaseType(BaseType e) { return EnumNamesBaseType()[e]; }
+
+struct Type FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ BaseType base_type() const { return static_cast(GetField(4, 0)); }
+ BaseType element() const { return static_cast(GetField(6, 0)); }
+ int32_t index() const { return GetField(8, -1); }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField(verifier, 4 /* base_type */) &&
+ VerifyField(verifier, 6 /* element */) &&
+ VerifyField(verifier, 8 /* index */) &&
+ verifier.EndTable();
+ }
+};
+
+struct TypeBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_base_type(BaseType base_type) { fbb_.AddElement(4, static_cast(base_type), 0); }
+ void add_element(BaseType element) { fbb_.AddElement(6, static_cast(element), 0); }
+ void add_index(int32_t index) { fbb_.AddElement(8, index, -1); }
+ TypeBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
+ TypeBuilder &operator=(const TypeBuilder &);
+ flatbuffers::Offset Finish() {
+ auto o = flatbuffers::Offset(fbb_.EndTable(start_, 3));
+ return o;
+ }
+};
+
+inline flatbuffers::Offset CreateType(flatbuffers::FlatBufferBuilder &_fbb,
+ BaseType base_type = None,
+ BaseType element = None,
+ int32_t index = -1) {
+ TypeBuilder builder_(_fbb);
+ builder_.add_index(index);
+ builder_.add_element(element);
+ builder_.add_base_type(base_type);
+ return builder_.Finish();
+}
+
+struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ const flatbuffers::String *name() const { return GetPointer(4); }
+ int64_t value() const { return GetField(6, 0); }
+ bool KeyCompareLessThan(const EnumVal *o) const { return value() < o->value(); }
+ int KeyCompareWithValue(int64_t val) const { return value() < val ? -1 : value() > val; }
+ const Object *object() const { return GetPointer(8); }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyFieldRequired(verifier, 4 /* name */) &&
+ verifier.Verify(name()) &&
+ VerifyField(verifier, 6 /* value */) &&
+ VerifyField(verifier, 8 /* object */) &&
+ verifier.VerifyTable(object()) &&
+ verifier.EndTable();
+ }
+};
+
+struct EnumValBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_name(flatbuffers::Offset name) { fbb_.AddOffset(4, name); }
+ void add_value(int64_t value) { fbb_.AddElement(6, value, 0); }
+ void add_object(flatbuffers::Offset