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