blob: e548f703ac6bf7929be03971e4cb3ccd5fb06ff4 [file] [log] [blame]
Austin Schuh09d7ffa2019-10-03 23:43:34 -07001#include "aos/flatbuffer_merge.h"
2
3#include <stdio.h>
4
5#include "aos/flatbuffer_utils.h"
6#include "flatbuffers/flatbuffers.h"
7#include "flatbuffers/minireflect.h"
8
9namespace aos {
10
11namespace {
12
13// Simple structure to hold both field_offsets and elements.
14struct OffsetAndFieldOffset {
15 OffsetAndFieldOffset(flatbuffers::voffset_t new_field_offset,
16 flatbuffers::Offset<flatbuffers::String> new_element)
17 : field_offset(new_field_offset), element(new_element) {}
Austin Schuhe93d8642019-10-13 15:27:07 -070018 OffsetAndFieldOffset(flatbuffers::voffset_t new_field_offset,
19 flatbuffers::Offset<flatbuffers::Table> new_element)
20 : field_offset(new_field_offset), element(new_element.o) {}
Austin Schuh09d7ffa2019-10-03 23:43:34 -070021
22 flatbuffers::voffset_t field_offset;
23 flatbuffers::Offset<flatbuffers::String> element;
24};
25
Austin Schuh09d7ffa2019-10-03 23:43:34 -070026// Merges a single element to a builder for the provided field.
27// One or both of t1 and t2 must be non-null. If one is null, this method
28// copies instead of merging.
29template <typename T>
30void MergeElement(flatbuffers::voffset_t field_offset,
31 const flatbuffers::Table *t1, const flatbuffers::Table *t2,
32 flatbuffers::FlatBufferBuilder *fbb) {
33 const uint8_t *val1 =
34 t1 != nullptr ? t1->GetAddressOf(field_offset) : nullptr;
35 const uint8_t *val2 =
36 t2 != nullptr ? t2->GetAddressOf(field_offset) : nullptr;
37 const bool t1_has = val1 != nullptr;
38 const bool t2_has = val2 != nullptr;
39
40 if (t2_has) {
41 fbb->AddElement<T>(field_offset, flatbuffers::ReadScalar<T>(val2), 0);
42 } else if (t1_has) {
43 fbb->AddElement<T>(field_offset, flatbuffers::ReadScalar<T>(val1), 0);
44 }
45}
46
47// Merges a single string to a builder for the provided field.
48// One or both of t1 and t2 must be non-null. If one is null, this method
49// copies instead of merging.
50void MergeString(flatbuffers::voffset_t field_offset,
51 const flatbuffers::Table *t1, const flatbuffers::Table *t2,
52 flatbuffers::FlatBufferBuilder *fbb,
53 ::std::vector<OffsetAndFieldOffset> *elements) {
54 const uint8_t *val1 =
55 t1 != nullptr ? t1->GetAddressOf(field_offset) : nullptr;
56 const uint8_t *val2 =
57 t2 != nullptr ? t2->GetAddressOf(field_offset) : nullptr;
58 const bool t1_has = val1 != nullptr;
59 const bool t2_has = val2 != nullptr;
60
61 if (t2_has) {
62 val2 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val2);
63 const flatbuffers::String *string2 =
64 reinterpret_cast<const flatbuffers::String *>(val2);
65 elements->emplace_back(field_offset,
66 fbb->CreateString(string2->data(), string2->size()));
67 } else if (t1_has) {
68 val1 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val1);
69 const flatbuffers::String *string1 =
70 reinterpret_cast<const flatbuffers::String *>(val1);
71 elements->emplace_back(field_offset,
72 fbb->CreateString(string1->data(), string1->size()));
73 }
74}
75
76// Merges an object to a builder for the provided field.
77// One or both of t1 and t2 must be non-null. If one is null, this method
78// copies instead of merging.
79void MergeTables(flatbuffers::voffset_t field_offset,
80 const flatbuffers::Table *t1, const flatbuffers::Table *t2,
81 const flatbuffers::TypeTable *sub_typetable,
82 flatbuffers::FlatBufferBuilder *fbb,
83 ::std::vector<OffsetAndFieldOffset> *elements) {
84 const uint8_t *val1 =
85 t1 != nullptr ? t1->GetAddressOf(field_offset) : nullptr;
86 const uint8_t *val2 =
87 t2 != nullptr ? t2->GetAddressOf(field_offset) : nullptr;
88 const bool t1_has = val1 != nullptr;
89 const bool t2_has = val2 != nullptr;
90 if (t1_has || t2_has) {
91 if (val1 != nullptr) {
92 val1 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val1);
93 }
94 if (val2 != nullptr) {
95 val2 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val2);
96 }
97
98 const flatbuffers::Table *sub_t1 =
99 reinterpret_cast<const flatbuffers::Table *>(val1);
100 const flatbuffers::Table *sub_t2 =
101 reinterpret_cast<const flatbuffers::Table *>(val2);
102
103 elements->emplace_back(field_offset,
104 MergeFlatBuffers(sub_typetable, sub_t1, sub_t2, fbb));
105 }
106}
107
108// Adds a vector of strings to the elements vector so it can be added later.
109// One or both of t1 and t2 must be non-null. If one is null, this method
110// copies instead of merging.
111void AddVectorOfStrings(flatbuffers::ElementaryType elementary_type,
112 flatbuffers::voffset_t field_offset,
113 const flatbuffers::Table *t1,
114 const flatbuffers::Table *t2,
115 flatbuffers::FlatBufferBuilder *fbb,
116 ::std::vector<OffsetAndFieldOffset> *elements) {
117 const uint8_t *val1 =
118 t1 != nullptr ? t1->GetAddressOf(field_offset) : nullptr;
119 const uint8_t *val2 =
120 t2 != nullptr ? t2->GetAddressOf(field_offset) : nullptr;
121 const bool t1_has = val1 != nullptr;
122 const bool t2_has = val2 != nullptr;
123
124 // Compute end size of the vector.
125 size_t size = 0;
126 if (t1_has) {
127 val1 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val1);
128 auto vec1 = reinterpret_cast<
129 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(
130 val1);
131 size += vec1->size();
132 }
133 if (t2_has) {
134 val2 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val2);
135 auto vec2 = reinterpret_cast<
136 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(
137 val2);
138 size += vec2->size();
139 }
140
141 // Only add the vector if there is something to add.
142 if (t1_has || t2_has) {
143 const size_t inline_size =
144 flatbuffers::InlineSize(elementary_type, nullptr);
145
146 ::std::vector<flatbuffers::Offset<flatbuffers::String>> string_elements;
147
148 // Pack the contents in in reverse order.
149 if (t2_has) {
150 auto vec2 = reinterpret_cast<const flatbuffers::Vector<
151 flatbuffers::Offset<flatbuffers::String>> *>(val2);
152 for (auto i = vec2->rbegin(); i != vec2->rend(); ++i) {
153 const flatbuffers::String *s = *i;
154 string_elements.emplace_back(fbb->CreateString(s->data(), s->size()));
155 }
156 }
157 if (t1_has) {
158 auto vec1 = reinterpret_cast<const flatbuffers::Vector<
159 flatbuffers::Offset<flatbuffers::String>> *>(val1);
160 for (auto i = vec1->rbegin(); i != vec1->rend(); ++i) {
161 const flatbuffers::String *s = *i;
162 string_elements.emplace_back(fbb->CreateString(s->data(), s->size()));
163 }
164 }
165
166 // Start the vector.
167 fbb->StartVector(size, inline_size);
168
169 for (const flatbuffers::Offset<flatbuffers::String> &element :
170 string_elements) {
171 fbb->PushElement(element);
172 }
173
174 // And then finish the vector and put it in the list of offsets to add to
175 // the message when it finishes.
176 elements->emplace_back(
177 field_offset,
178 flatbuffers::Offset<flatbuffers::String>(fbb->EndVector(size)));
179 }
180}
181
182// Adds a vector of values to the elements vector so it can be added later.
183// One or both of t1 and t2 must be non-null. If one is null, this method
184// copies instead of merging.
185template <typename T>
186void AddVector(flatbuffers::ElementaryType elementary_type,
187 flatbuffers::voffset_t field_offset,
188 const flatbuffers::Table *t1, const flatbuffers::Table *t2,
189 flatbuffers::FlatBufferBuilder *fbb,
190 ::std::vector<OffsetAndFieldOffset> *elements) {
191 const uint8_t *val1 =
192 t1 != nullptr ? t1->GetAddressOf(field_offset) : nullptr;
193 const uint8_t *val2 =
194 t2 != nullptr ? t2->GetAddressOf(field_offset) : nullptr;
195 const bool t1_has = val1 != nullptr;
196 const bool t2_has = val2 != nullptr;
197
198 // Compute end size of the vector.
199 size_t size = 0;
200 if (t1_has) {
201 val1 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val1);
202 auto vec1 = reinterpret_cast<const flatbuffers::Vector<T> *>(val1);
203 size += vec1->size();
204 }
205 if (t2_has) {
206 val2 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val2);
207 auto vec2 = reinterpret_cast<const flatbuffers::Vector<T> *>(val2);
208 size += vec2->size();
209 }
210
211 // Only add the vector if there is something to add.
212 if (t1_has || t2_has) {
213 const size_t inline_size =
214 flatbuffers::InlineSize(elementary_type, nullptr);
215
216 // Start the vector.
217 fbb->StartVector(size, inline_size);
218
219 // Pack the contents in in reverse order.
220 if (t2_has) {
221 auto vec2 = reinterpret_cast<const flatbuffers::Vector<T> *>(val2);
222 // Iterate backwards.
223 for (auto i = vec2->rbegin(); i != vec2->rend(); ++i) {
224 fbb->PushElement<T>(*i);
225 }
226 }
227 if (t1_has) {
228 auto vec1 = reinterpret_cast<const flatbuffers::Vector<T> *>(val1);
229 // Iterate backwards.
230 for (auto i = vec1->rbegin(); i != vec1->rend(); ++i) {
231 fbb->PushElement<T>(*i);
232 }
233 }
234 // And then finish the vector and put it in the list of offsets to add to
235 // the message when it finishes.
236 elements->emplace_back(
237 field_offset,
238 flatbuffers::Offset<flatbuffers::String>(fbb->EndVector(size)));
239 }
240}
241
242void AddVectorOfObjects(flatbuffers::FlatBufferBuilder *fbb,
243 ::std::vector<OffsetAndFieldOffset> *elements,
244 flatbuffers::ElementaryType elementary_type,
245 const flatbuffers::TypeTable *sub_typetable,
246 flatbuffers::voffset_t field_offset,
247 const flatbuffers::Table *t1,
248 const flatbuffers::Table *t2) {
249 const uint8_t *val1 =
250 t1 != nullptr ? t1->GetAddressOf(field_offset) : nullptr;
251 const uint8_t *val2 =
252 t2 != nullptr ? t2->GetAddressOf(field_offset) : nullptr;
253 const bool t1_has = val1 != nullptr;
254 const bool t2_has = val2 != nullptr;
255
256 // Compute end size of the vector.
257 size_t size = 0;
258 if (t1_has) {
259 val1 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val1);
260 auto vec1 = reinterpret_cast<
261 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::Table>> *>(
262 val1);
263 size += vec1->size();
264 }
265 if (t2_has) {
266 val2 += flatbuffers::ReadScalar<flatbuffers::uoffset_t>(val2);
267 auto vec2 = reinterpret_cast<
268 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::Table>> *>(
269 val2);
270 size += vec2->size();
271 }
272
273 // Only add the vector if there is something to add.
274 if (t1_has || t2_has) {
275 const size_t inline_size =
276 flatbuffers::InlineSize(elementary_type, sub_typetable);
277
278 ::std::vector<flatbuffers::Offset<flatbuffers::Table>> object_elements;
279
280 // Pack the contents in in reverse order.
281 if (t2_has) {
282 auto vec2 = reinterpret_cast<
283 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::Table>> *>(
284 val2);
285 for (auto i = vec2->rbegin(); i != vec2->rend(); ++i) {
286 const flatbuffers::Table *t = *i;
287
Austin Schuhe93d8642019-10-13 15:27:07 -0700288 flatbuffers::Offset<flatbuffers::Table> end =
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700289 MergeFlatBuffers(sub_typetable, t, nullptr, fbb);
290
Austin Schuhe93d8642019-10-13 15:27:07 -0700291 object_elements.emplace_back(end);
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700292 }
293 }
294 if (t1_has) {
295 auto vec1 = reinterpret_cast<
296 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::Table>> *>(
297 val1);
298 for (auto i = vec1->rbegin(); i != vec1->rend(); ++i) {
299 const flatbuffers::Table *t = *i;
300
Austin Schuhe93d8642019-10-13 15:27:07 -0700301 flatbuffers::Offset<flatbuffers::Table> end =
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700302 MergeFlatBuffers(sub_typetable, t, nullptr, fbb);
303
Austin Schuhe93d8642019-10-13 15:27:07 -0700304 object_elements.emplace_back(end);
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700305 }
306 }
307
308 // Start the vector.
309 fbb->StartVector(size, inline_size);
310
311 for (const flatbuffers::Offset<flatbuffers::Table> &element :
312 object_elements) {
313 fbb->PushElement(element);
314 }
315
316 // And then finish the vector and put it in the list of offsets to add to
317 // the message when it finishes.
318 elements->emplace_back(
319 field_offset,
320 flatbuffers::Offset<flatbuffers::String>(fbb->EndVector(size)));
321 }
322}
323
Austin Schuhe93d8642019-10-13 15:27:07 -0700324} // namespace
325
326flatbuffers::Offset<flatbuffers::Table> MergeFlatBuffers(
327 const flatbuffers::TypeTable *typetable, const flatbuffers::Table *t1,
328 const flatbuffers::Table *t2, flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700329 ::std::vector<OffsetAndFieldOffset> elements;
330
331 // We need to do this in 2 passes
332 // The first pass builds up all the sub-objects which are encoded in the
333 // message as offsets.
334 // The second pass builds up the actual table by adding all the values to the
335 // messages, and encoding the offsets in the table.
336 for (size_t field_index = 0; field_index < typetable->num_elems;
337 ++field_index) {
338 const flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
339 const flatbuffers::ElementaryType elementary_type =
340 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
341
342 const flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
343 static_cast<flatbuffers::voffset_t>(field_index));
344
345 switch (elementary_type) {
346 case flatbuffers::ElementaryType::ET_UTYPE:
347 if (!type_code.is_vector) continue;
348 printf("ET_UTYPE, %s\n", typetable->names[field_index]);
349 break;
350 case flatbuffers::ElementaryType::ET_BOOL:
351 if (!type_code.is_vector) continue;
352 AddVector<uint8_t>(elementary_type, field_offset, t1, t2, fbb,
353 &elements);
354 break;
355 case flatbuffers::ElementaryType::ET_CHAR:
356 if (!type_code.is_vector) continue;
357 AddVector<int8_t>(elementary_type, field_offset, t1, t2, fbb,
358 &elements);
359 break;
360 case flatbuffers::ElementaryType::ET_UCHAR:
361 if (!type_code.is_vector) continue;
362 AddVector<uint8_t>(elementary_type, field_offset, t1, t2, fbb,
363 &elements);
364 break;
365 case flatbuffers::ElementaryType::ET_SHORT:
366 if (!type_code.is_vector) continue;
367 AddVector<int16_t>(elementary_type, field_offset, t1, t2, fbb,
368 &elements);
369 break;
370 case flatbuffers::ElementaryType::ET_USHORT:
371 if (!type_code.is_vector) continue;
372 AddVector<uint16_t>(elementary_type, field_offset, t1, t2, fbb,
373 &elements);
374 break;
375 case flatbuffers::ElementaryType::ET_INT:
376 if (!type_code.is_vector) continue;
377 AddVector<int32_t>(elementary_type, field_offset, t1, t2, fbb,
378 &elements);
379 break;
380 case flatbuffers::ElementaryType::ET_UINT:
381 if (!type_code.is_vector) continue;
382 AddVector<uint32_t>(elementary_type, field_offset, t1, t2, fbb,
383 &elements);
384 break;
385 case flatbuffers::ElementaryType::ET_LONG:
386 if (!type_code.is_vector) continue;
387 AddVector<int64_t>(elementary_type, field_offset, t1, t2, fbb,
388 &elements);
389 break;
390 case flatbuffers::ElementaryType::ET_ULONG:
391 if (!type_code.is_vector) continue;
392 AddVector<uint64_t>(elementary_type, field_offset, t1, t2, fbb,
393 &elements);
394 break;
395 case flatbuffers::ElementaryType::ET_FLOAT:
396 if (!type_code.is_vector) continue;
397 AddVector<float>(elementary_type, field_offset, t1, t2, fbb, &elements);
398 break;
399 case flatbuffers::ElementaryType::ET_DOUBLE:
400 if (!type_code.is_vector) continue;
401 AddVector<double>(elementary_type, field_offset, t1, t2, fbb,
402 &elements);
403 break;
404 case flatbuffers::ElementaryType::ET_STRING:
405 if (!type_code.is_vector) {
406 MergeString(field_offset, t1, t2, fbb, &elements);
407 } else {
408 AddVectorOfStrings(elementary_type, field_offset, t1, t2, fbb,
409 &elements);
410 }
411 break;
412 case flatbuffers::ElementaryType::ET_SEQUENCE: {
413 const flatbuffers::TypeTable *sub_typetable =
414 typetable->type_refs[type_code.sequence_ref]();
415 if (!type_code.is_vector) {
416 MergeTables(field_offset, t1, t2, sub_typetable, fbb, &elements);
417 } else {
418 const flatbuffers::TypeTable *sub_typetable =
419 typetable->type_refs[type_code.sequence_ref]();
420
421 AddVectorOfObjects(fbb, &elements, elementary_type, sub_typetable,
422 field_offset, t1, t2);
423 }
424 } break;
425 }
426 }
427
428 const flatbuffers::uoffset_t start = fbb->StartTable();
429
430 // We want to do this the same way as the json library. Rip through the
431 // fields and generate a list of things to add. Then add them.
432 // Also need recursion for subtypes.
433 for (size_t field_index = 0; field_index < typetable->num_elems;
434 ++field_index) {
435 const flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
436 if (type_code.is_vector) {
437 continue;
438 }
439 const flatbuffers::ElementaryType elementary_type =
440 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
441
442 const flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
443 static_cast<flatbuffers::voffset_t>(field_index));
444
445 switch (elementary_type) {
446 case flatbuffers::ElementaryType::ET_UTYPE:
447 // TODO(austin): Need to see one and try it.
448 printf("ET_UTYPE, %s\n", typetable->names[field_index]);
449 break;
450 case flatbuffers::ElementaryType::ET_BOOL: {
451 MergeElement<uint8_t>(field_offset, t1, t2, fbb);
452 } break;
453 case flatbuffers::ElementaryType::ET_CHAR:
454 MergeElement<int8_t>(field_offset, t1, t2, fbb);
455 break;
456 case flatbuffers::ElementaryType::ET_UCHAR:
457 MergeElement<uint8_t>(field_offset, t1, t2, fbb);
458 break;
459 case flatbuffers::ElementaryType::ET_SHORT:
460 MergeElement<int16_t>(field_offset, t1, t2, fbb);
461 break;
462 case flatbuffers::ElementaryType::ET_USHORT:
463 MergeElement<uint16_t>(field_offset, t1, t2, fbb);
464 break;
465 case flatbuffers::ElementaryType::ET_INT:
466 MergeElement<int32_t>(field_offset, t1, t2, fbb);
467 break;
468 case flatbuffers::ElementaryType::ET_UINT:
469 MergeElement<uint32_t>(field_offset, t1, t2, fbb);
470 break;
471 case flatbuffers::ElementaryType::ET_LONG:
472 MergeElement<int64_t>(field_offset, t1, t2, fbb);
473 break;
474 case flatbuffers::ElementaryType::ET_ULONG:
475 MergeElement<uint64_t>(field_offset, t1, t2, fbb);
476 break;
477 case flatbuffers::ElementaryType::ET_FLOAT:
478 MergeElement<float>(field_offset, t1, t2, fbb);
479 break;
480 case flatbuffers::ElementaryType::ET_DOUBLE:
481 MergeElement<double>(field_offset, t1, t2, fbb);
482 break;
483 case flatbuffers::ElementaryType::ET_STRING:
484 case flatbuffers::ElementaryType::ET_SEQUENCE:
485 // Already handled above since this is an uoffset.
486 break;
487 }
488 }
489
490 // And there is no need to check for duplicates since we are creating this
491 // list very carefully from the type table.
492 for (const OffsetAndFieldOffset &element : elements) {
493 fbb->AddOffset(element.field_offset, element.element);
494 }
495
496 return fbb->EndTable(start);
497}
498
Austin Schuhe93d8642019-10-13 15:27:07 -0700499flatbuffers::Offset<flatbuffers::Table> MergeFlatBuffers(
500 const flatbuffers::TypeTable *typetable, const uint8_t *data1,
501 const uint8_t *data2, flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700502 // Grab the 2 tables.
503 const flatbuffers::Table *t1 =
504 data1 != nullptr ? flatbuffers::GetRoot<flatbuffers::Table>(data1)
505 : nullptr;
506 const flatbuffers::Table *t2 =
507 data2 != nullptr ? flatbuffers::GetRoot<flatbuffers::Table>(data2)
508 : nullptr;
509
510 // And then do the actual build. This doesn't contain finish so we can nest
511 // them nicely.
Austin Schuhe93d8642019-10-13 15:27:07 -0700512 return flatbuffers::Offset<flatbuffers::Table>(
513 MergeFlatBuffers(typetable, t1, t2, fbb));
514}
515
516flatbuffers::DetachedBuffer MergeFlatBuffers(
517 const flatbuffers::TypeTable *typetable, const uint8_t *data1,
518 const uint8_t *data2) {
519 // Build up a builder.
520 flatbuffers::FlatBufferBuilder fbb;
Austin Schuhd7b15da2020-02-17 15:06:11 -0800521 fbb.ForceDefaults(true);
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700522
523 // Finish up the buffer and return it.
Austin Schuhe93d8642019-10-13 15:27:07 -0700524 fbb.Finish(MergeFlatBuffers(typetable, data1, data2, &fbb));
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700525
Austin Schuhe93d8642019-10-13 15:27:07 -0700526 return fbb.Release();
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700527}
528
Austin Schuh30d7db92020-01-26 16:45:47 -0800529bool CompareFlatBuffer(const flatbuffers::TypeTable *typetable,
530 const flatbuffers::Table *t1,
531 const flatbuffers::Table *t2) {
532 // Copying flatbuffers is deterministic for the same typetable. So, copy both
533 // to guarantee that they are sorted the same, then check that the memory
534 // matches.
535 //
536 // There has to be a better way to do this, but the efficiency hit of this
537 // implementation is fine for the usages that we have now. We are better off
538 // abstracting this into a library call where we can fix it later easily.
539 flatbuffers::FlatBufferBuilder fbb1;
Austin Schuhd7b15da2020-02-17 15:06:11 -0800540 fbb1.ForceDefaults(true);
Austin Schuh30d7db92020-01-26 16:45:47 -0800541 fbb1.Finish(MergeFlatBuffers(typetable, t1, nullptr, &fbb1));
542 flatbuffers::FlatBufferBuilder fbb2;
Austin Schuhd7b15da2020-02-17 15:06:11 -0800543 fbb2.ForceDefaults(true);
Austin Schuh30d7db92020-01-26 16:45:47 -0800544 fbb2.Finish(MergeFlatBuffers(typetable, t2, nullptr, &fbb2));
545
546 if (fbb1.GetSize() != fbb2.GetSize()) return false;
547
548 return memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(),
549 fbb1.GetSize()) == 0;
550}
551
Austin Schuh09d7ffa2019-10-03 23:43:34 -0700552} // namespace aos