Home | History | Annotate | Download | only in flatbuffers
      1 /*
      2  * Copyright 2017 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 #ifndef FLATBUFFERS_MINIREFLECT_H_
     18 #define FLATBUFFERS_MINIREFLECT_H_
     19 
     20 #include "flatbuffers/flatbuffers.h"
     21 #include "flatbuffers/util.h"
     22 
     23 namespace flatbuffers {
     24 
     25 // Utilities that can be used with the "mini reflection" tables present
     26 // in generated code with --reflect-types (only types) or --reflect-names
     27 // (also names).
     28 // This allows basic reflection functionality such as pretty-printing
     29 // that does not require the use of the schema parser or loading of binary
     30 // schema files at runtime (reflection.h).
     31 
     32 // For any of the functions below that take `const TypeTable *`, you pass
     33 // `FooTypeTable()` if the type of the root is `Foo`.
     34 
     35 // First, a generic iterator that can be used by multiple algorithms.
     36 
     37 struct IterationVisitor {
     38   // These mark the scope of a table or struct.
     39   virtual void StartSequence() {}
     40   virtual void EndSequence() {}
     41   // Called for each field regardless of wether it is present or not.
     42   // If not present, val == nullptr. set_idx is the index of all set fields.
     43   virtual void Field(size_t /*field_idx*/, size_t /*set_idx*/,
     44                      ElementaryType /*type*/, bool /*is_vector*/,
     45                      const TypeTable * /*type_table*/, const char * /*name*/,
     46                      const uint8_t * /*val*/) {}
     47   // Called for a value that is actually present, after a field, or as part
     48   // of a vector.
     49   virtual void UType(uint8_t, const char *) {}
     50   virtual void Bool(bool) {}
     51   virtual void Char(int8_t, const char *) {}
     52   virtual void UChar(uint8_t, const char *) {}
     53   virtual void Short(int16_t, const char *) {}
     54   virtual void UShort(uint16_t, const char *) {}
     55   virtual void Int(int32_t, const char *) {}
     56   virtual void UInt(uint32_t, const char *) {}
     57   virtual void Long(int64_t) {}
     58   virtual void ULong(uint64_t) {}
     59   virtual void Float(float) {}
     60   virtual void Double(double) {}
     61   virtual void String(const String *) {}
     62   virtual void Unknown(const uint8_t *) {}  // From a future version.
     63   // These mark the scope of a vector.
     64   virtual void StartVector() {}
     65   virtual void EndVector() {}
     66   virtual void Element(size_t /*i*/, ElementaryType /*type*/,
     67                        const TypeTable * /*type_table*/,
     68                        const uint8_t * /*val*/) {}
     69   virtual ~IterationVisitor() {}
     70 };
     71 
     72 inline size_t InlineSize(ElementaryType type, const TypeTable *type_table) {
     73   switch (type) {
     74     case ET_UTYPE:
     75     case ET_BOOL:
     76     case ET_CHAR:
     77     case ET_UCHAR: return 1;
     78     case ET_SHORT:
     79     case ET_USHORT: return 2;
     80     case ET_INT:
     81     case ET_UINT:
     82     case ET_FLOAT:
     83     case ET_STRING: return 4;
     84     case ET_LONG:
     85     case ET_ULONG:
     86     case ET_DOUBLE: return 8;
     87     case ET_SEQUENCE:
     88       switch (type_table->st) {
     89         case ST_TABLE:
     90         case ST_UNION: return 4;
     91         case ST_STRUCT: return static_cast<size_t>(type_table->values[type_table->num_elems]);
     92         default: FLATBUFFERS_ASSERT(false); return 1;
     93       }
     94     default: FLATBUFFERS_ASSERT(false); return 1;
     95   }
     96 }
     97 
     98 inline int64_t LookupEnum(int64_t enum_val, const int64_t *values,
     99                           size_t num_values) {
    100   if (!values) return enum_val;
    101   for (size_t i = 0; i < num_values; i++) {
    102     if (enum_val == values[i]) return static_cast<int64_t>(i);
    103   }
    104   return -1;  // Unknown enum value.
    105 }
    106 
    107 template<typename T> const char *EnumName(T tval, const TypeTable *type_table) {
    108   if (!type_table || !type_table->names) return nullptr;
    109   auto i = LookupEnum(static_cast<int64_t>(tval), type_table->values,
    110                       type_table->num_elems);
    111   if (i >= 0 && i < static_cast<int64_t>(type_table->num_elems)) {
    112     return type_table->names[i];
    113   }
    114   return nullptr;
    115 }
    116 
    117 void IterateObject(const uint8_t *obj, const TypeTable *type_table,
    118                    IterationVisitor *visitor);
    119 
    120 inline void IterateValue(ElementaryType type, const uint8_t *val,
    121                          const TypeTable *type_table, const uint8_t *prev_val,
    122                          soffset_t vector_index, IterationVisitor *visitor) {
    123   switch (type) {
    124     case ET_UTYPE: {
    125       auto tval = ReadScalar<uint8_t>(val);
    126       visitor->UType(tval, EnumName(tval, type_table));
    127       break;
    128     }
    129     case ET_BOOL: {
    130       visitor->Bool(ReadScalar<uint8_t>(val) != 0);
    131       break;
    132     }
    133     case ET_CHAR: {
    134       auto tval = ReadScalar<int8_t>(val);
    135       visitor->Char(tval, EnumName(tval, type_table));
    136       break;
    137     }
    138     case ET_UCHAR: {
    139       auto tval = ReadScalar<uint8_t>(val);
    140       visitor->UChar(tval, EnumName(tval, type_table));
    141       break;
    142     }
    143     case ET_SHORT: {
    144       auto tval = ReadScalar<int16_t>(val);
    145       visitor->Short(tval, EnumName(tval, type_table));
    146       break;
    147     }
    148     case ET_USHORT: {
    149       auto tval = ReadScalar<uint16_t>(val);
    150       visitor->UShort(tval, EnumName(tval, type_table));
    151       break;
    152     }
    153     case ET_INT: {
    154       auto tval = ReadScalar<int32_t>(val);
    155       visitor->Int(tval, EnumName(tval, type_table));
    156       break;
    157     }
    158     case ET_UINT: {
    159       auto tval = ReadScalar<uint32_t>(val);
    160       visitor->UInt(tval, EnumName(tval, type_table));
    161       break;
    162     }
    163     case ET_LONG: {
    164       visitor->Long(ReadScalar<int64_t>(val));
    165       break;
    166     }
    167     case ET_ULONG: {
    168       visitor->ULong(ReadScalar<uint64_t>(val));
    169       break;
    170     }
    171     case ET_FLOAT: {
    172       visitor->Float(ReadScalar<float>(val));
    173       break;
    174     }
    175     case ET_DOUBLE: {
    176       visitor->Double(ReadScalar<double>(val));
    177       break;
    178     }
    179     case ET_STRING: {
    180       val += ReadScalar<uoffset_t>(val);
    181       visitor->String(reinterpret_cast<const String *>(val));
    182       break;
    183     }
    184     case ET_SEQUENCE: {
    185       switch (type_table->st) {
    186         case ST_TABLE:
    187           val += ReadScalar<uoffset_t>(val);
    188           IterateObject(val, type_table, visitor);
    189           break;
    190         case ST_STRUCT: IterateObject(val, type_table, visitor); break;
    191         case ST_UNION: {
    192           val += ReadScalar<uoffset_t>(val);
    193           FLATBUFFERS_ASSERT(prev_val);
    194           auto union_type = *prev_val;  // Always a uint8_t.
    195           if (vector_index >= 0) {
    196             auto type_vec = reinterpret_cast<const Vector<uint8_t> *>(prev_val);
    197             union_type = type_vec->Get(static_cast<uoffset_t>(vector_index));
    198           }
    199           auto type_code_idx =
    200               LookupEnum(union_type, type_table->values, type_table->num_elems);
    201           if (type_code_idx >= 0 &&
    202               type_code_idx < static_cast<int32_t>(type_table->num_elems)) {
    203             auto type_code = type_table->type_codes[type_code_idx];
    204             switch (type_code.base_type) {
    205               case ET_SEQUENCE: {
    206                 auto ref = type_table->type_refs[type_code.sequence_ref]();
    207                 IterateObject(val, ref, visitor);
    208                 break;
    209               }
    210               case ET_STRING:
    211                 visitor->String(reinterpret_cast<const String *>(val));
    212                 break;
    213               default: visitor->Unknown(val);
    214             }
    215           } else {
    216             visitor->Unknown(val);
    217           }
    218           break;
    219         }
    220         case ST_ENUM: FLATBUFFERS_ASSERT(false); break;
    221       }
    222       break;
    223     }
    224     default: {
    225       visitor->Unknown(val);
    226       break;
    227     }
    228   }
    229 }
    230 
    231 inline void IterateObject(const uint8_t *obj, const TypeTable *type_table,
    232                           IterationVisitor *visitor) {
    233   visitor->StartSequence();
    234   const uint8_t *prev_val = nullptr;
    235   size_t set_idx = 0;
    236   for (size_t i = 0; i < type_table->num_elems; i++) {
    237     auto type_code = type_table->type_codes[i];
    238     auto type = static_cast<ElementaryType>(type_code.base_type);
    239     auto is_vector = type_code.is_vector != 0;
    240     auto ref_idx = type_code.sequence_ref;
    241     const TypeTable *ref = nullptr;
    242     if (ref_idx >= 0) { ref = type_table->type_refs[ref_idx](); }
    243     auto name = type_table->names ? type_table->names[i] : nullptr;
    244     const uint8_t *val = nullptr;
    245     if (type_table->st == ST_TABLE) {
    246       val = reinterpret_cast<const Table *>(obj)->GetAddressOf(
    247           FieldIndexToOffset(static_cast<voffset_t>(i)));
    248     } else {
    249       val = obj + type_table->values[i];
    250     }
    251     visitor->Field(i, set_idx, type, is_vector, ref, name, val);
    252     if (val) {
    253       set_idx++;
    254       if (is_vector) {
    255         val += ReadScalar<uoffset_t>(val);
    256         auto vec = reinterpret_cast<const Vector<uint8_t> *>(val);
    257         visitor->StartVector();
    258         auto elem_ptr = vec->Data();
    259         for (size_t j = 0; j < vec->size(); j++) {
    260           visitor->Element(j, type, ref, elem_ptr);
    261           IterateValue(type, elem_ptr, ref, prev_val, static_cast<soffset_t>(j),
    262                        visitor);
    263           elem_ptr += InlineSize(type, ref);
    264         }
    265         visitor->EndVector();
    266       } else {
    267         IterateValue(type, val, ref, prev_val, -1, visitor);
    268       }
    269     }
    270     prev_val = val;
    271   }
    272   visitor->EndSequence();
    273 }
    274 
    275 inline void IterateFlatBuffer(const uint8_t *buffer,
    276                               const TypeTable *type_table,
    277                               IterationVisitor *callback) {
    278   IterateObject(GetRoot<uint8_t>(buffer), type_table, callback);
    279 }
    280 
    281 // Outputting a Flatbuffer to a string. Tries to conform as close to JSON /
    282 // the output generated by idl_gen_text.cpp.
    283 
    284 struct ToStringVisitor : public IterationVisitor {
    285   std::string s;
    286   std::string d;
    287   bool q;
    288   std::string in;
    289   size_t indent_level;
    290   ToStringVisitor(std::string delimiter, bool quotes, std::string indent)
    291       : d(delimiter), q(quotes), in(indent), indent_level(0) {}
    292   ToStringVisitor(std::string delimiter)
    293       : d(delimiter), q(false), in(""), indent_level(0) {}
    294 
    295   void append_indent() {
    296     for (size_t i = 0; i < indent_level; i++) { s += in; }
    297   }
    298 
    299   void StartSequence() {
    300     s += "{";
    301     s += d;
    302     indent_level++;
    303   }
    304   void EndSequence() {
    305     s += d;
    306     indent_level--;
    307     append_indent();
    308     s += "}";
    309   }
    310   void Field(size_t /*field_idx*/, size_t set_idx, ElementaryType /*type*/,
    311              bool /*is_vector*/, const TypeTable * /*type_table*/,
    312              const char *name, const uint8_t *val) {
    313     if (!val) return;
    314     if (set_idx) {
    315       s += ",";
    316       s += d;
    317     }
    318     append_indent();
    319     if (name) {
    320       if (q) s += "\"";
    321       s += name;
    322       if (q) s += "\"";
    323       s += ": ";
    324     }
    325   }
    326   template<typename T> void Named(T x, const char *name) {
    327     if (name) {
    328       if (q) s += "\"";
    329       s += name;
    330       if (q) s += "\"";
    331     } else {
    332       s += NumToString(x);
    333     }
    334   }
    335   void UType(uint8_t x, const char *name) { Named(x, name); }
    336   void Bool(bool x) { s += x ? "true" : "false"; }
    337   void Char(int8_t x, const char *name) { Named(x, name); }
    338   void UChar(uint8_t x, const char *name) { Named(x, name); }
    339   void Short(int16_t x, const char *name) { Named(x, name); }
    340   void UShort(uint16_t x, const char *name) { Named(x, name); }
    341   void Int(int32_t x, const char *name) { Named(x, name); }
    342   void UInt(uint32_t x, const char *name) { Named(x, name); }
    343   void Long(int64_t x) { s += NumToString(x); }
    344   void ULong(uint64_t x) { s += NumToString(x); }
    345   void Float(float x) { s += NumToString(x); }
    346   void Double(double x) { s += NumToString(x); }
    347   void String(const struct String *str) {
    348     EscapeString(str->c_str(), str->size(), &s, true, false);
    349   }
    350   void Unknown(const uint8_t *) { s += "(?)"; }
    351   void StartVector() {
    352     s += "[";
    353     s += d;
    354     indent_level++;
    355     append_indent();
    356   }
    357   void EndVector() {
    358     s += d;
    359     indent_level--;
    360     append_indent();
    361     s += "]";
    362   }
    363   void Element(size_t i, ElementaryType /*type*/,
    364                const TypeTable * /*type_table*/, const uint8_t * /*val*/) {
    365     if (i) {
    366       s += ",";
    367       s += d;
    368       append_indent();
    369     }
    370   }
    371 };
    372 
    373 inline std::string FlatBufferToString(const uint8_t *buffer,
    374                                       const TypeTable *type_table,
    375                                       bool multi_line = false) {
    376   ToStringVisitor tostring_visitor(multi_line ? "\n" : " ");
    377   IterateFlatBuffer(buffer, type_table, &tostring_visitor);
    378   return tostring_visitor.s;
    379 }
    380 
    381 }  // namespace flatbuffers
    382 
    383 #endif  // FLATBUFFERS_MINIREFLECT_H_
    384