• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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