1 /*
2  * Copyright 2015 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef FLATBUFFERS_REFLECTION_H_
18 #define FLATBUFFERS_REFLECTION_H_
19 
20 // This is somewhat of a circular dependency because flatc (and thus this
21 // file) is needed to generate this header in the first place.
22 // Should normally not be a problem since it can be generated by the
23 // previous version of flatc whenever this code needs to change.
24 // See reflection/generate_code.sh
25 #include "flatbuffers/reflection_generated.h"
26 
27 // Helper functionality for reflection.
28 
29 namespace flatbuffers {
30 
31 // ------------------------- GETTERS -------------------------
32 
IsScalar(reflection::BaseType t)33 inline bool IsScalar(reflection::BaseType t) {
34   return t >= reflection::UType && t <= reflection::Double;
35 }
IsInteger(reflection::BaseType t)36 inline bool IsInteger(reflection::BaseType t) {
37   return t >= reflection::UType && t <= reflection::ULong;
38 }
IsFloat(reflection::BaseType t)39 inline bool IsFloat(reflection::BaseType t) {
40   return t == reflection::Float || t == reflection::Double;
41 }
IsLong(reflection::BaseType t)42 inline bool IsLong(reflection::BaseType t) {
43   return t == reflection::Long || t == reflection::ULong;
44 }
45 
46 // Size of a basic type, don't use with structs.
GetTypeSize(reflection::BaseType base_type)47 inline size_t GetTypeSize(reflection::BaseType base_type) {
48   // This needs to correspond to the BaseType enum.
49   static size_t sizes[] = { 0, 1, 1, 1, 1, 2, 2, 4, 4, 8, 8, 4, 8, 4, 4, 4, 4 };
50   return sizes[base_type];
51 }
52 
53 // Same as above, but now correctly returns the size of a struct if
54 // the field (or vector element) is a struct.
GetTypeSizeInline(reflection::BaseType base_type,int type_index,const reflection::Schema & schema)55 inline size_t GetTypeSizeInline(reflection::BaseType base_type, int type_index,
56                                 const reflection::Schema &schema) {
57   if (base_type == reflection::Obj &&
58       schema.objects()->Get(type_index)->is_struct()) {
59     return schema.objects()->Get(type_index)->bytesize();
60   } else {
61     return GetTypeSize(base_type);
62   }
63 }
64 
65 // Get the root, regardless of what type it is.
GetAnyRoot(uint8_t * flatbuf)66 inline Table *GetAnyRoot(uint8_t *flatbuf) {
67   return GetMutableRoot<Table>(flatbuf);
68 }
GetAnyRoot(const uint8_t * flatbuf)69 inline const Table *GetAnyRoot(const uint8_t *flatbuf) {
70   return GetRoot<Table>(flatbuf);
71 }
72 
73 // Get a field's default, if you know it's an integer, and its exact type.
GetFieldDefaultI(const reflection::Field & field)74 template<typename T> T GetFieldDefaultI(const reflection::Field &field) {
75   FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type()));
76   return static_cast<T>(field.default_integer());
77 }
78 
79 // Get a field's default, if you know it's floating point and its exact type.
GetFieldDefaultF(const reflection::Field & field)80 template<typename T> T GetFieldDefaultF(const reflection::Field &field) {
81   FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type()));
82   return static_cast<T>(field.default_real());
83 }
84 
85 // Get a field, if you know it's an integer, and its exact type.
86 template<typename T>
GetFieldI(const Table & table,const reflection::Field & field)87 T GetFieldI(const Table &table, const reflection::Field &field) {
88   FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type()));
89   return table.GetField<T>(field.offset(),
90                            static_cast<T>(field.default_integer()));
91 }
92 
93 // Get a field, if you know it's floating point and its exact type.
94 template<typename T>
GetFieldF(const Table & table,const reflection::Field & field)95 T GetFieldF(const Table &table, const reflection::Field &field) {
96   FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type()));
97   return table.GetField<T>(field.offset(),
98                            static_cast<T>(field.default_real()));
99 }
100 
101 // Get a field, if you know it's a string.
GetFieldS(const Table & table,const reflection::Field & field)102 inline const String *GetFieldS(const Table &table,
103                                const reflection::Field &field) {
104   FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::String);
105   return table.GetPointer<const String *>(field.offset());
106 }
107 
108 // Get a field, if you know it's a vector.
109 template<typename T>
GetFieldV(const Table & table,const reflection::Field & field)110 Vector<T> *GetFieldV(const Table &table, const reflection::Field &field) {
111   FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Vector &&
112                      sizeof(T) == GetTypeSize(field.type()->element()));
113   return table.GetPointer<Vector<T> *>(field.offset());
114 }
115 
116 // Get a field, if you know it's a vector, generically.
117 // To actually access elements, use the return value together with
118 // field.type()->element() in any of GetAnyVectorElemI below etc.
GetFieldAnyV(const Table & table,const reflection::Field & field)119 inline VectorOfAny *GetFieldAnyV(const Table &table,
120                                  const reflection::Field &field) {
121   return table.GetPointer<VectorOfAny *>(field.offset());
122 }
123 
124 // Get a field, if you know it's a table.
GetFieldT(const Table & table,const reflection::Field & field)125 inline Table *GetFieldT(const Table &table, const reflection::Field &field) {
126   FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj ||
127                      field.type()->base_type() == reflection::Union);
128   return table.GetPointer<Table *>(field.offset());
129 }
130 
131 // Get a field, if you know it's a struct.
GetFieldStruct(const Table & table,const reflection::Field & field)132 inline const Struct *GetFieldStruct(const Table &table,
133                                     const reflection::Field &field) {
134   // TODO: This does NOT check if the field is a table or struct, but we'd need
135   // access to the schema to check the is_struct flag.
136   FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj);
137   return table.GetStruct<const Struct *>(field.offset());
138 }
139 
140 // Get a structure's field, if you know it's a struct.
GetFieldStruct(const Struct & structure,const reflection::Field & field)141 inline const Struct *GetFieldStruct(const Struct &structure,
142                                     const reflection::Field &field) {
143   FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj);
144   return structure.GetStruct<const Struct *>(field.offset());
145 }
146 
147 // Raw helper functions used below: get any value in memory as a 64bit int, a
148 // double or a string.
149 // All scalars get static_cast to an int64_t, strings use strtoull, every other
150 // data type returns 0.
151 int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data);
152 // All scalars static cast to double, strings use strtod, every other data
153 // type is 0.0.
154 double GetAnyValueF(reflection::BaseType type, const uint8_t *data);
155 // All scalars converted using stringstream, strings as-is, and all other
156 // data types provide some level of debug-pretty-printing.
157 std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data,
158                          const reflection::Schema *schema, int type_index);
159 
160 // Get any table field as a 64bit int, regardless of what type it is.
GetAnyFieldI(const Table & table,const reflection::Field & field)161 inline int64_t GetAnyFieldI(const Table &table,
162                             const reflection::Field &field) {
163   auto field_ptr = table.GetAddressOf(field.offset());
164   return field_ptr ? GetAnyValueI(field.type()->base_type(), field_ptr)
165                    : field.default_integer();
166 }
167 
168 // Get any table field as a double, regardless of what type it is.
GetAnyFieldF(const Table & table,const reflection::Field & field)169 inline double GetAnyFieldF(const Table &table, const reflection::Field &field) {
170   auto field_ptr = table.GetAddressOf(field.offset());
171   return field_ptr ? GetAnyValueF(field.type()->base_type(), field_ptr)
172                    : field.default_real();
173 }
174 
175 // Get any table field as a string, regardless of what type it is.
176 // You may pass nullptr for the schema if you don't care to have fields that
177 // are of table type pretty-printed.
GetAnyFieldS(const Table & table,const reflection::Field & field,const reflection::Schema * schema)178 inline std::string GetAnyFieldS(const Table &table,
179                                 const reflection::Field &field,
180                                 const reflection::Schema *schema) {
181   auto field_ptr = table.GetAddressOf(field.offset());
182   return field_ptr ? GetAnyValueS(field.type()->base_type(), field_ptr, schema,
183                                   field.type()->index())
184                    : "";
185 }
186 
187 // Get any struct field as a 64bit int, regardless of what type it is.
GetAnyFieldI(const Struct & st,const reflection::Field & field)188 inline int64_t GetAnyFieldI(const Struct &st, const reflection::Field &field) {
189   return GetAnyValueI(field.type()->base_type(),
190                       st.GetAddressOf(field.offset()));
191 }
192 
193 // Get any struct field as a double, regardless of what type it is.
GetAnyFieldF(const Struct & st,const reflection::Field & field)194 inline double GetAnyFieldF(const Struct &st, const reflection::Field &field) {
195   return GetAnyValueF(field.type()->base_type(),
196                       st.GetAddressOf(field.offset()));
197 }
198 
199 // Get any struct field as a string, regardless of what type it is.
GetAnyFieldS(const Struct & st,const reflection::Field & field)200 inline std::string GetAnyFieldS(const Struct &st,
201                                 const reflection::Field &field) {
202   return GetAnyValueS(field.type()->base_type(),
203                       st.GetAddressOf(field.offset()), nullptr, -1);
204 }
205 
206 // Get any vector element as a 64bit int, regardless of what type it is.
GetAnyVectorElemI(const VectorOfAny * vec,reflection::BaseType elem_type,size_t i)207 inline int64_t GetAnyVectorElemI(const VectorOfAny *vec,
208                                  reflection::BaseType elem_type, size_t i) {
209   return GetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i);
210 }
211 
212 // Get any vector element as a double, regardless of what type it is.
GetAnyVectorElemF(const VectorOfAny * vec,reflection::BaseType elem_type,size_t i)213 inline double GetAnyVectorElemF(const VectorOfAny *vec,
214                                 reflection::BaseType elem_type, size_t i) {
215   return GetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i);
216 }
217 
218 // Get any vector element as a string, regardless of what type it is.
GetAnyVectorElemS(const VectorOfAny * vec,reflection::BaseType elem_type,size_t i)219 inline std::string GetAnyVectorElemS(const VectorOfAny *vec,
220                                      reflection::BaseType elem_type, size_t i) {
221   return GetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i,
222                       nullptr, -1);
223 }
224 
225 // Get a vector element that's a table/string/vector from a generic vector.
226 // Pass Table/String/VectorOfAny as template parameter.
227 // Warning: does no typechecking.
228 template<typename T>
GetAnyVectorElemPointer(const VectorOfAny * vec,size_t i)229 T *GetAnyVectorElemPointer(const VectorOfAny *vec, size_t i) {
230   auto elem_ptr = vec->Data() + sizeof(uoffset_t) * i;
231   return reinterpret_cast<T *>(elem_ptr + ReadScalar<uoffset_t>(elem_ptr));
232 }
233 
234 // Get the inline-address of a vector element. Useful for Structs (pass Struct
235 // as template arg), or being able to address a range of scalars in-line.
236 // Get elem_size from GetTypeSizeInline().
237 // Note: little-endian data on all platforms, use EndianScalar() instead of
238 // raw pointer access with scalars).
239 template<typename T>
GetAnyVectorElemAddressOf(const VectorOfAny * vec,size_t i,size_t elem_size)240 T *GetAnyVectorElemAddressOf(const VectorOfAny *vec, size_t i,
241                              size_t elem_size) {
242   return reinterpret_cast<T *>(vec->Data() + elem_size * i);
243 }
244 
245 // Similarly, for elements of tables.
246 template<typename T>
GetAnyFieldAddressOf(const Table & table,const reflection::Field & field)247 T *GetAnyFieldAddressOf(const Table &table, const reflection::Field &field) {
248   return reinterpret_cast<T *>(table.GetAddressOf(field.offset()));
249 }
250 
251 // Similarly, for elements of structs.
252 template<typename T>
GetAnyFieldAddressOf(const Struct & st,const reflection::Field & field)253 T *GetAnyFieldAddressOf(const Struct &st, const reflection::Field &field) {
254   return reinterpret_cast<T *>(st.GetAddressOf(field.offset()));
255 }
256 
257 // ------------------------- SETTERS -------------------------
258 
259 // Set any scalar field, if you know its exact type.
260 template<typename T>
SetField(Table * table,const reflection::Field & field,T val)261 bool SetField(Table *table, const reflection::Field &field, T val) {
262   reflection::BaseType type = field.type()->base_type();
263   if (!IsScalar(type)) { return false; }
264   FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(type));
265   T def;
266   if (IsInteger(type)) {
267     def = GetFieldDefaultI<T>(field);
268   } else {
269     FLATBUFFERS_ASSERT(IsFloat(type));
270     def = GetFieldDefaultF<T>(field);
271   }
272   return table->SetField(field.offset(), val, def);
273 }
274 
275 // Raw helper functions used below: set any value in memory as a 64bit int, a
276 // double or a string.
277 // These work for all scalar values, but do nothing for other data types.
278 // To set a string, see SetString below.
279 void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val);
280 void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val);
281 void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val);
282 
283 // Set any table field as a 64bit int, regardless of type what it is.
SetAnyFieldI(Table * table,const reflection::Field & field,int64_t val)284 inline bool SetAnyFieldI(Table *table, const reflection::Field &field,
285                          int64_t val) {
286   auto field_ptr = table->GetAddressOf(field.offset());
287   if (!field_ptr) return val == GetFieldDefaultI<int64_t>(field);
288   SetAnyValueI(field.type()->base_type(), field_ptr, val);
289   return true;
290 }
291 
292 // Set any table field as a double, regardless of what type it is.
SetAnyFieldF(Table * table,const reflection::Field & field,double val)293 inline bool SetAnyFieldF(Table *table, const reflection::Field &field,
294                          double val) {
295   auto field_ptr = table->GetAddressOf(field.offset());
296   if (!field_ptr) return val == GetFieldDefaultF<double>(field);
297   SetAnyValueF(field.type()->base_type(), field_ptr, val);
298   return true;
299 }
300 
301 // Set any table field as a string, regardless of what type it is.
SetAnyFieldS(Table * table,const reflection::Field & field,const char * val)302 inline bool SetAnyFieldS(Table *table, const reflection::Field &field,
303                          const char *val) {
304   auto field_ptr = table->GetAddressOf(field.offset());
305   if (!field_ptr) return false;
306   SetAnyValueS(field.type()->base_type(), field_ptr, val);
307   return true;
308 }
309 
310 // Set any struct field as a 64bit int, regardless of type what it is.
SetAnyFieldI(Struct * st,const reflection::Field & field,int64_t val)311 inline void SetAnyFieldI(Struct *st, const reflection::Field &field,
312                          int64_t val) {
313   SetAnyValueI(field.type()->base_type(), st->GetAddressOf(field.offset()),
314                val);
315 }
316 
317 // Set any struct field as a double, regardless of type what it is.
SetAnyFieldF(Struct * st,const reflection::Field & field,double val)318 inline void SetAnyFieldF(Struct *st, const reflection::Field &field,
319                          double val) {
320   SetAnyValueF(field.type()->base_type(), st->GetAddressOf(field.offset()),
321                val);
322 }
323 
324 // Set any struct field as a string, regardless of type what it is.
SetAnyFieldS(Struct * st,const reflection::Field & field,const char * val)325 inline void SetAnyFieldS(Struct *st, const reflection::Field &field,
326                          const char *val) {
327   SetAnyValueS(field.type()->base_type(), st->GetAddressOf(field.offset()),
328                val);
329 }
330 
331 // Set any vector element as a 64bit int, regardless of type what it is.
SetAnyVectorElemI(VectorOfAny * vec,reflection::BaseType elem_type,size_t i,int64_t val)332 inline void SetAnyVectorElemI(VectorOfAny *vec, reflection::BaseType elem_type,
333                               size_t i, int64_t val) {
334   SetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
335 }
336 
337 // Set any vector element as a double, regardless of type what it is.
SetAnyVectorElemF(VectorOfAny * vec,reflection::BaseType elem_type,size_t i,double val)338 inline void SetAnyVectorElemF(VectorOfAny *vec, reflection::BaseType elem_type,
339                               size_t i, double val) {
340   SetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
341 }
342 
343 // Set any vector element as a string, regardless of type what it is.
SetAnyVectorElemS(VectorOfAny * vec,reflection::BaseType elem_type,size_t i,const char * val)344 inline void SetAnyVectorElemS(VectorOfAny *vec, reflection::BaseType elem_type,
345                               size_t i, const char *val) {
346   SetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
347 }
348 
349 // ------------------------- RESIZING SETTERS -------------------------
350 
351 // "smart" pointer for use with resizing vectors: turns a pointer inside
352 // a vector into a relative offset, such that it is not affected by resizes.
353 template<typename T, typename U> class pointer_inside_vector {
354  public:
pointer_inside_vector(T * ptr,std::vector<U> & vec)355   pointer_inside_vector(T *ptr, std::vector<U> &vec)
356       : offset_(reinterpret_cast<uint8_t *>(ptr) -
357                 reinterpret_cast<uint8_t *>(flatbuffers::vector_data(vec))),
358         vec_(vec) {}
359 
360   T *operator*() const {
361     return reinterpret_cast<T *>(
362         reinterpret_cast<uint8_t *>(flatbuffers::vector_data(vec_)) + offset_);
363   }
364   T *operator->() const { return operator*(); }
365   void operator=(const pointer_inside_vector &piv);
366 
367  private:
368   size_t offset_;
369   std::vector<U> &vec_;
370 };
371 
372 // Helper to create the above easily without specifying template args.
373 template<typename T, typename U>
piv(T * ptr,std::vector<U> & vec)374 pointer_inside_vector<T, U> piv(T *ptr, std::vector<U> &vec) {
375   return pointer_inside_vector<T, U>(ptr, vec);
376 }
377 
UnionTypeFieldSuffix()378 inline const char *UnionTypeFieldSuffix() { return "_type"; }
379 
380 // Helper to figure out the actual table type a union refers to.
GetUnionType(const reflection::Schema & schema,const reflection::Object & parent,const reflection::Field & unionfield,const Table & table)381 inline const reflection::Object &GetUnionType(
382     const reflection::Schema &schema, const reflection::Object &parent,
383     const reflection::Field &unionfield, const Table &table) {
384   auto enumdef = schema.enums()->Get(unionfield.type()->index());
385   // TODO: this is clumsy and slow, but no other way to find it?
386   auto type_field = parent.fields()->LookupByKey(
387       (unionfield.name()->str() + UnionTypeFieldSuffix()).c_str());
388   FLATBUFFERS_ASSERT(type_field);
389   auto union_type = GetFieldI<uint8_t>(table, *type_field);
390   auto enumval = enumdef->values()->LookupByKey(union_type);
391   return *enumval->object();
392 }
393 
394 // Changes the contents of a string inside a FlatBuffer. FlatBuffer must
395 // live inside a std::vector so we can resize the buffer if needed.
396 // "str" must live inside "flatbuf" and may be invalidated after this call.
397 // If your FlatBuffer's root table is not the schema's root table, you should
398 // pass in your root_table type as well.
399 void SetString(const reflection::Schema &schema, const std::string &val,
400                const String *str, std::vector<uint8_t> *flatbuf,
401                const reflection::Object *root_table = nullptr);
402 
403 // Resizes a flatbuffers::Vector inside a FlatBuffer. FlatBuffer must
404 // live inside a std::vector so we can resize the buffer if needed.
405 // "vec" must live inside "flatbuf" and may be invalidated after this call.
406 // If your FlatBuffer's root table is not the schema's root table, you should
407 // pass in your root_table type as well.
408 uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize,
409                          const VectorOfAny *vec, uoffset_t num_elems,
410                          uoffset_t elem_size, std::vector<uint8_t> *flatbuf,
411                          const reflection::Object *root_table = nullptr);
412 
413 template<typename T>
414 void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
415                   const Vector<T> *vec, std::vector<uint8_t> *flatbuf,
416                   const reflection::Object *root_table = nullptr) {
417   auto delta_elem = static_cast<int>(newsize) - static_cast<int>(vec->size());
418   auto newelems = ResizeAnyVector(
419       schema, newsize, reinterpret_cast<const VectorOfAny *>(vec), vec->size(),
420       static_cast<uoffset_t>(sizeof(T)), flatbuf, root_table);
421   // Set new elements to "val".
422   for (int i = 0; i < delta_elem; i++) {
423     auto loc = newelems + i * sizeof(T);
424     auto is_scalar = flatbuffers::is_scalar<T>::value;
425     if (is_scalar) {
426       WriteScalar(loc, val);
427     } else {  // struct
428       *reinterpret_cast<T *>(loc) = val;
429     }
430   }
431 }
432 
433 // Adds any new data (in the form of a new FlatBuffer) to an existing
434 // FlatBuffer. This can be used when any of the above methods are not
435 // sufficient, in particular for adding new tables and new fields.
436 // This is potentially slightly less efficient than a FlatBuffer constructed
437 // in one piece, since the new FlatBuffer doesn't share any vtables with the
438 // existing one.
439 // The return value can now be set using Vector::MutateOffset or SetFieldT
440 // below.
441 const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
442                              const uint8_t *newbuf, size_t newlen);
443 
SetFieldT(Table * table,const reflection::Field & field,const uint8_t * val)444 inline bool SetFieldT(Table *table, const reflection::Field &field,
445                       const uint8_t *val) {
446   FLATBUFFERS_ASSERT(sizeof(uoffset_t) ==
447                      GetTypeSize(field.type()->base_type()));
448   return table->SetPointer(field.offset(), val);
449 }
450 
451 // ------------------------- COPYING -------------------------
452 
453 // Generic copying of tables from a FlatBuffer into a FlatBuffer builder.
454 // Can be used to do any kind of merging/selecting you may want to do out
455 // of existing buffers. Also useful to reconstruct a whole buffer if the
456 // above resizing functionality has introduced garbage in a buffer you want
457 // to remove.
458 // Note: this does not deal with DAGs correctly. If the table passed forms a
459 // DAG, the copy will be a tree instead (with duplicates). Strings can be
460 // shared however, by passing true for use_string_pooling.
461 
462 Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
463                                 const reflection::Schema &schema,
464                                 const reflection::Object &objectdef,
465                                 const Table &table,
466                                 bool use_string_pooling = false);
467 
468 // Verifies the provided flatbuffer using reflection.
469 // root should point to the root type for this flatbuffer.
470 // buf should point to the start of flatbuffer data.
471 // length specifies the size of the flatbuffer data.
472 bool Verify(const reflection::Schema &schema, const reflection::Object &root,
473             const uint8_t *buf, size_t length);
474 
475 }  // namespace flatbuffers
476 
477 #endif  // FLATBUFFERS_REFLECTION_H_
478