/* * Copyright 2017 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FLATBUFFERS_MINIREFLECT_H_ #define FLATBUFFERS_MINIREFLECT_H_ #include "flatbuffers/flatbuffers.h" #include "flatbuffers/util.h" namespace flatbuffers { // Utilities that can be used with the "mini reflection" tables present // in generated code with --reflect-types (only types) or --reflect-names // (also names). // This allows basic reflection functionality such as pretty-printing // that does not require the use of the schema parser or loading of binary // schema files at runtime (reflection.h). // For any of the functions below that take `const TypeTable *`, you pass // `FooTypeTable()` if the type of the root is `Foo`. // First, a generic iterator that can be used by multiple algorithms. struct IterationVisitor { // These mark the scope of a table or struct. virtual void StartSequence() {} virtual void EndSequence() {} // Called for each field regardless of whether it is present or not. // If not present, val == nullptr. set_idx is the index of all set fields. virtual void Field(size_t /*field_idx*/, size_t /*set_idx*/, ElementaryType /*type*/, bool /*is_vector*/, const TypeTable * /*type_table*/, const char * /*name*/, const uint8_t * /*val*/) {} // Called for a value that is actually present, after a field, or as part // of a vector. virtual void UType(uint8_t, const char *) {} virtual void Bool(bool) {} virtual void Char(int8_t, const char *) {} virtual void UChar(uint8_t, const char *) {} virtual void Short(int16_t, const char *) {} virtual void UShort(uint16_t, const char *) {} virtual void Int(int32_t, const char *) {} virtual void UInt(uint32_t, const char *) {} virtual void Long(int64_t) {} virtual void ULong(uint64_t) {} virtual void Float(float) {} virtual void Double(double) {} virtual void String(const String *) {} virtual void Unknown(const uint8_t *) {} // From a future version. // These mark the scope of a vector. virtual void StartVector() {} virtual void EndVector() {} virtual void Element(size_t /*i*/, ElementaryType /*type*/, const TypeTable * /*type_table*/, const uint8_t * /*val*/) {} virtual ~IterationVisitor() {} }; inline size_t InlineSize(ElementaryType type, const TypeTable *type_table) { switch (type) { case ET_UTYPE: case ET_BOOL: case ET_CHAR: case ET_UCHAR: return 1; case ET_SHORT: case ET_USHORT: return 2; case ET_INT: case ET_UINT: case ET_FLOAT: case ET_STRING: return 4; case ET_LONG: case ET_ULONG: case ET_DOUBLE: return 8; case ET_SEQUENCE: switch (type_table->st) { case ST_TABLE: case ST_UNION: return 4; case ST_STRUCT: return static_cast(type_table->values[type_table->num_elems]); default: FLATBUFFERS_ASSERT(false); return 1; } default: FLATBUFFERS_ASSERT(false); return 1; } } inline int64_t LookupEnum(int64_t enum_val, const int64_t *values, size_t num_values) { if (!values) return enum_val; for (size_t i = 0; i < num_values; i++) { if (enum_val == values[i]) return static_cast(i); } return -1; // Unknown enum value. } template const char *EnumName(T tval, const TypeTable *type_table) { if (!type_table || !type_table->names) return nullptr; auto i = LookupEnum(static_cast(tval), type_table->values, type_table->num_elems); if (i >= 0 && i < static_cast(type_table->num_elems)) { return type_table->names[i]; } return nullptr; } void IterateObject(const uint8_t *obj, const TypeTable *type_table, IterationVisitor *visitor); inline void IterateValue(ElementaryType type, const uint8_t *val, const TypeTable *type_table, const uint8_t *prev_val, soffset_t vector_index, IterationVisitor *visitor) { switch (type) { case ET_UTYPE: { auto tval = ReadScalar(val); visitor->UType(tval, EnumName(tval, type_table)); break; } case ET_BOOL: { visitor->Bool(ReadScalar(val) != 0); break; } case ET_CHAR: { auto tval = ReadScalar(val); visitor->Char(tval, EnumName(tval, type_table)); break; } case ET_UCHAR: { auto tval = ReadScalar(val); visitor->UChar(tval, EnumName(tval, type_table)); break; } case ET_SHORT: { auto tval = ReadScalar(val); visitor->Short(tval, EnumName(tval, type_table)); break; } case ET_USHORT: { auto tval = ReadScalar(val); visitor->UShort(tval, EnumName(tval, type_table)); break; } case ET_INT: { auto tval = ReadScalar(val); visitor->Int(tval, EnumName(tval, type_table)); break; } case ET_UINT: { auto tval = ReadScalar(val); visitor->UInt(tval, EnumName(tval, type_table)); break; } case ET_LONG: { visitor->Long(ReadScalar(val)); break; } case ET_ULONG: { visitor->ULong(ReadScalar(val)); break; } case ET_FLOAT: { visitor->Float(ReadScalar(val)); break; } case ET_DOUBLE: { visitor->Double(ReadScalar(val)); break; } case ET_STRING: { val += ReadScalar(val); visitor->String(reinterpret_cast(val)); break; } case ET_SEQUENCE: { switch (type_table->st) { case ST_TABLE: val += ReadScalar(val); IterateObject(val, type_table, visitor); break; case ST_STRUCT: IterateObject(val, type_table, visitor); break; case ST_UNION: { val += ReadScalar(val); FLATBUFFERS_ASSERT(prev_val); auto union_type = *prev_val; // Always a uint8_t. if (vector_index >= 0) { auto type_vec = reinterpret_cast *>(prev_val); union_type = type_vec->Get(static_cast(vector_index)); } auto type_code_idx = LookupEnum(union_type, type_table->values, type_table->num_elems); if (type_code_idx >= 0 && type_code_idx < static_cast(type_table->num_elems)) { auto type_code = type_table->type_codes[type_code_idx]; switch (type_code.base_type) { case ET_SEQUENCE: { auto ref = type_table->type_refs[type_code.sequence_ref](); IterateObject(val, ref, visitor); break; } case ET_STRING: visitor->String(reinterpret_cast(val)); break; default: visitor->Unknown(val); } } else { visitor->Unknown(val); } break; } case ST_ENUM: FLATBUFFERS_ASSERT(false); break; } break; } default: { visitor->Unknown(val); break; } } } inline void IterateObject(const uint8_t *obj, const TypeTable *type_table, IterationVisitor *visitor) { visitor->StartSequence(); const uint8_t *prev_val = nullptr; size_t set_idx = 0; size_t array_idx = 0; for (size_t i = 0; i < type_table->num_elems; i++) { auto type_code = type_table->type_codes[i]; auto type = static_cast(type_code.base_type); auto is_repeating = type_code.is_repeating != 0; auto ref_idx = type_code.sequence_ref; const TypeTable *ref = nullptr; if (ref_idx >= 0) { ref = type_table->type_refs[ref_idx](); } auto name = type_table->names ? type_table->names[i] : nullptr; const uint8_t *val = nullptr; if (type_table->st == ST_TABLE) { val = reinterpret_cast(obj)->GetAddressOf( FieldIndexToOffset(static_cast(i))); } else { val = obj + type_table->values[i]; } visitor->Field(i, set_idx, type, is_repeating, ref, name, val); if (val) { set_idx++; if (is_repeating) { auto elem_ptr = val; size_t size = 0; if (type_table->st == ST_TABLE) { // variable length vector val += ReadScalar(val); auto vec = reinterpret_cast *>(val); elem_ptr = vec->Data(); size = vec->size(); } else { // otherwise fixed size array size = type_table->array_sizes[array_idx]; ++array_idx; } visitor->StartVector(); for (size_t j = 0; j < size; j++) { visitor->Element(j, type, ref, elem_ptr); IterateValue(type, elem_ptr, ref, prev_val, static_cast(j), visitor); elem_ptr += InlineSize(type, ref); } visitor->EndVector(); } else { IterateValue(type, val, ref, prev_val, -1, visitor); } } prev_val = val; } visitor->EndSequence(); } inline void IterateFlatBuffer(const uint8_t *buffer, const TypeTable *type_table, IterationVisitor *callback) { IterateObject(GetRoot(buffer), type_table, callback); } // Outputting a Flatbuffer to a string. Tries to conform as close to JSON / // the output generated by idl_gen_text.cpp. struct ToStringVisitor : public IterationVisitor { std::string s; std::string d; bool q; std::string in; size_t indent_level; bool vector_delimited; ToStringVisitor(std::string delimiter, bool quotes, std::string indent, bool vdelimited = true) : d(delimiter), q(quotes), in(indent), indent_level(0), vector_delimited(vdelimited) {} ToStringVisitor(std::string delimiter) : d(delimiter), q(false), in(""), indent_level(0), vector_delimited(true) {} void append_indent() { for (size_t i = 0; i < indent_level; i++) { s += in; } } void StartSequence() { s += "{"; s += d; indent_level++; } void EndSequence() { s += d; indent_level--; append_indent(); s += "}"; } void Field(size_t /*field_idx*/, size_t set_idx, ElementaryType /*type*/, bool /*is_vector*/, const TypeTable * /*type_table*/, const char *name, const uint8_t *val) { if (!val) return; if (set_idx) { s += ","; s += d; } append_indent(); if (name) { if (q) s += "\""; s += name; if (q) s += "\""; s += ": "; } } template void Named(T x, const char *name) { if (name) { if (q) s += "\""; s += name; if (q) s += "\""; } else { s += NumToString(x); } } void UType(uint8_t x, const char *name) { Named(x, name); } void Bool(bool x) { s += x ? "true" : "false"; } void Char(int8_t x, const char *name) { Named(x, name); } void UChar(uint8_t x, const char *name) { Named(x, name); } void Short(int16_t x, const char *name) { Named(x, name); } void UShort(uint16_t x, const char *name) { Named(x, name); } void Int(int32_t x, const char *name) { Named(x, name); } void UInt(uint32_t x, const char *name) { Named(x, name); } void Long(int64_t x) { s += NumToString(x); } void ULong(uint64_t x) { s += NumToString(x); } void Float(float x) { s += NumToString(x); } void Double(double x) { s += NumToString(x); } void String(const struct String *str) { EscapeString(str->c_str(), str->size(), &s, true, false); } void Unknown(const uint8_t *) { s += "(?)"; } void StartVector() { s += "["; if (vector_delimited) { s += d; indent_level++; append_indent(); } else { s += " "; } } void EndVector() { if (vector_delimited) { s += d; indent_level--; append_indent(); } else { s += " "; } s += "]"; } void Element(size_t i, ElementaryType /*type*/, const TypeTable * /*type_table*/, const uint8_t * /*val*/) { if (i) { s += ","; if (vector_delimited) { s += d; append_indent(); } else { s += " "; } } } }; inline std::string FlatBufferToString(const uint8_t *buffer, const TypeTable *type_table, bool multi_line = false, bool vector_delimited = true, const std::string &indent = "") { ToStringVisitor tostring_visitor(multi_line ? "\n" : " ", false, indent, vector_delimited); IterateFlatBuffer(buffer, type_table, &tostring_visitor); return tostring_visitor.s; } } // namespace flatbuffers #endif // FLATBUFFERS_MINIREFLECT_H_