Add flatbuffer to json using reflection schema
Change-Id: I6f32c0403a74ec951e6c730c4006ede3bc91099c
diff --git a/aos/BUILD b/aos/BUILD
index 8683d18..fca684b 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -336,6 +336,7 @@
flatbuffer_cc_library(
name = "json_to_flatbuffer_flatbuffer",
srcs = ["json_to_flatbuffer.fbs"],
+ gen_reflections = 1,
)
cc_library(
@@ -359,7 +360,10 @@
cc_library(
name = "json_to_flatbuffer",
- srcs = ["json_to_flatbuffer.cc"],
+ srcs = [
+ "flatbuffer_introspection.cc",
+ "json_to_flatbuffer.cc",
+ ],
hdrs = ["json_to_flatbuffer.h"],
visibility = ["//visibility:public"],
deps = [
@@ -384,6 +388,23 @@
],
)
+cc_test(
+ name = "flatbuffer_introspection_test",
+ srcs = [
+ "flatbuffer_introspection_test.cc",
+ ],
+ data = [
+ ":json_to_flatbuffer_flatbuffer_reflection_out",
+ ],
+ deps = [
+ ":json_to_flatbuffer",
+ ":json_to_flatbuffer_flatbuffer",
+ "//aos/testing:googletest",
+ "//aos/util:file",
+ "@com_github_google_flatbuffers//:flatbuffers",
+ ],
+)
+
cc_library(
name = "flatbuffer_merge",
srcs = ["flatbuffer_merge.cc"],
@@ -440,6 +461,7 @@
"testdata/expected.json",
"//aos/events:config.fb.json",
"//aos/events:ping.bfbs",
+ "//aos/events:pingpong_config.json",
"//aos/events:pong.bfbs",
],
deps = [
@@ -462,3 +484,18 @@
"@com_github_google_glog//:glog",
],
)
+
+cc_binary(
+ name = "log_fbs_shmem",
+ srcs = [
+ "log_fbs_shmem.cc",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":configuration",
+ ":json_to_flatbuffer",
+ "//aos:init",
+ "//aos/events:shm_event_loop",
+ "@com_github_google_glog//:glog",
+ ],
+)
diff --git a/aos/flatbuffer_introspection.cc b/aos/flatbuffer_introspection.cc
new file mode 100644
index 0000000..751666d
--- /dev/null
+++ b/aos/flatbuffer_introspection.cc
@@ -0,0 +1,279 @@
+#include <iostream>
+#include <sstream>
+
+#include "aos/json_to_flatbuffer.h"
+
+namespace aos {
+
+namespace {
+
+using reflection::BaseType;
+
+void IntToString(int64_t val, reflection::BaseType type,
+ std::stringstream *out) {
+ switch (type) {
+ case BaseType::Bool:
+ *out << (val ? "true" : "false");
+ break;
+ case BaseType::UByte:
+ *out << std::to_string(static_cast<uint8_t>(val));
+ break;
+ case BaseType::Byte:
+ *out << std::to_string(static_cast<int8_t>(val));
+ break;
+ case BaseType::Short:
+ *out << static_cast<int16_t>(val);
+ break;
+ case BaseType::UShort:
+ *out << static_cast<uint16_t>(val);
+ break;
+ case BaseType::Int:
+ *out << static_cast<int32_t>(val);
+ break;
+ case BaseType::UInt:
+ *out << static_cast<uint32_t>(val);
+ break;
+ case BaseType::Long:
+ *out << static_cast<int64_t>(val);
+ break;
+ case BaseType::ULong:
+ *out << static_cast<uint64_t>(val);
+ break;
+ default:
+ *out << "null";
+ }
+}
+
+void FloatToString(double val, reflection::BaseType type,
+ std::stringstream *out) {
+ switch (type) {
+ case BaseType::Float:
+ out->precision(std::numeric_limits<float>::digits10);
+ *out << static_cast<float>(val);
+ break;
+ case BaseType::Double:
+ out->precision(std::numeric_limits<double>::digits10);
+ *out << val;
+ break;
+ default:
+ *out << "null";
+ }
+}
+
+template <typename ObjT>
+void ObjectToString(
+ const reflection::Object *obj,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
+ const ObjT *object, std::stringstream *out);
+
+// Get enum value name
+const char *EnumToString(
+ int64_t enum_value,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::EnumVal>>
+ *values) {
+ // Replace with binary search? Enum values are pre-sorted.
+ for (auto iter = values->begin(); iter != values->end(); iter++) {
+ if (enum_value == iter->value()) {
+ return iter->name()->c_str();
+ }
+ }
+ return nullptr;
+}
+
+// Convert integer to string, checking if it is an enum.
+void IntOrEnumToString(
+ int64_t val, const reflection::Type *type,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
+ std::stringstream *out) {
+ // Check if integer is an enum and print string, otherwise fallback to
+ // printing as int.
+ if (type->index() > -1 && type->index() < (int32_t)enums->size()) {
+ const reflection::Enum *enum_props = enums->Get(type->index());
+ if (!enum_props->is_union()) {
+ const char *value_string = EnumToString(val, enum_props->values());
+
+ if (value_string != nullptr) {
+ *out << '"' << value_string << '"';
+ }
+ }
+ } else {
+ if (type->base_type() == BaseType::Vector ||
+ type->base_type() == BaseType::Array) {
+ IntToString(val, type->element(), out);
+ } else {
+ IntToString(val, type->base_type(), out);
+ }
+ }
+}
+
+// Print field in flatbuffer table. Field must be populated.
+template <typename ObjT>
+void FieldToString(
+ const ObjT *table, const reflection::Field *field,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
+ std::stringstream *out) {
+ const reflection::Type *type = field->type();
+
+ switch (type->base_type()) {
+ case BaseType::Bool:
+ case BaseType::UByte:
+ case BaseType::Byte:
+ case BaseType::Short:
+ case BaseType::UShort:
+ case BaseType::Int:
+ case BaseType::UInt:
+ case BaseType::Long:
+ case BaseType::ULong:
+ IntOrEnumToString(GetAnyFieldI(*table, *field), type, enums, out);
+ break;
+ case BaseType::Float:
+ case BaseType::Double:
+ FloatToString(GetAnyFieldF(*table, *field), type->base_type(), out);
+ break;
+ case BaseType::String:
+ if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
+ std::string str = flatbuffers::GetFieldS(*table, *field)->str();
+ std::string out_str;
+ out_str.reserve(str.size());
+ for (char c : str) {
+ // out_str += c;
+ switch (c) {
+ case '"':
+ out_str += "\\\"";
+ break;
+ case '\\':
+ out_str += "\\\\";
+ break;
+ case '\b':
+ out_str += "\\b";
+ break;
+ case '\f':
+ out_str += "\\f";
+ break;
+ case '\n':
+ out_str += "\\n";
+ break;
+ case '\r':
+ out_str += "\\r";
+ break;
+ case '\t':
+ out_str += "\\t";
+ break;
+ default:
+ out_str += c;
+ }
+ }
+ *out << '"' << out_str << '"';
+ } else {
+ *out << "null";
+ }
+ break;
+ case BaseType::Vector: {
+ if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
+ const flatbuffers::VectorOfAny *vector =
+ flatbuffers::GetFieldAnyV(*table, *field);
+ reflection::BaseType elem_type = type->element();
+
+ *out << '[';
+ for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
+ if (i != 0) {
+ *out << ", ";
+ }
+ if (flatbuffers::IsInteger(elem_type)) {
+ IntOrEnumToString(
+ flatbuffers::GetAnyVectorElemI(vector, elem_type, i), type,
+ enums, out);
+ } else if (flatbuffers::IsFloat(elem_type)) {
+ FloatToString(flatbuffers::GetAnyVectorElemF(vector, elem_type, i),
+ elem_type, out);
+ } else if (elem_type == BaseType::String) {
+ *out << '"' << flatbuffers::GetAnyVectorElemS(vector, elem_type, i)
+ << '"';
+ } else if (elem_type == BaseType::Obj) {
+ if (type->index() > -1 &&
+ type->index() < (int32_t)objects->size()) {
+ if (objects->Get(type->index())->is_struct()) {
+ ObjectToString(
+ objects->Get(type->index()), objects, enums,
+ flatbuffers::GetAnyVectorElemAddressOf<
+ const flatbuffers::Struct>(
+ vector, i, objects->Get(type->index())->bytesize()),
+ out);
+ } else {
+ ObjectToString(objects->Get(type->index()), objects, enums,
+ flatbuffers::GetAnyVectorElemPointer<
+ const flatbuffers::Table>(vector, i),
+ out);
+ }
+ }
+ }
+ }
+ *out << ']';
+ } else {
+ *out << "null";
+ }
+ } break;
+ case BaseType::Obj: {
+ if (type->index() > -1 && type->index() < (int32_t)objects->size()) {
+ if (objects->Get(type->index())->is_struct()) {
+ ObjectToString(objects->Get(type->index()), objects, enums,
+ flatbuffers::GetFieldStruct(*table, *field), out);
+ } else if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
+ ObjectToString(objects->Get(type->index()), objects, enums,
+ flatbuffers::GetFieldT(*table, *field), out);
+ }
+ } else {
+ *out << "null";
+ }
+ } break;
+ default:
+ *out << "null";
+ }
+}
+
+// Prints flatbuffer table or struct given list of possible child objects and
+// enums. Prints "null" if the child object type is not found.
+template <typename ObjT>
+void ObjectToString(
+ const reflection::Object *obj,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
+ const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
+ const ObjT *object, std::stringstream *out) {
+ static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
+ std::is_same<flatbuffers::Struct, ObjT>(),
+ "Type must be either flatbuffer table or struct");
+ bool print_sep = false;
+ *out << '{';
+ for (const reflection::Field *field : *obj->fields()) {
+ // Check whether this object has the field populated (even for structs,
+ // which should have all fields populated)
+ if (object->GetAddressOf(field->offset())) {
+ if (print_sep) {
+ *out << ", ";
+ } else {
+ print_sep = true;
+ }
+ *out << '"' << field->name()->c_str() << "\": ";
+ FieldToString(object, field, objects, enums, out);
+ }
+ }
+ *out << '}';
+}
+
+} // namespace
+
+std::string FlatbufferToJson(const reflection::Schema *schema,
+ const uint8_t *data) {
+ const flatbuffers::Table *table = flatbuffers::GetAnyRoot(data);
+
+ const reflection::Object *obj = schema->root_table();
+
+ std::stringstream out;
+
+ ObjectToString(obj, schema->objects(), schema->enums(), table, &out);
+
+ return out.str();
+}
+} // namespace aos
diff --git a/aos/flatbuffer_introspection_test.cc b/aos/flatbuffer_introspection_test.cc
new file mode 100644
index 0000000..54a2ab2
--- /dev/null
+++ b/aos/flatbuffer_introspection_test.cc
@@ -0,0 +1,317 @@
+#include "aos/json_to_flatbuffer.h"
+#include "aos/json_to_flatbuffer_generated.h"
+#include "aos/util/file.h"
+#include "flatbuffers/reflection.h"
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+class FlatbufferIntrospectionTest : public ::testing::Test {
+ public:
+ FlatbufferIntrospectionTest()
+ : schema_data_(
+ util::ReadFileToStringOrDie("aos/json_to_flatbuffer.bfbs")) {
+ schema_ = reflection::GetSchema(schema_data_.data());
+ }
+
+ protected:
+ FlatbufferString<reflection::Schema> schema_data_;
+ const reflection::Schema *schema_;
+};
+
+TEST_F(FlatbufferIntrospectionTest, IntegerTest) {
+ flatbuffers::FlatBufferBuilder builder;
+ ConfigurationBuilder config_builder(builder);
+
+ config_builder.add_foo_byte(-5);
+ config_builder.add_foo_ubyte(5);
+ config_builder.add_foo_bool(true);
+
+ config_builder.add_foo_short(-10);
+ config_builder.add_foo_ushort(10);
+
+ config_builder.add_foo_int(-20);
+ config_builder.add_foo_uint(20);
+
+ config_builder.add_foo_long(-100);
+ config_builder.add_foo_ulong(100);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out,
+ "{\"foo_bool\": true, \"foo_byte\": -5, \"foo_int\": -20, "
+ "\"foo_long\": -100, \"foo_short\": -10, \"foo_ubyte\": 5, "
+ "\"foo_uint\": 20, \"foo_ulong\": 100, \"foo_ushort\": 10}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, FloatTest) {
+ flatbuffers::FlatBufferBuilder builder;
+ ConfigurationBuilder config_builder(builder);
+
+ config_builder.add_foo_float(1.0 / 3.0);
+ config_builder.add_foo_double(5.0 / 9.0);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out,
+ "{\"foo_double\": 0.555555555555556, \"foo_float\": 0.333333}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, VectorScalarTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ // Flatbuffers don't like creating vectors simultaneously with table, so do
+ // first.
+ auto foo_bytes = builder.CreateVector<int8_t>({-3, -2, -1, 0, 1, 2, 3});
+ auto foo_ubytes = builder.CreateVector<uint8_t>({0, 1, 2, 3, 4, 5, 6});
+ auto foo_bools = builder.CreateVector<uint8_t>({true, false, true, false});
+
+ auto foo_shorts =
+ builder.CreateVector<int16_t>({-30, -20, -10, 0, 10, 20, 30});
+ auto foo_ushorts =
+ builder.CreateVector<uint16_t>({0, 10, 20, 30, 40, 50, 60});
+
+ auto foo_ints =
+ builder.CreateVector<int32_t>({-300, -200, -100, 0, 100, 200, 300});
+ auto foo_uints =
+ builder.CreateVector<uint32_t>({0, 100, 200, 300, 400, 500, 600});
+
+ auto foo_longs =
+ builder.CreateVector<int64_t>({-3000, -2000, -1000, 0, 1000, 2000, 3000});
+ auto foo_ulongs =
+ builder.CreateVector<uint64_t>({0, 1000, 2000, 3000, 4000, 5000, 6000});
+
+ auto foo_floats =
+ builder.CreateVector<float>({0.0, 1.0 / 9.0, 2.0 / 9.0, 3.0 / 9.0});
+ auto foo_doubles =
+ builder.CreateVector<double>({0, 1.0 / 9.0, 2.0 / 9.0, 3.0 / 9.0});
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_vector_foo_byte(foo_bytes);
+ config_builder.add_vector_foo_ubyte(foo_ubytes);
+ config_builder.add_vector_foo_bool(foo_bools);
+
+ config_builder.add_vector_foo_short(foo_shorts);
+ config_builder.add_vector_foo_ushort(foo_ushorts);
+
+ config_builder.add_vector_foo_int(foo_ints);
+ config_builder.add_vector_foo_uint(foo_uints);
+
+ config_builder.add_vector_foo_long(foo_longs);
+ config_builder.add_vector_foo_ulong(foo_ulongs);
+
+ config_builder.add_vector_foo_float(foo_floats);
+ config_builder.add_vector_foo_double(foo_doubles);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(
+ out,
+ "{\"vector_foo_bool\": [true, false, true, false], \"vector_foo_byte\": "
+ "[-3, -2, -1, 0, 1, 2, 3], \"vector_foo_double\": [0, 0.111111111111111, "
+ "0.222222222222222, 0.333333333333333], \"vector_foo_float\": [0, "
+ "0.111111, 0.222222, 0.333333], \"vector_foo_int\": [-300, -200, -100, "
+ "0, 100, 200, 300], \"vector_foo_long\": [-3000, -2000, -1000, 0, 1000, "
+ "2000, 3000], \"vector_foo_short\": [-30, -20, -10, 0, 10, 20, 30], "
+ "\"vector_foo_ubyte\": [0, 1, 2, 3, 4, 5, 6], \"vector_foo_uint\": [0, "
+ "100, 200, 300, 400, 500, 600], \"vector_foo_ulong\": [0, 1000, 2000, "
+ "3000, 4000, 5000, 6000], \"vector_foo_ushort\": [0, 10, 20, 30, 40, 50, "
+ "60]}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, StringTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ auto foo_string = builder.CreateString("I <3 FlatBuffers!");
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_foo_string(foo_string);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out, "{\"foo_string\": \"I <3 FlatBuffers!\"}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, EnumTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_foo_enum(BaseType_UShort);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out, "{\"foo_enum\": \"UShort\"}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, VectorStringTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ std::vector<std::vector<std::string>> words{
+ {"abc", "acb"}, {"bac", "bca"}, {"cab", "cba"}};
+ std::vector<flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>>>
+ strings;
+
+ for (const auto &v : words) {
+ strings.push_back(builder.CreateVectorOfStrings(v));
+ }
+
+ std::vector<flatbuffers::Offset<VectorOfStrings>> sub_vectors;
+
+ for (const auto &v : strings) {
+ VectorOfStringsBuilder v_builder(builder);
+ v_builder.add_str(v);
+ sub_vectors.push_back(v_builder.Finish());
+ }
+
+ auto foo_vov = builder.CreateVector(sub_vectors);
+
+ VectorOfVectorOfStringBuilder vov_builder(builder);
+ vov_builder.add_v(foo_vov);
+ auto vov = vov_builder.Finish();
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_vector_foo_string(strings[0]);
+ config_builder.add_vov(vov);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out,
+ "{\"vector_foo_string\": [\"abc\", \"acb\"], \"vov\": {\"v\": "
+ "[{\"str\": [\"abc\", \"acb\"]}, {\"str\": [\"bac\", \"bca\"]}, "
+ "{\"str\": [\"cab\", \"cba\"]}]}}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, TableTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ auto foo_string2 = builder.CreateString("Nested Config String");
+ auto foo_bytes2 = builder.CreateVector<int8_t>({6, 7, 8, 9, 10});
+
+ ConfigurationBuilder config_builder2(builder);
+ config_builder2.add_foo_byte(10);
+ config_builder2.add_foo_string(foo_string2);
+ config_builder2.add_vector_foo_byte(foo_bytes2);
+
+ flatbuffers::Offset<Configuration> config_2 = config_builder2.Finish();
+
+ auto foo_string = builder.CreateString("Root Config String");
+ auto foo_bytes = builder.CreateVector<int8_t>({0, 1, 2, 3, 4, 5});
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_nested_config(config_2);
+ config_builder.add_foo_byte(5);
+ config_builder.add_foo_string(foo_string);
+ config_builder.add_vector_foo_byte(foo_bytes);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out,
+ "{\"foo_byte\": 5, \"foo_string\": \"Root Config String\", "
+ "\"nested_config\": {\"foo_byte\": 10, \"foo_string\": \"Nested "
+ "Config String\", \"vector_foo_byte\": [6, 7, 8, 9, 10]}, "
+ "\"vector_foo_byte\": [0, 1, 2, 3, 4, 5]}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, StructTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ FooStructNested foo_struct2(10);
+
+ FooStruct foo_struct(5, foo_struct2);
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_foo_struct(&foo_struct);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out,
+ "{\"foo_struct\": {\"foo_byte\": 5, \"nested_struct\": "
+ "{\"foo_byte\": 10}}}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, VectorStructTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ FooStructNested foo_struct2(1);
+
+ auto structs = builder.CreateVectorOfStructs(
+ std::vector<FooStruct>({{5, foo_struct2}, {10, foo_struct2}}));
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_vector_foo_struct(structs);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out,
+ "{\"vector_foo_struct\": [{\"foo_byte\": 5, \"nested_struct\": "
+ "{\"foo_byte\": 1}}, {\"foo_byte\": 10, \"nested_struct\": "
+ "{\"foo_byte\": 1}}]}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, VectorEnumTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ auto enums = builder.CreateVector<int8_t>(
+ {BaseType_UShort, BaseType_Obj, BaseType_UInt});
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_vector_foo_enum(enums);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out, "{\"vector_foo_enum\": [\"UShort\", \"Obj\", \"UInt\"]}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, StructEnumTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ StructEnum foo_struct(BaseType_UShort);
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_foo_struct_enum(&foo_struct);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+
+ EXPECT_EQ(out, "{\"foo_struct_enum\": {\"foo_enum\": \"UShort\"}}");
+}
+
+TEST_F(FlatbufferIntrospectionTest, StringEscapeTest) {
+ flatbuffers::FlatBufferBuilder builder;
+
+ auto foo_string = builder.CreateString("\"\\\b\f\n\r\t");
+
+ ConfigurationBuilder config_builder(builder);
+ config_builder.add_foo_string(foo_string);
+
+ builder.Finish(config_builder.Finish());
+
+ std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer());
+ EXPECT_EQ(out, "{\"foo_string\": \"\\\"\\\\\\b\\f\\n\\r\\t\"}");
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/json_to_flatbuffer.fbs b/aos/json_to_flatbuffer.fbs
index 8942cc8..8c0402c 100644
--- a/aos/json_to_flatbuffer.fbs
+++ b/aos/json_to_flatbuffer.fbs
@@ -47,6 +47,19 @@
v:[VectorOfStrings];
}
+struct FooStructNested {
+ foo_byte:byte;
+}
+
+struct FooStruct {
+ foo_byte:byte;
+ nested_struct:FooStructNested;
+}
+
+struct StructEnum {
+ foo_enum:BaseType;
+}
+
table Configuration {
locations:[Location] (id: 0);
maps:[Map] (id: 1);
@@ -108,6 +121,10 @@
nested_config:Configuration (id: 32);
vov:VectorOfVectorOfString (id: 33);
+
+ foo_struct:FooStruct (id: 34);
+ vector_foo_struct:[FooStruct] (id: 35);
+ foo_struct_enum:StructEnum (id: 36);
}
root_type Configuration;
diff --git a/aos/json_to_flatbuffer.h b/aos/json_to_flatbuffer.h
index 94b982a..f90a602 100644
--- a/aos/json_to_flatbuffer.h
+++ b/aos/json_to_flatbuffer.h
@@ -7,13 +7,13 @@
#include "aos/flatbuffers.h"
#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/reflection.h"
namespace aos {
// Parses the flatbuffer into the vector, or returns an empty vector.
flatbuffers::DetachedBuffer JsonToFlatbuffer(
- const std::string_view data,
- const flatbuffers::TypeTable *typetable);
+ const std::string_view data, const flatbuffers::TypeTable *typetable);
// Converts a flatbuffer into a Json string.
// multi_line controls if the Json is written out on multiple lines or one.
@@ -53,6 +53,9 @@
Flatbuffer<T>::MiniReflectTypeTable(), multi_line);
}
+std::string FlatbufferToJson(const reflection::Schema *const schema,
+ const uint8_t *const data);
+
} // namespace aos
#endif // AOS_JSON_TO_FLATBUFFER_H_
diff --git a/aos/log_fbs_shmem.cc b/aos/log_fbs_shmem.cc
new file mode 100644
index 0000000..c2ed056
--- /dev/null
+++ b/aos/log_fbs_shmem.cc
@@ -0,0 +1,66 @@
+#include <iostream>
+#include <map>
+
+#include "aos/configuration.h"
+#include "aos/events/shm_event_loop.h"
+#include "aos/init.h"
+#include "aos/json_to_flatbuffer.h"
+#include "gflags/gflags.h"
+
+DEFINE_string(config, "./config.json", "File path of aos configuration");
+int main(int argc, char **argv) {
+ aos::InitGoogle(&argc, &argv);
+
+ std::string channel_name;
+ std::string message_type;
+ if (argc > 1) {
+ channel_name = argv[1];
+ }
+ if (argc > 2) {
+ message_type = argv[2];
+ }
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(FLAGS_config);
+
+ const aos::Configuration *config_msg = &config.message();
+ ::aos::ShmEventLoop event_loop(config_msg);
+
+ if (argc == 1) {
+ std::cout << "Channels:\n";
+ for (const aos::Channel *channel : *config_msg->channels()) {
+ std::cout << channel->name()->c_str() << ' ' << channel->type()->c_str()
+ << '\n';
+ }
+ return 0;
+ }
+
+ int found_channels = 0;
+ const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
+ config_msg->channels();
+ for (const aos::Channel *channel : *channels) {
+ if (channel->name()->c_str() == channel_name &&
+ channel->type()->str().find(message_type) != std::string::npos) {
+ event_loop.MakeRawWatcher(
+ channel,
+ [channel](const aos::Context /* &context*/, const void *message) {
+ LOG(INFO) << '(' << channel->type()->c_str() << ") "
+ << aos::FlatbufferToJson(
+ channel->schema(),
+ static_cast<const uint8_t *>(message))
+ << '\n';
+ });
+ found_channels++;
+ }
+ }
+
+ if (found_channels == 0) {
+ LOG(FATAL) << "Could not find any channels with the given name and type.";
+ } else if (found_channels > 1 && message_type.size() != 0) {
+ LOG(FATAL) << "Multiple channels found with same type";
+ }
+
+ event_loop.Run();
+ ::aos::Cleanup();
+ return 0;
+}