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. 31 const char *NewLine(const IDLOptions &opts) { 32 return opts.indent_step >= 0 ? "\n" : ""; 33 } 34 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. 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: 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 "[]". 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 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. 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. 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. 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 "{}" 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. 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 340 std::string TextFileName(const std::string &path, 341 const std::string &file_name) { 342 return path + file_name + ".json"; 343 } 344 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 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