Commit e635141d authored by svenk177's avatar svenk177 Committed by Wouter van Oortmerssen

Add support for fixed-size arrays (#5313)

parent 0d2cebcc
......@@ -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
......
......@@ -21,6 +21,7 @@ cd ..
# TODO: Linux and macos builds produce differences here for some reason.
git checkout HEAD -- tests/monster_test.bfbs
git checkout HEAD -- tests/arrays_test.bfbs
if ! git diff --quiet; then
echo >&2
......
......@@ -164,11 +164,15 @@ cc_test(
":tests/union_vector/union_vector.json",
":tests/monster_extra.fbs",
":tests/monsterdata_extra.json",
":tests/arrays_test.bfbs",
":tests/arrays_test.fbs",
":tests/arrays_test.golden",
],
includes = ["include/"],
deps = [
":monster_extra_cc_fbs",
":monster_test_cc_fbs",
":arrays_test_cc_fbs",
],
)
......@@ -188,3 +192,16 @@ flatbuffer_cc_library(
name = "monster_extra_cc_fbs",
srcs = ["tests/monster_extra.fbs"],
)
flatbuffer_cc_library(
name = "arrays_test_cc_fbs",
srcs = ["tests/arrays_test.fbs"],
flatc_args = [
"--gen-object-api",
"--gen-compare",
"--no-includes",
"--gen-mutable",
"--reflect-names",
"--cpp-ptr-type flatbuffers::unique_ptr",
"--scoped-enums" ],
)
......@@ -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
......
......@@ -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
......
......@@ -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,
......
......@@ -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();
......
......@@ -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();
......
......@@ -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 {
......
......@@ -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_ += " }";
}
}
......
This diff is collapsed.
......@@ -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;
}
......
......@@ -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) {
......
......@@ -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)) {
......
......@@ -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()];
......
......@@ -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>
......
......@@ -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);
}
}
}
......@@ -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());
......
// <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);
}
};
}
// 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();
}
}
# 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()
// <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"); }
};
}
// 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"); }
}
# 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()
// <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);
}
};
}
// 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();
}
}
# 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()
// <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,
};
}
// 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]; }
}
# automatically generated by the FlatBuffers compiler, do not modify
# namespace: Example
class TestEnum(object):
A = 0
B = 1
C = 2
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";
{
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
{
"$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"
}
This diff is collapsed.
......@@ -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
......
......@@ -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
......
This diff is collapsed.
......@@ -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()
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment