blob: 52f854dd458558ba30db5a51b31681ba45e3bd0e [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
2 * Copyright 2014 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// independent from idl_parser, since this code is not needed for most clients
18
James Kuszmaul3b15b0c2022-11-08 14:03:16 -080019#include <algorithm>
20
Austin Schuhe89fa2d2019-08-14 20:24:23 -070021#include "flatbuffers/flatbuffers.h"
22#include "flatbuffers/flexbuffers.h"
23#include "flatbuffers/idl.h"
24#include "flatbuffers/util.h"
25
26namespace flatbuffers {
27
Austin Schuh272c6132020-11-14 16:37:52 -080028struct PrintScalarTag {};
29struct PrintPointerTag {};
30template<typename T> struct PrintTag { typedef PrintScalarTag type; };
31template<> struct PrintTag<const void *> { typedef PrintPointerTag type; };
Austin Schuhe89fa2d2019-08-14 20:24:23 -070032
Austin Schuh272c6132020-11-14 16:37:52 -080033struct JsonPrinter {
34 // If indentation is less than 0, that indicates we don't want any newlines
35 // either.
36 void AddNewLine() {
37 if (opts.indent_step >= 0) text += '\n';
Austin Schuhe89fa2d2019-08-14 20:24:23 -070038 }
39
Austin Schuh272c6132020-11-14 16:37:52 -080040 void AddIndent(int ident) { text.append(ident, ' '); }
41
42 int Indent() const { return std::max(opts.indent_step, 0); }
43
44 // Output an identifier with or without quotes depending on strictness.
45 void OutputIdentifier(const std::string &name) {
46 if (opts.strict_json) text += '\"';
47 text += name;
48 if (opts.strict_json) text += '\"';
49 }
50
51 // Print (and its template specialization below for pointers) generate text
52 // for a single FlatBuffer value into JSON format.
53 // The general case for scalars:
54 template<typename T>
55 bool PrintScalar(T val, const Type &type, int /*indent*/) {
56 if (IsBool(type.base_type)) {
57 text += val != 0 ? "true" : "false";
58 return true; // done
59 }
60
61 if (opts.output_enum_identifiers && type.enum_def) {
62 const auto &enum_def = *type.enum_def;
63 if (auto ev = enum_def.ReverseLookup(static_cast<int64_t>(val))) {
64 text += '\"';
65 text += ev->name;
66 text += '\"';
67 return true; // done
68 } else if (val && enum_def.attributes.Lookup("bit_flags")) {
69 const auto entry_len = text.length();
70 const auto u64 = static_cast<uint64_t>(val);
71 uint64_t mask = 0;
72 text += '\"';
73 for (auto it = enum_def.Vals().begin(), e = enum_def.Vals().end();
74 it != e; ++it) {
75 auto f = (*it)->GetAsUInt64();
76 if (f & u64) {
77 mask |= f;
78 text += (*it)->name;
79 text += ' ';
80 }
81 }
82 // Don't slice if (u64 != mask)
83 if (mask && (u64 == mask)) {
84 text[text.length() - 1] = '\"';
85 return true; // done
86 }
87 text.resize(entry_len); // restore
88 }
89 // print as numeric value
90 }
91
Austin Schuhe89fa2d2019-08-14 20:24:23 -070092 text += NumToString(val);
Austin Schuh272c6132020-11-14 16:37:52 -080093 return true;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070094 }
95
Austin Schuh272c6132020-11-14 16:37:52 -080096 void AddComma() {
97 if (!opts.protobuf_ascii_alike) text += ',';
Austin Schuhe89fa2d2019-08-14 20:24:23 -070098 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -070099
Austin Schuh272c6132020-11-14 16:37:52 -0800100 // Print a vector or an array of JSON values, comma seperated, wrapped in
101 // "[]".
102 template<typename Container>
103 bool PrintContainer(PrintScalarTag, const Container &c, size_t size,
104 const Type &type, int indent, const uint8_t *) {
105 const auto elem_indent = indent + Indent();
106 text += '[';
107 AddNewLine();
108 for (uoffset_t i = 0; i < size; i++) {
109 if (i) {
110 AddComma();
111 AddNewLine();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700112 }
Austin Schuh272c6132020-11-14 16:37:52 -0800113 AddIndent(elem_indent);
114 if (!PrintScalar(c[i], type, elem_indent)) { return false; }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700115 }
Austin Schuh272c6132020-11-14 16:37:52 -0800116 AddNewLine();
117 AddIndent(indent);
118 text += ']';
119 return true;
120 }
121
122 // Print a vector or an array of JSON values, comma seperated, wrapped in
123 // "[]".
124 template<typename Container>
125 bool PrintContainer(PrintPointerTag, const Container &c, size_t size,
126 const Type &type, int indent, const uint8_t *prev_val) {
127 const auto is_struct = IsStruct(type);
128 const auto elem_indent = indent + Indent();
129 text += '[';
130 AddNewLine();
131 for (uoffset_t i = 0; i < size; i++) {
132 if (i) {
133 AddComma();
134 AddNewLine();
135 }
136 AddIndent(elem_indent);
137 auto ptr = is_struct ? reinterpret_cast<const void *>(
138 c.Data() + type.struct_def->bytesize * i)
139 : c[i];
140 if (!PrintOffset(ptr, type, elem_indent, prev_val,
141 static_cast<soffset_t>(i))) {
142 return false;
143 }
144 }
145 AddNewLine();
146 AddIndent(indent);
147 text += ']';
148 return true;
149 }
150
151 template<typename T>
152 bool PrintVector(const void *val, const Type &type, int indent,
153 const uint8_t *prev_val) {
154 typedef Vector<T> Container;
155 typedef typename PrintTag<typename Container::return_type>::type tag;
156 auto &vec = *reinterpret_cast<const Container *>(val);
157 return PrintContainer<Container>(tag(), vec, vec.size(), type, indent,
158 prev_val);
159 }
160
161 // Print an array a sequence of JSON values, comma separated, wrapped in "[]".
162 template<typename T>
163 bool PrintArray(const void *val, size_t size, const Type &type, int indent) {
164 typedef Array<T, 0xFFFF> Container;
165 typedef typename PrintTag<typename Container::return_type>::type tag;
166 auto &arr = *reinterpret_cast<const Container *>(val);
167 return PrintContainer<Container>(tag(), arr, size, type, indent, nullptr);
168 }
169
170 bool PrintOffset(const void *val, const Type &type, int indent,
171 const uint8_t *prev_val, soffset_t vector_index) {
172 switch (type.base_type) {
173 case BASE_TYPE_UNION: {
174 // If this assert hits, you have an corrupt buffer, a union type field
175 // was not present or was out of range.
176 FLATBUFFERS_ASSERT(prev_val);
177 auto union_type_byte = *prev_val; // Always a uint8_t.
178 if (vector_index >= 0) {
179 auto type_vec = reinterpret_cast<const Vector<uint8_t> *>(
180 prev_val + ReadScalar<uoffset_t>(prev_val));
181 union_type_byte = type_vec->Get(static_cast<uoffset_t>(vector_index));
182 }
183 auto enum_val = type.enum_def->ReverseLookup(union_type_byte, true);
184 if (enum_val) {
185 return PrintOffset(val, enum_val->union_type, indent, nullptr, -1);
186 } else {
187 return false;
188 }
189 }
190 case BASE_TYPE_STRUCT:
191 return GenStruct(*type.struct_def, reinterpret_cast<const Table *>(val),
192 indent);
193 case BASE_TYPE_STRING: {
194 auto s = reinterpret_cast<const String *>(val);
195 return EscapeString(s->c_str(), s->size(), &text, opts.allow_non_utf8,
196 opts.natural_utf8);
197 }
198 case BASE_TYPE_VECTOR: {
199 const auto vec_type = type.VectorType();
200 // Call PrintVector above specifically for each element type:
201 // clang-format off
202 switch (vec_type.base_type) {
203 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700204 case BASE_TYPE_ ## ENUM: \
205 if (!PrintVector<CTYPE>( \
Austin Schuh272c6132020-11-14 16:37:52 -0800206 val, vec_type, indent, prev_val)) { \
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700207 return false; \
208 } \
209 break;
210 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
211 #undef FLATBUFFERS_TD
Austin Schuh272c6132020-11-14 16:37:52 -0800212 }
213 // clang-format on
214 return true;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700215 }
Austin Schuh272c6132020-11-14 16:37:52 -0800216 case BASE_TYPE_ARRAY: {
217 const auto vec_type = type.VectorType();
218 // Call PrintArray above specifically for each element type:
219 // clang-format off
220 switch (vec_type.base_type) {
221 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \
222 case BASE_TYPE_ ## ENUM: \
223 if (!PrintArray<CTYPE>( \
224 val, type.fixed_length, vec_type, indent)) { \
225 return false; \
226 } \
227 break;
228 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
229 // Arrays of scalars or structs are only possible.
230 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700231 #undef FLATBUFFERS_TD
Austin Schuh272c6132020-11-14 16:37:52 -0800232 case BASE_TYPE_ARRAY: FLATBUFFERS_ASSERT(0);
233 }
234 // clang-format on
235 return true;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700236 }
Austin Schuh272c6132020-11-14 16:37:52 -0800237 default: FLATBUFFERS_ASSERT(0); return false;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700238 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700239 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700240
Austin Schuh272c6132020-11-14 16:37:52 -0800241 template<typename T> static T GetFieldDefault(const FieldDef &fd) {
242 T val;
243 auto check = StringToNumber(fd.value.constant.c_str(), &val);
244 (void)check;
245 FLATBUFFERS_ASSERT(check);
246 return val;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700247 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700248
Austin Schuh272c6132020-11-14 16:37:52 -0800249 // Generate text for a scalar field.
250 template<typename T>
251 bool GenField(const FieldDef &fd, const Table *table, bool fixed,
252 int indent) {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700253 if (fixed) {
254 return PrintScalar(
255 reinterpret_cast<const Struct *>(table)->GetField<T>(fd.value.offset),
256 fd.value.type, indent);
257 } else if (fd.IsOptional()) {
258 auto opt = table->GetOptional<T, T>(fd.value.offset);
259 if (opt) {
260 return PrintScalar(*opt, fd.value.type, indent);
261 } else {
262 text += "null";
263 return true;
264 }
265 } else {
266 return PrintScalar(
267 table->GetField<T>(fd.value.offset, GetFieldDefault<T>(fd)),
268 fd.value.type, indent);
269 }
Austin Schuh272c6132020-11-14 16:37:52 -0800270 }
271
272 // Generate text for non-scalar field.
273 bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
274 int indent, const uint8_t *prev_val) {
275 const void *val = nullptr;
276 if (fixed) {
277 // The only non-scalar fields in structs are structs or arrays.
278 FLATBUFFERS_ASSERT(IsStruct(fd.value.type) || IsArray(fd.value.type));
279 val = reinterpret_cast<const Struct *>(table)->GetStruct<const void *>(
280 fd.value.offset);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700281 } else if (fd.flexbuffer && opts.json_nested_flexbuffers) {
282 // We could verify this FlexBuffer before access, but since this sits
283 // inside a FlatBuffer that we don't know wether it has been verified or
284 // not, there is little point making this part safer than the parent..
285 // The caller should really be verifying the whole.
286 // If the whole buffer is corrupt, we likely crash before we even get
287 // here.
Austin Schuh272c6132020-11-14 16:37:52 -0800288 auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
289 auto root = flexbuffers::GetRoot(vec->data(), vec->size());
290 root.ToString(true, opts.strict_json, text);
291 return true;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700292 } else if (fd.nested_flatbuffer && opts.json_nested_flatbuffers) {
Austin Schuh272c6132020-11-14 16:37:52 -0800293 auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
294 auto root = GetRoot<Table>(vec->data());
295 return GenStruct(*fd.nested_flatbuffer, root, indent);
296 } else {
297 val = IsStruct(fd.value.type)
298 ? table->GetStruct<const void *>(fd.value.offset)
299 : table->GetPointer<const void *>(fd.value.offset);
300 }
301 return PrintOffset(val, fd.value.type, indent, prev_val, -1);
302 }
303
304 // Generate text for a struct or table, values separated by commas, indented,
305 // and bracketed by "{}"
306 bool GenStruct(const StructDef &struct_def, const Table *table, int indent) {
307 text += '{';
308 int fieldout = 0;
309 const uint8_t *prev_val = nullptr;
310 const auto elem_indent = indent + Indent();
311 for (auto it = struct_def.fields.vec.begin();
312 it != struct_def.fields.vec.end(); ++it) {
313 FieldDef &fd = **it;
314 auto is_present = struct_def.fixed || table->CheckField(fd.value.offset);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700315 auto output_anyway = (opts.output_default_scalars_in_json || fd.key) &&
Austin Schuh272c6132020-11-14 16:37:52 -0800316 IsScalar(fd.value.type.base_type) && !fd.deprecated;
317 if (is_present || output_anyway) {
318 if (fieldout++) { AddComma(); }
319 AddNewLine();
320 AddIndent(elem_indent);
321 OutputIdentifier(fd.name);
322 if (!opts.protobuf_ascii_alike ||
323 (fd.value.type.base_type != BASE_TYPE_STRUCT &&
324 fd.value.type.base_type != BASE_TYPE_VECTOR))
325 text += ':';
326 text += ' ';
327 // clang-format off
328 switch (fd.value.type.base_type) {
329 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \
330 case BASE_TYPE_ ## ENUM: \
331 if (!GenField<CTYPE>(fd, table, struct_def.fixed, elem_indent)) { \
332 return false; \
333 } \
334 break;
335 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700336 #undef FLATBUFFERS_TD
337 // Generate drop-thru case statements for all pointer types:
Austin Schuh272c6132020-11-14 16:37:52 -0800338 #define FLATBUFFERS_TD(ENUM, ...) \
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700339 case BASE_TYPE_ ## ENUM:
Austin Schuh272c6132020-11-14 16:37:52 -0800340 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
341 FLATBUFFERS_GEN_TYPE_ARRAY(FLATBUFFERS_TD)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700342 #undef FLATBUFFERS_TD
Austin Schuh272c6132020-11-14 16:37:52 -0800343 if (!GenFieldOffset(fd, table, struct_def.fixed, elem_indent, prev_val)) {
344 return false;
345 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700346 break;
Austin Schuh272c6132020-11-14 16:37:52 -0800347 }
348 // clang-format on
349 // Track prev val for use with union types.
350 if (struct_def.fixed) {
351 prev_val = reinterpret_cast<const uint8_t *>(table) + fd.value.offset;
352 } else {
353 prev_val = table->GetAddressOf(fd.value.offset);
354 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700355 }
356 }
Austin Schuh272c6132020-11-14 16:37:52 -0800357 AddNewLine();
358 AddIndent(indent);
359 text += '}';
360 return true;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700361 }
Austin Schuh272c6132020-11-14 16:37:52 -0800362
363 JsonPrinter(const Parser &parser, std::string &dest)
364 : opts(parser.opts), text(dest) {
365 text.reserve(1024); // Reduce amount of inevitable reallocs.
366 }
367
368 const IDLOptions &opts;
369 std::string &text;
370};
371
372static bool GenerateTextImpl(const Parser &parser, const Table *table,
373 const StructDef &struct_def, std::string *_text) {
374 JsonPrinter printer(parser, *_text);
375 if (!printer.GenStruct(struct_def, table, 0)) { return false; }
376 printer.AddNewLine();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700377 return true;
378}
379
380// Generate a text representation of a flatbuffer in JSON format.
381bool GenerateTextFromTable(const Parser &parser, const void *table,
382 const std::string &table_name, std::string *_text) {
383 auto struct_def = parser.LookupStruct(table_name);
Austin Schuh272c6132020-11-14 16:37:52 -0800384 if (struct_def == nullptr) { return false; }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700385 auto root = static_cast<const Table *>(table);
Austin Schuh272c6132020-11-14 16:37:52 -0800386 return GenerateTextImpl(parser, root, *struct_def, _text);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700387}
388
389// Generate a text representation of a flatbuffer in JSON format.
390bool GenerateText(const Parser &parser, const void *flatbuffer,
391 std::string *_text) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700392 FLATBUFFERS_ASSERT(parser.root_struct_def_); // call SetRootType()
Austin Schuh272c6132020-11-14 16:37:52 -0800393 auto root = parser.opts.size_prefixed ? GetSizePrefixedRoot<Table>(flatbuffer)
394 : GetRoot<Table>(flatbuffer);
395 return GenerateTextImpl(parser, root, *parser.root_struct_def_, _text);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700396}
397
Austin Schuh272c6132020-11-14 16:37:52 -0800398static std::string TextFileName(const std::string &path,
399 const std::string &file_name) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700400 return path + file_name + ".json";
401}
402
403bool GenerateTextFile(const Parser &parser, const std::string &path,
404 const std::string &file_name) {
Austin Schuh272c6132020-11-14 16:37:52 -0800405 if (parser.opts.use_flexbuffers) {
406 std::string json;
407 parser.flex_root_.ToString(true, parser.opts.strict_json, json);
408 return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(),
409 json.c_str(), json.size(), true);
410 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700411 if (!parser.builder_.GetSize() || !parser.root_struct_def_) return true;
412 std::string text;
413 if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &text)) {
414 return false;
415 }
416 return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(), text,
417 false);
418}
419
420std::string TextMakeRule(const Parser &parser, const std::string &path,
421 const std::string &file_name) {
422 if (!parser.builder_.GetSize() || !parser.root_struct_def_) return "";
423 std::string filebase =
424 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
425 std::string make_rule = TextFileName(path, filebase) + ": " + file_name;
426 auto included_files =
427 parser.GetIncludedFilesRecursive(parser.root_struct_def_->file);
428 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
429 make_rule += " " + *it;
430 }
431 return make_rule;
432}
433
434} // namespace flatbuffers