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 #include "flatbuffers/reflection.h"
18
19 #include "flatbuffers/util.h"
20
21 // Helper functionality for reflection.
22
23 namespace flatbuffers {
24
GetAnyValueI(reflection::BaseType type,const uint8_t * data)25 int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) {
26 // clang-format off
27 #define FLATBUFFERS_GET(T) static_cast<int64_t>(ReadScalar<T>(data))
28 switch (type) {
29 case reflection::UType:
30 case reflection::Bool:
31 case reflection::UByte: return FLATBUFFERS_GET(uint8_t);
32 case reflection::Byte: return FLATBUFFERS_GET(int8_t);
33 case reflection::Short: return FLATBUFFERS_GET(int16_t);
34 case reflection::UShort: return FLATBUFFERS_GET(uint16_t);
35 case reflection::Int: return FLATBUFFERS_GET(int32_t);
36 case reflection::UInt: return FLATBUFFERS_GET(uint32_t);
37 case reflection::Long: return FLATBUFFERS_GET(int64_t);
38 case reflection::ULong: return FLATBUFFERS_GET(uint64_t);
39 case reflection::Float: return FLATBUFFERS_GET(float);
40 case reflection::Double: return FLATBUFFERS_GET(double);
41 case reflection::String: {
42 auto s = reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) +
43 data);
44 return s ? StringToInt(s->c_str()) : 0;
45 }
46 default: return 0; // Tables & vectors do not make sense.
47 }
48 #undef FLATBUFFERS_GET
49 // clang-format on
50 }
51
GetAnyValueF(reflection::BaseType type,const uint8_t * data)52 double GetAnyValueF(reflection::BaseType type, const uint8_t *data) {
53 switch (type) {
54 case reflection::Float: return static_cast<double>(ReadScalar<float>(data));
55 case reflection::Double: return ReadScalar<double>(data);
56 case reflection::String: {
57 auto s =
58 reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) + data);
59 if (s) {
60 double d;
61 StringToNumber(s->c_str(), &d);
62 return d;
63 } else {
64 return 0.0;
65 }
66 }
67 default: return static_cast<double>(GetAnyValueI(type, data));
68 }
69 }
70
GetAnyValueS(reflection::BaseType type,const uint8_t * data,const reflection::Schema * schema,int type_index)71 std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data,
72 const reflection::Schema *schema, int type_index) {
73 switch (type) {
74 case reflection::Float:
75 case reflection::Double: return NumToString(GetAnyValueF(type, data));
76 case reflection::String: {
77 auto s =
78 reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) + data);
79 return s ? s->c_str() : "";
80 }
81 case reflection::Obj:
82 if (schema) {
83 // Convert the table to a string. This is mostly for debugging purposes,
84 // and does NOT promise to be JSON compliant.
85 // Also prefixes the type.
86 auto &objectdef = *schema->objects()->Get(type_index);
87 auto s = objectdef.name()->str();
88 if (objectdef.is_struct()) {
89 s += "(struct)"; // TODO: implement this as well.
90 } else {
91 auto table_field = reinterpret_cast<const Table *>(
92 ReadScalar<uoffset_t>(data) + data);
93 s += " { ";
94 auto fielddefs = objectdef.fields();
95 for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
96 auto &fielddef = **it;
97 if (!table_field->CheckField(fielddef.offset())) continue;
98 auto val = GetAnyFieldS(*table_field, fielddef, schema);
99 if (fielddef.type()->base_type() == reflection::String) {
100 std::string esc;
101 flatbuffers::EscapeString(val.c_str(), val.length(), &esc, true,
102 false);
103 val = esc;
104 }
105 s += fielddef.name()->str();
106 s += ": ";
107 s += val;
108 s += ", ";
109 }
110 s += "}";
111 }
112 return s;
113 } else {
114 return "(table)";
115 }
116 case reflection::Vector:
117 return "[(elements)]"; // TODO: implement this as well.
118 case reflection::Union: return "(union)"; // TODO: implement this as well.
119 default: return NumToString(GetAnyValueI(type, data));
120 }
121 }
122
SetAnyValueI(reflection::BaseType type,uint8_t * data,int64_t val)123 void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) {
124 // clang-format off
125 #define FLATBUFFERS_SET(T) WriteScalar(data, static_cast<T>(val))
126 switch (type) {
127 case reflection::UType:
128 case reflection::Bool:
129 case reflection::UByte: FLATBUFFERS_SET(uint8_t ); break;
130 case reflection::Byte: FLATBUFFERS_SET(int8_t ); break;
131 case reflection::Short: FLATBUFFERS_SET(int16_t ); break;
132 case reflection::UShort: FLATBUFFERS_SET(uint16_t); break;
133 case reflection::Int: FLATBUFFERS_SET(int32_t ); break;
134 case reflection::UInt: FLATBUFFERS_SET(uint32_t); break;
135 case reflection::Long: FLATBUFFERS_SET(int64_t ); break;
136 case reflection::ULong: FLATBUFFERS_SET(uint64_t); break;
137 case reflection::Float: FLATBUFFERS_SET(float ); break;
138 case reflection::Double: FLATBUFFERS_SET(double ); break;
139 // TODO: support strings
140 default: break;
141 }
142 #undef FLATBUFFERS_SET
143 // clang-format on
144 }
145
SetAnyValueF(reflection::BaseType type,uint8_t * data,double val)146 void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val) {
147 switch (type) {
148 case reflection::Float: WriteScalar(data, static_cast<float>(val)); break;
149 case reflection::Double: WriteScalar(data, val); break;
150 // TODO: support strings.
151 default: SetAnyValueI(type, data, static_cast<int64_t>(val)); break;
152 }
153 }
154
SetAnyValueS(reflection::BaseType type,uint8_t * data,const char * val)155 void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val) {
156 switch (type) {
157 case reflection::Float:
158 case reflection::Double: {
159 double d;
160 StringToNumber(val, &d);
161 SetAnyValueF(type, data, d);
162 break;
163 }
164 // TODO: support strings.
165 default: SetAnyValueI(type, data, StringToInt(val)); break;
166 }
167 }
168
169 // Resize a FlatBuffer in-place by iterating through all offsets in the buffer
170 // and adjusting them by "delta" if they straddle the start offset.
171 // Once that is done, bytes can now be inserted/deleted safely.
172 // "delta" may be negative (shrinking).
173 // Unless "delta" is a multiple of the largest alignment, you'll create a small
174 // amount of garbage space in the buffer (usually 0..7 bytes).
175 // If your FlatBuffer's root table is not the schema's root table, you should
176 // pass in your root_table type as well.
177 class ResizeContext {
178 public:
ResizeContext(const reflection::Schema & schema,uoffset_t start,int delta,std::vector<uint8_t> * flatbuf,const reflection::Object * root_table=nullptr)179 ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta,
180 std::vector<uint8_t> *flatbuf,
181 const reflection::Object *root_table = nullptr)
182 : schema_(schema),
183 startptr_(vector_data(*flatbuf) + start),
184 delta_(delta),
185 buf_(*flatbuf),
186 dag_check_(flatbuf->size() / sizeof(uoffset_t), false) {
187 auto mask = static_cast<int>(sizeof(largest_scalar_t) - 1);
188 delta_ = (delta_ + mask) & ~mask;
189 if (!delta_) return; // We can't shrink by less than largest_scalar_t.
190 // Now change all the offsets by delta_.
191 auto root = GetAnyRoot(vector_data(buf_));
192 Straddle<uoffset_t, 1>(vector_data(buf_), root, vector_data(buf_));
193 ResizeTable(root_table ? *root_table : *schema.root_table(), root);
194 // We can now add or remove bytes at start.
195 if (delta_ > 0)
196 buf_.insert(buf_.begin() + start, delta_, 0);
197 else
198 buf_.erase(buf_.begin() + start + delta_, buf_.begin() + start);
199 }
200
201 // Check if the range between first (lower address) and second straddles
202 // the insertion point. If it does, change the offset at offsetloc (of
203 // type T, with direction D).
204 template<typename T, int D>
Straddle(const void * first,const void * second,void * offsetloc)205 void Straddle(const void *first, const void *second, void *offsetloc) {
206 if (first <= startptr_ && second >= startptr_) {
207 WriteScalar<T>(offsetloc, ReadScalar<T>(offsetloc) + delta_ * D);
208 DagCheck(offsetloc) = true;
209 }
210 }
211
212 // This returns a boolean that records if the corresponding offset location
213 // has been modified already. If so, we can't even read the corresponding
214 // offset, since it is pointing to a location that is illegal until the
215 // resize actually happens.
216 // This must be checked for every offset, since we can't know which offsets
217 // will straddle and which won't.
DagCheck(const void * offsetloc)218 uint8_t &DagCheck(const void *offsetloc) {
219 auto dag_idx = reinterpret_cast<const uoffset_t *>(offsetloc) -
220 reinterpret_cast<const uoffset_t *>(vector_data(buf_));
221 return dag_check_[dag_idx];
222 }
223
ResizeTable(const reflection::Object & objectdef,Table * table)224 void ResizeTable(const reflection::Object &objectdef, Table *table) {
225 if (DagCheck(table)) return; // Table already visited.
226 auto vtable = table->GetVTable();
227 // Early out: since all fields inside the table must point forwards in
228 // memory, if the insertion point is before the table we can stop here.
229 auto tableloc = reinterpret_cast<uint8_t *>(table);
230 if (startptr_ <= tableloc) {
231 // Check if insertion point is between the table and a vtable that
232 // precedes it. This can't happen in current construction code, but check
233 // just in case we ever change the way flatbuffers are built.
234 Straddle<soffset_t, -1>(vtable, table, table);
235 } else {
236 // Check each field.
237 auto fielddefs = objectdef.fields();
238 for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
239 auto &fielddef = **it;
240 auto base_type = fielddef.type()->base_type();
241 // Ignore scalars.
242 if (base_type <= reflection::Double) continue;
243 // Ignore fields that are not stored.
244 auto offset = table->GetOptionalFieldOffset(fielddef.offset());
245 if (!offset) continue;
246 // Ignore structs.
247 auto subobjectdef =
248 base_type == reflection::Obj
249 ? schema_.objects()->Get(fielddef.type()->index())
250 : nullptr;
251 if (subobjectdef && subobjectdef->is_struct()) continue;
252 // Get this fields' offset, and read it if safe.
253 auto offsetloc = tableloc + offset;
254 if (DagCheck(offsetloc)) continue; // This offset already visited.
255 auto ref = offsetloc + ReadScalar<uoffset_t>(offsetloc);
256 Straddle<uoffset_t, 1>(offsetloc, ref, offsetloc);
257 // Recurse.
258 switch (base_type) {
259 case reflection::Obj: {
260 ResizeTable(*subobjectdef, reinterpret_cast<Table *>(ref));
261 break;
262 }
263 case reflection::Vector: {
264 auto elem_type = fielddef.type()->element();
265 if (elem_type != reflection::Obj && elem_type != reflection::String)
266 break;
267 auto vec = reinterpret_cast<Vector<uoffset_t> *>(ref);
268 auto elemobjectdef =
269 elem_type == reflection::Obj
270 ? schema_.objects()->Get(fielddef.type()->index())
271 : nullptr;
272 if (elemobjectdef && elemobjectdef->is_struct()) break;
273 for (uoffset_t i = 0; i < vec->size(); i++) {
274 auto loc = vec->Data() + i * sizeof(uoffset_t);
275 if (DagCheck(loc)) continue; // This offset already visited.
276 auto dest = loc + vec->Get(i);
277 Straddle<uoffset_t, 1>(loc, dest, loc);
278 if (elemobjectdef)
279 ResizeTable(*elemobjectdef, reinterpret_cast<Table *>(dest));
280 }
281 break;
282 }
283 case reflection::Union: {
284 ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table),
285 reinterpret_cast<Table *>(ref));
286 break;
287 }
288 case reflection::String: break;
289 default: FLATBUFFERS_ASSERT(false);
290 }
291 }
292 // Check if the vtable offset points beyond the insertion point.
293 // Must do this last, since GetOptionalFieldOffset above still reads
294 // this value.
295 Straddle<soffset_t, -1>(table, vtable, table);
296 }
297 }
298
299 private:
300 const reflection::Schema &schema_;
301 uint8_t *startptr_;
302 int delta_;
303 std::vector<uint8_t> &buf_;
304 std::vector<uint8_t> dag_check_;
305 };
306
SetString(const reflection::Schema & schema,const std::string & val,const String * str,std::vector<uint8_t> * flatbuf,const reflection::Object * root_table)307 void SetString(const reflection::Schema &schema, const std::string &val,
308 const String *str, std::vector<uint8_t> *flatbuf,
309 const reflection::Object *root_table) {
310 auto delta = static_cast<int>(val.size()) - static_cast<int>(str->size());
311 auto str_start = static_cast<uoffset_t>(
312 reinterpret_cast<const uint8_t *>(str) - vector_data(*flatbuf));
313 auto start = str_start + static_cast<uoffset_t>(sizeof(uoffset_t));
314 if (delta) {
315 // Clear the old string, since we don't want parts of it remaining.
316 memset(vector_data(*flatbuf) + start, 0, str->size());
317 // Different size, we must expand (or contract).
318 ResizeContext(schema, start, delta, flatbuf, root_table);
319 // Set the new length.
320 WriteScalar(vector_data(*flatbuf) + str_start,
321 static_cast<uoffset_t>(val.size()));
322 }
323 // Copy new data. Safe because we created the right amount of space.
324 memcpy(vector_data(*flatbuf) + start, val.c_str(), val.size() + 1);
325 }
326
ResizeAnyVector(const reflection::Schema & schema,uoffset_t newsize,const VectorOfAny * vec,uoffset_t num_elems,uoffset_t elem_size,std::vector<uint8_t> * flatbuf,const reflection::Object * root_table)327 uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize,
328 const VectorOfAny *vec, uoffset_t num_elems,
329 uoffset_t elem_size, std::vector<uint8_t> *flatbuf,
330 const reflection::Object *root_table) {
331 auto delta_elem = static_cast<int>(newsize) - static_cast<int>(num_elems);
332 auto delta_bytes = delta_elem * static_cast<int>(elem_size);
333 auto vec_start =
334 reinterpret_cast<const uint8_t *>(vec) - vector_data(*flatbuf);
335 auto start = static_cast<uoffset_t>(vec_start + sizeof(uoffset_t) +
336 elem_size * num_elems);
337 if (delta_bytes) {
338 if (delta_elem < 0) {
339 // Clear elements we're throwing away, since some might remain in the
340 // buffer.
341 auto size_clear = -delta_elem * elem_size;
342 memset(vector_data(*flatbuf) + start - size_clear, 0, size_clear);
343 }
344 ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
345 WriteScalar(vector_data(*flatbuf) + vec_start, newsize); // Length field.
346 // Set new elements to 0.. this can be overwritten by the caller.
347 if (delta_elem > 0) {
348 memset(vector_data(*flatbuf) + start, 0, delta_elem * elem_size);
349 }
350 }
351 return vector_data(*flatbuf) + start;
352 }
353
AddFlatBuffer(std::vector<uint8_t> & flatbuf,const uint8_t * newbuf,size_t newlen)354 const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
355 const uint8_t *newbuf, size_t newlen) {
356 // Align to sizeof(uoffset_t) past sizeof(largest_scalar_t) since we're
357 // going to chop off the root offset.
358 while ((flatbuf.size() & (sizeof(uoffset_t) - 1)) ||
359 !(flatbuf.size() & (sizeof(largest_scalar_t) - 1))) {
360 flatbuf.push_back(0);
361 }
362 auto insertion_point = static_cast<uoffset_t>(flatbuf.size());
363 // Insert the entire FlatBuffer minus the root pointer.
364 flatbuf.insert(flatbuf.end(), newbuf + sizeof(uoffset_t), newbuf + newlen);
365 auto root_offset = ReadScalar<uoffset_t>(newbuf) - sizeof(uoffset_t);
366 return vector_data(flatbuf) + insertion_point + root_offset;
367 }
368
CopyInline(FlatBufferBuilder & fbb,const reflection::Field & fielddef,const Table & table,size_t align,size_t size)369 void CopyInline(FlatBufferBuilder &fbb, const reflection::Field &fielddef,
370 const Table &table, size_t align, size_t size) {
371 fbb.Align(align);
372 fbb.PushBytes(table.GetStruct<const uint8_t *>(fielddef.offset()), size);
373 fbb.TrackField(fielddef.offset(), fbb.GetSize());
374 }
375
CopyTable(FlatBufferBuilder & fbb,const reflection::Schema & schema,const reflection::Object & objectdef,const Table & table,bool use_string_pooling)376 Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
377 const reflection::Schema &schema,
378 const reflection::Object &objectdef,
379 const Table &table, bool use_string_pooling) {
380 // Before we can construct the table, we have to first generate any
381 // subobjects, and collect their offsets.
382 std::vector<uoffset_t> offsets;
383 auto fielddefs = objectdef.fields();
384 for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
385 auto &fielddef = **it;
386 // Skip if field is not present in the source.
387 if (!table.CheckField(fielddef.offset())) continue;
388 uoffset_t offset = 0;
389 switch (fielddef.type()->base_type()) {
390 case reflection::String: {
391 offset = use_string_pooling
392 ? fbb.CreateSharedString(GetFieldS(table, fielddef)).o
393 : fbb.CreateString(GetFieldS(table, fielddef)).o;
394 break;
395 }
396 case reflection::Obj: {
397 auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
398 if (!subobjectdef.is_struct()) {
399 offset = CopyTable(fbb, schema, subobjectdef,
400 *GetFieldT(table, fielddef), use_string_pooling)
401 .o;
402 }
403 break;
404 }
405 case reflection::Union: {
406 auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table);
407 offset = CopyTable(fbb, schema, subobjectdef,
408 *GetFieldT(table, fielddef), use_string_pooling)
409 .o;
410 break;
411 }
412 case reflection::Vector: {
413 auto vec =
414 table.GetPointer<const Vector<Offset<Table>> *>(fielddef.offset());
415 auto element_base_type = fielddef.type()->element();
416 auto elemobjectdef =
417 element_base_type == reflection::Obj
418 ? schema.objects()->Get(fielddef.type()->index())
419 : nullptr;
420 switch (element_base_type) {
421 case reflection::String: {
422 std::vector<Offset<const String *>> elements(vec->size());
423 auto vec_s = reinterpret_cast<const Vector<Offset<String>> *>(vec);
424 for (uoffset_t i = 0; i < vec_s->size(); i++) {
425 elements[i] = use_string_pooling
426 ? fbb.CreateSharedString(vec_s->Get(i)).o
427 : fbb.CreateString(vec_s->Get(i)).o;
428 }
429 offset = fbb.CreateVector(elements).o;
430 break;
431 }
432 case reflection::Obj: {
433 if (!elemobjectdef->is_struct()) {
434 std::vector<Offset<const Table *>> elements(vec->size());
435 for (uoffset_t i = 0; i < vec->size(); i++) {
436 elements[i] = CopyTable(fbb, schema, *elemobjectdef,
437 *vec->Get(i), use_string_pooling);
438 }
439 offset = fbb.CreateVector(elements).o;
440 break;
441 }
442 }
443 FLATBUFFERS_FALLTHROUGH(); // fall thru
444 default: { // Scalars and structs.
445 auto element_size = GetTypeSize(element_base_type);
446 if (elemobjectdef && elemobjectdef->is_struct())
447 element_size = elemobjectdef->bytesize();
448 fbb.StartVector(vec->size(), element_size);
449 fbb.PushBytes(vec->Data(), element_size * vec->size());
450 offset = fbb.EndVector(vec->size());
451 break;
452 }
453 }
454 break;
455 }
456 default: // Scalars.
457 break;
458 }
459 if (offset) { offsets.push_back(offset); }
460 }
461 // Now we can build the actual table from either offsets or scalar data.
462 auto start = objectdef.is_struct() ? fbb.StartStruct(objectdef.minalign())
463 : fbb.StartTable();
464 size_t offset_idx = 0;
465 for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
466 auto &fielddef = **it;
467 if (!table.CheckField(fielddef.offset())) continue;
468 auto base_type = fielddef.type()->base_type();
469 switch (base_type) {
470 case reflection::Obj: {
471 auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
472 if (subobjectdef.is_struct()) {
473 CopyInline(fbb, fielddef, table, subobjectdef.minalign(),
474 subobjectdef.bytesize());
475 break;
476 }
477 }
478 FLATBUFFERS_FALLTHROUGH(); // fall thru
479 case reflection::Union:
480 case reflection::String:
481 case reflection::Vector:
482 fbb.AddOffset(fielddef.offset(), Offset<void>(offsets[offset_idx++]));
483 break;
484 default: { // Scalars.
485 auto size = GetTypeSize(base_type);
486 CopyInline(fbb, fielddef, table, size, size);
487 break;
488 }
489 }
490 }
491 FLATBUFFERS_ASSERT(offset_idx == offsets.size());
492 if (objectdef.is_struct()) {
493 fbb.ClearOffsets();
494 return fbb.EndStruct();
495 } else {
496 return fbb.EndTable(start);
497 }
498 }
499
VerifyStruct(flatbuffers::Verifier & v,const flatbuffers::Table & parent_table,voffset_t field_offset,const reflection::Object & obj,bool required)500 bool VerifyStruct(flatbuffers::Verifier &v,
501 const flatbuffers::Table &parent_table,
502 voffset_t field_offset, const reflection::Object &obj,
503 bool required) {
504 auto offset = parent_table.GetOptionalFieldOffset(field_offset);
505 if (required && !offset) { return false; }
506
507 return !offset || v.Verify(reinterpret_cast<const uint8_t *>(&parent_table),
508 offset, obj.bytesize());
509 }
510
VerifyVectorOfStructs(flatbuffers::Verifier & v,const flatbuffers::Table & parent_table,voffset_t field_offset,const reflection::Object & obj,bool required)511 bool VerifyVectorOfStructs(flatbuffers::Verifier &v,
512 const flatbuffers::Table &parent_table,
513 voffset_t field_offset,
514 const reflection::Object &obj, bool required) {
515 auto p = parent_table.GetPointer<const uint8_t *>(field_offset);
516 if (required && !p) { return false; }
517
518 return !p || v.VerifyVectorOrString(p, obj.bytesize());
519 }
520
521 // forward declare to resolve cyclic deps between VerifyObject and VerifyVector
522 bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
523 const reflection::Object &obj,
524 const flatbuffers::Table *table, bool required);
525
VerifyUnion(flatbuffers::Verifier & v,const reflection::Schema & schema,uint8_t utype,const uint8_t * elem,const reflection::Field & union_field)526 bool VerifyUnion(flatbuffers::Verifier &v, const reflection::Schema &schema,
527 uint8_t utype, const uint8_t *elem,
528 const reflection::Field &union_field) {
529 if (!utype) return true; // Not present.
530 auto fb_enum = schema.enums()->Get(union_field.type()->index());
531 if (utype >= fb_enum->values()->size()) return false;
532 auto elem_type = fb_enum->values()->Get(utype)->union_type();
533 switch (elem_type->base_type()) {
534 case reflection::Obj: {
535 auto elem_obj = schema.objects()->Get(elem_type->index());
536 if (elem_obj->is_struct()) {
537 return v.VerifyFromPointer(elem, elem_obj->bytesize());
538 } else {
539 return VerifyObject(v, schema, *elem_obj,
540 reinterpret_cast<const flatbuffers::Table *>(elem),
541 true);
542 }
543 }
544 case reflection::String:
545 return v.VerifyString(
546 reinterpret_cast<const flatbuffers::String *>(elem));
547 default: return false;
548 }
549 }
550
VerifyVector(flatbuffers::Verifier & v,const reflection::Schema & schema,const flatbuffers::Table & table,const reflection::Field & vec_field)551 bool VerifyVector(flatbuffers::Verifier &v, const reflection::Schema &schema,
552 const flatbuffers::Table &table,
553 const reflection::Field &vec_field) {
554 FLATBUFFERS_ASSERT(vec_field.type()->base_type() == reflection::Vector);
555 if (!table.VerifyField<uoffset_t>(v, vec_field.offset())) return false;
556
557 switch (vec_field.type()->element()) {
558 case reflection::UType:
559 return v.VerifyVector(flatbuffers::GetFieldV<uint8_t>(table, vec_field));
560 case reflection::Bool:
561 case reflection::Byte:
562 case reflection::UByte:
563 return v.VerifyVector(flatbuffers::GetFieldV<int8_t>(table, vec_field));
564 case reflection::Short:
565 case reflection::UShort:
566 return v.VerifyVector(flatbuffers::GetFieldV<int16_t>(table, vec_field));
567 case reflection::Int:
568 case reflection::UInt:
569 return v.VerifyVector(flatbuffers::GetFieldV<int32_t>(table, vec_field));
570 case reflection::Long:
571 case reflection::ULong:
572 return v.VerifyVector(flatbuffers::GetFieldV<int64_t>(table, vec_field));
573 case reflection::Float:
574 return v.VerifyVector(flatbuffers::GetFieldV<float>(table, vec_field));
575 case reflection::Double:
576 return v.VerifyVector(flatbuffers::GetFieldV<double>(table, vec_field));
577 case reflection::String: {
578 auto vec_string =
579 flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
580 table, vec_field);
581 if (v.VerifyVector(vec_string) && v.VerifyVectorOfStrings(vec_string)) {
582 return true;
583 } else {
584 return false;
585 }
586 }
587 case reflection::Obj: {
588 auto obj = schema.objects()->Get(vec_field.type()->index());
589 if (obj->is_struct()) {
590 return VerifyVectorOfStructs(v, table, vec_field.offset(), *obj,
591 vec_field.required());
592 } else {
593 auto vec =
594 flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::Table>>(
595 table, vec_field);
596 if (!v.VerifyVector(vec)) return false;
597 if (!vec) return true;
598 for (uoffset_t j = 0; j < vec->size(); j++) {
599 if (!VerifyObject(v, schema, *obj, vec->Get(j), true)) {
600 return false;
601 }
602 }
603 return true;
604 }
605 }
606 case reflection::Union: {
607 auto vec = flatbuffers::GetFieldV<flatbuffers::Offset<uint8_t>>(
608 table, vec_field);
609 if (!v.VerifyVector(vec)) return false;
610 if (!vec) return true;
611 auto type_vec = table.GetPointer<Vector<uint8_t> *>(vec_field.offset() -
612 sizeof(voffset_t));
613 if (!v.VerifyVector(type_vec)) return false;
614 for (uoffset_t j = 0; j < vec->size(); j++) {
615 // get union type from the prev field
616 auto utype = type_vec->Get(j);
617 auto elem = vec->Get(j);
618 if (!VerifyUnion(v, schema, utype, elem, vec_field)) return false;
619 }
620 return true;
621 }
622 case reflection::Vector:
623 case reflection::None:
624 default: FLATBUFFERS_ASSERT(false); return false;
625 }
626 }
627
VerifyObject(flatbuffers::Verifier & v,const reflection::Schema & schema,const reflection::Object & obj,const flatbuffers::Table * table,bool required)628 bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
629 const reflection::Object &obj,
630 const flatbuffers::Table *table, bool required) {
631 if (!table) return !required;
632 if (!table->VerifyTableStart(v)) return false;
633 for (uoffset_t i = 0; i < obj.fields()->size(); i++) {
634 auto field_def = obj.fields()->Get(i);
635 switch (field_def->type()->base_type()) {
636 case reflection::None: FLATBUFFERS_ASSERT(false); break;
637 case reflection::UType:
638 if (!table->VerifyField<uint8_t>(v, field_def->offset())) return false;
639 break;
640 case reflection::Bool:
641 case reflection::Byte:
642 case reflection::UByte:
643 if (!table->VerifyField<int8_t>(v, field_def->offset())) return false;
644 break;
645 case reflection::Short:
646 case reflection::UShort:
647 if (!table->VerifyField<int16_t>(v, field_def->offset())) return false;
648 break;
649 case reflection::Int:
650 case reflection::UInt:
651 if (!table->VerifyField<int32_t>(v, field_def->offset())) return false;
652 break;
653 case reflection::Long:
654 case reflection::ULong:
655 if (!table->VerifyField<int64_t>(v, field_def->offset())) return false;
656 break;
657 case reflection::Float:
658 if (!table->VerifyField<float>(v, field_def->offset())) return false;
659 break;
660 case reflection::Double:
661 if (!table->VerifyField<double>(v, field_def->offset())) return false;
662 break;
663 case reflection::String:
664 if (!table->VerifyField<uoffset_t>(v, field_def->offset()) ||
665 !v.VerifyString(flatbuffers::GetFieldS(*table, *field_def))) {
666 return false;
667 }
668 break;
669 case reflection::Vector:
670 if (!VerifyVector(v, schema, *table, *field_def)) return false;
671 break;
672 case reflection::Obj: {
673 auto child_obj = schema.objects()->Get(field_def->type()->index());
674 if (child_obj->is_struct()) {
675 if (!VerifyStruct(v, *table, field_def->offset(), *child_obj,
676 field_def->required())) {
677 return false;
678 }
679 } else {
680 if (!VerifyObject(v, schema, *child_obj,
681 flatbuffers::GetFieldT(*table, *field_def),
682 field_def->required())) {
683 return false;
684 }
685 }
686 break;
687 }
688 case reflection::Union: {
689 // get union type from the prev field
690 voffset_t utype_offset = field_def->offset() - sizeof(voffset_t);
691 auto utype = table->GetField<uint8_t>(utype_offset, 0);
692 auto uval = reinterpret_cast<const uint8_t *>(
693 flatbuffers::GetFieldT(*table, *field_def));
694 if (!VerifyUnion(v, schema, utype, uval, *field_def)) { return false; }
695 break;
696 }
697 default: FLATBUFFERS_ASSERT(false); break;
698 }
699 }
700
701 if (!v.EndTable()) return false;
702
703 return true;
704 }
705
Verify(const reflection::Schema & schema,const reflection::Object & root,const uint8_t * buf,size_t length,uoffset_t max_depth,uoffset_t max_tables)706 bool Verify(const reflection::Schema &schema, const reflection::Object &root,
707 const uint8_t *buf, size_t length, uoffset_t max_depth /*= 64*/,
708 uoffset_t max_tables /*= 1000000*/) {
709 Verifier v(buf, length, max_depth, max_tables);
710 return VerifyObject(v, schema, root, flatbuffers::GetAnyRoot(buf), true);
711 }
712
713 } // namespace flatbuffers
714