mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-08 06:05:17 +00:00
Add buffer verification functionality to FlatBuffers
Bug: 15732628 Change-Id: I0b7cb65982d6b8957d5a899cca7d2b5d2ef53206 Tested: On Windows, OS X and Linux
This commit is contained in:
@@ -47,7 +47,7 @@ we can construct them in a familiar way.
|
||||
We have now serialized the non-scalar components of of the monster
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
(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
|
||||
shipping on a big endian machine (an `assert(FLATBUFFERS_LITTLEENDIAN)`
|
||||
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
|
||||
|
||||
Using binary buffers with the generated header provides a super low
|
||||
|
||||
@@ -76,7 +76,7 @@ typedef uintmax_t largest_scalar_t;
|
||||
template<typename T> struct Offset {
|
||||
uoffset_t o;
|
||||
Offset() : o(0) {}
|
||||
explicit Offset(uoffset_t _o) : o(_o) {}
|
||||
Offset(uoffset_t _o) : o(_o) {}
|
||||
Offset<void> Union() const { return Offset<void>(o); }
|
||||
};
|
||||
|
||||
@@ -407,13 +407,13 @@ class FlatBufferBuilder {
|
||||
buf_.fill(numfields * sizeof(voffset_t));
|
||||
auto table_object_size = vtableoffsetloc - start;
|
||||
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));
|
||||
// Write the offsets into the table
|
||||
for (auto field_location = offsetbuf_.begin();
|
||||
field_location != offsetbuf_.end();
|
||||
++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.
|
||||
assert(!ReadScalar<voffset_t>(buf_.data() + field_location->id));
|
||||
WriteScalar<voffset_t>(buf_.data() + field_location->id, pos);
|
||||
@@ -563,7 +563,90 @@ template<typename T> const T *GetRoot(const void *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
|
||||
// compatible extensions.
|
||||
|
||||
@@ -594,7 +677,7 @@ class Table {
|
||||
// if the field was not present.
|
||||
voffset_t GetOptionalFieldOffset(voffset_t field) const {
|
||||
// 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).
|
||||
auto vtsize = ReadScalar<voffset_t>(vtable);
|
||||
// If the field we're accessing is outside the vtable, we're reading older
|
||||
@@ -604,12 +687,12 @@ class Table {
|
||||
|
||||
template<typename T> T GetField(voffset_t field, T defaultval) const {
|
||||
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 {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
auto p = &data_[field_offset];
|
||||
auto p = data_ + field_offset;
|
||||
return field_offset
|
||||
? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p))
|
||||
: nullptr;
|
||||
@@ -617,7 +700,7 @@ class Table {
|
||||
|
||||
template<typename P> P GetStruct(voffset_t field) const {
|
||||
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) {
|
||||
@@ -626,18 +709,39 @@ class Table {
|
||||
// (or should we return a bool instead?).
|
||||
// check if it exists first using CheckField()
|
||||
assert(field_offset);
|
||||
WriteScalar(&data_[field_offset], val);
|
||||
WriteScalar(data_ + field_offset, val);
|
||||
}
|
||||
|
||||
bool CheckField(voffset_t field) const {
|
||||
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 constructor & copy constructor: you obtain instances of this
|
||||
// class by pointing to existing data only
|
||||
Table() {};
|
||||
Table(const Table &other) {};
|
||||
Table();
|
||||
Table(const Table &other);
|
||||
|
||||
uint8_t data_[1];
|
||||
};
|
||||
@@ -645,10 +749,10 @@ class Table {
|
||||
// Utility function for reverse lookups on the EnumNames*() functions
|
||||
// (in the generated C++ code)
|
||||
// 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++)
|
||||
if (!strcmp(*p, name))
|
||||
return p - names;
|
||||
return static_cast<int>(p - names);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,16 @@ static std::string GenTypeWire(const Type &type, const char *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
|
||||
// using a flatbuffer.
|
||||
static std::string GenTypeGet(const Type &type, const char *afterbasic,
|
||||
@@ -82,9 +92,11 @@ static void GenComment(const std::string &dc,
|
||||
}
|
||||
|
||||
// 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;
|
||||
std::string &code = *code_ptr;
|
||||
std::string &code_post = *code_ptr_post;
|
||||
GenComment(enum_def.doc_comment, code_ptr);
|
||||
code += "enum {\n";
|
||||
for (auto it = enum_def.vals.vec.begin();
|
||||
@@ -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 += "]; }\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.
|
||||
@@ -155,6 +193,58 @@ static void GenTable(StructDef &struct_def, std::string *code_ptr) {
|
||||
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";
|
||||
|
||||
// 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
|
||||
using namespace cpp;
|
||||
|
||||
// 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();
|
||||
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
|
||||
@@ -361,13 +451,20 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
|
||||
code += forward_decl_code;
|
||||
code += "\n";
|
||||
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) {
|
||||
code += "inline const " + parser.root_struct_def->name + " *Get";
|
||||
code += parser.root_struct_def->name;
|
||||
code += "(const void *buf) { return flatbuffers::GetRoot<";
|
||||
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.
|
||||
|
||||
@@ -24,8 +24,13 @@ public class Monster extends Table {
|
||||
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 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 addMana(FlatBufferBuilder builder, short mana) { builder.addShort(1, mana, 150); }
|
||||
public static void addHp(FlatBufferBuilder builder, short hp) { builder.addShort(2, hp, 100); }
|
||||
@@ -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 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 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(); }
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ table Monster {
|
||||
color:Color = Blue;
|
||||
test:Any;
|
||||
test4:[Test];
|
||||
testarrayofstring:[string];
|
||||
testarrayoftables:[Monster];
|
||||
}
|
||||
|
||||
root_type Monster;
|
||||
|
||||
@@ -33,6 +33,8 @@ inline const char **EnumNamesAny() {
|
||||
|
||||
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 Vec3;
|
||||
struct Monster;
|
||||
@@ -88,6 +90,30 @@ struct Monster : private flatbuffers::Table {
|
||||
uint8_t test_type() const { return GetField<uint8_t>(18, 0); }
|
||||
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<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 {
|
||||
@@ -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(flatbuffers::Offset<void> test) { fbb_.AddOffset(20, test); }
|
||||
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(); }
|
||||
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);
|
||||
builder_.add_testarrayoftables(testarrayoftables);
|
||||
builder_.add_testarrayofstring(testarrayofstring);
|
||||
builder_.add_test4(test4);
|
||||
builder_.add_test(test);
|
||||
builder_.add_inventory(inventory);
|
||||
@@ -120,8 +151,18 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
|
||||
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 bool VerifyMonsterBuffer(const flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<Monster>(); }
|
||||
|
||||
}; // namespace MyGame
|
||||
}; // namespace Example
|
||||
|
||||
|
||||
Binary file not shown.
@@ -79,10 +79,19 @@ std::string CreateFlatBufferTest() {
|
||||
mb.add_hp(20);
|
||||
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:
|
||||
auto mloc = CreateMonster(builder, &vec, 150, 80, name, inventory, Color_Blue,
|
||||
Any_Monster, mloc2.Union(), // Store a union.
|
||||
testv);
|
||||
testv, vecofstrings, vecoftables);
|
||||
|
||||
builder.Finish(mloc);
|
||||
|
||||
@@ -101,6 +110,13 @@ std::string CreateFlatBufferTest() {
|
||||
// example of accessing a buffer loaded in memory:
|
||||
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());
|
||||
|
||||
TEST_EQ(monster->hp(), 80);
|
||||
@@ -128,11 +144,22 @@ void AccessFlatBufferTest(const std::string &flatbuf) {
|
||||
TEST_NOTNULL(monster2);
|
||||
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
|
||||
// compiler alignment, double check that the compiler indeed obeys them:
|
||||
// (Test consists of a short and byte):
|
||||
TEST_EQ(flatbuffers::AlignOf<Test>(), static_cast<size_t>(2));
|
||||
TEST_EQ(sizeof(Test), static_cast<size_t>(4));
|
||||
TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
|
||||
TEST_EQ(sizeof(Test), 4UL);
|
||||
|
||||
auto tests = monster->test4();
|
||||
TEST_NOTNULL(tests);
|
||||
@@ -163,6 +190,11 @@ void ParseAndGenerateTextTest() {
|
||||
|
||||
// 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,
|
||||
// and compare the two:
|
||||
std::string jsongen;
|
||||
|
||||
Reference in New Issue
Block a user