Add buffer verification functionality to FlatBuffers

Bug: 15732628
Change-Id: I0b7cb65982d6b8957d5a899cca7d2b5d2ef53206
Tested: On Windows, OS X and Linux
parent 59043114
...@@ -47,7 +47,7 @@ we can construct them in a familiar way. ...@@ -47,7 +47,7 @@ we can construct them in a familiar way.
We have now serialized the non-scalar components of of the monster We have now serialized the non-scalar components of of the monster
example, so we could create the monster something like this: example, so we could create the monster something like this:
auto mloc = CreateMonster(fbb, &vec, 150, 80, name, inventory, Color_Red, Offset<void>(0), Any_NONE); auto mloc = CreateMonster(fbb, &vec, 150, 80, name, inventory, Color_Red, 0, Any_NONE);
Note that we're passing `150` for the `mana` field, which happens to be the Note that we're passing `150` for the `mana` field, which happens to be the
default value: this means the field will not actually be written to the buffer, default value: this means the field will not actually be written to the buffer,
...@@ -58,7 +58,8 @@ since they won't bloat up the buffer sizes if they're not actually used. ...@@ -58,7 +58,8 @@ since they won't bloat up the buffer sizes if they're not actually used.
We do something similarly for the union field `test` by specifying a `0` offset We do something similarly for the union field `test` by specifying a `0` offset
and the `NONE` enum value (part of every union) to indicate we don't actually and the `NONE` enum value (part of every union) to indicate we don't actually
want to write this field. want to write this field. You can use `0` also as a default for other
non-scalar types, such as strings, vectors and tables.
Tables (like `Monster`) give you full flexibility on what fields you write Tables (like `Monster`) give you full flexibility on what fields you write
(unlike `Vec3`, which always has all fields set because it is a `struct`). (unlike `Vec3`, which always has all fields set because it is a `struct`).
...@@ -155,6 +156,38 @@ machines, so only use tricks like this if you can guarantee you're not ...@@ -155,6 +156,38 @@ machines, so only use tricks like this if you can guarantee you're not
shipping on a big endian machine (an `assert(FLATBUFFERS_LITTLEENDIAN)` shipping on a big endian machine (an `assert(FLATBUFFERS_LITTLEENDIAN)`
would be wise). would be wise).
### Access of untrusted buffers
The generated accessor functions access fields over offsets, which is
very quick. These offsets are not verified at run-time, so a malformed
buffer could cause a program to crash by accessing random memory.
When you're processing large amounts of data from a source you know (e.g.
your own generated data on disk), this is acceptable, but when reading
data from the network that can potentially have been modified by an
attacker, this is undesirable.
For this reason, you can optionally use a buffer verifier before you
access the data. This verifier will check all offsets, all sizes of
fields, and null termination of strings to ensure that when a buffer
is accessed, all reads will end up inside the buffer.
Each root type will have a verification function generated for it,
e.g. for `Monster`, you can call:
bool ok = VerifyMonsterBuffer(Verifier(buf, len));
if `ok` is true, the buffer is safe to read.
Besides untrusted data, this function may be useful to call in debug
mode, as extra insurance against data being corrupted somewhere along
the way.
While verifying a buffer isn't "free", it is typically faster than
a full traversal (since any scalar data is not actually touched),
and since it may cause the buffer to be brought into cache before
reading, the actual overhead may be even lower than expected.
## Text & schema parsing ## Text & schema parsing
Using binary buffers with the generated header provides a super low Using binary buffers with the generated header provides a super low
......
...@@ -76,7 +76,7 @@ typedef uintmax_t largest_scalar_t; ...@@ -76,7 +76,7 @@ typedef uintmax_t largest_scalar_t;
template<typename T> struct Offset { template<typename T> struct Offset {
uoffset_t o; uoffset_t o;
Offset() : o(0) {} Offset() : o(0) {}
explicit Offset(uoffset_t _o) : o(_o) {} Offset(uoffset_t _o) : o(_o) {}
Offset<void> Union() const { return Offset<void>(o); } Offset<void> Union() const { return Offset<void>(o); }
}; };
...@@ -407,13 +407,13 @@ class FlatBufferBuilder { ...@@ -407,13 +407,13 @@ class FlatBufferBuilder {
buf_.fill(numfields * sizeof(voffset_t)); buf_.fill(numfields * sizeof(voffset_t));
auto table_object_size = vtableoffsetloc - start; auto table_object_size = vtableoffsetloc - start;
assert(table_object_size < 0x10000); // Vtable use 16bit offsets. assert(table_object_size < 0x10000); // Vtable use 16bit offsets.
PushElement<voffset_t>(table_object_size); PushElement<voffset_t>(static_cast<voffset_t>(table_object_size));
PushElement<voffset_t>(FieldIndexToOffset(numfields)); PushElement<voffset_t>(FieldIndexToOffset(numfields));
// Write the offsets into the table // Write the offsets into the table
for (auto field_location = offsetbuf_.begin(); for (auto field_location = offsetbuf_.begin();
field_location != offsetbuf_.end(); field_location != offsetbuf_.end();
++field_location) { ++field_location) {
auto pos = (vtableoffsetloc - field_location->off); auto pos = static_cast<voffset_t>(vtableoffsetloc - field_location->off);
// If this asserts, it means you've set a field twice. // If this asserts, it means you've set a field twice.
assert(!ReadScalar<voffset_t>(buf_.data() + field_location->id)); assert(!ReadScalar<voffset_t>(buf_.data() + field_location->id));
WriteScalar<voffset_t>(buf_.data() + field_location->id, pos); WriteScalar<voffset_t>(buf_.data() + field_location->id, pos);
...@@ -563,7 +563,90 @@ template<typename T> const T *GetRoot(const void *buf) { ...@@ -563,7 +563,90 @@ template<typename T> const T *GetRoot(const void *buf) {
EndianScalar(*reinterpret_cast<const uoffset_t *>(buf))); EndianScalar(*reinterpret_cast<const uoffset_t *>(buf)));
} }
// "structs_" are flat structures that do not have an offset table, thus // Helper class to verify the integrity of a FlatBuffer
class Verifier {
public:
Verifier(const uint8_t *buf, size_t buf_len)
: buf_(buf), end_(buf + buf_len)
{}
// Verify any range within the buffer.
bool Verify(const void *elem, size_t elem_len) const {
bool ok = elem >= buf_ && elem <= end_ - elem_len;
assert(ok);
return ok;
}
// Verify a range indicated by sizeof(T).
template<typename T> bool Verify(const void *elem) const {
return Verify(elem, sizeof(T));
}
// Verify a pointer (may be NULL) of any vector type.
template<typename T> bool Verify(const Vector<T> *vec) const {
const uint8_t *end;
return !vec ||
VerifyVector(reinterpret_cast<const uint8_t *>(vec), sizeof(T),
&end);
}
// Verify a pointer (may be NULL) to string.
bool Verify(const String *str) const {
const uint8_t *end;
return !str ||
(VerifyVector(reinterpret_cast<const uint8_t *>(str), 1, &end) &&
Verify(end, 1) && // Must have terminator
*end == '\0'); // Terminating byte must be 0.
}
// Common code between vectors and strings.
bool VerifyVector(const uint8_t *vec, size_t elem_size,
const uint8_t **end) const {
// Check we can read the size field.
if (!Verify<uoffset_t>(vec)) return false;
// Check the whole array. If this is a string, the byte past the array
// must be 0.
auto size = ReadScalar<uoffset_t>(vec);
auto byte_size = sizeof(size) + elem_size * size;
*end = vec + byte_size;
return Verify(vec, byte_size);
}
// Special case for string contents, after the above has been called.
bool VerifyVectorOfStrings(const Vector<Offset<String>> *vec) const {
if (vec) {
for (uoffset_t i = 0; i < vec->Length(); i++) {
if (!Verify(vec->Get(i))) return false;
}
}
return true;
}
// Special case for table contents, after the above has been called.
template<typename T> bool VerifyVectorOfTables(const Vector<Offset<T>> *vec)
const {
if (vec) {
for (uoffset_t i = 0; i < vec->Length(); i++) {
if (!vec->Get(i)->Verify(*this)) return false;
}
}
return true;
}
// Verify this whole buffer, starting with root type T.
template<typename T> bool VerifyBuffer() const {
// Call T::Verify, which must be in the generated code for this type.
return Verify<uoffset_t>(buf_) &&
reinterpret_cast<const T *>(buf_ + ReadScalar<uoffset_t>(buf_))->
Verify(*this);
}
private:
const uint8_t *buf_;
const uint8_t *end_;
};
// "structs" are flat structures that do not have an offset table, thus
// always have all members present and do not support forwards/backwards // always have all members present and do not support forwards/backwards
// compatible extensions. // compatible extensions.
...@@ -594,7 +677,7 @@ class Table { ...@@ -594,7 +677,7 @@ class Table {
// if the field was not present. // if the field was not present.
voffset_t GetOptionalFieldOffset(voffset_t field) const { voffset_t GetOptionalFieldOffset(voffset_t field) const {
// The vtable offset is always at the start. // The vtable offset is always at the start.
auto vtable = &data_ - ReadScalar<soffset_t>(&data_); auto vtable = data_ - ReadScalar<soffset_t>(data_);
// The first element is the size of the vtable (fields + type id + itself). // The first element is the size of the vtable (fields + type id + itself).
auto vtsize = ReadScalar<voffset_t>(vtable); auto vtsize = ReadScalar<voffset_t>(vtable);
// If the field we're accessing is outside the vtable, we're reading older // If the field we're accessing is outside the vtable, we're reading older
...@@ -604,12 +687,12 @@ class Table { ...@@ -604,12 +687,12 @@ class Table {
template<typename T> T GetField(voffset_t field, T defaultval) const { template<typename T> T GetField(voffset_t field, T defaultval) const {
auto field_offset = GetOptionalFieldOffset(field); auto field_offset = GetOptionalFieldOffset(field);
return field_offset ? ReadScalar<T>(&data_[field_offset]) : defaultval; return field_offset ? ReadScalar<T>(data_ + field_offset) : defaultval;
} }
template<typename P> P GetPointer(voffset_t field) const { template<typename P> P GetPointer(voffset_t field) const {
auto field_offset = GetOptionalFieldOffset(field); auto field_offset = GetOptionalFieldOffset(field);
auto p = &data_[field_offset]; auto p = data_ + field_offset;
return field_offset return field_offset
? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p)) ? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p))
: nullptr; : nullptr;
...@@ -617,7 +700,7 @@ class Table { ...@@ -617,7 +700,7 @@ class Table {
template<typename P> P GetStruct(voffset_t field) const { template<typename P> P GetStruct(voffset_t field) const {
auto field_offset = GetOptionalFieldOffset(field); auto field_offset = GetOptionalFieldOffset(field);
return field_offset ? reinterpret_cast<P>(&data_[field_offset]) : nullptr; return field_offset ? reinterpret_cast<P>(data_ + field_offset) : nullptr;
} }
template<typename T> void SetField(voffset_t field, T val) { template<typename T> void SetField(voffset_t field, T val) {
...@@ -626,18 +709,39 @@ class Table { ...@@ -626,18 +709,39 @@ class Table {
// (or should we return a bool instead?). // (or should we return a bool instead?).
// check if it exists first using CheckField() // check if it exists first using CheckField()
assert(field_offset); assert(field_offset);
WriteScalar(&data_[field_offset], val); WriteScalar(data_ + field_offset, val);
} }
bool CheckField(voffset_t field) const { bool CheckField(voffset_t field) const {
return GetOptionalFieldOffset(field) != 0; return GetOptionalFieldOffset(field) != 0;
} }
// Verify the vtable of this table.
// Call this once per table, followed by VerifyField once per field.
bool VerifyTable(const Verifier &verifier) const {
// Check the vtable offset.
if (!verifier.Verify<soffset_t>(data_)) return false;
auto vtable = data_ - ReadScalar<soffset_t>(data_);
// Check the vtable size field, then check vtable fits in its entirety.
return verifier.Verify<voffset_t>(vtable) &&
verifier.Verify(vtable, ReadScalar<voffset_t>(vtable));
}
// Verify a particular field.
template<typename T> bool VerifyField(const Verifier &verifier,
voffset_t field) const {
// Calling GetOptionalFieldOffset should be safe now thanks to
// VerifyTable().
auto field_offset = GetOptionalFieldOffset(field);
// Check the actual field.
return !field_offset || verifier.Verify<T>(data_ + field_offset);
}
private: private:
// private constructor & copy constructor: you obtain instances of this // private constructor & copy constructor: you obtain instances of this
// class by pointing to existing data only // class by pointing to existing data only
Table() {}; Table();
Table(const Table &other) {}; Table(const Table &other);
uint8_t data_[1]; uint8_t data_[1];
}; };
...@@ -645,10 +749,10 @@ class Table { ...@@ -645,10 +749,10 @@ class Table {
// Utility function for reverse lookups on the EnumNames*() functions // Utility function for reverse lookups on the EnumNames*() functions
// (in the generated C++ code) // (in the generated C++ code)
// names must be NULL terminated. // names must be NULL terminated.
inline size_t LookupEnum(const char **names, const char *name) { inline int LookupEnum(const char **names, const char *name) {
for (const char **p = names; *p; p++) for (const char **p = names; *p; p++)
if (!strcmp(*p, name)) if (!strcmp(*p, name))
return p - names; return static_cast<int>(p - names);
return -1; return -1;
} }
......
...@@ -62,6 +62,16 @@ static std::string GenTypeWire(const Type &type, const char *postfix) { ...@@ -62,6 +62,16 @@ static std::string GenTypeWire(const Type &type, const char *postfix) {
: "flatbuffers::Offset<" + GenTypePointer(type) + ">" + postfix; : "flatbuffers::Offset<" + GenTypePointer(type) + ">" + postfix;
} }
// Return a C++ type for any type (scalar/pointer) that reflects its
// serialized size.
static std::string GenTypeSize(const Type &type) {
return IsScalar(type.base_type)
? GenTypeBasic(type)
: IsStruct(type)
? GenTypePointer(type)
: "flatbuffers::uoffset_t";
}
// Return a C++ type for any type (scalar/pointer) specifically for // Return a C++ type for any type (scalar/pointer) specifically for
// using a flatbuffer. // using a flatbuffer.
static std::string GenTypeGet(const Type &type, const char *afterbasic, static std::string GenTypeGet(const Type &type, const char *afterbasic,
...@@ -82,9 +92,11 @@ static void GenComment(const std::string &dc, ...@@ -82,9 +92,11 @@ static void GenComment(const std::string &dc,
} }
// Generate an enum declaration and an enum string lookup table. // Generate an enum declaration and an enum string lookup table.
static void GenEnum(EnumDef &enum_def, std::string *code_ptr) { static void GenEnum(EnumDef &enum_def, std::string *code_ptr,
std::string *code_ptr_post) {
if (enum_def.generated) return; if (enum_def.generated) return;
std::string &code = *code_ptr; std::string &code = *code_ptr;
std::string &code_post = *code_ptr_post;
GenComment(enum_def.doc_comment, code_ptr); GenComment(enum_def.doc_comment, code_ptr);
code += "enum {\n"; code += "enum {\n";
for (auto it = enum_def.vals.vec.begin(); for (auto it = enum_def.vals.vec.begin();
...@@ -123,6 +135,32 @@ static void GenEnum(EnumDef &enum_def, std::string *code_ptr) { ...@@ -123,6 +135,32 @@ static void GenEnum(EnumDef &enum_def, std::string *code_ptr) {
code += " - " + enum_def.name + "_" + enum_def.vals.vec.front()->name; code += " - " + enum_def.name + "_" + enum_def.vals.vec.front()->name;
code += "]; }\n\n"; code += "]; }\n\n";
} }
if (enum_def.is_union) {
// Generate a verifier function for this union that can be called by the
// table verifier functions. It uses a switch case to select a specific
// verifier function to call, this should be safe even if the union type
// has been corrupted, since the verifiers will simply fail when called
// on the wrong type.
auto signature = "bool Verify" + enum_def.name +
"(const flatbuffers::Verifier &verifier, " +
"const void *union_obj, uint8_t type)";
code += signature + ";\n\n";
code_post += signature + " {\n switch (type) {\n";
for (auto it = enum_def.vals.vec.begin();
it != enum_def.vals.vec.end();
++it) {
auto &ev = **it;
code_post += " case " + enum_def.name + "_" + ev.name;
if (!ev.value) {
code_post += ": return true;\n"; // "NONE" enum value.
} else {
code_post += ": return reinterpret_cast<const " + ev.struct_def->name;
code_post += " *>(union_obj)->Verify(verifier);\n";
}
}
code_post += " default: return false;\n }\n}\n\n";
}
} }
// Generate an accessor struct, builder structs & function for a table. // Generate an accessor struct, builder structs & function for a table.
...@@ -155,6 +193,58 @@ static void GenTable(StructDef &struct_def, std::string *code_ptr) { ...@@ -155,6 +193,58 @@ static void GenTable(StructDef &struct_def, std::string *code_ptr) {
code += "); }\n"; code += "); }\n";
} }
} }
// Generate a verifier function that can check a buffer from an untrusted
// source will never cause reads outside the buffer.
code += " bool Verify(const flatbuffers::Verifier &verifier) const {\n";
code += " return VerifyTable(verifier)";
std::string prefix = " &&\n ";
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end();
++it) {
auto &field = **it;
if (!field.deprecated) {
code += prefix + "VerifyField<" + GenTypeSize(field.value.type);
code += ">(verifier, " + NumToString(field.value.offset);
code += " /* " + field.name + " */)";
switch (field.value.type.base_type) {
case BASE_TYPE_UNION:
code += prefix + "Verify" + field.value.type.enum_def->name;
code += "(verifier, " + field.name + "(), " + field.name + "_type())";
break;
case BASE_TYPE_STRUCT:
if (!field.value.type.struct_def->fixed) {
code += prefix + field.value.type.struct_def->name;
code += "()->Verify()";
}
break;
case BASE_TYPE_STRING:
code += prefix + "verifier.Verify(" + field.name + "())";
break;
case BASE_TYPE_VECTOR:
code += prefix + "verifier.Verify(" + field.name + "())";
switch (field.value.type.element) {
case BASE_TYPE_STRING: {
code += prefix + "verifier.VerifyVectorOfStrings(" + field.name;
code += "())";
break;
}
case BASE_TYPE_STRUCT: {
if (!field.value.type.struct_def->fixed) {
code += prefix + "verifier.VerifyVectorOfTables(" + field.name;
code += "())";
}
break;
}
default:
break;
}
break;
default:
break;
}
}
}
code += ";\n }\n";
code += "};\n\n"; code += "};\n\n";
// Generate a builder struct, with methods of the form: // Generate a builder struct, with methods of the form:
...@@ -302,10 +392,10 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i ...@@ -302,10 +392,10 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
using namespace cpp; using namespace cpp;
// Generate code for all the enum declarations. // Generate code for all the enum declarations.
std::string enum_code; std::string enum_code, enum_code_post;
for (auto it = parser.enums_.vec.begin(); for (auto it = parser.enums_.vec.begin();
it != parser.enums_.vec.end(); ++it) { it != parser.enums_.vec.end(); ++it) {
GenEnum(**it, &enum_code); GenEnum(**it, &enum_code, &enum_code_post);
} }
// Generate forward declarations for all structs/tables, since they may // Generate forward declarations for all structs/tables, since they may
...@@ -361,13 +451,20 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i ...@@ -361,13 +451,20 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
code += forward_decl_code; code += forward_decl_code;
code += "\n"; code += "\n";
code += decl_code; code += decl_code;
code += enum_code_post;
// Generate convenient root datatype accessor. // Generate convenient root datatype accessor, and root verifier.
if (parser.root_struct_def) { if (parser.root_struct_def) {
code += "inline const " + parser.root_struct_def->name + " *Get"; code += "inline const " + parser.root_struct_def->name + " *Get";
code += parser.root_struct_def->name; code += parser.root_struct_def->name;
code += "(const void *buf) { return flatbuffers::GetRoot<"; code += "(const void *buf) { return flatbuffers::GetRoot<";
code += parser.root_struct_def->name + ">(buf); }\n\n"; code += parser.root_struct_def->name + ">(buf); }\n\n";
code += "inline bool Verify";
code += parser.root_struct_def->name;
code += "Buffer(const flatbuffers::Verifier &verifier) { "
"return verifier.VerifyBuffer<";
code += parser.root_struct_def->name + ">(); }\n\n";
} }
// Close the namespaces. // Close the namespaces.
......
...@@ -24,8 +24,13 @@ public class Monster extends Table { ...@@ -24,8 +24,13 @@ public class Monster extends Table {
public Test test4(int j) { return test4(new Test(), j); } public Test test4(int j) { return test4(new Test(), j); }
public Test test4(Test obj, int j) { int o = __offset(22); return o != 0 ? obj.__init(__vector(o) + j * 4, bb) : null; } public Test test4(Test obj, int j) { int o = __offset(22); return o != 0 ? obj.__init(__vector(o) + j * 4, bb) : null; }
public int test4Length() { int o = __offset(22); return o != 0 ? __vector_len(o) : 0; } public int test4Length() { int o = __offset(22); return o != 0 ? __vector_len(o) : 0; }
public String testarrayofstring(int j) { int o = __offset(24); return o != 0 ? __string(__vector(o) + j * 4) : null; }
public int testarrayofstringLength() { int o = __offset(24); return o != 0 ? __vector_len(o) : 0; }
public Monster testarrayoftables(int j) { return testarrayoftables(new Monster(), j); }
public Monster testarrayoftables(Monster obj, int j) { int o = __offset(26); return o != 0 ? obj.__init(__indirect(__vector(o) + j * 4), bb) : null; }
public int testarrayoftablesLength() { int o = __offset(26); return o != 0 ? __vector_len(o) : 0; }
public static void startMonster(FlatBufferBuilder builder) { builder.startObject(10); } public static void startMonster(FlatBufferBuilder builder) { builder.startObject(12); }
public static void addPos(FlatBufferBuilder builder, int pos) { builder.addStruct(0, pos, 0); } public static void addPos(FlatBufferBuilder builder, int pos) { builder.addStruct(0, pos, 0); }
public static void addMana(FlatBufferBuilder builder, short mana) { builder.addShort(1, mana, 150); } public static void addMana(FlatBufferBuilder builder, short mana) { builder.addShort(1, mana, 150); }
public static void addHp(FlatBufferBuilder builder, short hp) { builder.addShort(2, hp, 100); } public static void addHp(FlatBufferBuilder builder, short hp) { builder.addShort(2, hp, 100); }
...@@ -37,6 +42,10 @@ public class Monster extends Table { ...@@ -37,6 +42,10 @@ public class Monster extends Table {
public static void addTest(FlatBufferBuilder builder, int test) { builder.addOffset(8, test, 0); } public static void addTest(FlatBufferBuilder builder, int test) { builder.addOffset(8, test, 0); }
public static void addTest4(FlatBufferBuilder builder, int test4) { builder.addOffset(9, test4, 0); } public static void addTest4(FlatBufferBuilder builder, int test4) { builder.addOffset(9, test4, 0); }
public static void startTest4Vector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); } public static void startTest4Vector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
public static void addTestarrayofstring(FlatBufferBuilder builder, int testarrayofstring) { builder.addOffset(10, testarrayofstring, 0); }
public static void startTestarrayofstringVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
public static void addTestarrayoftables(FlatBufferBuilder builder, int testarrayoftables) { builder.addOffset(11, testarrayoftables, 0); }
public static void startTestarrayoftablesVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
public static int endMonster(FlatBufferBuilder builder) { return builder.endObject(); } public static int endMonster(FlatBufferBuilder builder) { return builder.endObject(); }
}; };
...@@ -29,6 +29,8 @@ table Monster { ...@@ -29,6 +29,8 @@ table Monster {
color:Color = Blue; color:Color = Blue;
test:Any; test:Any;
test4:[Test]; test4:[Test];
testarrayofstring:[string];
testarrayoftables:[Monster];
} }
root_type Monster; root_type Monster;
...@@ -33,6 +33,8 @@ inline const char **EnumNamesAny() { ...@@ -33,6 +33,8 @@ inline const char **EnumNamesAny() {
inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; } inline const char *EnumNameAny(int e) { return EnumNamesAny()[e]; }
bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type);
struct Test; struct Test;
struct Vec3; struct Vec3;
struct Monster; struct Monster;
...@@ -88,6 +90,30 @@ struct Monster : private flatbuffers::Table { ...@@ -88,6 +90,30 @@ struct Monster : private flatbuffers::Table {
uint8_t test_type() const { return GetField<uint8_t>(18, 0); } uint8_t test_type() const { return GetField<uint8_t>(18, 0); }
const void *test() const { return GetPointer<const void *>(20); } const void *test() const { return GetPointer<const void *>(20); }
const flatbuffers::Vector<const Test *> *test4() const { return GetPointer<const flatbuffers::Vector<const Test *> *>(22); } const flatbuffers::Vector<const Test *> *test4() const { return GetPointer<const flatbuffers::Vector<const Test *> *>(22); }
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *testarrayofstring() const { return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(24); }
const flatbuffers::Vector<flatbuffers::Offset<Monster>> *testarrayoftables() const { return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Monster>> *>(26); }
bool Verify(const flatbuffers::Verifier &verifier) const {
return VerifyTable(verifier) &&
VerifyField<Vec3>(verifier, 4 /* pos */) &&
VerifyField<int16_t>(verifier, 6 /* mana */) &&
VerifyField<int16_t>(verifier, 8 /* hp */) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 10 /* name */) &&
verifier.Verify(name()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 14 /* inventory */) &&
verifier.Verify(inventory()) &&
VerifyField<int8_t>(verifier, 16 /* color */) &&
VerifyField<uint8_t>(verifier, 18 /* test_type */) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 20 /* test */) &&
VerifyAny(verifier, test(), test_type()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 22 /* test4 */) &&
verifier.Verify(test4()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 24 /* testarrayofstring */) &&
verifier.Verify(testarrayofstring()) &&
verifier.VerifyVectorOfStrings(testarrayofstring()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 26 /* testarrayoftables */) &&
verifier.Verify(testarrayoftables()) &&
verifier.VerifyVectorOfTables(testarrayoftables());
}
}; };
struct MonsterBuilder { struct MonsterBuilder {
...@@ -102,12 +128,17 @@ struct MonsterBuilder { ...@@ -102,12 +128,17 @@ struct MonsterBuilder {
void add_test_type(uint8_t test_type) { fbb_.AddElement<uint8_t>(18, test_type, 0); } void add_test_type(uint8_t test_type) { fbb_.AddElement<uint8_t>(18, test_type, 0); }
void add_test(flatbuffers::Offset<void> test) { fbb_.AddOffset(20, test); } void add_test(flatbuffers::Offset<void> test) { fbb_.AddOffset(20, test); }
void add_test4(flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4) { fbb_.AddOffset(22, test4); } void add_test4(flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4) { fbb_.AddOffset(22, test4); }
void add_testarrayofstring(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring) { fbb_.AddOffset(24, testarrayofstring); }
void add_testarrayoftables(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Monster>>> testarrayoftables) { fbb_.AddOffset(26, testarrayoftables); }
MonsterBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } MonsterBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
flatbuffers::Offset<Monster> Finish() { return flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 10)); } MonsterBuilder &operator=(const MonsterBuilder &);
flatbuffers::Offset<Monster> Finish() { return flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 12)); }
}; };
inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const Vec3 *pos, int16_t mana, int16_t hp, flatbuffers::Offset<flatbuffers::String> name, flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory, int8_t color, uint8_t test_type, flatbuffers::Offset<void> test, flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4) { inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const Vec3 *pos, int16_t mana, int16_t hp, flatbuffers::Offset<flatbuffers::String> name, flatbuffers::Offset<flatbuffers::Vector<uint8_t>> inventory, int8_t color, uint8_t test_type, flatbuffers::Offset<void> test, flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4, flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring, flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Monster>>> testarrayoftables) {
MonsterBuilder builder_(_fbb); MonsterBuilder builder_(_fbb);
builder_.add_testarrayoftables(testarrayoftables);
builder_.add_testarrayofstring(testarrayofstring);
builder_.add_test4(test4); builder_.add_test4(test4);
builder_.add_test(test); builder_.add_test(test);
builder_.add_inventory(inventory); builder_.add_inventory(inventory);
...@@ -120,8 +151,18 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder ...@@ -120,8 +151,18 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
return builder_.Finish(); return builder_.Finish();
} }
bool VerifyAny(const flatbuffers::Verifier &verifier, const void *union_obj, uint8_t type) {
switch (type) {
case Any_NONE: return true;
case Any_Monster: return reinterpret_cast<const Monster *>(union_obj)->Verify(verifier);
default: return false;
}
}
inline const Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<Monster>(buf); } inline const Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<Monster>(buf); }
inline bool VerifyMonsterBuffer(const flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
}; // namespace MyGame }; // namespace MyGame
}; // namespace Example }; // namespace Example
......
...@@ -79,10 +79,19 @@ std::string CreateFlatBufferTest() { ...@@ -79,10 +79,19 @@ std::string CreateFlatBufferTest() {
mb.add_hp(20); mb.add_hp(20);
auto mloc2 = mb.Finish(); auto mloc2 = mb.Finish();
// Create an array of strings:
flatbuffers::Offset<flatbuffers::String> strings[2];
strings[0] = builder.CreateString("bob");
strings[1] = builder.CreateString("fred");
auto vecofstrings = builder.CreateVector(strings, 2);
// Create an array of tables:
auto vecoftables = builder.CreateVector(&mloc2, 1);
// shortcut for creating monster with all fields set: // shortcut for creating monster with all fields set:
auto mloc = CreateMonster(builder, &vec, 150, 80, name, inventory, Color_Blue, auto mloc = CreateMonster(builder, &vec, 150, 80, name, inventory, Color_Blue,
Any_Monster, mloc2.Union(), // Store a union. Any_Monster, mloc2.Union(), // Store a union.
testv); testv, vecofstrings, vecoftables);
builder.Finish(mloc); builder.Finish(mloc);
...@@ -101,6 +110,13 @@ std::string CreateFlatBufferTest() { ...@@ -101,6 +110,13 @@ std::string CreateFlatBufferTest() {
// example of accessing a buffer loaded in memory: // example of accessing a buffer loaded in memory:
void AccessFlatBufferTest(const std::string &flatbuf) { void AccessFlatBufferTest(const std::string &flatbuf) {
// First, verify the buffers integrity (optional)
flatbuffers::Verifier verifier(
reinterpret_cast<const uint8_t *>(flatbuf.c_str()),
flatbuf.length());
TEST_EQ(VerifyMonsterBuffer(verifier), true);
// Access the buffer from the root.
auto monster = GetMonster(flatbuf.c_str()); auto monster = GetMonster(flatbuf.c_str());
TEST_EQ(monster->hp(), 80); TEST_EQ(monster->hp(), 80);
...@@ -128,11 +144,22 @@ void AccessFlatBufferTest(const std::string &flatbuf) { ...@@ -128,11 +144,22 @@ void AccessFlatBufferTest(const std::string &flatbuf) {
TEST_NOTNULL(monster2); TEST_NOTNULL(monster2);
TEST_EQ(monster2->hp(), 20); TEST_EQ(monster2->hp(), 20);
// Example of accessing a vector of strings:
auto vecofstrings = monster->testarrayofstring();
TEST_EQ(vecofstrings->Length(), 2U);
TEST_EQ(strcmp(vecofstrings->Get(0)->c_str(), "bob"), 0);
TEST_EQ(strcmp(vecofstrings->Get(1)->c_str(), "fred"), 0);
// Example of accessing a vector of tables:
auto vecoftables = monster->testarrayoftables();
TEST_EQ(vecoftables->Length(), 1U);
TEST_EQ(vecoftables->Get(0)->hp(), 20);
// Since Flatbuffers uses explicit mechanisms to override the default // Since Flatbuffers uses explicit mechanisms to override the default
// compiler alignment, double check that the compiler indeed obeys them: // compiler alignment, double check that the compiler indeed obeys them:
// (Test consists of a short and byte): // (Test consists of a short and byte):
TEST_EQ(flatbuffers::AlignOf<Test>(), static_cast<size_t>(2)); TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
TEST_EQ(sizeof(Test), static_cast<size_t>(4)); TEST_EQ(sizeof(Test), 4UL);
auto tests = monster->test4(); auto tests = monster->test4();
TEST_NOTNULL(tests); TEST_NOTNULL(tests);
...@@ -163,6 +190,11 @@ void ParseAndGenerateTextTest() { ...@@ -163,6 +190,11 @@ void ParseAndGenerateTextTest() {
// here, parser.builder_ contains a binary buffer that is the parsed data. // here, parser.builder_ contains a binary buffer that is the parsed data.
// First, verify it, just in case:
flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
parser.builder_.GetSize());
TEST_EQ(VerifyMonsterBuffer(verifier), true);
// to ensure it is correct, we now generate text back from the binary, // to ensure it is correct, we now generate text back from the binary,
// and compare the two: // and compare the two:
std::string jsongen; std::string jsongen;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment