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