| #include "aos/json_to_flatbuffer.h" |
| |
| #include <cstddef> |
| #include "stdio.h" |
| |
| #include <string_view> |
| |
| #include "aos/flatbuffer_utils.h" |
| #include "aos/json_tokenizer.h" |
| #include "flatbuffers/flatbuffers.h" |
| #include "flatbuffers/minireflect.h" |
| #include "glog/logging.h" |
| |
| // TODO(austin): Can we just do an Offset<void> ? It doesn't matter, so maybe |
| // just say that. |
| // |
| // TODO(austin): I've yet to see how to create an ET_UTYPE, so I don't know what |
| // one is and how to test it. So everything rejects it. |
| |
| namespace aos { |
| |
| // Finds the field index in the table given the name. |
| int FieldIndex(const flatbuffers::TypeTable *typetable, |
| const char *field_name) { |
| CHECK(typetable->values == nullptr); |
| for (size_t i = 0; i < typetable->num_elems; ++i) { |
| if (strcmp(field_name, typetable->names[i]) == 0) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| namespace { |
| |
| // Class to hold one of the 3 json types for an array. |
| struct Element { |
| // The type. |
| enum class ElementType { INT, DOUBLE, OFFSET }; |
| |
| // Constructs an Element holding an integer. |
| Element(int64_t new_int_element) |
| : int_element(new_int_element), type(ElementType::INT) {} |
| // Constructs an Element holding an double. |
| Element(double new_double_element) |
| : double_element(new_double_element), type(ElementType::DOUBLE) {} |
| // Constructs an Element holding an Offset. |
| Element(flatbuffers::Offset<flatbuffers::String> new_offset_element) |
| : offset_element(new_offset_element), type(ElementType::OFFSET) {} |
| |
| // Union for the various datatypes. |
| union { |
| int64_t int_element; |
| double double_element; |
| flatbuffers::Offset<flatbuffers::String> offset_element; |
| }; |
| |
| // And an enum signaling which one is in use. |
| ElementType type; |
| }; |
| |
| // Structure to represent a field element. |
| struct FieldElement { |
| FieldElement(int new_field_index, int64_t int_element) |
| : element(int_element), field_index(new_field_index) {} |
| FieldElement(int new_field_index, double double_element) |
| : element(double_element), field_index(new_field_index) {} |
| FieldElement(int new_field_index, |
| flatbuffers::Offset<flatbuffers::String> offset_element) |
| : element(offset_element), field_index(new_field_index) {} |
| |
| // Data to write. |
| Element element; |
| // Field index. The type table which this index is for is stored outside this |
| // object. |
| int field_index; |
| }; |
| |
| // Adds a single element. This assumes that vectors have been dealt with |
| // already. Returns true on success. |
| bool AddSingleElement(const flatbuffers::TypeTable *typetable, |
| const FieldElement &field_element, |
| ::std::vector<bool> *fields_in_use, |
| flatbuffers::FlatBufferBuilder *fbb); |
| bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index, |
| int64_t int_value, flatbuffers::FlatBufferBuilder *fbb); |
| bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index, |
| double double_value, flatbuffers::FlatBufferBuilder *fbb); |
| bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index, |
| flatbuffers::Offset<flatbuffers::String> offset_element, |
| flatbuffers::FlatBufferBuilder *fbb); |
| |
| |
| // Writes an array of FieldElement (with the definition in the type |
| // table) to the builder. Returns the offset of the table. |
| flatbuffers::uoffset_t WriteTable(const flatbuffers::TypeTable *typetable, |
| const ::std::vector<FieldElement> &elements, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| // End of a nested struct! Add it. |
| const flatbuffers::uoffset_t start = fbb->StartTable(); |
| |
| ::std::vector<bool> fields_in_use(typetable->num_elems, false); |
| |
| for (const FieldElement &field_element : elements) { |
| AddSingleElement(typetable, field_element, &fields_in_use, fbb); |
| } |
| |
| return fbb->EndTable(start); |
| } |
| |
| // Class to parse JSON into a flatbuffer. |
| // |
| // The basic strategy is that we need to do everything backwards. So we need to |
| // build up what we need to do fully in memory, then do it. |
| // |
| // The driver for this is that strings need to be fully created before the |
| // tables that use them. Same for sub messages. But, we only know we have them |
| // all when the structure ends. So, store each sub message in a |
| // FieldElement and put them in the table at the end when we finish up |
| // each message. Same goes for vectors. |
| class JsonParser { |
| public: |
| JsonParser(flatbuffers::FlatBufferBuilder *fbb) : fbb_(fbb) {} |
| ~JsonParser() {} |
| |
| // Parses the json into a flatbuffer. Returns either an empty vector on |
| // error, or a vector with the flatbuffer data in it. |
| flatbuffers::Offset<flatbuffers::Table> Parse( |
| const std::string_view data, const flatbuffers::TypeTable *typetable) { |
| flatbuffers::uoffset_t end = 0; |
| bool result = DoParse(typetable, data, &end); |
| |
| if (result) { |
| // On success, finish the table and build the vector. |
| return flatbuffers::Offset<flatbuffers::Table>(end); |
| } else { |
| return flatbuffers::Offset<flatbuffers::Table>(0); |
| } |
| } |
| |
| private: |
| // Setters and getters for in_vector (at the current level of the stack) |
| bool in_vector() const { return stack_.back().in_vector; } |
| void set_in_vector(bool in_vector) { stack_.back().in_vector = in_vector; } |
| |
| // Parses the flatbuffer. This is a second method so we can do easier |
| // cleanup at the top level. Returns true on success. |
| bool DoParse(const flatbuffers::TypeTable *typetable, |
| const std::string_view data, |
| flatbuffers::uoffset_t *table_end); |
| |
| // Adds *_value for the provided field. If we are in a vector, queues the |
| // data up in vector_elements. Returns true on success. |
| bool AddElement(int field_index, int64_t int_value); |
| bool AddElement(int field_index, double double_value); |
| bool AddElement(int field_index, const ::std::string &data); |
| |
| // Finishes a vector for the provided field index. Returns true on success. |
| bool FinishVector(int field_index); |
| |
| // Pushes an element as part of a vector. Returns true on success. |
| bool PushElement(flatbuffers::ElementaryType elementary_type, |
| int64_t int_value); |
| bool PushElement(flatbuffers::ElementaryType elementary_type, |
| double double_value); |
| bool PushElement(flatbuffers::ElementaryType elementary_type, |
| flatbuffers::Offset<flatbuffers::String> offset_value); |
| |
| flatbuffers::FlatBufferBuilder *fbb_; |
| |
| // This holds the state information that is needed as you recurse into |
| // nested structures. |
| struct FlatBufferContext { |
| // Type of the current type. |
| const flatbuffers::TypeTable *typetable; |
| // If true, we are parsing a vector. |
| bool in_vector; |
| // The field index of the current field. |
| int field_index; |
| // Name of the current field. |
| ::std::string field_name; |
| |
| // Field elements that need to be inserted. |
| ::std::vector<FieldElement> elements; |
| |
| // For scalar types (not strings, and not nested tables), the vector ends |
| // up being implemented as a start and end, and a block of data. So we |
| // can't just push offsets in as we go. We either need to reproduce the |
| // logic inside flatbuffers, or build up vectors of the data. Vectors will |
| // be a bit of extra stack space, but whatever. |
| // |
| // Strings and nested structures are vectors of offsets. |
| // into the vector. Once you get to the end, you build up a vector and |
| // push that into the field. |
| ::std::vector<Element> vector_elements; |
| }; |
| ::std::vector<FlatBufferContext> stack_; |
| }; |
| |
| bool JsonParser::DoParse(const flatbuffers::TypeTable *typetable, |
| const std::string_view data, |
| flatbuffers::uoffset_t *table_end) { |
| ::std::vector<const flatbuffers::TypeTable *> stack; |
| |
| Tokenizer t(data); |
| |
| // Main loop. Run until we get an end. |
| while (true) { |
| Tokenizer::TokenType token = t.Next(); |
| |
| switch (token) { |
| case Tokenizer::TokenType::kEnd: |
| if (stack_.size() != 0) { |
| fprintf(stderr, "Failed to unwind stack all the way\n"); |
| return false; |
| } else { |
| return true; |
| } |
| break; |
| case Tokenizer::TokenType::kError: |
| return false; |
| break; |
| |
| case Tokenizer::TokenType::kStartObject: // { |
| if (stack_.size() == 0) { |
| stack_.push_back({typetable, false, -1, "", {}, {}}); |
| } else { |
| int field_index = stack_.back().field_index; |
| |
| const flatbuffers::TypeCode &type_code = |
| stack_.back().typetable->type_codes[field_index]; |
| |
| if (type_code.base_type != flatbuffers::ET_SEQUENCE) { |
| fprintf(stderr, "Field '%s' is not a sequence\n", |
| stack_.back().field_name.c_str()); |
| return false; |
| } |
| |
| flatbuffers::TypeFunction type_function = |
| stack_.back().typetable->type_refs[type_code.sequence_ref]; |
| |
| stack_.push_back({type_function(), false, -1, "", {}, {}}); |
| } |
| break; |
| case Tokenizer::TokenType::kEndObject: // } |
| if (stack_.size() == 0) { |
| // Somehow we popped more than we pushed. Error. |
| fprintf(stderr, "Empty stack\n"); |
| return false; |
| } else { |
| // End of a nested struct! Add it. |
| const flatbuffers::uoffset_t end = WriteTable( |
| stack_.back().typetable, stack_.back().elements, fbb_); |
| |
| // We now want to talk about the parent structure. Pop the child. |
| stack_.pop_back(); |
| |
| if (stack_.size() == 0) { |
| // Instead of queueing it up in the stack, return it through the |
| // passed in variable. |
| *table_end = end; |
| } else { |
| // And now we can add it. |
| const int field_index = stack_.back().field_index; |
| |
| // Do the right thing if we are in a vector. |
| if (in_vector()) { |
| stack_.back().vector_elements.emplace_back( |
| flatbuffers::Offset<flatbuffers::String>(end)); |
| } else { |
| stack_.back().elements.emplace_back( |
| field_index, flatbuffers::Offset<flatbuffers::String>(end)); |
| } |
| } |
| } |
| break; |
| |
| case Tokenizer::TokenType::kStartArray: // [ |
| if (stack_.size() == 0) { |
| // We don't support an array of structs at the root level. |
| return false; |
| } |
| // Sanity check that we aren't trying to make a vector of vectors. |
| if (in_vector()) { |
| return false; |
| } |
| set_in_vector(true); |
| |
| break; |
| case Tokenizer::TokenType::kEndArray: { // ] |
| if (!in_vector()) { |
| return false; |
| } |
| |
| const int field_index = stack_.back().field_index; |
| |
| if (!FinishVector(field_index)) return false; |
| |
| set_in_vector(false); |
| } break; |
| |
| case Tokenizer::TokenType::kTrueValue: // true |
| case Tokenizer::TokenType::kFalseValue: // false |
| case Tokenizer::TokenType::kNumberValue: { |
| bool is_int = true; |
| double double_value; |
| long long int_value; |
| if (token == Tokenizer::TokenType::kTrueValue) { |
| int_value = 1; |
| } else if (token == Tokenizer::TokenType::kFalseValue) { |
| int_value = 0; |
| } else if (!t.FieldAsInt(&int_value)) { |
| if (t.FieldAsDouble(&double_value)) { |
| is_int = false; |
| } else { |
| fprintf(stderr, "Got a invalid number '%s'\n", |
| t.field_value().c_str()); |
| return false; |
| } |
| } |
| |
| const int field_index = stack_.back().field_index; |
| |
| if (is_int) { |
| // No need to get too stressed about bool vs int. Convert them all. |
| int64_t val = int_value; |
| if (!AddElement(field_index, val)) return false; |
| } else { |
| if (!AddElement(field_index, double_value)) return false; |
| } |
| } break; |
| case Tokenizer::TokenType::kStringValue: // string value |
| { |
| const int field_index = stack_.back().field_index; |
| |
| if (!AddElement(field_index, t.field_value())) return false; |
| } break; |
| case Tokenizer::TokenType::kField: // field name |
| { |
| stack_.back().field_name = t.field_name(); |
| stack_.back().field_index = FieldIndex( |
| stack_.back().typetable, stack_.back().field_name.c_str()); |
| |
| if (stack_.back().field_index == -1) { |
| fprintf(stderr, "Invalid field name '%s'\n", |
| stack_.back().field_name.c_str()); |
| return false; |
| } |
| } break; |
| } |
| } |
| return false; |
| } |
| |
| bool JsonParser::AddElement(int field_index, int64_t int_value) { |
| flatbuffers::TypeCode type_code = |
| stack_.back().typetable->type_codes[field_index]; |
| |
| if (type_code.is_vector != in_vector()) { |
| fprintf(stderr, "Type and json disagree on if we are in a vector or not\n"); |
| return false; |
| } |
| |
| if (in_vector()) { |
| stack_.back().vector_elements.emplace_back(int_value); |
| } else { |
| stack_.back().elements.emplace_back(field_index, int_value); |
| } |
| return true; |
| } |
| |
| bool JsonParser::AddElement(int field_index, double double_value) { |
| flatbuffers::TypeCode type_code = |
| stack_.back().typetable->type_codes[field_index]; |
| |
| if (type_code.is_vector != in_vector()) { |
| fprintf(stderr, "Type and json disagree on if we are in a vector or not\n"); |
| return false; |
| } |
| |
| if (in_vector()) { |
| stack_.back().vector_elements.emplace_back(double_value); |
| } else { |
| stack_.back().elements.emplace_back(field_index, double_value); |
| } |
| return true; |
| } |
| |
| bool JsonParser::AddElement(int field_index, const ::std::string &data) { |
| flatbuffers::TypeCode type_code = |
| stack_.back().typetable->type_codes[field_index]; |
| |
| if (type_code.is_vector != in_vector()) { |
| fprintf(stderr, "Type and json disagree on if we are in a vector or not\n"); |
| return false; |
| } |
| |
| const flatbuffers::ElementaryType elementary_type = |
| static_cast<flatbuffers::ElementaryType>(type_code.base_type); |
| switch (elementary_type) { |
| case flatbuffers::ET_CHAR: |
| case flatbuffers::ET_UCHAR: |
| case flatbuffers::ET_SHORT: |
| case flatbuffers::ET_USHORT: |
| case flatbuffers::ET_INT: |
| case flatbuffers::ET_UINT: |
| case flatbuffers::ET_LONG: |
| case flatbuffers::ET_ULONG: |
| if (type_code.sequence_ref != -1) { |
| // We have an enum. |
| const flatbuffers::TypeTable *type_table = stack_.back().typetable; |
| flatbuffers::TypeFunction type_function = |
| type_table->type_refs[type_code.sequence_ref]; |
| |
| const flatbuffers::TypeTable *enum_type_table = type_function(); |
| |
| CHECK_EQ(enum_type_table->st, flatbuffers::ST_ENUM); |
| |
| int64_t int_value = 0; |
| bool found = false; |
| for (size_t i = 0; i < enum_type_table->num_elems; ++i) { |
| if (data == enum_type_table->names[i]) { |
| int_value = i; |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| fprintf(stderr, "Enum value '%s' not found for field '%s'\n", |
| data.c_str(), type_table->names[field_index]); |
| return false; |
| } |
| |
| if (in_vector()) { |
| stack_.back().vector_elements.emplace_back(int_value); |
| } else { |
| stack_.back().elements.emplace_back(field_index, int_value); |
| } |
| return true; |
| } |
| case flatbuffers::ET_UTYPE: |
| case flatbuffers::ET_BOOL: |
| case flatbuffers::ET_FLOAT: |
| case flatbuffers::ET_DOUBLE: |
| case flatbuffers::ET_STRING: |
| case flatbuffers::ET_SEQUENCE: |
| break; |
| } |
| |
| if (in_vector()) { |
| stack_.back().vector_elements.emplace_back(fbb_->CreateString(data)); |
| } else { |
| stack_.back().elements.emplace_back(field_index, fbb_->CreateString(data)); |
| } |
| return true; |
| } |
| |
| bool AddSingleElement(const flatbuffers::TypeTable *typetable, |
| const FieldElement &field_element, |
| ::std::vector<bool> *fields_in_use, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| if ((*fields_in_use)[field_element.field_index]) { |
| fprintf(stderr, "Duplicate field: '%s'\n", |
| typetable->names[field_element.field_index]); |
| return false; |
| } |
| |
| (*fields_in_use)[field_element.field_index] = true; |
| |
| switch (field_element.element.type) { |
| case Element::ElementType::INT: |
| return AddSingleElement(typetable, field_element.field_index, |
| field_element.element.int_element, fbb); |
| case Element::ElementType::DOUBLE: |
| return AddSingleElement(typetable, field_element.field_index, |
| field_element.element.double_element, fbb); |
| case Element::ElementType::OFFSET: |
| return AddSingleElement(typetable, field_element.field_index, |
| field_element.element.offset_element, fbb); |
| } |
| return false; |
| } |
| |
| bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index, |
| int64_t int_value, flatbuffers::FlatBufferBuilder *fbb |
| |
| ) { |
| flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset( |
| static_cast<flatbuffers::voffset_t>(field_index)); |
| |
| flatbuffers::TypeCode type_code = typetable->type_codes[field_index]; |
| |
| const flatbuffers::ElementaryType elementary_type = |
| static_cast<flatbuffers::ElementaryType>(type_code.base_type); |
| switch (elementary_type) { |
| case flatbuffers::ET_BOOL: |
| fbb->AddElement<bool>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_CHAR: |
| fbb->AddElement<int8_t>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_UCHAR: |
| fbb->AddElement<uint8_t>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_SHORT: |
| fbb->AddElement<int16_t>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_USHORT: |
| fbb->AddElement<uint16_t>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_INT: |
| fbb->AddElement<int32_t>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_UINT: |
| fbb->AddElement<uint32_t>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_LONG: |
| fbb->AddElement<int64_t>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_ULONG: |
| fbb->AddElement<uint64_t>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_FLOAT: |
| fbb->AddElement<float>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_DOUBLE: |
| fbb->AddElement<double>(field_offset, int_value, 0); |
| return true; |
| case flatbuffers::ET_STRING: |
| case flatbuffers::ET_UTYPE: |
| case flatbuffers::ET_SEQUENCE: |
| fprintf( |
| stderr, "Mismatched type for field '%s'. Got: integer, expected %s\n", |
| typetable->names[field_index], ElementaryTypeName(elementary_type)); |
| return false; |
| }; |
| return false; |
| } |
| |
| bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index, |
| double double_value, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset( |
| static_cast<flatbuffers::voffset_t>(field_index)); |
| |
| flatbuffers::TypeCode type_code = typetable->type_codes[field_index]; |
| |
| const flatbuffers::ElementaryType elementary_type = |
| static_cast<flatbuffers::ElementaryType>(type_code.base_type); |
| switch (elementary_type) { |
| case flatbuffers::ET_UTYPE: |
| case flatbuffers::ET_BOOL: |
| case flatbuffers::ET_CHAR: |
| case flatbuffers::ET_UCHAR: |
| case flatbuffers::ET_SHORT: |
| case flatbuffers::ET_USHORT: |
| case flatbuffers::ET_INT: |
| case flatbuffers::ET_UINT: |
| case flatbuffers::ET_LONG: |
| case flatbuffers::ET_ULONG: |
| case flatbuffers::ET_STRING: |
| case flatbuffers::ET_SEQUENCE: |
| fprintf( |
| stderr, "Mismatched type for field '%s'. Got: double, expected %s\n", |
| typetable->names[field_index], ElementaryTypeName(elementary_type)); |
| return false; |
| case flatbuffers::ET_FLOAT: |
| fbb->AddElement<float>(field_offset, double_value, 0); |
| return true; |
| case flatbuffers::ET_DOUBLE: |
| fbb->AddElement<double>(field_offset, double_value, 0); |
| return true; |
| } |
| return false; |
| } |
| bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index, |
| flatbuffers::Offset<flatbuffers::String> offset_element, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| flatbuffers::TypeCode type_code = typetable->type_codes[field_index]; |
| |
| flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset( |
| static_cast<flatbuffers::voffset_t>(field_index)); |
| |
| // Vectors will always be Offset<>'s. |
| if (type_code.is_vector) { |
| fbb->AddOffset(field_offset, offset_element); |
| return true; |
| } |
| |
| const flatbuffers::ElementaryType elementary_type = |
| static_cast<flatbuffers::ElementaryType>(type_code.base_type); |
| switch (elementary_type) { |
| case flatbuffers::ET_CHAR: |
| case flatbuffers::ET_UCHAR: |
| case flatbuffers::ET_SHORT: |
| case flatbuffers::ET_USHORT: |
| case flatbuffers::ET_INT: |
| case flatbuffers::ET_UINT: |
| case flatbuffers::ET_LONG: |
| case flatbuffers::ET_ULONG: |
| case flatbuffers::ET_UTYPE: |
| case flatbuffers::ET_BOOL: |
| case flatbuffers::ET_FLOAT: |
| case flatbuffers::ET_DOUBLE: |
| fprintf( |
| stderr, "Mismatched type for field '%s'. Got: string, expected %s\n", |
| typetable->names[field_index], ElementaryTypeName(elementary_type)); |
| CHECK_EQ(type_code.sequence_ref, -1) |
| << ": Field name " << typetable->names[field_index] |
| << " Got string expected " << ElementaryTypeName(elementary_type); |
| return false; |
| case flatbuffers::ET_STRING: |
| CHECK_EQ(type_code.sequence_ref, -1); |
| case flatbuffers::ET_SEQUENCE: |
| fbb->AddOffset(field_offset, offset_element); |
| return true; |
| } |
| return false; |
| } |
| |
| bool JsonParser::FinishVector(int field_index) { |
| flatbuffers::TypeCode type_code = |
| stack_.back().typetable->type_codes[field_index]; |
| |
| const flatbuffers::ElementaryType elementary_type = |
| static_cast<flatbuffers::ElementaryType>(type_code.base_type); |
| |
| // Vectors have a start (unfortunately which needs to know the size) |
| fbb_->StartVector( |
| stack_.back().vector_elements.size(), |
| flatbuffers::InlineSize(elementary_type, stack_.back().typetable)); |
| |
| // Then the data (in reverse order for some reason...) |
| for (size_t i = stack_.back().vector_elements.size(); i > 0;) { |
| const Element &element = stack_.back().vector_elements[--i]; |
| switch (element.type) { |
| case Element::ElementType::INT: |
| if (!PushElement(elementary_type, element.int_element)) return false; |
| break; |
| case Element::ElementType::DOUBLE: |
| CHECK_EQ(type_code.sequence_ref, -1) |
| << ": Field index is " << field_index; |
| if (!PushElement(elementary_type, element.double_element)) return false; |
| break; |
| case Element::ElementType::OFFSET: |
| if (!PushElement(elementary_type, element.offset_element)) return false; |
| break; |
| } |
| } |
| |
| // Then an End which is placed into the buffer the same as any other offset. |
| stack_.back().elements.emplace_back( |
| field_index, flatbuffers::Offset<flatbuffers::String>( |
| fbb_->EndVector(stack_.back().vector_elements.size()))); |
| stack_.back().vector_elements.clear(); |
| return true; |
| } |
| |
| bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type, |
| int64_t int_value) { |
| switch (elementary_type) { |
| case flatbuffers::ET_BOOL: |
| fbb_->PushElement<bool>(int_value); |
| return true; |
| case flatbuffers::ET_CHAR: |
| fbb_->PushElement<int8_t>(int_value); |
| return true; |
| case flatbuffers::ET_UCHAR: |
| fbb_->PushElement<uint8_t>(int_value); |
| return true; |
| case flatbuffers::ET_SHORT: |
| fbb_->PushElement<int16_t>(int_value); |
| return true; |
| case flatbuffers::ET_USHORT: |
| fbb_->PushElement<uint16_t>(int_value); |
| return true; |
| case flatbuffers::ET_INT: |
| fbb_->PushElement<int32_t>(int_value); |
| return true; |
| case flatbuffers::ET_UINT: |
| fbb_->PushElement<uint32_t>(int_value); |
| return true; |
| case flatbuffers::ET_LONG: |
| fbb_->PushElement<int64_t>(int_value); |
| return true; |
| case flatbuffers::ET_ULONG: |
| fbb_->PushElement<uint64_t>(int_value); |
| return true; |
| case flatbuffers::ET_FLOAT: |
| fbb_->PushElement<float>(int_value); |
| return true; |
| case flatbuffers::ET_DOUBLE: |
| fbb_->PushElement<double>(int_value); |
| return true; |
| case flatbuffers::ET_STRING: |
| case flatbuffers::ET_UTYPE: |
| case flatbuffers::ET_SEQUENCE: |
| fprintf(stderr, |
| "Mismatched type for field '%s'. Got: integer, expected %s\n", |
| stack_.back().field_name.c_str(), |
| ElementaryTypeName(elementary_type)); |
| return false; |
| }; |
| return false; |
| } |
| |
| bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type, |
| double double_value) { |
| switch (elementary_type) { |
| case flatbuffers::ET_UTYPE: |
| case flatbuffers::ET_BOOL: |
| case flatbuffers::ET_CHAR: |
| case flatbuffers::ET_UCHAR: |
| case flatbuffers::ET_SHORT: |
| case flatbuffers::ET_USHORT: |
| case flatbuffers::ET_INT: |
| case flatbuffers::ET_UINT: |
| case flatbuffers::ET_LONG: |
| case flatbuffers::ET_ULONG: |
| case flatbuffers::ET_STRING: |
| case flatbuffers::ET_SEQUENCE: |
| fprintf(stderr, |
| "Mismatched type for field '%s'. Got: double, expected %s\n", |
| stack_.back().field_name.c_str(), |
| ElementaryTypeName(elementary_type)); |
| return false; |
| case flatbuffers::ET_FLOAT: |
| fbb_->PushElement<float>(double_value); |
| return true; |
| case flatbuffers::ET_DOUBLE: |
| fbb_->PushElement<double>(double_value); |
| return true; |
| } |
| return false; |
| } |
| |
| bool JsonParser::PushElement( |
| flatbuffers::ElementaryType elementary_type, |
| flatbuffers::Offset<flatbuffers::String> offset_value) { |
| switch (elementary_type) { |
| case flatbuffers::ET_UTYPE: |
| case flatbuffers::ET_BOOL: |
| case flatbuffers::ET_CHAR: |
| case flatbuffers::ET_UCHAR: |
| case flatbuffers::ET_SHORT: |
| case flatbuffers::ET_USHORT: |
| case flatbuffers::ET_INT: |
| case flatbuffers::ET_UINT: |
| case flatbuffers::ET_LONG: |
| case flatbuffers::ET_ULONG: |
| case flatbuffers::ET_FLOAT: |
| case flatbuffers::ET_DOUBLE: |
| fprintf(stderr, |
| "Mismatched type for field '%s'. Got: sequence, expected %s\n", |
| stack_.back().field_name.c_str(), |
| ElementaryTypeName(elementary_type)); |
| return false; |
| case flatbuffers::ET_STRING: |
| case flatbuffers::ET_SEQUENCE: |
| fbb_->PushElement(offset_value); |
| return true; |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| flatbuffers::Offset<flatbuffers::Table> JsonToFlatbuffer( |
| const std::string_view data, const flatbuffers::TypeTable *typetable, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| JsonParser p(fbb); |
| return p.Parse(data, typetable); |
| } |
| |
| flatbuffers::DetachedBuffer JsonToFlatbuffer( |
| const std::string_view data, |
| const flatbuffers::TypeTable *typetable) { |
| flatbuffers::FlatBufferBuilder fbb; |
| fbb.ForceDefaults(true); |
| |
| const flatbuffers::Offset<flatbuffers::Table> result = |
| JsonToFlatbuffer(data, typetable, &fbb); |
| if (result.o != 0) { |
| fbb.Finish(result); |
| |
| return fbb.Release(); |
| } else { |
| // Otherwise return an empty vector. |
| return flatbuffers::DetachedBuffer(); |
| } |
| } |
| |
| ::std::string BufferFlatbufferToJson(const uint8_t *buffer, |
| const ::flatbuffers::TypeTable *typetable, |
| bool multi_line) { |
| // It is pretty common to get passed in a nullptr when a test fails. Rather |
| // than CHECK, return a more user friendly result. |
| if (buffer == nullptr) { |
| return "null"; |
| } |
| return TableFlatbufferToJson(reinterpret_cast<const flatbuffers::Table *>( |
| flatbuffers::GetRoot<uint8_t>(buffer)), |
| typetable, multi_line); |
| } |
| |
| ::std::string TableFlatbufferToJson(const flatbuffers::Table *t, |
| const ::flatbuffers::TypeTable *typetable, |
| bool multi_line) { |
| // It is pretty common to get passed in a nullptr when a test fails. Rather |
| // than CHECK, return a more user friendly result. |
| if (t == nullptr) { |
| return "null"; |
| } |
| ::flatbuffers::ToStringVisitor tostring_visitor( |
| multi_line ? "\n" : " ", true, multi_line ? " " : "", multi_line); |
| flatbuffers::IterateObject(reinterpret_cast<const uint8_t *>(t), typetable, |
| &tostring_visitor); |
| return tostring_visitor.s; |
| } |
| |
| } // namespace aos |