mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-07 22:03:40 +00:00
Merge branch 'master' of https://github.com/google/flatbuffers
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "flatbuffers/util.h"
|
||||
#include <limits>
|
||||
|
||||
static void Error(const std::string &err, bool usage = false,
|
||||
bool show_exe_name = true);
|
||||
|
||||
@@ -274,16 +274,24 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
|
||||
// Generate field id constants.
|
||||
if (struct_def.fields.vec.size() > 0) {
|
||||
code += " enum {\n";
|
||||
bool is_first_field = true; // track the first field that's not deprecated
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (!field.deprecated) { // Deprecated fields won't be accessible.
|
||||
if (!is_first_field) {
|
||||
// Add trailing comma and newline to previous element. Don't add trailing comma to
|
||||
// last element since older versions of gcc complain about this.
|
||||
code += ",\n";
|
||||
} else {
|
||||
is_first_field = false;
|
||||
}
|
||||
code += " " + GenFieldOffsetName(field) + " = ";
|
||||
code += NumToString(field.value.offset) + ",\n";
|
||||
code += NumToString(field.value.offset);
|
||||
}
|
||||
}
|
||||
code += " };\n";
|
||||
code += "\n };\n";
|
||||
}
|
||||
// Generate the accessors.
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
@@ -520,15 +528,32 @@ static void GenTable(const Parser &parser, StructDef &struct_def,
|
||||
}
|
||||
|
||||
static void GenPadding(const FieldDef &field,
|
||||
const std::function<void (int bits)> &f) {
|
||||
std::string &code,
|
||||
int &padding_id,
|
||||
const std::function<void (int bits, std::string &code, int &padding_id)> &f) {
|
||||
if (field.padding) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (static_cast<int>(field.padding) & (1 << i))
|
||||
f((1 << i) * 8);
|
||||
f((1 << i) * 8, code, padding_id);
|
||||
assert(!(field.padding & ~0xF));
|
||||
}
|
||||
}
|
||||
|
||||
static void PaddingDefinition(int bits, std::string &code, int &padding_id) {
|
||||
code += " int" + NumToString(bits) +
|
||||
"_t __padding" + NumToString(padding_id++) + ";\n";
|
||||
}
|
||||
|
||||
static void PaddingDeclaration(int bits, std::string &code, int &padding_id) {
|
||||
(void)bits;
|
||||
code += " (void)__padding" + NumToString(padding_id++) + ";";
|
||||
}
|
||||
|
||||
static void PaddingInitializer(int bits, std::string &code, int &padding_id) {
|
||||
(void)bits;
|
||||
code += ", __padding" + NumToString(padding_id++) + "(0)";
|
||||
}
|
||||
|
||||
// Generate an accessor struct with constructor for a flatbuffers struct.
|
||||
static void GenStruct(const Parser &parser, StructDef &struct_def,
|
||||
std::string *code_ptr) {
|
||||
@@ -550,10 +575,7 @@ static void GenStruct(const Parser &parser, StructDef &struct_def,
|
||||
auto &field = **it;
|
||||
code += " " + GenTypeGet(parser, field.value.type, " ", "", " ", false);
|
||||
code += field.name + "_;\n";
|
||||
GenPadding(field, [&code, &padding_id](int bits) {
|
||||
code += " int" + NumToString(bits) +
|
||||
"_t __padding" + NumToString(padding_id++) + ";\n";
|
||||
});
|
||||
GenPadding(field, code, padding_id, PaddingDefinition);
|
||||
}
|
||||
|
||||
// Generate a constructor that takes all fields as arguments.
|
||||
@@ -581,21 +603,16 @@ static void GenStruct(const Parser &parser, StructDef &struct_def,
|
||||
} else {
|
||||
code += "_" + field.name + ")";
|
||||
}
|
||||
GenPadding(field, [&code, &padding_id](int bits) {
|
||||
(void)bits;
|
||||
code += ", __padding" + NumToString(padding_id++) + "(0)";
|
||||
});
|
||||
GenPadding(field, code, padding_id, PaddingInitializer);
|
||||
}
|
||||
|
||||
code += " {";
|
||||
padding_id = 0;
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
GenPadding(field, [&code, &padding_id](int bits) {
|
||||
(void)bits;
|
||||
code += " (void)__padding" + NumToString(padding_id++) + ";";
|
||||
});
|
||||
GenPadding(field, code, padding_id, PaddingDeclaration);
|
||||
}
|
||||
code += " }\n\n";
|
||||
|
||||
@@ -649,6 +666,12 @@ void CloseNestedNameSpaces(Namespace *ns, std::string *code_ptr) {
|
||||
|
||||
} // namespace cpp
|
||||
|
||||
struct IsAlnum {
|
||||
bool operator()(char c) {
|
||||
return !isalnum(c);
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate through all definitions we haven't generate code for (enums, structs,
|
||||
// and tables) and output them to a single file.
|
||||
std::string GenerateCPP(const Parser &parser,
|
||||
@@ -719,7 +742,7 @@ std::string GenerateCPP(const Parser &parser,
|
||||
include_guard_ident.erase(
|
||||
std::remove_if(include_guard_ident.begin(),
|
||||
include_guard_ident.end(),
|
||||
[](char c) { return !isalnum(c); }),
|
||||
IsAlnum()),
|
||||
include_guard_ident.end());
|
||||
std::string include_guard = "FLATBUFFERS_GENERATED_" + include_guard_ident;
|
||||
include_guard += "_";
|
||||
|
||||
@@ -876,7 +876,27 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// generate object accessors if is nested_flatbuffer
|
||||
auto nested = field.attributes.Lookup("nested_flatbuffer");
|
||||
if (nested) {
|
||||
auto nested_qualified_name =
|
||||
parser.namespaces_.back()->GetFullyQualifiedName(nested->constant);
|
||||
auto nested_type = parser.structs_.Lookup(nested_qualified_name);
|
||||
auto nested_type_name = WrapInNameSpace(parser, *nested_type);
|
||||
auto nestedMethodName = MakeCamel(field.name, lang.first_camel_upper)
|
||||
+ "As" + nested_type_name;
|
||||
auto getNestedMethodName = nestedMethodName;
|
||||
if (lang.language == IDLOptions::kCSharp) {
|
||||
getNestedMethodName = "Get" + nestedMethodName;
|
||||
}
|
||||
code += " public " + nested_type_name + " ";
|
||||
code += nestedMethodName + "() { return ";
|
||||
code += getNestedMethodName + "(new " + nested_type_name + "()); }\n";
|
||||
code += " public " + nested_type_name + " " + getNestedMethodName;
|
||||
code += "(" + nested_type_name + " obj) { ";
|
||||
code += "int o = __offset(" + NumToString(field.value.offset) +"); ";
|
||||
code += "return o != 0 ? obj.__init(__indirect(__vector(o)), bb) : null; }\n";
|
||||
}
|
||||
// generate mutators for scalar fields or vectors of scalars
|
||||
if (parser.opts.mutable_buffer) {
|
||||
auto underlying_type = field.value.type.base_type == BASE_TYPE_VECTOR
|
||||
|
||||
@@ -201,6 +201,14 @@ CheckedError Parser::ParseHexNum(int nibbles, int64_t *val) {
|
||||
return NoError();
|
||||
}
|
||||
|
||||
CheckedError Parser::SkipByteOrderMark() {
|
||||
if (static_cast<unsigned char>(*cursor_) != 0xef) return NoError();
|
||||
cursor_++;
|
||||
if (static_cast<unsigned char>(*cursor_++) != 0xbb) return Error("invalid utf-8 byte order mark");
|
||||
if (static_cast<unsigned char>(*cursor_++) != 0xbf) return Error("invalid utf-8 byte order mark");
|
||||
return NoError();
|
||||
}
|
||||
|
||||
CheckedError Parser::Next() {
|
||||
doc_comment_.clear();
|
||||
bool seen_newline = false;
|
||||
@@ -1118,6 +1126,33 @@ CheckedError Parser::StartStruct(const std::string &name, StructDef **dest) {
|
||||
return NoError();
|
||||
}
|
||||
|
||||
CheckedError Parser::CheckClash(std::vector<FieldDef*> &fields,
|
||||
StructDef *struct_def,
|
||||
const char *suffix,
|
||||
BaseType basetype) {
|
||||
auto len = strlen(suffix);
|
||||
for (auto it = fields.begin(); it != fields.end(); ++it) {
|
||||
auto &fname = (*it)->name;
|
||||
if (fname.length() > len &&
|
||||
fname.compare(fname.length() - len, len, suffix) == 0 &&
|
||||
(*it)->value.type.base_type != BASE_TYPE_UTYPE) {
|
||||
auto field = struct_def->fields.Lookup(
|
||||
fname.substr(0, fname.length() - len));
|
||||
if (field && field->value.type.base_type == basetype)
|
||||
return Error("Field " + fname +
|
||||
" would clash with generated functions for field " +
|
||||
field->name);
|
||||
}
|
||||
}
|
||||
return NoError();
|
||||
}
|
||||
|
||||
static bool compareFieldDefs(const FieldDef *a, const FieldDef *b) {
|
||||
auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
|
||||
auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
|
||||
return a_id < b_id;
|
||||
}
|
||||
|
||||
CheckedError Parser::ParseDecl() {
|
||||
std::vector<std::string> dc = doc_comment_;
|
||||
bool fixed = Is(kTokenStruct);
|
||||
@@ -1160,12 +1195,7 @@ CheckedError Parser::ParseDecl() {
|
||||
"either all fields or no fields must have an 'id' attribute");
|
||||
// Simply sort by id, then the fields are the same as if no ids had
|
||||
// been specified.
|
||||
std::sort(fields.begin(), fields.end(),
|
||||
[](const FieldDef *a, const FieldDef *b) -> bool {
|
||||
auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
|
||||
auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
|
||||
return a_id < b_id;
|
||||
});
|
||||
std::sort(fields.begin(), fields.end(), compareFieldDefs);
|
||||
// Verify we have a contiguous set, and reassign vtable offsets.
|
||||
for (int i = 0; i < static_cast<int>(fields.size()); i++) {
|
||||
if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str()))
|
||||
@@ -1175,34 +1205,13 @@ CheckedError Parser::ParseDecl() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check that no identifiers clash with auto generated fields.
|
||||
// This is not an ideal situation, but should occur very infrequently,
|
||||
// and allows us to keep using very readable names for type & length fields
|
||||
// without inducing compile errors.
|
||||
auto CheckClash = [&fields, &struct_def, this](const char *suffix,
|
||||
BaseType basetype) -> CheckedError {
|
||||
auto len = strlen(suffix);
|
||||
for (auto it = fields.begin(); it != fields.end(); ++it) {
|
||||
auto &fname = (*it)->name;
|
||||
if (fname.length() > len &&
|
||||
fname.compare(fname.length() - len, len, suffix) == 0 &&
|
||||
(*it)->value.type.base_type != BASE_TYPE_UTYPE) {
|
||||
auto field = struct_def->fields.Lookup(
|
||||
fname.substr(0, fname.length() - len));
|
||||
if (field && field->value.type.base_type == basetype)
|
||||
return Error("Field " + fname +
|
||||
" would clash with generated functions for field " +
|
||||
field->name);
|
||||
}
|
||||
}
|
||||
return NoError();
|
||||
};
|
||||
ECHECK(CheckClash("_type", BASE_TYPE_UNION));
|
||||
ECHECK(CheckClash("Type", BASE_TYPE_UNION));
|
||||
ECHECK(CheckClash("_length", BASE_TYPE_VECTOR));
|
||||
ECHECK(CheckClash("Length", BASE_TYPE_VECTOR));
|
||||
ECHECK(CheckClash("_byte_vector", BASE_TYPE_STRING));
|
||||
ECHECK(CheckClash("ByteVector", BASE_TYPE_STRING));
|
||||
|
||||
ECHECK(CheckClash(fields, struct_def, "_type", BASE_TYPE_UNION));
|
||||
ECHECK(CheckClash(fields, struct_def, "Type", BASE_TYPE_UNION));
|
||||
ECHECK(CheckClash(fields, struct_def, "_length", BASE_TYPE_VECTOR));
|
||||
ECHECK(CheckClash(fields, struct_def, "Length", BASE_TYPE_VECTOR));
|
||||
ECHECK(CheckClash(fields, struct_def, "_byte_vector", BASE_TYPE_STRING));
|
||||
ECHECK(CheckClash(fields, struct_def, "ByteVector", BASE_TYPE_STRING));
|
||||
EXPECT('}');
|
||||
return NoError();
|
||||
}
|
||||
@@ -1244,6 +1253,10 @@ CheckedError Parser::ParseNamespace() {
|
||||
return NoError();
|
||||
}
|
||||
|
||||
static bool compareEnumVals(const EnumVal *a, const EnumVal* b) {
|
||||
return a->value < b->value;
|
||||
}
|
||||
|
||||
// Best effort parsing of .proto declarations, with the aim to turn them
|
||||
// in the closest corresponding FlatBuffer equivalent.
|
||||
// We parse everything as identifiers instead of keywords, since we don't
|
||||
@@ -1294,9 +1307,8 @@ CheckedError Parser::ParseProtoDecl() {
|
||||
if (Is(';')) NEXT();
|
||||
// Protobuf allows them to be specified in any order, so sort afterwards.
|
||||
auto &v = enum_def->vals.vec;
|
||||
std::sort(v.begin(), v.end(), [](const EnumVal *a, const EnumVal *b) {
|
||||
return a->value < b->value;
|
||||
});
|
||||
std::sort(v.begin(), v.end(), compareEnumVals);
|
||||
|
||||
// Temp: remove any duplicates, as .fbs files can't handle them.
|
||||
for (auto it = v.begin(); it != v.end(); ) {
|
||||
if (it != v.begin() && it[0]->value == it[-1]->value) it = v.erase(it);
|
||||
@@ -1593,6 +1605,7 @@ CheckedError Parser::DoParse(const char *source, const char **include_paths,
|
||||
builder_.Clear();
|
||||
// Start with a blank namespace just in case this file doesn't have one.
|
||||
namespaces_.push_back(new Namespace());
|
||||
ECHECK(SkipByteOrderMark());
|
||||
NEXT();
|
||||
// Includes must come before type declarations:
|
||||
for (;;) {
|
||||
@@ -1744,11 +1757,14 @@ std::set<std::string> Parser::GetIncludedFilesRecursive(
|
||||
|
||||
// Schema serialization functionality:
|
||||
|
||||
template<typename T> bool compareName(const T* a, const T* b) {
|
||||
return a->name < b->name;
|
||||
}
|
||||
|
||||
template<typename T> void AssignIndices(const std::vector<T *> &defvec) {
|
||||
// Pre-sort these vectors, such that we can set the correct indices for them.
|
||||
auto vec = defvec;
|
||||
std::sort(vec.begin(), vec.end(),
|
||||
[](const T *a, const T *b) { return a->name < b->name; });
|
||||
std::sort(vec.begin(), vec.end(), compareName<T>);
|
||||
for (int i = 0; i < static_cast<int>(vec.size()); i++) vec[i]->index = i;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user