/* * 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_FLEXBUFFERS_H_ #define FLATBUFFERS_FLEXBUFFERS_H_ #include #include // Used to select STL variant. #include "flatbuffers/base.h" // We use the basic binary writing functions from the regular FlatBuffers. #include "flatbuffers/util.h" #ifdef _MSC_VER # include #endif #if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable : 4127) // C4127: conditional expression is constant #endif namespace flexbuffers { class Reference; class Map; // These are used in the lower 2 bits of a type field to determine the size of // the elements (and or size field) of the item pointed to (e.g. vector). enum BitWidth { BIT_WIDTH_8 = 0, BIT_WIDTH_16 = 1, BIT_WIDTH_32 = 2, BIT_WIDTH_64 = 3, }; // These are used as the upper 6 bits of a type field to indicate the actual // type. enum Type { FBT_NULL = 0, FBT_INT = 1, FBT_UINT = 2, FBT_FLOAT = 3, // Types above stored inline, types below (except FBT_BOOL) store an offset. FBT_KEY = 4, FBT_STRING = 5, FBT_INDIRECT_INT = 6, FBT_INDIRECT_UINT = 7, FBT_INDIRECT_FLOAT = 8, FBT_MAP = 9, FBT_VECTOR = 10, // Untyped. FBT_VECTOR_INT = 11, // Typed any size (stores no type table). FBT_VECTOR_UINT = 12, FBT_VECTOR_FLOAT = 13, FBT_VECTOR_KEY = 14, // DEPRECATED, use FBT_VECTOR or FBT_VECTOR_KEY instead. // Read test.cpp/FlexBuffersDeprecatedTest() for details on why. FBT_VECTOR_STRING_DEPRECATED = 15, FBT_VECTOR_INT2 = 16, // Typed tuple (no type table, no size field). FBT_VECTOR_UINT2 = 17, FBT_VECTOR_FLOAT2 = 18, FBT_VECTOR_INT3 = 19, // Typed triple (no type table, no size field). FBT_VECTOR_UINT3 = 20, FBT_VECTOR_FLOAT3 = 21, FBT_VECTOR_INT4 = 22, // Typed quad (no type table, no size field). FBT_VECTOR_UINT4 = 23, FBT_VECTOR_FLOAT4 = 24, FBT_BLOB = 25, FBT_BOOL = 26, FBT_VECTOR_BOOL = 36, // To Allow the same type of conversion of type to vector type FBT_MAX_TYPE = 37 }; inline bool IsInline(Type t) { return t <= FBT_FLOAT || t == FBT_BOOL; } inline bool IsTypedVectorElementType(Type t) { return (t >= FBT_INT && t <= FBT_STRING) || t == FBT_BOOL; } inline bool IsTypedVector(Type t) { return (t >= FBT_VECTOR_INT && t <= FBT_VECTOR_STRING_DEPRECATED) || t == FBT_VECTOR_BOOL; } inline bool IsFixedTypedVector(Type t) { return t >= FBT_VECTOR_INT2 && t <= FBT_VECTOR_FLOAT4; } inline Type ToTypedVector(Type t, size_t fixed_len = 0) { FLATBUFFERS_ASSERT(IsTypedVectorElementType(t)); switch (fixed_len) { case 0: return static_cast(t - FBT_INT + FBT_VECTOR_INT); case 2: return static_cast(t - FBT_INT + FBT_VECTOR_INT2); case 3: return static_cast(t - FBT_INT + FBT_VECTOR_INT3); case 4: return static_cast(t - FBT_INT + FBT_VECTOR_INT4); default: FLATBUFFERS_ASSERT(0); return FBT_NULL; } } inline Type ToTypedVectorElementType(Type t) { FLATBUFFERS_ASSERT(IsTypedVector(t)); return static_cast(t - FBT_VECTOR_INT + FBT_INT); } inline Type ToFixedTypedVectorElementType(Type t, uint8_t *len) { FLATBUFFERS_ASSERT(IsFixedTypedVector(t)); auto fixed_type = t - FBT_VECTOR_INT2; *len = static_cast(fixed_type / 3 + 2); // 3 types each, starting from length 2. return static_cast(fixed_type % 3 + FBT_INT); } // TODO: implement proper support for 8/16bit floats, or decide not to // support them. typedef int16_t half; typedef int8_t quarter; // TODO: can we do this without conditionals using intrinsics or inline asm // on some platforms? Given branch prediction the method below should be // decently quick, but it is the most frequently executed function. // We could do an (unaligned) 64-bit read if we ifdef out the platforms for // which that doesn't work (or where we'd read into un-owned memory). template R ReadSizedScalar(const uint8_t *data, uint8_t byte_width) { return byte_width < 4 ? (byte_width < 2 ? static_cast(flatbuffers::ReadScalar(data)) : static_cast(flatbuffers::ReadScalar(data))) : (byte_width < 8 ? static_cast(flatbuffers::ReadScalar(data)) : static_cast(flatbuffers::ReadScalar(data))); } inline int64_t ReadInt64(const uint8_t *data, uint8_t byte_width) { return ReadSizedScalar( data, byte_width); } inline uint64_t ReadUInt64(const uint8_t *data, uint8_t byte_width) { // This is the "hottest" function (all offset lookups use this), so worth // optimizing if possible. // TODO: GCC apparently replaces memcpy by a rep movsb, but only if count is a // constant, which here it isn't. Test if memcpy is still faster than // the conditionals in ReadSizedScalar. Can also use inline asm. // clang-format off #if defined(_MSC_VER) && defined(_M_X64) && !defined(_M_ARM64EC) // This is 64-bit Windows only, __movsb does not work on 32-bit Windows. uint64_t u = 0; __movsb(reinterpret_cast(&u), reinterpret_cast(data), byte_width); return flatbuffers::EndianScalar(u); #else return ReadSizedScalar( data, byte_width); #endif // clang-format on } inline double ReadDouble(const uint8_t *data, uint8_t byte_width) { return ReadSizedScalar(data, byte_width); } inline const uint8_t *Indirect(const uint8_t *offset, uint8_t byte_width) { return offset - ReadUInt64(offset, byte_width); } template const uint8_t *Indirect(const uint8_t *offset) { return offset - flatbuffers::ReadScalar(offset); } inline BitWidth WidthU(uint64_t u) { #define FLATBUFFERS_GET_FIELD_BIT_WIDTH(value, width) \ { \ if (!((u) & ~((1ULL << (width)) - 1ULL))) return BIT_WIDTH_##width; \ } FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 8); FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 16); FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 32); #undef FLATBUFFERS_GET_FIELD_BIT_WIDTH return BIT_WIDTH_64; } inline BitWidth WidthI(int64_t i) { auto u = static_cast(i) << 1; return WidthU(i >= 0 ? u : ~u); } inline BitWidth WidthF(double f) { return static_cast(static_cast(f)) == f ? BIT_WIDTH_32 : BIT_WIDTH_64; } // Base class of all types below. // Points into the data buffer and allows access to one type. class Object { public: Object(const uint8_t *data, uint8_t byte_width) : data_(data), byte_width_(byte_width) {} protected: const uint8_t *data_; uint8_t byte_width_; }; // Object that has a size, obtained either from size prefix, or elsewhere. class Sized : public Object { public: // Size prefix. Sized(const uint8_t *data, uint8_t byte_width) : Object(data, byte_width), size_(read_size()) {} // Manual size. Sized(const uint8_t *data, uint8_t byte_width, size_t sz) : Object(data, byte_width), size_(sz) {} size_t size() const { return size_; } // Access size stored in `byte_width_` bytes before data_ pointer. size_t read_size() const { return static_cast(ReadUInt64(data_ - byte_width_, byte_width_)); } protected: size_t size_; }; class String : public Sized { public: // Size prefix. String(const uint8_t *data, uint8_t byte_width) : Sized(data, byte_width) {} // Manual size. String(const uint8_t *data, uint8_t byte_width, size_t sz) : Sized(data, byte_width, sz) {} size_t length() const { return size(); } const char *c_str() const { return reinterpret_cast(data_); } std::string str() const { return std::string(c_str(), size()); } static String EmptyString() { static const char *empty_string = ""; return String(reinterpret_cast(empty_string), 1, 0); } bool IsTheEmptyString() const { return data_ == EmptyString().data_; } }; class Blob : public Sized { public: Blob(const uint8_t *data_buf, uint8_t byte_width) : Sized(data_buf, byte_width) {} static Blob EmptyBlob() { static const uint8_t empty_blob[] = { 0 /*len*/ }; return Blob(empty_blob + 1, 1); } bool IsTheEmptyBlob() const { return data_ == EmptyBlob().data_; } const uint8_t *data() const { return data_; } }; class Vector : public Sized { public: Vector(const uint8_t *data, uint8_t byte_width) : Sized(data, byte_width) {} Reference operator[](size_t i) const; static Vector EmptyVector() { static const uint8_t empty_vector[] = { 0 /*len*/ }; return Vector(empty_vector + 1, 1); } bool IsTheEmptyVector() const { return data_ == EmptyVector().data_; } }; class TypedVector : public Sized { public: TypedVector(const uint8_t *data, uint8_t byte_width, Type element_type) : Sized(data, byte_width), type_(element_type) {} Reference operator[](size_t i) const; static TypedVector EmptyTypedVector() { static const uint8_t empty_typed_vector[] = { 0 /*len*/ }; return TypedVector(empty_typed_vector + 1, 1, FBT_INT); } bool IsTheEmptyVector() const { return data_ == TypedVector::EmptyTypedVector().data_; } Type ElementType() { return type_; } friend Reference; private: Type type_; friend Map; }; class FixedTypedVector : public Object { public: FixedTypedVector(const uint8_t *data, uint8_t byte_width, Type element_type, uint8_t len) : Object(data, byte_width), type_(element_type), len_(len) {} Reference operator[](size_t i) const; static FixedTypedVector EmptyFixedTypedVector() { static const uint8_t fixed_empty_vector[] = { 0 /* unused */ }; return FixedTypedVector(fixed_empty_vector, 1, FBT_INT, 0); } bool IsTheEmptyFixedTypedVector() const { return data_ == FixedTypedVector::EmptyFixedTypedVector().data_; } Type ElementType() const { return type_; } uint8_t size() const { return len_; } private: Type type_; uint8_t len_; }; class Map : public Vector { public: Map(const uint8_t *data, uint8_t byte_width) : Vector(data, byte_width) {} Reference operator[](const char *key) const; Reference operator[](const std::string &key) const; Vector Values() const { return Vector(data_, byte_width_); } TypedVector Keys() const { const size_t num_prefixed_fields = 3; auto keys_offset = data_ - byte_width_ * num_prefixed_fields; return TypedVector(Indirect(keys_offset, byte_width_), static_cast( ReadUInt64(keys_offset + byte_width_, byte_width_)), FBT_KEY); } static Map EmptyMap() { static const uint8_t empty_map[] = { 0 /*keys_len*/, 0 /*keys_offset*/, 1 /*keys_width*/, 0 /*len*/ }; return Map(empty_map + 4, 1); } bool IsTheEmptyMap() const { return data_ == EmptyMap().data_; } }; inline void IndentString(std::string &s, int indent, const char *indent_string) { for (int i = 0; i < indent; i++) s += indent_string; } template void AppendToString(std::string &s, T &&v, bool keys_quoted, bool indented, int cur_indent, const char *indent_string) { s += "["; s += indented ? "\n" : " "; for (size_t i = 0; i < v.size(); i++) { if (i) { s += ","; s += indented ? "\n" : " "; } if (indented) IndentString(s, cur_indent, indent_string); v[i].ToString(true, keys_quoted, s, indented, cur_indent, indent_string); } if (indented) { s += "\n"; IndentString(s, cur_indent - 1, indent_string); } else { s += " "; } s += "]"; } template void AppendToString(std::string &s, T &&v, bool keys_quoted) { AppendToString(s, v, keys_quoted); } class Reference { public: Reference() : data_(nullptr), parent_width_(0), byte_width_(0), type_(FBT_NULL) {} Reference(const uint8_t *data, uint8_t parent_width, uint8_t byte_width, Type type) : data_(data), parent_width_(parent_width), byte_width_(byte_width), type_(type) {} Reference(const uint8_t *data, uint8_t parent_width, uint8_t packed_type) : data_(data), parent_width_(parent_width), byte_width_(static_cast(1 << (packed_type & 3))), type_(static_cast(packed_type >> 2)) {} Type GetType() const { return type_; } bool IsNull() const { return type_ == FBT_NULL; } bool IsBool() const { return type_ == FBT_BOOL; } bool IsInt() const { return type_ == FBT_INT || type_ == FBT_INDIRECT_INT; } bool IsUInt() const { return type_ == FBT_UINT || type_ == FBT_INDIRECT_UINT; } bool IsIntOrUint() const { return IsInt() || IsUInt(); } bool IsFloat() const { return type_ == FBT_FLOAT || type_ == FBT_INDIRECT_FLOAT; } bool IsNumeric() const { return IsIntOrUint() || IsFloat(); } bool IsString() const { return type_ == FBT_STRING; } bool IsKey() const { return type_ == FBT_KEY; } bool IsVector() const { return type_ == FBT_VECTOR || type_ == FBT_MAP; } bool IsUntypedVector() const { return type_ == FBT_VECTOR; } bool IsTypedVector() const { return flexbuffers::IsTypedVector(type_); } bool IsFixedTypedVector() const { return flexbuffers::IsFixedTypedVector(type_); } bool IsAnyVector() const { return (IsTypedVector() || IsFixedTypedVector() || IsVector()); } bool IsMap() const { return type_ == FBT_MAP; } bool IsBlob() const { return type_ == FBT_BLOB; } bool AsBool() const { return (type_ == FBT_BOOL ? ReadUInt64(data_, parent_width_) : AsUInt64()) != 0; } // Reads any type as a int64_t. Never fails, does most sensible conversion. // Truncates floats, strings are attempted to be parsed for a number, // vectors/maps return their size. Returns 0 if all else fails. int64_t AsInt64() const { if (type_ == FBT_INT) { // A fast path for the common case. return ReadInt64(data_, parent_width_); } else switch (type_) { case FBT_INDIRECT_INT: return ReadInt64(Indirect(), byte_width_); case FBT_UINT: return ReadUInt64(data_, parent_width_); case FBT_INDIRECT_UINT: return ReadUInt64(Indirect(), byte_width_); case FBT_FLOAT: return static_cast(ReadDouble(data_, parent_width_)); case FBT_INDIRECT_FLOAT: return static_cast(ReadDouble(Indirect(), byte_width_)); case FBT_NULL: return 0; case FBT_STRING: return flatbuffers::StringToInt(AsString().c_str()); case FBT_VECTOR: return static_cast(AsVector().size()); case FBT_BOOL: return ReadInt64(data_, parent_width_); default: // Convert other things to int. return 0; } } // TODO: could specialize these to not use AsInt64() if that saves // extension ops in generated code, and use a faster op than ReadInt64. int32_t AsInt32() const { return static_cast(AsInt64()); } int16_t AsInt16() const { return static_cast(AsInt64()); } int8_t AsInt8() const { return static_cast(AsInt64()); } uint64_t AsUInt64() const { if (type_ == FBT_UINT) { // A fast path for the common case. return ReadUInt64(data_, parent_width_); } else switch (type_) { case FBT_INDIRECT_UINT: return ReadUInt64(Indirect(), byte_width_); case FBT_INT: return ReadInt64(data_, parent_width_); case FBT_INDIRECT_INT: return ReadInt64(Indirect(), byte_width_); case FBT_FLOAT: return static_cast(ReadDouble(data_, parent_width_)); case FBT_INDIRECT_FLOAT: return static_cast(ReadDouble(Indirect(), byte_width_)); case FBT_NULL: return 0; case FBT_STRING: return flatbuffers::StringToUInt(AsString().c_str()); case FBT_VECTOR: return static_cast(AsVector().size()); case FBT_BOOL: return ReadUInt64(data_, parent_width_); default: // Convert other things to uint. return 0; } } uint32_t AsUInt32() const { return static_cast(AsUInt64()); } uint16_t AsUInt16() const { return static_cast(AsUInt64()); } uint8_t AsUInt8() const { return static_cast(AsUInt64()); } double AsDouble() const { if (type_ == FBT_FLOAT) { // A fast path for the common case. return ReadDouble(data_, parent_width_); } else switch (type_) { case FBT_INDIRECT_FLOAT: return ReadDouble(Indirect(), byte_width_); case FBT_INT: return static_cast(ReadInt64(data_, parent_width_)); case FBT_UINT: return static_cast(ReadUInt64(data_, parent_width_)); case FBT_INDIRECT_INT: return static_cast(ReadInt64(Indirect(), byte_width_)); case FBT_INDIRECT_UINT: return static_cast(ReadUInt64(Indirect(), byte_width_)); case FBT_NULL: return 0.0; case FBT_STRING: { double d; flatbuffers::StringToNumber(AsString().c_str(), &d); return d; } case FBT_VECTOR: return static_cast(AsVector().size()); case FBT_BOOL: return static_cast(ReadUInt64(data_, parent_width_)); default: // Convert strings and other things to float. return 0; } } float AsFloat() const { return static_cast(AsDouble()); } const char *AsKey() const { if (type_ == FBT_KEY || type_ == FBT_STRING) { return reinterpret_cast(Indirect()); } else { return ""; } } // This function returns the empty string if you try to read something that // is not a string or key. String AsString() const { if (type_ == FBT_STRING) { return String(Indirect(), byte_width_); } else if (type_ == FBT_KEY) { auto key = Indirect(); return String(key, byte_width_, strlen(reinterpret_cast(key))); } else { return String::EmptyString(); } } // Unlike AsString(), this will convert any type to a std::string. std::string ToString() const { std::string s; ToString(false, false, s); return s; } // Convert any type to a JSON-like string. strings_quoted determines if // string values at the top level receive "" quotes (inside other values // they always do). keys_quoted determines if keys are quoted, at any level. void ToString(bool strings_quoted, bool keys_quoted, std::string &s) const { ToString(strings_quoted, keys_quoted, s, false, 0, ""); } // This version additionally allow you to specify if you want indentation. void ToString(bool strings_quoted, bool keys_quoted, std::string &s, bool indented, int cur_indent, const char *indent_string) const { if (type_ == FBT_STRING) { String str(Indirect(), byte_width_); if (strings_quoted) { flatbuffers::EscapeString(str.c_str(), str.length(), &s, true, false); } else { s.append(str.c_str(), str.length()); } } else if (IsKey()) { auto str = AsKey(); if (keys_quoted) { flatbuffers::EscapeString(str, strlen(str), &s, true, false); } else { s += str; } } else if (IsInt()) { s += flatbuffers::NumToString(AsInt64()); } else if (IsUInt()) { s += flatbuffers::NumToString(AsUInt64()); } else if (IsFloat()) { s += flatbuffers::NumToString(AsDouble()); } else if (IsNull()) { s += "null"; } else if (IsBool()) { s += AsBool() ? "true" : "false"; } else if (IsMap()) { s += "{"; s += indented ? "\n" : " "; auto m = AsMap(); auto keys = m.Keys(); auto vals = m.Values(); for (size_t i = 0; i < keys.size(); i++) { bool kq = keys_quoted; if (!kq) { // FlexBuffers keys may contain arbitrary characters, only allow // unquoted if it looks like an "identifier": const char *p = keys[i].AsKey(); if (!flatbuffers::is_alpha(*p) && *p != '_') { kq = true; } else { while (*++p) { if (!flatbuffers::is_alnum(*p) && *p != '_') { kq = true; break; } } } } if (indented) IndentString(s, cur_indent + 1, indent_string); keys[i].ToString(true, kq, s); s += ": "; vals[i].ToString(true, keys_quoted, s, indented, cur_indent + 1, indent_string); if (i < keys.size() - 1) { s += ","; if (!indented) s += " "; } if (indented) s += "\n"; } if (!indented) s += " "; if (indented) IndentString(s, cur_indent, indent_string); s += "}"; } else if (IsVector()) { AppendToString(s, AsVector(), keys_quoted, indented, cur_indent + 1, indent_string); } else if (IsTypedVector()) { AppendToString(s, AsTypedVector(), keys_quoted, indented, cur_indent + 1, indent_string); } else if (IsFixedTypedVector()) { AppendToString(s, AsFixedTypedVector(), keys_quoted, indented, cur_indent + 1, indent_string); } else if (IsBlob()) { auto blob = AsBlob(); flatbuffers::EscapeString(reinterpret_cast(blob.data()), blob.size(), &s, true, false); } else { s += "(?)"; } } // This function returns the empty blob if you try to read a not-blob. // Strings can be viewed as blobs too. Blob AsBlob() const { if (type_ == FBT_BLOB || type_ == FBT_STRING) { return Blob(Indirect(), byte_width_); } else { return Blob::EmptyBlob(); } } // This function returns the empty vector if you try to read a not-vector. // Maps can be viewed as vectors too. Vector AsVector() const { if (type_ == FBT_VECTOR || type_ == FBT_MAP) { return Vector(Indirect(), byte_width_); } else { return Vector::EmptyVector(); } } TypedVector AsTypedVector() const { if (IsTypedVector()) { auto tv = TypedVector(Indirect(), byte_width_, ToTypedVectorElementType(type_)); if (tv.type_ == FBT_STRING) { // These can't be accessed as strings, since we don't know the bit-width // of the size field, see the declaration of // FBT_VECTOR_STRING_DEPRECATED above for details. // We change the type here to be keys, which are a subtype of strings, // and will ignore the size field. This will truncate strings with // embedded nulls. tv.type_ = FBT_KEY; } return tv; } else { return TypedVector::EmptyTypedVector(); } } FixedTypedVector AsFixedTypedVector() const { if (IsFixedTypedVector()) { uint8_t len = 0; auto vtype = ToFixedTypedVectorElementType(type_, &len); return FixedTypedVector(Indirect(), byte_width_, vtype, len); } else { return FixedTypedVector::EmptyFixedTypedVector(); } } Map AsMap() const { if (type_ == FBT_MAP) { return Map(Indirect(), byte_width_); } else { return Map::EmptyMap(); } } template T As() const; // Experimental: Mutation functions. // These allow scalars in an already created buffer to be updated in-place. // Since by default scalars are stored in the smallest possible space, // the new value may not fit, in which case these functions return false. // To avoid this, you can construct the values you intend to mutate using // Builder::ForceMinimumBitWidth. bool MutateInt(int64_t i) { if (type_ == FBT_INT) { return Mutate(data_, i, parent_width_, WidthI(i)); } else if (type_ == FBT_INDIRECT_INT) { return Mutate(Indirect(), i, byte_width_, WidthI(i)); } else if (type_ == FBT_UINT) { auto u = static_cast(i); return Mutate(data_, u, parent_width_, WidthU(u)); } else if (type_ == FBT_INDIRECT_UINT) { auto u = static_cast(i); return Mutate(Indirect(), u, byte_width_, WidthU(u)); } else { return false; } } bool MutateBool(bool b) { return type_ == FBT_BOOL && Mutate(data_, b, parent_width_, BIT_WIDTH_8); } bool MutateUInt(uint64_t u) { if (type_ == FBT_UINT) { return Mutate(data_, u, parent_width_, WidthU(u)); } else if (type_ == FBT_INDIRECT_UINT) { return Mutate(Indirect(), u, byte_width_, WidthU(u)); } else if (type_ == FBT_INT) { auto i = static_cast(u); return Mutate(data_, i, parent_width_, WidthI(i)); } else if (type_ == FBT_INDIRECT_INT) { auto i = static_cast(u); return Mutate(Indirect(), i, byte_width_, WidthI(i)); } else { return false; } } bool MutateFloat(float f) { if (type_ == FBT_FLOAT) { return MutateF(data_, f, parent_width_, BIT_WIDTH_32); } else if (type_ == FBT_INDIRECT_FLOAT) { return MutateF(Indirect(), f, byte_width_, BIT_WIDTH_32); } else { return false; } } bool MutateFloat(double d) { if (type_ == FBT_FLOAT) { return MutateF(data_, d, parent_width_, WidthF(d)); } else if (type_ == FBT_INDIRECT_FLOAT) { return MutateF(Indirect(), d, byte_width_, WidthF(d)); } else { return false; } } bool MutateString(const char *str, size_t len) { auto s = AsString(); if (s.IsTheEmptyString()) return false; // This is very strict, could allow shorter strings, but that creates // garbage. if (s.length() != len) return false; memcpy(const_cast(s.c_str()), str, len); return true; } bool MutateString(const char *str) { return MutateString(str, strlen(str)); } bool MutateString(const std::string &str) { return MutateString(str.data(), str.length()); } private: const uint8_t *Indirect() const { return flexbuffers::Indirect(data_, parent_width_); } template bool Mutate(const uint8_t *dest, T t, size_t byte_width, BitWidth value_width) { auto fits = static_cast(static_cast(1U) << value_width) <= byte_width; if (fits) { t = flatbuffers::EndianScalar(t); memcpy(const_cast(dest), &t, byte_width); } return fits; } template bool MutateF(const uint8_t *dest, T t, size_t byte_width, BitWidth value_width) { if (byte_width == sizeof(double)) return Mutate(dest, static_cast(t), byte_width, value_width); if (byte_width == sizeof(float)) return Mutate(dest, static_cast(t), byte_width, value_width); FLATBUFFERS_ASSERT(false); return false; } friend class Verifier; const uint8_t *data_; uint8_t parent_width_; uint8_t byte_width_; Type type_; }; // Template specialization for As(). template<> inline bool Reference::As() const { return AsBool(); } template<> inline int8_t Reference::As() const { return AsInt8(); } template<> inline int16_t Reference::As() const { return AsInt16(); } template<> inline int32_t Reference::As() const { return AsInt32(); } template<> inline int64_t Reference::As() const { return AsInt64(); } template<> inline uint8_t Reference::As() const { return AsUInt8(); } template<> inline uint16_t Reference::As() const { return AsUInt16(); } template<> inline uint32_t Reference::As() const { return AsUInt32(); } template<> inline uint64_t Reference::As() const { return AsUInt64(); } template<> inline double Reference::As() const { return AsDouble(); } template<> inline float Reference::As() const { return AsFloat(); } template<> inline String Reference::As() const { return AsString(); } template<> inline std::string Reference::As() const { return AsString().str(); } template<> inline Blob Reference::As() const { return AsBlob(); } template<> inline Vector Reference::As() const { return AsVector(); } template<> inline TypedVector Reference::As() const { return AsTypedVector(); } template<> inline FixedTypedVector Reference::As() const { return AsFixedTypedVector(); } template<> inline Map Reference::As() const { return AsMap(); } inline uint8_t PackedType(BitWidth bit_width, Type type) { return static_cast(bit_width | (type << 2)); } inline uint8_t NullPackedType() { return PackedType(BIT_WIDTH_8, FBT_NULL); } // Vector accessors. // Note: if you try to access outside of bounds, you get a Null value back // instead. Normally this would be an assert, but since this is "dynamically // typed" data, you may not want that (someone sends you a 2d vector and you // wanted 3d). // The Null converts seamlessly into a default value for any other type. // TODO(wvo): Could introduce an #ifdef that makes this into an assert? inline Reference Vector::operator[](size_t i) const { auto len = size(); if (i >= len) return Reference(nullptr, 1, NullPackedType()); auto packed_type = (data_ + len * byte_width_)[i]; auto elem = data_ + i * byte_width_; return Reference(elem, byte_width_, packed_type); } inline Reference TypedVector::operator[](size_t i) const { auto len = size(); if (i >= len) return Reference(nullptr, 1, NullPackedType()); auto elem = data_ + i * byte_width_; return Reference(elem, byte_width_, 1, type_); } inline Reference FixedTypedVector::operator[](size_t i) const { if (i >= len_) return Reference(nullptr, 1, NullPackedType()); auto elem = data_ + i * byte_width_; return Reference(elem, byte_width_, 1, type_); } template int KeyCompare(const void *key, const void *elem) { auto str_elem = reinterpret_cast( Indirect(reinterpret_cast(elem))); auto skey = reinterpret_cast(key); return strcmp(skey, str_elem); } inline Reference Map::operator[](const char *key) const { auto keys = Keys(); // We can't pass keys.byte_width_ to the comparison function, so we have // to pick the right one ahead of time. int (*comp)(const void *, const void *) = nullptr; switch (keys.byte_width_) { case 1: comp = KeyCompare; break; case 2: comp = KeyCompare; break; case 4: comp = KeyCompare; break; case 8: comp = KeyCompare; break; default: FLATBUFFERS_ASSERT(false); return Reference(); } auto res = std::bsearch(key, keys.data_, keys.size(), keys.byte_width_, comp); if (!res) return Reference(nullptr, 1, NullPackedType()); auto i = (reinterpret_cast(res) - keys.data_) / keys.byte_width_; return (*static_cast(this))[i]; } inline Reference Map::operator[](const std::string &key) const { return (*this)[key.c_str()]; } inline Reference GetRoot(const uint8_t *buffer, size_t size) { // See Finish() below for the serialization counterpart of this. // The root starts at the end of the buffer, so we parse backwards from there. auto end = buffer + size; auto byte_width = *--end; auto packed_type = *--end; end -= byte_width; // The root data item. return Reference(end, byte_width, packed_type); } inline Reference GetRoot(const std::vector &buffer) { return GetRoot(buffer.data(), buffer.size()); } // Flags that configure how the Builder behaves. // The "Share" flags determine if the Builder automatically tries to pool // this type. Pooling can reduce the size of serialized data if there are // multiple maps of the same kind, at the expense of slightly slower // serialization (the cost of lookups) and more memory use (std::set). // By default this is on for keys, but off for strings. // Turn keys off if you have e.g. only one map. // Turn strings on if you expect many non-unique string values. // Additionally, sharing key vectors can save space if you have maps with // identical field populations. enum BuilderFlag { BUILDER_FLAG_NONE = 0, BUILDER_FLAG_SHARE_KEYS = 1, BUILDER_FLAG_SHARE_STRINGS = 2, BUILDER_FLAG_SHARE_KEYS_AND_STRINGS = 3, BUILDER_FLAG_SHARE_KEY_VECTORS = 4, BUILDER_FLAG_SHARE_ALL = 7, }; class Builder FLATBUFFERS_FINAL_CLASS { public: Builder(size_t initial_size = 256, BuilderFlag flags = BUILDER_FLAG_SHARE_KEYS) : buf_(initial_size), finished_(false), has_duplicate_keys_(false), flags_(flags), force_min_bit_width_(BIT_WIDTH_8), key_pool(KeyOffsetCompare(buf_)), string_pool(StringOffsetCompare(buf_)) { buf_.clear(); } #ifdef FLATBUFFERS_DEFAULT_DECLARATION Builder(Builder &&) = default; Builder &operator=(Builder &&) = default; #endif /// @brief Get the serialized buffer (after you call `Finish()`). /// @return Returns a vector owned by this class. const std::vector &GetBuffer() const { Finished(); return buf_; } // Size of the buffer. Does not include unfinished values. size_t GetSize() const { return buf_.size(); } // Reset all state so we can re-use the buffer. void Clear() { buf_.clear(); stack_.clear(); finished_ = false; // flags_ remains as-is; force_min_bit_width_ = BIT_WIDTH_8; key_pool.clear(); string_pool.clear(); } // All value constructing functions below have two versions: one that // takes a key (for placement inside a map) and one that doesn't (for inside // vectors and elsewhere). void Null() { stack_.push_back(Value()); } void Null(const char *key) { Key(key); Null(); } void Int(int64_t i) { stack_.push_back(Value(i, FBT_INT, WidthI(i))); } void Int(const char *key, int64_t i) { Key(key); Int(i); } void UInt(uint64_t u) { stack_.push_back(Value(u, FBT_UINT, WidthU(u))); } void UInt(const char *key, uint64_t u) { Key(key); UInt(u); } void Float(float f) { stack_.push_back(Value(f)); } void Float(const char *key, float f) { Key(key); Float(f); } void Double(double f) { stack_.push_back(Value(f)); } void Double(const char *key, double d) { Key(key); Double(d); } void Bool(bool b) { stack_.push_back(Value(b)); } void Bool(const char *key, bool b) { Key(key); Bool(b); } void IndirectInt(int64_t i) { PushIndirect(i, FBT_INDIRECT_INT, WidthI(i)); } void IndirectInt(const char *key, int64_t i) { Key(key); IndirectInt(i); } void IndirectUInt(uint64_t u) { PushIndirect(u, FBT_INDIRECT_UINT, WidthU(u)); } void IndirectUInt(const char *key, uint64_t u) { Key(key); IndirectUInt(u); } void IndirectFloat(float f) { PushIndirect(f, FBT_INDIRECT_FLOAT, BIT_WIDTH_32); } void IndirectFloat(const char *key, float f) { Key(key); IndirectFloat(f); } void IndirectDouble(double f) { PushIndirect(f, FBT_INDIRECT_FLOAT, WidthF(f)); } void IndirectDouble(const char *key, double d) { Key(key); IndirectDouble(d); } size_t Key(const char *str, size_t len) { auto sloc = buf_.size(); WriteBytes(str, len + 1); if (flags_ & BUILDER_FLAG_SHARE_KEYS) { auto it = key_pool.find(sloc); if (it != key_pool.end()) { // Already in the buffer. Remove key we just serialized, and use // existing offset instead. buf_.resize(sloc); sloc = *it; } else { key_pool.insert(sloc); } } stack_.push_back(Value(static_cast(sloc), FBT_KEY, BIT_WIDTH_8)); return sloc; } size_t Key(const char *str) { return Key(str, strlen(str)); } size_t Key(const std::string &str) { return Key(str.c_str(), str.size()); } size_t String(const char *str, size_t len) { auto reset_to = buf_.size(); auto sloc = CreateBlob(str, len, 1, FBT_STRING); if (flags_ & BUILDER_FLAG_SHARE_STRINGS) { StringOffset so(sloc, len); auto it = string_pool.find(so); if (it != string_pool.end()) { // Already in the buffer. Remove string we just serialized, and use // existing offset instead. buf_.resize(reset_to); sloc = it->first; stack_.back().u_ = sloc; } else { string_pool.insert(so); } } return sloc; } size_t String(const char *str) { return String(str, strlen(str)); } size_t String(const std::string &str) { return String(str.c_str(), str.size()); } void String(const flexbuffers::String &str) { String(str.c_str(), str.length()); } void String(const char *key, const char *str) { Key(key); String(str); } void String(const char *key, const std::string &str) { Key(key); String(str); } void String(const char *key, const flexbuffers::String &str) { Key(key); String(str); } size_t Blob(const void *data, size_t len) { return CreateBlob(data, len, 0, FBT_BLOB); } size_t Blob(const std::vector &v) { return CreateBlob(v.data(), v.size(), 0, FBT_BLOB); } void Blob(const char *key, const void *data, size_t len) { Key(key); Blob(data, len); } void Blob(const char *key, const std::vector &v) { Key(key); Blob(v); } // TODO(wvo): support all the FlexBuffer types (like flexbuffers::String), // e.g. Vector etc. Also in overloaded versions. // Also some FlatBuffers types? size_t StartVector() { return stack_.size(); } size_t StartVector(const char *key) { Key(key); return stack_.size(); } size_t StartMap() { return stack_.size(); } size_t StartMap(const char *key) { Key(key); return stack_.size(); } // TODO(wvo): allow this to specify an alignment greater than the natural // alignment. size_t EndVector(size_t start, bool typed, bool fixed) { auto vec = CreateVector(start, stack_.size() - start, 1, typed, fixed); // Remove temp elements and return vector. stack_.resize(start); stack_.push_back(vec); return static_cast(vec.u_); } size_t EndMap(size_t start) { // We should have interleaved keys and values on the stack. auto len = MapElementCount(start); // Make sure keys are all strings: for (auto key = start; key < stack_.size(); key += 2) { FLATBUFFERS_ASSERT(stack_[key].type_ == FBT_KEY); } // Now sort values, so later we can do a binary search lookup. // We want to sort 2 array elements at a time. struct TwoValue { Value key; Value val; }; // TODO(wvo): strict aliasing? // TODO(wvo): allow the caller to indicate the data is already sorted // for maximum efficiency? With an assert to check sortedness to make sure // we're not breaking binary search. // Or, we can track if the map is sorted as keys are added which would be // be quite cheap (cheaper than checking it here), so we can skip this // step automatically when appliccable, and encourage people to write in // sorted fashion. // std::sort is typically already a lot faster on sorted data though. auto dict = reinterpret_cast(stack_.data() + start); std::sort( dict, dict + len, [&](const TwoValue &a, const TwoValue &b) -> bool { auto as = reinterpret_cast(buf_.data() + a.key.u_); auto bs = reinterpret_cast(buf_.data() + b.key.u_); auto comp = strcmp(as, bs); // We want to disallow duplicate keys, since this results in a // map where values cannot be found. // But we can't assert here (since we don't want to fail on // random JSON input) or have an error mechanism. // Instead, we set has_duplicate_keys_ in the builder to // signal this. // TODO: Have to check for pointer equality, as some sort // implementation apparently call this function with the same // element?? Why? if (!comp && &a != &b) has_duplicate_keys_ = true; return comp < 0; }); // First create a vector out of all keys. // TODO(wvo): if kBuilderFlagShareKeyVectors is true, see if we can share // the first vector. auto keys = CreateVector(start, len, 2, true, false); auto vec = CreateVector(start + 1, len, 2, false, false, &keys); // Remove temp elements and return map. stack_.resize(start); stack_.push_back(vec); return static_cast(vec.u_); } // Call this after EndMap to see if the map had any duplicate keys. // Any map with such keys won't be able to retrieve all values. bool HasDuplicateKeys() const { return has_duplicate_keys_; } template size_t Vector(F f) { auto start = StartVector(); f(); return EndVector(start, false, false); } template size_t Vector(F f, T &state) { auto start = StartVector(); f(state); return EndVector(start, false, false); } template size_t Vector(const char *key, F f) { auto start = StartVector(key); f(); return EndVector(start, false, false); } template size_t Vector(const char *key, F f, T &state) { auto start = StartVector(key); f(state); return EndVector(start, false, false); } template void Vector(const T *elems, size_t len) { if (flatbuffers::is_scalar::value) { // This path should be a lot quicker and use less space. ScalarVector(elems, len, false); } else { auto start = StartVector(); for (size_t i = 0; i < len; i++) Add(elems[i]); EndVector(start, false, false); } } template void Vector(const char *key, const T *elems, size_t len) { Key(key); Vector(elems, len); } template void Vector(const std::vector &vec) { Vector(vec.data(), vec.size()); } template size_t TypedVector(F f) { auto start = StartVector(); f(); return EndVector(start, true, false); } template size_t TypedVector(F f, T &state) { auto start = StartVector(); f(state); return EndVector(start, true, false); } template size_t TypedVector(const char *key, F f) { auto start = StartVector(key); f(); return EndVector(start, true, false); } template size_t TypedVector(const char *key, F f, T &state) { auto start = StartVector(key); f(state); return EndVector(start, true, false); } template size_t FixedTypedVector(const T *elems, size_t len) { // We only support a few fixed vector lengths. Anything bigger use a // regular typed vector. FLATBUFFERS_ASSERT(len >= 2 && len <= 4); // And only scalar values. static_assert(flatbuffers::is_scalar::value, "Unrelated types"); return ScalarVector(elems, len, true); } template size_t FixedTypedVector(const char *key, const T *elems, size_t len) { Key(key); return FixedTypedVector(elems, len); } template size_t Map(F f) { auto start = StartMap(); f(); return EndMap(start); } template size_t Map(F f, T &state) { auto start = StartMap(); f(state); return EndMap(start); } template size_t Map(const char *key, F f) { auto start = StartMap(key); f(); return EndMap(start); } template size_t Map(const char *key, F f, T &state) { auto start = StartMap(key); f(state); return EndMap(start); } template void Map(const std::map &map) { auto start = StartMap(); for (auto it = map.begin(); it != map.end(); ++it) Add(it->first.c_str(), it->second); EndMap(start); } size_t MapElementCount(size_t start) { // Make sure it is an even number: auto len = stack_.size() - start; FLATBUFFERS_ASSERT(!(len & 1)); len /= 2; return len; } // If you wish to share a value explicitly (a value not shared automatically // through one of the BUILDER_FLAG_SHARE_* flags) you can do so with these // functions. Or if you wish to turn those flags off for performance reasons // and still do some explicit sharing. For example: // builder.IndirectDouble(M_PI); // auto id = builder.LastValue(); // Remember where we stored it. // .. more code goes here .. // builder.ReuseValue(id); // Refers to same double by offset. // LastValue works regardless of whether the value has a key or not. // Works on any data type. struct Value; Value LastValue() { return stack_.back(); } void ReuseValue(Value v) { stack_.push_back(v); } void ReuseValue(const char *key, Value v) { Key(key); ReuseValue(v); } // Undo the last element serialized. Call once for a value and once for a // key. void Undo() { stack_.pop_back(); } // Overloaded Add that tries to call the correct function above. void Add(int8_t i) { Int(i); } void Add(int16_t i) { Int(i); } void Add(int32_t i) { Int(i); } void Add(int64_t i) { Int(i); } void Add(uint8_t u) { UInt(u); } void Add(uint16_t u) { UInt(u); } void Add(uint32_t u) { UInt(u); } void Add(uint64_t u) { UInt(u); } void Add(float f) { Float(f); } void Add(double d) { Double(d); } void Add(bool b) { Bool(b); } void Add(const char *str) { String(str); } void Add(const std::string &str) { String(str); } void Add(const flexbuffers::String &str) { String(str); } template void Add(const std::vector &vec) { Vector(vec); } template void Add(const char *key, const T &t) { Key(key); Add(t); } template void Add(const std::map &map) { Map(map); } template void operator+=(const T &t) { Add(t); } // This function is useful in combination with the Mutate* functions above. // It forces elements of vectors and maps to have a minimum size, such that // they can later be updated without failing. // Call with no arguments to reset. void ForceMinimumBitWidth(BitWidth bw = BIT_WIDTH_8) { force_min_bit_width_ = bw; } void Finish() { // If you hit this assert, you likely have objects that were never included // in a parent. You need to have exactly one root to finish a buffer. // Check your Start/End calls are matched, and all objects are inside // some other object. FLATBUFFERS_ASSERT(stack_.size() == 1); // Write root value. auto byte_width = Align(stack_[0].ElemWidth(buf_.size(), 0)); WriteAny(stack_[0], byte_width); // Write root type. Write(stack_[0].StoredPackedType(), 1); // Write root size. Normally determined by parent, but root has no parent :) Write(byte_width, 1); finished_ = true; } private: void Finished() const { // If you get this assert, you're attempting to get access a buffer // which hasn't been finished yet. Be sure to call // Builder::Finish with your root object. FLATBUFFERS_ASSERT(finished_); } // Align to prepare for writing a scalar with a certain size. uint8_t Align(BitWidth alignment) { auto byte_width = 1U << alignment; buf_.insert(buf_.end(), flatbuffers::PaddingBytes(buf_.size(), byte_width), 0); return static_cast(byte_width); } void WriteBytes(const void *val, size_t size) { buf_.insert(buf_.end(), reinterpret_cast(val), reinterpret_cast(val) + size); } template void Write(T val, size_t byte_width) { FLATBUFFERS_ASSERT(sizeof(T) >= byte_width); val = flatbuffers::EndianScalar(val); WriteBytes(&val, byte_width); } void WriteDouble(double f, uint8_t byte_width) { switch (byte_width) { case 8: Write(f, byte_width); break; case 4: Write(static_cast(f), byte_width); break; // case 2: Write(static_cast(f), byte_width); break; // case 1: Write(static_cast(f), byte_width); break; default: FLATBUFFERS_ASSERT(0); } } void WriteOffset(uint64_t o, uint8_t byte_width) { auto reloff = buf_.size() - o; FLATBUFFERS_ASSERT(byte_width == 8 || reloff < 1ULL << (byte_width * 8)); Write(reloff, byte_width); } template void PushIndirect(T val, Type type, BitWidth bit_width) { auto byte_width = Align(bit_width); auto iloc = buf_.size(); Write(val, byte_width); stack_.push_back(Value(static_cast(iloc), type, bit_width)); } static BitWidth WidthB(size_t byte_width) { switch (byte_width) { case 1: return BIT_WIDTH_8; case 2: return BIT_WIDTH_16; case 4: return BIT_WIDTH_32; case 8: return BIT_WIDTH_64; default: FLATBUFFERS_ASSERT(false); return BIT_WIDTH_64; } } template static Type GetScalarType() { static_assert(flatbuffers::is_scalar::value, "Unrelated types"); return flatbuffers::is_floating_point::value ? FBT_FLOAT : flatbuffers::is_same::value ? FBT_BOOL : (flatbuffers::is_unsigned::value ? FBT_UINT : FBT_INT); } public: // This was really intended to be private, except for LastValue/ReuseValue. struct Value { union { int64_t i_; uint64_t u_; double f_; }; Type type_; // For scalars: of itself, for vector: of its elements, for string: length. BitWidth min_bit_width_; Value() : i_(0), type_(FBT_NULL), min_bit_width_(BIT_WIDTH_8) {} Value(bool b) : u_(static_cast(b)), type_(FBT_BOOL), min_bit_width_(BIT_WIDTH_8) {} Value(int64_t i, Type t, BitWidth bw) : i_(i), type_(t), min_bit_width_(bw) {} Value(uint64_t u, Type t, BitWidth bw) : u_(u), type_(t), min_bit_width_(bw) {} Value(float f) : f_(static_cast(f)), type_(FBT_FLOAT), min_bit_width_(BIT_WIDTH_32) {} Value(double f) : f_(f), type_(FBT_FLOAT), min_bit_width_(WidthF(f)) {} uint8_t StoredPackedType(BitWidth parent_bit_width_ = BIT_WIDTH_8) const { return PackedType(StoredWidth(parent_bit_width_), type_); } BitWidth ElemWidth(size_t buf_size, size_t elem_index) const { if (IsInline(type_)) { return min_bit_width_; } else { // We have an absolute offset, but want to store a relative offset // elem_index elements beyond the current buffer end. Since whether // the relative offset fits in a certain byte_width depends on // the size of the elements before it (and their alignment), we have // to test for each size in turn. for (size_t byte_width = 1; byte_width <= sizeof(flatbuffers::largest_scalar_t); byte_width *= 2) { // Where are we going to write this offset? auto offset_loc = buf_size + flatbuffers::PaddingBytes(buf_size, byte_width) + elem_index * byte_width; // Compute relative offset. auto offset = offset_loc - u_; // Does it fit? auto bit_width = WidthU(offset); if (static_cast(static_cast(1U) << bit_width) == byte_width) return bit_width; } FLATBUFFERS_ASSERT(false); // Must match one of the sizes above. return BIT_WIDTH_64; } } BitWidth StoredWidth(BitWidth parent_bit_width_ = BIT_WIDTH_8) const { if (IsInline(type_)) { return (std::max)(min_bit_width_, parent_bit_width_); } else { return min_bit_width_; } } }; private: void WriteAny(const Value &val, uint8_t byte_width) { switch (val.type_) { case FBT_NULL: case FBT_INT: Write(val.i_, byte_width); break; case FBT_BOOL: case FBT_UINT: Write(val.u_, byte_width); break; case FBT_FLOAT: WriteDouble(val.f_, byte_width); break; default: WriteOffset(val.u_, byte_width); break; } } size_t CreateBlob(const void *data, size_t len, size_t trailing, Type type) { auto bit_width = WidthU(len); auto byte_width = Align(bit_width); Write(len, byte_width); auto sloc = buf_.size(); WriteBytes(data, len + trailing); stack_.push_back(Value(static_cast(sloc), type, bit_width)); return sloc; } template size_t ScalarVector(const T *elems, size_t len, bool fixed) { auto vector_type = GetScalarType(); auto byte_width = sizeof(T); auto bit_width = WidthB(byte_width); // If you get this assert, you're trying to write a vector with a size // field that is bigger than the scalars you're trying to write (e.g. a // byte vector > 255 elements). For such types, write a "blob" instead. // TODO: instead of asserting, could write vector with larger elements // instead, though that would be wasteful. FLATBUFFERS_ASSERT(WidthU(len) <= bit_width); Align(bit_width); if (!fixed) Write(len, byte_width); auto vloc = buf_.size(); for (size_t i = 0; i < len; i++) Write(elems[i], byte_width); stack_.push_back(Value(static_cast(vloc), ToTypedVector(vector_type, fixed ? len : 0), bit_width)); return vloc; } Value CreateVector(size_t start, size_t vec_len, size_t step, bool typed, bool fixed, const Value *keys = nullptr) { FLATBUFFERS_ASSERT( !fixed || typed); // typed=false, fixed=true combination is not supported. // Figure out smallest bit width we can store this vector with. auto bit_width = (std::max)(force_min_bit_width_, WidthU(vec_len)); auto prefix_elems = 1; if (keys) { // If this vector is part of a map, we will pre-fix an offset to the keys // to this vector. bit_width = (std::max)(bit_width, keys->ElemWidth(buf_.size(), 0)); prefix_elems += 2; } Type vector_type = FBT_KEY; // Check bit widths and types for all elements. for (size_t i = start; i < stack_.size(); i += step) { auto elem_width = stack_[i].ElemWidth(buf_.size(), i - start + prefix_elems); bit_width = (std::max)(bit_width, elem_width); if (typed) { if (i == start) { vector_type = stack_[i].type_; } else { // If you get this assert, you are writing a typed vector with // elements that are not all the same type. FLATBUFFERS_ASSERT(vector_type == stack_[i].type_); } } } // If you get this assert, your typed types are not one of: // Int / UInt / Float / Key. FLATBUFFERS_ASSERT(!typed || IsTypedVectorElementType(vector_type)); auto byte_width = Align(bit_width); // Write vector. First the keys width/offset if available, and size. if (keys) { WriteOffset(keys->u_, byte_width); Write(1ULL << keys->min_bit_width_, byte_width); } if (!fixed) Write(vec_len, byte_width); // Then the actual data. auto vloc = buf_.size(); for (size_t i = start; i < stack_.size(); i += step) { WriteAny(stack_[i], byte_width); } // Then the types. if (!typed) { for (size_t i = start; i < stack_.size(); i += step) { buf_.push_back(stack_[i].StoredPackedType(bit_width)); } } return Value(static_cast(vloc), keys ? FBT_MAP : (typed ? ToTypedVector(vector_type, fixed ? vec_len : 0) : FBT_VECTOR), bit_width); } // You shouldn't really be copying instances of this class. Builder(const Builder &); Builder &operator=(const Builder &); std::vector buf_; std::vector stack_; bool finished_; bool has_duplicate_keys_; BuilderFlag flags_; BitWidth force_min_bit_width_; struct KeyOffsetCompare { explicit KeyOffsetCompare(const std::vector &buf) : buf_(&buf) {} bool operator()(size_t a, size_t b) const { auto stra = reinterpret_cast(buf_->data() + a); auto strb = reinterpret_cast(buf_->data() + b); return strcmp(stra, strb) < 0; } const std::vector *buf_; }; typedef std::pair StringOffset; struct StringOffsetCompare { explicit StringOffsetCompare(const std::vector &buf) : buf_(&buf) {} bool operator()(const StringOffset &a, const StringOffset &b) const { auto stra = buf_->data() + a.first; auto strb = buf_->data() + b.first; auto cr = memcmp(stra, strb, (std::min)(a.second, b.second) + 1); return cr < 0 || (cr == 0 && a.second < b.second); } const std::vector *buf_; }; typedef std::set KeyOffsetMap; typedef std::set StringOffsetMap; KeyOffsetMap key_pool; StringOffsetMap string_pool; friend class Verifier; }; // Helper class to verify the integrity of a FlexBuffer class Verifier FLATBUFFERS_FINAL_CLASS { public: Verifier(const uint8_t *buf, size_t buf_len, // Supplying this vector likely results in faster verification // of larger buffers with many shared keys/strings, but // comes at the cost of using additional memory the same size of // the buffer being verified, so it is by default off. std::vector *reuse_tracker = nullptr, bool _check_alignment = true, size_t max_depth = 64) : buf_(buf), size_(buf_len), depth_(0), max_depth_(max_depth), num_vectors_(0), max_vectors_(buf_len), check_alignment_(_check_alignment), reuse_tracker_(reuse_tracker) { FLATBUFFERS_ASSERT(static_cast(size_) < FLATBUFFERS_MAX_BUFFER_SIZE); if (reuse_tracker_) { reuse_tracker_->clear(); reuse_tracker_->resize(size_, PackedType(BIT_WIDTH_8, FBT_NULL)); } } private: // Central location where any verification failures register. bool Check(bool ok) const { // clang-format off #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE FLATBUFFERS_ASSERT(ok); #endif // clang-format on return ok; } // Verify any range within the buffer. bool VerifyFrom(size_t elem, size_t elem_len) const { return Check(elem_len < size_ && elem <= size_ - elem_len); } bool VerifyBefore(size_t elem, size_t elem_len) const { return Check(elem_len <= elem); } bool VerifyFromPointer(const uint8_t *p, size_t len) { auto o = static_cast(p - buf_); return VerifyFrom(o, len); } bool VerifyBeforePointer(const uint8_t *p, size_t len) { auto o = static_cast(p - buf_); return VerifyBefore(o, len); } bool VerifyByteWidth(size_t width) { return Check(width == 1 || width == 2 || width == 4 || width == 8); } bool VerifyType(int type) { return Check(type >= 0 && type < FBT_MAX_TYPE); } bool VerifyOffset(uint64_t off, const uint8_t *p) { return Check(off <= static_cast(size_)) && off <= static_cast(p - buf_); } bool VerifyAlignment(const uint8_t *p, size_t size) const { auto o = static_cast(p - buf_); return Check((o & (size - 1)) == 0 || !check_alignment_); } // Macro, since we want to escape from parent function & use lazy args. #define FLEX_CHECK_VERIFIED(P, PACKED_TYPE) \ if (reuse_tracker_) { \ auto packed_type = PACKED_TYPE; \ auto existing = (*reuse_tracker_)[P - buf_]; \ if (existing == packed_type) return true; \ /* Fail verification if already set with different type! */ \ if (!Check(existing == 0)) return false; \ (*reuse_tracker_)[P - buf_] = packed_type; \ } bool VerifyVector(Reference r, const uint8_t *p, Type elem_type) { // Any kind of nesting goes thru this function, so guard against that // here, both with simple nesting checks, and the reuse tracker if on. depth_++; num_vectors_++; if (!Check(depth_ <= max_depth_ && num_vectors_ <= max_vectors_)) return false; auto size_byte_width = r.byte_width_; if (!VerifyBeforePointer(p, size_byte_width)) return false; FLEX_CHECK_VERIFIED(p - size_byte_width, PackedType(Builder::WidthB(size_byte_width), r.type_)); auto sized = Sized(p, size_byte_width); auto num_elems = sized.size(); auto elem_byte_width = r.type_ == FBT_STRING || r.type_ == FBT_BLOB ? uint8_t(1) : r.byte_width_; auto max_elems = SIZE_MAX / elem_byte_width; if (!Check(num_elems < max_elems)) return false; // Protect against byte_size overflowing. auto byte_size = num_elems * elem_byte_width; if (!VerifyFromPointer(p, byte_size)) return false; if (elem_type == FBT_NULL) { // Verify type bytes after the vector. if (!VerifyFromPointer(p + byte_size, num_elems)) return false; auto v = Vector(p, size_byte_width); for (size_t i = 0; i < num_elems; i++) if (!VerifyRef(v[i])) return false; } else if (elem_type == FBT_KEY) { auto v = TypedVector(p, elem_byte_width, FBT_KEY); for (size_t i = 0; i < num_elems; i++) if (!VerifyRef(v[i])) return false; } else { FLATBUFFERS_ASSERT(IsInline(elem_type)); } depth_--; return true; } bool VerifyKeys(const uint8_t *p, uint8_t byte_width) { // The vector part of the map has already been verified. const size_t num_prefixed_fields = 3; if (!VerifyBeforePointer(p, byte_width * num_prefixed_fields)) return false; p -= byte_width * num_prefixed_fields; auto off = ReadUInt64(p, byte_width); if (!VerifyOffset(off, p)) return false; auto key_byte_with = static_cast(ReadUInt64(p + byte_width, byte_width)); if (!VerifyByteWidth(key_byte_with)) return false; return VerifyVector(Reference(p, byte_width, key_byte_with, FBT_VECTOR_KEY), p - off, FBT_KEY); } bool VerifyKey(const uint8_t *p) { FLEX_CHECK_VERIFIED(p, PackedType(BIT_WIDTH_8, FBT_KEY)); while (p < buf_ + size_) if (*p++) return true; return false; } #undef FLEX_CHECK_VERIFIED bool VerifyTerminator(const String &s) { return VerifyFromPointer(reinterpret_cast(s.c_str()), s.size() + 1); } bool VerifyRef(Reference r) { // r.parent_width_ and r.data_ already verified. if (!VerifyByteWidth(r.byte_width_) || !VerifyType(r.type_)) { return false; } if (IsInline(r.type_)) { // Inline scalars, don't require further verification. return true; } // All remaining types are an offset. auto off = ReadUInt64(r.data_, r.parent_width_); if (!VerifyOffset(off, r.data_)) return false; auto p = r.Indirect(); if (!VerifyAlignment(p, r.byte_width_)) return false; switch (r.type_) { case FBT_INDIRECT_INT: case FBT_INDIRECT_UINT: case FBT_INDIRECT_FLOAT: return VerifyFromPointer(p, r.byte_width_); case FBT_KEY: return VerifyKey(p); case FBT_MAP: return VerifyVector(r, p, FBT_NULL) && VerifyKeys(p, r.byte_width_); case FBT_VECTOR: return VerifyVector(r, p, FBT_NULL); case FBT_VECTOR_INT: return VerifyVector(r, p, FBT_INT); case FBT_VECTOR_BOOL: case FBT_VECTOR_UINT: return VerifyVector(r, p, FBT_UINT); case FBT_VECTOR_FLOAT: return VerifyVector(r, p, FBT_FLOAT); case FBT_VECTOR_KEY: return VerifyVector(r, p, FBT_KEY); case FBT_VECTOR_STRING_DEPRECATED: // Use of FBT_KEY here intentional, see elsewhere. return VerifyVector(r, p, FBT_KEY); case FBT_BLOB: return VerifyVector(r, p, FBT_UINT); case FBT_STRING: return VerifyVector(r, p, FBT_UINT) && VerifyTerminator(String(p, r.byte_width_)); case FBT_VECTOR_INT2: case FBT_VECTOR_UINT2: case FBT_VECTOR_FLOAT2: case FBT_VECTOR_INT3: case FBT_VECTOR_UINT3: case FBT_VECTOR_FLOAT3: case FBT_VECTOR_INT4: case FBT_VECTOR_UINT4: case FBT_VECTOR_FLOAT4: { uint8_t len = 0; auto vtype = ToFixedTypedVectorElementType(r.type_, &len); if (!VerifyType(vtype)) return false; return VerifyFromPointer(p, static_cast(r.byte_width_) * len); } default: return false; } } public: bool VerifyBuffer() { if (!Check(size_ >= 3)) return false; auto end = buf_ + size_; auto byte_width = *--end; auto packed_type = *--end; return VerifyByteWidth(byte_width) && Check(end - buf_ >= byte_width) && VerifyRef(Reference(end - byte_width, byte_width, packed_type)); } private: const uint8_t *buf_; size_t size_; size_t depth_; const size_t max_depth_; size_t num_vectors_; const size_t max_vectors_; bool check_alignment_; std::vector *reuse_tracker_; }; // Utility function that constructs the Verifier for you, see above for // parameters. inline bool VerifyBuffer(const uint8_t *buf, size_t buf_len, std::vector *reuse_tracker = nullptr) { Verifier verifier(buf, buf_len, reuse_tracker); return verifier.VerifyBuffer(); } } // namespace flexbuffers #if defined(_MSC_VER) # pragma warning(pop) #endif #endif // FLATBUFFERS_FLEXBUFFERS_H_