blob: 751666dc1e0f5ebf62c9b6c2f7e331850673ab18 [file] [log] [blame]
Tyler Chatow5e369a42019-11-23 11:57:31 -08001#include <iostream>
2#include <sstream>
3
4#include "aos/json_to_flatbuffer.h"
5
6namespace aos {
7
8namespace {
9
10using reflection::BaseType;
11
12void IntToString(int64_t val, reflection::BaseType type,
13 std::stringstream *out) {
14 switch (type) {
15 case BaseType::Bool:
16 *out << (val ? "true" : "false");
17 break;
18 case BaseType::UByte:
19 *out << std::to_string(static_cast<uint8_t>(val));
20 break;
21 case BaseType::Byte:
22 *out << std::to_string(static_cast<int8_t>(val));
23 break;
24 case BaseType::Short:
25 *out << static_cast<int16_t>(val);
26 break;
27 case BaseType::UShort:
28 *out << static_cast<uint16_t>(val);
29 break;
30 case BaseType::Int:
31 *out << static_cast<int32_t>(val);
32 break;
33 case BaseType::UInt:
34 *out << static_cast<uint32_t>(val);
35 break;
36 case BaseType::Long:
37 *out << static_cast<int64_t>(val);
38 break;
39 case BaseType::ULong:
40 *out << static_cast<uint64_t>(val);
41 break;
42 default:
43 *out << "null";
44 }
45}
46
47void FloatToString(double val, reflection::BaseType type,
48 std::stringstream *out) {
49 switch (type) {
50 case BaseType::Float:
51 out->precision(std::numeric_limits<float>::digits10);
52 *out << static_cast<float>(val);
53 break;
54 case BaseType::Double:
55 out->precision(std::numeric_limits<double>::digits10);
56 *out << val;
57 break;
58 default:
59 *out << "null";
60 }
61}
62
63template <typename ObjT>
64void ObjectToString(
65 const reflection::Object *obj,
66 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
67 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
68 const ObjT *object, std::stringstream *out);
69
70// Get enum value name
71const char *EnumToString(
72 int64_t enum_value,
73 const flatbuffers::Vector<flatbuffers::Offset<reflection::EnumVal>>
74 *values) {
75 // Replace with binary search? Enum values are pre-sorted.
76 for (auto iter = values->begin(); iter != values->end(); iter++) {
77 if (enum_value == iter->value()) {
78 return iter->name()->c_str();
79 }
80 }
81 return nullptr;
82}
83
84// Convert integer to string, checking if it is an enum.
85void IntOrEnumToString(
86 int64_t val, const reflection::Type *type,
87 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
88 std::stringstream *out) {
89 // Check if integer is an enum and print string, otherwise fallback to
90 // printing as int.
91 if (type->index() > -1 && type->index() < (int32_t)enums->size()) {
92 const reflection::Enum *enum_props = enums->Get(type->index());
93 if (!enum_props->is_union()) {
94 const char *value_string = EnumToString(val, enum_props->values());
95
96 if (value_string != nullptr) {
97 *out << '"' << value_string << '"';
98 }
99 }
100 } else {
101 if (type->base_type() == BaseType::Vector ||
102 type->base_type() == BaseType::Array) {
103 IntToString(val, type->element(), out);
104 } else {
105 IntToString(val, type->base_type(), out);
106 }
107 }
108}
109
110// Print field in flatbuffer table. Field must be populated.
111template <typename ObjT>
112void FieldToString(
113 const ObjT *table, const reflection::Field *field,
114 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
115 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
116 std::stringstream *out) {
117 const reflection::Type *type = field->type();
118
119 switch (type->base_type()) {
120 case BaseType::Bool:
121 case BaseType::UByte:
122 case BaseType::Byte:
123 case BaseType::Short:
124 case BaseType::UShort:
125 case BaseType::Int:
126 case BaseType::UInt:
127 case BaseType::Long:
128 case BaseType::ULong:
129 IntOrEnumToString(GetAnyFieldI(*table, *field), type, enums, out);
130 break;
131 case BaseType::Float:
132 case BaseType::Double:
133 FloatToString(GetAnyFieldF(*table, *field), type->base_type(), out);
134 break;
135 case BaseType::String:
136 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
137 std::string str = flatbuffers::GetFieldS(*table, *field)->str();
138 std::string out_str;
139 out_str.reserve(str.size());
140 for (char c : str) {
141 // out_str += c;
142 switch (c) {
143 case '"':
144 out_str += "\\\"";
145 break;
146 case '\\':
147 out_str += "\\\\";
148 break;
149 case '\b':
150 out_str += "\\b";
151 break;
152 case '\f':
153 out_str += "\\f";
154 break;
155 case '\n':
156 out_str += "\\n";
157 break;
158 case '\r':
159 out_str += "\\r";
160 break;
161 case '\t':
162 out_str += "\\t";
163 break;
164 default:
165 out_str += c;
166 }
167 }
168 *out << '"' << out_str << '"';
169 } else {
170 *out << "null";
171 }
172 break;
173 case BaseType::Vector: {
174 if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
175 const flatbuffers::VectorOfAny *vector =
176 flatbuffers::GetFieldAnyV(*table, *field);
177 reflection::BaseType elem_type = type->element();
178
179 *out << '[';
180 for (flatbuffers::uoffset_t i = 0; i < vector->size(); ++i) {
181 if (i != 0) {
182 *out << ", ";
183 }
184 if (flatbuffers::IsInteger(elem_type)) {
185 IntOrEnumToString(
186 flatbuffers::GetAnyVectorElemI(vector, elem_type, i), type,
187 enums, out);
188 } else if (flatbuffers::IsFloat(elem_type)) {
189 FloatToString(flatbuffers::GetAnyVectorElemF(vector, elem_type, i),
190 elem_type, out);
191 } else if (elem_type == BaseType::String) {
192 *out << '"' << flatbuffers::GetAnyVectorElemS(vector, elem_type, i)
193 << '"';
194 } else if (elem_type == BaseType::Obj) {
195 if (type->index() > -1 &&
196 type->index() < (int32_t)objects->size()) {
197 if (objects->Get(type->index())->is_struct()) {
198 ObjectToString(
199 objects->Get(type->index()), objects, enums,
200 flatbuffers::GetAnyVectorElemAddressOf<
201 const flatbuffers::Struct>(
202 vector, i, objects->Get(type->index())->bytesize()),
203 out);
204 } else {
205 ObjectToString(objects->Get(type->index()), objects, enums,
206 flatbuffers::GetAnyVectorElemPointer<
207 const flatbuffers::Table>(vector, i),
208 out);
209 }
210 }
211 }
212 }
213 *out << ']';
214 } else {
215 *out << "null";
216 }
217 } break;
218 case BaseType::Obj: {
219 if (type->index() > -1 && type->index() < (int32_t)objects->size()) {
220 if (objects->Get(type->index())->is_struct()) {
221 ObjectToString(objects->Get(type->index()), objects, enums,
222 flatbuffers::GetFieldStruct(*table, *field), out);
223 } else if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
224 ObjectToString(objects->Get(type->index()), objects, enums,
225 flatbuffers::GetFieldT(*table, *field), out);
226 }
227 } else {
228 *out << "null";
229 }
230 } break;
231 default:
232 *out << "null";
233 }
234}
235
236// Prints flatbuffer table or struct given list of possible child objects and
237// enums. Prints "null" if the child object type is not found.
238template <typename ObjT>
239void ObjectToString(
240 const reflection::Object *obj,
241 const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
242 const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
243 const ObjT *object, std::stringstream *out) {
244 static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
245 std::is_same<flatbuffers::Struct, ObjT>(),
246 "Type must be either flatbuffer table or struct");
247 bool print_sep = false;
248 *out << '{';
249 for (const reflection::Field *field : *obj->fields()) {
250 // Check whether this object has the field populated (even for structs,
251 // which should have all fields populated)
252 if (object->GetAddressOf(field->offset())) {
253 if (print_sep) {
254 *out << ", ";
255 } else {
256 print_sep = true;
257 }
258 *out << '"' << field->name()->c_str() << "\": ";
259 FieldToString(object, field, objects, enums, out);
260 }
261 }
262 *out << '}';
263}
264
265} // namespace
266
267std::string FlatbufferToJson(const reflection::Schema *schema,
268 const uint8_t *data) {
269 const flatbuffers::Table *table = flatbuffers::GetAnyRoot(data);
270
271 const reflection::Object *obj = schema->root_table();
272
273 std::stringstream out;
274
275 ObjectToString(obj, schema->objects(), schema->enums(), table, &out);
276
277 return out.str();
278}
279} // namespace aos