blob: 34cdffecc5112f127b91ceab17a3c5d0a579a17f [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
Austin Schuhd339a9b2019-10-05 21:33:32 -07006#include "absl/strings/string_view.h"
Austin Schuh43c6a352019-09-30 22:22:10 -07007#include "aos/flatbuffer_utils.h"
Austin Schuhd7e252d2019-10-06 13:51:02 -07008#include "aos/json_tokenizer.h"
Austin Schuh3e95e5d2019-09-20 00:08:54 -07009#include "flatbuffers/flatbuffers.h"
10#include "flatbuffers/minireflect.h"
Austin Schuhe93d8642019-10-13 15:27:07 -070011#include "glog/logging.h"
Austin Schuh3e95e5d2019-09-20 00:08:54 -070012
13// TODO(austin): Can we just do an Offset<void> ? It doesn't matter, so maybe
14// just say that.
15//
16// TODO(austin): I've yet to see how to create an ET_UTYPE, so I don't know what
17// one is and how to test it. So everything rejects it.
18
19namespace aos {
20
21// Finds the field index in the table given the name.
22int FieldIndex(const flatbuffers::TypeTable *typetable,
23 const char *field_name) {
24 CHECK(typetable->values == nullptr);
25 for (size_t i = 0; i < typetable->num_elems; ++i) {
26 if (strcmp(field_name, typetable->names[i]) == 0) {
27 return i;
28 }
29 }
30 return -1;
31}
32
33namespace {
34
35// Class to hold one of the 3 json types for an array.
36struct Element {
37 // The type.
38 enum class ElementType { INT, DOUBLE, OFFSET };
39
40 // Constructs an Element holding an integer.
41 Element(int64_t new_int_element)
42 : int_element(new_int_element), type(ElementType::INT) {}
43 // Constructs an Element holding an double.
44 Element(double new_double_element)
45 : double_element(new_double_element), type(ElementType::DOUBLE) {}
46 // Constructs an Element holding an Offset.
47 Element(flatbuffers::Offset<flatbuffers::String> new_offset_element)
48 : offset_element(new_offset_element), type(ElementType::OFFSET) {}
49
50 // Union for the various datatypes.
51 union {
52 int64_t int_element;
53 double double_element;
54 flatbuffers::Offset<flatbuffers::String> offset_element;
55 };
56
57 // And an enum signaling which one is in use.
58 ElementType type;
59};
60
61// Structure to represent a field element.
62struct FieldElement {
63 FieldElement(int new_field_index, int64_t int_element)
64 : element(int_element), field_index(new_field_index) {}
65 FieldElement(int new_field_index, double double_element)
66 : element(double_element), field_index(new_field_index) {}
67 FieldElement(int new_field_index,
68 flatbuffers::Offset<flatbuffers::String> offset_element)
69 : element(offset_element), field_index(new_field_index) {}
70
71 // Data to write.
72 Element element;
73 // Field index. The type table which this index is for is stored outside this
74 // object.
75 int field_index;
76};
77
Austin Schuh43c6a352019-09-30 22:22:10 -070078// Adds a single element. This assumes that vectors have been dealt with
79// already. Returns true on success.
80bool AddSingleElement(const flatbuffers::TypeTable *typetable,
81 const FieldElement &field_element,
82 ::std::vector<bool> *fields_in_use,
83 flatbuffers::FlatBufferBuilder *fbb);
84bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
85 int64_t int_value, flatbuffers::FlatBufferBuilder *fbb);
86bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
87 double double_value, flatbuffers::FlatBufferBuilder *fbb);
88bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
89 flatbuffers::Offset<flatbuffers::String> offset_element,
90 flatbuffers::FlatBufferBuilder *fbb);
91
92
93// 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:
122 JsonParser() { fbb_.ForceDefaults(1); }
123 ~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 Schuhe93d8642019-10-13 15:27:07 -0700127 flatbuffers::DetachedBuffer Parse(const absl::string_view data,
128 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.
134 auto o = flatbuffers::Offset<flatbuffers::Table>(end);
135 fbb_.Finish(o);
136
Austin Schuhe93d8642019-10-13 15:27:07 -0700137 return fbb_.Release();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700138 } else {
139 // Otherwise return an empty vector.
Austin Schuhe93d8642019-10-13 15:27:07 -0700140 return flatbuffers::DetachedBuffer();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700141 }
142 }
143
144 private:
145 // Setters and getters for in_vector (at the current level of the stack)
146 bool in_vector() const { return stack_.back().in_vector; }
147 void set_in_vector(bool in_vector) { stack_.back().in_vector = in_vector; }
148
149 // Parses the flatbuffer. This is a second method so we can do easier
150 // cleanup at the top level. Returns true on success.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700151 bool DoParse(const flatbuffers::TypeTable *typetable,
152 const absl::string_view data, flatbuffers::uoffset_t *table_end);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700153
154 // Adds *_value for the provided field. If we are in a vector, queues the
Alex Perrycb7da4b2019-08-28 19:35:56 -0700155 // data up in vector_elements. Returns true on success.
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700156 bool AddElement(int field_index, int64_t int_value);
157 bool AddElement(int field_index, double double_value);
158 bool AddElement(int field_index, const ::std::string &data);
159
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700160 // Finishes a vector for the provided field index. Returns true on success.
161 bool FinishVector(int field_index);
162
163 // Pushes an element as part of a vector. Returns true on success.
164 bool PushElement(flatbuffers::ElementaryType elementary_type,
165 int64_t int_value);
166 bool PushElement(flatbuffers::ElementaryType elementary_type,
167 double double_value);
168 bool PushElement(flatbuffers::ElementaryType elementary_type,
169 flatbuffers::Offset<flatbuffers::String> offset_value);
170
171 flatbuffers::FlatBufferBuilder fbb_;
172
173 // This holds the state information that is needed as you recurse into
174 // nested structures.
175 struct FlatBufferContext {
176 // Type of the current type.
177 const flatbuffers::TypeTable *typetable;
178 // If true, we are parsing a vector.
179 bool in_vector;
180 // The field index of the current field.
181 int field_index;
182 // Name of the current field.
183 ::std::string field_name;
184
185 // Field elements that need to be inserted.
186 ::std::vector<FieldElement> elements;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700187
188 // For scalar types (not strings, and not nested tables), the vector ends
189 // up being implemented as a start and end, and a block of data. So we
190 // can't just push offsets in as we go. We either need to reproduce the
191 // logic inside flatbuffers, or build up vectors of the data. Vectors will
192 // be a bit of extra stack space, but whatever.
193 //
194 // Strings and nested structures are vectors of offsets.
195 // into the vector. Once you get to the end, you build up a vector and
196 // push that into the field.
197 ::std::vector<Element> vector_elements;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700198 };
199 ::std::vector<FlatBufferContext> stack_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700200};
201
202bool JsonParser::DoParse(const flatbuffers::TypeTable *typetable,
Austin Schuhd339a9b2019-10-05 21:33:32 -0700203 const absl::string_view data,
204 flatbuffers::uoffset_t *table_end) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700205 ::std::vector<const flatbuffers::TypeTable *> stack;
206
207 Tokenizer t(data);
208
209 // Main loop. Run until we get an end.
210 while (true) {
211 Tokenizer::TokenType token = t.Next();
212
213 switch (token) {
214 case Tokenizer::TokenType::kEnd:
215 if (stack_.size() != 0) {
216 printf("Failed to unwind stack all the way\n");
217 return false;
218 } else {
219 return true;
220 }
221 break;
222 case Tokenizer::TokenType::kError:
223 return false;
224 break;
225
226 case Tokenizer::TokenType::kStartObject: // {
227 if (stack_.size() == 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700228 stack_.push_back({typetable, false, -1, "", {}, {}});
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700229 } else {
230 int field_index = stack_.back().field_index;
231
232 const flatbuffers::TypeCode &type_code =
233 stack_.back().typetable->type_codes[field_index];
234
235 if (type_code.base_type != flatbuffers::ET_SEQUENCE) {
236 printf("Field '%s' is not a sequence\n",
237 stack_.back().field_name.c_str());
238 return false;
239 }
240
241 flatbuffers::TypeFunction type_function =
242 stack_.back().typetable->type_refs[type_code.sequence_ref];
243
Alex Perrycb7da4b2019-08-28 19:35:56 -0700244 stack_.push_back({type_function(), false, -1, "", {}, {}});
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700245 }
246 break;
247 case Tokenizer::TokenType::kEndObject: // }
248 if (stack_.size() == 0) {
249 // Somehow we popped more than we pushed. Error.
250 printf("Empty stack\n");
251 return false;
252 } else {
253 // End of a nested struct! Add it.
Austin Schuh43c6a352019-09-30 22:22:10 -0700254 const flatbuffers::uoffset_t end = WriteTable(
255 stack_.back().typetable, stack_.back().elements, &fbb_);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700256
257 // We now want to talk about the parent structure. Pop the child.
258 stack_.pop_back();
259
260 if (stack_.size() == 0) {
261 // Instead of queueing it up in the stack, return it through the
262 // passed in variable.
263 *table_end = end;
264 } else {
265 // And now we can add it.
266 const int field_index = stack_.back().field_index;
267
268 // Do the right thing if we are in a vector.
269 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700270 stack_.back().vector_elements.emplace_back(
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700271 flatbuffers::Offset<flatbuffers::String>(end));
272 } else {
273 stack_.back().elements.emplace_back(
274 field_index, flatbuffers::Offset<flatbuffers::String>(end));
275 }
276 }
277 }
278 break;
279
280 case Tokenizer::TokenType::kStartArray: // [
281 if (stack_.size() == 0) {
282 // We don't support an array of structs at the root level.
283 return false;
284 }
285 // Sanity check that we aren't trying to make a vector of vectors.
286 if (in_vector()) {
287 return false;
288 }
289 set_in_vector(true);
290
291 break;
292 case Tokenizer::TokenType::kEndArray: { // ]
293 if (!in_vector()) {
294 return false;
295 }
296
297 const int field_index = stack_.back().field_index;
298
299 if (!FinishVector(field_index)) return false;
300
301 set_in_vector(false);
302 } break;
303
304 case Tokenizer::TokenType::kTrueValue: // true
305 case Tokenizer::TokenType::kFalseValue: // false
306 case Tokenizer::TokenType::kNumberValue: {
307 bool is_int = true;
308 double double_value;
309 long long int_value;
310 if (token == Tokenizer::TokenType::kTrueValue) {
311 int_value = 1;
312 } else if (token == Tokenizer::TokenType::kFalseValue) {
313 int_value = 0;
314 } else if (!t.FieldAsInt(&int_value)) {
315 if (t.FieldAsDouble(&double_value)) {
316 is_int = false;
317 } else {
318 fprintf(stderr, "Got a invalid number '%s'\n",
319 t.field_value().c_str());
320 return false;
321 }
322 }
323
324 const int field_index = stack_.back().field_index;
325
326 if (is_int) {
327 // No need to get too stressed about bool vs int. Convert them all.
328 int64_t val = int_value;
329 if (!AddElement(field_index, val)) return false;
330 } else {
331 if (!AddElement(field_index, double_value)) return false;
332 }
333 } break;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700334 case Tokenizer::TokenType::kStringValue: // string value
335 {
336 const int field_index = stack_.back().field_index;
337
338 if (!AddElement(field_index, t.field_value())) return false;
339 } break;
340 case Tokenizer::TokenType::kField: // field name
341 {
342 stack_.back().field_name = t.field_name();
343 stack_.back().field_index = FieldIndex(
344 stack_.back().typetable, stack_.back().field_name.c_str());
345
346 if (stack_.back().field_index == -1) {
347 printf("Invalid field name '%s'\n", stack_.back().field_name.c_str());
348 return false;
349 }
350 } break;
351 }
352 }
353 return false;
354}
355
356bool JsonParser::AddElement(int field_index, int64_t int_value) {
357 flatbuffers::TypeCode type_code =
358 stack_.back().typetable->type_codes[field_index];
359
360 if (type_code.is_vector != in_vector()) {
361 printf("Type and json disagree on if we are in a vector or not\n");
362 return false;
363 }
364
365 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700366 stack_.back().vector_elements.emplace_back(int_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700367 } else {
368 stack_.back().elements.emplace_back(field_index, int_value);
369 }
370 return true;
371}
372
373bool JsonParser::AddElement(int field_index, double double_value) {
374 flatbuffers::TypeCode type_code =
375 stack_.back().typetable->type_codes[field_index];
376
377 if (type_code.is_vector != in_vector()) {
378 printf("Type and json disagree on if we are in a vector or not\n");
379 return false;
380 }
381
382 if (in_vector()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700383 stack_.back().vector_elements.emplace_back(double_value);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700384 } else {
385 stack_.back().elements.emplace_back(field_index, double_value);
386 }
387 return true;
388}
389
390bool JsonParser::AddElement(int field_index, const ::std::string &data) {
391 flatbuffers::TypeCode type_code =
392 stack_.back().typetable->type_codes[field_index];
393
394 if (type_code.is_vector != in_vector()) {
395 printf("Type and json disagree on if we are in a vector or not\n");
396 return false;
397 }
398
Alex Perrycb7da4b2019-08-28 19:35:56 -0700399 const flatbuffers::ElementaryType elementary_type =
400 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
401 switch (elementary_type) {
402 case flatbuffers::ET_CHAR:
403 case flatbuffers::ET_UCHAR:
404 case flatbuffers::ET_SHORT:
405 case flatbuffers::ET_USHORT:
406 case flatbuffers::ET_INT:
407 case flatbuffers::ET_UINT:
408 case flatbuffers::ET_LONG:
409 case flatbuffers::ET_ULONG:
410 if (type_code.sequence_ref != -1) {
411 // We have an enum.
412 const flatbuffers::TypeTable *type_table = stack_.back().typetable;
413 flatbuffers::TypeFunction type_function =
414 type_table->type_refs[type_code.sequence_ref];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700415
Alex Perrycb7da4b2019-08-28 19:35:56 -0700416 const flatbuffers::TypeTable *enum_type_table = type_function();
417
418 CHECK_EQ(enum_type_table->st, flatbuffers::ST_ENUM);
419
420 int64_t int_value = 0;
421 bool found = false;
422 for (size_t i = 0; i < enum_type_table->num_elems; ++i) {
423 if (data == enum_type_table->names[i]) {
424 int_value = i;
425 found = true;
426 break;
427 }
428 }
429
430 if (!found) {
431 printf("Enum value '%s' not found for field '%s'\n", data.c_str(),
432 type_table->names[field_index]);
433 return false;
434 }
435
436 if (in_vector()) {
437 stack_.back().vector_elements.emplace_back(int_value);
438 } else {
439 stack_.back().elements.emplace_back(field_index, int_value);
440 }
441 return true;
442 }
443 case flatbuffers::ET_UTYPE:
444 case flatbuffers::ET_BOOL:
445 case flatbuffers::ET_FLOAT:
446 case flatbuffers::ET_DOUBLE:
447 case flatbuffers::ET_STRING:
448 case flatbuffers::ET_SEQUENCE:
449 break;
450 }
451
452 if (in_vector()) {
453 stack_.back().vector_elements.emplace_back(fbb_.CreateString(data));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700454 } else {
455 stack_.back().elements.emplace_back(field_index, fbb_.CreateString(data));
456 }
457 return true;
458}
459
Austin Schuh43c6a352019-09-30 22:22:10 -0700460bool AddSingleElement(const flatbuffers::TypeTable *typetable,
461 const FieldElement &field_element,
462 ::std::vector<bool> *fields_in_use,
463 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700464 if ((*fields_in_use)[field_element.field_index]) {
465 printf("Duplicate field: '%s'\n",
Austin Schuh43c6a352019-09-30 22:22:10 -0700466 typetable->names[field_element.field_index]);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700467 return false;
468 }
469
470 (*fields_in_use)[field_element.field_index] = true;
471
472 switch (field_element.element.type) {
473 case Element::ElementType::INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700474 return AddSingleElement(typetable, field_element.field_index,
475 field_element.element.int_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700476 case Element::ElementType::DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700477 return AddSingleElement(typetable, field_element.field_index,
478 field_element.element.double_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700479 case Element::ElementType::OFFSET:
Austin Schuh43c6a352019-09-30 22:22:10 -0700480 return AddSingleElement(typetable, field_element.field_index,
481 field_element.element.offset_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700482 }
483 return false;
484}
485
Austin Schuh43c6a352019-09-30 22:22:10 -0700486bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
487 int64_t int_value, flatbuffers::FlatBufferBuilder *fbb
488
489) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700490 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
491 static_cast<flatbuffers::voffset_t>(field_index));
492
Austin Schuh43c6a352019-09-30 22:22:10 -0700493 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700494
495 const flatbuffers::ElementaryType elementary_type =
496 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
497 switch (elementary_type) {
498 case flatbuffers::ET_BOOL:
Austin Schuh43c6a352019-09-30 22:22:10 -0700499 fbb->AddElement<bool>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700500 return true;
501 case flatbuffers::ET_CHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700502 fbb->AddElement<int8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700503 return true;
504 case flatbuffers::ET_UCHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700505 fbb->AddElement<uint8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700506 return true;
507 case flatbuffers::ET_SHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700508 fbb->AddElement<int16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700509 return true;
510 case flatbuffers::ET_USHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700511 fbb->AddElement<uint16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700512 return true;
513 case flatbuffers::ET_INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700514 fbb->AddElement<int32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700515 return true;
516 case flatbuffers::ET_UINT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700517 fbb->AddElement<uint32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700518 return true;
519 case flatbuffers::ET_LONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700520 fbb->AddElement<int64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700521 return true;
522 case flatbuffers::ET_ULONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700523 fbb->AddElement<uint64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700524 return true;
525 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700526 fbb->AddElement<float>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700527 return true;
528 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700529 fbb->AddElement<double>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700530 return true;
531 case flatbuffers::ET_STRING:
532 case flatbuffers::ET_UTYPE:
533 case flatbuffers::ET_SEQUENCE:
534 printf("Mismatched type for field '%s'. Got: integer, expected %s\n",
Austin Schuh43c6a352019-09-30 22:22:10 -0700535 typetable->names[field_index],
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700536 ElementaryTypeName(elementary_type));
537 return false;
538 };
539 return false;
540}
541
Austin Schuh43c6a352019-09-30 22:22:10 -0700542bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
543 double double_value,
544 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700545 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
546 static_cast<flatbuffers::voffset_t>(field_index));
547
Austin Schuh43c6a352019-09-30 22:22:10 -0700548 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700549
550 const flatbuffers::ElementaryType elementary_type =
551 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
552 switch (elementary_type) {
553 case flatbuffers::ET_UTYPE:
554 case flatbuffers::ET_BOOL:
555 case flatbuffers::ET_CHAR:
556 case flatbuffers::ET_UCHAR:
557 case flatbuffers::ET_SHORT:
558 case flatbuffers::ET_USHORT:
559 case flatbuffers::ET_INT:
560 case flatbuffers::ET_UINT:
561 case flatbuffers::ET_LONG:
562 case flatbuffers::ET_ULONG:
563 case flatbuffers::ET_STRING:
564 case flatbuffers::ET_SEQUENCE:
565 printf("Mismatched type for field '%s'. Got: double, expected %s\n",
Austin Schuh43c6a352019-09-30 22:22:10 -0700566 typetable->names[field_index],
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700567 ElementaryTypeName(elementary_type));
568 return false;
569 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700570 fbb->AddElement<float>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700571 return true;
572 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700573 fbb->AddElement<double>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700574 return true;
575 }
576 return false;
577}
Austin Schuh43c6a352019-09-30 22:22:10 -0700578bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
579 flatbuffers::Offset<flatbuffers::String> offset_element,
580 flatbuffers::FlatBufferBuilder *fbb) {
581 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700582
583 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
584 static_cast<flatbuffers::voffset_t>(field_index));
585
586 // Vectors will always be Offset<>'s.
587 if (type_code.is_vector) {
Austin Schuh43c6a352019-09-30 22:22:10 -0700588 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700589 return true;
590 }
591
592 const flatbuffers::ElementaryType elementary_type =
593 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
594 switch (elementary_type) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700595 case flatbuffers::ET_CHAR:
596 case flatbuffers::ET_UCHAR:
597 case flatbuffers::ET_SHORT:
598 case flatbuffers::ET_USHORT:
599 case flatbuffers::ET_INT:
600 case flatbuffers::ET_UINT:
601 case flatbuffers::ET_LONG:
602 case flatbuffers::ET_ULONG:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700603 case flatbuffers::ET_UTYPE:
604 case flatbuffers::ET_BOOL:
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700605 case flatbuffers::ET_FLOAT:
606 case flatbuffers::ET_DOUBLE:
607 printf("Mismatched type for field '%s'. Got: string, expected %s\n",
Austin Schuh43c6a352019-09-30 22:22:10 -0700608 typetable->names[field_index],
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700609 ElementaryTypeName(elementary_type));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700610 CHECK_EQ(type_code.sequence_ref, -1)
611 << ": Field name " << typetable->names[field_index]
612 << " Got string expected " << ElementaryTypeName(elementary_type);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700613 return false;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700614 case flatbuffers::ET_STRING:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700615 CHECK_EQ(type_code.sequence_ref, -1);
616 case flatbuffers::ET_SEQUENCE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700617 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700618 return true;
619 }
620 return false;
621}
622
623bool JsonParser::FinishVector(int field_index) {
624 flatbuffers::TypeCode type_code =
625 stack_.back().typetable->type_codes[field_index];
626
627 const flatbuffers::ElementaryType elementary_type =
628 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
629
630 // Vectors have a start (unfortunately which needs to know the size)
631 fbb_.StartVector(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700632 stack_.back().vector_elements.size(),
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700633 flatbuffers::InlineSize(elementary_type, stack_.back().typetable));
634
635 // Then the data (in reverse order for some reason...)
Alex Perrycb7da4b2019-08-28 19:35:56 -0700636 for (size_t i = stack_.back().vector_elements.size(); i > 0;) {
637 const Element &element = stack_.back().vector_elements[--i];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700638 switch (element.type) {
639 case Element::ElementType::INT:
640 if (!PushElement(elementary_type, element.int_element)) return false;
641 break;
642 case Element::ElementType::DOUBLE:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700643 CHECK_EQ(type_code.sequence_ref, -1)
644 << ": Field index is " << field_index;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700645 if (!PushElement(elementary_type, element.double_element)) return false;
646 break;
647 case Element::ElementType::OFFSET:
648 if (!PushElement(elementary_type, element.offset_element)) return false;
649 break;
650 }
651 }
652
653 // Then an End which is placed into the buffer the same as any other offset.
654 stack_.back().elements.emplace_back(
655 field_index, flatbuffers::Offset<flatbuffers::String>(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700656 fbb_.EndVector(stack_.back().vector_elements.size())));
657 stack_.back().vector_elements.clear();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700658 return true;
659}
660
661bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
662 int64_t int_value) {
663 switch (elementary_type) {
664 case flatbuffers::ET_BOOL:
665 fbb_.PushElement<bool>(int_value);
666 return true;
667 case flatbuffers::ET_CHAR:
668 fbb_.PushElement<int8_t>(int_value);
669 return true;
670 case flatbuffers::ET_UCHAR:
671 fbb_.PushElement<uint8_t>(int_value);
672 return true;
673 case flatbuffers::ET_SHORT:
674 fbb_.PushElement<int16_t>(int_value);
675 return true;
676 case flatbuffers::ET_USHORT:
677 fbb_.PushElement<uint16_t>(int_value);
678 return true;
679 case flatbuffers::ET_INT:
680 fbb_.PushElement<int32_t>(int_value);
681 return true;
682 case flatbuffers::ET_UINT:
683 fbb_.PushElement<uint32_t>(int_value);
684 return true;
685 case flatbuffers::ET_LONG:
686 fbb_.PushElement<int64_t>(int_value);
687 return true;
688 case flatbuffers::ET_ULONG:
689 fbb_.PushElement<uint64_t>(int_value);
690 return true;
691 case flatbuffers::ET_FLOAT:
692 fbb_.PushElement<float>(int_value);
693 return true;
694 case flatbuffers::ET_DOUBLE:
695 fbb_.PushElement<double>(int_value);
696 return true;
697 case flatbuffers::ET_STRING:
698 case flatbuffers::ET_UTYPE:
699 case flatbuffers::ET_SEQUENCE:
700 printf("Mismatched type for field '%s'. Got: integer, expected %s\n",
701 stack_.back().field_name.c_str(),
702 ElementaryTypeName(elementary_type));
703 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:
723 printf("Mismatched type for field '%s'. Got: double, expected %s\n",
724 stack_.back().field_name.c_str(),
725 ElementaryTypeName(elementary_type));
726 return false;
727 case flatbuffers::ET_FLOAT:
728 fbb_.PushElement<float>(double_value);
729 return true;
730 case flatbuffers::ET_DOUBLE:
731 fbb_.PushElement<double>(double_value);
732 return true;
733 }
734 return false;
735}
736
737bool JsonParser::PushElement(
738 flatbuffers::ElementaryType elementary_type,
739 flatbuffers::Offset<flatbuffers::String> offset_value) {
740 switch (elementary_type) {
741 case flatbuffers::ET_UTYPE:
742 case flatbuffers::ET_BOOL:
743 case flatbuffers::ET_CHAR:
744 case flatbuffers::ET_UCHAR:
745 case flatbuffers::ET_SHORT:
746 case flatbuffers::ET_USHORT:
747 case flatbuffers::ET_INT:
748 case flatbuffers::ET_UINT:
749 case flatbuffers::ET_LONG:
750 case flatbuffers::ET_ULONG:
751 case flatbuffers::ET_FLOAT:
752 case flatbuffers::ET_DOUBLE:
753 printf("Mismatched type for field '%s'. Got: sequence, expected %s\n",
754 stack_.back().field_name.c_str(),
755 ElementaryTypeName(elementary_type));
756 return false;
757 case flatbuffers::ET_STRING:
758 case flatbuffers::ET_SEQUENCE:
759 fbb_.PushElement(offset_value);
760 return true;
761 }
762 return false;
763}
764
765} // namespace
766
Austin Schuhe93d8642019-10-13 15:27:07 -0700767flatbuffers::DetachedBuffer JsonToFlatbuffer(
Austin Schuhd339a9b2019-10-05 21:33:32 -0700768 const absl::string_view data, const flatbuffers::TypeTable *typetable) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700769 JsonParser p;
770 return p.Parse(data, typetable);
771}
772
Austin Schuhe93d8642019-10-13 15:27:07 -0700773::std::string BufferFlatbufferToJson(const uint8_t *buffer,
774 const ::flatbuffers::TypeTable *typetable,
775 bool multi_line) {
776 // It is pretty common to get passed in a nullptr when a test fails. Rather
777 // than CHECK, return a more user friendly result.
778 if (buffer == nullptr) {
779 return "null";
780 }
781 return TableFlatbufferToJson(reinterpret_cast<const flatbuffers::Table *>(
782 flatbuffers::GetRoot<uint8_t>(buffer)),
783 typetable, multi_line);
784}
785
786::std::string TableFlatbufferToJson(const flatbuffers::Table *t,
787 const ::flatbuffers::TypeTable *typetable,
788 bool multi_line) {
789 // It is pretty common to get passed in a nullptr when a test fails. Rather
790 // than CHECK, return a more user friendly result.
791 if (t == nullptr) {
792 return "null";
793 }
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700794 ::flatbuffers::ToStringVisitor tostring_visitor(
795 multi_line ? "\n" : " ", true, multi_line ? " " : "", multi_line);
Austin Schuhe93d8642019-10-13 15:27:07 -0700796 flatbuffers::IterateObject(reinterpret_cast<const uint8_t *>(t), typetable,
797 &tostring_visitor);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700798 return tostring_visitor.s;
799}
800
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700801} // namespace aos