| #include "aos/json_to_flatbuffer.h" |
| |
| #include <cstddef> |
| #include <cstdio> |
| #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 { |
| 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(FlatbufferType type, const FieldElement &field_element, |
| ::std::vector<bool> *fields_in_use, |
| flatbuffers::FlatBufferBuilder *fbb); |
| bool AddSingleElement(FlatbufferType type, int field_index, int64_t int_value, |
| flatbuffers::FlatBufferBuilder *fbb); |
| bool AddSingleElement(FlatbufferType type, int field_index, double double_value, |
| flatbuffers::FlatBufferBuilder *fbb); |
| bool AddSingleElement(FlatbufferType type, int field_index, |
| flatbuffers::Offset<flatbuffers::String> offset_element, |
| flatbuffers::FlatBufferBuilder *fbb); |
| |
| // Writes an array of FieldElement (with the definition in "type") to the |
| // builder. Returns the offset of the resulting table. |
| flatbuffers::uoffset_t WriteTable(FlatbufferType type, |
| 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(type.NumberFields(), false); |
| |
| for (const FieldElement &field_element : elements) { |
| AddSingleElement(type, 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, |
| FlatbufferType type) { |
| flatbuffers::uoffset_t end = 0; |
| bool result = DoParse(type, 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(FlatbufferType type, 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. |
| FlatbufferType type; |
| // 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(FlatbufferType type, const std::string_view data, |
| flatbuffers::uoffset_t *table_end) { |
| ::std::vector<FlatbufferType> 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({type, false, -1, "", {}, {}}); |
| } else { |
| int field_index = stack_.back().field_index; |
| |
| if (!stack_.back().type.FieldIsSequence(field_index)) { |
| fprintf(stderr, "Field '%s' is not a sequence\n", |
| stack_.back().field_name.c_str()); |
| return false; |
| } |
| |
| if (in_vector() != stack_.back().type.FieldIsRepeating(field_index)) { |
| fprintf(stderr, |
| "Field '%s' is%s supposed to be a vector, but is a %s.\n", |
| stack_.back().field_name.c_str(), in_vector() ? " not" : "", |
| in_vector() ? "vector" : "bare object"); |
| return false; |
| } |
| |
| stack_.push_back({stack_.back().type.FieldType(field_index), |
| 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().type, 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 = |
| stack_.back().type.FieldIndex(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) { |
| if (stack_.back().type.FieldIsRepeating(field_index) != 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) { |
| if (stack_.back().type.FieldIsRepeating(field_index) != 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) { |
| if (stack_.back().type.FieldIsRepeating(field_index) != 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 = |
| stack_.back().type.FieldElementaryType(field_index); |
| 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 (stack_.back().type.FieldIsEnum(field_index)) { |
| // We have an enum. |
| const FlatbufferType type = stack_.back().type; |
| const FlatbufferType enum_type = type.FieldType(field_index); |
| CHECK(enum_type.IsEnum()); |
| |
| const std::optional<int64_t> int_value = enum_type.EnumValue(data); |
| |
| if (!int_value) { |
| const std::string_view name = type.FieldName(field_index); |
| fprintf(stderr, "Enum value '%s' not found for field '%.*s'\n", |
| data.c_str(), static_cast<int>(name.size()), name.data()); |
| 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(FlatbufferType type, const FieldElement &field_element, |
| ::std::vector<bool> *fields_in_use, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| if ((*fields_in_use)[field_element.field_index]) { |
| const std::string_view name = type.FieldName(field_element.field_index); |
| fprintf(stderr, "Duplicate field: '%.*s'\n", static_cast<int>(name.size()), |
| name.data()); |
| return false; |
| } |
| |
| (*fields_in_use)[field_element.field_index] = true; |
| |
| switch (field_element.element.type) { |
| case Element::ElementType::INT: |
| return AddSingleElement(type, field_element.field_index, |
| field_element.element.int_element, fbb); |
| case Element::ElementType::DOUBLE: |
| return AddSingleElement(type, field_element.field_index, |
| field_element.element.double_element, fbb); |
| case Element::ElementType::OFFSET: |
| return AddSingleElement(type, field_element.field_index, |
| field_element.element.offset_element, fbb); |
| } |
| return false; |
| } |
| |
| bool AddSingleElement(FlatbufferType type, int field_index, int64_t int_value, |
| flatbuffers::FlatBufferBuilder *fbb |
| |
| ) { |
| flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset( |
| static_cast<flatbuffers::voffset_t>(field_index)); |
| |
| const flatbuffers::ElementaryType elementary_type = |
| type.FieldElementaryType(field_index); |
| switch (elementary_type) { |
| case flatbuffers::ET_BOOL: |
| fbb->AddElement<bool>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_CHAR: |
| fbb->AddElement<int8_t>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_UCHAR: |
| fbb->AddElement<uint8_t>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_SHORT: |
| fbb->AddElement<int16_t>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_USHORT: |
| fbb->AddElement<uint16_t>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_INT: |
| fbb->AddElement<int32_t>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_UINT: |
| fbb->AddElement<uint32_t>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_LONG: |
| fbb->AddElement<int64_t>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_ULONG: |
| fbb->AddElement<uint64_t>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_FLOAT: |
| fbb->AddElement<float>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_DOUBLE: |
| fbb->AddElement<double>(field_offset, int_value); |
| return true; |
| case flatbuffers::ET_STRING: |
| case flatbuffers::ET_UTYPE: |
| case flatbuffers::ET_SEQUENCE: { |
| const std::string_view name = type.FieldName(field_index); |
| fprintf(stderr, |
| "Mismatched type for field '%.*s'. Got: integer, expected %s\n", |
| static_cast<int>(name.size()), name.data(), |
| ElementaryTypeName(elementary_type)); |
| return false; |
| } |
| }; |
| return false; |
| } |
| |
| bool AddSingleElement(FlatbufferType type, int field_index, double double_value, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset( |
| static_cast<flatbuffers::voffset_t>(field_index)); |
| |
| const flatbuffers::ElementaryType elementary_type = |
| type.FieldElementaryType(field_index); |
| 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: { |
| const std::string_view name = type.FieldName(field_index); |
| fprintf(stderr, |
| "Mismatched type for field '%.*s'. Got: double, expected %s\n", |
| static_cast<int>(name.size()), name.data(), |
| ElementaryTypeName(elementary_type)); |
| return false; |
| } |
| case flatbuffers::ET_FLOAT: |
| fbb->AddElement<float>(field_offset, double_value); |
| return true; |
| case flatbuffers::ET_DOUBLE: |
| fbb->AddElement<double>(field_offset, double_value); |
| return true; |
| } |
| return false; |
| } |
| bool AddSingleElement(FlatbufferType type, int field_index, |
| flatbuffers::Offset<flatbuffers::String> offset_element, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset( |
| static_cast<flatbuffers::voffset_t>(field_index)); |
| |
| // Vectors will always be Offset<>'s. |
| if (type.FieldIsRepeating(field_index)) { |
| fbb->AddOffset(field_offset, offset_element); |
| return true; |
| } |
| |
| const flatbuffers::ElementaryType elementary_type = |
| type.FieldElementaryType(field_index); |
| 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: { |
| const std::string_view name = type.FieldName(field_index); |
| fprintf(stderr, |
| "Mismatched type for field '%.*s'. Got: string, expected %s\n", |
| static_cast<int>(name.size()), name.data(), |
| ElementaryTypeName(elementary_type)); |
| return false; |
| } |
| case flatbuffers::ET_STRING: |
| case flatbuffers::ET_SEQUENCE: |
| fbb->AddOffset(field_offset, offset_element); |
| return true; |
| } |
| return false; |
| } |
| |
| bool JsonParser::FinishVector(int field_index) { |
| // Vectors have a start (unfortunately which needs to know the size) |
| const size_t inline_size = stack_.back().type.FieldInlineSize(field_index); |
| fbb_->StartVector(stack_.back().vector_elements.size(), inline_size, |
| /*align=*/inline_size); |
| |
| const flatbuffers::ElementaryType elementary_type = |
| stack_.back().type.FieldElementaryType(field_index); |
| |
| // 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: |
| 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, FlatbufferType type, |
| flatbuffers::FlatBufferBuilder *fbb) { |
| JsonParser p(fbb); |
| return p.Parse(data, type); |
| } |
| |
| flatbuffers::DetachedBuffer JsonToFlatbuffer(const std::string_view data, |
| FlatbufferType type) { |
| flatbuffers::FlatBufferBuilder fbb; |
| fbb.ForceDefaults(true); |
| |
| const flatbuffers::Offset<flatbuffers::Table> result = |
| JsonToFlatbuffer(data, type, &fbb); |
| if (result.o != 0) { |
| fbb.Finish(result); |
| |
| return fbb.Release(); |
| } else { |
| // Otherwise return an empty vector. |
| return flatbuffers::DetachedBuffer(); |
| } |
| } |
| |
| namespace { |
| |
| // A visitor which manages skipping the contents of vectors that are longer than |
| // a specified threshold. |
| class TruncatingStringVisitor : public flatbuffers::IterationVisitor { |
| public: |
| TruncatingStringVisitor(size_t max_vector_size, std::string delimiter, |
| bool quotes, std::string indent, bool vdelimited) |
| : max_vector_size_(max_vector_size), |
| to_string_(delimiter, quotes, indent, vdelimited) {} |
| ~TruncatingStringVisitor() override {} |
| |
| void StartSequence() override { |
| if (should_skip()) return; |
| to_string_.StartSequence(); |
| } |
| void EndSequence() override { |
| if (should_skip()) return; |
| to_string_.EndSequence(); |
| } |
| void Field(size_t field_idx, size_t set_idx, flatbuffers::ElementaryType type, |
| bool is_repeating, const flatbuffers::TypeTable *type_table, |
| const char *name, const uint8_t *val) override { |
| if (should_skip()) return; |
| to_string_.Field(field_idx, set_idx, type, is_repeating, type_table, name, |
| val); |
| } |
| void UType(uint8_t value, const char *name) override { |
| if (should_skip()) return; |
| to_string_.UType(value, name); |
| } |
| void Bool(bool value) override { |
| if (should_skip()) return; |
| to_string_.Bool(value); |
| } |
| void Char(int8_t value, const char *name) override { |
| if (should_skip()) return; |
| to_string_.Char(value, name); |
| } |
| void UChar(uint8_t value, const char *name) override { |
| if (should_skip()) return; |
| to_string_.UChar(value, name); |
| } |
| void Short(int16_t value, const char *name) override { |
| if (should_skip()) return; |
| to_string_.Short(value, name); |
| } |
| void UShort(uint16_t value, const char *name) override { |
| if (should_skip()) return; |
| to_string_.UShort(value, name); |
| } |
| void Int(int32_t value, const char *name) override { |
| if (should_skip()) return; |
| to_string_.Int(value, name); |
| } |
| void UInt(uint32_t value, const char *name) override { |
| if (should_skip()) return; |
| to_string_.UInt(value, name); |
| } |
| void Long(int64_t value) override { |
| if (should_skip()) return; |
| to_string_.Long(value); |
| } |
| void ULong(uint64_t value) override { |
| if (should_skip()) return; |
| to_string_.ULong(value); |
| } |
| void Float(float value) override { |
| if (should_skip()) return; |
| to_string_.Float(value); |
| } |
| void Double(double value) override { |
| if (should_skip()) return; |
| to_string_.Double(value); |
| } |
| void String(const flatbuffers::String *value) override { |
| if (should_skip()) return; |
| to_string_.String(value); |
| } |
| void Unknown(const uint8_t *value) override { |
| if (should_skip()) return; |
| to_string_.Unknown(value); |
| } |
| void Element(size_t i, flatbuffers::ElementaryType type, |
| const flatbuffers::TypeTable *type_table, |
| const uint8_t *val) override { |
| if (should_skip()) return; |
| to_string_.Element(i, type, type_table, val); |
| } |
| |
| virtual void StartVector(size_t size) override { |
| if (should_skip()) { |
| ++skip_levels_; |
| return; |
| } |
| if (size > max_vector_size_) { |
| ++skip_levels_; |
| to_string_.s += "[ \"... " + std::to_string(size) + " elements ...\" ]"; |
| return; |
| } |
| to_string_.StartVector(size); |
| } |
| virtual void EndVector() override { |
| if (should_skip()) { |
| --skip_levels_; |
| return; |
| } |
| to_string_.EndVector(); |
| } |
| |
| std::string &string() { return to_string_.s; } |
| |
| private: |
| bool should_skip() const { return skip_levels_ > 0; } |
| |
| const size_t max_vector_size_; |
| flatbuffers::ToStringVisitor to_string_; |
| int skip_levels_ = 0; |
| }; |
| |
| } // namespace |
| |
| ::std::string TableFlatbufferToJson(const flatbuffers::Table *t, |
| const ::flatbuffers::TypeTable *typetable, |
| JsonOptions json_options) { |
| // 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"; |
| } |
| TruncatingStringVisitor tostring_visitor( |
| json_options.max_vector_size, json_options.multi_line ? "\n" : " ", true, |
| json_options.multi_line ? " " : "", json_options.multi_line); |
| flatbuffers::IterateObject(reinterpret_cast<const uint8_t *>(t), typetable, |
| &tostring_visitor); |
| return tostring_visitor.string(); |
| } |
| |
| } // namespace aos |