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