blob: 0fc37a7b1bf7ad24943c3148b39295f79fbda2ce [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 Chatowfcf16f42020-07-26 12:41:36 -070079 auto result = std::find_if(values->begin(), values->end(),
80 [enum_value](const reflection::EnumVal *a) {
81 return a->value() == enum_value;
82 });
83 return result != values->end() ? result->name()->string_view()
84 : std::string_view();
Tyler Chatow5e369a42019-11-23 11:57:31 -080085}
86
87// Convert integer to string, checking if it is an enum.
88void IntOrEnumToString(
89 int64_t val, const reflection::Type *type,
90 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -070091 FastStringBuilder *out) {
Tyler Chatow5e369a42019-11-23 11:57:31 -080092 // Check if integer is an enum and print string, otherwise fallback to
93 // printing as int.
94 if (type->index() > -1 && type->index() < (int32_t)enums->size()) {
95 const reflection::Enum *enum_props = enums->Get(type->index());
96 if (!enum_props->is_union()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -070097 const std::string_view value_string =
98 EnumToString(val, enum_props->values());
Tyler Chatow5e369a42019-11-23 11:57:31 -080099
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700100 if (value_string.data() != nullptr) {
101 out->AppendChar('"');
102 out->Append(value_string);
103 out->AppendChar('"');
Austin Schuhd3936202020-04-07 20:11:07 -0700104 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700105 out->AppendInt(val);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800106 }
107 }
108 } else {
109 if (type->base_type() == BaseType::Vector ||
110 type->base_type() == BaseType::Array) {
111 IntToString(val, type->element(), out);
112 } else {
113 IntToString(val, type->base_type(), out);
114 }
115 }
116}
117
Ravago Jonese8338b02020-04-16 15:43:00 -0700118// Adds a newline and indents
119// Every increment in tree depth is two spaces
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700120void AddWrapping(FastStringBuilder *out, int tree_depth) {
121 out->Append("\n");
Ravago Jonese8338b02020-04-16 15:43:00 -0700122 for (int i = 0; i < tree_depth; i++) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700123 out->Append(" ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700124 }
125}
126
127// Detects if a field should trigger wrapping of the parent object.
128bool ShouldCauseWrapping(reflection::BaseType type) {
129 switch (type) {
130 case BaseType::Vector:
131 case BaseType::Obj:
132 return true;
133 default:
134 return false;
135 }
136}
137
Tyler Chatow5e369a42019-11-23 11:57:31 -0800138// Print field in flatbuffer table. Field must be populated.
139template <typename ObjT>
140void FieldToString(
141 const ObjT *table, const reflection::Field *field,
142 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
143 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700144 FastStringBuilder *out, JsonOptions json_options, int tree_depth) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800145 const reflection::Type *type = field->type();
146
147 switch (type->base_type()) {
148 case BaseType::Bool:
149 case BaseType::UByte:
150 case BaseType::Byte:
151 case BaseType::Short:
152 case BaseType::UShort:
153 case BaseType::Int:
154 case BaseType::UInt:
155 case BaseType::Long:
156 case BaseType::ULong:
157 IntOrEnumToString(GetAnyFieldI(*table, *field), type, enums, out);
158 break;
159 case BaseType::Float:
160 case BaseType::Double:
161 FloatToString(GetAnyFieldF(*table, *field), type->base_type(), out);
162 break;
163 case BaseType::String:
164 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700165 flatbuffers::string_view str =
166 flatbuffers::GetFieldS(*table, *field)->string_view();
167 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800168 for (char c : str) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800169 switch (c) {
170 case '"':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700171 out->Append("\\\"");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800172 break;
173 case '\\':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700174 out->Append("\\\\");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800175 break;
176 case '\b':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700177 out->Append("\\b");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800178 break;
179 case '\f':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700180 out->Append("\\f");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800181 break;
182 case '\n':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700183 out->Append("\\n");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800184 break;
185 case '\r':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700186 out->Append("\\r");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800187 break;
188 case '\t':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700189 out->Append("\\t");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800190 break;
191 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700192 out->AppendChar(c);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800193 }
194 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700195 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800196 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700197 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800198 }
199 break;
200 case BaseType::Vector: {
201 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
202 const flatbuffers::VectorOfAny *vector =
203 flatbuffers::GetFieldAnyV(*table, *field);
204 reflection::BaseType elem_type = type->element();
205
Ravago Jonescf453ab2020-05-06 21:14:53 -0700206 if (vector->size() > json_options.max_vector_size) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700207 out->Append("[ ... ");
208 out->AppendInt(vector->size());
209 out->Append(" elements ... ]");
Austin Schuhd3936202020-04-07 20:11:07 -0700210 break;
211 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700212
213 bool wrap = false;
214 const int child_tree_depth = tree_depth + 1;
215
Ravago Jonescf453ab2020-05-06 21:14:53 -0700216 if (json_options.multi_line) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700217 wrap = ShouldCauseWrapping(elem_type);
218 }
219
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700220 out->AppendChar('[');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800221 for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
222 if (i != 0) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700223 if (wrap) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700224 out->Append(",");
Ravago Jonese8338b02020-04-16 15:43:00 -0700225 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700226 out->Append(", ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700227 }
228 }
229 if (wrap) {
230 AddWrapping(out, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800231 }
232 if (flatbuffers::IsInteger(elem_type)) {
233 IntOrEnumToString(
234 flatbuffers::GetAnyVectorElemI(vector, elem_type, i), type,
235 enums, out);
236 } else if (flatbuffers::IsFloat(elem_type)) {
237 FloatToString(flatbuffers::GetAnyVectorElemF(vector, elem_type, i),
238 elem_type, out);
239 } else if (elem_type == BaseType::String) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700240 out->AppendChar('"');
241 out->Append(flatbuffers::GetAnyVectorElemS(vector, elem_type, i));
242 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800243 } else if (elem_type == BaseType::Obj) {
244 if (type->index() > -1 &&
245 type->index() < (int32_t)objects->size()) {
246 if (objects->Get(type->index())->is_struct()) {
247 ObjectToString(
248 objects->Get(type->index()), objects, enums,
249 flatbuffers::GetAnyVectorElemAddressOf<
250 const flatbuffers::Struct>(
251 vector, i, objects->Get(type->index())->bytesize()),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700252 out, json_options, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800253 } else {
254 ObjectToString(objects->Get(type->index()), objects, enums,
255 flatbuffers::GetAnyVectorElemPointer<
256 const flatbuffers::Table>(vector, i),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700257 out, json_options, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800258 }
259 }
260 }
261 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700262 if (wrap) {
263 AddWrapping(out, tree_depth);
264 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700265 out->AppendChar(']');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800266 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700267 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800268 }
269 } break;
270 case BaseType::Obj: {
271 if (type->index() > -1 && type->index() < (int32_t)objects->size()) {
272 if (objects->Get(type->index())->is_struct()) {
273 ObjectToString(objects->Get(type->index()), objects, enums,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700274 flatbuffers::GetFieldStruct(*table, *field), out,
275 json_options, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800276 } else if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
277 ObjectToString(objects->Get(type->index()), objects, enums,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700278 flatbuffers::GetFieldT(*table, *field), out,
279 json_options, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800280 }
281 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700282 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800283 }
284 } break;
285 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700286 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800287 }
288}
289
290// Prints flatbuffer table or struct given list of possible child objects and
291// enums. Prints "null" if the child object type is not found.
292template <typename ObjT>
293void ObjectToString(
294 const reflection::Object *obj,
295 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
296 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700297 const ObjT *object, FastStringBuilder *out, JsonOptions json_options,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700298 int tree_depth) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800299 static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
300 std::is_same<flatbuffers::Struct, ObjT>(),
301 "Type must be either flatbuffer table or struct");
302 bool print_sep = false;
Ravago Jonese8338b02020-04-16 15:43:00 -0700303
304 const int child_tree_depth = tree_depth + 1;
305
306 bool wrap = false;
Ravago Jonescf453ab2020-05-06 21:14:53 -0700307 if (json_options.multi_line) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700308 // Check whether this object has objects, vectors, or floats inside of it
309 for (const reflection::Field *field : *obj->fields()) {
310 if (ShouldCauseWrapping(field->type()->base_type())) {
311 wrap = true;
312 break;
313 }
314 }
315 }
316
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700317 out->AppendChar('{');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800318 for (const reflection::Field *field : *obj->fields()) {
319 // Check whether this object has the field populated (even for structs,
320 // which should have all fields populated)
321 if (object->GetAddressOf(field->offset())) {
322 if (print_sep) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700323 if (wrap) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700324 out->Append(",");
Ravago Jonese8338b02020-04-16 15:43:00 -0700325 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700326 out->Append(", ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700327 }
Tyler Chatow5e369a42019-11-23 11:57:31 -0800328 } else {
329 print_sep = true;
330 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700331
332 if (wrap) {
333 AddWrapping(out, child_tree_depth);
334 }
335
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700336 out->AppendChar('"');
337 out->Append(field->name()->string_view());
338 out->Append("\": ");
Ravago Jonescf453ab2020-05-06 21:14:53 -0700339 FieldToString(object, field, objects, enums, out, json_options,
340 child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800341 }
342 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700343
344 if (wrap) {
345 AddWrapping(out, tree_depth);
346 }
347
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700348 out->AppendChar('}');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800349}
350
351} // namespace
352
353std::string FlatbufferToJson(const reflection::Schema *schema,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700354 const uint8_t *data, JsonOptions json_options) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700355 FastStringBuilder builder;
356 FlatbufferToJson(&builder, schema, data, json_options);
357 return builder.MoveResult();
358}
359
360void FlatbufferToJson(FastStringBuilder *builder,
361 const reflection::Schema *schema, const uint8_t *data,
362 JsonOptions json_options) {
Austin Schuh7f99e472020-06-17 20:38:17 -0700363 CHECK(schema != nullptr) << ": Need to provide a schema";
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700364 CHECK(builder != nullptr) << ": Need to provide an output builder";
Austin Schuh7f99e472020-06-17 20:38:17 -0700365
366 // It is pretty common to get passed in a nullptr when a test fails. Rather
367 // than CHECK, return a more user friendly result.
368 if (data == nullptr) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700369 builder->Append("null");
370 return;
Austin Schuh7f99e472020-06-17 20:38:17 -0700371 }
372
Tyler Chatow5e369a42019-11-23 11:57:31 -0800373 const flatbuffers::Table *table = flatbuffers::GetAnyRoot(data);
374
375 const reflection::Object *obj = schema->root_table();
376
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700377 ObjectToString(obj, schema->objects(), schema->enums(), table, builder,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700378 json_options);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800379}
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700380
Tyler Chatow5e369a42019-11-23 11:57:31 -0800381} // namespace aos