Skip to content

Commit e635141

Browse files
svenk177aardappel
authored andcommitted
Add support for fixed-size arrays (google#5313)
1 parent 0d2cebc commit e635141

40 files changed

+2113
-220
lines changed

.appveyor/check-generate-code.bat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ call generate_code.bat -b %buildtype% || goto FAIL
1919

2020
:: TODO: Release and Debug builds produce differences here for some reason.
2121
git checkout HEAD -- monster_test.bfbs
22+
git checkout HEAD -- arrays_test.bfbs
2223

2324
git -c core.autocrlf=true diff --exit-code --quiet || goto :DIFFFOUND
2425
goto SUCCESS

.travis/check-generate-code.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ cd ..
2121

2222
# TODO: Linux and macos builds produce differences here for some reason.
2323
git checkout HEAD -- tests/monster_test.bfbs
24+
git checkout HEAD -- tests/arrays_test.bfbs
2425

2526
if ! git diff --quiet; then
2627
echo >&2

BUILD

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,15 @@ cc_test(
164164
":tests/union_vector/union_vector.json",
165165
":tests/monster_extra.fbs",
166166
":tests/monsterdata_extra.json",
167+
":tests/arrays_test.bfbs",
168+
":tests/arrays_test.fbs",
169+
":tests/arrays_test.golden",
167170
],
168171
includes = ["include/"],
169172
deps = [
170173
":monster_extra_cc_fbs",
171174
":monster_test_cc_fbs",
175+
":arrays_test_cc_fbs",
172176
],
173177
)
174178

@@ -188,3 +192,16 @@ flatbuffer_cc_library(
188192
name = "monster_extra_cc_fbs",
189193
srcs = ["tests/monster_extra.fbs"],
190194
)
195+
196+
flatbuffer_cc_library(
197+
name = "arrays_test_cc_fbs",
198+
srcs = ["tests/arrays_test.fbs"],
199+
flatc_args = [
200+
"--gen-object-api",
201+
"--gen-compare",
202+
"--no-includes",
203+
"--gen-mutable",
204+
"--reflect-names",
205+
"--cpp-ptr-type flatbuffers::unique_ptr",
206+
"--scoped-enums" ],
207+
)

CMakeLists.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ set(FlatBuffers_Tests_SRCS
119119
tests/test_builder.cpp
120120
# file generate by running compiler on tests/monster_test.fbs
121121
${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h
122+
# file generate by running compiler on tests/arrays_test.fbs
123+
${CMAKE_CURRENT_BINARY_DIR}/tests/arrays_test_generated.h
122124
)
123125

124126
set(FlatBuffers_Sample_Binary_SRCS
@@ -303,20 +305,24 @@ if(FLATBUFFERS_BUILD_SHAREDLIB)
303305
VERSION "${FlatBuffers_Library_SONAME_FULL}")
304306
endif()
305307

306-
function(compile_flatbuffers_schema_to_cpp SRC_FBS)
308+
function(compile_flatbuffers_schema_to_cpp_opt SRC_FBS OPT)
307309
get_filename_component(SRC_FBS_DIR ${SRC_FBS} PATH)
308310
string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS})
309311
add_custom_command(
310312
OUTPUT ${GEN_HEADER}
311313
COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable
312314
--gen-object-api --gen-compare -o "${SRC_FBS_DIR}"
313315
--cpp-ptr-type flatbuffers::unique_ptr # Used to test with C++98 STLs
314-
--reflect-names
316+
--reflect-names ${OPT}
315317
-I "${CMAKE_CURRENT_SOURCE_DIR}/tests/include_test"
316318
"${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}"
317319
DEPENDS flatc)
318320
endfunction()
319321

322+
function(compile_flatbuffers_schema_to_cpp SRC_FBS)
323+
compile_flatbuffers_schema_to_cpp_opt(${SRC_FBS} "")
324+
endfunction()
325+
320326
function(compile_flatbuffers_schema_to_binary SRC_FBS)
321327
get_filename_component(SRC_FBS_DIR ${SRC_FBS} PATH)
322328
string(REGEX REPLACE "\\.fbs$" ".bfbs" GEN_BINARY_SCHEMA ${SRC_FBS})
@@ -329,6 +335,7 @@ endfunction()
329335

