Home | History | Annotate | Download | only in src
      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