blob: 5f6ca4547ff7cbb7c4e6ad40c71478c96ca98000 [file] [log] [blame]
Austin Schuhd3936202020-04-07 20:11:07 -07001#include <cmath>
Tyler Chatow5e369a42019-11-23 11:57:31 -08002#include <iostream>
3#include <sstream>
4
5#include "aos/json_to_flatbuffer.h"
6
7namespace aos {
8
9namespace {
10
11using reflection::BaseType;
12
13void IntToString(int64_t val, reflection::BaseType type,
Tyler Chatowfcf16f42020-07-26 12:41:36 -070014 FastStringBuilder *out) {
Tyler Chatow5e369a42019-11-23 11:57:31 -080015 switch (type) {
16 case BaseType::Bool:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070017 out->AppendBool(static_cast<bool>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080018 break;
19 case BaseType::UByte:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070020 out->AppendInt(static_cast<uint8_t>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080021 break;
22 case BaseType::Byte:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070023 out->AppendInt(static_cast<int8_t>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080024 break;
25 case BaseType::Short:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070026 out->AppendInt(static_cast<int16_t>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080027 break;
28 case BaseType::UShort:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070029 out->AppendInt(static_cast<uint16_t>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080030 break;
31 case BaseType::Int:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070032 out->AppendInt(static_cast<int32_t>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080033 break;
34 case BaseType::UInt:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070035 out->AppendInt(static_cast<uint32_t>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080036 break;
37 case BaseType::Long:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070038 out->AppendInt(static_cast<int64_t>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080039 break;
40 case BaseType::ULong:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070041 out->AppendInt(static_cast<uint64_t>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080042 break;
43 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070044 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -080045 }
46}
47
48void FloatToString(double val, reflection::BaseType type,
Tyler Chatowfcf16f42020-07-26 12:41:36 -070049 FastStringBuilder *out) {
Austin Schuhd3936202020-04-07 20:11:07 -070050 if (std::isnan(val)) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -070051 out->Append("null");
Austin Schuhd3936202020-04-07 20:11:07 -070052 return;
53 }
Tyler Chatow5e369a42019-11-23 11:57:31 -080054 switch (type) {
55 case BaseType::Float:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070056 out->Append(static_cast<float>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080057 break;
58 case BaseType::Double:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070059 out->Append(val);
Tyler Chatow5e369a42019-11-23 11:57:31 -080060 break;
61 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070062 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -080063 }
64}
65
66template <typename ObjT>
67void ObjectToString(
68 const reflection::Object *obj,
69 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
70 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -070071 const ObjT *object, FastStringBuilder *out, JsonOptions json_options,
Ravago Jonescf453ab2020-05-06 21:14:53 -070072 int tree_depth = 0);
Tyler Chatow5e369a42019-11-23 11:57:31 -080073
74// Get enum value name
Tyler Chatowfcf16f42020-07-26 12:41:36 -070075std::string_view EnumToString(
Tyler Chatow5e369a42019-11-23 11:57:31 -080076 int64_t enum_value,
77 const flatbuffers::Vector<flatbuffers::Offset<reflection::EnumVal>>
78 *values) {
Tyler Chatow905f94c2020-09-30 20:07:37 -070079 auto search = values->LookupByKey(enum_value);
80 return search != nullptr ? search->name()->string_view() : std::string_view();
Tyler Chatow5e369a42019-11-23 11:57:31 -080081}
82
83// Convert integer to string, checking if it is an enum.
84void IntOrEnumToString(
85 int64_t val, const reflection::Type *type,
86 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -070087 FastStringBuilder *out) {
Tyler Chatow5e369a42019-11-23 11:57:31 -080088 // Check if integer is an enum and print string, otherwise fallback to
89 // printing as int.
90 if (type->index() > -1 && type->index() < (int32_t)enums->size()) {
91 const reflection::Enum *enum_props = enums->Get(type->index());
92 if (!enum_props->is_union()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -070093 const std::string_view value_string =
94 EnumToString(val, enum_props->values());
Tyler Chatow5e369a42019-11-23 11:57:31 -080095
Tyler Chatowfcf16f42020-07-26 12:41:36 -070096 if (value_string.data() != nullptr) {
97 out->AppendChar('"');
98 out->Append(value_string);
99 out->AppendChar('"');
Austin Schuhd3936202020-04-07 20:11:07 -0700100 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700101 out->AppendInt(val);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800102 }
103 }
104 } else {
105 if (type->base_type() == BaseType::Vector ||
106 type->base_type() == BaseType::Array) {
107 IntToString(val, type->element(), out);
108 } else {
109 IntToString(val, type->base_type(), out);
110 }
111 }
112}
113
Ravago Jonese8338b02020-04-16 15:43:00 -0700114// Adds a newline and indents
115// Every increment in tree depth is two spaces
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700116void AddWrapping(FastStringBuilder *out, int tree_depth) {
117 out->Append("\n");
Ravago Jonese8338b02020-04-16 15:43:00 -0700118 for (int i = 0; i < tree_depth; i++) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700119 out->Append(" ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700120 }
121}
122
123// Detects if a field should trigger wrapping of the parent object.
124bool ShouldCauseWrapping(reflection::BaseType type) {
125 switch (type) {
126 case BaseType::Vector:
127 case BaseType::Obj:
128 return true;
129 default:
130 return false;
131 }
132}
133
Tyler Chatow5e369a42019-11-23 11:57:31 -0800134// Print field in flatbuffer table. Field must be populated.
135template <typename ObjT>
136void FieldToString(
137 const ObjT *table, const reflection::Field *field,
138 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
139 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700140 FastStringBuilder *out, JsonOptions json_options, int tree_depth) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800141 const reflection::Type *type = field->type();
142
143 switch (type->base_type()) {
144 case BaseType::Bool:
145 case BaseType::UByte:
146 case BaseType::Byte:
147 case BaseType::Short:
148 case BaseType::UShort:
149 case BaseType::Int:
150 case BaseType::UInt:
151 case BaseType::Long:
152 case BaseType::ULong:
153 IntOrEnumToString(GetAnyFieldI(*table, *field), type, enums, out);
154 break;
155 case BaseType::Float:
156 case BaseType::Double:
157 FloatToString(GetAnyFieldF(*table, *field), type->base_type(), out);
158 break;
159 case BaseType::String:
160 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700161 flatbuffers::string_view str =
162 flatbuffers::GetFieldS(*table, *field)->string_view();
163 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800164 for (char c : str) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800165 switch (c) {
166 case '"':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700167 out->Append("\\\"");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800168 break;
169 case '\\':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700170 out->Append("\\\\");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800171 break;
172 case '\b':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700173 out->Append("\\b");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800174 break;
175 case '\f':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700176 out->Append("\\f");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800177 break;
178 case '\n':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700179 out->Append("\\n");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800180 break;
181 case '\r':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700182 out->Append("\\r");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800183 break;
184 case '\t':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700185 out->Append("\\t");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800186 break;
187 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700188 out->AppendChar(c);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800189 }
190 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700191 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800192 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700193 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800194 }
195 break;
196 case BaseType::Vector: {
197 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
198 const flatbuffers::VectorOfAny *vector =
199 flatbuffers::GetFieldAnyV(*table, *field);
200 reflection::BaseType elem_type = type->element();
201
Ravago Jonescf453ab2020-05-06 21:14:53 -0700202 if (vector->size() > json_options.max_vector_size) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700203 out->Append("[ ... ");
204 out->AppendInt(vector->size());
205 out->Append(" elements ... ]");
Austin Schuhd3936202020-04-07 20:11:07 -0700206 break;
207 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700208
209 bool wrap = false;
210 const int child_tree_depth = tree_depth + 1;
211
Ravago Jonescf453ab2020-05-06 21:14:53 -0700212 if (json_options.multi_line) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700213 wrap = ShouldCauseWrapping(elem_type);
214 }
215
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700216 out->AppendChar('[');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800217 for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
218 if (i != 0) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700219 if (wrap) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700220 out->Append(",");
Ravago Jonese8338b02020-04-16 15:43:00 -0700221 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700222 out->Append(", ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700223 }
224 }
225 if (wrap) {
226 AddWrapping(out, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800227 }
228 if (flatbuffers::IsInteger(elem_type)) {
229 IntOrEnumToString(
230 flatbuffers::GetAnyVectorElemI(vector, elem_type, i), type,
231 enums, out);
232 } else if (flatbuffers::IsFloat(elem_type)) {
233 FloatToString(flatbuffers::GetAnyVectorElemF(vector, elem_type, i),
234 elem_type, out);
235 } else if (elem_type == BaseType::String) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700236 out->AppendChar('"');
237 out->Append(flatbuffers::GetAnyVectorElemS(vector, elem_type, i));
238 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800239 } else if (elem_type == BaseType::Obj) {
240 if (type->index() > -1 &&
241 type->index() < (int32_t)objects->size()) {
242 if (objects->Get(type->index())->is_struct()) {
243 ObjectToString(
244 objects->Get(type->index()), objects, enums,
245 flatbuffers::GetAnyVectorElemAddressOf<
246 const flatbuffers::Struct>(
247 vector, i, objects->Get(type->index())->bytesize()),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700248 out, json_options, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800249 } else {
250 ObjectToString(objects->Get(type->index()), objects, enums,
251 flatbuffers::GetAnyVectorElemPointer<
252 const flatbuffers::Table>(vector, i),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700253 out, json_options, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800254 }
255 }
256 }
257 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700258 if (wrap) {
259 AddWrapping(out, tree_depth);
260 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700261 out->AppendChar(']');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800262 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700263 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800264 }
265 } break;
266 case BaseType::Obj: {
267 if (type->index() > -1 && type->index() < (int32_t)objects->size()) {
268 if (objects->Get(type->index())->is_struct()) {
269 ObjectToString(objects->Get(type->index()), objects, enums,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700270 flatbuffers::GetFieldStruct(*table, *field), out,
271 json_options, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800272 } else if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
273 ObjectToString(objects->Get(type->index()), objects, enums,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700274 flatbuffers::GetFieldT(*table, *field), out,
275 json_options, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800276 }
277 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700278 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800279 }
280 } break;
281 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700282 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800283 }
284}
285
286// Prints flatbuffer table or struct given list of possible child objects and
287// enums. Prints "null" if the child object type is not found.
288template <typename ObjT>
289void ObjectToString(
290 const reflection::Object *obj,
291 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
292 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700293 const ObjT *object, FastStringBuilder *out, JsonOptions json_options,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700294 int tree_depth) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800295 static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
296 std::is_same<flatbuffers::Struct, ObjT>(),
297 "Type must be either flatbuffer table or struct");
298 bool print_sep = false;
Ravago Jonese8338b02020-04-16 15:43:00 -0700299
300 const int child_tree_depth = tree_depth + 1;
301
302 bool wrap = false;
Ravago Jonescf453ab2020-05-06 21:14:53 -0700303 if (json_options.multi_line) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700304 // Check whether this object has objects, vectors, or floats inside of it
305 for (const reflection::Field *field : *obj->fields()) {
306 if (ShouldCauseWrapping(field->type()->base_type())) {
307 wrap = true;
308 break;
309 }
310 }
311 }
312
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700313 out->AppendChar('{');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800314 for (const reflection::Field *field : *obj->fields()) {
315 // Check whether this object has the field populated (even for structs,
316 // which should have all fields populated)
317 if (object->GetAddressOf(field->offset())) {
318 if (print_sep) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700319 if (wrap) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700320 out->Append(",");
Ravago Jonese8338b02020-04-16 15:43:00 -0700321 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700322 out->Append(", ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700323 }
Tyler Chatow5e369a42019-11-23 11:57:31 -0800324 } else {
325 print_sep = true;
326 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700327
328 if (wrap) {
329 AddWrapping(out, child_tree_depth);
330 }
331
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700332 out->AppendChar('"');
333 out->Append(field->name()->string_view());
334 out->Append("\": ");
Ravago Jonescf453ab2020-05-06 21:14:53 -0700335 FieldToString(object, field, objects, enums, out, json_options,
336 child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800337 }
338 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700339
340 if (wrap) {
341 AddWrapping(out, tree_depth);
342 }
343
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700344 out->AppendChar('}');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800345}
346
347} // namespace
348
349std::string FlatbufferToJson(const reflection::Schema *schema,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700350 const uint8_t *data, JsonOptions json_options) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700351 FastStringBuilder builder;
352 FlatbufferToJson(&builder, schema, data, json_options);
353 return builder.MoveResult();
354}
355
356void FlatbufferToJson(FastStringBuilder *builder,
357 const reflection::Schema *schema, const uint8_t *data,
358 JsonOptions json_options) {
Austin Schuh7f99e472020-06-17 20:38:17 -0700359 CHECK(schema != nullptr) << ": Need to provide a schema";
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700360 CHECK(builder != nullptr) << ": Need to provide an output builder";
Austin Schuh7f99e472020-06-17 20:38:17 -0700361
362 // It is pretty common to get passed in a nullptr when a test fails. Rather
363 // than CHECK, return a more user friendly result.
364 if (data == nullptr) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700365 builder->Append("null");
366 return;
Austin Schuh7f99e472020-06-17 20:38:17 -0700367 }
368
Tyler Chatow5e369a42019-11-23 11:57:31 -0800369 const flatbuffers::Table *table = flatbuffers::GetAnyRoot(data);
370
371 const reflection::Object *obj = schema->root_table();
372
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700373 ObjectToString(obj, schema->objects(), schema->enums(), table, builder,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700374 json_options);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800375}
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700376
Tyler Chatow5e369a42019-11-23 11:57:31 -0800377} // namespace aos