330336
if(FLATBUFFERS_BUILD_TESTS)
331337
compile_flatbuffers_schema_to_cpp(tests/monster_test.fbs)
338+
compile_flatbuffers_schema_to_cpp_opt(tests/arrays_test.fbs --scoped-enums)
332339
include_directories(${CMAKE_CURRENT_BINARY_DIR}/tests)
333340
add_executable(flattests ${FlatBuffers_Tests_SRCS})
334341
set_property(TARGET flattests

docs/source/Schemas.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,27 @@ of same-size data where a `reinterpret_cast` would give you a desirable result,
114114
e.g. you could change a `uint` to an `int` if no values in current data use the
115115
high bit yet.
116116

117+
### Arrays
118+
119+
Arrays are a convenience short-hand for a fixed-length collection of elements.
120+
Arrays can be used to replace the following schema:
121+
122+
struct Vec3 {
123+
x:float;
124+
y:float;
125+
z:float;
126+
}
127+
128+
with the following schema:
129+
130+
struct Vec3 {
131+
v:[float:3];
132+
}
133+
134+
Both representations are binary equivalent.
135+
136+
Arrays are currently only supported in a `struct`.
137+
117138
### (Default) Values
118139

119140
Values are a sequence of digits. Values may be optionally followed by a decimal

include/flatbuffers/flatbuffers.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,83 @@ template<typename T> static inline size_t VectorLength(const Vector<T> *v) {
395395
return v ? v->size() : 0;
396396
}
397397

398+
// This is used as a helper type for accessing arrays.
399+
template<typename T, uint16_t length> class Array {
400+
public:
401+
typedef VectorIterator<T, typename IndirectHelper<T>::return_type>
402+
const_iterator;
403+
typedef VectorReverseIterator<const_iterator> const_reverse_iterator;
404+
405+
typedef typename IndirectHelper<T>::return_type return_type;
406+
407+
FLATBUFFERS_CONSTEXPR uint16_t size() const { return length; }
408+
409+
return_type Get(uoffset_t i) const {
410+
FLATBUFFERS_ASSERT(i < size());
411+
return IndirectHelper<T>::Read(Data(), i);
412+
}
413+
414+
return_type operator[](uoffset_t i) const { return Get(i); }
415+
416+
const_iterator begin() const { return const_iterator(Data(), 0); }
417+
const_iterator end() const { return const_iterator(Data(), size()); }
418+
419+
const_reverse_iterator rbegin() const {
420+
return const_reverse_iterator(end());
421+
}
422+
const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
423+
424+
const_iterator cbegin() const { return begin(); }
425+
const_iterator cend() const { return end(); }
426+
427+
const_reverse_iterator crbegin() const { return rbegin(); }
428+
const_reverse_iterator crend() const { return rend(); }
429+
430+
// Change elements if you have a non-const pointer to this object.
431+
void Mutate(uoffset_t i, const T &val) {
432+
FLATBUFFERS_ASSERT(i < size());
433+
WriteScalar(data() + i, val);
434+
}
435+
436+
// Get a mutable pointer to elements inside this array.
437+
// @note This method should be only used to mutate arrays of structs followed
438+
// by a @p Mutate operation. For primitive types use @p Mutate directly.
439+
// @warning Assignments and reads to/from the dereferenced pointer are not
440+
// automatically converted to the correct endianness.
441+
T *GetMutablePointer(uoffset_t i) const {
442+
FLATBUFFERS_ASSERT(i < size());
443+
return const_cast<T *>(&data()[i]);
444+
}
445+
446+
// The raw data in little endian format. Use with care.
447+
const uint8_t *Data() const { return data_; }
448+
449+
uint8_t *Data() { return data_; }
450+
451+
// Similarly, but typed, much like std::vector::data
452+
const T *data() const { return reinterpret_cast<const T *>(Data()); }
453+
T *data() { return reinterpret_cast<T *>(Data()); }
454+
455+
protected:
456+
// This class is only used to access pre-existing data. Don't ever
457+
// try to construct these manually.
458+
// 'constexpr' allows us to use 'size()' at compile time.
459+
// @note Must not use 'FLATBUFFERS_CONSTEXPR' here, as const is not allowed on
460+
// a constructor.
461+
#if defined(__cpp_constexpr)
462+
constexpr Array();
463+
#else
464+
Array();
465+
#endif
466+
467+
uint8_t data_[length * sizeof(T)];
468+
469+
private:
470+
// This class is a pointer. Copying will therefore create an invalid object.
471+
// Private and unimplemented copy constructor.
472+
Array(const Array &);
473+
};
474+
398475
// Lexicographically compare two strings (possibly containing nulls), and
399476
// return true if the first is less than the second.
400477
static inline bool StringLessThan(const char *a_data, uoffset_t a_size,

include/flatbuffers/idl.h

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ namespace flatbuffers {
6565
TD(VECTOR, "", Offset<void>, int, int, VectorOffset, int, unused) \
6666
TD(STRUCT, "", Offset<void>, int, int, int, int, unused) \
6767
TD(UNION, "", Offset<void>, int, int, int, int, unused)
68-
68+
#define FLATBUFFERS_GEN_TYPE_ARRAY(TD) \
69+
TD(ARRAY, "", int, int, int, int, int, unused)
6970
// The fields are:
7071
// - enum
7172
// - FlatBuffers schema type.
@@ -91,7 +92,8 @@ switch (type) {
9192

9293
#define FLATBUFFERS_GEN_TYPES(TD) \
9394
FLATBUFFERS_GEN_TYPES_SCALAR(TD) \
94-
FLATBUFFERS_GEN_TYPES_POINTER(TD)
95+
FLATBUFFERS_GEN_TYPES_POINTER(TD) \
96+
FLATBUFFERS_GEN_TYPE_ARRAY(TD)
9597

9698
// Create an enum for all the types above.
9799
#ifdef __GNUC__
@@ -145,18 +147,21 @@ class Parser;
145147
// and additional information for vectors/structs_.
146148
struct Type {
147149
explicit Type(BaseType _base_type = BASE_TYPE_NONE, StructDef *_sd = nullptr,
148-
EnumDef *_ed = nullptr)
150+
EnumDef *_ed = nullptr, uint16_t _fixed_length = 0)
149151
: base_type(_base_type),
150152
element(BASE_TYPE_NONE),
151153
struct_def(_sd),
152-
enum_def(_ed) {}
154+
enum_def(_ed),
155+
fixed_length(_fixed_length) {}
153156

154157
bool operator==(const Type &o) {
155158
return base_type == o.base_type && element == o.element &&
156159
struct_def == o.struct_def && enum_def == o.enum_def;
157160
}
158161

159-
Type VectorType() const { return Type(element, struct_def, enum_def); }
162+
Type VectorType() const {
163+
return Type(element, struct_def, enum_def, fixed_length);
164+
}
160165

161166
Offset<reflection::Type> Serialize(FlatBufferBuilder *builder) const;
162167

@@ -167,6 +172,7 @@ struct Type {
167172
StructDef *struct_def; // only set if t or element == BASE_TYPE_STRUCT
168173
EnumDef *enum_def; // set if t == BASE_TYPE_UNION / BASE_TYPE_UTYPE,
169174
// or for an integral type derived from an enum.
175+
uint16_t fixed_length; // only set if t == BASE_TYPE_ARRAY
170176
};
171177

172178
// Represents a parsed scalar value, it's type, and field offset.
@@ -335,12 +341,34 @@ inline bool IsStruct(const Type &type) {
335341
return type.base_type == BASE_TYPE_STRUCT && type.struct_def->fixed;
336342
}
337343

344+
inline bool IsVector(const Type &type) {
345+
return type.base_type == BASE_TYPE_VECTOR;
346+
}
347+
348+
inline bool IsArray(const Type &type) {
349+
return type.base_type == BASE_TYPE_ARRAY;
350+
}
351+
352+
inline bool IsSeries(const Type &type) {
353+
return IsVector(type) || IsArray(type);
354+
}
355+
356+
inline bool IsEnum(const Type &type) {
357+
return type.enum_def != nullptr && IsInteger(type.base_type);
358+
}
359+
338360
inline size_t InlineSize(const Type &type) {
339-
return IsStruct(type) ? type.struct_def->bytesize : SizeOf(type.base_type);
361+
return IsStruct(type)
362+
? type.struct_def->bytesize
363+
: (IsArray(type)
364+
? InlineSize(type.VectorType()) * type.fixed_length
365+
: SizeOf(type.base_type));
340366
}
341367

342368
inline size_t InlineAlignment(const Type &type) {
343-
return IsStruct(type) ? type.struct_def->minalign : SizeOf(type.base_type);
369+
return IsStruct(type)
370+
? type.struct_def->minalign
371+
: (SizeOf(IsArray(type) ? type.element : type.base_type));
344372
}
345373

346374
struct EnumDef;
@@ -799,10 +827,13 @@ class Parser : public ParserState {
799827
FLATBUFFERS_CHECKED_ERROR ParseTable(const StructDef &struct_def,
800828
std::string *value, uoffset_t *ovalue);
801829
void SerializeStruct(const StructDef &struct_def, const Value &val);
830+
void SerializeStruct(FlatBufferBuilder &builder, const StructDef &struct_def,
831+
const Value &val);
802832
template<typename F>
803833
FLATBUFFERS_CHECKED_ERROR ParseVectorDelimiters(uoffset_t &count, F body);
804834
FLATBUFFERS_CHECKED_ERROR ParseVector(const Type &type, uoffset_t *ovalue,
805835
FieldDef *field, size_t fieldn);
836+
FLATBUFFERS_CHECKED_ERROR ParseArray(Value &array);
806837
FLATBUFFERS_CHECKED_ERROR ParseNestedFlatbuffer(Value &val, FieldDef *field,
807838
size_t fieldn,
808839
const StructDef *parent_struct_def);
@@ -849,6 +880,7 @@ class Parser : public ParserState {
849880
BaseType baseType);
850881

851882
bool SupportsAdvancedUnionFeatures() const;
883+
bool SupportsAdvancedArrayFeatures() const;
852884
Namespace *UniqueNamespace(Namespace *ns);
853885

854886
FLATBUFFERS_CHECKED_ERROR RecurseError();

0 commit comments

Comments
 (0)