diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 5909a4e20ae..d4b9513f9be 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -47,7 +47,8 @@ namespace flatbuffers { TD(LONG, "long", int64_t, long, int64, long, int64) \ TD(ULONG, "ulong", uint64_t, long, uint64, ulong, uint64) /* end int */ \ TD(FLOAT, "float", float, float, float32, float, float32) /* begin float */ \ - TD(DOUBLE, "double", double, double, float64, double, float64) /* end float/scalar */ + TD(DOUBLE, "double", double, double, float64, double, float64) /* end float/scalar */ \ + TD(ARRAY, "", int, int, int, int, int) #define FLATBUFFERS_GEN_TYPES_POINTER(TD) \ TD(STRING, "string", Offset<void>, int, int, StringOffset, int) \ TD(VECTOR, "", Offset<void>, int, int, VectorOffset, int) \ @@ -118,11 +119,13 @@ class Parser; // and additional information for vectors/structs_. struct Type { explicit Type(BaseType _base_type = BASE_TYPE_NONE, - StructDef *_sd = nullptr, EnumDef *_ed = nullptr) + StructDef *_sd = nullptr, EnumDef *_ed = nullptr, + short _fixed_length = 0) : base_type(_base_type), element(BASE_TYPE_NONE), struct_def(_sd), - enum_def(_ed) + enum_def(_ed), + fixed_length(_fixed_length) {} bool operator==(const Type &o) { @@ -135,10 +138,11 @@ struct Type { Offset<reflection::Type> Serialize(FlatBufferBuilder *builder) const; BaseType base_type; - BaseType element; // only set if t == BASE_TYPE_VECTOR + BaseType element; // only set if t == BASE_TYPE_VECTOR or t == BASE_TYPE_ARRAY StructDef *struct_def; // only set if t or element == BASE_TYPE_STRUCT EnumDef *enum_def; // set if t == BASE_TYPE_UNION / BASE_TYPE_UTYPE, // or for an integral type derived from an enum. + short fixed_length; // only set if t == BASE_TYPE_ARRAY }; // Represents a parsed scalar value, it's type, and field offset. @@ -269,12 +273,18 @@ inline bool IsStruct(const Type &type) { return type.base_type == BASE_TYPE_STRUCT && type.struct_def->fixed; } +inline bool IsArray(const Type &type) { + return type.base_type == BASE_TYPE_ARRAY; +} + inline size_t InlineSize(const Type &type) { - return IsStruct(type) ? type.struct_def->bytesize : SizeOf(type.base_type); + return IsStruct(type) ? type.struct_def->bytesize : IsArray(type) + ? SizeOf(type.element) * type.fixed_length : SizeOf(type.base_type); } inline size_t InlineAlignment(const Type &type) { - return IsStruct(type) ? type.struct_def->minalign : SizeOf(type.base_type); + return IsStruct(type) ? type.struct_def->minalign : + SizeOf(IsArray(type) ? type.element : type.base_type); } struct EnumVal { @@ -515,6 +525,7 @@ class Parser : public ParserState { std::string *value, uoffset_t *ovalue); void SerializeStruct(const StructDef &struct_def, const Value &val); void AddVector(bool sortbysize, int count); + FLATBUFFERS_CHECKED_ERROR ParseArray(const Type &type, const short length); FLATBUFFERS_CHECKED_ERROR ParseVector(const Type &type, uoffset_t *ovalue); FLATBUFFERS_CHECKED_ERROR ParseMetaData(SymbolTable<Value> *attributes); FLATBUFFERS_CHECKED_ERROR TryTypedValue(int dtoken, bool check, Value &e, diff --git a/include/flatbuffers/reflection_generated.h b/include/flatbuffers/reflection_generated.h index 3bd1590f64e..3524fdda195 100644 --- a/include/flatbuffers/reflection_generated.h +++ b/include/flatbuffers/reflection_generated.h @@ -35,14 +35,15 @@ enum BaseType { ULong = 10, Float = 11, Double = 12, - String = 13, - Vector = 14, - Obj = 15, - Union = 16, + Array = 13, + String = 14, + Vector = 15, + Obj = 16, + Union = 17 }; inline const char **EnumNamesBaseType() { - static const char *names[] = { "None", "UType", "Bool", "Byte", "UByte", "Short", "UShort", "Int", "UInt", "Long", "ULong", "Float", "Double", "String", "Vector", "Obj", "Union", nullptr }; + static const char *names[] = { "None", "UType", "Bool", "Byte", "UByte", "Short", "UShort", "Int", "UInt", "Long", "ULong", "Float", "Double", "Array", "String", "Vector", "Obj", "Union", nullptr }; return names; } @@ -52,15 +53,18 @@ struct Type FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_BASE_TYPE = 4, VT_ELEMENT = 6, - VT_INDEX = 8 + VT_FIXED_LENGTH = 8, + VT_INDEX = 10 }; BaseType base_type() const { return static_cast<BaseType>(GetField<int8_t>(VT_BASE_TYPE, 0)); } BaseType element() const { return static_cast<BaseType>(GetField<int8_t>(VT_ELEMENT, 0)); } + int16_t fixed_length() const { return GetField<int16_t>(VT_FIXED_LENGTH, 0); } int32_t index() const { return GetField<int32_t>(VT_INDEX, -1); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField<int8_t>(verifier, VT_BASE_TYPE) && VerifyField<int8_t>(verifier, VT_ELEMENT) && + VerifyField<int16_t>(verifier, VT_FIXED_LENGTH) && VerifyField<int32_t>(verifier, VT_INDEX) && verifier.EndTable(); } @@ -71,11 +75,12 @@ struct TypeBuilder { flatbuffers::uoffset_t start_; void add_base_type(BaseType base_type) { fbb_.AddElement<int8_t>(Type::VT_BASE_TYPE, static_cast<int8_t>(base_type), 0); } void add_element(BaseType element) { fbb_.AddElement<int8_t>(Type::VT_ELEMENT, static_cast<int8_t>(element), 0); } + void add_fixed_length(int16_t fixed_length) { fbb_.AddElement<int16_t>(Type::VT_FIXED_LENGTH, fixed_length, 0); } void add_index(int32_t index) { fbb_.AddElement<int32_t>(Type::VT_INDEX, index, -1); } TypeBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } TypeBuilder &operator=(const TypeBuilder &); flatbuffers::Offset<Type> Finish() { - auto o = flatbuffers::Offset<Type>(fbb_.EndTable(start_, 3)); + auto o = flatbuffers::Offset<Type>(fbb_.EndTable(start_, 4)); return o; } }; @@ -83,9 +88,11 @@ struct TypeBuilder { inline flatbuffers::Offset<Type> CreateType(flatbuffers::FlatBufferBuilder &_fbb, BaseType base_type = None, BaseType element = None, + int16_t fixed_length = 0, int32_t index = -1) { TypeBuilder builder_(_fbb); builder_.add_index(index); + builder_.add_fixed_length(fixed_length); builder_.add_element(element); builder_.add_base_type(base_type); return builder_.Finish(); diff --git a/reflection/reflection.fbs b/reflection/reflection.fbs index 57f234c6af8..30ad54bb82b 100644 --- a/reflection/reflection.fbs +++ b/reflection/reflection.fbs @@ -20,6 +20,7 @@ enum BaseType : byte { ULong, Float, Double, + Array, String, Vector, Obj, // Used for tables & structs. @@ -29,6 +30,7 @@ enum BaseType : byte { table Type { base_type:BaseType; element:BaseType = None; // Only if base_type == Vector. + fixed_length:short = 0; // Only if base_type == Array. index:int = -1; // If base_type == Object, index into "objects" below. // If base_type == Union, UnionType, or integral derived // from an enum, index into "enums" below. diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index edb14cc6bf9..93465f0b7e2 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -325,6 +325,8 @@ class CppGenerator : public BaseGenerator { bool user_facing_type) { return IsScalar(type.base_type) ? GenTypeBasic(type, user_facing_type) + afterbasic + : IsArray(type) ? beforeptr + + GenTypeBasic(type.VectorType(), user_facing_type) + afterptr : beforeptr + GenTypePointer(type) + afterptr; } @@ -1153,7 +1155,13 @@ class CppGenerator : public BaseGenerator { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; code += " " + GenTypeGet(field.value.type, " ", "", " ", false); - code += field.name + "_;\n"; + code += field.name + "_"; + if (IsArray(field.value.type)) { + code += "["; + code += NumToString<short>(field.value.type.fixed_length); + code += "]"; + } + code += ";\n"; GenPadding(field, code, padding_id, PaddingDefinition); } @@ -1176,7 +1184,8 @@ class CppGenerator : public BaseGenerator { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (it != struct_def.fields.vec.begin()) code += ", "; - code += GenTypeGet(field.value.type, " ", "const ", " &", true); + code += GenTypeGet(field.value.type, " ", "const ", + IsArray(field.value.type) ? " *" : " &", true); code += "_" + field.name; } code += ")\n : "; @@ -1184,19 +1193,31 @@ class CppGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; - if (it != struct_def.fields.vec.begin()) code += ", "; - code += field.name + "_("; - if (IsScalar(field.value.type.base_type)) { - code += "flatbuffers::EndianScalar("; - code += GenUnderlyingCast(field, false, "_" + field.name); - code += "))"; - } else { - code += "_" + field.name + ")"; + if (!IsArray(field.value.type)) { + if (it != struct_def.fields.vec.begin()) code += ", "; + code += field.name + "_("; + if (IsScalar(field.value.type.base_type)) { + code += "flatbuffers::EndianScalar("; + code += GenUnderlyingCast(field, false, "_" + field.name); + code += "))"; + } else { + code += "_" + field.name + ")"; + } } GenPadding(field, code, padding_id, PaddingInitializer); } code += " {"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (IsArray(field.value.type)) { + code += " memcpy("; + code += field.name + "_, "; + code += "_" + field.name + ", "; + code += NumToString(InlineSize(field.value.type)) + ");"; + } + } padding_id = 0; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { @@ -1212,13 +1233,17 @@ class CppGenerator : public BaseGenerator { auto &field = **it; GenComment(field.doc_comment, code_ptr, nullptr, " "); auto is_scalar = IsScalar(field.value.type.base_type); - code += " " + GenTypeGet(field.value.type, " ", "const ", " &", true); + code += " " + GenTypeGet(field.value.type, " ", "const ", + IsArray(field.value.type) ? " *" : " &", true); code += field.name + "() const { return "; - code += GenUnderlyingCast( - field, true, is_scalar + code += GenUnderlyingCast(field, true, is_scalar ? "flatbuffers::EndianScalar(" + field.name + "_)" : field.name + "_"); code += "; }\n"; + if (IsArray(field.value.type)) { + code += " int16_t " + field.name + "_length() const { return "; + code += NumToString(field.value.type.fixed_length) + "; }\n"; + } if (parser_.opts.mutable_buffer) { if (is_scalar) { code += " void mutate_" + field.name + "("; @@ -1229,7 +1254,8 @@ class CppGenerator : public BaseGenerator { code += "); }\n"; } else { code += " "; - code += GenTypeGet(field.value.type, "", "", " &", true); + code += GenTypeGet(field.value.type, "", "", + IsArray(field.value.type) ? " *" : " &", true); code += "mutable_" + field.name + "() { return " + field.name; code += "_; }\n"; } diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 7e8e8c577a1..40f998b197d 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -731,6 +731,7 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; + assert(field.value.type.base_type != BASE_TYPE_ARRAY); if (field.deprecated) continue; GenComment(field.doc_comment, code_ptr, &lang_.comment_config, " "); std::string type_name = GenTypeGet(field.value.type); diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index 573300980da..03a9c71306b 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -598,6 +598,7 @@ static void GenStruct(const StructDef &struct_def, it != struct_def.fields.vec.end(); ++it) { auto &field = **it; + assert(field.value.type.base_type != BASE_TYPE_ARRAY); if (field.deprecated) continue; GenStructAccessor(struct_def, field, code_ptr); diff --git a/src/idl_gen_js.cpp b/src/idl_gen_js.cpp index fec57802989..a28faedcc0b 100644 --- a/src/idl_gen_js.cpp +++ b/src/idl_gen_js.cpp @@ -430,6 +430,7 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { auto &field = **it; + assert(field.value.type.base_type != BASE_TYPE_ARRAY); if (field.deprecated) continue; auto offset_prefix = " var offset = this.bb.__offset(this.bb_pos, " + NumToString(field.value.offset) + ");\n return offset ? "; diff --git a/src/idl_gen_php.cpp b/src/idl_gen_php.cpp index af3b06d57e6..c63d255df9c 100644 --- a/src/idl_gen_php.cpp +++ b/src/idl_gen_php.cpp @@ -809,6 +809,7 @@ namespace php { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; + assert(field.value.type.base_type != BASE_TYPE_ARRAY); if (field.deprecated) continue; GenStructAccessor(struct_def, field, code_ptr); diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 52944bfca3e..1e2e05aac59 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -495,6 +495,7 @@ static void GenStruct(const StructDef &struct_def, it != struct_def.fields.vec.end(); ++it) { auto &field = **it; + assert(field.value.type.base_type != BASE_TYPE_ARRAY); if (field.deprecated) continue; GenStructAccessor(struct_def, field, code_ptr); diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index 3e41a0a7683..1b78872a4ae 100644 --- a/src/idl_gen_text.cpp +++ b/src/idl_gen_text.cpp @@ -48,7 +48,7 @@ void OutputIdentifier(const std::string &name, const IDLOptions &opts, // Print (and its template specialization below for pointers) generate text // for a single FlatBuffer value into JSON format. // The general case for scalars: -template<typename T> void Print(T val, Type type, int /*indent*/, +template<typename T> void Print(T val, Type type, int indent, StructDef * /*union_sd*/, const IDLOptions &opts, std::string *_text) { @@ -63,6 +63,16 @@ template<typename T> void Print(T val, Type type, int /*indent*/, if (type.base_type == BASE_TYPE_BOOL) { text += val != 0 ? "true" : "false"; + } else if (type.base_type == BASE_TYPE_ARRAY) { + text += '['; + text += NewLine(opts); + for (short i = 1; i < type.fixed_length; i++) { + text.append(indent + Indent(opts), ' '); + text += ","; + text += NewLine(opts); + } + text.append(indent, ' '); + text += "]"; } else { text += NumToString(val); } diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index d845b837dbd..a241b238ef1 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -543,7 +543,8 @@ CheckedError Parser::ParseType(Type &type) { NEXT(); Type subtype; ECHECK(ParseType(subtype)); - if (subtype.base_type == BASE_TYPE_VECTOR) { + if (subtype.base_type == BASE_TYPE_VECTOR || + subtype.base_type == BASE_TYPE_ARRAY) { // 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( @@ -555,7 +556,27 @@ CheckedError Parser::ParseType(Type &type) { return Error( "vector of union types not supported (wrap in table first)."); } - type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def); + if (token_ == ':') { + if (!IsScalar(subtype.base_type)) { + return Error( + "arrays of non-scalar types not supported."); + } + NEXT(); + if (token_ != kTokenIntegerConstant) { + return Error( + "length of fixed-length array must be an integer value."); + } + int64_t fixed_length = StringToInt(attribute_.c_str()); + if (fixed_length < 1 || fixed_length > 0x7fff) { + return Error( + "length of fixed-length array must be positive and fit to short type."); + } + type = Type(BASE_TYPE_ARRAY, subtype.struct_def, + subtype.enum_def, (int16_t)fixed_length); + NEXT(); + } else { + type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def); + } type.element = subtype.base_type; EXPECT(']'); } else { @@ -597,8 +618,11 @@ 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("tables can't contain fixed-length arrays."); FieldDef *typefield = nullptr; if (type.base_type == BASE_TYPE_UNION) { @@ -753,6 +777,10 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field, case BASE_TYPE_STRUCT: ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr)); break; + case BASE_TYPE_ARRAY: + EXPECT('['); + ECHECK(ParseArray(val.type.VectorType(),val.type.fixed_length)); + break; case BASE_TYPE_STRING: { auto s = attribute_; EXPECT(kTokenStringConstant); @@ -852,6 +880,14 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, it != field_stack_.rbegin() + fieldn; ++it) { auto &field_value = it->first; auto field = it->second; + if (field->value.type.base_type == BASE_TYPE_ARRAY) { + int array_size = field->value.type.fixed_length * SizeOf(field->value.type.element); + uint8_t*bytes = new uint8_t[array_size]; + memset(bytes, 0, array_size); + builder_.Pad(field->padding); + builder_.PushBytes(bytes, array_size); + continue; + } if (!struct_def.sortbysize || size == SizeOf(field_value.type.base_type)) { switch (field_value.type.base_type) { @@ -912,6 +948,22 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value, return NoError(); } +CheckedError Parser::ParseArray(const Type &type, const short length) { + bool values_defined = token_ != ','; + int count = values_defined ? 0 : 1; + for (;;) { + if ((!opts.strict_json || !count) && Is(']')) { NEXT(); break; } + Value val; + val.type = type; + if (token_ != ',') ECHECK(ParseSingleValue(val)); + count++; + if (Is(']')) { NEXT(); break; } + EXPECT(','); + } + if (length != count) return Error("Fixed-length array size is incorrect."); + else return NoError(); +} + CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue) { int count = 0; for (;;) { @@ -1381,6 +1433,7 @@ CheckedError Parser::ParseDecl() { 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)); + ECHECK(CheckClash(fields, struct_def, "_length", BASE_TYPE_ARRAY)); EXPECT('}'); return NoError(); } @@ -2083,6 +2136,7 @@ Offset<reflection::Type> Type::Serialize(FlatBufferBuilder *builder) const { return reflection::CreateType(*builder, static_cast<reflection::BaseType>(base_type), static_cast<reflection::BaseType>(element), + fixed_length, struct_def ? struct_def->index : (enum_def ? enum_def->index : -1)); } diff --git a/tests/arrays_test.fbs b/tests/arrays_test.fbs new file mode 100644 index 00000000000..ec9f8000511 --- /dev/null +++ b/tests/arrays_test.fbs @@ -0,0 +1,13 @@ +namespace ArraysTest.Test1; + +struct ArrayStruct{ + a:float; + b:[int:15]; + c:byte; +} + +table ArrayTable{ + a:ArrayStruct; +} + +root_type ArrayTable; diff --git a/tests/arrays_test_generated.h b/tests/arrays_test_generated.h new file mode 100644 index 00000000000..2885ffa924f --- /dev/null +++ b/tests/arrays_test_generated.h @@ -0,0 +1,82 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +#ifndef FLATBUFFERS_GENERATED_ARRAYSTEST_ARRAYSTEST_TEST1_H_ +#define FLATBUFFERS_GENERATED_ARRAYSTEST_ARRAYSTEST_TEST1_H_ + +#include "flatbuffers/flatbuffers.h" + +namespace ArraysTest { +namespace Test1 { + +struct ArrayStruct; + +struct ArrayTable; + +MANUALLY_ALIGNED_STRUCT(4) ArrayStruct FLATBUFFERS_FINAL_CLASS { + private: + float a_; + int32_t b_[15]; + int8_t c_; + int8_t __padding0; + int16_t __padding1; + + public: + ArrayStruct() { memset(this, 0, sizeof(ArrayStruct)); } + ArrayStruct(const ArrayStruct &_o) { memcpy(this, &_o, sizeof(ArrayStruct)); } + ArrayStruct(float _a, const int32_t *_b, int8_t _c) + : a_(flatbuffers::EndianScalar(_a)), c_(flatbuffers::EndianScalar(_c)), __padding0(0), __padding1(0) { memcpy(b_, _b, 60); (void)__padding0; (void)__padding1; } + + float a() const { return flatbuffers::EndianScalar(a_); } + void mutate_a(float _a) { flatbuffers::WriteScalar(&a_, _a); } + const int32_t *b() const { return b_; } + int16_t b_length() const { return 15; } + int32_t *mutable_b() { return b_; } + int8_t c() const { return flatbuffers::EndianScalar(c_); } + void mutate_c(int8_t _c) { flatbuffers::WriteScalar(&c_, _c); } +}; +STRUCT_END(ArrayStruct, 68); + +struct ArrayTable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum { + VT_A = 4 + }; + const ArrayStruct *a() const { return GetStruct<const ArrayStruct *>(VT_A); } + ArrayStruct *mutable_a() { return GetStruct<ArrayStruct *>(VT_A); } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField<ArrayStruct>(verifier, VT_A) && + verifier.EndTable(); + } +}; + +struct ArrayTableBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(const ArrayStruct *a) { fbb_.AddStruct(ArrayTable::VT_A, a); } + ArrayTableBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } + ArrayTableBuilder &operator=(const ArrayTableBuilder &); + flatbuffers::Offset<ArrayTable> Finish() { + auto o = flatbuffers::Offset<ArrayTable>(fbb_.EndTable(start_, 1)); + return o; + } +}; + +inline flatbuffers::Offset<ArrayTable> CreateArrayTable(flatbuffers::FlatBufferBuilder &_fbb, + const ArrayStruct *a = 0) { + ArrayTableBuilder builder_(_fbb); + builder_.add_a(a); + return builder_.Finish(); +} + +inline const ArraysTest::Test1::ArrayTable *GetArrayTable(const void *buf) { return flatbuffers::GetRoot<ArraysTest::Test1::ArrayTable>(buf); } + +inline ArrayTable *GetMutableArrayTable(void *buf) { return flatbuffers::GetMutableRoot<ArrayTable>(buf); } + +inline bool VerifyArrayTableBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer<ArraysTest::Test1::ArrayTable>(nullptr); } + +inline void FinishArrayTableBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset<ArraysTest::Test1::ArrayTable> root) { fbb.Finish(root); } + +} // namespace Test1 +} // namespace ArraysTest + +#endif // FLATBUFFERS_GENERATED_ARRAYSTEST_ARRAYSTEST_TEST1_H_ diff --git a/tests/generate_code.bat b/tests/generate_code.bat index 4b4a535316a..6752945f152 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -15,3 +15,4 @@ ..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json ..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test\namespace_test1.fbs namespace_test\namespace_test2.fbs ..\flatc.exe --binary --schema monster_test.fbs +..\flatc.exe --cpp --gen-mutable arrays_test.fbs diff --git a/tests/generate_code.sh b/tests/generate_code.sh index bf6f3c8ebf0..185af89efb0 100644 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -17,6 +17,7 @@ ../flatc --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json ../flatc --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --binary --schema monster_test.fbs +../flatc --cpp --gen-mutable arrays_test.fbs cd ../samples ../flatc --cpp --gen-mutable --gen-object-api monster.fbs cd ../reflection diff --git a/tests/monster_test.bfbs b/tests/monster_test.bfbs index e131ac8c2ee..88ccb1cc40d 100644 Binary files a/tests/monster_test.bfbs and b/tests/monster_test.bfbs differ diff --git a/tests/test.cpp b/tests/test.cpp index fd2352bd18b..fe499f8b17e 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -24,6 +24,7 @@ #include "monster_test_generated.h" #include "namespace_test/namespace_test1_generated.h" #include "namespace_test/namespace_test2_generated.h" +#include "arrays_test_generated.h" #ifndef FLATBUFFERS_CPP98_STL #include <random> @@ -749,6 +750,15 @@ void FuzzTest2() { // Pick random type: int base_type = lcg_rand() % (flatbuffers::BASE_TYPE_UNION + 1); switch (base_type) { + case flatbuffers::BASE_TYPE_ARRAY: + if (!is_struct) { + AddToSchemaAndInstances("ubyte", + deprecated ? "" : "255"); // No fixed-length arrays in tables. + } else { + AddToSchemaAndInstances("[int:3]", + deprecated ? "" : "[\n,\n,\n]"); + } + break; case flatbuffers::BASE_TYPE_STRING: if (is_struct) { Dummy(); // No strings in structs. @@ -1194,6 +1204,23 @@ void ConformTest() { test_conform("enum E:byte { B, A }", "values differ for enum"); } +void FixedLengthArrayTest() { + flatbuffers::FlatBufferBuilder fbb; + int array[15]; + for (int i = 0; i < 15; i++) array[i] = i + 1; + ArraysTest::Test1::ArrayStruct aStruct(2, array, 12); + auto aTable = ArraysTest::Test1::CreateArrayTable(fbb, &aStruct); + fbb.Finish(aTable); + auto p = ArraysTest::Test1::GetMutableArrayTable(fbb.GetBufferPointer()); + auto mArStruct = p->mutable_a(); + mArStruct->mutable_b()[14] = -14; + TEST_EQ(mArStruct->a(), 2); + TEST_EQ(mArStruct->b_length(), 15); + TEST_EQ(mArStruct->c(), 12); + TEST_EQ(mArStruct->b()[14], -14); + for (int i = 0; i < 14; i++) TEST_EQ(mArStruct->b()[i], i + 1); +} + int main(int /*argc*/, const char * /*argv*/[]) { // Run our various test suites: @@ -1228,6 +1255,7 @@ int main(int /*argc*/, const char * /*argv*/[]) { UnknownFieldsTest(); ParseUnionTest(); ConformTest(); + FixedLengthArrayTest(); if (!testing_fails) { TEST_OUTPUT_LINE("ALL TESTS PASSED");