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