blob: 95c95ddbcf07dad0dedaf800338db90189f0d898 [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:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800123 JsonParser(flatbuffers::FlatBufferBuilder *fbb) : fbb_(fbb) {}
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700124 ~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.
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800128 flatbuffers::Offset<flatbuffers::Table> Parse(
129 const std::string_view data, 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.
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800135 return flatbuffers::Offset<flatbuffers::Table>(end);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700136 } else {
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800137 return flatbuffers::Offset<flatbuffers::Table>(0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700138 }
139 }
140
141 private:
142 // Setters and getters for in_vector (at the current level of the stack)
143 bool in_vector() const { return stack_.back().in_vector; }
144 void set_in_vector(bool in_vector) { stack_.back().in_vector = in_vector; }
145
146 // Parses the flatbuffer. This is a second method so we can do easier
147 // cleanup at the top level. Returns true on success.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700148 bool DoParse(const flatbuffers::TypeTable *typetable,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800149 const std::string_view data,
150 flatbuffers::uoffset_t *table_end);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700151
152 // Adds *_value for the provided field. If we are in a vector, queues the
Alex Perrycb7da4b2019-08-28 19:35:56 -0700153 // data up in vector_elements. Returns true on success.
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700154 bool AddElement(int field_index, int64_t int_value);
155 bool AddElement(int field_index, double double_value);
156 bool AddElement(int field_index, const ::std::string &data);
157
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700158 // Finishes a vector for the provided field index. Returns true on success.
159 bool FinishVector(int field_index);
160
161 // Pushes an element as part of a vector. Returns true on success.
162 bool PushElement(flatbuffers::ElementaryType elementary_type,
163 int64_t int_value);
164 bool PushElement(flatbuffers::ElementaryType elementary_type,
165 double double_value);
166 bool PushElement(flatbuffers::ElementaryType elementary_type,
167 flatbuffers::Offset<flatbuffers::String> offset_value);
168
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800169 flatbuffers::FlatBufferBuilder *fbb_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700170
171 // This holds the state information that is needed as you recurse into
172 // nested structures.
173 struct FlatBufferContext {
174 // Type of the current type.
175 const flatbuffers::TypeTable *typetable;
176 // If true, we are parsing a vector.
177 bool in_vector;
178 // The field index of the current field.
179 int field_index;
180 // Name of the current field.
181 ::std::string field_name;
182
183 // Field elements that need to be inserted.
184 ::std::vector<FieldElement> elements;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700185
186 // For scalar types (not strings, and not nested tables), the vector ends
187 // up being implemented as a start and end, and a block of data. So we
188 // can't just push offsets in as we go. We either need to reproduce the
189 // logic inside flatbuffers, or build up vectors of the data. Vectors will
190 // be a bit of extra stack space, but whatever.
191 //
192 // Strings and nested structures are vectors of offsets.
193 // into the vector. Once you get to the end, you build up a vector and
194 // push that into the field.
195 ::std::vector<Element> vector_elements;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700196 };
197 ::std::vector<FlatBufferContext> stack_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700198};
199
200bool JsonParser::DoParse(const flatbuffers::TypeTable *typetable,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800201 const std::string_view data,
Austin Schuhd339a9b2019-10-05 21:33:32 -0700202 flatbuffers::uoffset_t *table_end) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700203 ::std::vector<const flatbuffers::TypeTable *> stack;
204
205 Tokenizer t(data);
206
207 // Main loop. Run until we get an end.
208 while (true) {
209 Tokenizer::TokenType token = t.Next();
210
211 switch (token) {
212 case Tokenizer::TokenType::kEnd:
213 if (stack_.size() != 0) {
Austin Schuh217a9782019-12-21 23:02:50 -0800214 fprintf(stderr, "Failed to unwind stack all the way\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700215 return false;
216 } else {
217 return true;
218 }
219 break;
220 case Tokenizer::TokenType::kError:
221 return false;
222 break;
223
224 case Tokenizer::TokenType::kStartObject: // {
225 if (stack_.size() == 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700226 stack_.push_back({typetable, false, -1, "", {}, {}});
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700227 } else {
228 int field_index = stack_.back().field_index;
229
230 const flatbuffers::TypeCode &type_code =
231 stack_.back().typetable->type_codes[field_index];
232
233 if (type_code.base_type != flatbuffers::ET_SEQUENCE) {
Austin Schuh217a9782019-12-21 23:02:50 -0800234 fprintf(stderr, "Field '%s' is not a sequence\n",
235 stack_.back().field_name.c_str());
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700236 return false;
237 }
238
239 flatbuffers::TypeFunction type_function =
240 stack_.back().typetable->type_refs[type_code.sequence_ref];
241
Alex Perrycb7da4b2019-08-28 19:35:56 -0700242 stack_.push_back({type_function(), false, -1, "", {}, {}});
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700243 }
244 break;
245 case Tokenizer::TokenType::kEndObject: // }
246 if (stack_.size() == 0) {
247 // Somehow we popped more than we pushed. Error.
Austin Schuh217a9782019-12-21 23:02:50 -0800248 fprintf(stderr, "Empty stack\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700249 return false;
250 } else {
251 // End of a nested struct! Add it.
Austin Schuh43c6a352019-09-30 22:22:10 -0700252 const flatbuffers::uoffset_t end = WriteTable(
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800253 stack_.back().typetable, stack_.back().elements, fbb_);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700254
255 // We now want to talk about the parent structure. Pop the child.
256 stack_.pop_back();
257
258 if (stack_.size() == 0) {
259 // Instead of queueing it up in the stack, return it through the
260 // passed in variable.
261 *table_end = end;
262 } else {
263 // And now we can add it.
264 const int field_index = stack_.back().field_index;
265
266 // Do the right thing if we are in a vector.
267 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700268 stack_.back().vector_elements.emplace_back(
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700269 flatbuffers::Offset<flatbuffers::String>(end));
270 } else {
271 stack_.back().elements.emplace_back(
272 field_index, flatbuffers::Offset<flatbuffers::String>(end));
273 }
274 }
275 }
276 break;
277
278 case Tokenizer::TokenType::kStartArray: // [
279 if (stack_.size() == 0) {
280 // We don't support an array of structs at the root level.
281 return false;
282 }
283 // Sanity check that we aren't trying to make a vector of vectors.
284 if (in_vector()) {
285 return false;
286 }
287 set_in_vector(true);
288
289 break;
290 case Tokenizer::TokenType::kEndArray: { // ]
291 if (!in_vector()) {
292 return false;
293 }
294
295 const int field_index = stack_.back().field_index;
296
297 if (!FinishVector(field_index)) return false;
298
299 set_in_vector(false);
300 } break;
301
302 case Tokenizer::TokenType::kTrueValue: // true
303 case Tokenizer::TokenType::kFalseValue: // false
304 case Tokenizer::TokenType::kNumberValue: {
305 bool is_int = true;
306 double double_value;
307 long long int_value;
308 if (token == Tokenizer::TokenType::kTrueValue) {
309 int_value = 1;
310 } else if (token == Tokenizer::TokenType::kFalseValue) {
311 int_value = 0;
312 } else if (!t.FieldAsInt(&int_value)) {
313 if (t.FieldAsDouble(&double_value)) {
314 is_int = false;
315 } else {
316 fprintf(stderr, "Got a invalid number '%s'\n",
317 t.field_value().c_str());
318 return false;
319 }
320 }
321
322 const int field_index = stack_.back().field_index;
323
324 if (is_int) {
325 // No need to get too stressed about bool vs int. Convert them all.
326 int64_t val = int_value;
327 if (!AddElement(field_index, val)) return false;
328 } else {
329 if (!AddElement(field_index, double_value)) return false;
330 }
331 } break;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700332 case Tokenizer::TokenType::kStringValue: // string value
333 {
334 const int field_index = stack_.back().field_index;
335
336 if (!AddElement(field_index, t.field_value())) return false;
337 } break;
338 case Tokenizer::TokenType::kField: // field name
339 {
340 stack_.back().field_name = t.field_name();
341 stack_.back().field_index = FieldIndex(
342 stack_.back().typetable, stack_.back().field_name.c_str());
343
344 if (stack_.back().field_index == -1) {
Austin Schuh217a9782019-12-21 23:02:50 -0800345 fprintf(stderr, "Invalid field name '%s'\n",
346 stack_.back().field_name.c_str());
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700347 return false;
348 }
349 } break;
350 }
351 }
352 return false;
353}
354
355bool JsonParser::AddElement(int field_index, int64_t int_value) {
356 flatbuffers::TypeCode type_code =
357 stack_.back().typetable->type_codes[field_index];
358
359 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800360 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700361 return false;
362 }
363
364 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700365 stack_.back().vector_elements.emplace_back(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700366 } else {
367 stack_.back().elements.emplace_back(field_index, int_value);
368 }
369 return true;
370}
371
372bool JsonParser::AddElement(int field_index, double double_value) {
373 flatbuffers::TypeCode type_code =
374 stack_.back().typetable->type_codes[field_index];
375
376 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800377 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700378 return false;
379 }
380
381 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700382 stack_.back().vector_elements.emplace_back(double_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700383 } else {
384 stack_.back().elements.emplace_back(field_index, double_value);
385 }
386 return true;
387}
388
389bool JsonParser::AddElement(int field_index, const ::std::string &data) {
390 flatbuffers::TypeCode type_code =
391 stack_.back().typetable->type_codes[field_index];
392
393 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800394 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700395 return false;
396 }
397
Alex Perrycb7da4b2019-08-28 19:35:56 -0700398 const flatbuffers::ElementaryType elementary_type =
399 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
400 switch (elementary_type) {
401 case flatbuffers::ET_CHAR:
402 case flatbuffers::ET_UCHAR:
403 case flatbuffers::ET_SHORT:
404 case flatbuffers::ET_USHORT:
405 case flatbuffers::ET_INT:
406 case flatbuffers::ET_UINT:
407 case flatbuffers::ET_LONG:
408 case flatbuffers::ET_ULONG:
409 if (type_code.sequence_ref != -1) {
410 // We have an enum.
411 const flatbuffers::TypeTable *type_table = stack_.back().typetable;
412 flatbuffers::TypeFunction type_function =
413 type_table->type_refs[type_code.sequence_ref];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700414
Alex Perrycb7da4b2019-08-28 19:35:56 -0700415 const flatbuffers::TypeTable *enum_type_table = type_function();
416
417 CHECK_EQ(enum_type_table->st, flatbuffers::ST_ENUM);
418
419 int64_t int_value = 0;
420 bool found = false;
421 for (size_t i = 0; i < enum_type_table->num_elems; ++i) {
422 if (data == enum_type_table->names[i]) {
423 int_value = i;
424 found = true;
425 break;
426 }
427 }
428
429 if (!found) {
Austin Schuh217a9782019-12-21 23:02:50 -0800430 fprintf(stderr, "Enum value '%s' not found for field '%s'\n",
431 data.c_str(), type_table->names[field_index]);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700432 return false;
433 }
434
435 if (in_vector()) {
436 stack_.back().vector_elements.emplace_back(int_value);
437 } else {
438 stack_.back().elements.emplace_back(field_index, int_value);
439 }
440 return true;
441 }
442 case flatbuffers::ET_UTYPE:
443 case flatbuffers::ET_BOOL:
444 case flatbuffers::ET_FLOAT:
445 case flatbuffers::ET_DOUBLE:
446 case flatbuffers::ET_STRING:
447 case flatbuffers::ET_SEQUENCE:
448 break;
449 }
450
451 if (in_vector()) {
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800452 stack_.back().vector_elements.emplace_back(fbb_->CreateString(data));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700453 } else {
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800454 stack_.back().elements.emplace_back(field_index, fbb_->CreateString(data));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700455 }
456 return true;
457}
458
Austin Schuh43c6a352019-09-30 22:22:10 -0700459bool AddSingleElement(const flatbuffers::TypeTable *typetable,
460 const FieldElement &field_element,
461 ::std::vector<bool> *fields_in_use,
462 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700463 if ((*fields_in_use)[field_element.field_index]) {
Austin Schuh217a9782019-12-21 23:02:50 -0800464 fprintf(stderr, "Duplicate field: '%s'\n",
465 typetable->names[field_element.field_index]);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700466 return false;
467 }
468
469 (*fields_in_use)[field_element.field_index] = true;
470
471 switch (field_element.element.type) {
472 case Element::ElementType::INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700473 return AddSingleElement(typetable, field_element.field_index,
474 field_element.element.int_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700475 case Element::ElementType::DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700476 return AddSingleElement(typetable, field_element.field_index,
477 field_element.element.double_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700478 case Element::ElementType::OFFSET:
Austin Schuh43c6a352019-09-30 22:22:10 -0700479 return AddSingleElement(typetable, field_element.field_index,
480 field_element.element.offset_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700481 }
482 return false;
483}
484
Austin Schuh43c6a352019-09-30 22:22:10 -0700485bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
486 int64_t int_value, flatbuffers::FlatBufferBuilder *fbb
487
488) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700489 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
490 static_cast<flatbuffers::voffset_t>(field_index));
491
Austin Schuh43c6a352019-09-30 22:22:10 -0700492 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700493
494 const flatbuffers::ElementaryType elementary_type =
495 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
496 switch (elementary_type) {
497 case flatbuffers::ET_BOOL:
Austin Schuh43c6a352019-09-30 22:22:10 -0700498 fbb->AddElement<bool>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700499 return true;
500 case flatbuffers::ET_CHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700501 fbb->AddElement<int8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700502 return true;
503 case flatbuffers::ET_UCHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700504 fbb->AddElement<uint8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700505 return true;
506 case flatbuffers::ET_SHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700507 fbb->AddElement<int16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700508 return true;
509 case flatbuffers::ET_USHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700510 fbb->AddElement<uint16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700511 return true;
512 case flatbuffers::ET_INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700513 fbb->AddElement<int32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700514 return true;
515 case flatbuffers::ET_UINT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700516 fbb->AddElement<uint32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700517 return true;
518 case flatbuffers::ET_LONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700519 fbb->AddElement<int64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700520 return true;
521 case flatbuffers::ET_ULONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700522 fbb->AddElement<uint64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700523 return true;
524 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700525 fbb->AddElement<float>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700526 return true;
527 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700528 fbb->AddElement<double>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700529 return true;
530 case flatbuffers::ET_STRING:
531 case flatbuffers::ET_UTYPE:
532 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800533 fprintf(
534 stderr, "Mismatched type for field '%s'. Got: integer, expected %s\n",
535 typetable->names[field_index], ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700536 return false;
537 };
538 return false;
539}
540
Austin Schuh43c6a352019-09-30 22:22:10 -0700541bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
542 double double_value,
543 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700544 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
545 static_cast<flatbuffers::voffset_t>(field_index));
546
Austin Schuh43c6a352019-09-30 22:22:10 -0700547 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700548
549 const flatbuffers::ElementaryType elementary_type =
550 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
551 switch (elementary_type) {
552 case flatbuffers::ET_UTYPE:
553 case flatbuffers::ET_BOOL:
554 case flatbuffers::ET_CHAR:
555 case flatbuffers::ET_UCHAR:
556 case flatbuffers::ET_SHORT:
557 case flatbuffers::ET_USHORT:
558 case flatbuffers::ET_INT:
559 case flatbuffers::ET_UINT:
560 case flatbuffers::ET_LONG:
561 case flatbuffers::ET_ULONG:
562 case flatbuffers::ET_STRING:
563 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800564 fprintf(
565 stderr, "Mismatched type for field '%s'. Got: double, expected %s\n",
566 typetable->names[field_index], ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700567 return false;
568 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700569 fbb->AddElement<float>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700570 return true;
571 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700572 fbb->AddElement<double>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700573 return true;
574 }
575 return false;
576}
Austin Schuh43c6a352019-09-30 22:22:10 -0700577bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
578 flatbuffers::Offset<flatbuffers::String> offset_element,
579 flatbuffers::FlatBufferBuilder *fbb) {
580 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700581
582 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
583 static_cast<flatbuffers::voffset_t>(field_index));
584
585 // Vectors will always be Offset<>'s.
586 if (type_code.is_vector) {
Austin Schuh43c6a352019-09-30 22:22:10 -0700587 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700588 return true;
589 }
590
591 const flatbuffers::ElementaryType elementary_type =
592 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
593 switch (elementary_type) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700594 case flatbuffers::ET_CHAR:
595 case flatbuffers::ET_UCHAR:
596 case flatbuffers::ET_SHORT:
597 case flatbuffers::ET_USHORT:
598 case flatbuffers::ET_INT:
599 case flatbuffers::ET_UINT:
600 case flatbuffers::ET_LONG:
601 case flatbuffers::ET_ULONG:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700602 case flatbuffers::ET_UTYPE:
603 case flatbuffers::ET_BOOL:
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700604 case flatbuffers::ET_FLOAT:
605 case flatbuffers::ET_DOUBLE:
Austin Schuh217a9782019-12-21 23:02:50 -0800606 fprintf(
607 stderr, "Mismatched type for field '%s'. Got: string, expected %s\n",
608 typetable->names[field_index], ElementaryTypeName(elementary_type));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700609 CHECK_EQ(type_code.sequence_ref, -1)
610 << ": Field name " << typetable->names[field_index]
611 << " Got string expected " << ElementaryTypeName(elementary_type);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700612 return false;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700613 case flatbuffers::ET_STRING:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700614 CHECK_EQ(type_code.sequence_ref, -1);
615 case flatbuffers::ET_SEQUENCE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700616 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700617 return true;
618 }
619 return false;
620}
621
622bool JsonParser::FinishVector(int field_index) {
623 flatbuffers::TypeCode type_code =
624 stack_.back().typetable->type_codes[field_index];
625
626 const flatbuffers::ElementaryType elementary_type =
627 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
628
629 // Vectors have a start (unfortunately which needs to know the size)
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800630 fbb_->StartVector(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700631 stack_.back().vector_elements.size(),
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700632 flatbuffers::InlineSize(elementary_type, stack_.back().typetable));
633
634 // Then the data (in reverse order for some reason...)
Alex Perrycb7da4b2019-08-28 19:35:56 -0700635 for (size_t i = stack_.back().vector_elements.size(); i > 0;) {
636 const Element &element = stack_.back().vector_elements[--i];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700637 switch (element.type) {
638 case Element::ElementType::INT:
639 if (!PushElement(elementary_type, element.int_element)) return false;
640 break;
641 case Element::ElementType::DOUBLE:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700642 CHECK_EQ(type_code.sequence_ref, -1)
643 << ": Field index is " << field_index;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700644 if (!PushElement(elementary_type, element.double_element)) return false;
645 break;
646 case Element::ElementType::OFFSET:
647 if (!PushElement(elementary_type, element.offset_element)) return false;
648 break;
649 }
650 }
651
652 // Then an End which is placed into the buffer the same as any other offset.
653 stack_.back().elements.emplace_back(
654 field_index, flatbuffers::Offset<flatbuffers::String>(
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800655 fbb_->EndVector(stack_.back().vector_elements.size())));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700656 stack_.back().vector_elements.clear();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700657 return true;
658}
659
660bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
661 int64_t int_value) {
662 switch (elementary_type) {
663 case flatbuffers::ET_BOOL:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800664 fbb_->PushElement<bool>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700665 return true;
666 case flatbuffers::ET_CHAR:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800667 fbb_->PushElement<int8_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700668 return true;
669 case flatbuffers::ET_UCHAR:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800670 fbb_->PushElement<uint8_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700671 return true;
672 case flatbuffers::ET_SHORT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800673 fbb_->PushElement<int16_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700674 return true;
675 case flatbuffers::ET_USHORT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800676 fbb_->PushElement<uint16_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700677 return true;
678 case flatbuffers::ET_INT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800679 fbb_->PushElement<int32_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700680 return true;
681 case flatbuffers::ET_UINT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800682 fbb_->PushElement<uint32_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700683 return true;
684 case flatbuffers::ET_LONG:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800685 fbb_->PushElement<int64_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700686 return true;
687 case flatbuffers::ET_ULONG:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800688 fbb_->PushElement<uint64_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700689 return true;
690 case flatbuffers::ET_FLOAT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800691 fbb_->PushElement<float>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700692 return true;
693 case flatbuffers::ET_DOUBLE:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800694 fbb_->PushElement<double>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700695 return true;
696 case flatbuffers::ET_STRING:
697 case flatbuffers::ET_UTYPE:
698 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800699 fprintf(stderr,
700 "Mismatched type for field '%s'. Got: integer, expected %s\n",
701 stack_.back().field_name.c_str(),
702 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700703 return false;
704 };
705 return false;
706}
707
708bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
709 double double_value) {
710 switch (elementary_type) {
711 case flatbuffers::ET_UTYPE:
712 case flatbuffers::ET_BOOL:
713 case flatbuffers::ET_CHAR:
714 case flatbuffers::ET_UCHAR:
715 case flatbuffers::ET_SHORT:
716 case flatbuffers::ET_USHORT:
717 case flatbuffers::ET_INT:
718 case flatbuffers::ET_UINT:
719 case flatbuffers::ET_LONG:
720 case flatbuffers::ET_ULONG:
721 case flatbuffers::ET_STRING:
722 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800723 fprintf(stderr,
724 "Mismatched type for field '%s'. Got: double, expected %s\n",
725 stack_.back().field_name.c_str(),
726 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700727 return false;
728 case flatbuffers::ET_FLOAT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800729 fbb_->PushElement<float>(double_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700730 return true;
731 case flatbuffers::ET_DOUBLE:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800732 fbb_->PushElement<double>(double_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700733 return true;
734 }
735 return false;
736}
737
738bool JsonParser::PushElement(
739 flatbuffers::ElementaryType elementary_type,
740 flatbuffers::Offset<flatbuffers::String> offset_value) {
741 switch (elementary_type) {
742 case flatbuffers::ET_UTYPE:
743 case flatbuffers::ET_BOOL:
744 case flatbuffers::ET_CHAR:
745 case flatbuffers::ET_UCHAR:
746 case flatbuffers::ET_SHORT:
747 case flatbuffers::ET_USHORT:
748 case flatbuffers::ET_INT:
749 case flatbuffers::ET_UINT:
750 case flatbuffers::ET_LONG:
751 case flatbuffers::ET_ULONG:
752 case flatbuffers::ET_FLOAT:
753 case flatbuffers::ET_DOUBLE:
Austin Schuh217a9782019-12-21 23:02:50 -0800754 fprintf(stderr,
755 "Mismatched type for field '%s'. Got: sequence, expected %s\n",
756 stack_.back().field_name.c_str(),
757 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700758 return false;
759 case flatbuffers::ET_STRING:
760 case flatbuffers::ET_SEQUENCE:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800761 fbb_->PushElement(offset_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700762 return true;
763 }
764 return false;
765}
766
767} // namespace
768
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800769flatbuffers::Offset<flatbuffers::Table> JsonToFlatbuffer(
770 const std::string_view data, const flatbuffers::TypeTable *typetable,
771 flatbuffers::FlatBufferBuilder *fbb) {
772 JsonParser p(fbb);
773 return p.Parse(data, typetable);
774}
775
Austin Schuhe93d8642019-10-13 15:27:07 -0700776flatbuffers::DetachedBuffer JsonToFlatbuffer(
James Kuszmaul3ae42262019-11-08 12:33:41 -0800777 const std::string_view data,
778 const flatbuffers::TypeTable *typetable) {
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800779 flatbuffers::FlatBufferBuilder fbb;
Austin Schuhd7b15da2020-02-17 15:06:11 -0800780 fbb.ForceDefaults(true);
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800781
782 const flatbuffers::Offset<flatbuffers::Table> result =
783 JsonToFlatbuffer(data, typetable, &fbb);
784 if (result.o != 0) {
785 fbb.Finish(result);
786
787 return fbb.Release();
788 } else {
789 // Otherwise return an empty vector.
790 return flatbuffers::DetachedBuffer();
791 }
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700792}
793
Austin Schuhe93d8642019-10-13 15:27:07 -0700794::std::string BufferFlatbufferToJson(const uint8_t *buffer,
795 const ::flatbuffers::TypeTable *typetable,
796 bool multi_line) {
797 // It is pretty common to get passed in a nullptr when a test fails. Rather
798 // than CHECK, return a more user friendly result.
799 if (buffer == nullptr) {
800 return "null";
801 }
802 return TableFlatbufferToJson(reinterpret_cast<const flatbuffers::Table *>(
803 flatbuffers::GetRoot<uint8_t>(buffer)),
804 typetable, multi_line);
805}
806
807::std::string TableFlatbufferToJson(const flatbuffers::Table *t,
808 const ::flatbuffers::TypeTable *typetable,
809 bool multi_line) {
810 // It is pretty common to get passed in a nullptr when a test fails. Rather
811 // than CHECK, return a more user friendly result.
812 if (t == nullptr) {
813 return "null";
814 }
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700815 ::flatbuffers::ToStringVisitor tostring_visitor(
816 multi_line ? "\n" : " ", true, multi_line ? " " : "", multi_line);
Austin Schuhe93d8642019-10-13 15:27:07 -0700817 flatbuffers::IterateObject(reinterpret_cast<const uint8_t *>(t), typetable,
818 &tostring_visitor);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700819 return tostring_visitor.s;
820}
821
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700822} // namespace aos