id: n (on a table field): manually set the field identifier to n. If you use this attribute, you must use it on ALL fields of this table, and the numbers must be a contiguous range from 0 onwards. Additionally, since a union type effectively adds two fields, its id must be that of the second field (the first field is the type field and not explicitly declared in the schema). For example, if the last field before the union field had id 6, the union field should have id 8, and the unions type field will implicitly be 7. IDs allow the fields to be placed in any order in the schema. When a new field is added to the schema is must use the next available ID.
deprecated (on a field): do not generate accessors for this field anymore, code should stop using this data.
+
required (on a non-scalar table field): this field must always be set. By default, all fields are optional, i.e. may be left out. This is desirable, as it helps with forwards/backwards compatibility, and flexibility of data structures. It is also a burden on the reading code, since for non-scalar fields it requires you to check against NULL and take appropriate action. By specifying this field, you force code that constructs FlatBuffers to ensure this field is initialized, so the reading code may access it directly, without checking for NULL. If the constructing code does not initialize this field, they will get an assert, and also the verifier will fail on buffers that have missing required fields.
original_order (on a table): since elements in a table do not need to be stored in any particular order, they are often optimized for space by sorting them to size. This attribute stops that from happening.
force_align: size (on a struct): force the alignment of this struct to be something higher than what it is naturally aligned to. Causes these structs to be aligned to that amount inside a buffer, IF that buffer is allocated with that alignment (which is not necessarily the case for buffers accessed directly inside a FlatBufferBuilder).
bit_flags (on an enum): the values of this field indicate bits, meaning that any value N specified in the schema will end up representing 1<<N, or if you don't specify values at all, you'll get the sequence 1, 2, 4, 8, ...
diff --git a/docs/source/Schemas.md b/docs/source/Schemas.md
index f3afdfa6d..d7b905f8b 100755
--- a/docs/source/Schemas.md
+++ b/docs/source/Schemas.md
@@ -146,7 +146,7 @@ packages.
You can include other schemas files in your current one, e.g.:
include "mydefinitions.fbs"
-
+
This makes it easier to refer to types defined elsewhere. `include`
automatically ensures each file is parsed just once, even when referred to
more than once.
@@ -232,6 +232,16 @@ Current understood attributes:
When a new field is added to the schema is must use the next available ID.
- `deprecated` (on a field): do not generate accessors for this field
anymore, code should stop using this data.
+- `required` (on a non-scalar table field): this field must always be set.
+ By default, all fields are optional, i.e. may be left out. This is
+ desirable, as it helps with forwards/backwards compatibility, and
+ flexibility of data structures. It is also a burden on the reading code,
+ since for non-scalar fields it requires you to check against NULL and
+ take appropriate action. By specifying this field, you force code that
+ constructs FlatBuffers to ensure this field is initialized, so the reading
+ code may access it directly, without checking for NULL. If the constructing
+ code does not initialize this field, they will get an assert, and also
+ the verifier will fail on buffers that have missing required fields.
- `original_order` (on a table): since elements in a table do not need
to be stored in any particular order, they are often optimized for
space by sorting them to size. This attribute stops that from happening.
diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h
index 0ac4e81f0..17cba4c71 100644
--- a/include/flatbuffers/flatbuffers.h
+++ b/include/flatbuffers/flatbuffers.h
@@ -540,6 +540,17 @@ class FlatBufferBuilder {
return vtableoffsetloc;
}
+ // This checks a required field has been set in a given table that has
+ // just been constructed.
+ template void Required(Offset table, voffset_t field) {
+ auto table_ptr = buf_.data_at(table.o);
+ auto vtable_ptr = table_ptr - ReadScalar(table_ptr);
+ bool ok = ReadScalar(vtable_ptr + field) != 0;
+ // If this fails, the caller will show what field needs to be set.
+ assert(ok);
+ (void)ok;
+ }
+
uoffset_t StartStruct(size_t alignment) {
Align(alignment);
return GetSize();
@@ -678,15 +689,19 @@ class Verifier {
num_tables_(0), max_tables_(_max_tables)
{}
- // Verify any range within the buffer.
- bool Verify(const void *elem, size_t elem_len) const {
- bool ok = elem >= buf_ && elem <= end_ - elem_len;
+ // Central location where any verification failures register.
+ bool Check(bool ok) const {
#ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
assert(ok);
#endif
return ok;
}
+ // Verify any range within the buffer.
+ bool Verify(const void *elem, size_t elem_len) const {
+ return Check(elem >= buf_ && elem <= end_ - elem_len);
+ }
+
// Verify a range indicated by sizeof(T).
template bool Verify(const void *elem) const {
return Verify(elem, sizeof(T));
@@ -710,8 +725,8 @@ class Verifier {
const uint8_t *end;
return !str ||
(VerifyVector(reinterpret_cast(str), 1, &end) &&
- Verify(end, 1) && // Must have terminator
- *end == '\0'); // Terminating byte must be 0.
+ Verify(end, 1) && // Must have terminator
+ Check(*end == '\0')); // Terminating byte must be 0.
}
// Common code between vectors and strings.
@@ -762,11 +777,7 @@ class Verifier {
bool VerifyComplexity() {
depth_++;
num_tables_++;
- bool too_complex = depth_ > max_depth_ || num_tables_ > max_tables_;
- #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
- assert(!too_complex);
- #endif
- return !too_complex;
+ return Check(depth_ <= max_depth_ && num_tables_ <= max_tables_);
}
// Called at the end of a table to pop the depth count.
@@ -876,6 +887,14 @@ class Table {
return !field_offset || verifier.Verify(data_ + field_offset);
}
+ // VerifyField for required fields.
+ template bool VerifyFieldRequired(const Verifier &verifier,
+ voffset_t field) const {
+ auto field_offset = GetOptionalFieldOffset(field);
+ return verifier.Check(field_offset != 0) &&
+ verifier.Verify(data_ + field_offset);
+ }
+
private:
// private constructor & copy constructor: you obtain instances of this
// class by pointing to existing data only
diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h
index 4aa9a256c..0ab3ae151 100644
--- a/include/flatbuffers/idl.h
+++ b/include/flatbuffers/idl.h
@@ -184,10 +184,11 @@ struct Definition {
};
struct FieldDef : public Definition {
- FieldDef() : deprecated(false), padding(0), used(false) {}
+ FieldDef() : deprecated(false), required(false), padding(0), used(false) {}
Value value;
bool deprecated;
+ bool required;
size_t padding; // Bytes to always pad after this field.
bool used; // Used during JSON parsing to check for repeated fields.
};
diff --git a/java/com/google/flatbuffers/FlatBufferBuilder.java b/java/com/google/flatbuffers/FlatBufferBuilder.java
index 068981832..695a29a4a 100644
--- a/java/com/google/flatbuffers/FlatBufferBuilder.java
+++ b/java/com/google/flatbuffers/FlatBufferBuilder.java
@@ -242,6 +242,17 @@ public class FlatBufferBuilder {
return vtableloc;
}
+ // This checks a required field has been set in a given table that has
+ // just been constructed.
+ public void required(int table, int field) {
+ int table_start = bb.capacity() - table;
+ int vtable_start = table_start - bb.getInt(table_start);
+ boolean ok = bb.getShort(vtable_start + field) != 0;
+ // If this fails, the caller will show what field needs to be set.
+ if (!ok)
+ throw new AssertionError("FlatBuffers: field " + field + " must be set");
+ }
+
public void finish(int root_table) {
prep(minalign, SIZEOF_INT);
addOffset(root_table);
diff --git a/net/FlatBuffers/FlatBufferBuilder.cs b/net/FlatBuffers/FlatBufferBuilder.cs
index f4a6ca427..68225f93a 100644
--- a/net/FlatBuffers/FlatBufferBuilder.cs
+++ b/net/FlatBuffers/FlatBufferBuilder.cs
@@ -335,6 +335,19 @@ namespace FlatBuffers
return vtableloc;
}
+ // This checks a required field has been set in a given table that has
+ // just been constructed.
+ public void Required(int table, int field)
+ {
+ int table_start = _bb.Length - table;
+ int vtable_start = table_start - _bb.GetInt(table_start);
+ bool ok = _bb.GetShort(vtable_start + field) != 0;
+ // If this fails, the caller will show what field needs to be set.
+ if (!ok)
+ throw new InvalidOperationException("FlatBuffers: field " + field +
+ " must be set");
+ }
+
public void Finish(int rootTable)
{
Prep(_minAlign, sizeof(int));
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index e7497b784..8dfd0248f 100644
--- a/src/idl_gen_cpp.cpp
+++ b/src/idl_gen_cpp.cpp
@@ -225,7 +225,9 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
++it) {
auto &field = **it;
if (!field.deprecated) {
- code += prefix + "VerifyField<" + GenTypeSize(parser, field.value.type);
+ code += prefix + "VerifyField";
+ if (field.required) code += "Required";
+ code += "<" + GenTypeSize(parser, field.value.type);
code += ">(verifier, " + NumToString(field.value.offset);
code += " /* " + field.name + " */)";
switch (field.value.type.base_type) {
@@ -301,9 +303,19 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
code += " " + struct_def.name + "Builder &operator=(const ";
code += struct_def.name + "Builder &);\n";
code += " flatbuffers::Offset<" + struct_def.name;
- code += "> Finish() { return flatbuffers::Offset<" + struct_def.name;
+ code += "> Finish() {\n auto o = flatbuffers::Offset<" + struct_def.name;
code += ">(fbb_.EndTable(start_, ";
- code += NumToString(struct_def.fields.vec.size()) + ")); }\n};\n\n";
+ code += NumToString(struct_def.fields.vec.size()) + "));\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end();
+ ++it) {
+ auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code += " fbb_.Required(o, " + NumToString(field.value.offset);
+ code += "); // " + field.name + "\n";
+ }
+ }
+ code += " return o;\n }\n};\n\n";
// Generate a convenient CreateX function that uses the above builder
// to create a table in one go.
diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp
index 4c257403a..615db9240 100644
--- a/src/idl_gen_general.cpp
+++ b/src/idl_gen_general.cpp
@@ -554,8 +554,19 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
}
code += " public static int ";
code += FunctionStart(lang, 'E') + "nd" + struct_def.name;
- code += "(FlatBufferBuilder builder) { return builder.";
- code += FunctionStart(lang, 'E') + "ndObject(); }\n";
+ code += "(FlatBufferBuilder builder) {\n int o = builder.";
+ code += FunctionStart(lang, 'E') + "ndObject();\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end();
+ ++it) {
+ auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code += " builder." + FunctionStart(lang, 'R') + "equired(o, ";
+ code += NumToString(field.value.offset);
+ code += "); // " + field.name + "\n";
+ }
+ }
+ code += " return o;\n }\n";
if (parser.root_struct_def == &struct_def) {
code += " public static void ";
code += FunctionStart(lang, 'F') + "inish" + struct_def.name;
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index 316fb7173..e1f180cc0 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -376,6 +376,10 @@ void Parser::ParseField(StructDef &struct_def) {
field.deprecated = field.attributes.Lookup("deprecated") != nullptr;
if (field.deprecated && struct_def.fixed)
Error("can't deprecate fields in a struct");
+ field.required = field.attributes.Lookup("required") != nullptr;
+ if (field.required && (struct_def.fixed ||
+ IsScalar(field.value.type.base_type)))
+ Error("only non-scalar fields in tables may be 'required'");
auto nested = field.attributes.Lookup("nested_flatbuffer");
if (nested) {
if (nested->type.base_type != BASE_TYPE_STRING)
diff --git a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj
index 6d7a8def1..fd3f61f26 100644
--- a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj
+++ b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj
@@ -66,13 +66,13 @@
-
- Resources\monsterdata_test.bin
+
+ Resources\monsterdata_test.mon
PreserveNewest
-