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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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: |
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_(flatbuf->data() + 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(buf_.data()); |
192 | Straddle<uoffset_t, 1>(buf_.data(), root, buf_.data()); |
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> |
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. |
218 | uint8_t &DagCheck(const void *offsetloc) { |
219 | auto dag_idx = reinterpret_cast<const uoffset_t *>(offsetloc) - |
220 | reinterpret_cast<const uoffset_t *>(buf_.data()); |
221 | return dag_check_[dag_idx]; |
222 | } |
223 | |
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 | |
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) - flatbuf->data()); |
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(flatbuf->data() + 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(flatbuf->data() + 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(flatbuf->data() + start, val.c_str(), val.size() + 1); |
325 | } |
326 | |
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 = reinterpret_cast<const uint8_t *>(vec) - flatbuf->data(); |
334 | auto start = static_cast<uoffset_t>(vec_start) + |
335 | static_cast<uoffset_t>(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(flatbuf->data() + start - size_clear, 0, size_clear); |
343 | } |
344 | ResizeContext(schema, start, delta_bytes, flatbuf, root_table); |
345 | WriteScalar(flatbuf->data() + 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(flatbuf->data() + start, 0, |
349 | static_cast<size_t>(delta_elem) * elem_size); |
350 | } |
351 | } |
352 | return flatbuf->data() + start; |
353 | } |
354 | |
355 | const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf, |
356 | const uint8_t *newbuf, size_t newlen) { |
357 | // Align to sizeof(uoffset_t) past sizeof(largest_scalar_t) since we're |
358 | // going to chop off the root offset. |
359 | while ((flatbuf.size() & (sizeof(uoffset_t) - 1)) || |
360 | !(flatbuf.size() & (sizeof(largest_scalar_t) - 1))) { |
361 | flatbuf.push_back(0); |
362 | } |
363 | auto insertion_point = static_cast<uoffset_t>(flatbuf.size()); |
364 | // Insert the entire FlatBuffer minus the root pointer. |
365 | flatbuf.insert(flatbuf.end(), newbuf + sizeof(uoffset_t), newbuf + newlen); |
366 | auto root_offset = ReadScalar<uoffset_t>(newbuf) - sizeof(uoffset_t); |
367 | return flatbuf.data() + insertion_point + root_offset; |
368 | } |
369 | |
370 | void CopyInline(FlatBufferBuilder &fbb, const reflection::Field &fielddef, |
371 | const Table &table, size_t align, size_t size) { |
372 | fbb.Align(align); |
373 | fbb.PushBytes(table.GetStruct<const uint8_t *>(fielddef.offset()), size); |
374 | fbb.TrackField(fielddef.offset(), fbb.GetSize()); |
375 | } |
376 | |
377 | Offset<const Table *> CopyTable(FlatBufferBuilder &fbb, |
378 | const reflection::Schema &schema, |
379 | const reflection::Object &objectdef, |
380 | const Table &table, bool use_string_pooling) { |
381 | // Before we can construct the table, we have to first generate any |
382 | // subobjects, and collect their offsets. |
383 | std::vector<uoffset_t> offsets; |
384 | auto fielddefs = objectdef.fields(); |
385 | for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { |
386 | auto &fielddef = **it; |
387 | // Skip if field is not present in the source. |
388 | if (!table.CheckField(fielddef.offset())) continue; |
389 | uoffset_t offset = 0; |
390 | switch (fielddef.type()->base_type()) { |
391 | case reflection::String: { |
392 | offset = use_string_pooling |
393 | ? fbb.CreateSharedString(GetFieldS(table, fielddef)).o |
394 | : fbb.CreateString(GetFieldS(table, fielddef)).o; |
395 | break; |
396 | } |
397 | case reflection::Obj: { |
398 | auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index()); |
399 | if (!subobjectdef.is_struct()) { |
400 | offset = CopyTable(fbb, schema, subobjectdef, |
401 | *GetFieldT(table, fielddef), use_string_pooling) |
402 | .o; |
403 | } |
404 | break; |
405 | } |
406 | case reflection::Union: { |
407 | auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table); |
408 | offset = CopyTable(fbb, schema, subobjectdef, |
409 | *GetFieldT(table, fielddef), use_string_pooling) |
410 | .o; |
411 | break; |
412 | } |
413 | case reflection::Vector: { |
414 | auto vec = |
415 | table.GetPointer<const Vector<Offset<Table>> *>(fielddef.offset()); |
416 | auto element_base_type = fielddef.type()->element(); |
417 | auto elemobjectdef = |
418 | element_base_type == reflection::Obj |
419 | ? schema.objects()->Get(fielddef.type()->index()) |
420 | : nullptr; |
421 | switch (element_base_type) { |
422 | case reflection::String: { |
423 | std::vector<Offset<const String *>> elements(vec->size()); |
424 | auto vec_s = reinterpret_cast<const Vector<Offset<String>> *>(vec); |
425 | for (uoffset_t i = 0; i < vec_s->size(); i++) { |
426 | elements[i] = use_string_pooling |
427 | ? fbb.CreateSharedString(vec_s->Get(i)).o |
428 | : fbb.CreateString(vec_s->Get(i)).o; |
429 | } |
430 | offset = fbb.CreateVector(elements).o; |
431 | break; |
432 | } |
433 | case reflection::Obj: { |
434 | if (!elemobjectdef->is_struct()) { |
435 | std::vector<Offset<const Table *>> elements(vec->size()); |
436 | for (uoffset_t i = 0; i < vec->size(); i++) { |
437 | elements[i] = CopyTable(fbb, schema, *elemobjectdef, |
438 | *vec->Get(i), use_string_pooling); |
439 | } |
440 | offset = fbb.CreateVector(elements).o; |
441 | break; |
442 | } |
443 | } |
444 | FLATBUFFERS_FALLTHROUGH(); // fall thru |
445 | default: { // Scalars and structs. |
446 | auto element_size = GetTypeSize(element_base_type); |
447 | if (elemobjectdef && elemobjectdef->is_struct()) |
448 | element_size = elemobjectdef->bytesize(); |
449 | fbb.StartVector(vec->size(), element_size); |
450 | fbb.PushBytes(vec->Data(), element_size * vec->size()); |
451 | offset = fbb.EndVector(vec->size()); |
452 | break; |
453 | } |
454 | } |
455 | break; |
456 | } |
457 | default: // Scalars. |
458 | break; |
459 | } |
460 | if (offset) { offsets.push_back(offset); } |
461 | } |
462 | // Now we can build the actual table from either offsets or scalar data. |
463 | auto start = objectdef.is_struct() ? fbb.StartStruct(objectdef.minalign()) |
464 | : fbb.StartTable(); |
465 | size_t offset_idx = 0; |
466 | for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { |
467 | auto &fielddef = **it; |
468 | if (!table.CheckField(fielddef.offset())) continue; |
469 | auto base_type = fielddef.type()->base_type(); |
470 | switch (base_type) { |
471 | case reflection::Obj: { |
472 | auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index()); |
473 | if (subobjectdef.is_struct()) { |
474 | CopyInline(fbb, fielddef, table, subobjectdef.minalign(), |
475 | subobjectdef.bytesize()); |
476 | break; |
477 | } |
478 | } |
479 | FLATBUFFERS_FALLTHROUGH(); // fall thru |
480 | case reflection::Union: |
481 | case reflection::String: |
482 | case reflection::Vector: |
483 | fbb.AddOffset(fielddef.offset(), Offset<void>(offsets[offset_idx++])); |
484 | break; |
485 | default: { // Scalars. |
486 | auto size = GetTypeSize(base_type); |
487 | CopyInline(fbb, fielddef, table, size, size); |
488 | break; |
489 | } |
490 | } |
491 | } |
492 | FLATBUFFERS_ASSERT(offset_idx == offsets.size()); |
493 | if (objectdef.is_struct()) { |
494 | fbb.ClearOffsets(); |
495 | return fbb.EndStruct(); |
496 | } else { |
497 | return fbb.EndTable(start); |
498 | } |
499 | } |
500 | |
501 | bool VerifyStruct(flatbuffers::Verifier &v, |
502 | const flatbuffers::Table &parent_table, |
503 | voffset_t field_offset, const reflection::Object &obj, |
504 | bool required) { |
505 | auto offset = parent_table.GetOptionalFieldOffset(field_offset); |
506 | if (required && !offset) { return false; } |
507 | |
508 | return !offset || |
509 | v.VerifyFieldStruct(reinterpret_cast<const uint8_t *>(&parent_table), |
510 | offset, obj.bytesize(), obj.minalign()); |
511 | } |
512 | |
513 | bool VerifyVectorOfStructs(flatbuffers::Verifier &v, |
514 | const flatbuffers::Table &parent_table, |
515 | voffset_t field_offset, |
516 | const reflection::Object &obj, bool required) { |
517 | auto p = parent_table.GetPointer<const uint8_t *>(field_offset); |
518 | if (required && !p) { return false; } |
519 | |
520 | return !p || v.VerifyVectorOrString(p, obj.bytesize()); |
521 | } |
522 | |
523 | // forward declare to resolve cyclic deps between VerifyObject and VerifyVector |
524 | bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema, |
525 | const reflection::Object &obj, |
526 | const flatbuffers::Table *table, bool required); |
527 | |
528 | bool VerifyUnion(flatbuffers::Verifier &v, const reflection::Schema &schema, |
529 | uint8_t utype, const uint8_t *elem, |
530 | const reflection::Field &union_field) { |
531 | if (!utype) return true; // Not present. |
532 | auto fb_enum = schema.enums()->Get(union_field.type()->index()); |
533 | if (utype >= fb_enum->values()->size()) return false; |
534 | auto elem_type = fb_enum->values()->Get(utype)->union_type(); |
535 | switch (elem_type->base_type()) { |
536 | case reflection::Obj: { |
537 | auto elem_obj = schema.objects()->Get(elem_type->index()); |
538 | if (elem_obj->is_struct()) { |
539 | return v.VerifyFromPointer(elem, elem_obj->bytesize()); |
540 | } else { |
541 | return VerifyObject(v, schema, *elem_obj, |
542 | reinterpret_cast<const flatbuffers::Table *>(elem), |
543 | true); |
544 | } |
545 | } |
546 | case reflection::String: |
547 | return v.VerifyString( |
548 | reinterpret_cast<const flatbuffers::String *>(elem)); |
549 | default: return false; |
550 | } |
551 | } |
552 | |
553 | bool VerifyVector(flatbuffers::Verifier &v, const reflection::Schema &schema, |
554 | const flatbuffers::Table &table, |
555 | const reflection::Field &vec_field) { |
556 | FLATBUFFERS_ASSERT(vec_field.type()->base_type() == reflection::Vector); |
557 | if (!table.VerifyField<uoffset_t>(v, vec_field.offset(), sizeof(uoffset_t))) |
558 | return false; |
559 | |
560 | switch (vec_field.type()->element()) { |
561 | case reflection::UType: |
562 | return v.VerifyVector(flatbuffers::GetFieldV<uint8_t>(table, vec_field)); |
563 | case reflection::Bool: |
564 | case reflection::Byte: |
565 | case reflection::UByte: |
566 | return v.VerifyVector(flatbuffers::GetFieldV<int8_t>(table, vec_field)); |
567 | case reflection::Short: |
568 | case reflection::UShort: |
569 | return v.VerifyVector(flatbuffers::GetFieldV<int16_t>(table, vec_field)); |
570 | case reflection::Int: |
571 | case reflection::UInt: |
572 | return v.VerifyVector(flatbuffers::GetFieldV<int32_t>(table, vec_field)); |
573 | case reflection::Long: |
574 | case reflection::ULong: |
575 | return v.VerifyVector(flatbuffers::GetFieldV<int64_t>(table, vec_field)); |
576 | case reflection::Float: |
577 | return v.VerifyVector(flatbuffers::GetFieldV<float>(table, vec_field)); |
578 | case reflection::Double: |
579 | return v.VerifyVector(flatbuffers::GetFieldV<double>(table, vec_field)); |
580 | case reflection::String: { |
581 | auto vec_string = |
582 | flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>( |
583 | table, vec_field); |
584 | if (v.VerifyVector(vec_string) && v.VerifyVectorOfStrings(vec_string)) { |
585 | return true; |
586 | } else { |
587 | return false; |
588 | } |
589 | } |
590 | case reflection::Obj: { |
591 | auto obj = schema.objects()->Get(vec_field.type()->index()); |
592 | if (obj->is_struct()) { |
593 | return VerifyVectorOfStructs(v, table, vec_field.offset(), *obj, |
594 | vec_field.required()); |
595 | } else { |
596 | auto vec = |
597 | flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::Table>>( |
598 | table, vec_field); |
599 | if (!v.VerifyVector(vec)) return false; |
600 | if (!vec) return true; |
601 | for (uoffset_t j = 0; j < vec->size(); j++) { |
602 | if (!VerifyObject(v, schema, *obj, vec->Get(j), true)) { |
603 | return false; |
604 | } |
605 | } |
606 | return true; |
607 | } |
608 | } |
609 | case reflection::Union: { |
610 | auto vec = flatbuffers::GetFieldV<flatbuffers::Offset<uint8_t>>( |
611 | table, vec_field); |
612 | if (!v.VerifyVector(vec)) return false; |
613 | if (!vec) return true; |
614 | auto type_vec = table.GetPointer<Vector<uint8_t> *>(vec_field.offset() - |
615 | sizeof(voffset_t)); |
616 | if (!v.VerifyVector(type_vec)) return false; |
617 | for (uoffset_t j = 0; j < vec->size(); j++) { |
618 | // get union type from the prev field |
619 | auto utype = type_vec->Get(j); |
620 | auto elem = vec->Get(j); |
621 | if (!VerifyUnion(v, schema, utype, elem, vec_field)) return false; |
622 | } |
623 | return true; |
624 | } |
625 | case reflection::Vector: |
626 | case reflection::None: |
627 | default: FLATBUFFERS_ASSERT(false); return false; |
628 | } |
629 | } |
630 | |
631 | bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema, |
632 | const reflection::Object &obj, |
633 | const flatbuffers::Table *table, bool required) { |
634 | if (!table) return !required; |
635 | if (!table->VerifyTableStart(v)) return false; |
636 | for (uoffset_t i = 0; i < obj.fields()->size(); i++) { |
637 | auto field_def = obj.fields()->Get(i); |
638 | switch (field_def->type()->base_type()) { |
639 | case reflection::None: FLATBUFFERS_ASSERT(false); break; |
640 | case reflection::UType: |
641 | if (!table->VerifyField<uint8_t>(v, field_def->offset(), |
642 | sizeof(uint8_t))) |
643 | return false; |
644 | break; |
645 | case reflection::Bool: |
646 | case reflection::Byte: |
647 | case reflection::UByte: |
648 | if (!table->VerifyField<int8_t>(v, field_def->offset(), sizeof(int8_t))) |
649 | return false; |
650 | break; |
651 | case reflection::Short: |
652 | case reflection::UShort: |
653 | if (!table->VerifyField<int16_t>(v, field_def->offset(), |
654 | sizeof(int16_t))) |
655 | return false; |
656 | break; |
657 | case reflection::Int: |
658 | case reflection::UInt: |
659 | if (!table->VerifyField<int32_t>(v, field_def->offset(), |
660 | sizeof(int32_t))) |
661 | return false; |
662 | break; |
663 | case reflection::Long: |
664 | case reflection::ULong: |
665 | if (!table->VerifyField<int64_t>(v, field_def->offset(), |
666 | sizeof(int64_t))) |
667 | return false; |
668 | break; |
669 | case reflection::Float: |
670 | if (!table->VerifyField<float>(v, field_def->offset(), sizeof(float))) |
671 | return false; |
672 | break; |
673 | case reflection::Double: |
674 | if (!table->VerifyField<double>(v, field_def->offset(), sizeof(double))) |
675 | return false; |
676 | break; |
677 | case reflection::String: |
678 | if (!table->VerifyField<uoffset_t>(v, field_def->offset(), |
679 | sizeof(uoffset_t)) || |
680 | !v.VerifyString(flatbuffers::GetFieldS(*table, *field_def))) { |
681 | return false; |
682 | } |
683 | break; |
684 | case reflection::Vector: |
685 | if (!VerifyVector(v, schema, *table, *field_def)) return false; |
686 | break; |
687 | case reflection::Obj: { |
688 | auto child_obj = schema.objects()->Get(field_def->type()->index()); |
689 | if (child_obj->is_struct()) { |
690 | if (!VerifyStruct(v, *table, field_def->offset(), *child_obj, |
691 | field_def->required())) { |
692 | return false; |
693 | } |
694 | } else { |
695 | if (!VerifyObject(v, schema, *child_obj, |
696 | flatbuffers::GetFieldT(*table, *field_def), |
697 | field_def->required())) { |
698 | return false; |
699 | } |
700 | } |
701 | break; |
702 | } |
703 | case reflection::Union: { |
704 | // get union type from the prev field |
705 | voffset_t utype_offset = field_def->offset() - sizeof(voffset_t); |
706 | auto utype = table->GetField<uint8_t>(utype_offset, 0); |
707 | auto uval = reinterpret_cast<const uint8_t *>( |
708 | flatbuffers::GetFieldT(*table, *field_def)); |
709 | if (!VerifyUnion(v, schema, utype, uval, *field_def)) { return false; } |
710 | break; |
711 | } |
712 | default: FLATBUFFERS_ASSERT(false); break; |
713 | } |
714 | } |
715 | |
716 | if (!v.EndTable()) return false; |
717 | |
718 | return true; |
719 | } |
720 | |
721 | bool Verify(const reflection::Schema &schema, const reflection::Object &root, |
722 | const uint8_t *buf, size_t length, uoffset_t max_depth /*= 64*/, |
723 | uoffset_t max_tables /*= 1000000*/) { |
724 | Verifier v(buf, length, max_depth, max_tables); |
725 | return VerifyObject(v, schema, root, flatbuffers::GetAnyRoot(buf), true); |
726 | } |
727 | |
728 | } // namespace flatbuffers |
729 | |