blob: 163c2814ccc99eb66b4e61e0cb6058c7b84381f5 [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
Philipp Schrader790cb542023-07-05 21:06:52 -070013void IntToString(int64_t val, reflection::BaseType type, FastStringBuilder *out,
14 bool use_hex) {
15 // For 1-byte types in hex mode, we need to cast to 2 bytes to get the desired
16 // output and not unprintable characters.
Brian J Griglak043e0e22022-08-18 12:51:18 -060017 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)) {
James Kuszmaul1ea5e862024-02-09 14:10:25 -080064 out->Append(std::signbit(val) ? "-nan" : "nan");
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:
Philipp Schrader790cb542023-07-05 21:06:52 -0700166 IntOrEnumToString(GetAnyFieldI(*table, *field), type, enums, out,
167 json_options.use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800168 break;
169 case BaseType::Float:
170 case BaseType::Double:
171 FloatToString(GetAnyFieldF(*table, *field), type->base_type(), out);
172 break;
173 case BaseType::String:
174 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700175 flatbuffers::string_view str =
176 flatbuffers::GetFieldS(*table, *field)->string_view();
177 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800178 for (char c : str) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800179 switch (c) {
180 case '"':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700181 out->Append("\\\"");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800182 break;
183 case '\\':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700184 out->Append("\\\\");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800185 break;
186 case '\b':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700187 out->Append("\\b");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800188 break;
189 case '\f':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700190 out->Append("\\f");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800191 break;
192 case '\n':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700193 out->Append("\\n");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800194 break;
195 case '\r':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700196 out->Append("\\r");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800197 break;
198 case '\t':
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700199 out->Append("\\t");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800200 break;
201 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700202 out->AppendChar(c);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800203 }
204 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700205 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800206 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700207 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800208 }
209 break;
210 case BaseType::Vector: {
211 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
212 const flatbuffers::VectorOfAny *vector =
213 flatbuffers::GetFieldAnyV(*table, *field);
214 reflection::BaseType elem_type = type->element();
215
Ravago Jonescf453ab2020-05-06 21:14:53 -0700216 if (vector->size() > json_options.max_vector_size) {
Austin Schuh041fe9f2021-10-16 23:01:15 -0700217 out->Append("[ \"... ");
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700218 out->AppendInt(vector->size());
Austin Schuh041fe9f2021-10-16 23:01:15 -0700219 out->Append(" elements ...\" ]");
Austin Schuhd3936202020-04-07 20:11:07 -0700220 break;
221 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700222
223 bool wrap = false;
224 const int child_tree_depth = tree_depth + 1;
225
Ravago Jonescf453ab2020-05-06 21:14:53 -0700226 if (json_options.multi_line) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700227 wrap = ShouldCauseWrapping(elem_type);
228 }
229
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700230 out->AppendChar('[');
James Kuszmaul1ea5e862024-02-09 14:10:25 -0800231 if (!wrap) {
232 out->AppendChar(' ');
233 }
Tyler Chatow5e369a42019-11-23 11:57:31 -0800234 for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
235 if (i != 0) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700236 if (wrap) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700237 out->Append(",");
Ravago Jonese8338b02020-04-16 15:43:00 -0700238 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700239 out->Append(", ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700240 }
241 }
242 if (wrap) {
243 AddWrapping(out, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800244 }
245 if (flatbuffers::IsInteger(elem_type)) {
246 IntOrEnumToString(
247 flatbuffers::GetAnyVectorElemI(vector, elem_type, i), type,
Brian J Griglak043e0e22022-08-18 12:51:18 -0600248 enums, out, json_options.use_hex);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800249 } else if (flatbuffers::IsFloat(elem_type)) {
250 FloatToString(flatbuffers::GetAnyVectorElemF(vector, elem_type, i),
251 elem_type, out);
252 } else if (elem_type == BaseType::String) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700253 out->AppendChar('"');
254 out->Append(flatbuffers::GetAnyVectorElemS(vector, elem_type, i));
255 out->AppendChar('"');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800256 } else if (elem_type == BaseType::Obj) {
257 if (type->index() > -1 &&
258 type->index() < (int32_t)objects->size()) {
259 if (objects->Get(type->index())->is_struct()) {
260 ObjectToString(
261 objects->Get(type->index()), objects, enums,
262 flatbuffers::GetAnyVectorElemAddressOf<
263 const flatbuffers::Struct>(
264 vector, i, objects->Get(type->index())->bytesize()),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700265 out, json_options, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800266 } else {
267 ObjectToString(objects->Get(type->index()), objects, enums,
268 flatbuffers::GetAnyVectorElemPointer<
269 const flatbuffers::Table>(vector, i),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700270 out, json_options, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800271 }
272 }
273 }
274 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700275 if (wrap) {
276 AddWrapping(out, tree_depth);
James Kuszmaul1ea5e862024-02-09 14:10:25 -0800277 } else {
278 out->AppendChar(' ');
Ravago Jonese8338b02020-04-16 15:43:00 -0700279 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700280 out->AppendChar(']');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800281 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700282 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800283 }
284 } break;
285 case BaseType::Obj: {
286 if (type->index() > -1 && type->index() < (int32_t)objects->size()) {
287 if (objects->Get(type->index())->is_struct()) {
288 ObjectToString(objects->Get(type->index()), objects, enums,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700289 flatbuffers::GetFieldStruct(*table, *field), out,
290 json_options, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800291 } else if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
292 ObjectToString(objects->Get(type->index()), objects, enums,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700293 flatbuffers::GetFieldT(*table, *field), out,
294 json_options, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800295 }
296 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700297 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800298 }
299 } break;
300 default:
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700301 out->Append("null");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800302 }
303}
304
305// Prints flatbuffer table or struct given list of possible child objects and
306// enums. Prints "null" if the child object type is not found.
307template <typename ObjT>
308void ObjectToString(
309 const reflection::Object *obj,
310 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
311 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700312 const ObjT *object, FastStringBuilder *out, JsonOptions json_options,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700313 int tree_depth) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800314 static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
315 std::is_same<flatbuffers::Struct, ObjT>(),
316 "Type must be either flatbuffer table or struct");
317 bool print_sep = false;
Ravago Jonese8338b02020-04-16 15:43:00 -0700318
319 const int child_tree_depth = tree_depth + 1;
320
321 bool wrap = false;
Dan Ford44e02512022-06-07 23:45:14 -0700322 if (json_options.max_multi_line) {
323 wrap = true;
324 } else if (json_options.multi_line) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700325 // Check whether this object has objects, vectors, or floats inside of it
326 for (const reflection::Field *field : *obj->fields()) {
327 if (ShouldCauseWrapping(field->type()->base_type())) {
328 wrap = true;
329 break;
330 }
331 }
332 }
333
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700334 out->AppendChar('{');
James Kuszmaul1ea5e862024-02-09 14:10:25 -0800335 if (!wrap) {
336 out->AppendChar(' ');
337 }
Tyler Chatow5e369a42019-11-23 11:57:31 -0800338 for (const reflection::Field *field : *obj->fields()) {
339 // Check whether this object has the field populated (even for structs,
340 // which should have all fields populated)
341 if (object->GetAddressOf(field->offset())) {
342 if (print_sep) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700343 if (wrap) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700344 out->Append(",");
Ravago Jonese8338b02020-04-16 15:43:00 -0700345 } else {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700346 out->Append(", ");
Ravago Jonese8338b02020-04-16 15:43:00 -0700347 }
Tyler Chatow5e369a42019-11-23 11:57:31 -0800348 } else {
349 print_sep = true;
350 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700351
352 if (wrap) {
353 AddWrapping(out, child_tree_depth);
354 }
355
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700356 out->AppendChar('"');
357 out->Append(field->name()->string_view());
358 out->Append("\": ");
Ravago Jonescf453ab2020-05-06 21:14:53 -0700359 FieldToString(object, field, objects, enums, out, json_options,
360 child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800361 }
362 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700363
364 if (wrap) {
365 AddWrapping(out, tree_depth);
James Kuszmaul1ea5e862024-02-09 14:10:25 -0800366 } else {
367 out->AppendChar(' ');
Ravago Jonese8338b02020-04-16 15:43:00 -0700368 }
369
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700370 out->AppendChar('}');
Tyler Chatow5e369a42019-11-23 11:57:31 -0800371}
372
373} // namespace
374
375std::string FlatbufferToJson(const reflection::Schema *schema,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700376 const uint8_t *data, JsonOptions json_options) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700377 FastStringBuilder builder;
378 FlatbufferToJson(&builder, schema, data, json_options);
379 return builder.MoveResult();
380}
381
382void FlatbufferToJson(FastStringBuilder *builder,
383 const reflection::Schema *schema, const uint8_t *data,
384 JsonOptions json_options) {
Austin Schuh7f99e472020-06-17 20:38:17 -0700385 CHECK(schema != nullptr) << ": Need to provide a schema";
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700386 CHECK(builder != nullptr) << ": Need to provide an output builder";
Austin Schuh7f99e472020-06-17 20:38:17 -0700387
388 // It is pretty common to get passed in a nullptr when a test fails. Rather
389 // than CHECK, return a more user friendly result.
390 if (data == nullptr) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700391 builder->Append("null");
392 return;
Austin Schuh7f99e472020-06-17 20:38:17 -0700393 }
394
Tyler Chatow5e369a42019-11-23 11:57:31 -0800395 const flatbuffers::Table *table = flatbuffers::GetAnyRoot(data);
396
397 const reflection::Object *obj = schema->root_table();
398
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700399 ObjectToString(obj, schema->objects(), schema->enums(), table, builder,
Ravago Jonescf453ab2020-05-06 21:14:53 -0700400 json_options);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800401}
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700402
Tyler Chatow5e369a42019-11-23 11:57:31 -0800403} // namespace aos