Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame^] | 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 | bool vector_delimited; |
| 291 | ToStringVisitor(std::string delimiter, bool quotes, std::string indent, |
| 292 | bool vdelimited = true) |
| 293 | : d(delimiter), |
| 294 | q(quotes), |
| 295 | in(indent), |
| 296 | indent_level(0), |
| 297 | vector_delimited(vdelimited) {} |
| 298 | ToStringVisitor(std::string delimiter) |
| 299 | : d(delimiter), |
| 300 | q(false), |
| 301 | in(""), |
| 302 | indent_level(0), |
| 303 | vector_delimited(true) {} |
| 304 | |
| 305 | void append_indent() { |
| 306 | for (size_t i = 0; i < indent_level; i++) { s += in; } |
| 307 | } |
| 308 | |
| 309 | void StartSequence() { |
| 310 | s += "{"; |
| 311 | s += d; |
| 312 | indent_level++; |
| 313 | } |
| 314 | void EndSequence() { |
| 315 | s += d; |
| 316 | indent_level--; |
| 317 | append_indent(); |
| 318 | s += "}"; |
| 319 | } |
| 320 | void Field(size_t /*field_idx*/, size_t set_idx, ElementaryType /*type*/, |
| 321 | bool /*is_vector*/, const TypeTable * /*type_table*/, |
| 322 | const char *name, const uint8_t *val) { |
| 323 | if (!val) return; |
| 324 | if (set_idx) { |
| 325 | s += ","; |
| 326 | s += d; |
| 327 | } |
| 328 | append_indent(); |
| 329 | if (name) { |
| 330 | if (q) s += "\""; |
| 331 | s += name; |
| 332 | if (q) s += "\""; |
| 333 | s += ": "; |
| 334 | } |
| 335 | } |
| 336 | template<typename T> void Named(T x, const char *name) { |
| 337 | if (name) { |
| 338 | if (q) s += "\""; |
| 339 | s += name; |
| 340 | if (q) s += "\""; |
| 341 | } else { |
| 342 | s += NumToString(x); |
| 343 | } |
| 344 | } |
| 345 | void UType(uint8_t x, const char *name) { Named(x, name); } |
| 346 | void Bool(bool x) { s += x ? "true" : "false"; } |
| 347 | void Char(int8_t x, const char *name) { Named(x, name); } |
| 348 | void UChar(uint8_t x, const char *name) { Named(x, name); } |
| 349 | void Short(int16_t x, const char *name) { Named(x, name); } |
| 350 | void UShort(uint16_t x, const char *name) { Named(x, name); } |
| 351 | void Int(int32_t x, const char *name) { Named(x, name); } |
| 352 | void UInt(uint32_t x, const char *name) { Named(x, name); } |
| 353 | void Long(int64_t x) { s += NumToString(x); } |
| 354 | void ULong(uint64_t x) { s += NumToString(x); } |
| 355 | void Float(float x) { s += NumToString(x); } |
| 356 | void Double(double x) { s += NumToString(x); } |
| 357 | void String(const struct String *str) { |
| 358 | EscapeString(str->c_str(), str->size(), &s, true, false); |
| 359 | } |
| 360 | void Unknown(const uint8_t *) { s += "(?)"; } |
| 361 | void StartVector() { |
| 362 | s += "["; |
| 363 | if (vector_delimited) { |
| 364 | s += d; |
| 365 | indent_level++; |
| 366 | append_indent(); |
| 367 | } else { |
| 368 | s += " "; |
| 369 | } |
| 370 | } |
| 371 | void EndVector() { |
| 372 | if (vector_delimited) { |
| 373 | s += d; |
| 374 | indent_level--; |
| 375 | append_indent(); |
| 376 | } else { |
| 377 | s += " "; |
| 378 | } |
| 379 | s += "]"; |
| 380 | } |
| 381 | void Element(size_t i, ElementaryType /*type*/, |
| 382 | const TypeTable * /*type_table*/, const uint8_t * /*val*/) { |
| 383 | if (i) { |
| 384 | s += ","; |
| 385 | if (vector_delimited) { |
| 386 | s += d; |
| 387 | append_indent(); |
| 388 | } else { |
| 389 | s += " "; |
| 390 | } |
| 391 | } |
| 392 | } |
| 393 | }; |
| 394 | |
| 395 | inline std::string FlatBufferToString(const uint8_t *buffer, |
| 396 | const TypeTable *type_table, |
| 397 | bool multi_line = false, |
| 398 | bool vector_delimited = true) { |
| 399 | ToStringVisitor tostring_visitor(multi_line ? "\n" : " ", false, "", |
| 400 | vector_delimited); |
| 401 | IterateFlatBuffer(buffer, type_table, &tostring_visitor); |
| 402 | return tostring_visitor.s; |
| 403 | } |
| 404 | |
| 405 | } // namespace flatbuffers |
| 406 | |
| 407 | #endif // FLATBUFFERS_MINIREFLECT_H_ |