/* * 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_ // We use the basic binary writing functions from the regular FlatBuffers. #include "flatbuffers/flatbuffers.h" #include "flatbuffers/util.h" #ifdef _MSC_VER #include #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 { TYPE_NULL = 0, TYPE_INT = 1, TYPE_UINT = 2, TYPE_FLOAT = 3, // Types above stored inline, types below store an offset. TYPE_KEY = 4, TYPE_STRING = 5, TYPE_INDIRECT_INT = 6, TYPE_INDIRECT_UINT = 7, TYPE_INDIRECT_FLOAT = 8, TYPE_MAP = 9, TYPE_VECTOR = 10, // Untyped. TYPE_VECTOR_INT = 11, // Typed any size (stores no type table). TYPE_VECTOR_UINT = 12, TYPE_VECTOR_FLOAT = 13, TYPE_VECTOR_KEY = 14, TYPE_VECTOR_STRING = 15, TYPE_VECTOR_INT2 = 16, // Typed tuple (no type table, no size field). TYPE_VECTOR_UINT2 = 17, TYPE_VECTOR_FLOAT2 = 18, TYPE_VECTOR_INT3 = 19, // Typed triple (no type table, no size field). TYPE_VECTOR_UINT3 = 20, TYPE_VECTOR_FLOAT3 = 21, TYPE_VECTOR_INT4 = 22, // Typed quad (no type table, no size field). TYPE_VECTOR_UINT4 = 23, TYPE_VECTOR_FLOAT4 = 24, TYPE_BLOB = 25, }; inline bool IsInline(Type t) { return t <= TYPE_FLOAT; } inline bool IsTypedVectorElementType(Type t) { return t >= TYPE_INT && t <= TYPE_STRING; } inline bool IsTypedVector(Type t) { return t >= TYPE_VECTOR_INT && t <= TYPE_VECTOR_STRING; } inline bool IsFixedTypedVector(Type t) { return t >= TYPE_VECTOR_INT2 && t <= TYPE_VECTOR_FLOAT4; } inline Type ToTypedVector(Type t, int fixed_len = 0) { assert(IsTypedVectorElementType(t)); switch (fixed_len) { case 0: return static_cast(t - TYPE_INT + TYPE_VECTOR_INT); case 2: return static_cast(t - TYPE_INT + TYPE_VECTOR_INT2); case 3: return static_cast(t - TYPE_INT + TYPE_VECTOR_INT3); case 4: return static_cast(t - TYPE_INT + TYPE_VECTOR_INT4); default: assert(0); return TYPE_NULL; } } inline Type ToTypedVectorElementType(Type t) { assert(IsTypedVector(t)); return static_cast(t - TYPE_VECTOR_INT + TYPE_INT); } inline Type ToFixedTypedVectorElementType(Type t, uint8_t *len) { assert(IsFixedTypedVector(t)); auto fixed_type = t - TYPE_VECTOR_INT2; *len = fixed_type / 3 + 2; // 3 types each, starting from length 2. return static_cast(fixed_type % 3 + TYPE_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. #ifdef _MSC_VER uint64_t u = 0; __movsb(reinterpret_cast(&u), reinterpret_cast(data), byte_width); return flatbuffers::EndianScalar(u); #else return ReadSizedScalar( data, byte_width); #endif } inline double ReadDouble(const uint8_t *data, uint8_t byte_width) { return ReadSizedScalar(data, byte_width); } 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); } static 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; } static BitWidth WidthI(int64_t i) { auto u = static_cast(i) << 1; return WidthU(i >= 0 ? u : ~u); } static 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_; }; // Stores size in `byte_width_` bytes before data_ pointer. class Sized : public Object { public: Sized(const uint8_t *data, uint8_t byte_width) : Object(data, byte_width) {} size_t size() const { return static_cast(ReadUInt64(data_ - byte_width_, byte_width_)); } }; class String : public Sized { public: String(const uint8_t *data, uint8_t byte_width) : Sized(data, byte_width) {} size_t length() const { return size(); } const char *c_str() const { return reinterpret_cast(data_); } static String EmptyString() { static const uint8_t empty_string[] = { 0/*len*/, 0/*terminator*/ }; return String(empty_string + 1, 1); } bool IsTheEmptyString() const { return data_ == EmptyString().data_; } }; class Blob : public Sized { public: Blob(const uint8_t *data, uint8_t byte_width) : Sized(data, 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_; } }; 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, TYPE_INT); } bool IsTheEmptyVector() const { return data_ == TypedVector::EmptyTypedVector().data_; } Type ElementType() { return type_; } 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, TYPE_INT, 0); } bool IsTheEmptyFixedTypedVector() const { return data_ == FixedTypedVector::EmptyFixedTypedVector().data_; } Type ElementType() { return type_; } uint8_t size() { 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_)), TYPE_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_; } }; class Reference { public: 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_ = 1U << static_cast(packed_type & 3); type_ = static_cast(packed_type >> 2); } Type GetType() const { return type_; } bool IsNull() const { return type_ == TYPE_NULL; } bool IsInt() const { return type_ == TYPE_INT || type_ == TYPE_INDIRECT_INT; } bool IsUInt() const { return type_ == TYPE_UINT|| type_ == TYPE_INDIRECT_UINT;; } bool IsIntOrUint() const { return IsInt() || IsUInt(); } bool IsFloat() const { return type_ == TYPE_FLOAT || type_ == TYPE_INDIRECT_FLOAT; } bool IsNumeric() const { return IsIntOrUint() || IsFloat(); } bool IsString() const { return type_ == TYPE_STRING; } bool IsKey() const { return type_ == TYPE_KEY; } bool IsVector() const { return type_ == TYPE_VECTOR || type_ == TYPE_MAP; } bool IsMap() const { return type_ == TYPE_MAP; } // 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_ == TYPE_INT) { // A fast path for the common case. return ReadInt64(data_, parent_width_); } else switch (type_) { case TYPE_INDIRECT_INT: return ReadInt64(Indirect(), byte_width_); case TYPE_UINT: return ReadUInt64(data_, parent_width_); case TYPE_INDIRECT_UINT: return ReadUInt64(Indirect(), byte_width_); case TYPE_FLOAT: return static_cast( ReadDouble(data_, parent_width_)); case TYPE_INDIRECT_FLOAT: return static_cast( ReadDouble(Indirect(), byte_width_)); case TYPE_NULL: return 0; case TYPE_STRING: return flatbuffers::StringToInt(AsString().c_str()); case TYPE_VECTOR: return static_cast(AsVector().size()); 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_ == TYPE_UINT) { // A fast path for the common case. return ReadUInt64(data_, parent_width_); } else switch (type_) { case TYPE_INDIRECT_UINT: return ReadUInt64(Indirect(), byte_width_); case TYPE_INT: return ReadInt64(data_, parent_width_); case TYPE_INDIRECT_INT: return ReadInt64(Indirect(), byte_width_); case TYPE_FLOAT: return static_cast( ReadDouble(data_, parent_width_)); case TYPE_INDIRECT_FLOAT: return static_cast( ReadDouble(Indirect(), byte_width_)); case TYPE_NULL: return 0; case TYPE_STRING: return flatbuffers::StringToUInt(AsString().c_str()); case TYPE_VECTOR: return static_cast(AsVector().size()); 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_ == TYPE_FLOAT) { // A fast path for the common case. return ReadDouble(data_, parent_width_); } else switch (type_) { case TYPE_INDIRECT_FLOAT: return ReadDouble(Indirect(), byte_width_); case TYPE_INT: return static_cast( ReadInt64(data_, parent_width_)); case TYPE_UINT: return static_cast( ReadUInt64(data_, parent_width_)); case TYPE_INDIRECT_INT: return static_cast( ReadInt64(Indirect(), byte_width_)); case TYPE_INDIRECT_UINT: return static_cast( ReadUInt64(Indirect(), byte_width_)); case TYPE_NULL: return 0.0; case TYPE_STRING: return strtod(AsString().c_str(), nullptr); case TYPE_VECTOR: return static_cast(AsVector().size()); default: // Convert strings and other things to float. return 0; } } float AsFloat() const { return static_cast(AsDouble()); } const char *AsKey() const { if (type_ == TYPE_KEY) { return reinterpret_cast(Indirect()); } else { return ""; } } // This function returns the empty string if you try to read a not-string. String AsString() const { if (type_ == TYPE_STRING) { return String(Indirect(), byte_width_); } else { return String::EmptyString(); } } // Unlike AsString(), this will convert any type to a std::string. std::string ToString() const { if (type_ == TYPE_STRING) { return String(Indirect(), byte_width_).c_str(); } else if (IsKey()) { return AsKey(); } else if (IsInt()) { return flatbuffers::NumToString(AsInt64()); } else if (IsUInt()) { return flatbuffers::NumToString(AsUInt64()); } else if (IsFloat()) { return flatbuffers::NumToString(AsDouble()); } else if (IsNull()) { return "null"; } else if (IsMap()) { return "{..}"; // TODO: show elements. } else if (IsVector()) { return "[..]"; // TODO: show elements. } else { return "(?)"; } } // 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_ == TYPE_BLOB || type_ == TYPE_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_ == TYPE_VECTOR || type_ == TYPE_MAP) { return Vector(Indirect(), byte_width_); } else { return Vector::EmptyVector(); } } TypedVector AsTypedVector() const { if (IsTypedVector(type_)) { return TypedVector(Indirect(), byte_width_, ToTypedVectorElementType(type_)); } else { return TypedVector::EmptyTypedVector(); } } FixedTypedVector AsFixedTypedVector() const { if (IsFixedTypedVector(type_)) { 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_ == TYPE_MAP) { return Map(Indirect(), byte_width_); } else { return Map::EmptyMap(); } } // 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_ == TYPE_INT) { return Mutate(data_, i, parent_width_, WidthI(i)); } else if (type_ == TYPE_INDIRECT_INT) { return Mutate(Indirect(), i, byte_width_, WidthI(i)); } else if (type_ == TYPE_UINT) { auto u = static_cast(i); return Mutate(data_, u, parent_width_, WidthU(u)); } else if (type_ == TYPE_INDIRECT_UINT) { auto u = static_cast(i); return Mutate(Indirect(), u, byte_width_, WidthU(u)); } else { return false; } } bool MutateUInt(uint64_t u) { if (type_ == TYPE_UINT) { return Mutate(data_, u, parent_width_, WidthU(u)); } else if (type_ == TYPE_INDIRECT_UINT) { return Mutate(Indirect(), u, byte_width_, WidthU(u)); } else if (type_ == TYPE_INT) { auto i = static_cast(u); return Mutate(data_, i, parent_width_, WidthI(i)); } else if (type_ == TYPE_INDIRECT_INT) { auto i = static_cast(u); return Mutate(Indirect(), i, byte_width_, WidthI(i)); } else { return false; } } bool MutateFloat(float f) { if (type_ == TYPE_FLOAT) { return MutateF(data_, f, parent_width_, BIT_WIDTH_32); } else if (type_ == TYPE_INDIRECT_FLOAT) { return MutateF(Indirect(), f, byte_width_, BIT_WIDTH_32); } else { return false; } } bool MutateFloat(double d) { if (type_ == TYPE_FLOAT) { return MutateF(data_, d, parent_width_, WidthF(d)); } else if (type_ == TYPE_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 = (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); assert(false); return false; } const uint8_t *data_; uint8_t parent_width_; uint8_t byte_width_; Type type_; }; 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, TYPE_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; } 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), flags_(flags), force_min_bit_width_(BIT_WIDTH_8), key_pool(KeyOffsetCompare(buf_)), string_pool(StringOffsetCompare(buf_)) { buf_.clear(); } /// @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_; } // 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, TYPE_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, TYPE_UINT, WidthU(u))); } void UInt(const char *key, uint64_t u) { Key(key); Int(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) { Int(static_cast(b)); } void Bool(const char *key, bool b) { Key(key); Bool(b); } void IndirectInt(int64_t i) { PushIndirect(i, TYPE_INDIRECT_INT, WidthI(i)); } void IndirectInt(const char *key, int64_t i) { Key(key); IndirectInt(i); } void IndirectUInt(uint64_t u) { PushIndirect(u, TYPE_INDIRECT_UINT, WidthU(u)); } void IndirectUInt(const char *key, uint64_t u) { Key(key); IndirectUInt(u); } void IndirectFloat(float f) { PushIndirect(f, TYPE_INDIRECT_FLOAT, BIT_WIDTH_32); } void IndirectFloat(const char *key, float f) { Key(key); IndirectFloat(f); } void IndirectDouble(double f) { PushIndirect(f, TYPE_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), TYPE_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, TYPE_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, TYPE_BLOB); } size_t Blob(const std::vector &v) { return CreateBlob(v.data(), v.size(), 0, TYPE_BLOB); } // 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 aligment 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. // Make sure it is an even number: auto len = stack_.size() - start; assert(!(len & 1)); len /= 2; // Make sure keys are all strings: for (auto key = start; key < stack_.size(); key += 2) { assert(stack_[key].type_ == TYPE_KEY); } // Now sort values, so later we can do a binary seach 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); // If this assertion hits, you've added two keys with the same value to // this map. // TODO: Have to check for pointer equality, as some sort implementation // apparently call this function with the same element?? Why? assert(comp || &a == &b); 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_); } template size_t Vector(F f) { auto start = StartVector(); f(); 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 void Vector(const T *elems, size_t len) { if (std::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(const char *key, F f) { auto start = StartVector(key); f(); 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. assert(len >= 2 && len <= 4); // And only scalar values. assert(std::is_scalar::value); 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(const char *key, F f) { auto start = StartMap(key); f(); 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); } // 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. 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. 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 byte_width; } void WriteBytes(const void *val, size_t size) { buf_.insert(buf_.end(), reinterpret_cast(val), reinterpret_cast(val) + size); } // For values T >= byte_width template void Write(T val, uint8_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: assert(0); } } void WriteOffset(uint64_t o, uint8_t byte_width) { auto reloff = buf_.size() - o; assert(reloff < 1ULL << (byte_width * 8) || 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: assert(false); return BIT_WIDTH_64; } } template static Type GetScalarType() { assert(std::is_scalar::value); return std::is_floating_point::value ? TYPE_FLOAT : (std::is_unsigned::value ? TYPE_UINT : TYPE_INT); } 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_(TYPE_NULL), 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_(f), type_(TYPE_FLOAT), min_bit_width_(BIT_WIDTH_32) {} Value(double f) : f_(f), type_(TYPE_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 (1U << bit_width == byte_width) return bit_width; } 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_; } } }; void WriteAny(const Value &val, uint8_t byte_width) { switch (val.type_) { case TYPE_NULL: case TYPE_INT: Write(val.i_, byte_width); break; case TYPE_UINT: Write(val.u_, byte_width); break; case TYPE_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. assert(WidthU(len) <= 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) { // 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 = TYPE_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 + 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. assert(vector_type == stack_[i].type_); } } } // If you get this assert, your fixed types are not one of: // Int / UInt / Float / Key. assert(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(1U << 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 ? TYPE_MAP : (typed ? ToTypedVector(vector_type, fixed ? vec_len : 0) : TYPE_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_; BuilderFlag flags_; BitWidth force_min_bit_width_; struct KeyOffsetCompare { 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 { StringOffsetCompare(const std::vector &buf) : buf_(&buf) {} bool operator() (const StringOffset &a, const StringOffset &b) const { auto stra = reinterpret_cast(buf_->data() + a.first); auto strb = reinterpret_cast(buf_->data() + b.first); return strncmp(stra, strb, std::min(a.second, b.second) + 1) < 0; } const std::vector *buf_; }; typedef std::set KeyOffsetMap; typedef std::set StringOffsetMap; KeyOffsetMap key_pool; StringOffsetMap string_pool; }; } // namespace flexbuffers #endif // FLATBUFFERS_FLEXBUFFERS_H_