summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsvenk177 <50141814+svenk177@users.noreply.github.com>2019-06-18 00:15:13 +0200
committerWouter van Oortmerssen <aardappel@gmail.com>2019-06-18 00:15:13 +0200
commite635141d5bc66f056c90bcc9da5fdd766610492f (patch)
treeb69da4d3b4d2e6e592083a2c31c6d7f809666575
parent0d2cebccfeffae9df998f3ac819bf17b7ec7a6d0 (diff)
downloadflatbuffers-e635141d5bc66f056c90bcc9da5fdd766610492f.tar.gz
flatbuffers-e635141d5bc66f056c90bcc9da5fdd766610492f.tar.bz2
flatbuffers-e635141d5bc66f056c90bcc9da5fdd766610492f.zip
Add support for fixed-size arrays (#5313)
-rw-r--r--.appveyor/check-generate-code.bat1
-rwxr-xr-x.travis/check-generate-code.sh1
-rw-r--r--BUILD17
-rw-r--r--CMakeLists.txt11
-rw-r--r--docs/source/Schemas.md21
-rw-r--r--include/flatbuffers/flatbuffers.h77
-rw-r--r--include/flatbuffers/idl.h46
-rw-r--r--include/flatbuffers/reflection_generated.h27
-rw-r--r--reflection/reflection.fbs7
-rw-r--r--src/idl_gen_cpp.cpp105
-rw-r--r--src/idl_gen_general.cpp164
-rw-r--r--src/idl_gen_json_schema.cpp14
-rw-r--r--src/idl_gen_python.cpp91
-rw-r--r--src/idl_gen_text.cpp72
-rw-r--r--src/idl_parser.cpp137
-rw-r--r--tests/FlatBuffers.Test/FlatBuffers.Test.csproj12
-rw-r--r--tests/FlatBuffers.Test/FlatBuffersExampleTests.cs53
-rw-r--r--tests/JavaTest.java54
-rw-r--r--tests/MyGame/Example/ArrayStruct.cs50
-rw-r--r--tests/MyGame/Example/ArrayStruct.java45
-rw-r--r--tests/MyGame/Example/ArrayStruct.py41
-rw-r--r--tests/MyGame/Example/ArrayTable.cs34
-rw-r--r--tests/MyGame/Example/ArrayTable.java30
-rw-r--r--tests/MyGame/Example/ArrayTable.py34
-rw-r--r--tests/MyGame/Example/NestedStruct.cs40
-rw-r--r--tests/MyGame/Example/NestedStruct.java35
-rw-r--r--tests/MyGame/Example/NestedStruct.py29
-rw-r--r--tests/MyGame/Example/TestEnum.cs16
-rw-r--r--tests/MyGame/Example/TestEnum.java15
-rw-r--r--tests/MyGame/Example/TestEnum.py9
-rw-r--r--tests/arrays_test.bfbsbin0 -> 1064 bytes
-rw-r--r--tests/arrays_test.fbs24
-rw-r--r--tests/arrays_test.golden19
-rw-r--r--tests/arrays_test.schema.json60
-rw-r--r--tests/arrays_test_generated.h419
-rw-r--r--tests/generate_code.bat2
-rwxr-xr-xtests/generate_code.sh4
-rw-r--r--tests/monster_test.schema.json312
-rw-r--r--tests/py_test.py54
-rw-r--r--tests/test.cpp151
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
diff --git a/BUILD b/BUILD
index 4e97efca..e6661819 100644
--- a/BUILD
+++ b/BUILD
@@ -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
new file mode 100644
index 00000000..2a899681
--- /dev/null
+++ b/tests/arrays_test.bfbs
Binary files differ
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;
}