blob: 2a19557fbbe4faaab414be24a9d7df5866a35477 [file] [log] [blame]
#include "aos/json_to_flatbuffer.h"
#include <cstddef>
#include <cstdio>
#include <string_view>
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/minireflect.h"
#include "aos/flatbuffer_utils.h"
#include "aos/json_tokenizer.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, STRUCT };
// Constructs an Element holding an integer.
Element(absl::int128 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) {}
// Constructs an Element holding a struct.
Element(std::vector<uint8_t> struct_data)
: /*initialize the union member to keep the compiler happy*/ int_element(
0),
struct_data(std::move(struct_data)),
type(ElementType::STRUCT) {}
// Union for the various datatypes.
union {
absl::int128 int_element;
double double_element;
flatbuffers::Offset<flatbuffers::String> offset_element;
};
// Because we can't know the maximum size of any potential structs at
// compile-time, we will use a vector to store the vector data inline.
// If you were to do a reinterpret_cast<StructType*>(struct_data.data()) then
// you would have an instance of the struct in question.
std::vector<uint8_t> struct_data;
// And an enum signaling which one is in use.
ElementType type;
};
// Structure to represent a field element.
struct FieldElement {
FieldElement(int new_field_index, absl::int128 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) {}
FieldElement(int new_field_index, const Element &element)
: element(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,
absl::int128 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);
bool AddSingleElement(FlatbufferType type, int field_index,
const std::vector<uint8_t> &struct_data,
flatbuffers::FlatBufferBuilder *fbb);
template <typename T, typename U>
void SetMemory(U value, uint8_t *destination) {
// destination may be poorly aligned. As such, we should not simply do
// *reinterpret_cast<T*>(destination) = value directly.
const T casted = static_cast<T>(value);
memcpy(destination, &casted, sizeof(T));
}
bool SetStructElement(FlatbufferType type, int field_index, absl::int128 value,
uint8_t *destination) {
const flatbuffers::ElementaryType elementary_type =
type.FieldElementaryType(field_index);
switch (elementary_type) {
case flatbuffers::ET_CHAR:
SetMemory<int8_t>(value, destination);
break;
case flatbuffers::ET_UCHAR:
SetMemory<uint8_t>(value, destination);
break;
case flatbuffers::ET_SHORT:
SetMemory<int16_t>(value, destination);
break;
case flatbuffers::ET_USHORT:
SetMemory<uint16_t>(value, destination);
break;
case flatbuffers::ET_INT:
SetMemory<int32_t>(value, destination);
break;
case flatbuffers::ET_UINT:
SetMemory<uint32_t>(value, destination);
break;
case flatbuffers::ET_LONG:
SetMemory<int64_t>(value, destination);
break;
case flatbuffers::ET_ULONG:
SetMemory<uint64_t>(value, destination);
break;
case flatbuffers::ET_BOOL:
SetMemory<bool>(value, destination);
break;
case flatbuffers::ET_FLOAT:
SetMemory<float>(value, destination);
break;
case flatbuffers::ET_DOUBLE:
SetMemory<double>(value, destination);
break;
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 true;
}
bool SetStructElement(FlatbufferType type, int field_index, double value,
uint8_t *destination) {
const flatbuffers::ElementaryType elementary_type =
type.FieldElementaryType(field_index);
switch (elementary_type) {
case flatbuffers::ET_FLOAT:
SetMemory<float>(value, destination);
break;
case flatbuffers::ET_DOUBLE:
SetMemory<double>(value, destination);
break;
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_BOOL:
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 true;
}
// Writes an array of FieldElement (with the definition in "type") to the
// builder. Returns the offset of the resulting table.
std::optional<Element> WriteObject(FlatbufferType type,
const ::std::vector<FieldElement> &elements,
flatbuffers::FlatBufferBuilder *fbb) {
// End of a nested object! Add it.
if (type.IsTable()) {
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 Element{
flatbuffers::Offset<flatbuffers::String>{fbb->EndTable(start)}};
} else if (type.IsStruct()) {
// In order to write an inline struct, we need to fill out each field at the
// correct position inline in memory. In order to do this, we retrieve the
// offset/size of each field, and directly populate that memory with the
// relevant value.
std::vector<uint8_t> buffer(type.InlineSize(), 0);
for (size_t field_index = 0;
field_index < static_cast<size_t>(type.NumberFields());
++field_index) {
auto it = std::find_if(elements.begin(), elements.end(),
[field_index](const FieldElement &field) {
return field.field_index ==
static_cast<int>(field_index);
});
if (it == elements.end()) {
fprintf(stderr,
"All fields must be specified for struct types (field %s "
"missing).\n",
type.FieldName(field_index).data());
return std::nullopt;
}
uint8_t *field_data = buffer.data() + type.StructFieldOffset(field_index);
const size_t field_size = type.FieldInlineSize(field_index);
switch (it->element.type) {
case Element::ElementType::INT:
if (!SetStructElement(type, field_index, it->element.int_element,
field_data)) {
return std::nullopt;
}
break;
case Element::ElementType::DOUBLE:
if (!SetStructElement(type, field_index, it->element.double_element,
field_data)) {
return std::nullopt;
}
break;
case Element::ElementType::STRUCT:
CHECK_EQ(field_size, it->element.struct_data.size());
memcpy(field_data, it->element.struct_data.data(), field_size);
break;
case Element::ElementType::OFFSET:
LOG(FATAL)
<< "This should be unreachable; structs cannot contain offsets.";
break;
}
}
return Element{buffer};
}
LOG(FATAL) << "Unimplemented.";
}
// 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, absl::int128 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,
absl::int128 int_value);
bool PushElement(flatbuffers::ElementaryType elementary_type,
double double_value);
bool PushElement(flatbuffers::ElementaryType elementary_type,
flatbuffers::Offset<flatbuffers::String> offset_value);
bool PushElement(const FlatbufferType &type,
const std::vector<uint8_t> &struct_data);
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:
fprintf(stderr, "Encountered an error in the tokenizer\n");
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 object! Add it.
std::optional<Element> object =
WriteObject(stack_.back().type, stack_.back().elements, fbb_);
if (!object.has_value()) {
return false;
}
// We now want to talk about the parent structure. Pop the child.
stack_.pop_back();
if (stack_.size() == 0) {
CHECK_EQ(static_cast<int>(object->type),
static_cast<int>(Element::ElementType::OFFSET))
<< ": JSON parsing only supports parsing flatbuffer tables.";
// Instead of queueing it up in the stack, return it through the
// passed in variable.
*table_end = object->offset_element.o;
} 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(
std::move(object.value()));
} else {
stack_.back().elements.emplace_back(field_index,
std::move(object.value()));
}
}
}
break;
case Tokenizer::TokenType::kStartArray: // [
if (stack_.size() == 0) {
fprintf(stderr,
"We don't support an array of structs at the root level.\n");
return false;
}
// Sanity check that we aren't trying to make a vector of vectors.
if (in_vector()) {
fprintf(stderr, "We don't support vectors of vectors.\n");
return false;
}
set_in_vector(true);
break;
case Tokenizer::TokenType::kEndArray: { // ]
if (!in_vector()) {
fprintf(stderr, "Encountered ']' with no prior '['.\n");
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;
absl::int128 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.
absl::int128 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, absl::int128 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 (JSON "
"believes that we are%s in a vector for field '%s').\n",
in_vector() ? "" : " not",
stack_.back().type.FieldName(field_index).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;
}
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 (JSON "
"believes that we are%s in a vector for field '%s').\n",
in_vector() ? "" : " not",
stack_.back().type.FieldName(field_index).data());
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 (JSON "
"believes that we are%s in a vector for field '%s').\n",
in_vector() ? "" : " not",
stack_.back().type.FieldName(field_index).data());
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<absl::int128> 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);
case Element::ElementType::STRUCT:
return AddSingleElement(type, field_element.field_index,
field_element.element.struct_data, fbb);
}
return false;
}
bool AddSingleElement(FlatbufferType type, int field_index,
absl::int128 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, static_cast<bool>(int_value));
return true;
case flatbuffers::ET_CHAR:
fbb->AddElement<int8_t>(field_offset, static_cast<int8_t>(int_value));
return true;
case flatbuffers::ET_UCHAR:
fbb->AddElement<uint8_t>(field_offset, static_cast<uint8_t>(int_value));
return true;
case flatbuffers::ET_SHORT:
fbb->AddElement<int16_t>(field_offset, static_cast<int16_t>(int_value));
return true;
case flatbuffers::ET_USHORT:
fbb->AddElement<uint16_t>(field_offset, static_cast<uint16_t>(int_value));
return true;
case flatbuffers::ET_INT:
fbb->AddElement<int32_t>(field_offset, static_cast<int32_t>(int_value));
return true;
case flatbuffers::ET_UINT:
fbb->AddElement<uint32_t>(field_offset, static_cast<uint32_t>(int_value));
return true;
case flatbuffers::ET_LONG:
fbb->AddElement<int64_t>(field_offset, static_cast<int64_t>(int_value));
return true;
case flatbuffers::ET_ULONG:
fbb->AddElement<uint64_t>(field_offset, static_cast<uint64_t>(int_value));
return true;
// The floating point cases occur when someone specifies an integer in the
// JSON for a double field.
case flatbuffers::ET_FLOAT:
fbb->AddElement<float>(field_offset, static_cast<float>(int_value));
return true;
case flatbuffers::ET_DOUBLE:
fbb->AddElement<double>(field_offset, static_cast<double>(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 AddSingleElement(FlatbufferType type, int field_index,
const std::vector<uint8_t> &data,
flatbuffers::FlatBufferBuilder *fbb) {
// Structs are always inline.
// We have to do somewhat manual serialization to get the struct into place,
// since the regular FlatBufferBuilder assumes that you will know the type of
// the struct that you are constructing at compile time.
fbb->Align(type.FieldType(field_index).Alignment());
fbb->PushBytes(data.data(), data.size());
fbb->AddStructOffset(flatbuffers::FieldIndexToOffset(
static_cast<flatbuffers::voffset_t>(field_index)),
fbb->GetSize());
return true;
}
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);
const size_t alignment = stack_.back().type.FieldInlineAlignment(field_index);
fbb_->StartVector(stack_.back().vector_elements.size(), inline_size,
/*align=*/alignment);
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;
case Element::ElementType::STRUCT:
if (!PushElement(stack_.back().type.FieldType(field_index),
element.struct_data))
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,
absl::int128 int_value) {
switch (elementary_type) {
case flatbuffers::ET_BOOL:
fbb_->PushElement<bool>(static_cast<bool>(int_value));
return true;
case flatbuffers::ET_CHAR:
fbb_->PushElement<int8_t>(static_cast<int8_t>(int_value));
return true;
case flatbuffers::ET_UCHAR:
fbb_->PushElement<uint8_t>(static_cast<uint8_t>(int_value));
return true;
case flatbuffers::ET_SHORT:
fbb_->PushElement<int16_t>(static_cast<int16_t>(int_value));
return true;
case flatbuffers::ET_USHORT:
fbb_->PushElement<uint16_t>(static_cast<uint16_t>(int_value));
return true;
case flatbuffers::ET_INT:
fbb_->PushElement<int32_t>(static_cast<int32_t>(int_value));
return true;
case flatbuffers::ET_UINT:
fbb_->PushElement<uint32_t>(static_cast<uint32_t>(int_value));
return true;
case flatbuffers::ET_LONG:
fbb_->PushElement<int64_t>(static_cast<int64_t>(int_value));
return true;
case flatbuffers::ET_ULONG:
fbb_->PushElement<uint64_t>(static_cast<uint64_t>(int_value));
return true;
case flatbuffers::ET_FLOAT:
fbb_->PushElement<float>(static_cast<float>(int_value));
return true;
case flatbuffers::ET_DOUBLE:
fbb_->PushElement<double>(static_cast<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(const FlatbufferType &type,
const std::vector<uint8_t> &struct_data) {
// To add a struct to a vector, we just need to get the relevant bytes pushed
// straight into the builder. The FlatBufferBuilder normally expects that you
// will know the type of your struct at compile-time, so doesn't have a
// first-class way to do this.
fbb_->Align(type.Alignment());
fbb_->PushBytes(struct_data.data(), struct_data.size());
return true;
}
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