Improve flatbuffer reflection performance
Change-Id: I86b4ce21e8fe4ac2913dcd2c9e3c466c3f66a227
diff --git a/aos/flatbuffer_introspection.cc b/aos/flatbuffer_introspection.cc
index 48200b7..0fc37a7 100644
--- a/aos/flatbuffer_introspection.cc
+++ b/aos/flatbuffer_introspection.cc
@@ -11,57 +11,55 @@
using reflection::BaseType;
void IntToString(int64_t val, reflection::BaseType type,
- std::stringstream *out) {
+ FastStringBuilder *out) {
switch (type) {
case BaseType::Bool:
- *out << (val ? "true" : "false");
+ out->AppendBool(static_cast<bool>(val));
break;
case BaseType::UByte:
- *out << std::to_string(static_cast<uint8_t>(val));
+ out->AppendInt(static_cast<uint8_t>(val));
break;
case BaseType::Byte:
- *out << std::to_string(static_cast<int8_t>(val));
+ out->AppendInt(static_cast<int8_t>(val));
break;
case BaseType::Short:
- *out << static_cast<int16_t>(val);
+ out->AppendInt(static_cast<int16_t>(val));
break;
case BaseType::UShort:
- *out << static_cast<uint16_t>(val);
+ out->AppendInt(static_cast<uint16_t>(val));
break;
case BaseType::Int:
- *out << static_cast<int32_t>(val);
+ out->AppendInt(static_cast<int32_t>(val));
break;
case BaseType::UInt:
- *out << static_cast<uint32_t>(val);
+ out->AppendInt(static_cast<uint32_t>(val));
break;
case BaseType::Long:
- *out << static_cast<int64_t>(val);
+ out->AppendInt(static_cast<int64_t>(val));
break;
case BaseType::ULong:
- *out << static_cast<uint64_t>(val);
+ out->AppendInt(static_cast<uint64_t>(val));
break;
default:
- *out << "null";
+ out->Append("null");
}
}
void FloatToString(double val, reflection::BaseType type,
- std::stringstream *out) {
+ FastStringBuilder *out) {
if (std::isnan(val)) {
- *out << "null";
+ out->Append("null");
return;
}
switch (type) {
case BaseType::Float:
- out->precision(std::numeric_limits<float>::digits10);
- *out << static_cast<float>(val);
+ out->Append(static_cast<float>(val));
break;
case BaseType::Double:
- out->precision(std::numeric_limits<double>::digits10);
- *out << val;
+ out->Append(val);
break;
default:
- *out << "null";
+ out->Append("null");
}
}
@@ -70,39 +68,41 @@
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, JsonOptions json_options,
+ const ObjT *object, FastStringBuilder *out, JsonOptions json_options,
int tree_depth = 0);
// Get enum value name
-const char *EnumToString(
+std::string_view 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;
+ auto result = std::find_if(values->begin(), values->end(),
+ [enum_value](const reflection::EnumVal *a) {
+ return a->value() == enum_value;
+ });
+ return result != values->end() ? result->name()->string_view()
+ : std::string_view();
}
// 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) {
+ FastStringBuilder *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());
+ const std::string_view value_string =
+ EnumToString(val, enum_props->values());
- if (value_string != nullptr) {
- *out << '"' << value_string << '"';
+ if (value_string.data() != nullptr) {
+ out->AppendChar('"');
+ out->Append(value_string);
+ out->AppendChar('"');
} else {
- *out << val;
+ out->AppendInt(val);
}
}
} else {
@@ -117,10 +117,10 @@
// Adds a newline and indents
// Every increment in tree depth is two spaces
-void AddWrapping(std::stringstream *out, int tree_depth) {
- *out << "\n";
+void AddWrapping(FastStringBuilder *out, int tree_depth) {
+ out->Append("\n");
for (int i = 0; i < tree_depth; i++) {
- *out << " ";
+ out->Append(" ");
}
}
@@ -141,7 +141,7 @@
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, JsonOptions json_options, int tree_depth) {
+ FastStringBuilder *out, JsonOptions json_options, int tree_depth) {
const reflection::Type *type = field->type();
switch (type->base_type()) {
@@ -162,40 +162,39 @@
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());
+ flatbuffers::string_view str =
+ flatbuffers::GetFieldS(*table, *field)->string_view();
+ out->AppendChar('"');
for (char c : str) {
- // out_str += c;
switch (c) {
case '"':
- out_str += "\\\"";
+ out->Append("\\\"");
break;
case '\\':
- out_str += "\\\\";
+ out->Append("\\\\");
break;
case '\b':
- out_str += "\\b";
+ out->Append("\\b");
break;
case '\f':
- out_str += "\\f";
+ out->Append("\\f");
break;
case '\n':
- out_str += "\\n";
+ out->Append("\\n");
break;
case '\r':
- out_str += "\\r";
+ out->Append("\\r");
break;
case '\t':
- out_str += "\\t";
+ out->Append("\\t");
break;
default:
- out_str += c;
+ out->AppendChar(c);
}
}
- *out << '"' << out_str << '"';
+ out->AppendChar('"');
} else {
- *out << "null";
+ out->Append("null");
}
break;
case BaseType::Vector: {
@@ -205,7 +204,9 @@
reflection::BaseType elem_type = type->element();
if (vector->size() > json_options.max_vector_size) {
- *out << "[ ... " << vector->size() << " elements ... ]";
+ out->Append("[ ... ");
+ out->AppendInt(vector->size());
+ out->Append(" elements ... ]");
break;
}
@@ -216,13 +217,13 @@
wrap = ShouldCauseWrapping(elem_type);
}
- *out << '[';
+ out->AppendChar('[');
for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
if (i != 0) {
if (wrap) {
- *out << ",";
+ out->Append(",");
} else {
- *out << ", ";
+ out->Append(", ");
}
}
if (wrap) {
@@ -236,8 +237,9 @@
FloatToString(flatbuffers::GetAnyVectorElemF(vector, elem_type, i),
elem_type, out);
} else if (elem_type == BaseType::String) {
- *out << '"' << flatbuffers::GetAnyVectorElemS(vector, elem_type, i)
- << '"';
+ out->AppendChar('"');
+ out->Append(flatbuffers::GetAnyVectorElemS(vector, elem_type, i));
+ out->AppendChar('"');
} else if (elem_type == BaseType::Obj) {
if (type->index() > -1 &&
type->index() < (int32_t)objects->size()) {
@@ -260,9 +262,9 @@
if (wrap) {
AddWrapping(out, tree_depth);
}
- *out << ']';
+ out->AppendChar(']');
} else {
- *out << "null";
+ out->Append("null");
}
} break;
case BaseType::Obj: {
@@ -277,11 +279,11 @@
json_options, tree_depth);
}
} else {
- *out << "null";
+ out->Append("null");
}
} break;
default:
- *out << "null";
+ out->Append("null");
}
}
@@ -292,7 +294,7 @@
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, JsonOptions json_options,
+ const ObjT *object, FastStringBuilder *out, JsonOptions json_options,
int tree_depth) {
static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
std::is_same<flatbuffers::Struct, ObjT>(),
@@ -312,16 +314,16 @@
}
}
- *out << '{';
+ out->AppendChar('{');
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) {
if (wrap) {
- *out << ",";
+ out->Append(",");
} else {
- *out << ", ";
+ out->Append(", ");
}
} else {
print_sep = true;
@@ -331,7 +333,9 @@
AddWrapping(out, child_tree_depth);
}
- *out << '"' << field->name()->c_str() << "\": ";
+ out->AppendChar('"');
+ out->Append(field->name()->string_view());
+ out->Append("\": ");
FieldToString(object, field, objects, enums, out, json_options,
child_tree_depth);
}
@@ -341,30 +345,37 @@
AddWrapping(out, tree_depth);
}
- *out << '}';
+ out->AppendChar('}');
}
} // namespace
std::string FlatbufferToJson(const reflection::Schema *schema,
const uint8_t *data, JsonOptions json_options) {
+ FastStringBuilder builder;
+ FlatbufferToJson(&builder, schema, data, json_options);
+ return builder.MoveResult();
+}
+
+void FlatbufferToJson(FastStringBuilder *builder,
+ const reflection::Schema *schema, const uint8_t *data,
+ JsonOptions json_options) {
CHECK(schema != nullptr) << ": Need to provide a schema";
+ CHECK(builder != nullptr) << ": Need to provide an output builder";
// It is pretty common to get passed in a nullptr when a test fails. Rather
// than CHECK, return a more user friendly result.
if (data == nullptr) {
- return "null";
+ builder->Append("null");
+ return;
}
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,
+ ObjectToString(obj, schema->objects(), schema->enums(), table, builder,
json_options);
-
- return out.str();
}
+
} // namespace aos