diff options
author | svenk177 <50141814+svenk177@users.noreply.github.com> | 2019-06-18 00:15:13 +0200 |
---|---|---|
committer | Wouter van Oortmerssen <aardappel@gmail.com> | 2019-06-18 00:15:13 +0200 |
commit | e635141d5bc66f056c90bcc9da5fdd766610492f (patch) | |
tree | b69da4d3b4d2e6e592083a2c31c6d7f809666575 | |
parent | 0d2cebccfeffae9df998f3ac819bf17b7ec7a6d0 (diff) | |
download | flatbuffers-e635141d5bc66f056c90bcc9da5fdd766610492f.tar.gz flatbuffers-e635141d5bc66f056c90bcc9da5fdd766610492f.tar.bz2 flatbuffers-e635141d5bc66f056c90bcc9da5fdd766610492f.zip |
Add support for fixed-size arrays (#5313)
40 files changed, 2113 insertions, 220 deletions
diff --git a/.appveyor/check-generate-code.bat b/.appveyor/check-generate-code.bat index 053c8b12..ba7398a2 100644 --- a/.appveyor/check-generate-code.bat +++ b/.appveyor/check-generate-code.bat @@ -19,6 +19,7 @@ call generate_code.bat -b %buildtype% || goto FAIL :: TODO: Release and Debug builds produce differences here for some reason. git checkout HEAD -- monster_test.bfbs +git checkout HEAD -- arrays_test.bfbs git -c core.autocrlf=true diff --exit-code --quiet || goto :DIFFFOUND goto SUCCESS diff --git a/.travis/check-generate-code.sh b/.travis/check-generate-code.sh index 88024914..37d81bb7 100755 --- a/.travis/check-generate-code.sh +++ b/.travis/check-generate-code.sh @@ -21,6 +21,7 @@ cd .. # TODO: Linux and macos builds produce differences here for some reason. git checkout HEAD -- tests/monster_test.bfbs +git checkout HEAD -- tests/arrays_test.bfbs if ! git diff --quiet; then echo >&2 @@ -164,11 +164,15 @@ cc_test( ":tests/union_vector/union_vector.json", ":tests/monster_extra.fbs", ":tests/monsterdata_extra.json", + ":tests/arrays_test.bfbs", + ":tests/arrays_test.fbs", + ":tests/arrays_test.golden", ], includes = ["include/"], deps = [ ":monster_extra_cc_fbs", ":monster_test_cc_fbs", + ":arrays_test_cc_fbs", ], ) @@ -188,3 +192,16 @@ flatbuffer_cc_library( name = "monster_extra_cc_fbs", srcs = ["tests/monster_extra.fbs"], ) + +flatbuffer_cc_library( + name = "arrays_test_cc_fbs", + srcs = ["tests/arrays_test.fbs"], + flatc_args = [ + "--gen-object-api", + "--gen-compare", + "--no-includes", + "--gen-mutable", + "--reflect-names", + "--cpp-ptr-type flatbuffers::unique_ptr", + "--scoped-enums" ], +) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30be238f..c7da97f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,8 @@ set(FlatBuffers_Tests_SRCS tests/test_builder.cpp # file generate by running compiler on tests/monster_test.fbs ${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h + # file generate by running compiler on tests/arrays_test.fbs + ${CMAKE_CURRENT_BINARY_DIR}/tests/arrays_test_generated.h ) set(FlatBuffers_Sample_Binary_SRCS @@ -303,7 +305,7 @@ if(FLATBUFFERS_BUILD_SHAREDLIB) VERSION "${FlatBuffers_Library_SONAME_FULL}") endif() -function(compile_flatbuffers_schema_to_cpp SRC_FBS) +function(compile_flatbuffers_schema_to_cpp_opt SRC_FBS OPT) get_filename_component(SRC_FBS_DIR ${SRC_FBS} PATH) string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS}) add_custom_command( @@ -311,12 +313,16 @@ function(compile_flatbuffers_schema_to_cpp SRC_FBS) COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable --gen-object-api --gen-compare -o "${SRC_FBS_DIR}" --cpp-ptr-type flatbuffers::unique_ptr # Used to test with C++98 STLs - --reflect-names + --reflect-names ${OPT} -I "${CMAKE_CURRENT_SOURCE_DIR}/tests/include_test" "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" DEPENDS flatc) endfunction() +function(compile_flatbuffers_schema_to_cpp SRC_FBS) + compile_flatbuffers_schema_to_cpp_opt(${SRC_FBS} "") +endfunction() + function(compile_flatbuffers_schema_to_binary SRC_FBS) get_filename_component(SRC_FBS_DIR ${SRC_FBS} PATH) string(REGEX REPLACE "\\.fbs$" ".bfbs" GEN_BINARY_SCHEMA ${SRC_FBS}) @@ -329,6 +335,7 @@ endfunction() if(FLATBUFFERS_BUILD_TESTS) compile_flatbuffers_schema_to_cpp(tests/monster_test.fbs) + compile_flatbuffers_schema_to_cpp_opt(tests/arrays_test.fbs --scoped-enums) include_directories(${CMAKE_CURRENT_BINARY_DIR}/tests) add_executable(flattests ${FlatBuffers_Tests_SRCS}) set_property(TARGET flattests diff --git a/docs/source/Schemas.md b/docs/source/Schemas.md index 4f7bd728..58bedae6 100644 --- a/docs/source/Schemas.md +++ b/docs/source/Schemas.md @@ -114,6 +114,27 @@ of same-size data where a `reinterpret_cast` would give you a desirable result, e.g. you could change a `uint` to an `int` if no values in current data use the high bit yet. +### Arrays + +Arrays are a convenience short-hand for a fixed-length collection of elements. +Arrays can be used to replace the following schema: + + struct Vec3 { + x:float; + y:float; + z:float; + } + +with the following schema: + + struct Vec3 { + v:[float:3]; + } + +Both representations are binary equivalent. + +Arrays are currently only supported in a `struct`. + ### (Default) Values Values are a sequence of digits. Values may be optionally followed by a decimal diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 855abc4d..a831163a 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -395,6 +395,83 @@ template<typename T> static inline size_t VectorLength(const Vector<T> *v) { return v ? v->size() : 0; } +// This is used as a helper type for accessing arrays. +template<typename T, uint16_t length> class Array { + public: + typedef VectorIterator<T, typename IndirectHelper<T>::return_type> + const_iterator; + typedef VectorReverseIterator<const_iterator> const_reverse_iterator; + + typedef typename IndirectHelper<T>::return_type return_type; + + FLATBUFFERS_CONSTEXPR uint16_t size() const { return length; } + + return_type Get(uoffset_t i) const { + FLATBUFFERS_ASSERT(i < size()); + return IndirectHelper<T>::Read(Data(), i); + } + + return_type operator[](uoffset_t i) const { return Get(i); } + + const_iterator begin() const { return const_iterator(Data(), 0); } + const_iterator end() const { return const_iterator(Data(), size()); } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const { return const_reverse_iterator(end()); } + + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + const_reverse_iterator crbegin() const { return rbegin(); } + const_reverse_iterator crend() const { return rend(); } + + // Change elements if you have a non-const pointer to this object. + void Mutate(uoffset_t i, const T &val) { + FLATBUFFERS_ASSERT(i < size()); + WriteScalar(data() + i, val); + } + + // Get a mutable pointer to elements inside this array. + // @note This method should be only used to mutate arrays of structs followed + // by a @p Mutate operation. For primitive types use @p Mutate directly. + // @warning Assignments and reads to/from the dereferenced pointer are not + // automatically converted to the correct endianness. + T *GetMutablePointer(uoffset_t i) const { + FLATBUFFERS_ASSERT(i < size()); + return const_cast<T *>(&data()[i]); + } + + // The raw data in little endian format. Use with care. + const uint8_t *Data() const { return data_; } + + uint8_t *Data() { return data_; } + + // Similarly, but typed, much like std::vector::data + const T *data() const { return reinterpret_cast<const T *>(Data()); } + T *data() { return reinterpret_cast<T *>(Data()); } + + protected: + // This class is only used to access pre-existing data. Don't ever + // try to construct these manually. + // 'constexpr' allows us to use 'size()' at compile time. + // @note Must not use 'FLATBUFFERS_CONSTEXPR' here, as const is not allowed on + // a constructor. +#if defined(__cpp_constexpr) + constexpr Array(); +#else + Array(); +#endif + + uint8_t data_[length * sizeof(T)]; + + private: + // This class is a pointer. Copying will therefore create an invalid object. + // Private and unimplemented copy constructor. + Array(const Array &); +}; + // Lexicographically compare two strings (possibly containing nulls), and // return true if the first is less than the second. static inline bool StringLessThan(const char *a_data, uoffset_t a_size, diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 6f45a7ba..48dda408 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -65,7 +65,8 @@ namespace flatbuffers { TD(VECTOR, "", Offset<void>, int, int, VectorOffset, int, unused) \ TD(STRUCT, "", Offset<void>, int, int, int, int, unused) \ TD(UNION, "", Offset<void>, int, int, int, int, unused) - +#define FLATBUFFERS_GEN_TYPE_ARRAY(TD) \ + TD(ARRAY, "", int, int, int, int, int, unused) // The fields are: // - enum // - FlatBuffers schema type. @@ -91,7 +92,8 @@ switch (type) { #define FLATBUFFERS_GEN_TYPES(TD) \ FLATBUFFERS_GEN_TYPES_SCALAR(TD) \ - FLATBUFFERS_GEN_TYPES_POINTER(TD) + FLATBUFFERS_GEN_TYPES_POINTER(TD) \ + FLATBUFFERS_GEN_TYPE_ARRAY(TD) // Create an enum for all the types above. #ifdef __GNUC__ @@ -145,18 +147,21 @@ class Parser; // and additional information for vectors/structs_. struct Type { explicit Type(BaseType _base_type = BASE_TYPE_NONE, StructDef *_sd = nullptr, - EnumDef *_ed = nullptr) + EnumDef *_ed = nullptr, uint16_t _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) { return base_type == o.base_type && element == o.element && struct_def == o.struct_def && enum_def == o.enum_def; } - Type VectorType() const { return Type(element, struct_def, enum_def); } + Type VectorType() const { + return Type(element, struct_def, enum_def, fixed_length); + } Offset<reflection::Type> Serialize(FlatBufferBuilder *builder) const; @@ -167,6 +172,7 @@ struct Type { 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. + uint16_t fixed_length; // only set if t == BASE_TYPE_ARRAY }; // Represents a parsed scalar value, it's type, and field offset. @@ -335,12 +341,34 @@ inline bool IsStruct(const Type &type) { return type.base_type == BASE_TYPE_STRUCT && type.struct_def->fixed; } +inline bool IsVector(const Type &type) { + return type.base_type == BASE_TYPE_VECTOR; +} + +inline bool IsArray(const Type &type) { + return type.base_type == BASE_TYPE_ARRAY; +} + +inline bool IsSeries(const Type &type) { + return IsVector(type) || IsArray(type); +} + +inline bool IsEnum(const Type &type) { + return type.enum_def != nullptr && IsInteger(type.base_type); +} + 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) + ? InlineSize(type.VectorType()) * 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 EnumDef; @@ -799,10 +827,13 @@ class Parser : public ParserState { FLATBUFFERS_CHECKED_ERROR ParseTable(const StructDef &struct_def, std::string *value, uoffset_t *ovalue); void SerializeStruct(const StructDef &struct_def, const Value &val); + void SerializeStruct(FlatBufferBuilder &builder, const StructDef &struct_def, + const Value &val); template<typename F> FLATBUFFERS_CHECKED_ERROR ParseVectorDelimiters(uoffset_t &count, F body); FLATBUFFERS_CHECKED_ERROR ParseVector(const Type &type, uoffset_t *ovalue, FieldDef *field, size_t fieldn); + FLATBUFFERS_CHECKED_ERROR ParseArray(Value &array); FLATBUFFERS_CHECKED_ERROR ParseNestedFlatbuffer(Value &val, FieldDef *field, size_t fieldn, const StructDef *parent_struct_def); @@ -849,6 +880,7 @@ class Parser : public ParserState { BaseType baseType); bool SupportsAdvancedUnionFeatures() const; + bool SupportsAdvancedArrayFeatures() const; Namespace *UniqueNamespace(Namespace *ns); FLATBUFFERS_CHECKED_ERROR RecurseError(); diff --git a/include/flatbuffers/reflection_generated.h b/include/flatbuffers/reflection_generated.h index 47a8a0c2..0e73a0fe 100644 --- a/include/flatbuffers/reflection_generated.h +++ b/include/flatbuffers/reflection_generated.h @@ -43,10 +43,11 @@ enum BaseType { String = 13, Vector = 14, Obj = 15, - Union = 16 + Union = 16, + Array = 17 }; -inline const BaseType (&EnumValuesBaseType())[17] { +inline const BaseType (&EnumValuesBaseType())[18] { static const BaseType values[] = { None, UType, @@ -64,13 +65,14 @@ inline const BaseType (&EnumValuesBaseType())[17] { String, Vector, Obj, - Union + Union, + Array }; return values; } inline const char * const *EnumNamesBaseType() { - static const char * const names[18] = { + static const char * const names[19] = { "None", "UType", "Bool", @@ -88,13 +90,14 @@ inline const char * const *EnumNamesBaseType() { "Vector", "Obj", "Union", + "Array", nullptr }; return names; } inline const char *EnumNameBaseType(BaseType e) { - if (e < None || e > Union) return ""; + if (e < None || e > Array) return ""; const size_t index = static_cast<size_t>(e); return EnumNamesBaseType()[index]; } @@ -103,7 +106,8 @@ struct Type FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_BASE_TYPE = 4, VT_ELEMENT = 6, - VT_INDEX = 8 + VT_INDEX = 8, + VT_FIXED_LENGTH = 10 }; reflection::BaseType base_type() const { return static_cast<reflection::BaseType>(GetField<int8_t>(VT_BASE_TYPE, 0)); @@ -114,11 +118,15 @@ struct Type FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { int32_t index() const { return GetField<int32_t>(VT_INDEX, -1); } + uint16_t fixed_length() const { + return GetField<uint16_t>(VT_FIXED_LENGTH, 0); + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField<int8_t>(verifier, VT_BASE_TYPE) && VerifyField<int8_t>(verifier, VT_ELEMENT) && VerifyField<int32_t>(verifier, VT_INDEX) && + VerifyField<uint16_t>(verifier, VT_FIXED_LENGTH) && verifier.EndTable(); } }; @@ -135,6 +143,9 @@ struct TypeBuilder { void add_index(int32_t index) { fbb_.AddElement<int32_t>(Type::VT_INDEX, index, -1); } + void add_fixed_length(uint16_t fixed_length) { + fbb_.AddElement<uint16_t>(Type::VT_FIXED_LENGTH, fixed_length, 0); + } explicit TypeBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -151,9 +162,11 @@ inline flatbuffers::Offset<Type> CreateType( flatbuffers::FlatBufferBuilder &_fbb, reflection::BaseType base_type = reflection::None, reflection::BaseType element = reflection::None, - int32_t index = -1) { + int32_t index = -1, + uint16_t fixed_length = 0) { 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 a344d7b8..8fed025f 100644 --- a/reflection/reflection.fbs +++ b/reflection/reflection.fbs @@ -23,15 +23,18 @@ enum BaseType : byte { String, Vector, Obj, // Used for tables & structs. - Union + Union, + Array } table Type { base_type:BaseType; - element:BaseType = None; // Only if base_type == Vector. + element:BaseType = None; // Only if base_type == Vector + // or 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. + fixed_length:uint16 = 0; // Only if base_type == Array. } table KeyValue { diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 4d81ac96..6b33f1d9 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -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_ += " }"; } } diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 6cb0db24..dd98c0e6 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -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); diff --git a/src/idl_gen_json_schema.cpp b/src/idl_gen_json_schema.cpp index a0f193b5..27e2cd46 100644 --- a/src/idl_gen_json_schema.cpp +++ b/src/idl_gen_json_schema.cpp @@ -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; } diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index a3a7d1bf..05367d4b 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -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) { diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index eeb34ffb..77088d22 100644 --- a/src/idl_gen_text.cpp +++ b/src/idl_gen_text.cpp @@ -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)) { diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 21ce4d82..6ae5c433 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -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()]; diff --git a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj index bbba231a..d698d202 100644 --- a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj +++ b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj @@ -99,6 +99,18 @@ <Compile Include="..\MyGame\Example\Ability.cs"> <Link>MyGame\Example\Ability.cs</Link> </Compile> + <Compile Include="..\MyGame\Example\ArrayTable.cs"> + <Link>MyGame\Example\ArrayTable.cs</Link> + </Compile> + <Compile Include="..\MyGame\Example\ArrayStruct.cs"> + <Link>MyGame\Example\ArrayStruct.cs</Link> + </Compile> + <Compile Include="..\MyGame\Example\NestedStruct.cs"> + <Link>MyGame\Example\NestedStruct.cs</Link> + </Compile> + <Compile Include="..\MyGame\Example\TestEnum.cs"> + <Link>MyGame\Example\TestEnum.cs</Link> + </Compile> <Compile Include="..\MyGame\InParentNamespace.cs"> <Link>MyGame\InParentNamespace.cs</Link> </Compile> diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs index a670b194..b09119cb 100644 --- a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs +++ b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs @@ -330,5 +330,58 @@ namespace FlatBuffers.Test Assert.AreEqual(nestedMonsterHp, nestedMonster.Hp); Assert.AreEqual(nestedMonsterName, nestedMonster.Name); } + + [FlatBuffersTestMethod] + public void TestFixedLenghtArrays() + { + FlatBufferBuilder builder = new FlatBufferBuilder(100); + + float a; + int[] b = new int[15]; + sbyte c; + int[,] d_a = new int[2, 2]; + TestEnum[] d_b = new TestEnum[2]; + TestEnum[,] d_c = new TestEnum[2, 2]; + + a = 0.5f; + for (int i = 0; i < 15; i++) b[i] = i; + c = 1; + d_a[0, 0] = 1; + d_a[0, 1] = 2; + d_a[1, 0] = 3; + d_a[1, 1] = 4; + d_b[0] = TestEnum.B; + d_b[1] = TestEnum.C; + d_c[0, 0] = TestEnum.A; + d_c[0, 1] = TestEnum.B; + d_c[1, 0] = TestEnum.C; + d_c[1, 1] = TestEnum.B; + + Offset<ArrayStruct> arrayOffset = ArrayStruct.CreateArrayStruct( + builder, a, b, c, d_a, d_b, d_c); + + // Create a table with the ArrayStruct. + ArrayTable.StartArrayTable(builder); + ArrayTable.AddA(builder, arrayOffset); + Offset<ArrayTable> tableOffset = ArrayTable.EndArrayTable(builder); + + ArrayTable.FinishArrayTableBuffer(builder, tableOffset); + + ArrayTable table = ArrayTable.GetRootAsArrayTable(builder.DataBuffer); + + Assert.AreEqual(table.A?.A, 0.5f); + for (int i = 0; i < 15; i++) Assert.AreEqual(table.A?.B(i), i); + Assert.AreEqual(table.A?.C, (sbyte)1); + Assert.AreEqual(table.A?.D(0).A(0), 1); + Assert.AreEqual(table.A?.D(0).A(1), 2); + Assert.AreEqual(table.A?.D(1).A(0), 3); + Assert.AreEqual(table.A?.D(1).A(1), 4); + Assert.AreEqual(table.A?.D(0).B, TestEnum.B); + Assert.AreEqual(table.A?.D(1).B, TestEnum.C); + Assert.AreEqual(table.A?.D(0).C(0), TestEnum.A); + Assert.AreEqual(table.A?.D(0).C(1), TestEnum.B); + Assert.AreEqual(table.A?.D(1).C(0), TestEnum.C); + Assert.AreEqual(table.A?.D(1).C(1), TestEnum.B); + } } } diff --git a/tests/JavaTest.java b/tests/JavaTest.java index cc079506..65057673 100644 --- a/tests/JavaTest.java +++ b/tests/JavaTest.java @@ -75,6 +75,8 @@ class JavaTest { TestVectorOfUnions(); + TestFixedLengthArrays(); + System.out.println("FlatBuffers test: completed successfully"); } @@ -452,6 +454,58 @@ class JavaTest { TestEq(((Attacker)movie.characters(new Attacker(), 0)).swordAttackDamage(), swordAttackDamage); } + static void TestFixedLengthArrays() { + FlatBufferBuilder builder = new FlatBufferBuilder(0); + + float a; + int[] b = new int[15]; + byte c; + int[][] d_a = new int[2][2]; + byte[] d_b = new byte[2]; + byte[][] d_c = new byte[2][2]; + + a = 0.5f; + for (int i = 0; i < 15; i++) b[i] = i; + c = 1; + d_a[0][0] = 1; + d_a[0][1] = 2; + d_a[1][0] = 3; + d_a[1][1] = 4; + d_b[0] = TestEnum.B; + d_b[1] = TestEnum.C; + d_c[0][0] = TestEnum.A; + d_c[0][1] = TestEnum.B; + d_c[1][0] = TestEnum.C; + d_c[1][1] = TestEnum.B; + + int arrayOffset = ArrayStruct.createArrayStruct(builder, + a, b, c, d_a, d_b, d_c); + + // Create a table with the ArrayStruct. + ArrayTable.startArrayTable(builder); + ArrayTable.addA(builder, arrayOffset); + int tableOffset = ArrayTable.endArrayTable(builder); + + ArrayTable.finishArrayTableBuffer(builder, tableOffset); + + ArrayTable table = ArrayTable.getRootAsArrayTable(builder.dataBuffer()); + NestedStruct nested = new NestedStruct(); + + TestEq(table.a().a(), 0.5f); + for (int i = 0; i < 15; i++) TestEq(table.a().b(i), i); + TestEq(table.a().c(), (byte)1); + TestEq(table.a().d(nested, 0).a(0), 1); + TestEq(table.a().d(nested, 0).a(1), 2); + TestEq(table.a().d(nested, 1).a(0), 3); + TestEq(table.a().d(nested, 1).a(1), 4); + TestEq(table.a().d(nested, 0).b(), TestEnum.B); + TestEq(table.a().d(nested, 1).b(), TestEnum.C); + TestEq(table.a().d(nested, 0).c(0), TestEnum.A); + TestEq(table.a().d(nested, 0).c(1), TestEnum.B); + TestEq(table.a().d(nested, 1).c(0), TestEnum.C); + TestEq(table.a().d(nested, 1).c(1), TestEnum.B); + } + static <T> void TestEq(T a, T b) { if (!a.equals(b)) { System.out.println("" + a.getClass().getName() + " " + b.getClass().getName()); diff --git a/tests/MyGame/Example/ArrayStruct.cs b/tests/MyGame/Example/ArrayStruct.cs new file mode 100644 index 00000000..80f3a9f2 --- /dev/null +++ b/tests/MyGame/Example/ArrayStruct.cs @@ -0,0 +1,50 @@ +// <auto-generated> +// automatically generated by the FlatBuffers compiler, do not modify +// </auto-generated> + +namespace MyGame.Example +{ + +using global::System; +using global::FlatBuffers; + +public struct ArrayStruct : IFlatbufferObject +{ + private Struct __p; + public ByteBuffer ByteBuffer { get { return __p.bb; } } + public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; } + public ArrayStruct __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public float A { get { return __p.bb.GetFloat(__p.bb_pos + 0); } } + public void MutateA(float a) { __p.bb.PutFloat(__p.bb_pos + 0, a); } + public int B(int j) { return __p.bb.GetInt(__p.bb_pos + 4 + j * 4); } + public void MutateB(int j, int b) { __p.bb.PutInt(__p.bb_pos + 4 + j * 4, b); } + public sbyte C { get { return __p.bb.GetSbyte(__p.bb_pos + 64); } } + public void MutateC(sbyte c) { __p.bb.PutSbyte(__p.bb_pos + 64, c); } + public MyGame.Example.NestedStruct D(int j) { return (new MyGame.Example.NestedStruct()).__assign(__p.bb_pos + 68 + j * 12, __p.bb); } + + public static Offset<MyGame.Example.ArrayStruct> CreateArrayStruct(FlatBufferBuilder builder, float A, int[] B, sbyte C, int[,] d_A, MyGame.Example.TestEnum[] d_B, MyGame.Example.TestEnum[,] d_C) { + builder.Prep(4, 92); + for (int _idx0 = 2; _idx0 > 0; _idx0--) { + builder.Prep(4, 12); + builder.Pad(1); + for (int _idx1 = 2; _idx1 > 0; _idx1--) { + builder.PutSbyte((sbyte)d_C[_idx0-1,_idx1-1]); + } + builder.PutSbyte((sbyte)d_B[_idx0-1]); + for (int _idx1 = 2; _idx1 > 0; _idx1--) { + builder.PutInt(d_A[_idx0-1,_idx1-1]); + } + } + builder.Pad(3); + builder.PutSbyte(C); + for (int _idx0 = 15; _idx0 > 0; _idx0--) { + builder.PutInt(B[_idx0-1]); + } + builder.PutFloat(A); + return new Offset<MyGame.Example.ArrayStruct>(builder.Offset); + } +}; + + +} diff --git a/tests/MyGame/Example/ArrayStruct.java b/tests/MyGame/Example/ArrayStruct.java new file mode 100644 index 00000000..5874d3dd --- /dev/null +++ b/tests/MyGame/Example/ArrayStruct.java @@ -0,0 +1,45 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package MyGame.Example; + +import java.nio.*; +import java.lang.*; +import java.util.*; +import com.google.flatbuffers.*; + +@SuppressWarnings("unused") +public final class ArrayStruct extends Struct { + public void __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; } + public ArrayStruct __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public float a() { return bb.getFloat(bb_pos + 0); } + public void mutateA(float a) { bb.putFloat(bb_pos + 0, a); } + public int b(int j) { return bb.getInt(bb_pos + 4 + j * 4); } + public void mutateB(int j, int b) { bb.putInt(bb_pos + 4 + j * 4, b); } + public byte c() { return bb.get(bb_pos + 64); } + public void mutateC(byte c) { bb.put(bb_pos + 64, c); } + public MyGame.Example.NestedStruct d(MyGame.Example.NestedStruct obj, int j) { return obj.__assign(bb_pos + 68 + j * 12, bb); } + + public static int createArrayStruct(FlatBufferBuilder builder, float a, int[] b, byte c, int[][] d_a, byte[] d_b, byte[][] d_c) { + builder.prep(4, 92); + for (int _idx0 = 2; _idx0 > 0; _idx0--) { + builder.prep(4, 12); + builder.pad(1); + for (int _idx1 = 2; _idx1 > 0; _idx1--) { + builder.putByte(d_c[_idx0-1][_idx1-1]); + } + builder.putByte(d_b[_idx0-1]); + for (int _idx1 = 2; _idx1 > 0; _idx1--) { + builder.putInt(d_a[_idx0-1][_idx1-1]); + } + } + builder.pad(3); + builder.putByte(c); + for (int _idx0 = 15; _idx0 > 0; _idx0--) { + builder.putInt(b[_idx0-1]); + } + builder.putFloat(a); + return builder.offset(); + } +} + diff --git a/tests/MyGame/Example/ArrayStruct.py b/tests/MyGame/Example/ArrayStruct.py new file mode 100644 index 00000000..79dda312 --- /dev/null +++ b/tests/MyGame/Example/ArrayStruct.py @@ -0,0 +1,41 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: Example + +import flatbuffers + +class ArrayStruct(object): + __slots__ = ['_tab'] + + # ArrayStruct + def Init(self, buf, pos): + self._tab = flatbuffers.table.Table(buf, pos) + + # ArrayStruct + def A(self): return self._tab.Get(flatbuffers.number_types.Float32Flags, self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(0)) + # ArrayStruct + def B(self): return [self._tab.Get(flatbuffers.number_types.Int32Flags, self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(4 + i * 4)) for i in range(15)] + # ArrayStruct + def C(self): return self._tab.Get(flatbuffers.number_types.Int8Flags, self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(64)) + # ArrayStruct + def D(self, obj, i): + obj.Init(self._tab.Bytes, self._tab.Pos + 68 + i * 12) + return obj + + +def CreateArrayStruct(builder, a, b, c, d_a, d_b, d_c): + builder.Prep(4, 92) + for _idx0 in range(2 , 0, -1): + builder.Prep(4, 12) + builder.Pad(1) + for _idx1 in range(2 , 0, -1): + builder.PrependInt8(d_c[_idx0-1][_idx1-1]) + builder.PrependInt8(d_b[_idx0-1]) + for _idx1 in range(2 , 0, -1): + builder.PrependInt32(d_a[_idx0-1][_idx1-1]) + builder.Pad(3) + builder.PrependInt8(c) + for _idx0 in range(15 , 0, -1): + builder.PrependInt32(b[_idx0-1]) + builder.PrependFloat32(a) + return builder.Offset() diff --git a/tests/MyGame/Example/ArrayTable.cs b/tests/MyGame/Example/ArrayTable.cs new file mode 100644 index 00000000..fe9b8c9a --- /dev/null +++ b/tests/MyGame/Example/ArrayTable.cs @@ -0,0 +1,34 @@ +// <auto-generated> +// automatically generated by the FlatBuffers compiler, do not modify +// </auto-generated> + +namespace MyGame.Example +{ + +using global::System; +using global::FlatBuffers; + +public struct ArrayTable : IFlatbufferObject +{ + private Table __p; + public ByteBuffer ByteBuffer { get { return __p.bb; } } + public static ArrayTable GetRootAsArrayTable(ByteBuffer _bb) { return GetRootAsArrayTable(_bb, new ArrayTable()); } + public static ArrayTable GetRootAsArrayTable(ByteBuffer _bb, ArrayTable obj) { FlatBufferConstants.FLATBUFFERS_1_11_1(); return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); } + public static bool ArrayTableBufferHasIdentifier(ByteBuffer _bb) { return Table.__has_identifier(_bb, "ARRT"); } + public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; } + public ArrayTable __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public MyGame.Example.ArrayStruct? A { get { int o = __p.__offset(4); return o != 0 ? (MyGame.Example.ArrayStruct?)(new MyGame.Example.ArrayStruct()).__assign(o + __p.bb_pos, __p.bb) : null; } } + + public static void StartArrayTable(FlatBufferBuilder builder) { builder.StartTable(1); } + public static void AddA(FlatBufferBuilder builder, Offset<MyGame.Example.ArrayStruct> aOffset) { builder.AddStruct(0, aOffset.Value, 0); } + public static Offset<MyGame.Example.ArrayTable> EndArrayTable(FlatBufferBuilder builder) { + int o = builder.EndTable(); + return new Offset<MyGame.Example.ArrayTable>(o); + } + public static void FinishArrayTableBuffer(FlatBufferBuilder builder, Offset<MyGame.Example.ArrayTable> offset) { builder.Finish(offset.Value, "ARRT"); } + public static void FinishSizePrefixedArrayTableBuffer(FlatBufferBuilder builder, Offset<MyGame.Example.ArrayTable> offset) { builder.FinishSizePrefixed(offset.Value, "ARRT"); } +}; + + +} diff --git a/tests/MyGame/Example/ArrayTable.java b/tests/MyGame/Example/ArrayTable.java new file mode 100644 index 00000000..2ef18f66 --- /dev/null +++ b/tests/MyGame/Example/ArrayTable.java @@ -0,0 +1,30 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package MyGame.Example; + +import java.nio.*; +import java.lang.*; +import java.util.*; +import com.google.flatbuffers.*; + +@SuppressWarnings("unused") +public final class ArrayTable extends Table { + public static ArrayTable getRootAsArrayTable(ByteBuffer _bb) { return getRootAsArrayTable(_bb, new ArrayTable()); } + public static ArrayTable getRootAsArrayTable(ByteBuffer _bb, ArrayTable obj) { Constants.FLATBUFFERS_1_11_1(); _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__assign(_bb.getInt(_bb.position()) + _bb.position(), _bb)); } + public static boolean ArrayTableBufferHasIdentifier(ByteBuffer _bb) { return __has_identifier(_bb, "ARRT"); } + public void __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; vtable_start = bb_pos - bb.getInt(bb_pos); vtable_size = bb.getShort(vtable_start); } + public ArrayTable __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public MyGame.Example.ArrayStruct a() { return a(new MyGame.Example.ArrayStruct()); } + public MyGame.Example.ArrayStruct a(MyGame.Example.ArrayStruct obj) { int o = __offset(4); return o != 0 ? obj.__assign(o + bb_pos, bb) : null; } + + public static void startArrayTable(FlatBufferBuilder builder) { builder.startTable(1); } + public static void addA(FlatBufferBuilder builder, int aOffset) { builder.addStruct(0, aOffset, 0); } + public static int endArrayTable(FlatBufferBuilder builder) { + int o = builder.endTable(); + return o; + } + public static void finishArrayTableBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset, "ARRT"); } + public static void finishSizePrefixedArrayTableBuffer(FlatBufferBuilder builder, int offset) { builder.finishSizePrefixed(offset, "ARRT"); } +} + diff --git a/tests/MyGame/Example/ArrayTable.py b/tests/MyGame/Example/ArrayTable.py new file mode 100644 index 00000000..12eefd31 --- /dev/null +++ b/tests/MyGame/Example/ArrayTable.py @@ -0,0 +1,34 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: Example + +import flatbuffers + +class ArrayTable(object): + __slots__ = ['_tab'] + + @classmethod + def GetRootAsArrayTable(cls, buf, offset): + n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) + x = ArrayTable() + x.Init(buf, n + offset) + return x + + # ArrayTable + def Init(self, buf, pos): + self._tab = flatbuffers.table.Table(buf, pos) + + # ArrayTable + def A(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) + if o != 0: + x = o + self._tab.Pos + from .ArrayStruct import ArrayStruct + obj = ArrayStruct() + obj.Init(self._tab.Bytes, x) + return obj + return None + +def ArrayTableStart(builder): builder.StartObject(1) +def ArrayTableAddA(builder, a): builder.PrependStructSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(a), 0) +def ArrayTableEnd(builder): return builder.EndObject() diff --git a/tests/MyGame/Example/NestedStruct.cs b/tests/MyGame/Example/NestedStruct.cs new file mode 100644 index 00000000..b5da5c02 --- /dev/null +++ b/tests/MyGame/Example/NestedStruct.cs @@ -0,0 +1,40 @@ +// <auto-generated> +// automatically generated by the FlatBuffers compiler, do not modify +// </auto-generated> + +namespace MyGame.Example +{ + +using global::System; +using global::FlatBuffers; + +public struct NestedStruct : IFlatbufferObject +{ + private Struct __p; + public ByteBuffer ByteBuffer { get { return __p.bb; } } + public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; } + public NestedStruct __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public int A(int j) { return __p.bb.GetInt(__p.bb_pos + 0 + j * 4); } + public void MutateA(int j, int a) { __p.bb.PutInt(__p.bb_pos + 0 + j * 4, a); } + public MyGame.Example.TestEnum B { get { return (MyGame.Example.TestEnum)__p.bb.GetSbyte(__p.bb_pos + 8); } } + public void MutateB(MyGame.Example.TestEnum b) { __p.bb.PutSbyte(__p.bb_pos + 8, (sbyte)b); } + public MyGame.Example.TestEnum C(int j) { return (MyGame.Example.TestEnum)__p.bb.GetSbyte(__p.bb_pos + 9 + j * 1); } + public void MutateC(int j, MyGame.Example.TestEnum c) { __p.bb.PutSbyte(__p.bb_pos + 9 + j * 1, (sbyte)c); } + + public static Offset<MyGame.Example.NestedStruct> CreateNestedStruct(FlatBufferBuilder builder, int[] A, MyGame.Example.TestEnum B, MyGame.Example.TestEnum[] C) { + builder.Prep(4, 12); + builder.Pad(1); + for (int _idx0 = 2; _idx0 > 0; _idx0--) { + builder.PutSbyte((sbyte)C[_idx0-1]); + } + builder.PutSbyte((sbyte)B); + for (int _idx0 = 2; _idx0 > 0; _idx0--) { + builder.PutInt(A[_idx0-1]); + } + return new Offset<MyGame.Example.NestedStruct>(builder.Offset); + } +}; + + +} diff --git a/tests/MyGame/Example/NestedStruct.java b/tests/MyGame/Example/NestedStruct.java new file mode 100644 index 00000000..6e1df4c5 --- /dev/null +++ b/tests/MyGame/Example/NestedStruct.java @@ -0,0 +1,35 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package MyGame.Example; + +import java.nio.*; +import java.lang.*; +import java.util.*; +import com.google.flatbuffers.*; + +@SuppressWarnings("unused") +public final class NestedStruct extends Struct { + public void __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; } + public NestedStruct __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public int a(int j) { return bb.getInt(bb_pos + 0 + j * 4); } + public void mutateA(int j, int a) { bb.putInt(bb_pos + 0 + j * 4, a); } + public byte b() { return bb.get(bb_pos + 8); } + public void mutateB(byte b) { bb.put(bb_pos + 8, b); } + public byte c(int j) { return bb.get(bb_pos + 9 + j * 1); } + public void mutateC(int j, byte c) { bb.put(bb_pos + 9 + j * 1, c); } + + public static int createNestedStruct(FlatBufferBuilder builder, int[] a, byte b, byte[] c) { + builder.prep(4, 12); + builder.pad(1); + for (int _idx0 = 2; _idx0 > 0; _idx0--) { + builder.putByte(c[_idx0-1]); + } + builder.putByte(b); + for (int _idx0 = 2; _idx0 > 0; _idx0--) { + builder.putInt(a[_idx0-1]); + } + return builder.offset(); + } +} + diff --git a/tests/MyGame/Example/NestedStruct.py b/tests/MyGame/Example/NestedStruct.py new file mode 100644 index 00000000..aa742f4b --- /dev/null +++ b/tests/MyGame/Example/NestedStruct.py @@ -0,0 +1,29 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: Example + +import flatbuffers + +class NestedStruct(object): + __slots__ = ['_tab'] + + # NestedStruct + def Init(self, buf, pos): + self._tab = flatbuffers.table.Table(buf, pos) + + # NestedStruct + def A(self): return [self._tab.Get(flatbuffers.number_types.Int32Flags, self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(0 + i * 4)) for i in range(2)] + # NestedStruct + def B(self): return self._tab.Get(flatbuffers.number_types.Int8Flags, self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(8)) + # NestedStruct + def C(self): return [self._tab.Get(flatbuffers.number_types.Int8Flags, self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(9 + i * 1)) for i in range(2)] + +def CreateNestedStruct(builder, a, b, c): + builder.Prep(4, 12) + builder.Pad(1) + for _idx0 in range(2 , 0, -1): + builder.PrependInt8(c[_idx0-1]) + builder.PrependInt8(b) + for _idx0 in range(2 , 0, -1): + builder.PrependInt32(a[_idx0-1]) + return builder.Offset() diff --git a/tests/MyGame/Example/TestEnum.cs b/tests/MyGame/Example/TestEnum.cs new file mode 100644 index 00000000..22e83b33 --- /dev/null +++ b/tests/MyGame/Example/TestEnum.cs @@ -0,0 +1,16 @@ +// <auto-generated> +// automatically generated by the FlatBuffers compiler, do not modify +// </auto-generated> + +namespace MyGame.Example +{ + +public enum TestEnum : sbyte +{ + A = 0, + B = 1, + C = 2, +}; + + +} diff --git a/tests/MyGame/Example/TestEnum.java b/tests/MyGame/Example/TestEnum.java new file mode 100644 index 00000000..411bf8e3 --- /dev/null +++ b/tests/MyGame/Example/TestEnum.java @@ -0,0 +1,15 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package MyGame.Example; + +public final class TestEnum { + private TestEnum() { } + public static final byte A = 0; + public static final byte B = 1; + public static final byte C = 2; + + public static final String[] names = { "A", "B", "C", }; + + public static String name(int e) { return names[e]; } +} + diff --git a/tests/MyGame/Example/TestEnum.py b/tests/MyGame/Example/TestEnum.py new file mode 100644 index 00000000..d49f10a1 --- /dev/null +++ b/tests/MyGame/Example/TestEnum.py @@ -0,0 +1,9 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: Example + +class TestEnum(object): + A = 0 + B = 1 + C = 2 + diff --git a/tests/arrays_test.bfbs b/tests/arrays_test.bfbs Binary files differnew file mode 100644 index 00000000..2a899681 --- /dev/null +++ b/tests/arrays_test.bfbs diff --git a/tests/arrays_test.fbs b/tests/arrays_test.fbs new file mode 100644 index 00000000..40bce66e --- /dev/null +++ b/tests/arrays_test.fbs @@ -0,0 +1,24 @@ +namespace MyGame.Example; + +enum TestEnum : byte { A, B, C } + +struct NestedStruct{ + a:[int:2]; + b:TestEnum; + c:[TestEnum:2]; +} + +struct ArrayStruct{ + a:float; + b:[int:0xF]; + c:byte; + d:[NestedStruct:2]; +} + +table ArrayTable{ + a:ArrayStruct; +} + +root_type ArrayTable; +file_identifier "ARRT"; +file_extension "mon"; diff --git a/tests/arrays_test.golden b/tests/arrays_test.golden new file mode 100644 index 00000000..c032688b --- /dev/null +++ b/tests/arrays_test.golden @@ -0,0 +1,19 @@ +{ + a : { + a: 12.34, + b: [1,2,3,4,5,6,7,8,9,0xA,0xB,0xC,0xD,0xE,0xF], + c: -127, + d: [ + { + a : [-1,2], + b : A, + c : [C, B] + }, + { + a : [3,-4], + b : B, + c : [B, A] + } + ] + } +}
\ No newline at end of file diff --git a/tests/arrays_test.schema.json b/tests/arrays_test.schema.json new file mode 100644 index 00000000..6803a1a5 --- /dev/null +++ b/tests/arrays_test.schema.json @@ -0,0 +1,60 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "MyGame_Example_TestEnum" : { + "type" : "string", + "enum": ["A", "B", "C"] + }, + "MyGame_Example_NestedStruct" : { + "type" : "object", + "properties" : { + "a" : { + "type" : "array", "items" : { "type" : "number" }, + "minItems": 2, + "maxItems": 2 + }, + "b" : { + "$ref" : "#/definitions/MyGame_Example_TestEnum" + }, + "c" : { + "$ref" : "#/definitions/MyGame_Example_TestEnum", + "minItems": 2, + "maxItems": 2 + } + }, + "additionalProperties" : false + }, + "MyGame_Example_ArrayStruct" : { + "type" : "object", + "properties" : { + "a" : { + "type" : "number" + }, + "b" : { + "type" : "array", "items" : { "type" : "number" }, + "minItems": 15, + "maxItems": 15 + }, + "c" : { + "type" : "number" + }, + "d" : { + "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_NestedStruct" }, + "minItems": 2, + "maxItems": 2 + } + }, + "additionalProperties" : false + }, + "MyGame_Example_ArrayTable" : { + "type" : "object", + "properties" : { + "a" : { + "$ref" : "#/definitions/MyGame_Example_ArrayStruct" + } + }, + "additionalProperties" : false + } + }, + "$ref" : "#/definitions/MyGame_Example_ArrayTable" +} diff --git a/tests/arrays_test_generated.h b/tests/arrays_test_generated.h new file mode 100644 index 00000000..9875785b --- /dev/null +++ b/tests/arrays_test_generated.h @@ -0,0 +1,419 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_ARRAYSTEST_MYGAME_EXAMPLE_H_ +#define FLATBUFFERS_GENERATED_ARRAYSTEST_MYGAME_EXAMPLE_H_ + +#include "flatbuffers/flatbuffers.h" + +namespace MyGame { +namespace Example { + +struct NestedStruct; + +struct ArrayStruct; + +struct ArrayTable; +struct ArrayTableT; + +bool operator==(const NestedStruct &lhs, const NestedStruct &rhs); +bool operator!=(const NestedStruct &lhs, const NestedStruct &rhs); +bool operator==(const ArrayStruct &lhs, const ArrayStruct &rhs); +bool operator!=(const ArrayStruct &lhs, const ArrayStruct &rhs); +bool operator==(const ArrayTableT &lhs, const ArrayTableT &rhs); +bool operator!=(const ArrayTableT &lhs, const ArrayTableT &rhs); + +inline const flatbuffers::TypeTable *NestedStructTypeTable(); + +inline const flatbuffers::TypeTable *ArrayStructTypeTable(); + +inline const flatbuffers::TypeTable *ArrayTableTypeTable(); + +enum class TestEnum : int8_t { + A = 0, + B = 1, + C = 2, + MIN = A, + MAX = C +}; + +inline const TestEnum (&EnumValuesTestEnum())[3] { + static const TestEnum values[] = { + TestEnum::A, + TestEnum::B, + TestEnum::C + }; + return values; +} + +inline const char * const *EnumNamesTestEnum() { + static const char * const names[4] = { + "A", + "B", + "C", + nullptr + }; + return names; +} + +inline const char *EnumNameTestEnum(TestEnum e) { + if (e < TestEnum::A || e > TestEnum::C) return ""; + const size_t index = static_cast<size_t>(e); + return EnumNamesTestEnum()[index]; +} + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) NestedStruct FLATBUFFERS_FINAL_CLASS { + private: + int32_t a_[2]; + int8_t b_; + int8_t c_[2]; + int8_t padding0__; + + public: + static const flatbuffers::TypeTable *MiniReflectTypeTable() { + return NestedStructTypeTable(); + } + NestedStruct() { + memset(static_cast<void *>(this), 0, sizeof(NestedStruct)); + } + NestedStruct(MyGame::Example::TestEnum _b) + : b_(flatbuffers::EndianScalar(static_cast<int8_t>(_b))) { + std::memset(a_, 0, sizeof(a_)); + std::memset(c_, 0, sizeof(c_)); + (void)padding0__; + } + const flatbuffers::Array<int32_t, 2> *a() const { + return reinterpret_cast<const flatbuffers::Array<int32_t, 2> *>(a_); + } + flatbuffers::Array<int32_t, 2> *mutable_a() { + return reinterpret_cast<flatbuffers::Array<int32_t, 2> *>(a_); + } + MyGame::Example::TestEnum b() const { + return static_cast<MyGame::Example::TestEnum>(flatbuffers::EndianScalar(b_)); + } + void mutate_b(MyGame::Example::TestEnum _b) { + flatbuffers::WriteScalar(&b_, static_cast<int8_t>(_b)); + } + const flatbuffers::Array<MyGame::Example::TestEnum, 2> *c() const { + return reinterpret_cast<const flatbuffers::Array<MyGame::Example::TestEnum, 2> *>(c_); + } + flatbuffers::Array<MyGame::Example::TestEnum, 2> *mutable_c() { + return reinterpret_cast<flatbuffers::Array<MyGame::Example::TestEnum, 2> *>(c_); + } +}; +FLATBUFFERS_STRUCT_END(NestedStruct, 12); + +inline bool operator==(const NestedStruct &lhs, const NestedStruct &rhs) { + return + (lhs.a() == rhs.a()) && + (lhs.b() == rhs.b()) && + (lhs.c() == rhs.c()); +} + +inline bool operator!=(const NestedStruct &lhs, const NestedStruct &rhs) { + return !(lhs == rhs); +} + + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) ArrayStruct FLATBUFFERS_FINAL_CLASS { + private: + float a_; + int32_t b_[15]; + int8_t c_; + int8_t padding0__; int16_t padding1__; + MyGame::Example::NestedStruct d_[2]; + + public: + static const flatbuffers::TypeTable *MiniReflectTypeTable() { + return ArrayStructTypeTable(); + } + ArrayStruct() { + memset(static_cast<void *>(this), 0, sizeof(ArrayStruct)); + } + ArrayStruct(float _a, int8_t _c) + : a_(flatbuffers::EndianScalar(_a)), + c_(flatbuffers::EndianScalar(_c)), + padding0__(0), + padding1__(0) { + std::memset(b_, 0, sizeof(b_)); + (void)padding0__; (void)padding1__; + std::memset(d_, 0, sizeof(d_)); + } + float a() const { + return flatbuffers::EndianScalar(a_); + } + void mutate_a(float _a) { + flatbuffers::WriteScalar(&a_, _a); + } + const flatbuffers::Array<int32_t, 15> *b() const { + return reinterpret_cast<const flatbuffers::Array<int32_t, 15> *>(b_); + } + flatbuffers::Array<int32_t, 15> *mutable_b() { + return reinterpret_cast<flatbuffers::Array<int32_t, 15> *>(b_); + } + int8_t c() const { + return flatbuffers::EndianScalar(c_); + } + void mutate_c(int8_t _c) { + flatbuffers::WriteScalar(&c_, _c); + } + const flatbuffers::Array<MyGame::Example::NestedStruct, 2> *d() const { + return reinterpret_cast<const flatbuffers::Array<MyGame::Example::NestedStruct, 2> *>(d_); + } + flatbuffers::Array<MyGame::Example::NestedStruct, 2> *mutable_d() { + return reinterpret_cast<flatbuffers::Array<MyGame::Example::NestedStruct, 2> *>(d_); + } +}; +FLATBUFFERS_STRUCT_END(ArrayStruct, 92); + +inline bool operator==(const ArrayStruct &lhs, const ArrayStruct &rhs) { + return + (lhs.a() == rhs.a()) && + (lhs.b() == rhs.b()) && + (lhs.c() == rhs.c()) && + (lhs.d() == rhs.d()); +} + +inline bool operator!=(const ArrayStruct &lhs, const ArrayStruct &rhs) { + return !(lhs == rhs); +} + + +struct ArrayTableT : public flatbuffers::NativeTable { + typedef ArrayTable TableType; + flatbuffers::unique_ptr<MyGame::Example::ArrayStruct> a; + ArrayTableT() { + } +}; + +inline bool operator==(const ArrayTableT &lhs, const ArrayTableT &rhs) { + return + (lhs.a == rhs.a); +} + +inline bool operator!=(const ArrayTableT &lhs, const ArrayTableT &rhs) { + return !(lhs == rhs); +} + + +struct ArrayTable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef ArrayTableT NativeTableType; + static const flatbuffers::TypeTable *MiniReflectTypeTable() { + return ArrayTableTypeTable(); + } + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_A = 4 + }; + const MyGame::Example::ArrayStruct *a() const { + return GetStruct<const MyGame::Example::ArrayStruct *>(VT_A); + } + MyGame::Example::ArrayStruct *mutable_a() { + return GetStruct<MyGame::Example::ArrayStruct *>(VT_A); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField<MyGame::Example::ArrayStruct>(verifier, VT_A) && + verifier.EndTable(); + } + ArrayTableT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ArrayTableT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset<ArrayTable> Pack(flatbuffers::FlatBufferBuilder &_fbb, const ArrayTableT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ArrayTableBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(const MyGame::Example::ArrayStruct *a) { + fbb_.AddStruct(ArrayTable::VT_A, a); + } + explicit ArrayTableBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ArrayTableBuilder &operator=(const ArrayTableBuilder &); + flatbuffers::Offset<ArrayTable> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<ArrayTable>(end); + return o; + } +}; + +inline flatbuffers::Offset<ArrayTable> CreateArrayTable( + flatbuffers::FlatBufferBuilder &_fbb, + const MyGame::Example::ArrayStruct *a = 0) { + ArrayTableBuilder builder_(_fbb); + builder_.add_a(a); + return builder_.Finish(); +} + +flatbuffers::Offset<ArrayTable> CreateArrayTable(flatbuffers::FlatBufferBuilder &_fbb, const ArrayTableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +inline ArrayTableT *ArrayTable::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new ArrayTableT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void ArrayTable::UnPackTo(ArrayTableT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = a(); if (_e) _o->a = flatbuffers::unique_ptr<MyGame::Example::ArrayStruct>(new MyGame::Example::ArrayStruct(*_e)); }; +} + +inline flatbuffers::Offset<ArrayTable> ArrayTable::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ArrayTableT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateArrayTable(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset<ArrayTable> CreateArrayTable(flatbuffers::FlatBufferBuilder &_fbb, const ArrayTableT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const ArrayTableT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _a = _o->a ? _o->a.get() : 0; + return MyGame::Example::CreateArrayTable( + _fbb, + _a); +} + +inline const flatbuffers::TypeTable *TestEnumTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_CHAR, 0, 0 } + }; + static const flatbuffers::TypeFunction type_refs[] = { + MyGame::Example::TestEnumTypeTable + }; + static const char * const names[] = { + "A", + "B", + "C" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_ENUM, 3, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +inline const flatbuffers::TypeTable *NestedStructTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, -1 }, + { flatbuffers::ET_CHAR, 0, 0 }, + { flatbuffers::ET_SEQUENCE, 0, 0 } + }; + static const flatbuffers::TypeFunction type_refs[] = { + MyGame::Example::TestEnumTypeTable + }; + static const int64_t values[] = { 0, 8, 9, 12 }; + static const char * const names[] = { + "a", + "b", + "c" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 3, type_codes, type_refs, values, names + }; + return &tt; +} + +inline const flatbuffers::TypeTable *ArrayStructTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_FLOAT, 0, -1 }, + { flatbuffers::ET_SEQUENCE, 0, -1 }, + { flatbuffers::ET_CHAR, 0, -1 }, + { flatbuffers::ET_SEQUENCE, 0, 0 } + }; + static const flatbuffers::TypeFunction type_refs[] = { + MyGame::Example::NestedStructTypeTable + }; + static const int64_t values[] = { 0, 4, 64, 68, 92 }; + static const char * const names[] = { + "a", + "b", + "c", + "d" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_STRUCT, 4, type_codes, type_refs, values, names + }; + return &tt; +} + +inline const flatbuffers::TypeTable *ArrayTableTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, 0 } + }; + static const flatbuffers::TypeFunction type_refs[] = { + MyGame::Example::ArrayStructTypeTable + }; + static const char * const names[] = { + "a" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +inline const MyGame::Example::ArrayTable *GetArrayTable(const void *buf) { + return flatbuffers::GetRoot<MyGame::Example::ArrayTable>(buf); +} + +inline const MyGame::Example::ArrayTable *GetSizePrefixedArrayTable(const void *buf) { + return flatbuffers::GetSizePrefixedRoot<MyGame::Example::ArrayTable>(buf); +} + +inline ArrayTable *GetMutableArrayTable(void *buf) { + return flatbuffers::GetMutableRoot<ArrayTable>(buf); +} + +inline const char *ArrayTableIdentifier() { + return "ARRT"; +} + +inline bool ArrayTableBufferHasIdentifier(const void *buf) { + return flatbuffers::BufferHasIdentifier( + buf, ArrayTableIdentifier()); +} + +inline bool VerifyArrayTableBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer<MyGame::Example::ArrayTable>(ArrayTableIdentifier()); +} + +inline bool VerifySizePrefixedArrayTableBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer<MyGame::Example::ArrayTable>(ArrayTableIdentifier()); +} + +inline const char *ArrayTableExtension() { + return "mon"; +} + +inline void FinishArrayTableBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset<MyGame::Example::ArrayTable> root) { + fbb.Finish(root, ArrayTableIdentifier()); +} + +inline void FinishSizePrefixedArrayTableBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset<MyGame::Example::ArrayTable> root) { + fbb.FinishSizePrefixed(root, ArrayTableIdentifier()); +} + +inline flatbuffers::unique_ptr<MyGame::Example::ArrayTableT> UnPackArrayTable( + const void *buf, + const flatbuffers::resolver_function_t *res = nullptr) { + return flatbuffers::unique_ptr<MyGame::Example::ArrayTableT>(GetArrayTable(buf)->UnPack(res)); +} + +inline flatbuffers::unique_ptr<MyGame::Example::ArrayTableT> UnPackSizePrefixedArrayTable( + const void *buf, + const flatbuffers::resolver_function_t *res = nullptr) { + return flatbuffers::unique_ptr<MyGame::Example::ArrayTableT>(GetSizePrefixedArrayTable(buf)->UnPack(res)); +} + +} // namespace Example +} // namespace MyGame + +#endif // FLATBUFFERS_GENERATED_ARRAYSTEST_MYGAME_EXAMPLE_H_ diff --git a/tests/generate_code.bat b/tests/generate_code.bat index 712e76d0..3164dff4 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -19,7 +19,9 @@ if "%1"=="-b" set buildtype=%2 ..\%buildtype%\flatc.exe --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs || goto FAIL ..\%buildtype%\flatc.exe --cpp --java --csharp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs || goto FAIL ..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs || goto FAIL +..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins -I include_test arrays_test.fbs || goto FAIL ..\%buildtype%\flatc.exe --jsonschema --schema -I include_test monster_test.fbs || goto FAIL +..\%buildtype%\flatc.exe --cpp --java --csharp --python --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --scoped-enums --jsonschema --cpp-ptr-type flatbuffers::unique_ptr arrays_test.fbs || goto FAIL IF NOT "%MONSTER_EXTRA%"=="skip" ( @echo Generate MosterExtra diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 07aa7200..3220b987 100755 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -19,8 +19,10 @@ set -e ../flatc --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --cpp --java --csharp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs ../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs +../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test arrays_test.fbs ../flatc --jsonschema --schema -I include_test monster_test.fbs -../flatc --cpp --java --csharp --python --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes monster_extra.fbs monsterdata_extra.json || goto FAIL +../flatc --cpp --java --csharp --python --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes monster_extra.fbs monsterdata_extra.json +../flatc --cpp --java --csharp --python --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --scoped-enums --jsonschema --cpp-ptr-type flatbuffers::unique_ptr arrays_test.fbs cd ../samples ../flatc --cpp --lobster --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr monster.fbs ../flatc -b --schema --bfbs-comments --bfbs-builtins monster.fbs diff --git a/tests/monster_test.schema.json b/tests/monster_test.schema.json index 5fac0778..7fb7500b 100644 --- a/tests/monster_test.schema.json +++ b/tests/monster_test.schema.json @@ -24,21 +24,27 @@ "MyGame_OtherNameSpace_Unused" : { "type" : "object", "properties" : { - "a" : { "type" : "number" } + "a" : { + "type" : "number" + } }, "additionalProperties" : false }, "MyGame_OtherNameSpace_TableB" : { "type" : "object", "properties" : { - "a" : { "$ref" : "#/definitions/TableA" } + "a" : { + "$ref" : "#/definitions/TableA" + } }, "additionalProperties" : false }, "TableA" : { "type" : "object", "properties" : { - "b" : { "$ref" : "#/definitions/MyGame_OtherNameSpace_TableB" } + "b" : { + "$ref" : "#/definitions/MyGame_OtherNameSpace_TableB" + } }, "additionalProperties" : false }, @@ -57,51 +63,81 @@ "MyGame_Example_Test" : { "type" : "object", "properties" : { - "a" : { "type" : "number" }, - "b" : { "type" : "number" } + "a" : { + "type" : "number" + }, + "b" : { + "type" : "number" + } }, "additionalProperties" : false }, "MyGame_Example_TestSimpleTableWithEnum" : { "type" : "object", "properties" : { - "color" : { "$ref" : "#/definitions/MyGame_Example_Color" } + "color" : { + "$ref" : "#/definitions/MyGame_Example_Color" + } }, "additionalProperties" : false }, "MyGame_Example_Vec3" : { "type" : "object", "properties" : { - "x" : { "type" : "number" }, - "y" : { "type" : "number" }, - "z" : { "type" : "number" }, - "test1" : { "type" : "number" }, - "test2" : { "$ref" : "#/definitions/MyGame_Example_Color" }, - "test3" : { "$ref" : "#/definitions/MyGame_Example_Test" } + "x" : { + "type" : "number" + }, + "y" : { + "type" : "number" + }, + "z" : { + "type" : "number" + }, + "test1" : { + "type" : "number" + }, + "test2" : { + "$ref" : "#/definitions/MyGame_Example_Color" + }, + "test3" : { + "$ref" : "#/definitions/MyGame_Example_Test" + } }, "additionalProperties" : false }, "MyGame_Example_Ability" : { "type" : "object", "properties" : { - "id" : { "type" : "number" }, - "distance" : { "type" : "number" } + "id" : { + "type" : "number" + }, + "distance" : { + "type" : "number" + } }, "additionalProperties" : false }, "MyGame_Example_Stat" : { "type" : "object", "properties" : { - "id" : { "type" : "string" }, - "val" : { "type" : "number" }, - "count" : { "type" : "number" } + "id" : { + "type" : "string" + }, + "val" : { + "type" : "number" + }, + "count" : { + "type" : "number" + } }, "additionalProperties" : false }, "MyGame_Example_Referrable" : { "type" : "object", "properties" : { - "id" : { "type" : "number" } + "id" : { + "type" : "number" + } }, "additionalProperties" : false }, @@ -109,54 +145,150 @@ "type" : "object", "description" : " an example documentation comment: monster object", "properties" : { - "pos" : { "$ref" : "#/definitions/MyGame_Example_Vec3" }, - "mana" : { "type" : "number" }, - "hp" : { "type" : "number" }, - "name" : { "type" : "string" }, - "friendly" : { "type" : "boolean" }, - "inventory" : { "type" : "array", "items" : { "type" : "number" } }, - "color" : { "$ref" : "#/definitions/MyGame_Example_Color" }, - "test_type" : { "$ref" : "#/definitions/MyGame_Example_Any" }, - "test" : { "anyOf": [{ "$ref" : "#/definitions/MyGame_Example_Monster" },{ "$ref" : "#/definitions/MyGame_Example_TestSimpleTableWithEnum" },{ "$ref" : "#/definitions/MyGame_Example2_Monster" }] }, - "test4" : { "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Test" } }, - "testarrayofstring" : { "type" : "array", "items" : { "type" : "string" } }, - "testarrayoftables" : { "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Monster" } }, - "enemy" : { "$ref" : "#/definitions/MyGame_Example_Monster" }, - "testnestedflatbuffer" : { "type" : "array", "items" : { "type" : "number" } }, - "testempty" : { "$ref" : "#/definitions/MyGame_Example_Stat" }, - "testbool" : { "type" : "boolean" }, - "testhashs32_fnv1" : { "type" : "number" }, - "testhashu32_fnv1" : { "type" : "number" }, - "testhashs64_fnv1" : { "type" : "number" }, - "testhashu64_fnv1" : { "type" : "number" }, - "testhashs32_fnv1a" : { "type" : "number" }, - "testhashu32_fnv1a" : { "type" : "number" }, - "testhashs64_fnv1a" : { "type" : "number" }, - "testhashu64_fnv1a" : { "type" : "number" }, - "testarrayofbools" : { "type" : "array", "items" : { "type" : "boolean" } }, - "testf" : { "type" : "number" }, - "testf2" : { "type" : "number" }, - "testf3" : { "type" : "number" }, - "testarrayofstring2" : { "type" : "array", "items" : { "type" : "string" } }, - "testarrayofsortedstruct" : { "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Ability" } }, - "flex" : { "type" : "array", "items" : { "type" : "number" } }, - "test5" : { "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Test" } }, - "vector_of_longs" : { "type" : "array", "items" : { "type" : "number" } }, - "vector_of_doubles" : { "type" : "array", "items" : { "type" : "number" } }, - "parent_namespace_test" : { "$ref" : "#/definitions/MyGame_InParentNamespace" }, - "vector_of_referrables" : { "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Referrable" } }, - "single_weak_reference" : { "type" : "number" }, - "vector_of_weak_references" : { "type" : "array", "items" : { "type" : "number" } }, - "vector_of_strong_referrables" : { "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Referrable" } }, - "co_owning_reference" : { "type" : "number" }, - "vector_of_co_owning_references" : { "type" : "array", "items" : { "type" : "number" } }, - "non_owning_reference" : { "type" : "number" }, - "vector_of_non_owning_references" : { "type" : "array", "items" : { "type" : "number" } }, - "any_unique_type" : { "$ref" : "#/definitions/MyGame_Example_AnyUniqueAliases" }, - "any_unique" : { "anyOf": [{ "$ref" : "#/definitions/MyGame_Example_Monster" },{ "$ref" : "#/definitions/MyGame_Example_TestSimpleTableWithEnum" },{ "$ref" : "#/definitions/MyGame_Example2_Monster" }] }, - "any_ambiguous_type" : { "$ref" : "#/definitions/MyGame_Example_AnyAmbiguousAliases" }, - "any_ambiguous" : { "anyOf": [{ "$ref" : "#/definitions/MyGame_Example_Monster" },{ "$ref" : "#/definitions/MyGame_Example_Monster" },{ "$ref" : "#/definitions/MyGame_Example_Monster" }] }, - "vector_of_enums" : { "$ref" : "#/definitions/MyGame_Example_Color" } + "pos" : { + "$ref" : "#/definitions/MyGame_Example_Vec3" + }, + "mana" : { + "type" : "number" + }, + "hp" : { + "type" : "number" + }, + "name" : { + "type" : "string" + }, + "friendly" : { + "type" : "boolean" + }, + "inventory" : { + "type" : "array", "items" : { "type" : "number" } + }, + "color" : { + "$ref" : "#/definitions/MyGame_Example_Color" + }, + "test_type" : { + "$ref" : "#/definitions/MyGame_Example_Any" + }, + "test" : { + "anyOf": [{ "$ref" : "#/definitions/MyGame_Example_Monster" },{ "$ref" : "#/definitions/MyGame_Example_TestSimpleTableWithEnum" },{ "$ref" : "#/definitions/MyGame_Example2_Monster" }] + }, + "test4" : { + "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Test" } + }, + "testarrayofstring" : { + "type" : "array", "items" : { "type" : "string" } + }, + "testarrayoftables" : { + "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Monster" } + }, + "enemy" : { + "$ref" : "#/definitions/MyGame_Example_Monster" + }, + "testnestedflatbuffer" : { + "type" : "array", "items" : { "type" : "number" } + }, + "testempty" : { + "$ref" : "#/definitions/MyGame_Example_Stat" + }, + "testbool" : { + "type" : "boolean" + }, + "testhashs32_fnv1" : { + "type" : "number" + }, + "testhashu32_fnv1" : { + "type" : "number" + }, + "testhashs64_fnv1" : { + "type" : "number" + }, + "testhashu64_fnv1" : { + "type" : "number" + }, + "testhashs32_fnv1a" : { + "type" : "number" + }, + "testhashu32_fnv1a" : { + "type" : "number" + }, + "testhashs64_fnv1a" : { + "type" : "number" + }, + "testhashu64_fnv1a" : { + "type" : "number" + }, + "testarrayofbools" : { + "type" : "array", "items" : { "type" : "boolean" } + }, + "testf" : { + "type" : "number" + }, + "testf2" : { + "type" : "number" + }, + "testf3" : { + "type" : "number" + }, + "testarrayofstring2" : { + "type" : "array", "items" : { "type" : "string" } + }, + "testarrayofsortedstruct" : { + "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Ability" } + }, + "flex" : { + "type" : "array", "items" : { "type" : "number" } + }, + "test5" : { + "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Test" } + }, + "vector_of_longs" : { + "type" : "array", "items" : { "type" : "number" } + }, + "vector_of_doubles" : { + "type" : "array", "items" : { "type" : "number" } + }, + "parent_namespace_test" : { + "$ref" : "#/definitions/MyGame_InParentNamespace" + }, + "vector_of_referrables" : { + "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Referrable" } + }, + "single_weak_reference" : { + "type" : "number" + }, + "vector_of_weak_references" : { + "type" : "array", "items" : { "type" : "number" } + }, + "vector_of_strong_referrables" : { + "type" : "array", "items" : { "$ref" : "#/definitions/MyGame_Example_Referrable" } + }, + "co_owning_reference" : { + "type" : "number" + }, + "vector_of_co_owning_references" : { + "type" : "array", "items" : { "type" : "number" } + }, + "non_owning_reference" : { + "type" : "number" + }, + "vector_of_non_owning_references" : { + "type" : "array", "items" : { "type" : "number" } + }, + "any_unique_type" : { + "$ref" : "#/definitions/MyGame_Example_AnyUniqueAliases" + }, + "any_unique" : { + "anyOf": [{ "$ref" : "#/definitions/MyGame_Example_Monster" },{ "$ref" : "#/definitions/MyGame_Example_TestSimpleTableWithEnum" },{ "$ref" : "#/definitions/MyGame_Example2_Monster" }] + }, + "any_ambiguous_type" : { + "$ref" : "#/definitions/MyGame_Example_AnyAmbiguousAliases" + }, + "any_ambiguous" : { + "anyOf": [{ "$ref" : "#/definitions/MyGame_Example_Monster" },{ "$ref" : "#/definitions/MyGame_Example_Monster" },{ "$ref" : "#/definitions/MyGame_Example_Monster" }] + }, + "vector_of_enums" : { + "$ref" : "#/definitions/MyGame_Example_Color" + } }, "required" : ["name"], "additionalProperties" : false @@ -164,18 +296,42 @@ "MyGame_Example_TypeAliases" : { "type" : "object", "properties" : { - "i8" : { "type" : "number" }, - "u8" : { "type" : "number" }, - "i16" : { "type" : "number" }, - "u16" : { "type" : "number" }, - "i32" : { "type" : "number" }, - "u32" : { "type" : "number" }, - "i64" : { "type" : "number" }, - "u64" : { "type" : "number" }, - "f32" : { "type" : "number" }, - "f64" : { "type" : "number" }, - "v8" : { "type" : "array", "items" : { "type" : "number" } }, - "vf64" : { "type" : "array", "items" : { "type" : "number" } } + "i8" : { + "type" : "number" + }, + "u8" : { + "type" : "number" + }, + "i16" : { + "type" : "number" + }, + "u16" : { + "type" : "number" + }, + "i32" : { + "type" : "number" + }, + "u32" : { + "type" : "number" + }, + "i64" : { + "type" : "number" + }, + "u64" : { + "type" : "number" + }, + "f32" : { + "type" : "number" + }, + "f64" : { + "type" : "number" + }, + "v8" : { + "type" : "array", "items" : { "type" : "number" } + }, + "vf64" : { + "type" : "array", "items" : { "type" : "number" } + } }, "additionalProperties" : false } diff --git a/tests/py_test.py b/tests/py_test.py index d8eb6728..e7f72782 100644 --- a/tests/py_test.py +++ b/tests/py_test.py @@ -42,7 +42,10 @@ import MyGame.Example.Test # refers to generated code import MyGame.Example.Stat # refers to generated code import MyGame.Example.Vec3 # refers to generated code import MyGame.MonsterExtra # refers to generated code - +import MyGame.Example.ArrayTable # refers to generated code +import MyGame.Example.ArrayStruct # refers to generated code +import MyGame.Example.NestedStruct # refers to generated code +import MyGame.Example.TestEnum # refers to generated code def assertRaises(test_case, fn, exception_class): ''' Backwards-compatible assertion for exceptions raised. ''' @@ -1544,6 +1547,55 @@ class TestExceptions(unittest.TestCase): flatbuffers.builder.BuilderNotFinishedError) +class TestFixedLengthArrays(unittest.TestCase): + def test_fixed_length_array(self): + builder = flatbuffers.Builder(0) + + a = 0.5 + b = range(0, 15) + c = 1 + d_a = [[1, 2], [3, 4]] + d_b = [MyGame.Example.TestEnum.TestEnum.B, \ + MyGame.Example.TestEnum.TestEnum.C] + d_c = [[MyGame.Example.TestEnum.TestEnum.A, \ + MyGame.Example.TestEnum.TestEnum.B], \ + [MyGame.Example.TestEnum.TestEnum.C, \ + MyGame.Example.TestEnum.TestEnum.B]] + + arrayOffset = MyGame.Example.ArrayStruct.CreateArrayStruct(builder, \ + a, b, c, d_a, d_b, d_c) + + # Create a table with the ArrayStruct. + MyGame.Example.ArrayTable.ArrayTableStart(builder) + MyGame.Example.ArrayTable.ArrayTableAddA(builder, arrayOffset) + tableOffset = MyGame.Example.ArrayTable.ArrayTableEnd(builder) + + builder.Finish(tableOffset) + + buf = builder.Output() + + table = MyGame.Example.ArrayTable.ArrayTable.GetRootAsArrayTable(buf, 0) + + # Verify structure. + nested = MyGame.Example.NestedStruct.NestedStruct() + self.assertEqual(table.A().A(), 0.5) + self.assertEqual(table.A().B(), \ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) + self.assertEqual(table.A().C(), 1) + self.assertEqual(table.A().D(nested, 0).A(), [1, 2]) + self.assertEqual(table.A().D(nested, 1).A(), [3, 4]) + self.assertEqual(table.A().D(nested, 0).B(), \ + MyGame.Example.TestEnum.TestEnum.B) + self.assertEqual(table.A().D(nested, 1).B(), \ + MyGame.Example.TestEnum.TestEnum.C) + self.assertEqual(table.A().D(nested, 0).C(), \ + [MyGame.Example.TestEnum.TestEnum.A, \ + MyGame.Example.TestEnum.TestEnum.B]) + self.assertEqual(table.A().D(nested, 1).C(), \ + [MyGame.Example.TestEnum.TestEnum.C, \ + MyGame.Example.TestEnum.TestEnum.B]) + + def CheckAgainstGoldDataGo(): try: gen_buf, gen_off = make_monster_from_generated_code() diff --git a/tests/test.cpp b/tests/test.cpp index 86eb164b..c70bbeb2 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -34,6 +34,9 @@ #include "namespace_test/namespace_test2_generated.h" #include "union_vector/union_vector_generated.h" #include "monster_extra_generated.h" +#if !defined(_MSC_VER) || _MSC_VER >= 1700 +# include "arrays_test_generated.h" +#endif #include "test_assert.h" #include "flatbuffers/flexbuffers.h" @@ -1203,6 +1206,15 @@ void FuzzTest2() { AddToSchemaAndInstances( "bool", deprecated ? "" : (lcg_rand() % 2 ? "true" : "false")); break; + 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; default: // All the scalar types. schema += flatbuffers::kTypeNames[base_type]; @@ -2671,6 +2683,142 @@ void CreateSharedStringTest() { TEST_EQ((*a[6]) < (*a[5]), true); } +void FixedLengthArrayTest() { + // VS10 does not support typed enums, exclude from tests +#if !defined(_MSC_VER) || _MSC_VER >= 1700 + // Generate an ArrayTable containing one ArrayStruct. + flatbuffers::FlatBufferBuilder fbb; + MyGame::Example::NestedStruct nStruct0(MyGame::Example::TestEnum::B); + TEST_NOTNULL(nStruct0.mutable_a()); + nStruct0.mutable_a()->Mutate(0, 1); + nStruct0.mutable_a()->Mutate(1, 2); + TEST_NOTNULL(nStruct0.mutable_c()); + nStruct0.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C); + nStruct0.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A); + MyGame::Example::NestedStruct nStruct1(MyGame::Example::TestEnum::C); + TEST_NOTNULL(nStruct1.mutable_a()); + nStruct1.mutable_a()->Mutate(0, 3); + nStruct1.mutable_a()->Mutate(1, 4); + TEST_NOTNULL(nStruct1.mutable_c()); + nStruct1.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C); + nStruct1.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A); + MyGame::Example::ArrayStruct aStruct(2, 12); + TEST_NOTNULL(aStruct.b()); + TEST_NOTNULL(aStruct.mutable_b()); + TEST_NOTNULL(aStruct.mutable_d()); + for (int i = 0; i < aStruct.b()->size(); i++) + aStruct.mutable_b()->Mutate(i, i + 1); + aStruct.mutable_d()->Mutate(0, nStruct0); + aStruct.mutable_d()->Mutate(1, nStruct1); + auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct); + fbb.Finish(aTable); + + // Verify correctness of the ArrayTable. + flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize()); + MyGame::Example::VerifyArrayTableBuffer(verifier); + auto p = MyGame::Example::GetMutableArrayTable(fbb.GetBufferPointer()); + auto mArStruct = p->mutable_a(); + TEST_NOTNULL(mArStruct); + TEST_NOTNULL(mArStruct->b()); + TEST_NOTNULL(mArStruct->d()); + TEST_NOTNULL(mArStruct->mutable_b()); + TEST_NOTNULL(mArStruct->mutable_d()); + mArStruct->mutable_b()->Mutate(14, -14); + TEST_EQ(mArStruct->a(), 2); + TEST_EQ(mArStruct->b()->size(), 15); + TEST_EQ(mArStruct->b()->Get(aStruct.b()->size() - 1), -14); + TEST_EQ(mArStruct->c(), 12); + TEST_NOTNULL(mArStruct->d()->Get(0).a()); + TEST_EQ(mArStruct->d()->Get(0).a()->Get(0), 1); + TEST_EQ(mArStruct->d()->Get(0).a()->Get(1), 2); + TEST_NOTNULL(mArStruct->d()->Get(1).a()); + TEST_EQ(mArStruct->d()->Get(1).a()->Get(0), 3); + TEST_EQ(mArStruct->d()->Get(1).a()->Get(1), 4); + TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1)); + TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a()); + mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a()->Mutate(1, 5); + TEST_EQ(mArStruct->d()->Get(1).a()->Get(1), 5); + TEST_EQ(mArStruct->d()->Get(0).b() == MyGame::Example::TestEnum::B, true); + TEST_NOTNULL(mArStruct->d()->Get(0).c()); + TEST_EQ(mArStruct->d()->Get(0).c()->Get(0) == MyGame::Example::TestEnum::C, + true); + TEST_EQ(mArStruct->d()->Get(0).c()->Get(1) == MyGame::Example::TestEnum::A, + true); + TEST_EQ(mArStruct->d()->Get(1).b() == MyGame::Example::TestEnum::C, true); + TEST_NOTNULL(mArStruct->d()->Get(1).c()); + TEST_EQ(mArStruct->d()->Get(1).c()->Get(0) == MyGame::Example::TestEnum::C, + true); + TEST_EQ(mArStruct->d()->Get(1).c()->Get(1) == MyGame::Example::TestEnum::A, + true); + for (int i = 0; i < mArStruct->b()->size() - 1; i++) + TEST_EQ(mArStruct->b()->Get(i), i + 1); +#endif +} + +void FixedLengthArrayJsonTest(bool binary) { + // VS10 does not support typed enums, exclude from tests +#if !defined(_MSC_VER) || _MSC_VER >= 1700 + // load FlatBuffer schema (.fbs) and JSON from disk + std::string schemafile; + std::string jsonfile; + TEST_EQ( + flatbuffers::LoadFile( + (test_data_path + "arrays_test." + (binary ? "bfbs" : "fbs")).c_str(), + binary, &schemafile), + true); + TEST_EQ(flatbuffers::LoadFile((test_data_path + "arrays_test.golden").c_str(), + false, &jsonfile), + true); + + // parse schema first, so we can use it to parse the data after + flatbuffers::Parser parserOrg, parserGen; + if (binary) { + flatbuffers::Verifier verifier( + reinterpret_cast<const uint8_t *>(schemafile.c_str()), + schemafile.size()); + TEST_EQ(reflection::VerifySchemaBuffer(verifier), true); + TEST_EQ(parserOrg.Deserialize((const uint8_t *)schemafile.c_str(), + schemafile.size()), + true); + TEST_EQ(parserGen.Deserialize((const uint8_t *)schemafile.c_str(), + schemafile.size()), + true); + } else { + TEST_EQ(parserOrg.Parse(schemafile.c_str()), true); + TEST_EQ(parserGen.Parse(schemafile.c_str()), true); + } + TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true); + + // First, verify it, just in case: + flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(), + parserOrg.builder_.GetSize()); + TEST_EQ(VerifyArrayTableBuffer(verifierOrg), true); + + // Export to JSON + std::string jsonGen; + TEST_EQ( + GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen), + true); + + // Import from JSON + TEST_EQ(parserGen.Parse(jsonGen.c_str()), true); + + // Verify buffer from generated JSON + flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(), + parserGen.builder_.GetSize()); + TEST_EQ(VerifyArrayTableBuffer(verifierGen), true); + + // Compare generated buffer to original + TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize()); + TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(), + parserGen.builder_.GetBufferPointer(), + parserOrg.builder_.GetSize()), + 0); +#else + (void)binary; +#endif +} + int FlatBufferTests() { // clang-format off @@ -2705,6 +2853,8 @@ int FlatBufferTests() { #endif ParseAndGenerateTextTest(false); ParseAndGenerateTextTest(true); + FixedLengthArrayJsonTest(false); + FixedLengthArrayJsonTest(true); ReflectionTest(flatbuf.data(), flatbuf.size()); ParseProtoTest(); UnionVectorTest(); @@ -2747,6 +2897,7 @@ int FlatBufferTests() { ValidFloatTest(); InvalidFloatTest(); TestMonsterExtraFloats(); + FixedLengthArrayTest(); return 0; } |