blob: 65087869857a72f4e7e45315ce2a820dd74cee23 [file] [log] [blame]
Austin Schuh3e95e5d2019-09-20 00:08:54 -07001#include "aos/json_to_flatbuffer.h"
2
3#include <cstddef>
4#include "stdio.h"
5
James Kuszmaul3ae42262019-11-08 12:33:41 -08006#include <string_view>
7
Austin Schuh43c6a352019-09-30 22:22:10 -07008#include "aos/flatbuffer_utils.h"
Austin Schuhd7e252d2019-10-06 13:51:02 -07009#include "aos/json_tokenizer.h"
Austin Schuh3e95e5d2019-09-20 00:08:54 -070010#include "flatbuffers/flatbuffers.h"
11#include "flatbuffers/minireflect.h"
Austin Schuhe93d8642019-10-13 15:27:07 -070012#include "glog/logging.h"
Austin Schuh3e95e5d2019-09-20 00:08:54 -070013
14// TODO(austin): Can we just do an Offset<void> ? It doesn't matter, so maybe
15// just say that.
16//
17// TODO(austin): I've yet to see how to create an ET_UTYPE, so I don't know what
18// one is and how to test it. So everything rejects it.
19
20namespace aos {
21
22// Finds the field index in the table given the name.
23int FieldIndex(const flatbuffers::TypeTable *typetable,
24 const char *field_name) {
25 CHECK(typetable->values == nullptr);
26 for (size_t i = 0; i < typetable->num_elems; ++i) {
27 if (strcmp(field_name, typetable->names[i]) == 0) {
28 return i;
29 }
30 }
31 return -1;
32}
33
34namespace {
35
36// Class to hold one of the 3 json types for an array.
37struct Element {
38 // The type.
39 enum class ElementType { INT, DOUBLE, OFFSET };
40
41 // Constructs an Element holding an integer.
42 Element(int64_t new_int_element)
43 : int_element(new_int_element), type(ElementType::INT) {}
44 // Constructs an Element holding an double.
45 Element(double new_double_element)
46 : double_element(new_double_element), type(ElementType::DOUBLE) {}
47 // Constructs an Element holding an Offset.
48 Element(flatbuffers::Offset<flatbuffers::String> new_offset_element)
49 : offset_element(new_offset_element), type(ElementType::OFFSET) {}
50
51 // Union for the various datatypes.
52 union {
53 int64_t int_element;
54 double double_element;
55 flatbuffers::Offset<flatbuffers::String> offset_element;
56 };
57
58 // And an enum signaling which one is in use.
59 ElementType type;
60};
61
62// Structure to represent a field element.
63struct FieldElement {
64 FieldElement(int new_field_index, int64_t int_element)
65 : element(int_element), field_index(new_field_index) {}
66 FieldElement(int new_field_index, double double_element)
67 : element(double_element), field_index(new_field_index) {}
68 FieldElement(int new_field_index,
69 flatbuffers::Offset<flatbuffers::String> offset_element)
70 : element(offset_element), field_index(new_field_index) {}
71
72 // Data to write.
73 Element element;
74 // Field index. The type table which this index is for is stored outside this
75 // object.
76 int field_index;
77};
78
Austin Schuh43c6a352019-09-30 22:22:10 -070079// Adds a single element. This assumes that vectors have been dealt with
80// already. Returns true on success.
81bool AddSingleElement(const flatbuffers::TypeTable *typetable,
82 const FieldElement &field_element,
83 ::std::vector<bool> *fields_in_use,
84 flatbuffers::FlatBufferBuilder *fbb);
85bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
86 int64_t int_value, flatbuffers::FlatBufferBuilder *fbb);
87bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
88 double double_value, flatbuffers::FlatBufferBuilder *fbb);
89bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
90 flatbuffers::Offset<flatbuffers::String> offset_element,
91 flatbuffers::FlatBufferBuilder *fbb);
92
93
94// Writes an array of FieldElement (with the definition in the type
95// table) to the builder. Returns the offset of the table.
96flatbuffers::uoffset_t WriteTable(const flatbuffers::TypeTable *typetable,
97 const ::std::vector<FieldElement> &elements,
98 flatbuffers::FlatBufferBuilder *fbb) {
99 // End of a nested struct! Add it.
100 const flatbuffers::uoffset_t start = fbb->StartTable();
101
102 ::std::vector<bool> fields_in_use(typetable->num_elems, false);
103
104 for (const FieldElement &field_element : elements) {
105 AddSingleElement(typetable, field_element, &fields_in_use, fbb);
106 }
107
108 return fbb->EndTable(start);
109}
110
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700111// Class to parse JSON into a flatbuffer.
112//
113// The basic strategy is that we need to do everything backwards. So we need to
114// build up what we need to do fully in memory, then do it.
115//
116// The driver for this is that strings need to be fully created before the
117// tables that use them. Same for sub messages. But, we only know we have them
Austin Schuh43c6a352019-09-30 22:22:10 -0700118// all when the structure ends. So, store each sub message in a
119// FieldElement and put them in the table at the end when we finish up
120// each message. Same goes for vectors.
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700121class JsonParser {
122 public:
123 JsonParser() { fbb_.ForceDefaults(1); }
124 ~JsonParser() {}
125
126 // Parses the json into a flatbuffer. Returns either an empty vector on
127 // error, or a vector with the flatbuffer data in it.
James Kuszmaul3ae42262019-11-08 12:33:41 -0800128 flatbuffers::DetachedBuffer Parse(const std::string_view data,
Austin Schuhe93d8642019-10-13 15:27:07 -0700129 const flatbuffers::TypeTable *typetable) {
Austin Schuh43c6a352019-09-30 22:22:10 -0700130 flatbuffers::uoffset_t end = 0;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700131 bool result = DoParse(typetable, data, &end);
132
133 if (result) {
134 // On success, finish the table and build the vector.
135 auto o = flatbuffers::Offset<flatbuffers::Table>(end);
136 fbb_.Finish(o);
137
Austin Schuhe93d8642019-10-13 15:27:07 -0700138 return fbb_.Release();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700139 } else {
140 // Otherwise return an empty vector.
Austin Schuhe93d8642019-10-13 15:27:07 -0700141 return flatbuffers::DetachedBuffer();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700142 }
143 }
144
145 private:
146 // Setters and getters for in_vector (at the current level of the stack)
147 bool in_vector() const { return stack_.back().in_vector; }
148 void set_in_vector(bool in_vector) { stack_.back().in_vector = in_vector; }
149
150 // Parses the flatbuffer. This is a second method so we can do easier
151 // cleanup at the top level. Returns true on success.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700152 bool DoParse(const flatbuffers::TypeTable *typetable,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800153 const std::string_view data,
154 flatbuffers::uoffset_t *table_end);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700155
156 // Adds *_value for the provided field. If we are in a vector, queues the
Alex Perrycb7da4b2019-08-28 19:35:56 -0700157 // data up in vector_elements. Returns true on success.
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700158 bool AddElement(int field_index, int64_t int_value);
159 bool AddElement(int field_index, double double_value);
160 bool AddElement(int field_index, const ::std::string &data);
161
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700162 // Finishes a vector for the provided field index. Returns true on success.
163 bool FinishVector(int field_index);
164
165 // Pushes an element as part of a vector. Returns true on success.
166 bool PushElement(flatbuffers::ElementaryType elementary_type,
167 int64_t int_value);
168 bool PushElement(flatbuffers::ElementaryType elementary_type,
169 double double_value);
170 bool PushElement(flatbuffers::ElementaryType elementary_type,
171 flatbuffers::Offset<flatbuffers::String> offset_value);
172
173 flatbuffers::FlatBufferBuilder fbb_;
174
175 // This holds the state information that is needed as you recurse into
176 // nested structures.
177 struct FlatBufferContext {
178 // Type of the current type.
179 const flatbuffers::TypeTable *typetable;
180 // If true, we are parsing a vector.
181 bool in_vector;
182 // The field index of the current field.
183 int field_index;
184 // Name of the current field.
185 ::std::string field_name;
186
187 // Field elements that need to be inserted.
188 ::std::vector<FieldElement> elements;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700189
190 // For scalar types (not strings, and not nested tables), the vector ends
191 // up being implemented as a start and end, and a block of data. So we
192 // can't just push offsets in as we go. We either need to reproduce the
193 // logic inside flatbuffers, or build up vectors of the data. Vectors will
194 // be a bit of extra stack space, but whatever.
195 //
196 // Strings and nested structures are vectors of offsets.
197 // into the vector. Once you get to the end, you build up a vector and
198 // push that into the field.
199 ::std::vector<Element> vector_elements;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700200 };
201 ::std::vector<FlatBufferContext> stack_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700202};
203
204bool JsonParser::DoParse(const flatbuffers::TypeTable *typetable,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800205 const std::string_view data,
Austin Schuhd339a9b2019-10-05 21:33:32 -0700206 flatbuffers::uoffset_t *table_end) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700207 ::std::vector<const flatbuffers::TypeTable *> stack;
208
209 Tokenizer t(data);
210
211 // Main loop. Run until we get an end.
212 while (true) {
213 Tokenizer::TokenType token = t.Next();
214
215 switch (token) {
216 case Tokenizer::TokenType::kEnd:
217 if (stack_.size() != 0) {
Austin Schuh217a9782019-12-21 23:02:50 -0800218 fprintf(stderr, "Failed to unwind stack all the way\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700219 return false;
220 } else {
221 return true;
222 }
223 break;
224 case Tokenizer::TokenType::kError:
225 return false;
226 break;
227
228 case Tokenizer::TokenType::kStartObject: // {
229 if (stack_.size() == 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700230 stack_.push_back({typetable, false, -1, "", {}, {}});
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700231 } else {
232 int field_index = stack_.back().field_index;
233
234 const flatbuffers::TypeCode &type_code =
235 stack_.back().typetable->type_codes[field_index];
236
237 if (type_code.base_type != flatbuffers::ET_SEQUENCE) {
Austin Schuh217a9782019-12-21 23:02:50 -0800238 fprintf(stderr, "Field '%s' is not a sequence\n",
239 stack_.back().field_name.c_str());
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700240 return false;
241 }
242
243 flatbuffers::TypeFunction type_function =
244 stack_.back().typetable->type_refs[type_code.sequence_ref];
245
Alex Perrycb7da4b2019-08-28 19:35:56 -0700246 stack_.push_back({type_function(), false, -1, "", {}, {}});
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700247 }
248 break;
249 case Tokenizer::TokenType::kEndObject: // }
250 if (stack_.size() == 0) {
251 // Somehow we popped more than we pushed. Error.
Austin Schuh217a9782019-12-21 23:02:50 -0800252 fprintf(stderr, "Empty stack\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700253 return false;
254 } else {
255 // End of a nested struct! Add it.
Austin Schuh43c6a352019-09-30 22:22:10 -0700256 const flatbuffers::uoffset_t end = WriteTable(
257 stack_.back().typetable, stack_.back().elements, &fbb_);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700258
259 // We now want to talk about the parent structure. Pop the child.
260 stack_.pop_back();
261
262 if (stack_.size() == 0) {
263 // Instead of queueing it up in the stack, return it through the
264 // passed in variable.
265 *table_end = end;
266 } else {
267 // And now we can add it.
268 const int field_index = stack_.back().field_index;
269
270 // Do the right thing if we are in a vector.
271 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700272 stack_.back().vector_elements.emplace_back(
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700273 flatbuffers::Offset<flatbuffers::String>(end));
274 } else {
275 stack_.back().elements.emplace_back(
276 field_index, flatbuffers::Offset<flatbuffers::String>(end));
277 }
278 }
279 }
280 break;
281
282 case Tokenizer::TokenType::kStartArray: // [
283 if (stack_.size() == 0) {
284 // We don't support an array of structs at the root level.
285 return false;
286 }
287 // Sanity check that we aren't trying to make a vector of vectors.
288 if (in_vector()) {
289 return false;
290 }
291 set_in_vector(true);
292
293 break;
294 case Tokenizer::TokenType::kEndArray: { // ]
295 if (!in_vector()) {
296 return false;
297 }
298
299 const int field_index = stack_.back().field_index;
300
301 if (!FinishVector(field_index)) return false;
302
303 set_in_vector(false);
304 } break;
305
306 case Tokenizer::TokenType::kTrueValue: // true
307 case Tokenizer::TokenType::kFalseValue: // false
308 case Tokenizer::TokenType::kNumberValue: {
309 bool is_int = true;
310 double double_value;
311 long long int_value;
312 if (token == Tokenizer::TokenType::kTrueValue) {
313 int_value = 1;
314 } else if (token == Tokenizer::TokenType::kFalseValue) {
315 int_value = 0;
316 } else if (!t.FieldAsInt(&int_value)) {
317 if (t.FieldAsDouble(&double_value)) {
318 is_int = false;
319 } else {
320 fprintf(stderr, "Got a invalid number '%s'\n",
321 t.field_value().c_str());
322 return false;
323 }
324 }
325
326 const int field_index = stack_.back().field_index;
327
328 if (is_int) {
329 // No need to get too stressed about bool vs int. Convert them all.
330 int64_t val = int_value;
331 if (!AddElement(field_index, val)) return false;
332 } else {
333 if (!AddElement(field_index, double_value)) return false;
334 }
335 } break;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700336 case Tokenizer::TokenType::kStringValue: // string value
337 {
338 const int field_index = stack_.back().field_index;
339
340 if (!AddElement(field_index, t.field_value())) return false;
341 } break;
342 case Tokenizer::TokenType::kField: // field name
343 {
344 stack_.back().field_name = t.field_name();
345 stack_.back().field_index = FieldIndex(
346 stack_.back().typetable, stack_.back().field_name.c_str());
347
348 if (stack_.back().field_index == -1) {
Austin Schuh217a9782019-12-21 23:02:50 -0800349 fprintf(stderr, "Invalid field name '%s'\n",
350 stack_.back().field_name.c_str());
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700351 return false;
352 }
353 } break;
354 }
355 }
356 return false;
357}
358
359bool JsonParser::AddElement(int field_index, int64_t int_value) {
360 flatbuffers::TypeCode type_code =
361 stack_.back().typetable->type_codes[field_index];
362
363 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800364 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700365 return false;
366 }
367
368 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700369 stack_.back().vector_elements.emplace_back(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700370 } else {
371 stack_.back().elements.emplace_back(field_index, int_value);
372 }
373 return true;
374}
375
376bool JsonParser::AddElement(int field_index, double double_value) {
377 flatbuffers::TypeCode type_code =
378 stack_.back().typetable->type_codes[field_index];
379
380 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800381 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700382 return false;
383 }
384
385 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700386 stack_.back().vector_elements.emplace_back(double_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700387 } else {
388 stack_.back().elements.emplace_back(field_index, double_value);
389 }
390 return true;
391}
392
393bool JsonParser::AddElement(int field_index, const ::std::string &data) {
394 flatbuffers::TypeCode type_code =
395 stack_.back().typetable->type_codes[field_index];
396
397 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800398 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700399 return false;
400 }
401
Alex Perrycb7da4b2019-08-28 19:35:56 -0700402 const flatbuffers::ElementaryType elementary_type =
403 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
404 switch (elementary_type) {
405 case flatbuffers::ET_CHAR:
406 case flatbuffers::ET_UCHAR:
407 case flatbuffers::ET_SHORT:
408 case flatbuffers::ET_USHORT:
409 case flatbuffers::ET_INT:
410 case flatbuffers::ET_UINT:
411 case flatbuffers::ET_LONG:
412 case flatbuffers::ET_ULONG:
413 if (type_code.sequence_ref != -1) {
414 // We have an enum.
415 const flatbuffers::TypeTable *type_table = stack_.back().typetable;
416 flatbuffers::TypeFunction type_function =
417 type_table->type_refs[type_code.sequence_ref];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700418
Alex Perrycb7da4b2019-08-28 19:35:56 -0700419 const flatbuffers::TypeTable *enum_type_table = type_function();
420
421 CHECK_EQ(enum_type_table->st, flatbuffers::ST_ENUM);
422
423 int64_t int_value = 0;
424 bool found = false;
425 for (size_t i = 0; i < enum_type_table->num_elems; ++i) {
426 if (data == enum_type_table->names[i]) {
427 int_value = i;
428 found = true;
429 break;
430 }
431 }
432
433 if (!found) {
Austin Schuh217a9782019-12-21 23:02:50 -0800434 fprintf(stderr, "Enum value '%s' not found for field '%s'\n",
435 data.c_str(), type_table->names[field_index]);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700436 return false;
437 }
438
439 if (in_vector()) {
440 stack_.back().vector_elements.emplace_back(int_value);
441 } else {
442 stack_.back().elements.emplace_back(field_index, int_value);
443 }
444 return true;
445 }
446 case flatbuffers::ET_UTYPE:
447 case flatbuffers::ET_BOOL:
448 case flatbuffers::ET_FLOAT:
449 case flatbuffers::ET_DOUBLE:
450 case flatbuffers::ET_STRING:
451 case flatbuffers::ET_SEQUENCE:
452 break;
453 }
454
455 if (in_vector()) {
456 stack_.back().vector_elements.emplace_back(fbb_.CreateString(data));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700457 } else {
458 stack_.back().elements.emplace_back(field_index, fbb_.CreateString(data));
459 }
460 return true;
461}
462
Austin Schuh43c6a352019-09-30 22:22:10 -0700463bool AddSingleElement(const flatbuffers::TypeTable *typetable,
464 const FieldElement &field_element,
465 ::std::vector<bool> *fields_in_use,
466 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700467 if ((*fields_in_use)[field_element.field_index]) {
Austin Schuh217a9782019-12-21 23:02:50 -0800468 fprintf(stderr, "Duplicate field: '%s'\n",
469 typetable->names[field_element.field_index]);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700470 return false;
471 }
472
473 (*fields_in_use)[field_element.field_index] = true;
474
475 switch (field_element.element.type) {
476 case Element::ElementType::INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700477 return AddSingleElement(typetable, field_element.field_index,
478 field_element.element.int_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700479 case Element::ElementType::DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700480 return AddSingleElement(typetable, field_element.field_index,
481 field_element.element.double_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700482 case Element::ElementType::OFFSET:
Austin Schuh43c6a352019-09-30 22:22:10 -0700483 return AddSingleElement(typetable, field_element.field_index,
484 field_element.element.offset_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700485 }
486 return false;
487}
488
Austin Schuh43c6a352019-09-30 22:22:10 -0700489bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
490 int64_t int_value, flatbuffers::FlatBufferBuilder *fbb
491
492) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700493 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
494 static_cast<flatbuffers::voffset_t>(field_index));
495
Austin Schuh43c6a352019-09-30 22:22:10 -0700496 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700497
498 const flatbuffers::ElementaryType elementary_type =
499 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
500 switch (elementary_type) {
501 case flatbuffers::ET_BOOL:
Austin Schuh43c6a352019-09-30 22:22:10 -0700502 fbb->AddElement<bool>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700503 return true;
504 case flatbuffers::ET_CHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700505 fbb->AddElement<int8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700506 return true;
507 case flatbuffers::ET_UCHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700508 fbb->AddElement<uint8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700509 return true;
510 case flatbuffers::ET_SHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700511 fbb->AddElement<int16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700512 return true;
513 case flatbuffers::ET_USHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700514 fbb->AddElement<uint16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700515 return true;
516 case flatbuffers::ET_INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700517 fbb->AddElement<int32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700518 return true;
519 case flatbuffers::ET_UINT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700520 fbb->AddElement<uint32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700521 return true;
522 case flatbuffers::ET_LONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700523 fbb->AddElement<int64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700524 return true;
525 case flatbuffers::ET_ULONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700526 fbb->AddElement<uint64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700527 return true;
528 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700529 fbb->AddElement<float>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700530 return true;
531 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700532 fbb->AddElement<double>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700533 return true;
534 case flatbuffers::ET_STRING:
535 case flatbuffers::ET_UTYPE:
536 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800537 fprintf(
538 stderr, "Mismatched type for field '%s'. Got: integer, expected %s\n",
539 typetable->names[field_index], ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700540 return false;
541 };
542 return false;
543}
544
Austin Schuh43c6a352019-09-30 22:22:10 -0700545bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
546 double double_value,
547 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700548 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
549 static_cast<flatbuffers::voffset_t>(field_index));
550
Austin Schuh43c6a352019-09-30 22:22:10 -0700551 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700552
553 const flatbuffers::ElementaryType elementary_type =
554 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
555 switch (elementary_type) {
556 case flatbuffers::ET_UTYPE:
557 case flatbuffers::ET_BOOL:
558 case flatbuffers::ET_CHAR:
559 case flatbuffers::ET_UCHAR:
560 case flatbuffers::ET_SHORT:
561 case flatbuffers::ET_USHORT:
562 case flatbuffers::ET_INT:
563 case flatbuffers::ET_UINT:
564 case flatbuffers::ET_LONG:
565 case flatbuffers::ET_ULONG:
566 case flatbuffers::ET_STRING:
567 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800568 fprintf(
569 stderr, "Mismatched type for field '%s'. Got: double, expected %s\n",
570 typetable->names[field_index], ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700571 return false;
572 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700573 fbb->AddElement<float>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700574 return true;
575 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700576 fbb->AddElement<double>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700577 return true;
578 }
579 return false;
580}
Austin Schuh43c6a352019-09-30 22:22:10 -0700581bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
582 flatbuffers::Offset<flatbuffers::String> offset_element,
583 flatbuffers::FlatBufferBuilder *fbb) {
584 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700585
586 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
587 static_cast<flatbuffers::voffset_t>(field_index));
588
589 // Vectors will always be Offset<>'s.
590 if (type_code.is_vector) {
Austin Schuh43c6a352019-09-30 22:22:10 -0700591 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700592 return true;
593 }
594
595 const flatbuffers::ElementaryType elementary_type =
596 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
597 switch (elementary_type) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700598 case flatbuffers::ET_CHAR:
599 case flatbuffers::ET_UCHAR:
600 case flatbuffers::ET_SHORT:
601 case flatbuffers::ET_USHORT:
602 case flatbuffers::ET_INT:
603 case flatbuffers::ET_UINT:
604 case flatbuffers::ET_LONG:
605 case flatbuffers::ET_ULONG:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700606 case flatbuffers::ET_UTYPE:
607 case flatbuffers::ET_BOOL:
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700608 case flatbuffers::ET_FLOAT:
609 case flatbuffers::ET_DOUBLE:
Austin Schuh217a9782019-12-21 23:02:50 -0800610 fprintf(
611 stderr, "Mismatched type for field '%s'. Got: string, expected %s\n",
612 typetable->names[field_index], ElementaryTypeName(elementary_type));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700613 CHECK_EQ(type_code.sequence_ref, -1)
614 << ": Field name " << typetable->names[field_index]
615 << " Got string expected " << ElementaryTypeName(elementary_type);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700616 return false;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700617 case flatbuffers::ET_STRING:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700618 CHECK_EQ(type_code.sequence_ref, -1);
619 case flatbuffers::ET_SEQUENCE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700620 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700621 return true;
622 }
623 return false;
624}
625
626bool JsonParser::FinishVector(int field_index) {
627 flatbuffers::TypeCode type_code =
628 stack_.back().typetable->type_codes[field_index];
629
630 const flatbuffers::ElementaryType elementary_type =
631 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
632
633 // Vectors have a start (unfortunately which needs to know the size)
634 fbb_.StartVector(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700635 stack_.back().vector_elements.size(),
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700636 flatbuffers::InlineSize(elementary_type, stack_.back().typetable));
637
638 // Then the data (in reverse order for some reason...)
Alex Perrycb7da4b2019-08-28 19:35:56 -0700639 for (size_t i = stack_.back().vector_elements.size(); i > 0;) {
640 const Element &element = stack_.back().vector_elements[--i];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700641 switch (element.type) {
642 case Element::ElementType::INT:
643 if (!PushElement(elementary_type, element.int_element)) return false;
644 break;
645 case Element::ElementType::DOUBLE:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700646 CHECK_EQ(type_code.sequence_ref, -1)
647 << ": Field index is " << field_index;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700648 if (!PushElement(elementary_type, element.double_element)) return false;
649 break;
650 case Element::ElementType::OFFSET:
651 if (!PushElement(elementary_type, element.offset_element)) return false;
652 break;
653 }
654 }
655
656 // Then an End which is placed into the buffer the same as any other offset.
657 stack_.back().elements.emplace_back(
658 field_index, flatbuffers::Offset<flatbuffers::String>(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700659 fbb_.EndVector(stack_.back().vector_elements.size())));
660 stack_.back().vector_elements.clear();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700661 return true;
662}
663
664bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
665 int64_t int_value) {
666 switch (elementary_type) {
667 case flatbuffers::ET_BOOL:
668 fbb_.PushElement<bool>(int_value);
669 return true;
670 case flatbuffers::ET_CHAR:
671 fbb_.PushElement<int8_t>(int_value);
672 return true;
673 case flatbuffers::ET_UCHAR:
674 fbb_.PushElement<uint8_t>(int_value);
675 return true;
676 case flatbuffers::ET_SHORT:
677 fbb_.PushElement<int16_t>(int_value);
678 return true;
679 case flatbuffers::ET_USHORT:
680 fbb_.PushElement<uint16_t>(int_value);
681 return true;
682 case flatbuffers::ET_INT:
683 fbb_.PushElement<int32_t>(int_value);
684 return true;
685 case flatbuffers::ET_UINT:
686 fbb_.PushElement<uint32_t>(int_value);
687 return true;
688 case flatbuffers::ET_LONG:
689 fbb_.PushElement<int64_t>(int_value);
690 return true;
691 case flatbuffers::ET_ULONG:
692 fbb_.PushElement<uint64_t>(int_value);
693 return true;
694 case flatbuffers::ET_FLOAT:
695 fbb_.PushElement<float>(int_value);
696 return true;
697 case flatbuffers::ET_DOUBLE:
698 fbb_.PushElement<double>(int_value);
699 return true;
700 case flatbuffers::ET_STRING:
701 case flatbuffers::ET_UTYPE:
702 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800703 fprintf(stderr,
704 "Mismatched type for field '%s'. Got: integer, expected %s\n",
705 stack_.back().field_name.c_str(),
706 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700707 return false;
708 };
709 return false;
710}
711
712bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
713 double double_value) {
714 switch (elementary_type) {
715 case flatbuffers::ET_UTYPE:
716 case flatbuffers::ET_BOOL:
717 case flatbuffers::ET_CHAR:
718 case flatbuffers::ET_UCHAR:
719 case flatbuffers::ET_SHORT:
720 case flatbuffers::ET_USHORT:
721 case flatbuffers::ET_INT:
722 case flatbuffers::ET_UINT:
723 case flatbuffers::ET_LONG:
724 case flatbuffers::ET_ULONG:
725 case flatbuffers::ET_STRING:
726 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800727 fprintf(stderr,
728 "Mismatched type for field '%s'. Got: double, expected %s\n",
729 stack_.back().field_name.c_str(),
730 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700731 return false;
732 case flatbuffers::ET_FLOAT:
733 fbb_.PushElement<float>(double_value);
734 return true;
735 case flatbuffers::ET_DOUBLE:
736 fbb_.PushElement<double>(double_value);
737 return true;
738 }
739 return false;
740}
741
742bool JsonParser::PushElement(
743 flatbuffers::ElementaryType elementary_type,
744 flatbuffers::Offset<flatbuffers::String> offset_value) {
745 switch (elementary_type) {
746 case flatbuffers::ET_UTYPE:
747 case flatbuffers::ET_BOOL:
748 case flatbuffers::ET_CHAR:
749 case flatbuffers::ET_UCHAR:
750 case flatbuffers::ET_SHORT:
751 case flatbuffers::ET_USHORT:
752 case flatbuffers::ET_INT:
753 case flatbuffers::ET_UINT:
754 case flatbuffers::ET_LONG:
755 case flatbuffers::ET_ULONG:
756 case flatbuffers::ET_FLOAT:
757 case flatbuffers::ET_DOUBLE:
Austin Schuh217a9782019-12-21 23:02:50 -0800758 fprintf(stderr,
759 "Mismatched type for field '%s'. Got: sequence, expected %s\n",
760 stack_.back().field_name.c_str(),
761 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700762 return false;
763 case flatbuffers::ET_STRING:
764 case flatbuffers::ET_SEQUENCE:
765 fbb_.PushElement(offset_value);
766 return true;
767 }
768 return false;
769}
770
771} // namespace
772
Austin Schuhe93d8642019-10-13 15:27:07 -0700773flatbuffers::DetachedBuffer JsonToFlatbuffer(
James Kuszmaul3ae42262019-11-08 12:33:41 -0800774 const std::string_view data,
775 const flatbuffers::TypeTable *typetable) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700776 JsonParser p;
777 return p.Parse(data, typetable);
778}
779
Austin Schuhe93d8642019-10-13 15:27:07 -0700780::std::string BufferFlatbufferToJson(const uint8_t *buffer,
781 const ::flatbuffers::TypeTable *typetable,
782 bool multi_line) {
783 // It is pretty common to get passed in a nullptr when a test fails. Rather
784 // than CHECK, return a more user friendly result.
785 if (buffer == nullptr) {
786 return "null";
787 }
788 return TableFlatbufferToJson(reinterpret_cast<const flatbuffers::Table *>(
789 flatbuffers::GetRoot<uint8_t>(buffer)),
790 typetable, multi_line);
791}
792
793::std::string TableFlatbufferToJson(const flatbuffers::Table *t,
794 const ::flatbuffers::TypeTable *typetable,
795 bool multi_line) {
796 // It is pretty common to get passed in a nullptr when a test fails. Rather
797 // than CHECK, return a more user friendly result.
798 if (t == nullptr) {
799 return "null";
800 }
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700801 ::flatbuffers::ToStringVisitor tostring_visitor(
802 multi_line ? "\n" : " ", true, multi_line ? " " : "", multi_line);
Austin Schuhe93d8642019-10-13 15:27:07 -0700803 flatbuffers::IterateObject(reinterpret_cast<const uint8_t *>(t), typetable,
804 &tostring_visitor);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700805 return tostring_visitor.s;
806}
807
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700808} // namespace aos