blob: ae402fec1e2338a4e0cb9c145ed610d19e6a57ad [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,
14 std::stringstream *out) {
15 switch (type) {
16 case BaseType::Bool:
17 *out << (val ? "true" : "false");
18 break;
19 case BaseType::UByte:
20 *out << std::to_string(static_cast<uint8_t>(val));
21 break;
22 case BaseType::Byte:
23 *out << std::to_string(static_cast<int8_t>(val));
24 break;
25 case BaseType::Short:
26 *out << static_cast<int16_t>(val);
27 break;
28 case BaseType::UShort:
29 *out << static_cast<uint16_t>(val);
30 break;
31 case BaseType::Int:
32 *out << static_cast<int32_t>(val);
33 break;
34 case BaseType::UInt:
35 *out << static_cast<uint32_t>(val);
36 break;
37 case BaseType::Long:
38 *out << static_cast<int64_t>(val);
39 break;
40 case BaseType::ULong:
41 *out << static_cast<uint64_t>(val);
42 break;
43 default:
44 *out << "null";
45 }
46}
47
48void FloatToString(double val, reflection::BaseType type,
49 std::stringstream *out) {
Austin Schuhd3936202020-04-07 20:11:07 -070050 if (std::isnan(val)) {
51 *out << "null";
52 return;
53 }
Tyler Chatow5e369a42019-11-23 11:57:31 -080054 switch (type) {
55 case BaseType::Float:
56 out->precision(std::numeric_limits<float>::digits10);
57 *out << static_cast<float>(val);
58 break;
59 case BaseType::Double:
60 out->precision(std::numeric_limits<double>::digits10);
61 *out << val;
62 break;
63 default:
64 *out << "null";
65 }
66}
67
68template <typename ObjT>
69void ObjectToString(
70 const reflection::Object *obj,
71 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
72 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
Ravago Jonese8338b02020-04-16 15:43:00 -070073 const ObjT *object, size_t max_vector_size, std::stringstream *out,
74 bool multi_line = false, int tree_depth = 0);
Tyler Chatow5e369a42019-11-23 11:57:31 -080075
76// Get enum value name
77const char *EnumToString(
78 int64_t enum_value,
79 const flatbuffers::Vector<flatbuffers::Offset<reflection::EnumVal>>
80 *values) {
81 // Replace with binary search? Enum values are pre-sorted.
82 for (auto iter = values->begin(); iter != values->end(); iter++) {
83 if (enum_value == iter->value()) {
84 return iter->name()->c_str();
85 }
86 }
87 return nullptr;
88}
89
90// Convert integer to string, checking if it is an enum.
91void IntOrEnumToString(
92 int64_t val, const reflection::Type *type,
93 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
94 std::stringstream *out) {
95 // Check if integer is an enum and print string, otherwise fallback to
96 // printing as int.
97 if (type->index() > -1 && type->index() < (int32_t)enums->size()) {
98 const reflection::Enum *enum_props = enums->Get(type->index());
99 if (!enum_props->is_union()) {
100 const char *value_string = EnumToString(val, enum_props->values());
101
102 if (value_string != nullptr) {
103 *out << '"' << value_string << '"';
Austin Schuhd3936202020-04-07 20:11:07 -0700104 } else {
105 *out << 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
120void AddWrapping(std::stringstream *out, int tree_depth) {
121 *out << "\n";
122 for (int i = 0; i < tree_depth; i++) {
123 *out << " ";
124 }
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,
Ravago Jonese8338b02020-04-16 15:43:00 -0700144 size_t max_vector_size, std::stringstream *out, bool multi_line,
145 int tree_depth) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800146 const reflection::Type *type = field->type();
147
148 switch (type->base_type()) {
149 case BaseType::Bool:
150 case BaseType::UByte:
151 case BaseType::Byte:
152 case BaseType::Short:
153 case BaseType::UShort:
154 case BaseType::Int:
155 case BaseType::UInt:
156 case BaseType::Long:
157 case BaseType::ULong:
158 IntOrEnumToString(GetAnyFieldI(*table, *field), type, enums, out);
159 break;
160 case BaseType::Float:
161 case BaseType::Double:
162 FloatToString(GetAnyFieldF(*table, *field), type->base_type(), out);
163 break;
164 case BaseType::String:
165 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
166 std::string str = flatbuffers::GetFieldS(*table, *field)->str();
167 std::string out_str;
168 out_str.reserve(str.size());
169 for (char c : str) {
170 // out_str += c;
171 switch (c) {
172 case '"':
173 out_str += "\\\"";
174 break;
175 case '\\':
176 out_str += "\\\\";
177 break;
178 case '\b':
179 out_str += "\\b";
180 break;
181 case '\f':
182 out_str += "\\f";
183 break;
184 case '\n':
185 out_str += "\\n";
186 break;
187 case '\r':
188 out_str += "\\r";
189 break;
190 case '\t':
191 out_str += "\\t";
192 break;
193 default:
194 out_str += c;
195 }
196 }
197 *out << '"' << out_str << '"';
198 } else {
199 *out << "null";
200 }
201 break;
202 case BaseType::Vector: {
203 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
204 const flatbuffers::VectorOfAny *vector =
205 flatbuffers::GetFieldAnyV(*table, *field);
206 reflection::BaseType elem_type = type->element();
207
Austin Schuhd3936202020-04-07 20:11:07 -0700208 if (vector->size() > max_vector_size) {
209 *out << "[ ... " << vector->size() << " elements ... ]";
210 break;
211 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700212
213 bool wrap = false;
214 const int child_tree_depth = tree_depth + 1;
215
216 if (multi_line) {
217 wrap = ShouldCauseWrapping(elem_type);
218 }
219
Tyler Chatow5e369a42019-11-23 11:57:31 -0800220 *out << '[';
221 for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
222 if (i != 0) {
Ravago Jonese8338b02020-04-16 15:43:00 -0700223 if (wrap) {
224 *out << ",";
225 } else {
226 *out << ", ";
227 }
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) {
240 *out << '"' << flatbuffers::GetAnyVectorElemS(vector, elem_type, i)
241 << '"';
242 } else if (elem_type == BaseType::Obj) {
243 if (type->index() > -1 &&
244 type->index() < (int32_t)objects->size()) {
245 if (objects->Get(type->index())->is_struct()) {
246 ObjectToString(
247 objects->Get(type->index()), objects, enums,
248 flatbuffers::GetAnyVectorElemAddressOf<
249 const flatbuffers::Struct>(
250 vector, i, objects->Get(type->index())->bytesize()),
Ravago Jonese8338b02020-04-16 15:43:00 -0700251 max_vector_size, out, multi_line, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800252 } else {
253 ObjectToString(objects->Get(type->index()), objects, enums,
254 flatbuffers::GetAnyVectorElemPointer<
255 const flatbuffers::Table>(vector, i),
Ravago Jonese8338b02020-04-16 15:43:00 -0700256 max_vector_size, out, multi_line,
257 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 Chatow5e369a42019-11-23 11:57:31 -0800265 *out << ']';
266 } else {
267 *out << "null";
268 }
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,
Brian Silvermanc8a5b552020-04-28 16:43:59 -0700274 flatbuffers::GetFieldStruct(*table, *field),
Ravago Jonese8338b02020-04-16 15:43:00 -0700275 max_vector_size, out, multi_line, 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,
Brian Silvermanc8a5b552020-04-28 16:43:59 -0700278 flatbuffers::GetFieldT(*table, *field),
Ravago Jonese8338b02020-04-16 15:43:00 -0700279 max_vector_size, out, multi_line, tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800280 }
281 } else {
282 *out << "null";
283 }
284 } break;
285 default:
286 *out << "null";
287 }
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,
Ravago Jonese8338b02020-04-16 15:43:00 -0700297 const ObjT *object, size_t max_vector_size, std::stringstream *out,
298 bool multi_line, 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;
307 if (multi_line) {
308 // 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 Chatow5e369a42019-11-23 11:57:31 -0800317 *out << '{';
318 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) {
324 *out << ",";
325 } else {
326 *out << ", ";
327 }
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 Chatow5e369a42019-11-23 11:57:31 -0800336 *out << '"' << field->name()->c_str() << "\": ";
Ravago Jonese8338b02020-04-16 15:43:00 -0700337 FieldToString(object, field, objects, enums, max_vector_size, out,
338 multi_line, child_tree_depth);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800339 }
340 }
Ravago Jonese8338b02020-04-16 15:43:00 -0700341
342 if (wrap) {
343 AddWrapping(out, tree_depth);
344 }
345
Tyler Chatow5e369a42019-11-23 11:57:31 -0800346 *out << '}';
347}
348
349} // namespace
350
351std::string FlatbufferToJson(const reflection::Schema *schema,
Ravago Jonese8338b02020-04-16 15:43:00 -0700352 const uint8_t *data, bool multi_line,
353 size_t max_vector_size) {
Austin Schuh7f99e472020-06-17 20:38:17 -0700354 CHECK(schema != nullptr) << ": Need to provide a schema";
355
356 // It is pretty common to get passed in a nullptr when a test fails. Rather
357 // than CHECK, return a more user friendly result.
358 if (data == nullptr) {
359 return "null";
360 }
361
Tyler Chatow5e369a42019-11-23 11:57:31 -0800362 const flatbuffers::Table *table = flatbuffers::GetAnyRoot(data);
363
364 const reflection::Object *obj = schema->root_table();
365
366 std::stringstream out;
367
Austin Schuhd3936202020-04-07 20:11:07 -0700368 ObjectToString(obj, schema->objects(), schema->enums(), table,
Ravago Jonese8338b02020-04-16 15:43:00 -0700369 max_vector_size, &out, multi_line);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800370
371 return out.str();
372}
373} // namespace aos