blob: fdab330f9c2e3ede9928547dac2c924d059ae867 [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
Austin Schuh43c6a352019-09-30 22:22:10 -070093// Writes an array of FieldElement (with the definition in the type
94// table) to the builder. Returns the offset of the table.
95flatbuffers::uoffset_t WriteTable(const flatbuffers::TypeTable *typetable,
96 const ::std::vector<FieldElement> &elements,
97 flatbuffers::FlatBufferBuilder *fbb) {
98 // End of a nested struct! Add it.
99 const flatbuffers::uoffset_t start = fbb->StartTable();
100
101 ::std::vector<bool> fields_in_use(typetable->num_elems, false);
102
103 for (const FieldElement &field_element : elements) {
104 AddSingleElement(typetable, field_element, &fields_in_use, fbb);
105 }
106
107 return fbb->EndTable(start);
108}
109
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700110// Class to parse JSON into a flatbuffer.
111//
112// The basic strategy is that we need to do everything backwards. So we need to
113// build up what we need to do fully in memory, then do it.
114//
115// The driver for this is that strings need to be fully created before the
116// tables that use them. Same for sub messages. But, we only know we have them
Austin Schuh43c6a352019-09-30 22:22:10 -0700117// all when the structure ends. So, store each sub message in a
118// FieldElement and put them in the table at the end when we finish up
119// each message. Same goes for vectors.
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700120class JsonParser {
121 public:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800122 JsonParser(flatbuffers::FlatBufferBuilder *fbb) : fbb_(fbb) {}
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700123 ~JsonParser() {}
124
125 // Parses the json into a flatbuffer. Returns either an empty vector on
126 // error, or a vector with the flatbuffer data in it.
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800127 flatbuffers::Offset<flatbuffers::Table> Parse(
128 const std::string_view data, const flatbuffers::TypeTable *typetable) {
Austin Schuh43c6a352019-09-30 22:22:10 -0700129 flatbuffers::uoffset_t end = 0;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700130 bool result = DoParse(typetable, data, &end);
131
132 if (result) {
133 // On success, finish the table and build the vector.
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800134 return flatbuffers::Offset<flatbuffers::Table>(end);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700135 } else {
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800136 return flatbuffers::Offset<flatbuffers::Table>(0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700137 }
138 }
139
140 private:
141 // Setters and getters for in_vector (at the current level of the stack)
142 bool in_vector() const { return stack_.back().in_vector; }
143 void set_in_vector(bool in_vector) { stack_.back().in_vector = in_vector; }
144
145 // Parses the flatbuffer. This is a second method so we can do easier
146 // cleanup at the top level. Returns true on success.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700147 bool DoParse(const flatbuffers::TypeTable *typetable,
Austin Schuhd3936202020-04-07 20:11:07 -0700148 const std::string_view data, flatbuffers::uoffset_t *table_end);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700149
150 // Adds *_value for the provided field. If we are in a vector, queues the
Alex Perrycb7da4b2019-08-28 19:35:56 -0700151 // data up in vector_elements. Returns true on success.
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700152 bool AddElement(int field_index, int64_t int_value);
153 bool AddElement(int field_index, double double_value);
154 bool AddElement(int field_index, const ::std::string &data);
155
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700156 // Finishes a vector for the provided field index. Returns true on success.
157 bool FinishVector(int field_index);
158
159 // Pushes an element as part of a vector. Returns true on success.
160 bool PushElement(flatbuffers::ElementaryType elementary_type,
161 int64_t int_value);
162 bool PushElement(flatbuffers::ElementaryType elementary_type,
163 double double_value);
164 bool PushElement(flatbuffers::ElementaryType elementary_type,
165 flatbuffers::Offset<flatbuffers::String> offset_value);
166
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800167 flatbuffers::FlatBufferBuilder *fbb_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700168
169 // This holds the state information that is needed as you recurse into
170 // nested structures.
171 struct FlatBufferContext {
172 // Type of the current type.
173 const flatbuffers::TypeTable *typetable;
174 // If true, we are parsing a vector.
175 bool in_vector;
176 // The field index of the current field.
177 int field_index;
178 // Name of the current field.
179 ::std::string field_name;
180
181 // Field elements that need to be inserted.
182 ::std::vector<FieldElement> elements;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700183
184 // For scalar types (not strings, and not nested tables), the vector ends
185 // up being implemented as a start and end, and a block of data. So we
186 // can't just push offsets in as we go. We either need to reproduce the
187 // logic inside flatbuffers, or build up vectors of the data. Vectors will
188 // be a bit of extra stack space, but whatever.
189 //
190 // Strings and nested structures are vectors of offsets.
191 // into the vector. Once you get to the end, you build up a vector and
192 // push that into the field.
193 ::std::vector<Element> vector_elements;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700194 };
195 ::std::vector<FlatBufferContext> stack_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700196};
197
198bool JsonParser::DoParse(const flatbuffers::TypeTable *typetable,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800199 const std::string_view data,
Austin Schuhd339a9b2019-10-05 21:33:32 -0700200 flatbuffers::uoffset_t *table_end) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700201 ::std::vector<const flatbuffers::TypeTable *> stack;
202
203 Tokenizer t(data);
204
205 // Main loop. Run until we get an end.
206 while (true) {
207 Tokenizer::TokenType token = t.Next();
208
209 switch (token) {
210 case Tokenizer::TokenType::kEnd:
211 if (stack_.size() != 0) {
Austin Schuh217a9782019-12-21 23:02:50 -0800212 fprintf(stderr, "Failed to unwind stack all the way\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700213 return false;
214 } else {
215 return true;
216 }
217 break;
218 case Tokenizer::TokenType::kError:
219 return false;
220 break;
221
222 case Tokenizer::TokenType::kStartObject: // {
223 if (stack_.size() == 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700224 stack_.push_back({typetable, false, -1, "", {}, {}});
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700225 } else {
226 int field_index = stack_.back().field_index;
227
228 const flatbuffers::TypeCode &type_code =
229 stack_.back().typetable->type_codes[field_index];
230
231 if (type_code.base_type != flatbuffers::ET_SEQUENCE) {
Austin Schuh217a9782019-12-21 23:02:50 -0800232 fprintf(stderr, "Field '%s' is not a sequence\n",
233 stack_.back().field_name.c_str());
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700234 return false;
235 }
236
237 flatbuffers::TypeFunction type_function =
238 stack_.back().typetable->type_refs[type_code.sequence_ref];
239
Alex Perrycb7da4b2019-08-28 19:35:56 -0700240 stack_.push_back({type_function(), false, -1, "", {}, {}});
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700241 }
242 break;
243 case Tokenizer::TokenType::kEndObject: // }
244 if (stack_.size() == 0) {
245 // Somehow we popped more than we pushed. Error.
Austin Schuh217a9782019-12-21 23:02:50 -0800246 fprintf(stderr, "Empty stack\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700247 return false;
248 } else {
249 // End of a nested struct! Add it.
Austin Schuhd3936202020-04-07 20:11:07 -0700250 const flatbuffers::uoffset_t end =
251 WriteTable(stack_.back().typetable, stack_.back().elements, fbb_);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700252
253 // We now want to talk about the parent structure. Pop the child.
254 stack_.pop_back();
255
256 if (stack_.size() == 0) {
257 // Instead of queueing it up in the stack, return it through the
258 // passed in variable.
259 *table_end = end;
260 } else {
261 // And now we can add it.
262 const int field_index = stack_.back().field_index;
263
264 // Do the right thing if we are in a vector.
265 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700266 stack_.back().vector_elements.emplace_back(
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700267 flatbuffers::Offset<flatbuffers::String>(end));
268 } else {
269 stack_.back().elements.emplace_back(
270 field_index, flatbuffers::Offset<flatbuffers::String>(end));
271 }
272 }
273 }
274 break;
275
276 case Tokenizer::TokenType::kStartArray: // [
277 if (stack_.size() == 0) {
278 // We don't support an array of structs at the root level.
279 return false;
280 }
281 // Sanity check that we aren't trying to make a vector of vectors.
282 if (in_vector()) {
283 return false;
284 }
285 set_in_vector(true);
286
287 break;
288 case Tokenizer::TokenType::kEndArray: { // ]
289 if (!in_vector()) {
290 return false;
291 }
292
293 const int field_index = stack_.back().field_index;
294
295 if (!FinishVector(field_index)) return false;
296
297 set_in_vector(false);
298 } break;
299
300 case Tokenizer::TokenType::kTrueValue: // true
301 case Tokenizer::TokenType::kFalseValue: // false
302 case Tokenizer::TokenType::kNumberValue: {
303 bool is_int = true;
304 double double_value;
305 long long int_value;
306 if (token == Tokenizer::TokenType::kTrueValue) {
307 int_value = 1;
308 } else if (token == Tokenizer::TokenType::kFalseValue) {
309 int_value = 0;
310 } else if (!t.FieldAsInt(&int_value)) {
311 if (t.FieldAsDouble(&double_value)) {
312 is_int = false;
313 } else {
314 fprintf(stderr, "Got a invalid number '%s'\n",
315 t.field_value().c_str());
316 return false;
317 }
318 }
319
320 const int field_index = stack_.back().field_index;
321
322 if (is_int) {
323 // No need to get too stressed about bool vs int. Convert them all.
324 int64_t val = int_value;
325 if (!AddElement(field_index, val)) return false;
326 } else {
327 if (!AddElement(field_index, double_value)) return false;
328 }
329 } break;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700330 case Tokenizer::TokenType::kStringValue: // string value
331 {
332 const int field_index = stack_.back().field_index;
333
334 if (!AddElement(field_index, t.field_value())) return false;
335 } break;
336 case Tokenizer::TokenType::kField: // field name
337 {
338 stack_.back().field_name = t.field_name();
339 stack_.back().field_index = FieldIndex(
340 stack_.back().typetable, stack_.back().field_name.c_str());
341
342 if (stack_.back().field_index == -1) {
Austin Schuh217a9782019-12-21 23:02:50 -0800343 fprintf(stderr, "Invalid field name '%s'\n",
344 stack_.back().field_name.c_str());
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700345 return false;
346 }
347 } break;
348 }
349 }
350 return false;
351}
352
353bool JsonParser::AddElement(int field_index, int64_t int_value) {
354 flatbuffers::TypeCode type_code =
355 stack_.back().typetable->type_codes[field_index];
356
357 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800358 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700359 return false;
360 }
361
362 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700363 stack_.back().vector_elements.emplace_back(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700364 } else {
365 stack_.back().elements.emplace_back(field_index, int_value);
366 }
367 return true;
368}
369
370bool JsonParser::AddElement(int field_index, double double_value) {
371 flatbuffers::TypeCode type_code =
372 stack_.back().typetable->type_codes[field_index];
373
374 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800375 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700376 return false;
377 }
378
379 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700380 stack_.back().vector_elements.emplace_back(double_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700381 } else {
382 stack_.back().elements.emplace_back(field_index, double_value);
383 }
384 return true;
385}
386
387bool JsonParser::AddElement(int field_index, const ::std::string &data) {
388 flatbuffers::TypeCode type_code =
389 stack_.back().typetable->type_codes[field_index];
390
391 if (type_code.is_vector != in_vector()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800392 fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700393 return false;
394 }
395
Alex Perrycb7da4b2019-08-28 19:35:56 -0700396 const flatbuffers::ElementaryType elementary_type =
397 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
398 switch (elementary_type) {
399 case flatbuffers::ET_CHAR:
400 case flatbuffers::ET_UCHAR:
401 case flatbuffers::ET_SHORT:
402 case flatbuffers::ET_USHORT:
403 case flatbuffers::ET_INT:
404 case flatbuffers::ET_UINT:
405 case flatbuffers::ET_LONG:
406 case flatbuffers::ET_ULONG:
407 if (type_code.sequence_ref != -1) {
408 // We have an enum.
409 const flatbuffers::TypeTable *type_table = stack_.back().typetable;
410 flatbuffers::TypeFunction type_function =
411 type_table->type_refs[type_code.sequence_ref];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700412
Alex Perrycb7da4b2019-08-28 19:35:56 -0700413 const flatbuffers::TypeTable *enum_type_table = type_function();
414
415 CHECK_EQ(enum_type_table->st, flatbuffers::ST_ENUM);
416
417 int64_t int_value = 0;
418 bool found = false;
419 for (size_t i = 0; i < enum_type_table->num_elems; ++i) {
420 if (data == enum_type_table->names[i]) {
421 int_value = i;
422 found = true;
423 break;
424 }
425 }
426
427 if (!found) {
Austin Schuh217a9782019-12-21 23:02:50 -0800428 fprintf(stderr, "Enum value '%s' not found for field '%s'\n",
429 data.c_str(), type_table->names[field_index]);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700430 return false;
431 }
432
433 if (in_vector()) {
434 stack_.back().vector_elements.emplace_back(int_value);
435 } else {
436 stack_.back().elements.emplace_back(field_index, int_value);
437 }
438 return true;
439 }
440 case flatbuffers::ET_UTYPE:
441 case flatbuffers::ET_BOOL:
442 case flatbuffers::ET_FLOAT:
443 case flatbuffers::ET_DOUBLE:
444 case flatbuffers::ET_STRING:
445 case flatbuffers::ET_SEQUENCE:
446 break;
447 }
448
449 if (in_vector()) {
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800450 stack_.back().vector_elements.emplace_back(fbb_->CreateString(data));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700451 } else {
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800452 stack_.back().elements.emplace_back(field_index, fbb_->CreateString(data));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700453 }
454 return true;
455}
456
Austin Schuh43c6a352019-09-30 22:22:10 -0700457bool AddSingleElement(const flatbuffers::TypeTable *typetable,
458 const FieldElement &field_element,
459 ::std::vector<bool> *fields_in_use,
460 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700461 if ((*fields_in_use)[field_element.field_index]) {
Austin Schuh217a9782019-12-21 23:02:50 -0800462 fprintf(stderr, "Duplicate field: '%s'\n",
463 typetable->names[field_element.field_index]);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700464 return false;
465 }
466
467 (*fields_in_use)[field_element.field_index] = true;
468
469 switch (field_element.element.type) {
470 case Element::ElementType::INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700471 return AddSingleElement(typetable, field_element.field_index,
472 field_element.element.int_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700473 case Element::ElementType::DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700474 return AddSingleElement(typetable, field_element.field_index,
475 field_element.element.double_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700476 case Element::ElementType::OFFSET:
Austin Schuh43c6a352019-09-30 22:22:10 -0700477 return AddSingleElement(typetable, field_element.field_index,
478 field_element.element.offset_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700479 }
480 return false;
481}
482
Austin Schuh43c6a352019-09-30 22:22:10 -0700483bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
484 int64_t int_value, flatbuffers::FlatBufferBuilder *fbb
485
486) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700487 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
488 static_cast<flatbuffers::voffset_t>(field_index));
489
Austin Schuh43c6a352019-09-30 22:22:10 -0700490 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700491
492 const flatbuffers::ElementaryType elementary_type =
493 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
494 switch (elementary_type) {
495 case flatbuffers::ET_BOOL:
Austin Schuh43c6a352019-09-30 22:22:10 -0700496 fbb->AddElement<bool>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700497 return true;
498 case flatbuffers::ET_CHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700499 fbb->AddElement<int8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700500 return true;
501 case flatbuffers::ET_UCHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700502 fbb->AddElement<uint8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700503 return true;
504 case flatbuffers::ET_SHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700505 fbb->AddElement<int16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700506 return true;
507 case flatbuffers::ET_USHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700508 fbb->AddElement<uint16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700509 return true;
510 case flatbuffers::ET_INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700511 fbb->AddElement<int32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700512 return true;
513 case flatbuffers::ET_UINT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700514 fbb->AddElement<uint32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700515 return true;
516 case flatbuffers::ET_LONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700517 fbb->AddElement<int64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700518 return true;
519 case flatbuffers::ET_ULONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700520 fbb->AddElement<uint64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700521 return true;
522 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700523 fbb->AddElement<float>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700524 return true;
525 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700526 fbb->AddElement<double>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700527 return true;
528 case flatbuffers::ET_STRING:
529 case flatbuffers::ET_UTYPE:
530 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800531 fprintf(
532 stderr, "Mismatched type for field '%s'. Got: integer, expected %s\n",
533 typetable->names[field_index], ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700534 return false;
535 };
536 return false;
537}
538
Austin Schuh43c6a352019-09-30 22:22:10 -0700539bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
540 double double_value,
541 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700542 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
543 static_cast<flatbuffers::voffset_t>(field_index));
544
Austin Schuh43c6a352019-09-30 22:22:10 -0700545 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700546
547 const flatbuffers::ElementaryType elementary_type =
548 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
549 switch (elementary_type) {
550 case flatbuffers::ET_UTYPE:
551 case flatbuffers::ET_BOOL:
552 case flatbuffers::ET_CHAR:
553 case flatbuffers::ET_UCHAR:
554 case flatbuffers::ET_SHORT:
555 case flatbuffers::ET_USHORT:
556 case flatbuffers::ET_INT:
557 case flatbuffers::ET_UINT:
558 case flatbuffers::ET_LONG:
559 case flatbuffers::ET_ULONG:
560 case flatbuffers::ET_STRING:
561 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800562 fprintf(
563 stderr, "Mismatched type for field '%s'. Got: double, expected %s\n",
564 typetable->names[field_index], ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700565 return false;
566 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700567 fbb->AddElement<float>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700568 return true;
569 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700570 fbb->AddElement<double>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700571 return true;
572 }
573 return false;
574}
Austin Schuh43c6a352019-09-30 22:22:10 -0700575bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
576 flatbuffers::Offset<flatbuffers::String> offset_element,
577 flatbuffers::FlatBufferBuilder *fbb) {
578 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700579
580 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
581 static_cast<flatbuffers::voffset_t>(field_index));
582
583 // Vectors will always be Offset<>'s.
584 if (type_code.is_vector) {
Austin Schuh43c6a352019-09-30 22:22:10 -0700585 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700586 return true;
587 }
588
589 const flatbuffers::ElementaryType elementary_type =
590 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
591 switch (elementary_type) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700592 case flatbuffers::ET_CHAR:
593 case flatbuffers::ET_UCHAR:
594 case flatbuffers::ET_SHORT:
595 case flatbuffers::ET_USHORT:
596 case flatbuffers::ET_INT:
597 case flatbuffers::ET_UINT:
598 case flatbuffers::ET_LONG:
599 case flatbuffers::ET_ULONG:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700600 case flatbuffers::ET_UTYPE:
601 case flatbuffers::ET_BOOL:
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700602 case flatbuffers::ET_FLOAT:
603 case flatbuffers::ET_DOUBLE:
Austin Schuh217a9782019-12-21 23:02:50 -0800604 fprintf(
605 stderr, "Mismatched type for field '%s'. Got: string, expected %s\n",
606 typetable->names[field_index], ElementaryTypeName(elementary_type));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700607 CHECK_EQ(type_code.sequence_ref, -1)
608 << ": Field name " << typetable->names[field_index]
609 << " Got string expected " << ElementaryTypeName(elementary_type);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700610 return false;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700611 case flatbuffers::ET_STRING:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700612 CHECK_EQ(type_code.sequence_ref, -1);
613 case flatbuffers::ET_SEQUENCE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700614 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700615 return true;
616 }
617 return false;
618}
619
620bool JsonParser::FinishVector(int field_index) {
621 flatbuffers::TypeCode type_code =
622 stack_.back().typetable->type_codes[field_index];
623
624 const flatbuffers::ElementaryType elementary_type =
625 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
626
627 // Vectors have a start (unfortunately which needs to know the size)
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800628 fbb_->StartVector(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700629 stack_.back().vector_elements.size(),
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700630 flatbuffers::InlineSize(elementary_type, stack_.back().typetable));
631
632 // Then the data (in reverse order for some reason...)
Alex Perrycb7da4b2019-08-28 19:35:56 -0700633 for (size_t i = stack_.back().vector_elements.size(); i > 0;) {
634 const Element &element = stack_.back().vector_elements[--i];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700635 switch (element.type) {
636 case Element::ElementType::INT:
637 if (!PushElement(elementary_type, element.int_element)) return false;
638 break;
639 case Element::ElementType::DOUBLE:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700640 CHECK_EQ(type_code.sequence_ref, -1)
641 << ": Field index is " << field_index;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700642 if (!PushElement(elementary_type, element.double_element)) return false;
643 break;
644 case Element::ElementType::OFFSET:
645 if (!PushElement(elementary_type, element.offset_element)) return false;
646 break;
647 }
648 }
649
650 // Then an End which is placed into the buffer the same as any other offset.
651 stack_.back().elements.emplace_back(
652 field_index, flatbuffers::Offset<flatbuffers::String>(
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800653 fbb_->EndVector(stack_.back().vector_elements.size())));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700654 stack_.back().vector_elements.clear();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700655 return true;
656}
657
658bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
659 int64_t int_value) {
660 switch (elementary_type) {
661 case flatbuffers::ET_BOOL:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800662 fbb_->PushElement<bool>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700663 return true;
664 case flatbuffers::ET_CHAR:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800665 fbb_->PushElement<int8_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700666 return true;
667 case flatbuffers::ET_UCHAR:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800668 fbb_->PushElement<uint8_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700669 return true;
670 case flatbuffers::ET_SHORT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800671 fbb_->PushElement<int16_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700672 return true;
673 case flatbuffers::ET_USHORT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800674 fbb_->PushElement<uint16_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700675 return true;
676 case flatbuffers::ET_INT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800677 fbb_->PushElement<int32_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700678 return true;
679 case flatbuffers::ET_UINT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800680 fbb_->PushElement<uint32_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700681 return true;
682 case flatbuffers::ET_LONG:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800683 fbb_->PushElement<int64_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700684 return true;
685 case flatbuffers::ET_ULONG:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800686 fbb_->PushElement<uint64_t>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700687 return true;
688 case flatbuffers::ET_FLOAT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800689 fbb_->PushElement<float>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700690 return true;
691 case flatbuffers::ET_DOUBLE:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800692 fbb_->PushElement<double>(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700693 return true;
694 case flatbuffers::ET_STRING:
695 case flatbuffers::ET_UTYPE:
696 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800697 fprintf(stderr,
698 "Mismatched type for field '%s'. Got: integer, expected %s\n",
699 stack_.back().field_name.c_str(),
700 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700701 return false;
702 };
703 return false;
704}
705
706bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
707 double double_value) {
708 switch (elementary_type) {
709 case flatbuffers::ET_UTYPE:
710 case flatbuffers::ET_BOOL:
711 case flatbuffers::ET_CHAR:
712 case flatbuffers::ET_UCHAR:
713 case flatbuffers::ET_SHORT:
714 case flatbuffers::ET_USHORT:
715 case flatbuffers::ET_INT:
716 case flatbuffers::ET_UINT:
717 case flatbuffers::ET_LONG:
718 case flatbuffers::ET_ULONG:
719 case flatbuffers::ET_STRING:
720 case flatbuffers::ET_SEQUENCE:
Austin Schuh217a9782019-12-21 23:02:50 -0800721 fprintf(stderr,
722 "Mismatched type for field '%s'. Got: double, expected %s\n",
723 stack_.back().field_name.c_str(),
724 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700725 return false;
726 case flatbuffers::ET_FLOAT:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800727 fbb_->PushElement<float>(double_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700728 return true;
729 case flatbuffers::ET_DOUBLE:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800730 fbb_->PushElement<double>(double_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700731 return true;
732 }
733 return false;
734}
735
736bool JsonParser::PushElement(
737 flatbuffers::ElementaryType elementary_type,
738 flatbuffers::Offset<flatbuffers::String> offset_value) {
739 switch (elementary_type) {
740 case flatbuffers::ET_UTYPE:
741 case flatbuffers::ET_BOOL:
742 case flatbuffers::ET_CHAR:
743 case flatbuffers::ET_UCHAR:
744 case flatbuffers::ET_SHORT:
745 case flatbuffers::ET_USHORT:
746 case flatbuffers::ET_INT:
747 case flatbuffers::ET_UINT:
748 case flatbuffers::ET_LONG:
749 case flatbuffers::ET_ULONG:
750 case flatbuffers::ET_FLOAT:
751 case flatbuffers::ET_DOUBLE:
Austin Schuh217a9782019-12-21 23:02:50 -0800752 fprintf(stderr,
753 "Mismatched type for field '%s'. Got: sequence, expected %s\n",
754 stack_.back().field_name.c_str(),
755 ElementaryTypeName(elementary_type));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700756 return false;
757 case flatbuffers::ET_STRING:
758 case flatbuffers::ET_SEQUENCE:
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800759 fbb_->PushElement(offset_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700760 return true;
761 }
762 return false;
763}
764
765} // namespace
766
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800767flatbuffers::Offset<flatbuffers::Table> JsonToFlatbuffer(
768 const std::string_view data, const flatbuffers::TypeTable *typetable,
769 flatbuffers::FlatBufferBuilder *fbb) {
770 JsonParser p(fbb);
771 return p.Parse(data, typetable);
772}
773
Austin Schuhe93d8642019-10-13 15:27:07 -0700774flatbuffers::DetachedBuffer JsonToFlatbuffer(
Austin Schuhd3936202020-04-07 20:11:07 -0700775 const std::string_view data, const flatbuffers::TypeTable *typetable) {
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800776 flatbuffers::FlatBufferBuilder fbb;
Austin Schuhd7b15da2020-02-17 15:06:11 -0800777 fbb.ForceDefaults(true);
Austin Schuh53b1a6f2020-01-10 19:31:28 -0800778
779 const flatbuffers::Offset<flatbuffers::Table> result =
780 JsonToFlatbuffer(data, typetable, &fbb);
781 if (result.o != 0) {
782 fbb.Finish(result);
783
784 return fbb.Release();
785 } else {
786 // Otherwise return an empty vector.
787 return flatbuffers::DetachedBuffer();
788 }
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700789}
790
Austin Schuhe93d8642019-10-13 15:27:07 -0700791::std::string BufferFlatbufferToJson(const uint8_t *buffer,
792 const ::flatbuffers::TypeTable *typetable,
Austin Schuhd3936202020-04-07 20:11:07 -0700793 bool multi_line, size_t max_vector_size) {
Austin Schuhe93d8642019-10-13 15:27:07 -0700794 // It is pretty common to get passed in a nullptr when a test fails. Rather
795 // than CHECK, return a more user friendly result.
796 if (buffer == nullptr) {
797 return "null";
798 }
799 return TableFlatbufferToJson(reinterpret_cast<const flatbuffers::Table *>(
800 flatbuffers::GetRoot<uint8_t>(buffer)),
Austin Schuhd3936202020-04-07 20:11:07 -0700801 typetable, multi_line, max_vector_size);
Austin Schuhe93d8642019-10-13 15:27:07 -0700802}
803
Austin Schuhd3936202020-04-07 20:11:07 -0700804namespace {
805
806// A visitor which manages skipping the contents of vectors that are longer than
807// a specified threshold.
808class TruncatingStringVisitor : public flatbuffers::IterationVisitor {
809 public:
810 TruncatingStringVisitor(size_t max_vector_size, std::string delimiter,
811 bool quotes, std::string indent, bool vdelimited)
812 : max_vector_size_(max_vector_size),
813 to_string_(delimiter, quotes, indent, vdelimited) {}
814 ~TruncatingStringVisitor() override {}
815
816 void StartSequence() override {
817 if (should_skip()) return;
818 to_string_.StartSequence();
819 }
820 void EndSequence() override {
821 if (should_skip()) return;
822 to_string_.EndSequence();
823 }
824 void Field(size_t field_idx, size_t set_idx, flatbuffers::ElementaryType type,
825 bool is_vector, const flatbuffers::TypeTable *type_table,
826 const char *name, const uint8_t *val) override {
827 if (should_skip()) return;
828 to_string_.Field(field_idx, set_idx, type, is_vector, type_table, name,
829 val);
830 }
831 void UType(uint8_t value, const char *name) override {
832 if (should_skip()) return;
833 to_string_.UType(value, name);
834 }
835 void Bool(bool value) override {
836 if (should_skip()) return;
837 to_string_.Bool(value);
838 }
839 void Char(int8_t value, const char *name) override {
840 if (should_skip()) return;
841 to_string_.Char(value, name);
842 }
843 void UChar(uint8_t value, const char *name) override {
844 if (should_skip()) return;
845 to_string_.UChar(value, name);
846 }
847 void Short(int16_t value, const char *name) override {
848 if (should_skip()) return;
849 to_string_.Short(value, name);
850 }
851 void UShort(uint16_t value, const char *name) override {
852 if (should_skip()) return;
853 to_string_.UShort(value, name);
854 }
855 void Int(int32_t value, const char *name) override {
856 if (should_skip()) return;
857 to_string_.Int(value, name);
858 }
859 void UInt(uint32_t value, const char *name) override {
860 if (should_skip()) return;
861 to_string_.UInt(value, name);
862 }
863 void Long(int64_t value) override {
864 if (should_skip()) return;
865 to_string_.Long(value);
866 }
867 void ULong(uint64_t value) override {
868 if (should_skip()) return;
869 to_string_.ULong(value);
870 }
871 void Float(float value) override {
872 if (should_skip()) return;
873 to_string_.Float(value);
874 }
875 void Double(double value) override {
876 if (should_skip()) return;
877 to_string_.Double(value);
878 }
879 void String(const flatbuffers::String *value) override {
880 if (should_skip()) return;
881 to_string_.String(value);
882 }
883 void Unknown(const uint8_t *value) override {
884 if (should_skip()) return;
885 to_string_.Unknown(value);
886 }
887 void Element(size_t i, flatbuffers::ElementaryType type,
888 const flatbuffers::TypeTable *type_table,
889 const uint8_t *val) override {
890 if (should_skip()) return;
891 to_string_.Element(i, type, type_table, val);
892 }
893
894 virtual void StartVector(size_t size) override {
895 if (should_skip()) {
896 ++skip_levels_;
897 return;
898 }
899 if (size > max_vector_size_) {
900 ++skip_levels_;
901 to_string_.s += "[ ... " + std::to_string(size) + " elements ... ]";
902 return;
903 }
904 to_string_.StartVector(size);
905 }
906 virtual void EndVector() override {
907 if (should_skip()) {
908 --skip_levels_;
909 return;
910 }
911 to_string_.EndVector();
912 }
913
914 std::string &string() { return to_string_.s; }
915
916 private:
917 bool should_skip() const { return skip_levels_ > 0; }
918
919 const size_t max_vector_size_;
920 flatbuffers::ToStringVisitor to_string_;
921 int skip_levels_ = 0;
922};
923
924} // namespace
925
Austin Schuhe93d8642019-10-13 15:27:07 -0700926::std::string TableFlatbufferToJson(const flatbuffers::Table *t,
927 const ::flatbuffers::TypeTable *typetable,
Austin Schuhd3936202020-04-07 20:11:07 -0700928 bool multi_line, size_t max_vector_size) {
Austin Schuhe93d8642019-10-13 15:27:07 -0700929 // It is pretty common to get passed in a nullptr when a test fails. Rather
930 // than CHECK, return a more user friendly result.
931 if (t == nullptr) {
932 return "null";
933 }
Austin Schuhd3936202020-04-07 20:11:07 -0700934 TruncatingStringVisitor tostring_visitor(max_vector_size,
935 multi_line ? "\n" : " ", true,
936 multi_line ? " " : "", multi_line);
Austin Schuhe93d8642019-10-13 15:27:07 -0700937 flatbuffers::IterateObject(reinterpret_cast<const uint8_t *>(t), typetable,
938 &tostring_visitor);
Austin Schuhd3936202020-04-07 20:11:07 -0700939 return tostring_visitor.string();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700940}
941
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700942} // namespace aos