blob: ddac7953c310db24ff72acec5274cb587e4e251b [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,
Brian J Griglak043e0e22022-08-18 12:51:18 -060014 FastStringBuilder *out, bool use_hex) {
15 // For 1-byte types in hex mode, we need to cast to 2 bytes to get the desired output and
16 // not unprintable characters.
17 if (use_hex) {
18 if (BaseType::UByte == type) {
19 out->AppendInt(static_cast<uint16_t>(val), true);
20 return;
21 }
22 if (BaseType::Byte == type) {
23 out->AppendInt(static_cast<int16_t>(val), true);
24 return;
25 }
26 }
27
Tyler Chatow5e369a42019-11-23 11:57:31 -080028 switch (type) {
29 case BaseType::Bool:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070030 out->AppendBool(static_cast<bool>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080031 break;
32 case BaseType::UByte:
Brian J Griglak043e0e22022-08-18 12:51:18 -060033 out->AppendInt(static_cast<uint8_t>(val), use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -080034 break;
35 case BaseType::Byte:
Brian J Griglak043e0e22022-08-18 12:51:18 -060036 out->AppendInt(static_cast<int8_t>(val), use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -080037 break;
38 case BaseType::Short:
Brian J Griglak043e0e22022-08-18 12:51:18 -060039 out->AppendInt(static_cast<int16_t>(val), use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -080040 break;
41 case BaseType::UShort:
Brian J Griglak043e0e22022-08-18 12:51:18 -060042 out->AppendInt(static_cast<uint16_t>(val), use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -080043 break;
44 case BaseType::Int:
Brian J Griglak043e0e22022-08-18 12:51:18 -060045 out->AppendInt(static_cast<int32_t>(val), use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -080046 break;
47 case BaseType::UInt:
Brian J Griglak043e0e22022-08-18 12:51:18 -060048 out->AppendInt(static_cast<uint32_t>(val), use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -080049 break;
50 case BaseType::Long:
Brian J Griglak043e0e22022-08-18 12:51:18 -060051 out->AppendInt(static_cast<int64_t>(val), use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -080052 break;
53 case BaseType::ULong:
Brian J Griglak043e0e22022-08-18 12:51:18 -060054 out->AppendInt(static_cast<uint64_t>(val), use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -080055 break;
56 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070057 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -080058 }
59}
60
61void FloatToString(double val, reflection::BaseType type,
Tyler Chatowfcf16f42020-07-26 12:41:36 -070062 FastStringBuilder *out) {
Austin Schuhd3936202020-04-07 20:11:07 -070063 if (std::isnan(val)) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -070064 out->Append("null");
Austin Schuhd3936202020-04-07 20:11:07 -070065 return;
66 }
Tyler Chatow5e369a42019-11-23 11:57:31 -080067 switch (type) {
68 case BaseType::Float:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070069 out->Append(static_cast<float>(val));
Tyler Chatow5e369a42019-11-23 11:57:31 -080070 break;
71 case BaseType::Double:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070072 out->Append(val);
Tyler Chatow5e369a42019-11-23 11:57:31 -080073 break;
74 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -070075 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -080076 }
77}
78
79template <typename ObjT>
80void ObjectToString(
81 const reflection::Object *obj,
82 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
83 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -070084 const ObjT *object, FastStringBuilder *out, JsonOptions json_options,
Ravago Jonescf453ab2020-05-06 21:14:53 -070085 int tree_depth = 0);
Tyler Chatow5e369a42019-11-23 11:57:31 -080086
87// Get enum value name
Tyler Chatowfcf16f42020-07-26 12:41:36 -070088std::string_view EnumToString(
Tyler Chatow5e369a42019-11-23 11:57:31 -080089 int64_t enum_value,
90 const flatbuffers::Vector<flatbuffers::Offset<reflection::EnumVal>>
91 *values) {
Tyler Chatow905f94c2020-09-30 20:07:37 -070092 auto search = values->LookupByKey(enum_value);
93 return search != nullptr ? search->name()->string_view() : std::string_view();
Tyler Chatow5e369a42019-11-23 11:57:31 -080094}
95
96// Convert integer to string, checking if it is an enum.
97void IntOrEnumToString(
98 int64_t val, const reflection::Type *type,
99 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Brian J Griglak043e0e22022-08-18 12:51:18 -0600100 FastStringBuilder *out, bool use_hex = false) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800101 // Check if integer is an enum and print string, otherwise fallback to
102 // printing as int.
103 if (type->index() > -1 && type->index() < (int32_t)enums->size()) {
104 const reflection::Enum *enum_props = enums->Get(type->index());
105 if (!enum_props->is_union()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700106 const std::string_view value_string =
107 EnumToString(val, enum_props->values());
Tyler Chatow5e369a42019-11-23 11:57:31 -0800108
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700109 if (value_string.data() != nullptr) {
110 out->AppendChar('"');
111 out->Append(value_string);
112 out->AppendChar('"');
Austin Schuhd3936202020-04-07 20:11:07 -0700113 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700114 out->AppendInt(val);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800115 }
116 }
117 } else {
118 if (type->base_type() == BaseType::Vector ||
119 type->base_type() == BaseType::Array) {
Brian J Griglak043e0e22022-08-18 12:51:18 -0600120 IntToString(val, type->element(), out, use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800121 } else {
Brian J Griglak043e0e22022-08-18 12:51:18 -0600122 IntToString(val, type->base_type(), out, use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800123 }
124 }
125}
126
Ravago Jonese8338b02020-04-16 15:43:00 -0700127// Adds a newline and indents
128// Every increment in tree depth is two spaces
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700129void AddWrapping(FastStringBuilder *out, int tree_depth) {
130 out->Append("\n");
Ravago Jonese8338b02020-04-16 15:43:00 -0700131 for (int i = 0; i < tree_depth; i++) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700132 out->Append(" ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700133 }
134}
135
136// Detects if a field should trigger wrapping of the parent object.
137bool ShouldCauseWrapping(reflection::BaseType type) {
138 switch (type) {
139 case BaseType::Vector:
140 case BaseType::Obj:
141 return true;
142 default:
143 return false;
144 }
145}
146
Tyler Chatow5e369a42019-11-23 11:57:31 -0800147// Print field in flatbuffer table. Field must be populated.
148template <typename ObjT>
149void FieldToString(
150 const ObjT *table, const reflection::Field *field,
151 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
152 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700153 FastStringBuilder *out, JsonOptions json_options, int tree_depth) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800154 const reflection::Type *type = field->type();
155
156 switch (type->base_type()) {
157 case BaseType::Bool:
158 case BaseType::UByte:
159 case BaseType::Byte:
160 case BaseType::Short:
161 case BaseType::UShort:
162 case BaseType::Int:
163 case BaseType::UInt:
164 case BaseType::Long:
165 case BaseType::ULong:
Brian J Griglak043e0e22022-08-18 12:51:18 -0600166 IntOrEnumToString(GetAnyFieldI(*table, *field), type, enums, out, json_options.use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800167 break;
168 case BaseType::Float:
169 case BaseType::Double:
170 FloatToString(GetAnyFieldF(*table, *field), type->base_type(), out);
171 break;
172 case BaseType::String:
173 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700174 flatbuffers::string_view str =
175 flatbuffers::GetFieldS(*table, *field)->string_view();
176 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800177 for (char c : str) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800178 switch (c) {
179 case '"':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700180 out->Append("\\\"");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800181 break;
182 case '\\':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700183 out->Append("\\\\");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800184 break;
185 case '\b':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700186 out->Append("\\b");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800187 break;
188 case '\f':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700189 out->Append("\\f");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800190 break;
191 case '\n':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700192 out->Append("\\n");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800193 break;
194 case '\r':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700195 out->Append("\\r");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800196 break;
197 case '\t':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700198 out->Append("\\t");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800199 break;
200 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700201 out->AppendChar(c);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800202 }
203 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700204 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800205 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700206 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800207 }
208 break;
209 case BaseType::Vector: {
210 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
211 const flatbuffers::VectorOfAny *vector =
212 flatbuffers::GetFieldAnyV(*table, *field);
213 reflection::BaseType elem_type = type->element();
214
Ravago Jonescf453ab2020-05-06 21:14:53 -0700215 if (vector->size() > json_options.max_vector_size) {
Austin Schuh041fe9f2021-10-16 23:01:15 -0700216 out->Append("[ \"... ");
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700217 out->AppendInt(vector->size());
Austin Schuh041fe9f2021-10-16 23:01:15 -0700218 out->Append(" elements ...\" ]");
Austin Schuhd3936202020-04-07 20:11:07 -0700219 break;
220 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700221
222 bool wrap = false;
223 const int child_tree_depth = tree_depth + 1;
224
Ravago Jonescf453ab2020-05-06 21:14:53 -0700225 if (json_options.multi_line) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700226 wrap = ShouldCauseWrapping(elem_type);
227 }
228
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700229 out->AppendChar('[');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800230 for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
231 if (i != 0) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700232 if (wrap) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700233 out->Append(",");
Ravago Jonese8338b02020-04-16 15:43:00 -0700234 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700235 out->Append(", ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700236 }
237 }
238 if (wrap) {
239 AddWrapping(out, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800240 }
241 if (flatbuffers::IsInteger(elem_type)) {
242 IntOrEnumToString(
243 flatbuffers::GetAnyVectorElemI(vector, elem_type, i), type,
Brian J Griglak043e0e22022-08-18 12:51:18 -0600244 enums, out, json_options.use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800245 } else if (flatbuffers::IsFloat(elem_type)) {
246 FloatToString(flatbuffers::GetAnyVectorElemF(vector, elem_type, i),
247 elem_type, out);
248 } else if (elem_type == BaseType::String) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700249 out->AppendChar('"');
250 out->Append(flatbuffers::GetAnyVectorElemS(vector, elem_type, i));
251 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800252 } else if (elem_type == BaseType::Obj) {
253 if (type->index() > -1 &&
254 type->index() < (int32_t)objects->size()) {
255 if (objects->Get(type->index())->is_struct()) {
256 ObjectToString(
257 objects->Get(type->index()), objects, enums,
258 flatbuffers::GetAnyVectorElemAddressOf<
259 const flatbuffers::Struct>(
260 vector, i, objects->Get(type->index())->bytesize()),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700261 out, json_options, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800262 } else {
263 ObjectToString(objects->Get(type->index()), objects, enums,
264 flatbuffers::GetAnyVectorElemPointer<
265 const flatbuffers::Table>(vector, i),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700266 out, json_options, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800267 }
268 }
269 }
270 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700271 if (wrap) {
272 AddWrapping(out, tree_depth);
273 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700274 out->AppendChar(']');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800275 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700276 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800277 }
278 } break;
279 case BaseType::Obj: {
280 if (type->index() > -1 && type->index() < (int32_t)objects->size()) {
281 if (objects->Get(type->index())->is_struct()) {
282 ObjectToString(objects->Get(type->index()), objects, enums,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700283 flatbuffers::GetFieldStruct(*table, *field), out,
284 json_options, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800285 } else if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
286 ObjectToString(objects->Get(type->index()), objects, enums,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700287 flatbuffers::GetFieldT(*table, *field), out,
288 json_options, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800289 }
290 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700291 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800292 }
293 } break;
294 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700295 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800296 }
297}
298
299// Prints flatbuffer table or struct given list of possible child objects and
300// enums. Prints "null" if the child object type is not found.
301template <typename ObjT>
302void ObjectToString(
303 const reflection::Object *obj,
304 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
305 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700306 const ObjT *object, FastStringBuilder *out, JsonOptions json_options,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700307 int tree_depth) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800308 static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
309 std::is_same<flatbuffers::Struct, ObjT>(),
310 "Type must be either flatbuffer table or struct");
311 bool print_sep = false;
Ravago Jonese8338b02020-04-16 15:43:00 -0700312
313 const int child_tree_depth = tree_depth + 1;
314
315 bool wrap = false;
Dan Ford44e02512022-06-07 23:45:14 -0700316 if (json_options.max_multi_line) {
317 wrap = true;
318 } else if (json_options.multi_line) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700319 // Check whether this object has objects, vectors, or floats inside of it
320 for (const reflection::Field *field : *obj->fields()) {
321 if (ShouldCauseWrapping(field->type()->base_type())) {
322 wrap = true;
323 break;
324 }
325 }
326 }
327
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700328 out->AppendChar('{');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800329 for (const reflection::Field *field : *obj->fields()) {
330 // Check whether this object has the field populated (even for structs,
331 // which should have all fields populated)
332 if (object->GetAddressOf(field->offset())) {
333 if (print_sep) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700334 if (wrap) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700335 out->Append(",");
Ravago Jonese8338b02020-04-16 15:43:00 -0700336 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700337 out->Append(", ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700338 }
Tyler Chatow5e369a42019-11-23 11:57:31 -0800339 } else {
340 print_sep = true;
341 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700342
343 if (wrap) {
344 AddWrapping(out, child_tree_depth);
345 }
346
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700347 out->AppendChar('"');
348 out->Append(field->name()->string_view());
349 out->Append("\": ");
Ravago Jonescf453ab2020-05-06 21:14:53 -0700350 FieldToString(object, field, objects, enums, out, json_options,
351 child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800352 }
353 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700354
355 if (wrap) {
356 AddWrapping(out, tree_depth);
357 }
358
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700359 out->AppendChar('}');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800360}
361
362} // namespace
363
364std::string FlatbufferToJson(const reflection::Schema *schema,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700365 const uint8_t *data, JsonOptions json_options) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700366 FastStringBuilder builder;
367 FlatbufferToJson(&builder, schema, data, json_options);
368 return builder.MoveResult();
369}
370
371void FlatbufferToJson(FastStringBuilder *builder,
372 const reflection::Schema *schema, const uint8_t *data,
373 JsonOptions json_options) {
Austin Schuh7f99e472020-06-17 20:38:17 -0700374 CHECK(schema != nullptr) << ": Need to provide a schema";
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700375 CHECK(builder != nullptr) << ": Need to provide an output builder";
Austin Schuh7f99e472020-06-17 20:38:17 -0700376
377 // It is pretty common to get passed in a nullptr when a test fails. Rather
378 // than CHECK, return a more user friendly result.
379 if (data == nullptr) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700380 builder->Append("null");
381 return;
Austin Schuh7f99e472020-06-17 20:38:17 -0700382 }
383
Tyler Chatow5e369a42019-11-23 11:57:31 -0800384 const flatbuffers::Table *table = flatbuffers::GetAnyRoot(data);
385
386 const reflection::Object *obj = schema->root_table();
387
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700388 ObjectToString(obj, schema->objects(), schema->enums(), table, builder,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700389 json_options);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800390}
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700391
Tyler Chatow5e369a42019-11-23 11:57:31 -0800392} // namespace aos