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,
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`).
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
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); }
};
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);
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.
// 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
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;
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) {
// (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];
};
// 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;
}
: "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,
}
// 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();
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.
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:
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
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.
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); }
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(); }
};
color:Color = Blue;
test:Any;
test4:[Test];
+ testarrayofstring:[string];
+ testarrayoftables:[Monster];
}
root_type Monster;
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;
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 {
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);
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
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);
// 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);
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);
// 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;