Add support for fixed-size arrays (#5313)

This commit is contained in:
svenk177
2019-06-18 00:15:13 +02:00
committed by Wouter van Oortmerssen
parent 0d2cebccfe
commit e635141d5b
40 changed files with 2113 additions and 220 deletions

View File

@@ -697,6 +697,13 @@ class CppGenerator : public BaseGenerator {
bool user_facing_type) {
if (IsScalar(type.base_type)) {
return GenTypeBasic(type, user_facing_type) + afterbasic;
} else if (IsArray(type)) {
auto element_type = type.VectorType();
return beforeptr +
(IsScalar(element_type.base_type)
? GenTypeBasic(element_type, user_facing_type)
: GenTypePointer(element_type)) +
afterptr;
} else {
return beforeptr + GenTypePointer(type) + afterptr;
}
@@ -2689,7 +2696,8 @@ class CppGenerator : public BaseGenerator {
static void PaddingInitializer(int bits, std::string *code_ptr, int *id) {
(void)bits;
*code_ptr += ",\n padding" + NumToString((*id)++) + "__(0)";
if (*code_ptr != "") *code_ptr += ",\n ";
*code_ptr += "padding" + NumToString((*id)++) + "__(0)";
}
static void PaddingNoop(int bits, std::string *code_ptr, int *id) {
@@ -2717,10 +2725,14 @@ class CppGenerator : public BaseGenerator {
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
const auto &field = **it;
code_.SetValue("FIELD_TYPE",
GenTypeGet(field.value.type, " ", "", " ", false));
const auto &field_type = field.value.type;
code_.SetValue("FIELD_TYPE", GenTypeGet(field_type, " ", "", " ", false));
code_.SetValue("FIELD_NAME", Name(field));
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}_;";
code_.SetValue("ARRAY",
IsArray(field_type)
? "[" + NumToString(field_type.fixed_length) + "]"
: "");
code_ += (" {{FIELD_TYPE}}{{FIELD_NAME}}_{{ARRAY}};");
if (field.padding) {
std::string padding;
@@ -2745,33 +2757,40 @@ class CppGenerator : public BaseGenerator {
// Generate a default constructor.
code_ += " {{STRUCT_NAME}}() {";
code_ += " memset(static_cast<void *>(this), 0, sizeof({{STRUCT_NAME}}));";
code_ +=
" memset(static_cast<void *>(this), 0, sizeof({{STRUCT_NAME}}));";
code_ += " }";
// Generate a constructor that takes all fields as arguments.
// Generate a constructor that takes all fields as arguments,
// excluding arrays
std::string arg_list;
std::string init_list;
padding_id = 0;
auto first = struct_def.fields.vec.begin();
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
const auto &field = **it;
if (IsArray(field.value.type)) {
first++;
continue;
}
const auto member_name = Name(field) + "_";
const auto arg_name = "_" + Name(field);
const auto arg_type =
GenTypeGet(field.value.type, " ", "const ", " &", true);
if (it != struct_def.fields.vec.begin()) {
arg_list += ", ";
init_list += ",\n ";
}
if (it != first) { arg_list += ", "; }
arg_list += arg_type;
arg_list += arg_name;
init_list += member_name;
if (IsScalar(field.value.type.base_type)) {
auto type = GenUnderlyingCast(field, false, arg_name);
init_list += "(flatbuffers::EndianScalar(" + type + "))";
} else {
init_list += "(" + arg_name + ")";
if (!IsArray(field.value.type)) {
if (it != first && init_list != "") { init_list += ",\n "; }
init_list += member_name;
if (IsScalar(field.value.type.base_type)) {
auto type = GenUnderlyingCast(field, false, arg_name);
init_list += "(flatbuffers::EndianScalar(" + type + "))";
} else {
init_list += "(" + arg_name + ")";
}
}
if (field.padding) {
GenPadding(field, &init_list, &padding_id, PaddingInitializer);
@@ -2781,12 +2800,21 @@ class CppGenerator : public BaseGenerator {
if (!arg_list.empty()) {
code_.SetValue("ARG_LIST", arg_list);
code_.SetValue("INIT_LIST", init_list);
code_ += " {{STRUCT_NAME}}({{ARG_LIST}})";
code_ += " : {{INIT_LIST}} {";
if (!init_list.empty()) {
code_ += " {{STRUCT_NAME}}({{ARG_LIST}})";
code_ += " : {{INIT_LIST}} {";
} else {
code_ += " {{STRUCT_NAME}}({{ARG_LIST}}) {";
}
padding_id = 0;
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
const auto &field = **it;
if (IsArray(field.value.type)) {
const auto &member = Name(field) + "_";
code_ +=
" std::memset(" + member + ", 0, sizeof(" + member + "));";
}
if (field.padding) {
std::string padding;
GenPadding(field, &padding, &padding_id, PaddingNoop);
@@ -2802,7 +2830,9 @@ class CppGenerator : public BaseGenerator {
it != struct_def.fields.vec.end(); ++it) {
const auto &field = **it;
auto field_type = GenTypeGet(field.value.type, " ", "const ", " &", true);
auto field_type = GenTypeGet(field.value.type, " ",
IsArray(field.value.type) ? "" : "const ",
IsArray(field.value.type) ? "" : " &", true);
auto is_scalar = IsScalar(field.value.type.base_type);
auto member = Name(field) + "_";
auto value =
@@ -2813,12 +2843,29 @@ class CppGenerator : public BaseGenerator {
code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, value));
GenComment(field.doc_comment, " ");
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
code_ += " return {{FIELD_VALUE}};";
code_ += " }";
// Generate a const accessor function.
if (IsArray(field.value.type)) {
auto underlying = GenTypeGet(field.value.type, "", "", "", false);
code_ += " const flatbuffers::Array<" + field_type + ", " +
NumToString(field.value.type.fixed_length) + "> *" +
"{{FIELD_NAME}}() const {";
code_ += " return reinterpret_cast<const flatbuffers::Array<" +
field_type + ", " +
NumToString(field.value.type.fixed_length) +
"> *>({{FIELD_VALUE}});";
code_ += " }";
} else {
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
code_ += " return {{FIELD_VALUE}};";
code_ += " }";
}
// Generate a mutable accessor function.
if (parser_.opts.mutable_buffer) {
auto mut_field_type = GenTypeGet(field.value.type, " ", "", " &", true);
auto mut_field_type =
GenTypeGet(field.value.type, " ", "",
IsArray(field.value.type) ? "" : " &", true);
code_.SetValue("FIELD_TYPE", mut_field_type);
if (is_scalar) {
code_.SetValue("ARG", GenTypeBasic(field.value.type, true));
@@ -2830,9 +2877,19 @@ class CppGenerator : public BaseGenerator {
" flatbuffers::WriteScalar(&{{FIELD_NAME}}_, "
"{{FIELD_VALUE}});";
code_ += " }";
} else if (IsArray(field.value.type)) {
auto underlying = GenTypeGet(field.value.type, "", "", "", false);
code_ += " flatbuffers::Array<" + mut_field_type + ", " +
NumToString(field.value.type.fixed_length) +
"> *" + "mutable_{{FIELD_NAME}}() {";
code_ += " return reinterpret_cast<flatbuffers::Array<" +
mut_field_type + ", " +
NumToString(field.value.type.fixed_length) +
"> *>({{FIELD_VALUE}});";
code_ += " }";
} else {
code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
code_ += " return {{FIELD_NAME}}_;";
code_ += " return {{FIELD_VALUE}};";
code_ += " }";
}
}

View File

@@ -254,10 +254,6 @@ class GeneralGenerator : public BaseGenerator {
: "";
}
static bool IsEnum(const Type &type) {
return type.enum_def != nullptr && IsInteger(type.base_type);
}
std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
// clang-format off
static const char * const java_typename[] = {
@@ -312,7 +308,10 @@ class GeneralGenerator : public BaseGenerator {
}
std::string GenTypeGet(const Type &type) const {
return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
return IsScalar(type.base_type)
? GenTypeBasic(type)
: (IsArray(type) ? GenTypeGet(type.VectorType())
: GenTypePointer(type));
}
// Find the destination type the user wants to receive the value in (e.g.
@@ -325,6 +324,7 @@ class GeneralGenerator : public BaseGenerator {
case BASE_TYPE_UCHAR: return Type(BASE_TYPE_INT);
case BASE_TYPE_USHORT: return Type(BASE_TYPE_INT);
case BASE_TYPE_UINT: return Type(BASE_TYPE_LONG);
case BASE_TYPE_ARRAY:
case BASE_TYPE_VECTOR:
if (vectorelem) return DestinationType(type.VectorType(), vectorelem);
FLATBUFFERS_FALLTHROUGH(); // else fall thru
@@ -378,7 +378,7 @@ class GeneralGenerator : public BaseGenerator {
// Casts necessary to correctly read serialized data
std::string DestinationCast(const Type &type) const {
if (type.base_type == BASE_TYPE_VECTOR) {
if (IsSeries(type)) {
return DestinationCast(type.VectorType());
} else {
switch (lang_.language) {
@@ -405,7 +405,7 @@ class GeneralGenerator : public BaseGenerator {
// directly cast an Enum to its underlying type, which is essential before
// putting it onto the buffer.
std::string SourceCast(const Type &type, bool castFromDest) const {
if (type.base_type == BASE_TYPE_VECTOR) {
if (IsSeries(type)) {
return SourceCast(type.VectorType(), castFromDest);
} else {
switch (lang_.language) {
@@ -602,6 +602,7 @@ class GeneralGenerator : public BaseGenerator {
case BASE_TYPE_STRUCT: return lang_.accessor_prefix + "__struct";
case BASE_TYPE_UNION: return lang_.accessor_prefix + "__union";
case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
case BASE_TYPE_ARRAY: return GenGetter(type.VectorType());
default: {
std::string getter =
lang_.accessor_prefix + "bb." + FunctionStart('G') + "et";
@@ -656,20 +657,36 @@ class GeneralGenerator : public BaseGenerator {
// Recursively generate arguments for a constructor, to deal with nested
// structs.
void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
const char *nameprefix) const {
const char *nameprefix, size_t array_count = 0) const {
std::string &code = *code_ptr;
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto &field = **it;
if (IsStruct(field.value.type)) {
const auto &field_type = field.value.type;
const auto array_field = IsArray(field_type);
const auto &type = array_field ? field_type.VectorType()
: DestinationType(field_type, false);
const auto array_cnt = array_field ? (array_count + 1) : array_count;
if (IsStruct(type)) {
// Generate arguments for a struct inside a struct. To ensure names
// don't clash, and to make it obvious these arguments are constructing
// a nested struct, prefix the name with the field name.
GenStructArgs(*field.value.type.struct_def, code_ptr,
(nameprefix + (field.name + "_")).c_str());
GenStructArgs(*field_type.struct_def, code_ptr,
(nameprefix + (field.name + "_")).c_str(), array_cnt);
} else {
code += ", ";
code += GenTypeBasic(DestinationType(field.value.type, false));
code += GenTypeBasic(type);
if (lang_.language == IDLOptions::kJava) {
for (size_t i = 0; i < array_cnt; i++) code += "[]";
} else if (lang_.language == IDLOptions::kCSharp) {
if (array_cnt > 0) {
code += "[";
for (size_t i = 1; i < array_cnt; i++) code += ",";
code += "]";
}
} else {
FLATBUFFERS_ASSERT(0);
}
code += " ";
code += nameprefix;
code += MakeCamel(field.name, lang_.first_camel_upper);
@@ -681,29 +698,67 @@ class GeneralGenerator : public BaseGenerator {
// builder.putType(name);
// and insert manual padding.
void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
const char *nameprefix) const {
const char *nameprefix, size_t index = 0,
bool in_array = false) const {
std::string &code = *code_ptr;
code += " builder." + FunctionStart('P') + "rep(";
std::string indent((index + 1) * 2, ' ');
code += indent + " builder." + FunctionStart('P') + "rep(";
code += NumToString(struct_def.minalign) + ", ";
code += NumToString(struct_def.bytesize) + ");\n";
for (auto it = struct_def.fields.vec.rbegin();
it != struct_def.fields.vec.rend(); ++it) {
auto &field = **it;
const auto &field_type = field.value.type;
if (field.padding) {
code += " builder." + FunctionStart('P') + "ad(";
code += indent + " builder." + FunctionStart('P') + "ad(";
code += NumToString(field.padding) + ");\n";
}
if (IsStruct(field.value.type)) {
GenStructBody(*field.value.type.struct_def, code_ptr,
(nameprefix + (field.name + "_")).c_str());
if (IsStruct(field_type)) {
GenStructBody(*field_type.struct_def, code_ptr,
(nameprefix + (field.name + "_")).c_str(), index,
in_array);
} else {
code += " builder." + FunctionStart('P') + "ut";
code += GenMethod(field.value.type) + "(";
code += SourceCast(field.value.type);
auto argname =
nameprefix + MakeCamel(field.name, lang_.first_camel_upper);
code += argname;
code += ");\n";
const auto &type =
IsArray(field_type) ? field_type.VectorType() : field_type;
const auto index_var = "_idx" + NumToString(index);
if (IsArray(field_type)) {
code += indent + " for (int " + index_var + " = ";
code += NumToString(field_type.fixed_length);
code += "; " + index_var + " > 0; " + index_var + "--) {\n";
in_array = true;
}
if (IsStruct(type)) {
GenStructBody(*field_type.struct_def, code_ptr,
(nameprefix + (field.name + "_")).c_str(), index + 1,
in_array);
} else {
code += IsArray(field_type) ? " " : "";
code += indent + " builder." + FunctionStart('P') + "ut";
code += GenMethod(type) + "(";
code += SourceCast(type);
auto argname =
nameprefix + MakeCamel(field.name, lang_.first_camel_upper);
code += argname;
size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
if (lang_.language == IDLOptions::kJava) {
for (size_t i = 0; in_array && i < array_cnt; i++) {
code += "[_idx" + NumToString(i) + "-1]";
}
} else if (lang_.language == IDLOptions::kCSharp) {
if (array_cnt > 0) {
code += "[";
for (size_t i = 0; in_array && i < array_cnt; i++) {
code += "_idx" + NumToString(i) + "-1";
if (i != (array_cnt - 1)) code += ",";
}
code += "]";
}
} else {
FLATBUFFERS_ASSERT(0);
}
code += ");\n";
}
if (IsArray(field_type)) { code += indent + " }\n"; }
}
}
}
@@ -924,9 +979,11 @@ class GeneralGenerator : public BaseGenerator {
// Most field accessors need to retrieve and test the field offset first,
// this is the prefix code for that:
auto offset_prefix = " { int o = " + lang_.accessor_prefix + "__offset(" +
NumToString(field.value.offset) +
"); return o != 0 ? ";
auto offset_prefix =
IsArray(field.value.type)
? " { return "
: (" { int o = " + lang_.accessor_prefix + "__offset(" +
NumToString(field.value.offset) + "); return o != 0 ? ");
// Generate the accessors that don't do object reuse.
if (field.value.type.base_type == BASE_TYPE_STRUCT) {
// Calls the accessor that takes an accessor object with a new object.
@@ -1017,6 +1074,7 @@ class GeneralGenerator : public BaseGenerator {
code += offset_prefix + getter + "(o + " + lang_.accessor_prefix;
code += "bb_pos) : null";
break;
case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
case BASE_TYPE_VECTOR: {
auto vectortype = field.value.type.VectorType();
if (vectortype.base_type == BASE_TYPE_UNION &&
@@ -1043,8 +1101,13 @@ class GeneralGenerator : public BaseGenerator {
} else {
code += body;
}
auto index = lang_.accessor_prefix + "__vector(o) + j * " +
NumToString(InlineSize(vectortype));
auto index = lang_.accessor_prefix;
if (IsArray(field.value.type)) {
index += "bb_pos + " + NumToString(field.value.offset) + " + ";
} else {
index += "__vector(o) + ";
}
index += "j * " + NumToString(InlineSize(vectortype));
if (vectortype.base_type == BASE_TYPE_STRUCT) {
code += vectortype.struct_def->fixed
? index
@@ -1055,13 +1118,16 @@ class GeneralGenerator : public BaseGenerator {
} else {
code += index;
}
code += ")" + dest_mask + " : ";
code += ")" + dest_mask;
if (!IsArray(field.value.type)) {
code += " : ";
code +=
field.value.type.element == BASE_TYPE_BOOL
? "false"
: (IsScalar(field.value.type.element) ? default_cast + "0"
: "null");
}
code +=
field.value.type.element == BASE_TYPE_BOOL
? "false"
: (IsScalar(field.value.type.element) ? default_cast + "0"
: "null");
break;
}
case BASE_TYPE_UNION:
@@ -1215,9 +1281,9 @@ class GeneralGenerator : public BaseGenerator {
}
// 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
? field.value.type.VectorType()
: field.value.type;
auto is_series = (IsSeries(field.value.type));
const auto &underlying_type =
is_series ? field.value.type.VectorType() : field.value.type;
// Boolean parameters have to be explicitly converted to byte
// representation.
auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
@@ -1226,21 +1292,21 @@ class GeneralGenerator : public BaseGenerator {
auto mutator_prefix = MakeCamel("mutate", lang_.first_camel_upper);
// A vector mutator also needs the index of the vector element it should
// mutate.
auto mutator_params =
(field.value.type.base_type == BASE_TYPE_VECTOR ? "(int j, "
: "(") +
GenTypeNameDest(underlying_type) + " " + field.name + ") { ";
auto mutator_params = (is_series ? "(int j, " : "(") +
GenTypeNameDest(underlying_type) + " " +
field.name + ") { ";
auto setter_index =
field.value.type.base_type == BASE_TYPE_VECTOR
? lang_.accessor_prefix + "__vector(o) + j * " +
NumToString(InlineSize(underlying_type))
is_series
? lang_.accessor_prefix +
(IsArray(field.value.type)
? "bb_pos + " + NumToString(field.value.offset)
: "__vector(o)") +
+" + j * " + NumToString(InlineSize(underlying_type))
: (struct_def.fixed
? lang_.accessor_prefix + "bb_pos + " +
NumToString(field.value.offset)
: "o + " + lang_.accessor_prefix + "bb_pos");
if (IsScalar(field.value.type.base_type) ||
(field.value.type.base_type == BASE_TYPE_VECTOR &&
IsScalar(field.value.type.VectorType().base_type))) {
if (IsScalar(underlying_type.base_type)) {
code += " public ";
code += struct_def.fixed ? "void " : lang_.bool_type;
code += mutator_prefix + MakeCamel(field.name, true);

View File

@@ -42,6 +42,7 @@ std::string GenNativeType(BaseType type) {
case BASE_TYPE_FLOAT:
case BASE_TYPE_DOUBLE: return "number";
case BASE_TYPE_STRING: return "string";
case BASE_TYPE_ARRAY: return "array";
default: return "";
}
}
@@ -70,6 +71,7 @@ std::string GenType(const Type &type) {
return GenTypeRef(type.enum_def);
}
switch (type.base_type) {
case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
case BASE_TYPE_VECTOR: {
std::string typeline;
typeline.append("\"type\" : \"array\", \"items\" : { ");
@@ -156,8 +158,16 @@ class JsonSchemaGenerator : public BaseGenerator {
const auto &properties = structure->fields.vec;
for (auto prop = properties.cbegin(); prop != properties.cend(); ++prop) {
const auto &property = *prop;
std::string typeLine(" \"" + property->name + "\" : { " +
GenType(property->value.type) + " }");
std::string arrayInfo = "";
if (IsArray(property->value.type)) {
arrayInfo = ",\n \"minItems\": " +
NumToString(property->value.type.fixed_length) +
",\n \"maxItems\": " +
NumToString(property->value.type.fixed_length);
}
std::string typeLine =
" \"" + property->name + "\" : {\n" + " " +
GenType(property->value.type) + arrayInfo + "\n }";
if (property != properties.back()) { typeLine.append(","); }
code_ += typeLine;
}

View File

@@ -224,6 +224,30 @@ class PythonGenerator : public BaseGenerator {
code += "\n" + Indent + Indent + "return obj\n\n";
}
// Get the value of a fixed size array.
void GetArrayOfStruct(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) {
std::string &code = *code_ptr;
const auto vec_type = field.value.type.VectorType();
GenReceiver(struct_def, code_ptr);
code += MakeCamel(NormalizedName(field));
if (IsStruct(vec_type)) {
code += "(self, obj, i):\n";
code += Indent + Indent + "obj.Init(self._tab.Bytes, self._tab.Pos + ";
code += NumToString(field.value.offset) + " + i * ";
code += NumToString(InlineSize(vec_type));
code += ")\n" + Indent + Indent + "return obj\n\n";
} else {
auto getter = GenGetter(vec_type);
code += "(self): return [" + getter;
code += "self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(";
code += NumToString(field.value.offset) + " + i * ";
code += NumToString(InlineSize(vec_type));
code += ")) for i in range(";
code += NumToString(field.value.type.fixed_length) + ")]\n";
}
}
// Get a struct by initializing an existing struct.
// Specific to Table.
void GetStructFieldOfTable(const StructDef &struct_def,
@@ -380,12 +404,16 @@ class PythonGenerator : public BaseGenerator {
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto &field = **it;
if (IsStruct(field.value.type)) {
const auto &field_type = field.value.type;
const auto &type =
IsArray(field_type) ? field_type.VectorType() : field_type;
if (IsStruct(type)) {
// Generate arguments for a struct inside a struct. To ensure names
// don't clash, and to make it obvious these arguments are constructing
// a nested struct, prefix the name with the field name.
StructBuilderArgs(*field.value.type.struct_def,
(nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
StructBuilderArgs(*field_type.struct_def,
(nameprefix + (NormalizedName(field) + "_")).c_str(),
code_ptr);
} else {
std::string &code = *code_ptr;
code += std::string(", ") + nameprefix;
@@ -402,22 +430,50 @@ class PythonGenerator : public BaseGenerator {
// Recursively generate struct construction statements and instert manual
// padding.
void StructBuilderBody(const StructDef &struct_def,
const char *nameprefix, std::string *code_ptr) {
void StructBuilderBody(const StructDef &struct_def, const char *nameprefix,
std::string *code_ptr, size_t index = 0,
bool in_array = false) {
std::string &code = *code_ptr;
code += " builder.Prep(" + NumToString(struct_def.minalign) + ", ";
std::string indent(index * 4, ' ');
code +=
indent + " builder.Prep(" + NumToString(struct_def.minalign) + ", ";
code += NumToString(struct_def.bytesize) + ")\n";
for (auto it = struct_def.fields.vec.rbegin();
it != struct_def.fields.vec.rend(); ++it) {
auto &field = **it;
const auto &field_type = field.value.type;
const auto &type =
IsArray(field_type) ? field_type.VectorType() : field_type;
if (field.padding)
code += " builder.Pad(" + NumToString(field.padding) + ")\n";
if (IsStruct(field.value.type)) {
StructBuilderBody(*field.value.type.struct_def,
(nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
code +=
indent + " builder.Pad(" + NumToString(field.padding) + ")\n";
if (IsStruct(field_type)) {
StructBuilderBody(*field_type.struct_def,
(nameprefix + (NormalizedName(field) + "_")).c_str(),
code_ptr, index, in_array);
} else {
code += " builder.Prepend" + GenMethod(field) + "(";
code += nameprefix + MakeCamel(NormalizedName(field), false) + ")\n";
const auto index_var = "_idx" + NumToString(index);
if (IsArray(field_type)) {
code += indent + " for " + index_var + " in range(";
code += NumToString(field_type.fixed_length);
code += " , 0, -1):\n";
in_array = true;
}
if (IsStruct(type)) {
StructBuilderBody(
*field_type.struct_def,
(nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr,
index + 1, in_array);
} else {
code += IsArray(field_type) ? " " : "";
code += indent + " builder.Prepend" + GenMethod(field) + "(";
code += nameprefix + MakeCamel(NormalizedName(field), false);
size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
for (size_t i = 0; in_array && i < array_cnt; i++) {
code += "[_idx" + NumToString(i) + "-1]";
}
code += ")\n";
}
}
}
}
@@ -505,6 +561,8 @@ class PythonGenerator : public BaseGenerator {
} else {
GetScalarFieldOfTable(struct_def, field, code_ptr);
}
} else if (IsArray(field.value.type)) {
GetArrayOfStruct(struct_def, field, code_ptr);
} else {
switch (field.value.type.base_type) {
case BASE_TYPE_STRUCT:
@@ -613,9 +671,9 @@ class PythonGenerator : public BaseGenerator {
// Returns the method name for use with add/put calls.
std::string GenMethod(const FieldDef &field) {
return IsScalar(field.value.type.base_type)
? MakeCamel(GenTypeBasic(field.value.type))
: (IsStruct(field.value.type) ? "Struct" : "UOffsetTRelative");
return (IsScalar(field.value.type.base_type) || IsArray(field.value.type))
? MakeCamel(GenTypeBasic(field.value.type))
: (IsStruct(field.value.type) ? "Struct" : "UOffsetTRelative");
}
std::string GenTypeBasic(const Type &type) {
@@ -628,7 +686,8 @@ class PythonGenerator : public BaseGenerator {
#undef FLATBUFFERS_TD
// clang-format on
};
return ctypename[type.base_type];
return ctypename[IsArray(type) ? type.VectorType().base_type
: type.base_type];
}
std::string GenTypePointer(const Type &type) {

View File

@@ -69,26 +69,27 @@ bool Print(T val, Type type, int /*indent*/, Type * /*union_type*/,
return true;
}
// Print a vector a sequence of JSON values, comma separated, wrapped in "[]".
template<typename T>
bool PrintVector(const Vector<T> &v, Type type, int indent,
const IDLOptions &opts, std::string *_text) {
// Print a vector or an array of JSON values, comma seperated, wrapped in "[]".
template<typename T, typename Container>
bool PrintContainer(const Container &c, size_t size, Type type, int indent,
const IDLOptions &opts, std::string *_text) {
std::string &text = *_text;
text += "[";
text += NewLine(opts);
for (uoffset_t i = 0; i < v.size(); i++) {
for (uoffset_t i = 0; i < size; i++) {
if (i) {
if (!opts.protobuf_ascii_alike) text += ",";
text += NewLine(opts);
}
text.append(indent + Indent(opts), ' ');
if (IsStruct(type)) {
if (!Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type,
indent + Indent(opts), nullptr, opts, _text)) {
if (!Print(reinterpret_cast<const void *>(c.Data() +
i * type.struct_def->bytesize),
type, indent + Indent(opts), nullptr, opts, _text)) {
return false;
}
} else {
if (!Print(v[i], type, indent + Indent(opts), nullptr, opts, _text)) {
if (!Print(c[i], type, indent + Indent(opts), nullptr, opts, _text)) {
return false;
}
}
@@ -99,6 +100,20 @@ bool PrintVector(const Vector<T> &v, Type type, int indent,
return true;
}
template<typename T>
bool PrintVector(const Vector<T> &v, Type type, int indent,
const IDLOptions &opts, std::string *_text) {
return PrintContainer<T, Vector<T>>(v, v.size(), type, indent, opts, _text);
}
// Print an array a sequence of JSON values, comma separated, wrapped in "[]".
template<typename T>
bool PrintArray(const Array<T, 0xFFFF> &a, size_t size, Type type, int indent,
const IDLOptions &opts, std::string *_text) {
return PrintContainer<T, Array<T, 0xFFFF>>(a, size, type, indent, opts,
_text);
}
// Specialization of Print above for pointer types.
template<>
bool Print<const void *>(const void *val, Type type, int indent,
@@ -125,25 +140,49 @@ bool Print<const void *>(const void *val, Type type, int indent,
}
break;
}
case BASE_TYPE_VECTOR:
type = type.VectorType();
case BASE_TYPE_VECTOR: {
const auto vec_type = type.VectorType();
// Call PrintVector above specifically for each element type:
switch (type.base_type) {
// clang-format off
// clang-format off
switch (vec_type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM: \
if (!PrintVector<CTYPE>( \
*reinterpret_cast<const Vector<CTYPE> *>(val), \
type, indent, opts, _text)) { \
vec_type, indent, opts, _text)) { \
return false; \
} \
break;
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
// clang-format on
}
// clang-format on
break;
}
case BASE_TYPE_ARRAY: {
const auto vec_type = type.VectorType();
// Call PrintArray above specifically for each element type:
// clang-format off
switch (vec_type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM: \
if (!PrintArray<CTYPE>( \
*reinterpret_cast<const Array<CTYPE, 0xFFFF> *>(val), \
type.fixed_length, \
vec_type, indent, opts, _text)) { \
return false; \
} \
break;
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
case BASE_TYPE_ARRAY: FLATBUFFERS_ASSERT(0);
}
// clang-format on
break;
}
default: FLATBUFFERS_ASSERT(0);
}
return true;
@@ -177,8 +216,8 @@ static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
std::string *_text) {
const void *val = nullptr;
if (fixed) {
// The only non-scalar fields in structs are structs.
FLATBUFFERS_ASSERT(IsStruct(fd.value.type));
// The only non-scalar fields in structs are structs or arrays.
FLATBUFFERS_ASSERT(IsStruct(fd.value.type) || IsArray(fd.value.type));
val = reinterpret_cast<const Struct *>(table)->GetStruct<const void *>(
fd.value.offset);
} else if (fd.flexbuffer) {
@@ -241,6 +280,7 @@ static bool GenStruct(const StructDef &struct_def, const Table *table,
CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM:
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
FLATBUFFERS_GEN_TYPE_ARRAY(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
union_type, opts, _text)) {

View File

@@ -601,12 +601,35 @@ CheckedError Parser::ParseType(Type &type) {
NEXT();
Type subtype;
ECHECK(Recurse([&]() { return ParseType(subtype); }));
if (subtype.base_type == BASE_TYPE_VECTOR) {
if (IsSeries(subtype)) {
// We could support this, but it will complicate things, and it's
// easier to work around with a struct around the inner vector.
return Error("nested vector types not supported (wrap in table first).");
return Error("nested vector types not supported (wrap in table first)");
}
if (token_ == ':') {
NEXT();
if (token_ != kTokenIntegerConstant) {
return Error("length of fixed-length array must be an integer value");
}
uint16_t fixed_length = 0;
bool check = StringToNumber(attribute_.c_str(), &fixed_length);
if (!check || fixed_length < 1) {
return Error(
"length of fixed-length array must be positive and fit to "
"uint16_t type");
}
// Check if enum arrays are used in C++ without specifying --scoped-enums
if ((opts.lang_to_generate & IDLOptions::kCpp) && !opts.scoped_enums &&
IsEnum(subtype)) {
return Error(
"--scoped-enums must be enabled to use enum arrays in C++\n");
}
type = Type(BASE_TYPE_ARRAY, subtype.struct_def, subtype.enum_def,
fixed_length);
NEXT();
} else {
type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def);
}
type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def);
type.element = subtype.base_type;
EXPECT(']');
} else {
@@ -651,9 +674,19 @@ CheckedError Parser::ParseField(StructDef &struct_def) {
Type type;
ECHECK(ParseType(type));
if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type))
if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type) &&
!IsArray(type))
return Error("structs_ may contain only scalar or struct fields");
if (!struct_def.fixed && IsArray(type))
return Error("fixed-length array in table must be wrapped in struct");
if (IsArray(type) && !SupportsAdvancedArrayFeatures()) {
return Error(
"Arrays are not yet supported in all "
"the specified programming languages.");
}
FieldDef *typefield = nullptr;
if (type.base_type == BASE_TYPE_UNION) {
// For union fields, add a second auto-generated field to hold the type,
@@ -703,12 +736,13 @@ CheckedError Parser::ParseField(StructDef &struct_def) {
}
}
if (type.enum_def) {
// The type.base_type can only be scalar, union or vector.
// The type.base_type can only be scalar, union, array or vector.
// Table, struct or string can't have enum_def.
// Default value of union and vector in NONE, NULL translated to "0".
FLATBUFFERS_ASSERT(IsInteger(type.base_type) ||
(type.base_type == BASE_TYPE_UNION) ||
(type.base_type == BASE_TYPE_VECTOR));
(type.base_type == BASE_TYPE_VECTOR) ||
(type.base_type == BASE_TYPE_ARRAY));
if (type.base_type == BASE_TYPE_VECTOR) {
// Vector can't use initialization list.
FLATBUFFERS_ASSERT(field->value.constant == "0");
@@ -963,6 +997,10 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field,
val.constant = NumToString(off);
break;
}
case BASE_TYPE_ARRAY: {
ECHECK(ParseArray(val));
break;
}
case BASE_TYPE_INT:
case BASE_TYPE_UINT:
case BASE_TYPE_LONG:
@@ -983,11 +1021,16 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field,
}
void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) {
SerializeStruct(builder_, struct_def, val);
}
void Parser::SerializeStruct(FlatBufferBuilder &builder,
const StructDef &struct_def, const Value &val) {
FLATBUFFERS_ASSERT(val.constant.length() == struct_def.bytesize);
builder_.Align(struct_def.minalign);
builder_.PushBytes(reinterpret_cast<const uint8_t *>(val.constant.c_str()),
struct_def.bytesize);
builder_.AddStructOffset(val.offset, builder_.GetSize());
builder.Align(struct_def.minalign);
builder.PushBytes(reinterpret_cast<const uint8_t *>(val.constant.c_str()),
struct_def.bytesize);
builder.AddStructOffset(val.offset, builder.GetSize());
}
template <typename F>
@@ -1161,7 +1204,13 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
break;
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD);
#undef FLATBUFFERS_TD
// clang-format on
case BASE_TYPE_ARRAY:
builder_.Pad(field->padding);
builder_.PushBytes(
reinterpret_cast<const uint8_t*>(field_value.constant.c_str()),
InlineSize(field_value.type));
break;
// clang-format on
}
}
}
@@ -1244,6 +1293,54 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue,
return NoError();
}
CheckedError Parser::ParseArray(Value &array) {
std::vector<Value> stack;
FlatBufferBuilder builder;
const auto &type = array.type.VectorType();
auto length = array.type.fixed_length;
uoffset_t count = 0;
auto err = ParseVectorDelimiters(count, [&](uoffset_t &) -> CheckedError {
vector_emplace_back(&stack, Value());
auto &val = stack.back();
val.type = type;
if (IsStruct(type)) {
ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr));
} else {
ECHECK(ParseSingleValue(nullptr, val, false));
}
return NoError();
});
ECHECK(err);
if (length != count) return Error("Fixed-length array size is incorrect.");
for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
auto &val = *it;
// clang-format off
switch (val.type.base_type) {
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE) \
case BASE_TYPE_ ## ENUM: \
if (IsStruct(val.type)) { \
SerializeStruct(builder, *val.type.struct_def, val); \
} else { \
CTYPE elem; \
ECHECK(atot(val.constant.c_str(), *this, &elem)); \
builder.PushElement(elem); \
} \
break;
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
#undef FLATBUFFERS_TD
default: FLATBUFFERS_ASSERT(0);
}
// clang-format on
}
array.constant.assign(
reinterpret_cast<const char *>(builder.GetCurrentBufferPointer()),
InlineSize(array.type));
return NoError();
}
CheckedError Parser::ParseNestedFlatbuffer(Value &val, FieldDef *field,
size_t fieldn,
const StructDef *parent_struct_def) {
@@ -1989,6 +2086,13 @@ bool Parser::SupportsAdvancedUnionFeatures() const {
IDLOptions::kBinary)) == 0;
}
bool Parser::SupportsAdvancedArrayFeatures() const {
return (opts.lang_to_generate &
~(IDLOptions::kCpp | IDLOptions::kPython | IDLOptions::kJava |
IDLOptions::kCSharp | IDLOptions::kJsonSchema | IDLOptions::kJson |
IDLOptions::kBinary)) == 0;
}
Namespace *Parser::UniqueNamespace(Namespace *ns) {
for (auto it = namespaces_.begin(); it != namespaces_.end(); ++it) {
if (ns->components == (*it)->components) {
@@ -3171,19 +3275,22 @@ bool EnumVal::Deserialize(const Parser &parser,
Offset<reflection::Type> Type::Serialize(FlatBufferBuilder *builder) const {
return reflection::CreateType(
*builder,
static_cast<reflection::BaseType>(base_type),
*builder, static_cast<reflection::BaseType>(base_type),
static_cast<reflection::BaseType>(element),
struct_def ? struct_def->index : (enum_def ? enum_def->index : -1));
struct_def ? struct_def->index : (enum_def ? enum_def->index : -1),
fixed_length);
}
bool Type::Deserialize(const Parser &parser, const reflection::Type *type) {
if (type == nullptr) return true;
base_type = static_cast<BaseType>(type->base_type());
element = static_cast<BaseType>(type->element());
fixed_length = type->fixed_length();
if (type->index() >= 0) {
bool is_series = type->base_type() == reflection::Vector ||
type->base_type() == reflection::Array;
if (type->base_type() == reflection::Obj ||
(type->base_type() == reflection::Vector &&
(is_series &&
type->element() == reflection::Obj)) {
if (static_cast<size_t>(type->index()) < parser.structs_.vec.size()) {
struct_def = parser.structs_.vec[type->index()];