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