1 /*
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
19 #include "flatbuffers/flatbuffers.h"
20 #include "flatbuffers/idl.h"
21 #include "flatbuffers/util.h"
22
23 namespace flatbuffers {
24
25 static bool GenStruct(const StructDef &struct_def, const Table *table,
26 int indent, const IDLOptions &opts,
27 std::string *_text);
28
29 // If indentation is less than 0, that indicates we don't want any newlines
30 // either.
NewLine(const IDLOptions & opts)31 const char *NewLine(const IDLOptions &opts) {
32 return opts.indent_step >= 0 ? "\n" : "";
33 }
34
Indent(const IDLOptions & opts)35 int Indent(const IDLOptions &opts) {
36 return std::max(opts.indent_step, 0);
37 }
38
39 // Output an identifier with or without quotes depending on strictness.
OutputIdentifier(const std::string & name,const IDLOptions & opts,std::string * _text)40 void OutputIdentifier(const std::string &name, const IDLOptions &opts,
41 std::string *_text) {
42 std::string &text = *_text;
43 if (opts.strict_json) text += "\"";
44 text += name;
45 if (opts.strict_json) text += "\"";
46 }
47
48 // Print (and its template specialization below for pointers) generate text
49 // for a single FlatBuffer value into JSON format.
50 // The general case for scalars:
Print(T val,Type type,int,StructDef *,const IDLOptions & opts,std::string * _text)51 template<typename T> bool Print(T val, Type type, int /*indent*/,
52 StructDef * /*union_sd*/,
53 const IDLOptions &opts,
54 std::string *_text) {
55 std::string &text = *_text;
56 if (type.enum_def && opts.output_enum_identifiers) {
57 auto enum_val = type.enum_def->ReverseLookup(static_cast<int>(val));
58 if (enum_val) {
59 OutputIdentifier(enum_val->name, opts, _text);
60 return true;
61 }
62 }
63
64 if (type.base_type == BASE_TYPE_BOOL) {
65 text += val != 0 ? "true" : "false";
66 } else {
67 text += NumToString(val);
68 }
69
70 return true;
71 }
72
73 // Print a vector a sequence of JSON values, comma separated, wrapped in "[]".
PrintVector(const Vector<T> & v,Type type,int indent,const IDLOptions & opts,std::string * _text)74 template<typename T> bool PrintVector(const Vector<T> &v, Type type,
75 int indent, const IDLOptions &opts,
76 std::string *_text) {
77 std::string &text = *_text;
78 text += "[";
79 text += NewLine(opts);
80 for (uoffset_t i = 0; i < v.size(); i++) {
81 if (i) {
82 text += ",";
83 text += NewLine(opts);
84 }
85 text.append(indent + Indent(opts), ' ');
86 if (IsStruct(type)) {
87 if (!Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type,
88 indent + Indent(opts), nullptr, opts, _text)) {
89 return false;
90 }
91 } else {
92 if (!Print(v[i], type, indent + Indent(opts), nullptr,
93 opts, _text)) {
94 return false;
95 }
96 }
97 }
98 text += NewLine(opts);
99 text.append(indent, ' ');
100 text += "]";
101 return true;
102 }
103
EscapeString(const String & s,std::string * _text,const IDLOptions & opts)104 static bool EscapeString(const String &s, std::string *_text, const IDLOptions& opts) {
105 std::string &text = *_text;
106 text += "\"";
107 for (uoffset_t i = 0; i < s.size(); i++) {
108 char c = s[i];
109 switch (c) {
110 case '\n': text += "\\n"; break;
111 case '\t': text += "\\t"; break;
112 case '\r': text += "\\r"; break;
113 case '\b': text += "\\b"; break;
114 case '\f': text += "\\f"; break;
115 case '\"': text += "\\\""; break;
116 case '\\': text += "\\\\"; break;
117 default:
118 if (c >= ' ' && c <= '~') {
119 text += c;
120 } else {
121 // Not printable ASCII data. Let's see if it's valid UTF-8 first:
122 const char *utf8 = s.c_str() + i;
123 int ucc = FromUTF8(&utf8);
124 if (ucc < 0) {
125 if (opts.allow_non_utf8) {
126 text += "\\x";
127 text += IntToStringHex(static_cast<uint8_t>(c), 2);
128 } else {
129 // There are two cases here:
130 //
131 // 1) We reached here by parsing an IDL file. In that case,
132 // we previously checked for non-UTF-8, so we shouldn't reach
133 // here.
134 //
135 // 2) We reached here by someone calling GenerateText()
136 // on a previously-serialized flatbuffer. The data might have
137 // non-UTF-8 Strings, or might be corrupt.
138 //
139 // In both cases, we have to give up and inform the caller
140 // they have no JSON.
141 return false;
142 }
143 } else {
144 if (ucc <= 0xFFFF) {
145 // Parses as Unicode within JSON's \uXXXX range, so use that.
146 text += "\\u";
147 text += IntToStringHex(ucc, 4);
148 } else if (ucc <= 0x10FFFF) {
149 // Encode Unicode SMP values to a surrogate pair using two \u escapes.
150 uint32_t base = ucc - 0x10000;
151 auto high_surrogate = (base >> 10) + 0xD800;
152 auto low_surrogate = (base & 0x03FF) + 0xDC00;
153 text += "\\u";
154 text += IntToStringHex(high_surrogate, 4);
155 text += "\\u";
156 text += IntToStringHex(low_surrogate, 4);
157 }
158 // Skip past characters recognized.
159 i = static_cast<uoffset_t>(utf8 - s.c_str() - 1);
160 }
161 }
162 break;
163 }
164 }
165 text += "\"";
166 return true;
167 }
168
169 // Specialization of Print above for pointer types.
Print(const void * val,Type type,int indent,StructDef * union_sd,const IDLOptions & opts,std::string * _text)170 template<> bool Print<const void *>(const void *val,
171 Type type, int indent,
172 StructDef *union_sd,
173 const IDLOptions &opts,
174 std::string *_text) {
175 switch (type.base_type) {
176 case BASE_TYPE_UNION:
177 // If this assert hits, you have an corrupt buffer, a union type field
178 // was not present or was out of range.
179 assert(union_sd);
180 if (!GenStruct(*union_sd,
181 reinterpret_cast<const Table *>(val),
182 indent,
183 opts,
184 _text)) {
185 return false;
186 }
187 break;
188 case BASE_TYPE_STRUCT:
189 if (!GenStruct(*type.struct_def,
190 reinterpret_cast<const Table *>(val),
191 indent,
192 opts,
193 _text)) {
194 return false;
195 }
196 break;
197 case BASE_TYPE_STRING: {
198 if (!EscapeString(*reinterpret_cast<const String *>(val), _text, opts)) {
199 return false;
200 }
201 break;
202 }
203 case BASE_TYPE_VECTOR:
204 type = type.VectorType();
205 // Call PrintVector above specifically for each element type:
206 switch (type.base_type) {
207 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
208 PTYPE) \
209 case BASE_TYPE_ ## ENUM: \
210 if (!PrintVector<CTYPE>( \
211 *reinterpret_cast<const Vector<CTYPE> *>(val), \
212 type, indent, opts, _text)) { \
213 return false; \
214 } \
215 break;
216 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
217 #undef FLATBUFFERS_TD
218 }
219 break;
220 default: assert(0);
221 }
222 return true;
223 }
224
225 // Generate text for a scalar field.
GenField(const FieldDef & fd,const Table * table,bool fixed,const IDLOptions & opts,int indent,std::string * _text)226 template<typename T> static bool GenField(const FieldDef &fd,
227 const Table *table, bool fixed,
228 const IDLOptions &opts,
229 int indent,
230 std::string *_text) {
231 return Print(fixed ?
232 reinterpret_cast<const Struct *>(table)->GetField<T>(fd.value.offset) :
233 table->GetField<T>(fd.value.offset, 0), fd.value.type, indent, nullptr,
234 opts, _text);
235 }
236
237 // Generate text for non-scalar field.
GenFieldOffset(const FieldDef & fd,const Table * table,bool fixed,int indent,StructDef * union_sd,const IDLOptions & opts,std::string * _text)238 static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
239 int indent, StructDef *union_sd,
240 const IDLOptions &opts, std::string *_text) {
241 const void *val = nullptr;
242 if (fixed) {
243 // The only non-scalar fields in structs are structs.
244 assert(IsStruct(fd.value.type));
245 val = reinterpret_cast<const Struct *>(table)->
246 GetStruct<const void *>(fd.value.offset);
247 } else {
248 val = IsStruct(fd.value.type)
249 ? table->GetStruct<const void *>(fd.value.offset)
250 : table->GetPointer<const void *>(fd.value.offset);
251 }
252 return Print(val, fd.value.type, indent, union_sd, opts, _text);
253 }
254
255 // Generate text for a struct or table, values separated by commas, indented,
256 // and bracketed by "{}"
GenStruct(const StructDef & struct_def,const Table * table,int indent,const IDLOptions & opts,std::string * _text)257 static bool GenStruct(const StructDef &struct_def, const Table *table,
258 int indent, const IDLOptions &opts,
259 std::string *_text) {
260 std::string &text = *_text;
261 text += "{";
262 int fieldout = 0;
263 StructDef *union_sd = nullptr;
264 for (auto it = struct_def.fields.vec.begin();
265 it != struct_def.fields.vec.end();
266 ++it) {
267 FieldDef &fd = **it;
268 auto is_present = struct_def.fixed || table->CheckField(fd.value.offset);
269 auto output_anyway = opts.output_default_scalars_in_json &&
270 IsScalar(fd.value.type.base_type) &&
271 !fd.deprecated;
272 if (is_present || output_anyway) {
273 if (fieldout++) {
274 text += ",";
275 }
276 text += NewLine(opts);
277 text.append(indent + Indent(opts), ' ');
278 OutputIdentifier(fd.name, opts, _text);
279 text += ": ";
280 if (is_present) {
281 switch (fd.value.type.base_type) {
282 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
283 PTYPE) \
284 case BASE_TYPE_ ## ENUM: \
285 if (!GenField<CTYPE>(fd, table, struct_def.fixed, \
286 opts, indent + Indent(opts), _text)) { \
287 return false; \
288 } \
289 break;
290 FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
291 #undef FLATBUFFERS_TD
292 // Generate drop-thru case statements for all pointer types:
293 #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
294 PTYPE) \
295 case BASE_TYPE_ ## ENUM:
296 FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
297 #undef FLATBUFFERS_TD
298 if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
299 union_sd, opts, _text)) {
300 return false;
301 }
302 break;
303 }
304 if (fd.value.type.base_type == BASE_TYPE_UTYPE) {
305 auto enum_val = fd.value.type.enum_def->ReverseLookup(
306 table->GetField<uint8_t>(fd.value.offset, 0));
307 assert(enum_val);
308 union_sd = enum_val->struct_def;
309 }
310 }
311 else
312 {
313 text += fd.value.constant;
314 }
315 }
316 }
317 text += NewLine(opts);
318 text.append(indent, ' ');
319 text += "}";
320 return true;
321 }
322
323 // Generate a text representation of a flatbuffer in JSON format.
GenerateText(const Parser & parser,const void * flatbuffer,std::string * _text)324 bool GenerateText(const Parser &parser, const void *flatbuffer,
325 std::string *_text) {
326 std::string &text = *_text;
327 assert(parser.root_struct_def_); // call SetRootType()
328 text.reserve(1024); // Reduce amount of inevitable reallocs.
329 if (!GenStruct(*parser.root_struct_def_,
330 GetRoot<Table>(flatbuffer),
331 0,
332 parser.opts,
333 _text)) {
334 return false;
335 }
336 text += NewLine(parser.opts);
337 return true;
338 }
339
TextFileName(const std::string & path,const std::string & file_name)340 std::string TextFileName(const std::string &path,
341 const std::string &file_name) {
342 return path + file_name + ".json";
343 }
344
GenerateTextFile(const Parser & parser,const std::string & path,const std::string & file_name)345 bool GenerateTextFile(const Parser &parser,
346 const std::string &path,
347 const std::string &file_name) {
348 if (!parser.builder_.GetSize() || !parser.root_struct_def_) return true;
349 std::string text;
350 if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &text)) {
351 return false;
352 }
353 return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(),
354 text,
355 false);
356 }
357
TextMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)358 std::string TextMakeRule(const Parser &parser,
359 const std::string &path,
360 const std::string &file_name) {
361 if (!parser.builder_.GetSize() || !parser.root_struct_def_) return "";
362 std::string filebase = flatbuffers::StripPath(
363 flatbuffers::StripExtension(file_name));
364 std::string make_rule = TextFileName(path, filebase) + ": " + file_name;
365 auto included_files = parser.GetIncludedFilesRecursive(
366 parser.root_struct_def_->file);
367 for (auto it = included_files.begin();
368 it != included_files.end(); ++it) {
369 make_rule += " " + *it;
370 }
371 return make_rule;
372 }
373
374 } // namespace flatbuffers
375
376