Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2017 Google Inc. All rights reserved. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef FLATBUFFERS_FLEXBUFFERS_H_ |
| 18 | #define FLATBUFFERS_FLEXBUFFERS_H_ |
| 19 | |
| 20 | #include <map> |
| 21 | // Used to select STL variant. |
| 22 | #include "flatbuffers/base.h" |
| 23 | // We use the basic binary writing functions from the regular FlatBuffers. |
| 24 | #include "flatbuffers/util.h" |
| 25 | |
| 26 | #ifdef _MSC_VER |
| 27 | # include <intrin.h> |
| 28 | #endif |
| 29 | |
| 30 | #if defined(_MSC_VER) |
| 31 | # pragma warning(push) |
| 32 | # pragma warning(disable : 4127) // C4127: conditional expression is constant |
| 33 | #endif |
| 34 | |
| 35 | namespace flexbuffers { |
| 36 | |
| 37 | class Reference; |
| 38 | class Map; |
| 39 | |
| 40 | // These are used in the lower 2 bits of a type field to determine the size of |
| 41 | // the elements (and or size field) of the item pointed to (e.g. vector). |
| 42 | enum BitWidth { |
| 43 | BIT_WIDTH_8 = 0, |
| 44 | BIT_WIDTH_16 = 1, |
| 45 | BIT_WIDTH_32 = 2, |
| 46 | BIT_WIDTH_64 = 3, |
| 47 | }; |
| 48 | |
| 49 | // These are used as the upper 6 bits of a type field to indicate the actual |
| 50 | // type. |
| 51 | enum Type { |
| 52 | FBT_NULL = 0, |
| 53 | FBT_INT = 1, |
| 54 | FBT_UINT = 2, |
| 55 | FBT_FLOAT = 3, |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 56 | // Types above stored inline, types below (except FBT_BOOL) store an offset. |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 57 | FBT_KEY = 4, |
| 58 | FBT_STRING = 5, |
| 59 | FBT_INDIRECT_INT = 6, |
| 60 | FBT_INDIRECT_UINT = 7, |
| 61 | FBT_INDIRECT_FLOAT = 8, |
| 62 | FBT_MAP = 9, |
| 63 | FBT_VECTOR = 10, // Untyped. |
| 64 | FBT_VECTOR_INT = 11, // Typed any size (stores no type table). |
| 65 | FBT_VECTOR_UINT = 12, |
| 66 | FBT_VECTOR_FLOAT = 13, |
| 67 | FBT_VECTOR_KEY = 14, |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 68 | // DEPRECATED, use FBT_VECTOR or FBT_VECTOR_KEY instead. |
| 69 | // Read test.cpp/FlexBuffersDeprecatedTest() for details on why. |
| 70 | FBT_VECTOR_STRING_DEPRECATED = 15, |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 71 | FBT_VECTOR_INT2 = 16, // Typed tuple (no type table, no size field). |
| 72 | FBT_VECTOR_UINT2 = 17, |
| 73 | FBT_VECTOR_FLOAT2 = 18, |
| 74 | FBT_VECTOR_INT3 = 19, // Typed triple (no type table, no size field). |
| 75 | FBT_VECTOR_UINT3 = 20, |
| 76 | FBT_VECTOR_FLOAT3 = 21, |
| 77 | FBT_VECTOR_INT4 = 22, // Typed quad (no type table, no size field). |
| 78 | FBT_VECTOR_UINT4 = 23, |
| 79 | FBT_VECTOR_FLOAT4 = 24, |
| 80 | FBT_BLOB = 25, |
| 81 | FBT_BOOL = 26, |
| 82 | FBT_VECTOR_BOOL = |
| 83 | 36, // To Allow the same type of conversion of type to vector type |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 84 | |
| 85 | FBT_MAX_TYPE = 37 |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 86 | }; |
| 87 | |
| 88 | inline bool IsInline(Type t) { return t <= FBT_FLOAT || t == FBT_BOOL; } |
| 89 | |
| 90 | inline bool IsTypedVectorElementType(Type t) { |
| 91 | return (t >= FBT_INT && t <= FBT_STRING) || t == FBT_BOOL; |
| 92 | } |
| 93 | |
| 94 | inline bool IsTypedVector(Type t) { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 95 | return (t >= FBT_VECTOR_INT && t <= FBT_VECTOR_STRING_DEPRECATED) || |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 96 | t == FBT_VECTOR_BOOL; |
| 97 | } |
| 98 | |
| 99 | inline bool IsFixedTypedVector(Type t) { |
| 100 | return t >= FBT_VECTOR_INT2 && t <= FBT_VECTOR_FLOAT4; |
| 101 | } |
| 102 | |
| 103 | inline Type ToTypedVector(Type t, size_t fixed_len = 0) { |
| 104 | FLATBUFFERS_ASSERT(IsTypedVectorElementType(t)); |
| 105 | switch (fixed_len) { |
| 106 | case 0: return static_cast<Type>(t - FBT_INT + FBT_VECTOR_INT); |
| 107 | case 2: return static_cast<Type>(t - FBT_INT + FBT_VECTOR_INT2); |
| 108 | case 3: return static_cast<Type>(t - FBT_INT + FBT_VECTOR_INT3); |
| 109 | case 4: return static_cast<Type>(t - FBT_INT + FBT_VECTOR_INT4); |
| 110 | default: FLATBUFFERS_ASSERT(0); return FBT_NULL; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | inline Type ToTypedVectorElementType(Type t) { |
| 115 | FLATBUFFERS_ASSERT(IsTypedVector(t)); |
| 116 | return static_cast<Type>(t - FBT_VECTOR_INT + FBT_INT); |
| 117 | } |
| 118 | |
| 119 | inline Type ToFixedTypedVectorElementType(Type t, uint8_t *len) { |
| 120 | FLATBUFFERS_ASSERT(IsFixedTypedVector(t)); |
| 121 | auto fixed_type = t - FBT_VECTOR_INT2; |
| 122 | *len = static_cast<uint8_t>(fixed_type / 3 + |
| 123 | 2); // 3 types each, starting from length 2. |
| 124 | return static_cast<Type>(fixed_type % 3 + FBT_INT); |
| 125 | } |
| 126 | |
| 127 | // TODO: implement proper support for 8/16bit floats, or decide not to |
| 128 | // support them. |
| 129 | typedef int16_t half; |
| 130 | typedef int8_t quarter; |
| 131 | |
| 132 | // TODO: can we do this without conditionals using intrinsics or inline asm |
| 133 | // on some platforms? Given branch prediction the method below should be |
| 134 | // decently quick, but it is the most frequently executed function. |
| 135 | // We could do an (unaligned) 64-bit read if we ifdef out the platforms for |
| 136 | // which that doesn't work (or where we'd read into un-owned memory). |
| 137 | template<typename R, typename T1, typename T2, typename T4, typename T8> |
| 138 | R ReadSizedScalar(const uint8_t *data, uint8_t byte_width) { |
| 139 | return byte_width < 4 |
| 140 | ? (byte_width < 2 |
| 141 | ? static_cast<R>(flatbuffers::ReadScalar<T1>(data)) |
| 142 | : static_cast<R>(flatbuffers::ReadScalar<T2>(data))) |
| 143 | : (byte_width < 8 |
| 144 | ? static_cast<R>(flatbuffers::ReadScalar<T4>(data)) |
| 145 | : static_cast<R>(flatbuffers::ReadScalar<T8>(data))); |
| 146 | } |
| 147 | |
| 148 | inline int64_t ReadInt64(const uint8_t *data, uint8_t byte_width) { |
| 149 | return ReadSizedScalar<int64_t, int8_t, int16_t, int32_t, int64_t>( |
| 150 | data, byte_width); |
| 151 | } |
| 152 | |
| 153 | inline uint64_t ReadUInt64(const uint8_t *data, uint8_t byte_width) { |
| 154 | // This is the "hottest" function (all offset lookups use this), so worth |
| 155 | // optimizing if possible. |
| 156 | // TODO: GCC apparently replaces memcpy by a rep movsb, but only if count is a |
| 157 | // constant, which here it isn't. Test if memcpy is still faster than |
| 158 | // the conditionals in ReadSizedScalar. Can also use inline asm. |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 159 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 160 | // clang-format off |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 161 | #if defined(_MSC_VER) && defined(_M_X64) && !defined(_M_ARM64EC) |
| 162 | // This is 64-bit Windows only, __movsb does not work on 32-bit Windows. |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 163 | uint64_t u = 0; |
| 164 | __movsb(reinterpret_cast<uint8_t *>(&u), |
| 165 | reinterpret_cast<const uint8_t *>(data), byte_width); |
| 166 | return flatbuffers::EndianScalar(u); |
| 167 | #else |
| 168 | return ReadSizedScalar<uint64_t, uint8_t, uint16_t, uint32_t, uint64_t>( |
| 169 | data, byte_width); |
| 170 | #endif |
| 171 | // clang-format on |
| 172 | } |
| 173 | |
| 174 | inline double ReadDouble(const uint8_t *data, uint8_t byte_width) { |
| 175 | return ReadSizedScalar<double, quarter, half, float, double>(data, |
| 176 | byte_width); |
| 177 | } |
| 178 | |
| 179 | inline const uint8_t *Indirect(const uint8_t *offset, uint8_t byte_width) { |
| 180 | return offset - ReadUInt64(offset, byte_width); |
| 181 | } |
| 182 | |
| 183 | template<typename T> const uint8_t *Indirect(const uint8_t *offset) { |
| 184 | return offset - flatbuffers::ReadScalar<T>(offset); |
| 185 | } |
| 186 | |
| 187 | inline BitWidth WidthU(uint64_t u) { |
| 188 | #define FLATBUFFERS_GET_FIELD_BIT_WIDTH(value, width) \ |
| 189 | { \ |
| 190 | if (!((u) & ~((1ULL << (width)) - 1ULL))) return BIT_WIDTH_##width; \ |
| 191 | } |
| 192 | FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 8); |
| 193 | FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 16); |
| 194 | FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 32); |
| 195 | #undef FLATBUFFERS_GET_FIELD_BIT_WIDTH |
| 196 | return BIT_WIDTH_64; |
| 197 | } |
| 198 | |
| 199 | inline BitWidth WidthI(int64_t i) { |
| 200 | auto u = static_cast<uint64_t>(i) << 1; |
| 201 | return WidthU(i >= 0 ? u : ~u); |
| 202 | } |
| 203 | |
| 204 | inline BitWidth WidthF(double f) { |
| 205 | return static_cast<double>(static_cast<float>(f)) == f ? BIT_WIDTH_32 |
| 206 | : BIT_WIDTH_64; |
| 207 | } |
| 208 | |
| 209 | // Base class of all types below. |
| 210 | // Points into the data buffer and allows access to one type. |
| 211 | class Object { |
| 212 | public: |
| 213 | Object(const uint8_t *data, uint8_t byte_width) |
| 214 | : data_(data), byte_width_(byte_width) {} |
| 215 | |
| 216 | protected: |
| 217 | const uint8_t *data_; |
| 218 | uint8_t byte_width_; |
| 219 | }; |
| 220 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 221 | // Object that has a size, obtained either from size prefix, or elsewhere. |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 222 | class Sized : public Object { |
| 223 | public: |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 224 | // Size prefix. |
| 225 | Sized(const uint8_t *data, uint8_t byte_width) |
| 226 | : Object(data, byte_width), size_(read_size()) {} |
| 227 | // Manual size. |
| 228 | Sized(const uint8_t *data, uint8_t byte_width, size_t sz) |
| 229 | : Object(data, byte_width), size_(sz) {} |
| 230 | size_t size() const { return size_; } |
| 231 | // Access size stored in `byte_width_` bytes before data_ pointer. |
| 232 | size_t read_size() const { |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 233 | return static_cast<size_t>(ReadUInt64(data_ - byte_width_, byte_width_)); |
| 234 | } |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 235 | |
| 236 | protected: |
| 237 | size_t size_; |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 238 | }; |
| 239 | |
| 240 | class String : public Sized { |
| 241 | public: |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 242 | // Size prefix. |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 243 | String(const uint8_t *data, uint8_t byte_width) : Sized(data, byte_width) {} |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 244 | // Manual size. |
| 245 | String(const uint8_t *data, uint8_t byte_width, size_t sz) |
| 246 | : Sized(data, byte_width, sz) {} |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 247 | |
| 248 | size_t length() const { return size(); } |
| 249 | const char *c_str() const { return reinterpret_cast<const char *>(data_); } |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 250 | std::string str() const { return std::string(c_str(), size()); } |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 251 | |
| 252 | static String EmptyString() { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 253 | static const char *empty_string = ""; |
| 254 | return String(reinterpret_cast<const uint8_t *>(empty_string), 1, 0); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 255 | } |
| 256 | bool IsTheEmptyString() const { return data_ == EmptyString().data_; } |
| 257 | }; |
| 258 | |
| 259 | class Blob : public Sized { |
| 260 | public: |
| 261 | Blob(const uint8_t *data_buf, uint8_t byte_width) |
| 262 | : Sized(data_buf, byte_width) {} |
| 263 | |
| 264 | static Blob EmptyBlob() { |
| 265 | static const uint8_t empty_blob[] = { 0 /*len*/ }; |
| 266 | return Blob(empty_blob + 1, 1); |
| 267 | } |
| 268 | bool IsTheEmptyBlob() const { return data_ == EmptyBlob().data_; } |
| 269 | const uint8_t *data() const { return data_; } |
| 270 | }; |
| 271 | |
| 272 | class Vector : public Sized { |
| 273 | public: |
| 274 | Vector(const uint8_t *data, uint8_t byte_width) : Sized(data, byte_width) {} |
| 275 | |
| 276 | Reference operator[](size_t i) const; |
| 277 | |
| 278 | static Vector EmptyVector() { |
| 279 | static const uint8_t empty_vector[] = { 0 /*len*/ }; |
| 280 | return Vector(empty_vector + 1, 1); |
| 281 | } |
| 282 | bool IsTheEmptyVector() const { return data_ == EmptyVector().data_; } |
| 283 | }; |
| 284 | |
| 285 | class TypedVector : public Sized { |
| 286 | public: |
| 287 | TypedVector(const uint8_t *data, uint8_t byte_width, Type element_type) |
| 288 | : Sized(data, byte_width), type_(element_type) {} |
| 289 | |
| 290 | Reference operator[](size_t i) const; |
| 291 | |
| 292 | static TypedVector EmptyTypedVector() { |
| 293 | static const uint8_t empty_typed_vector[] = { 0 /*len*/ }; |
| 294 | return TypedVector(empty_typed_vector + 1, 1, FBT_INT); |
| 295 | } |
| 296 | bool IsTheEmptyVector() const { |
| 297 | return data_ == TypedVector::EmptyTypedVector().data_; |
| 298 | } |
| 299 | |
| 300 | Type ElementType() { return type_; } |
| 301 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 302 | friend Reference; |
| 303 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 304 | private: |
| 305 | Type type_; |
| 306 | |
| 307 | friend Map; |
| 308 | }; |
| 309 | |
| 310 | class FixedTypedVector : public Object { |
| 311 | public: |
| 312 | FixedTypedVector(const uint8_t *data, uint8_t byte_width, Type element_type, |
| 313 | uint8_t len) |
| 314 | : Object(data, byte_width), type_(element_type), len_(len) {} |
| 315 | |
| 316 | Reference operator[](size_t i) const; |
| 317 | |
| 318 | static FixedTypedVector EmptyFixedTypedVector() { |
| 319 | static const uint8_t fixed_empty_vector[] = { 0 /* unused */ }; |
| 320 | return FixedTypedVector(fixed_empty_vector, 1, FBT_INT, 0); |
| 321 | } |
| 322 | bool IsTheEmptyFixedTypedVector() const { |
| 323 | return data_ == FixedTypedVector::EmptyFixedTypedVector().data_; |
| 324 | } |
| 325 | |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 326 | Type ElementType() const { return type_; } |
| 327 | uint8_t size() const { return len_; } |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 328 | |
| 329 | private: |
| 330 | Type type_; |
| 331 | uint8_t len_; |
| 332 | }; |
| 333 | |
| 334 | class Map : public Vector { |
| 335 | public: |
| 336 | Map(const uint8_t *data, uint8_t byte_width) : Vector(data, byte_width) {} |
| 337 | |
| 338 | Reference operator[](const char *key) const; |
| 339 | Reference operator[](const std::string &key) const; |
| 340 | |
| 341 | Vector Values() const { return Vector(data_, byte_width_); } |
| 342 | |
| 343 | TypedVector Keys() const { |
| 344 | const size_t num_prefixed_fields = 3; |
| 345 | auto keys_offset = data_ - byte_width_ * num_prefixed_fields; |
| 346 | return TypedVector(Indirect(keys_offset, byte_width_), |
| 347 | static_cast<uint8_t>( |
| 348 | ReadUInt64(keys_offset + byte_width_, byte_width_)), |
| 349 | FBT_KEY); |
| 350 | } |
| 351 | |
| 352 | static Map EmptyMap() { |
| 353 | static const uint8_t empty_map[] = { |
| 354 | 0 /*keys_len*/, 0 /*keys_offset*/, 1 /*keys_width*/, 0 /*len*/ |
| 355 | }; |
| 356 | return Map(empty_map + 4, 1); |
| 357 | } |
| 358 | |
| 359 | bool IsTheEmptyMap() const { return data_ == EmptyMap().data_; } |
| 360 | }; |
| 361 | |
| 362 | template<typename T> |
| 363 | void AppendToString(std::string &s, T &&v, bool keys_quoted) { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 364 | s += "[ "; |
| 365 | for (size_t i = 0; i < v.size(); i++) { |
| 366 | if (i) s += ", "; |
| 367 | v[i].ToString(true, keys_quoted, s); |
| 368 | } |
| 369 | s += " ]"; |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 370 | } |
| 371 | |
| 372 | class Reference { |
| 373 | public: |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 374 | Reference() |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 375 | : data_(nullptr), parent_width_(0), byte_width_(0), type_(FBT_NULL) {} |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 376 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 377 | Reference(const uint8_t *data, uint8_t parent_width, uint8_t byte_width, |
| 378 | Type type) |
| 379 | : data_(data), |
| 380 | parent_width_(parent_width), |
| 381 | byte_width_(byte_width), |
| 382 | type_(type) {} |
| 383 | |
| 384 | Reference(const uint8_t *data, uint8_t parent_width, uint8_t packed_type) |
| 385 | : data_(data), parent_width_(parent_width) { |
| 386 | byte_width_ = 1U << static_cast<BitWidth>(packed_type & 3); |
| 387 | type_ = static_cast<Type>(packed_type >> 2); |
| 388 | } |
| 389 | |
| 390 | Type GetType() const { return type_; } |
| 391 | |
| 392 | bool IsNull() const { return type_ == FBT_NULL; } |
| 393 | bool IsBool() const { return type_ == FBT_BOOL; } |
| 394 | bool IsInt() const { return type_ == FBT_INT || type_ == FBT_INDIRECT_INT; } |
| 395 | bool IsUInt() const { |
| 396 | return type_ == FBT_UINT || type_ == FBT_INDIRECT_UINT; |
| 397 | } |
| 398 | bool IsIntOrUint() const { return IsInt() || IsUInt(); } |
| 399 | bool IsFloat() const { |
| 400 | return type_ == FBT_FLOAT || type_ == FBT_INDIRECT_FLOAT; |
| 401 | } |
| 402 | bool IsNumeric() const { return IsIntOrUint() || IsFloat(); } |
| 403 | bool IsString() const { return type_ == FBT_STRING; } |
| 404 | bool IsKey() const { return type_ == FBT_KEY; } |
| 405 | bool IsVector() const { return type_ == FBT_VECTOR || type_ == FBT_MAP; } |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 406 | bool IsUntypedVector() const { return type_ == FBT_VECTOR; } |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 407 | bool IsTypedVector() const { return flexbuffers::IsTypedVector(type_); } |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 408 | bool IsFixedTypedVector() const { |
| 409 | return flexbuffers::IsFixedTypedVector(type_); |
| 410 | } |
| 411 | bool IsAnyVector() const { |
| 412 | return (IsTypedVector() || IsFixedTypedVector() || IsVector()); |
| 413 | } |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 414 | bool IsMap() const { return type_ == FBT_MAP; } |
| 415 | bool IsBlob() const { return type_ == FBT_BLOB; } |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 416 | bool AsBool() const { |
| 417 | return (type_ == FBT_BOOL ? ReadUInt64(data_, parent_width_) |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 418 | : AsUInt64()) != 0; |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 419 | } |
| 420 | |
| 421 | // Reads any type as a int64_t. Never fails, does most sensible conversion. |
| 422 | // Truncates floats, strings are attempted to be parsed for a number, |
| 423 | // vectors/maps return their size. Returns 0 if all else fails. |
| 424 | int64_t AsInt64() const { |
| 425 | if (type_ == FBT_INT) { |
| 426 | // A fast path for the common case. |
| 427 | return ReadInt64(data_, parent_width_); |
| 428 | } else |
| 429 | switch (type_) { |
| 430 | case FBT_INDIRECT_INT: return ReadInt64(Indirect(), byte_width_); |
| 431 | case FBT_UINT: return ReadUInt64(data_, parent_width_); |
| 432 | case FBT_INDIRECT_UINT: return ReadUInt64(Indirect(), byte_width_); |
| 433 | case FBT_FLOAT: |
| 434 | return static_cast<int64_t>(ReadDouble(data_, parent_width_)); |
| 435 | case FBT_INDIRECT_FLOAT: |
| 436 | return static_cast<int64_t>(ReadDouble(Indirect(), byte_width_)); |
| 437 | case FBT_NULL: return 0; |
| 438 | case FBT_STRING: return flatbuffers::StringToInt(AsString().c_str()); |
| 439 | case FBT_VECTOR: return static_cast<int64_t>(AsVector().size()); |
| 440 | case FBT_BOOL: return ReadInt64(data_, parent_width_); |
| 441 | default: |
| 442 | // Convert other things to int. |
| 443 | return 0; |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | // TODO: could specialize these to not use AsInt64() if that saves |
| 448 | // extension ops in generated code, and use a faster op than ReadInt64. |
| 449 | int32_t AsInt32() const { return static_cast<int32_t>(AsInt64()); } |
| 450 | int16_t AsInt16() const { return static_cast<int16_t>(AsInt64()); } |
| 451 | int8_t AsInt8() const { return static_cast<int8_t>(AsInt64()); } |
| 452 | |
| 453 | uint64_t AsUInt64() const { |
| 454 | if (type_ == FBT_UINT) { |
| 455 | // A fast path for the common case. |
| 456 | return ReadUInt64(data_, parent_width_); |
| 457 | } else |
| 458 | switch (type_) { |
| 459 | case FBT_INDIRECT_UINT: return ReadUInt64(Indirect(), byte_width_); |
| 460 | case FBT_INT: return ReadInt64(data_, parent_width_); |
| 461 | case FBT_INDIRECT_INT: return ReadInt64(Indirect(), byte_width_); |
| 462 | case FBT_FLOAT: |
| 463 | return static_cast<uint64_t>(ReadDouble(data_, parent_width_)); |
| 464 | case FBT_INDIRECT_FLOAT: |
| 465 | return static_cast<uint64_t>(ReadDouble(Indirect(), byte_width_)); |
| 466 | case FBT_NULL: return 0; |
| 467 | case FBT_STRING: return flatbuffers::StringToUInt(AsString().c_str()); |
| 468 | case FBT_VECTOR: return static_cast<uint64_t>(AsVector().size()); |
| 469 | case FBT_BOOL: return ReadUInt64(data_, parent_width_); |
| 470 | default: |
| 471 | // Convert other things to uint. |
| 472 | return 0; |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | uint32_t AsUInt32() const { return static_cast<uint32_t>(AsUInt64()); } |
| 477 | uint16_t AsUInt16() const { return static_cast<uint16_t>(AsUInt64()); } |
| 478 | uint8_t AsUInt8() const { return static_cast<uint8_t>(AsUInt64()); } |
| 479 | |
| 480 | double AsDouble() const { |
| 481 | if (type_ == FBT_FLOAT) { |
| 482 | // A fast path for the common case. |
| 483 | return ReadDouble(data_, parent_width_); |
| 484 | } else |
| 485 | switch (type_) { |
| 486 | case FBT_INDIRECT_FLOAT: return ReadDouble(Indirect(), byte_width_); |
| 487 | case FBT_INT: |
| 488 | return static_cast<double>(ReadInt64(data_, parent_width_)); |
| 489 | case FBT_UINT: |
| 490 | return static_cast<double>(ReadUInt64(data_, parent_width_)); |
| 491 | case FBT_INDIRECT_INT: |
| 492 | return static_cast<double>(ReadInt64(Indirect(), byte_width_)); |
| 493 | case FBT_INDIRECT_UINT: |
| 494 | return static_cast<double>(ReadUInt64(Indirect(), byte_width_)); |
| 495 | case FBT_NULL: return 0.0; |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 496 | case FBT_STRING: { |
| 497 | double d; |
| 498 | flatbuffers::StringToNumber(AsString().c_str(), &d); |
| 499 | return d; |
| 500 | } |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 501 | case FBT_VECTOR: return static_cast<double>(AsVector().size()); |
| 502 | case FBT_BOOL: |
| 503 | return static_cast<double>(ReadUInt64(data_, parent_width_)); |
| 504 | default: |
| 505 | // Convert strings and other things to float. |
| 506 | return 0; |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | float AsFloat() const { return static_cast<float>(AsDouble()); } |
| 511 | |
| 512 | const char *AsKey() const { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 513 | if (type_ == FBT_KEY || type_ == FBT_STRING) { |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 514 | return reinterpret_cast<const char *>(Indirect()); |
| 515 | } else { |
| 516 | return ""; |
| 517 | } |
| 518 | } |
| 519 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 520 | // This function returns the empty string if you try to read something that |
| 521 | // is not a string or key. |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 522 | String AsString() const { |
| 523 | if (type_ == FBT_STRING) { |
| 524 | return String(Indirect(), byte_width_); |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 525 | } else if (type_ == FBT_KEY) { |
| 526 | auto key = Indirect(); |
| 527 | return String(key, byte_width_, |
| 528 | strlen(reinterpret_cast<const char *>(key))); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 529 | } else { |
| 530 | return String::EmptyString(); |
| 531 | } |
| 532 | } |
| 533 | |
| 534 | // Unlike AsString(), this will convert any type to a std::string. |
| 535 | std::string ToString() const { |
| 536 | std::string s; |
| 537 | ToString(false, false, s); |
| 538 | return s; |
| 539 | } |
| 540 | |
| 541 | // Convert any type to a JSON-like string. strings_quoted determines if |
| 542 | // string values at the top level receive "" quotes (inside other values |
| 543 | // they always do). keys_quoted determines if keys are quoted, at any level. |
| 544 | // TODO(wvo): add further options to have indentation/newlines. |
| 545 | void ToString(bool strings_quoted, bool keys_quoted, std::string &s) const { |
| 546 | if (type_ == FBT_STRING) { |
| 547 | String str(Indirect(), byte_width_); |
| 548 | if (strings_quoted) { |
| 549 | flatbuffers::EscapeString(str.c_str(), str.length(), &s, true, false); |
| 550 | } else { |
| 551 | s.append(str.c_str(), str.length()); |
| 552 | } |
| 553 | } else if (IsKey()) { |
| 554 | auto str = AsKey(); |
| 555 | if (keys_quoted) { |
| 556 | flatbuffers::EscapeString(str, strlen(str), &s, true, false); |
| 557 | } else { |
| 558 | s += str; |
| 559 | } |
| 560 | } else if (IsInt()) { |
| 561 | s += flatbuffers::NumToString(AsInt64()); |
| 562 | } else if (IsUInt()) { |
| 563 | s += flatbuffers::NumToString(AsUInt64()); |
| 564 | } else if (IsFloat()) { |
| 565 | s += flatbuffers::NumToString(AsDouble()); |
| 566 | } else if (IsNull()) { |
| 567 | s += "null"; |
| 568 | } else if (IsBool()) { |
| 569 | s += AsBool() ? "true" : "false"; |
| 570 | } else if (IsMap()) { |
| 571 | s += "{ "; |
| 572 | auto m = AsMap(); |
| 573 | auto keys = m.Keys(); |
| 574 | auto vals = m.Values(); |
| 575 | for (size_t i = 0; i < keys.size(); i++) { |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 576 | bool kq = keys_quoted; |
| 577 | if (!kq) { |
| 578 | // FlexBuffers keys may contain arbitrary characters, only allow |
| 579 | // unquoted if it looks like an "identifier": |
| 580 | const char *p = keys[i].AsKey(); |
| 581 | if (!flatbuffers::is_alpha(*p) && *p != '_') { |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 582 | kq = true; |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 583 | } else { |
| 584 | while (*++p) { |
| 585 | if (!flatbuffers::is_alnum(*p) && *p != '_') { |
| 586 | kq = true; |
| 587 | break; |
| 588 | } |
| 589 | } |
| 590 | } |
| 591 | } |
| 592 | keys[i].ToString(true, kq, s); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 593 | s += ": "; |
| 594 | vals[i].ToString(true, keys_quoted, s); |
| 595 | if (i < keys.size() - 1) s += ", "; |
| 596 | } |
| 597 | s += " }"; |
| 598 | } else if (IsVector()) { |
| 599 | AppendToString<Vector>(s, AsVector(), keys_quoted); |
| 600 | } else if (IsTypedVector()) { |
| 601 | AppendToString<TypedVector>(s, AsTypedVector(), keys_quoted); |
| 602 | } else if (IsFixedTypedVector()) { |
| 603 | AppendToString<FixedTypedVector>(s, AsFixedTypedVector(), keys_quoted); |
| 604 | } else if (IsBlob()) { |
| 605 | auto blob = AsBlob(); |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 606 | flatbuffers::EscapeString(reinterpret_cast<const char *>(blob.data()), |
| 607 | blob.size(), &s, true, false); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 608 | } else { |
| 609 | s += "(?)"; |
| 610 | } |
| 611 | } |
| 612 | |
| 613 | // This function returns the empty blob if you try to read a not-blob. |
| 614 | // Strings can be viewed as blobs too. |
| 615 | Blob AsBlob() const { |
| 616 | if (type_ == FBT_BLOB || type_ == FBT_STRING) { |
| 617 | return Blob(Indirect(), byte_width_); |
| 618 | } else { |
| 619 | return Blob::EmptyBlob(); |
| 620 | } |
| 621 | } |
| 622 | |
| 623 | // This function returns the empty vector if you try to read a not-vector. |
| 624 | // Maps can be viewed as vectors too. |
| 625 | Vector AsVector() const { |
| 626 | if (type_ == FBT_VECTOR || type_ == FBT_MAP) { |
| 627 | return Vector(Indirect(), byte_width_); |
| 628 | } else { |
| 629 | return Vector::EmptyVector(); |
| 630 | } |
| 631 | } |
| 632 | |
| 633 | TypedVector AsTypedVector() const { |
| 634 | if (IsTypedVector()) { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 635 | auto tv = |
| 636 | TypedVector(Indirect(), byte_width_, ToTypedVectorElementType(type_)); |
| 637 | if (tv.type_ == FBT_STRING) { |
| 638 | // These can't be accessed as strings, since we don't know the bit-width |
| 639 | // of the size field, see the declaration of |
| 640 | // FBT_VECTOR_STRING_DEPRECATED above for details. |
| 641 | // We change the type here to be keys, which are a subtype of strings, |
| 642 | // and will ignore the size field. This will truncate strings with |
| 643 | // embedded nulls. |
| 644 | tv.type_ = FBT_KEY; |
| 645 | } |
| 646 | return tv; |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 647 | } else { |
| 648 | return TypedVector::EmptyTypedVector(); |
| 649 | } |
| 650 | } |
| 651 | |
| 652 | FixedTypedVector AsFixedTypedVector() const { |
| 653 | if (IsFixedTypedVector()) { |
| 654 | uint8_t len = 0; |
| 655 | auto vtype = ToFixedTypedVectorElementType(type_, &len); |
| 656 | return FixedTypedVector(Indirect(), byte_width_, vtype, len); |
| 657 | } else { |
| 658 | return FixedTypedVector::EmptyFixedTypedVector(); |
| 659 | } |
| 660 | } |
| 661 | |
| 662 | Map AsMap() const { |
| 663 | if (type_ == FBT_MAP) { |
| 664 | return Map(Indirect(), byte_width_); |
| 665 | } else { |
| 666 | return Map::EmptyMap(); |
| 667 | } |
| 668 | } |
| 669 | |
| 670 | template<typename T> T As() const; |
| 671 | |
| 672 | // Experimental: Mutation functions. |
| 673 | // These allow scalars in an already created buffer to be updated in-place. |
| 674 | // Since by default scalars are stored in the smallest possible space, |
| 675 | // the new value may not fit, in which case these functions return false. |
| 676 | // To avoid this, you can construct the values you intend to mutate using |
| 677 | // Builder::ForceMinimumBitWidth. |
| 678 | bool MutateInt(int64_t i) { |
| 679 | if (type_ == FBT_INT) { |
| 680 | return Mutate(data_, i, parent_width_, WidthI(i)); |
| 681 | } else if (type_ == FBT_INDIRECT_INT) { |
| 682 | return Mutate(Indirect(), i, byte_width_, WidthI(i)); |
| 683 | } else if (type_ == FBT_UINT) { |
| 684 | auto u = static_cast<uint64_t>(i); |
| 685 | return Mutate(data_, u, parent_width_, WidthU(u)); |
| 686 | } else if (type_ == FBT_INDIRECT_UINT) { |
| 687 | auto u = static_cast<uint64_t>(i); |
| 688 | return Mutate(Indirect(), u, byte_width_, WidthU(u)); |
| 689 | } else { |
| 690 | return false; |
| 691 | } |
| 692 | } |
| 693 | |
| 694 | bool MutateBool(bool b) { |
| 695 | return type_ == FBT_BOOL && Mutate(data_, b, parent_width_, BIT_WIDTH_8); |
| 696 | } |
| 697 | |
| 698 | bool MutateUInt(uint64_t u) { |
| 699 | if (type_ == FBT_UINT) { |
| 700 | return Mutate(data_, u, parent_width_, WidthU(u)); |
| 701 | } else if (type_ == FBT_INDIRECT_UINT) { |
| 702 | return Mutate(Indirect(), u, byte_width_, WidthU(u)); |
| 703 | } else if (type_ == FBT_INT) { |
| 704 | auto i = static_cast<int64_t>(u); |
| 705 | return Mutate(data_, i, parent_width_, WidthI(i)); |
| 706 | } else if (type_ == FBT_INDIRECT_INT) { |
| 707 | auto i = static_cast<int64_t>(u); |
| 708 | return Mutate(Indirect(), i, byte_width_, WidthI(i)); |
| 709 | } else { |
| 710 | return false; |
| 711 | } |
| 712 | } |
| 713 | |
| 714 | bool MutateFloat(float f) { |
| 715 | if (type_ == FBT_FLOAT) { |
| 716 | return MutateF(data_, f, parent_width_, BIT_WIDTH_32); |
| 717 | } else if (type_ == FBT_INDIRECT_FLOAT) { |
| 718 | return MutateF(Indirect(), f, byte_width_, BIT_WIDTH_32); |
| 719 | } else { |
| 720 | return false; |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | bool MutateFloat(double d) { |
| 725 | if (type_ == FBT_FLOAT) { |
| 726 | return MutateF(data_, d, parent_width_, WidthF(d)); |
| 727 | } else if (type_ == FBT_INDIRECT_FLOAT) { |
| 728 | return MutateF(Indirect(), d, byte_width_, WidthF(d)); |
| 729 | } else { |
| 730 | return false; |
| 731 | } |
| 732 | } |
| 733 | |
| 734 | bool MutateString(const char *str, size_t len) { |
| 735 | auto s = AsString(); |
| 736 | if (s.IsTheEmptyString()) return false; |
| 737 | // This is very strict, could allow shorter strings, but that creates |
| 738 | // garbage. |
| 739 | if (s.length() != len) return false; |
| 740 | memcpy(const_cast<char *>(s.c_str()), str, len); |
| 741 | return true; |
| 742 | } |
| 743 | bool MutateString(const char *str) { return MutateString(str, strlen(str)); } |
| 744 | bool MutateString(const std::string &str) { |
| 745 | return MutateString(str.data(), str.length()); |
| 746 | } |
| 747 | |
| 748 | private: |
| 749 | const uint8_t *Indirect() const { |
| 750 | return flexbuffers::Indirect(data_, parent_width_); |
| 751 | } |
| 752 | |
| 753 | template<typename T> |
| 754 | bool Mutate(const uint8_t *dest, T t, size_t byte_width, |
| 755 | BitWidth value_width) { |
| 756 | auto fits = static_cast<size_t>(static_cast<size_t>(1U) << value_width) <= |
| 757 | byte_width; |
| 758 | if (fits) { |
| 759 | t = flatbuffers::EndianScalar(t); |
| 760 | memcpy(const_cast<uint8_t *>(dest), &t, byte_width); |
| 761 | } |
| 762 | return fits; |
| 763 | } |
| 764 | |
| 765 | template<typename T> |
| 766 | bool MutateF(const uint8_t *dest, T t, size_t byte_width, |
| 767 | BitWidth value_width) { |
| 768 | if (byte_width == sizeof(double)) |
| 769 | return Mutate(dest, static_cast<double>(t), byte_width, value_width); |
| 770 | if (byte_width == sizeof(float)) |
| 771 | return Mutate(dest, static_cast<float>(t), byte_width, value_width); |
| 772 | FLATBUFFERS_ASSERT(false); |
| 773 | return false; |
| 774 | } |
| 775 | |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 776 | friend class Verifier; |
| 777 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 778 | const uint8_t *data_; |
| 779 | uint8_t parent_width_; |
| 780 | uint8_t byte_width_; |
| 781 | Type type_; |
| 782 | }; |
| 783 | |
| 784 | // Template specialization for As(). |
| 785 | template<> inline bool Reference::As<bool>() const { return AsBool(); } |
| 786 | |
| 787 | template<> inline int8_t Reference::As<int8_t>() const { return AsInt8(); } |
| 788 | template<> inline int16_t Reference::As<int16_t>() const { return AsInt16(); } |
| 789 | template<> inline int32_t Reference::As<int32_t>() const { return AsInt32(); } |
| 790 | template<> inline int64_t Reference::As<int64_t>() const { return AsInt64(); } |
| 791 | |
| 792 | template<> inline uint8_t Reference::As<uint8_t>() const { return AsUInt8(); } |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 793 | template<> inline uint16_t Reference::As<uint16_t>() const { |
| 794 | return AsUInt16(); |
| 795 | } |
| 796 | template<> inline uint32_t Reference::As<uint32_t>() const { |
| 797 | return AsUInt32(); |
| 798 | } |
| 799 | template<> inline uint64_t Reference::As<uint64_t>() const { |
| 800 | return AsUInt64(); |
| 801 | } |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 802 | |
| 803 | template<> inline double Reference::As<double>() const { return AsDouble(); } |
| 804 | template<> inline float Reference::As<float>() const { return AsFloat(); } |
| 805 | |
| 806 | template<> inline String Reference::As<String>() const { return AsString(); } |
| 807 | template<> inline std::string Reference::As<std::string>() const { |
| 808 | return AsString().str(); |
| 809 | } |
| 810 | |
| 811 | template<> inline Blob Reference::As<Blob>() const { return AsBlob(); } |
| 812 | template<> inline Vector Reference::As<Vector>() const { return AsVector(); } |
| 813 | template<> inline TypedVector Reference::As<TypedVector>() const { |
| 814 | return AsTypedVector(); |
| 815 | } |
| 816 | template<> inline FixedTypedVector Reference::As<FixedTypedVector>() const { |
| 817 | return AsFixedTypedVector(); |
| 818 | } |
| 819 | template<> inline Map Reference::As<Map>() const { return AsMap(); } |
| 820 | |
| 821 | inline uint8_t PackedType(BitWidth bit_width, Type type) { |
| 822 | return static_cast<uint8_t>(bit_width | (type << 2)); |
| 823 | } |
| 824 | |
| 825 | inline uint8_t NullPackedType() { return PackedType(BIT_WIDTH_8, FBT_NULL); } |
| 826 | |
| 827 | // Vector accessors. |
| 828 | // Note: if you try to access outside of bounds, you get a Null value back |
| 829 | // instead. Normally this would be an assert, but since this is "dynamically |
| 830 | // typed" data, you may not want that (someone sends you a 2d vector and you |
| 831 | // wanted 3d). |
| 832 | // The Null converts seamlessly into a default value for any other type. |
| 833 | // TODO(wvo): Could introduce an #ifdef that makes this into an assert? |
| 834 | inline Reference Vector::operator[](size_t i) const { |
| 835 | auto len = size(); |
| 836 | if (i >= len) return Reference(nullptr, 1, NullPackedType()); |
| 837 | auto packed_type = (data_ + len * byte_width_)[i]; |
| 838 | auto elem = data_ + i * byte_width_; |
| 839 | return Reference(elem, byte_width_, packed_type); |
| 840 | } |
| 841 | |
| 842 | inline Reference TypedVector::operator[](size_t i) const { |
| 843 | auto len = size(); |
| 844 | if (i >= len) return Reference(nullptr, 1, NullPackedType()); |
| 845 | auto elem = data_ + i * byte_width_; |
| 846 | return Reference(elem, byte_width_, 1, type_); |
| 847 | } |
| 848 | |
| 849 | inline Reference FixedTypedVector::operator[](size_t i) const { |
| 850 | if (i >= len_) return Reference(nullptr, 1, NullPackedType()); |
| 851 | auto elem = data_ + i * byte_width_; |
| 852 | return Reference(elem, byte_width_, 1, type_); |
| 853 | } |
| 854 | |
| 855 | template<typename T> int KeyCompare(const void *key, const void *elem) { |
| 856 | auto str_elem = reinterpret_cast<const char *>( |
| 857 | Indirect<T>(reinterpret_cast<const uint8_t *>(elem))); |
| 858 | auto skey = reinterpret_cast<const char *>(key); |
| 859 | return strcmp(skey, str_elem); |
| 860 | } |
| 861 | |
| 862 | inline Reference Map::operator[](const char *key) const { |
| 863 | auto keys = Keys(); |
| 864 | // We can't pass keys.byte_width_ to the comparison function, so we have |
| 865 | // to pick the right one ahead of time. |
| 866 | int (*comp)(const void *, const void *) = nullptr; |
| 867 | switch (keys.byte_width_) { |
| 868 | case 1: comp = KeyCompare<uint8_t>; break; |
| 869 | case 2: comp = KeyCompare<uint16_t>; break; |
| 870 | case 4: comp = KeyCompare<uint32_t>; break; |
| 871 | case 8: comp = KeyCompare<uint64_t>; break; |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 872 | default: FLATBUFFERS_ASSERT(false); return Reference(); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 873 | } |
| 874 | auto res = std::bsearch(key, keys.data_, keys.size(), keys.byte_width_, comp); |
| 875 | if (!res) return Reference(nullptr, 1, NullPackedType()); |
| 876 | auto i = (reinterpret_cast<uint8_t *>(res) - keys.data_) / keys.byte_width_; |
| 877 | return (*static_cast<const Vector *>(this))[i]; |
| 878 | } |
| 879 | |
| 880 | inline Reference Map::operator[](const std::string &key) const { |
| 881 | return (*this)[key.c_str()]; |
| 882 | } |
| 883 | |
| 884 | inline Reference GetRoot(const uint8_t *buffer, size_t size) { |
| 885 | // See Finish() below for the serialization counterpart of this. |
| 886 | // The root starts at the end of the buffer, so we parse backwards from there. |
| 887 | auto end = buffer + size; |
| 888 | auto byte_width = *--end; |
| 889 | auto packed_type = *--end; |
| 890 | end -= byte_width; // The root data item. |
| 891 | return Reference(end, byte_width, packed_type); |
| 892 | } |
| 893 | |
| 894 | inline Reference GetRoot(const std::vector<uint8_t> &buffer) { |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 895 | return GetRoot(buffer.data(), buffer.size()); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 896 | } |
| 897 | |
| 898 | // Flags that configure how the Builder behaves. |
| 899 | // The "Share" flags determine if the Builder automatically tries to pool |
| 900 | // this type. Pooling can reduce the size of serialized data if there are |
| 901 | // multiple maps of the same kind, at the expense of slightly slower |
| 902 | // serialization (the cost of lookups) and more memory use (std::set). |
| 903 | // By default this is on for keys, but off for strings. |
| 904 | // Turn keys off if you have e.g. only one map. |
| 905 | // Turn strings on if you expect many non-unique string values. |
| 906 | // Additionally, sharing key vectors can save space if you have maps with |
| 907 | // identical field populations. |
| 908 | enum BuilderFlag { |
| 909 | BUILDER_FLAG_NONE = 0, |
| 910 | BUILDER_FLAG_SHARE_KEYS = 1, |
| 911 | BUILDER_FLAG_SHARE_STRINGS = 2, |
| 912 | BUILDER_FLAG_SHARE_KEYS_AND_STRINGS = 3, |
| 913 | BUILDER_FLAG_SHARE_KEY_VECTORS = 4, |
| 914 | BUILDER_FLAG_SHARE_ALL = 7, |
| 915 | }; |
| 916 | |
| 917 | class Builder FLATBUFFERS_FINAL_CLASS { |
| 918 | public: |
| 919 | Builder(size_t initial_size = 256, |
| 920 | BuilderFlag flags = BUILDER_FLAG_SHARE_KEYS) |
| 921 | : buf_(initial_size), |
| 922 | finished_(false), |
Austin Schuh | 58b9b47 | 2020-11-25 19:12:44 -0800 | [diff] [blame] | 923 | has_duplicate_keys_(false), |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 924 | flags_(flags), |
| 925 | force_min_bit_width_(BIT_WIDTH_8), |
| 926 | key_pool(KeyOffsetCompare(buf_)), |
| 927 | string_pool(StringOffsetCompare(buf_)) { |
| 928 | buf_.clear(); |
| 929 | } |
| 930 | |
Austin Schuh | 58b9b47 | 2020-11-25 19:12:44 -0800 | [diff] [blame] | 931 | #ifdef FLATBUFFERS_DEFAULT_DECLARATION |
| 932 | Builder(Builder &&) = default; |
| 933 | Builder &operator=(Builder &&) = default; |
| 934 | #endif |
| 935 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 936 | /// @brief Get the serialized buffer (after you call `Finish()`). |
| 937 | /// @return Returns a vector owned by this class. |
| 938 | const std::vector<uint8_t> &GetBuffer() const { |
| 939 | Finished(); |
| 940 | return buf_; |
| 941 | } |
| 942 | |
| 943 | // Size of the buffer. Does not include unfinished values. |
| 944 | size_t GetSize() const { return buf_.size(); } |
| 945 | |
| 946 | // Reset all state so we can re-use the buffer. |
| 947 | void Clear() { |
| 948 | buf_.clear(); |
| 949 | stack_.clear(); |
| 950 | finished_ = false; |
| 951 | // flags_ remains as-is; |
| 952 | force_min_bit_width_ = BIT_WIDTH_8; |
| 953 | key_pool.clear(); |
| 954 | string_pool.clear(); |
| 955 | } |
| 956 | |
| 957 | // All value constructing functions below have two versions: one that |
| 958 | // takes a key (for placement inside a map) and one that doesn't (for inside |
| 959 | // vectors and elsewhere). |
| 960 | |
| 961 | void Null() { stack_.push_back(Value()); } |
| 962 | void Null(const char *key) { |
| 963 | Key(key); |
| 964 | Null(); |
| 965 | } |
| 966 | |
| 967 | void Int(int64_t i) { stack_.push_back(Value(i, FBT_INT, WidthI(i))); } |
| 968 | void Int(const char *key, int64_t i) { |
| 969 | Key(key); |
| 970 | Int(i); |
| 971 | } |
| 972 | |
| 973 | void UInt(uint64_t u) { stack_.push_back(Value(u, FBT_UINT, WidthU(u))); } |
| 974 | void UInt(const char *key, uint64_t u) { |
| 975 | Key(key); |
| 976 | UInt(u); |
| 977 | } |
| 978 | |
| 979 | void Float(float f) { stack_.push_back(Value(f)); } |
| 980 | void Float(const char *key, float f) { |
| 981 | Key(key); |
| 982 | Float(f); |
| 983 | } |
| 984 | |
| 985 | void Double(double f) { stack_.push_back(Value(f)); } |
| 986 | void Double(const char *key, double d) { |
| 987 | Key(key); |
| 988 | Double(d); |
| 989 | } |
| 990 | |
| 991 | void Bool(bool b) { stack_.push_back(Value(b)); } |
| 992 | void Bool(const char *key, bool b) { |
| 993 | Key(key); |
| 994 | Bool(b); |
| 995 | } |
| 996 | |
| 997 | void IndirectInt(int64_t i) { PushIndirect(i, FBT_INDIRECT_INT, WidthI(i)); } |
| 998 | void IndirectInt(const char *key, int64_t i) { |
| 999 | Key(key); |
| 1000 | IndirectInt(i); |
| 1001 | } |
| 1002 | |
| 1003 | void IndirectUInt(uint64_t u) { |
| 1004 | PushIndirect(u, FBT_INDIRECT_UINT, WidthU(u)); |
| 1005 | } |
| 1006 | void IndirectUInt(const char *key, uint64_t u) { |
| 1007 | Key(key); |
| 1008 | IndirectUInt(u); |
| 1009 | } |
| 1010 | |
| 1011 | void IndirectFloat(float f) { |
| 1012 | PushIndirect(f, FBT_INDIRECT_FLOAT, BIT_WIDTH_32); |
| 1013 | } |
| 1014 | void IndirectFloat(const char *key, float f) { |
| 1015 | Key(key); |
| 1016 | IndirectFloat(f); |
| 1017 | } |
| 1018 | |
| 1019 | void IndirectDouble(double f) { |
| 1020 | PushIndirect(f, FBT_INDIRECT_FLOAT, WidthF(f)); |
| 1021 | } |
| 1022 | void IndirectDouble(const char *key, double d) { |
| 1023 | Key(key); |
| 1024 | IndirectDouble(d); |
| 1025 | } |
| 1026 | |
| 1027 | size_t Key(const char *str, size_t len) { |
| 1028 | auto sloc = buf_.size(); |
| 1029 | WriteBytes(str, len + 1); |
| 1030 | if (flags_ & BUILDER_FLAG_SHARE_KEYS) { |
| 1031 | auto it = key_pool.find(sloc); |
| 1032 | if (it != key_pool.end()) { |
| 1033 | // Already in the buffer. Remove key we just serialized, and use |
| 1034 | // existing offset instead. |
| 1035 | buf_.resize(sloc); |
| 1036 | sloc = *it; |
| 1037 | } else { |
| 1038 | key_pool.insert(sloc); |
| 1039 | } |
| 1040 | } |
| 1041 | stack_.push_back(Value(static_cast<uint64_t>(sloc), FBT_KEY, BIT_WIDTH_8)); |
| 1042 | return sloc; |
| 1043 | } |
| 1044 | |
| 1045 | size_t Key(const char *str) { return Key(str, strlen(str)); } |
| 1046 | size_t Key(const std::string &str) { return Key(str.c_str(), str.size()); } |
| 1047 | |
| 1048 | size_t String(const char *str, size_t len) { |
| 1049 | auto reset_to = buf_.size(); |
| 1050 | auto sloc = CreateBlob(str, len, 1, FBT_STRING); |
| 1051 | if (flags_ & BUILDER_FLAG_SHARE_STRINGS) { |
| 1052 | StringOffset so(sloc, len); |
| 1053 | auto it = string_pool.find(so); |
| 1054 | if (it != string_pool.end()) { |
| 1055 | // Already in the buffer. Remove string we just serialized, and use |
| 1056 | // existing offset instead. |
| 1057 | buf_.resize(reset_to); |
| 1058 | sloc = it->first; |
| 1059 | stack_.back().u_ = sloc; |
| 1060 | } else { |
| 1061 | string_pool.insert(so); |
| 1062 | } |
| 1063 | } |
| 1064 | return sloc; |
| 1065 | } |
| 1066 | size_t String(const char *str) { return String(str, strlen(str)); } |
| 1067 | size_t String(const std::string &str) { |
| 1068 | return String(str.c_str(), str.size()); |
| 1069 | } |
| 1070 | void String(const flexbuffers::String &str) { |
| 1071 | String(str.c_str(), str.length()); |
| 1072 | } |
| 1073 | |
| 1074 | void String(const char *key, const char *str) { |
| 1075 | Key(key); |
| 1076 | String(str); |
| 1077 | } |
| 1078 | void String(const char *key, const std::string &str) { |
| 1079 | Key(key); |
| 1080 | String(str); |
| 1081 | } |
| 1082 | void String(const char *key, const flexbuffers::String &str) { |
| 1083 | Key(key); |
| 1084 | String(str); |
| 1085 | } |
| 1086 | |
| 1087 | size_t Blob(const void *data, size_t len) { |
| 1088 | return CreateBlob(data, len, 0, FBT_BLOB); |
| 1089 | } |
| 1090 | size_t Blob(const std::vector<uint8_t> &v) { |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1091 | return CreateBlob(v.data(), v.size(), 0, FBT_BLOB); |
| 1092 | } |
| 1093 | |
| 1094 | void Blob(const char *key, const void *data, size_t len) { |
| 1095 | Key(key); |
| 1096 | Blob(data, len); |
| 1097 | } |
| 1098 | void Blob(const char *key, const std::vector<uint8_t> &v) { |
| 1099 | Key(key); |
| 1100 | Blob(v); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1101 | } |
| 1102 | |
| 1103 | // TODO(wvo): support all the FlexBuffer types (like flexbuffers::String), |
| 1104 | // e.g. Vector etc. Also in overloaded versions. |
| 1105 | // Also some FlatBuffers types? |
| 1106 | |
| 1107 | size_t StartVector() { return stack_.size(); } |
| 1108 | size_t StartVector(const char *key) { |
| 1109 | Key(key); |
| 1110 | return stack_.size(); |
| 1111 | } |
| 1112 | size_t StartMap() { return stack_.size(); } |
| 1113 | size_t StartMap(const char *key) { |
| 1114 | Key(key); |
| 1115 | return stack_.size(); |
| 1116 | } |
| 1117 | |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1118 | // TODO(wvo): allow this to specify an alignment greater than the natural |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1119 | // alignment. |
| 1120 | size_t EndVector(size_t start, bool typed, bool fixed) { |
| 1121 | auto vec = CreateVector(start, stack_.size() - start, 1, typed, fixed); |
| 1122 | // Remove temp elements and return vector. |
| 1123 | stack_.resize(start); |
| 1124 | stack_.push_back(vec); |
| 1125 | return static_cast<size_t>(vec.u_); |
| 1126 | } |
| 1127 | |
| 1128 | size_t EndMap(size_t start) { |
| 1129 | // We should have interleaved keys and values on the stack. |
| 1130 | // Make sure it is an even number: |
| 1131 | auto len = stack_.size() - start; |
| 1132 | FLATBUFFERS_ASSERT(!(len & 1)); |
| 1133 | len /= 2; |
| 1134 | // Make sure keys are all strings: |
| 1135 | for (auto key = start; key < stack_.size(); key += 2) { |
| 1136 | FLATBUFFERS_ASSERT(stack_[key].type_ == FBT_KEY); |
| 1137 | } |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1138 | // Now sort values, so later we can do a binary search lookup. |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1139 | // We want to sort 2 array elements at a time. |
| 1140 | struct TwoValue { |
| 1141 | Value key; |
| 1142 | Value val; |
| 1143 | }; |
| 1144 | // TODO(wvo): strict aliasing? |
| 1145 | // TODO(wvo): allow the caller to indicate the data is already sorted |
| 1146 | // for maximum efficiency? With an assert to check sortedness to make sure |
| 1147 | // we're not breaking binary search. |
| 1148 | // Or, we can track if the map is sorted as keys are added which would be |
| 1149 | // be quite cheap (cheaper than checking it here), so we can skip this |
| 1150 | // step automatically when appliccable, and encourage people to write in |
| 1151 | // sorted fashion. |
| 1152 | // std::sort is typically already a lot faster on sorted data though. |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1153 | auto dict = reinterpret_cast<TwoValue *>(stack_.data() + start); |
| 1154 | std::sort( |
| 1155 | dict, dict + len, [&](const TwoValue &a, const TwoValue &b) -> bool { |
| 1156 | auto as = reinterpret_cast<const char *>(buf_.data() + a.key.u_); |
| 1157 | auto bs = reinterpret_cast<const char *>(buf_.data() + b.key.u_); |
| 1158 | auto comp = strcmp(as, bs); |
| 1159 | // We want to disallow duplicate keys, since this results in a |
| 1160 | // map where values cannot be found. |
| 1161 | // But we can't assert here (since we don't want to fail on |
| 1162 | // random JSON input) or have an error mechanism. |
| 1163 | // Instead, we set has_duplicate_keys_ in the builder to |
| 1164 | // signal this. |
| 1165 | // TODO: Have to check for pointer equality, as some sort |
| 1166 | // implementation apparently call this function with the same |
| 1167 | // element?? Why? |
| 1168 | if (!comp && &a != &b) has_duplicate_keys_ = true; |
| 1169 | return comp < 0; |
| 1170 | }); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1171 | // First create a vector out of all keys. |
| 1172 | // TODO(wvo): if kBuilderFlagShareKeyVectors is true, see if we can share |
| 1173 | // the first vector. |
| 1174 | auto keys = CreateVector(start, len, 2, true, false); |
| 1175 | auto vec = CreateVector(start + 1, len, 2, false, false, &keys); |
| 1176 | // Remove temp elements and return map. |
| 1177 | stack_.resize(start); |
| 1178 | stack_.push_back(vec); |
| 1179 | return static_cast<size_t>(vec.u_); |
| 1180 | } |
| 1181 | |
Austin Schuh | 58b9b47 | 2020-11-25 19:12:44 -0800 | [diff] [blame] | 1182 | // Call this after EndMap to see if the map had any duplicate keys. |
| 1183 | // Any map with such keys won't be able to retrieve all values. |
| 1184 | bool HasDuplicateKeys() const { return has_duplicate_keys_; } |
| 1185 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1186 | template<typename F> size_t Vector(F f) { |
| 1187 | auto start = StartVector(); |
| 1188 | f(); |
| 1189 | return EndVector(start, false, false); |
| 1190 | } |
| 1191 | template<typename F, typename T> size_t Vector(F f, T &state) { |
| 1192 | auto start = StartVector(); |
| 1193 | f(state); |
| 1194 | return EndVector(start, false, false); |
| 1195 | } |
| 1196 | template<typename F> size_t Vector(const char *key, F f) { |
| 1197 | auto start = StartVector(key); |
| 1198 | f(); |
| 1199 | return EndVector(start, false, false); |
| 1200 | } |
| 1201 | template<typename F, typename T> |
| 1202 | size_t Vector(const char *key, F f, T &state) { |
| 1203 | auto start = StartVector(key); |
| 1204 | f(state); |
| 1205 | return EndVector(start, false, false); |
| 1206 | } |
| 1207 | |
| 1208 | template<typename T> void Vector(const T *elems, size_t len) { |
| 1209 | if (flatbuffers::is_scalar<T>::value) { |
| 1210 | // This path should be a lot quicker and use less space. |
| 1211 | ScalarVector(elems, len, false); |
| 1212 | } else { |
| 1213 | auto start = StartVector(); |
| 1214 | for (size_t i = 0; i < len; i++) Add(elems[i]); |
| 1215 | EndVector(start, false, false); |
| 1216 | } |
| 1217 | } |
| 1218 | template<typename T> |
| 1219 | void Vector(const char *key, const T *elems, size_t len) { |
| 1220 | Key(key); |
| 1221 | Vector(elems, len); |
| 1222 | } |
| 1223 | template<typename T> void Vector(const std::vector<T> &vec) { |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1224 | Vector(vec.data(), vec.size()); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1225 | } |
| 1226 | |
| 1227 | template<typename F> size_t TypedVector(F f) { |
| 1228 | auto start = StartVector(); |
| 1229 | f(); |
| 1230 | return EndVector(start, true, false); |
| 1231 | } |
| 1232 | template<typename F, typename T> size_t TypedVector(F f, T &state) { |
| 1233 | auto start = StartVector(); |
| 1234 | f(state); |
| 1235 | return EndVector(start, true, false); |
| 1236 | } |
| 1237 | template<typename F> size_t TypedVector(const char *key, F f) { |
| 1238 | auto start = StartVector(key); |
| 1239 | f(); |
| 1240 | return EndVector(start, true, false); |
| 1241 | } |
| 1242 | template<typename F, typename T> |
| 1243 | size_t TypedVector(const char *key, F f, T &state) { |
| 1244 | auto start = StartVector(key); |
| 1245 | f(state); |
| 1246 | return EndVector(start, true, false); |
| 1247 | } |
| 1248 | |
| 1249 | template<typename T> size_t FixedTypedVector(const T *elems, size_t len) { |
| 1250 | // We only support a few fixed vector lengths. Anything bigger use a |
| 1251 | // regular typed vector. |
| 1252 | FLATBUFFERS_ASSERT(len >= 2 && len <= 4); |
| 1253 | // And only scalar values. |
| 1254 | static_assert(flatbuffers::is_scalar<T>::value, "Unrelated types"); |
| 1255 | return ScalarVector(elems, len, true); |
| 1256 | } |
| 1257 | |
| 1258 | template<typename T> |
| 1259 | size_t FixedTypedVector(const char *key, const T *elems, size_t len) { |
| 1260 | Key(key); |
| 1261 | return FixedTypedVector(elems, len); |
| 1262 | } |
| 1263 | |
| 1264 | template<typename F> size_t Map(F f) { |
| 1265 | auto start = StartMap(); |
| 1266 | f(); |
| 1267 | return EndMap(start); |
| 1268 | } |
| 1269 | template<typename F, typename T> size_t Map(F f, T &state) { |
| 1270 | auto start = StartMap(); |
| 1271 | f(state); |
| 1272 | return EndMap(start); |
| 1273 | } |
| 1274 | template<typename F> size_t Map(const char *key, F f) { |
| 1275 | auto start = StartMap(key); |
| 1276 | f(); |
| 1277 | return EndMap(start); |
| 1278 | } |
| 1279 | template<typename F, typename T> size_t Map(const char *key, F f, T &state) { |
| 1280 | auto start = StartMap(key); |
| 1281 | f(state); |
| 1282 | return EndMap(start); |
| 1283 | } |
| 1284 | template<typename T> void Map(const std::map<std::string, T> &map) { |
| 1285 | auto start = StartMap(); |
| 1286 | for (auto it = map.begin(); it != map.end(); ++it) |
| 1287 | Add(it->first.c_str(), it->second); |
| 1288 | EndMap(start); |
| 1289 | } |
| 1290 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1291 | // If you wish to share a value explicitly (a value not shared automatically |
| 1292 | // through one of the BUILDER_FLAG_SHARE_* flags) you can do so with these |
| 1293 | // functions. Or if you wish to turn those flags off for performance reasons |
| 1294 | // and still do some explicit sharing. For example: |
| 1295 | // builder.IndirectDouble(M_PI); |
| 1296 | // auto id = builder.LastValue(); // Remember where we stored it. |
| 1297 | // .. more code goes here .. |
| 1298 | // builder.ReuseValue(id); // Refers to same double by offset. |
| 1299 | // LastValue works regardless of whether the value has a key or not. |
| 1300 | // Works on any data type. |
| 1301 | struct Value; |
| 1302 | Value LastValue() { return stack_.back(); } |
| 1303 | void ReuseValue(Value v) { stack_.push_back(v); } |
| 1304 | void ReuseValue(const char *key, Value v) { |
| 1305 | Key(key); |
| 1306 | ReuseValue(v); |
| 1307 | } |
| 1308 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1309 | // Overloaded Add that tries to call the correct function above. |
| 1310 | void Add(int8_t i) { Int(i); } |
| 1311 | void Add(int16_t i) { Int(i); } |
| 1312 | void Add(int32_t i) { Int(i); } |
| 1313 | void Add(int64_t i) { Int(i); } |
| 1314 | void Add(uint8_t u) { UInt(u); } |
| 1315 | void Add(uint16_t u) { UInt(u); } |
| 1316 | void Add(uint32_t u) { UInt(u); } |
| 1317 | void Add(uint64_t u) { UInt(u); } |
| 1318 | void Add(float f) { Float(f); } |
| 1319 | void Add(double d) { Double(d); } |
| 1320 | void Add(bool b) { Bool(b); } |
| 1321 | void Add(const char *str) { String(str); } |
| 1322 | void Add(const std::string &str) { String(str); } |
| 1323 | void Add(const flexbuffers::String &str) { String(str); } |
| 1324 | |
| 1325 | template<typename T> void Add(const std::vector<T> &vec) { Vector(vec); } |
| 1326 | |
| 1327 | template<typename T> void Add(const char *key, const T &t) { |
| 1328 | Key(key); |
| 1329 | Add(t); |
| 1330 | } |
| 1331 | |
| 1332 | template<typename T> void Add(const std::map<std::string, T> &map) { |
| 1333 | Map(map); |
| 1334 | } |
| 1335 | |
| 1336 | template<typename T> void operator+=(const T &t) { Add(t); } |
| 1337 | |
| 1338 | // This function is useful in combination with the Mutate* functions above. |
| 1339 | // It forces elements of vectors and maps to have a minimum size, such that |
| 1340 | // they can later be updated without failing. |
| 1341 | // Call with no arguments to reset. |
| 1342 | void ForceMinimumBitWidth(BitWidth bw = BIT_WIDTH_8) { |
| 1343 | force_min_bit_width_ = bw; |
| 1344 | } |
| 1345 | |
| 1346 | void Finish() { |
| 1347 | // If you hit this assert, you likely have objects that were never included |
| 1348 | // in a parent. You need to have exactly one root to finish a buffer. |
| 1349 | // Check your Start/End calls are matched, and all objects are inside |
| 1350 | // some other object. |
| 1351 | FLATBUFFERS_ASSERT(stack_.size() == 1); |
| 1352 | |
| 1353 | // Write root value. |
| 1354 | auto byte_width = Align(stack_[0].ElemWidth(buf_.size(), 0)); |
| 1355 | WriteAny(stack_[0], byte_width); |
| 1356 | // Write root type. |
| 1357 | Write(stack_[0].StoredPackedType(), 1); |
| 1358 | // Write root size. Normally determined by parent, but root has no parent :) |
| 1359 | Write(byte_width, 1); |
| 1360 | |
| 1361 | finished_ = true; |
| 1362 | } |
| 1363 | |
| 1364 | private: |
| 1365 | void Finished() const { |
| 1366 | // If you get this assert, you're attempting to get access a buffer |
| 1367 | // which hasn't been finished yet. Be sure to call |
| 1368 | // Builder::Finish with your root object. |
| 1369 | FLATBUFFERS_ASSERT(finished_); |
| 1370 | } |
| 1371 | |
| 1372 | // Align to prepare for writing a scalar with a certain size. |
| 1373 | uint8_t Align(BitWidth alignment) { |
| 1374 | auto byte_width = 1U << alignment; |
| 1375 | buf_.insert(buf_.end(), flatbuffers::PaddingBytes(buf_.size(), byte_width), |
| 1376 | 0); |
| 1377 | return static_cast<uint8_t>(byte_width); |
| 1378 | } |
| 1379 | |
| 1380 | void WriteBytes(const void *val, size_t size) { |
| 1381 | buf_.insert(buf_.end(), reinterpret_cast<const uint8_t *>(val), |
| 1382 | reinterpret_cast<const uint8_t *>(val) + size); |
| 1383 | } |
| 1384 | |
| 1385 | template<typename T> void Write(T val, size_t byte_width) { |
| 1386 | FLATBUFFERS_ASSERT(sizeof(T) >= byte_width); |
| 1387 | val = flatbuffers::EndianScalar(val); |
| 1388 | WriteBytes(&val, byte_width); |
| 1389 | } |
| 1390 | |
| 1391 | void WriteDouble(double f, uint8_t byte_width) { |
| 1392 | switch (byte_width) { |
| 1393 | case 8: Write(f, byte_width); break; |
| 1394 | case 4: Write(static_cast<float>(f), byte_width); break; |
| 1395 | // case 2: Write(static_cast<half>(f), byte_width); break; |
| 1396 | // case 1: Write(static_cast<quarter>(f), byte_width); break; |
| 1397 | default: FLATBUFFERS_ASSERT(0); |
| 1398 | } |
| 1399 | } |
| 1400 | |
| 1401 | void WriteOffset(uint64_t o, uint8_t byte_width) { |
| 1402 | auto reloff = buf_.size() - o; |
| 1403 | FLATBUFFERS_ASSERT(byte_width == 8 || reloff < 1ULL << (byte_width * 8)); |
| 1404 | Write(reloff, byte_width); |
| 1405 | } |
| 1406 | |
| 1407 | template<typename T> void PushIndirect(T val, Type type, BitWidth bit_width) { |
| 1408 | auto byte_width = Align(bit_width); |
| 1409 | auto iloc = buf_.size(); |
| 1410 | Write(val, byte_width); |
| 1411 | stack_.push_back(Value(static_cast<uint64_t>(iloc), type, bit_width)); |
| 1412 | } |
| 1413 | |
| 1414 | static BitWidth WidthB(size_t byte_width) { |
| 1415 | switch (byte_width) { |
| 1416 | case 1: return BIT_WIDTH_8; |
| 1417 | case 2: return BIT_WIDTH_16; |
| 1418 | case 4: return BIT_WIDTH_32; |
| 1419 | case 8: return BIT_WIDTH_64; |
| 1420 | default: FLATBUFFERS_ASSERT(false); return BIT_WIDTH_64; |
| 1421 | } |
| 1422 | } |
| 1423 | |
| 1424 | template<typename T> static Type GetScalarType() { |
| 1425 | static_assert(flatbuffers::is_scalar<T>::value, "Unrelated types"); |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 1426 | return flatbuffers::is_floating_point<T>::value |
| 1427 | ? FBT_FLOAT |
| 1428 | : flatbuffers::is_same<T, bool>::value |
| 1429 | ? FBT_BOOL |
| 1430 | : (flatbuffers::is_unsigned<T>::value ? FBT_UINT |
| 1431 | : FBT_INT); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1432 | } |
| 1433 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1434 | public: |
| 1435 | // This was really intended to be private, except for LastValue/ReuseValue. |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1436 | struct Value { |
| 1437 | union { |
| 1438 | int64_t i_; |
| 1439 | uint64_t u_; |
| 1440 | double f_; |
| 1441 | }; |
| 1442 | |
| 1443 | Type type_; |
| 1444 | |
| 1445 | // For scalars: of itself, for vector: of its elements, for string: length. |
| 1446 | BitWidth min_bit_width_; |
| 1447 | |
| 1448 | Value() : i_(0), type_(FBT_NULL), min_bit_width_(BIT_WIDTH_8) {} |
| 1449 | |
| 1450 | Value(bool b) |
| 1451 | : u_(static_cast<uint64_t>(b)), |
| 1452 | type_(FBT_BOOL), |
| 1453 | min_bit_width_(BIT_WIDTH_8) {} |
| 1454 | |
| 1455 | Value(int64_t i, Type t, BitWidth bw) |
| 1456 | : i_(i), type_(t), min_bit_width_(bw) {} |
| 1457 | Value(uint64_t u, Type t, BitWidth bw) |
| 1458 | : u_(u), type_(t), min_bit_width_(bw) {} |
| 1459 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1460 | Value(float f) |
| 1461 | : f_(static_cast<double>(f)), |
| 1462 | type_(FBT_FLOAT), |
| 1463 | min_bit_width_(BIT_WIDTH_32) {} |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1464 | Value(double f) : f_(f), type_(FBT_FLOAT), min_bit_width_(WidthF(f)) {} |
| 1465 | |
| 1466 | uint8_t StoredPackedType(BitWidth parent_bit_width_ = BIT_WIDTH_8) const { |
| 1467 | return PackedType(StoredWidth(parent_bit_width_), type_); |
| 1468 | } |
| 1469 | |
| 1470 | BitWidth ElemWidth(size_t buf_size, size_t elem_index) const { |
| 1471 | if (IsInline(type_)) { |
| 1472 | return min_bit_width_; |
| 1473 | } else { |
| 1474 | // We have an absolute offset, but want to store a relative offset |
| 1475 | // elem_index elements beyond the current buffer end. Since whether |
| 1476 | // the relative offset fits in a certain byte_width depends on |
| 1477 | // the size of the elements before it (and their alignment), we have |
| 1478 | // to test for each size in turn. |
| 1479 | for (size_t byte_width = 1; |
| 1480 | byte_width <= sizeof(flatbuffers::largest_scalar_t); |
| 1481 | byte_width *= 2) { |
| 1482 | // Where are we going to write this offset? |
| 1483 | auto offset_loc = buf_size + |
| 1484 | flatbuffers::PaddingBytes(buf_size, byte_width) + |
| 1485 | elem_index * byte_width; |
| 1486 | // Compute relative offset. |
| 1487 | auto offset = offset_loc - u_; |
| 1488 | // Does it fit? |
| 1489 | auto bit_width = WidthU(offset); |
| 1490 | if (static_cast<size_t>(static_cast<size_t>(1U) << bit_width) == |
| 1491 | byte_width) |
| 1492 | return bit_width; |
| 1493 | } |
| 1494 | FLATBUFFERS_ASSERT(false); // Must match one of the sizes above. |
| 1495 | return BIT_WIDTH_64; |
| 1496 | } |
| 1497 | } |
| 1498 | |
| 1499 | BitWidth StoredWidth(BitWidth parent_bit_width_ = BIT_WIDTH_8) const { |
| 1500 | if (IsInline(type_)) { |
| 1501 | return (std::max)(min_bit_width_, parent_bit_width_); |
| 1502 | } else { |
| 1503 | return min_bit_width_; |
| 1504 | } |
| 1505 | } |
| 1506 | }; |
| 1507 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1508 | private: |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1509 | void WriteAny(const Value &val, uint8_t byte_width) { |
| 1510 | switch (val.type_) { |
| 1511 | case FBT_NULL: |
| 1512 | case FBT_INT: Write(val.i_, byte_width); break; |
| 1513 | case FBT_BOOL: |
| 1514 | case FBT_UINT: Write(val.u_, byte_width); break; |
| 1515 | case FBT_FLOAT: WriteDouble(val.f_, byte_width); break; |
| 1516 | default: WriteOffset(val.u_, byte_width); break; |
| 1517 | } |
| 1518 | } |
| 1519 | |
| 1520 | size_t CreateBlob(const void *data, size_t len, size_t trailing, Type type) { |
| 1521 | auto bit_width = WidthU(len); |
| 1522 | auto byte_width = Align(bit_width); |
| 1523 | Write<uint64_t>(len, byte_width); |
| 1524 | auto sloc = buf_.size(); |
| 1525 | WriteBytes(data, len + trailing); |
| 1526 | stack_.push_back(Value(static_cast<uint64_t>(sloc), type, bit_width)); |
| 1527 | return sloc; |
| 1528 | } |
| 1529 | |
| 1530 | template<typename T> |
| 1531 | size_t ScalarVector(const T *elems, size_t len, bool fixed) { |
| 1532 | auto vector_type = GetScalarType<T>(); |
| 1533 | auto byte_width = sizeof(T); |
| 1534 | auto bit_width = WidthB(byte_width); |
| 1535 | // If you get this assert, you're trying to write a vector with a size |
| 1536 | // field that is bigger than the scalars you're trying to write (e.g. a |
| 1537 | // byte vector > 255 elements). For such types, write a "blob" instead. |
| 1538 | // TODO: instead of asserting, could write vector with larger elements |
| 1539 | // instead, though that would be wasteful. |
| 1540 | FLATBUFFERS_ASSERT(WidthU(len) <= bit_width); |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1541 | Align(bit_width); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1542 | if (!fixed) Write<uint64_t>(len, byte_width); |
| 1543 | auto vloc = buf_.size(); |
| 1544 | for (size_t i = 0; i < len; i++) Write(elems[i], byte_width); |
| 1545 | stack_.push_back(Value(static_cast<uint64_t>(vloc), |
| 1546 | ToTypedVector(vector_type, fixed ? len : 0), |
| 1547 | bit_width)); |
| 1548 | return vloc; |
| 1549 | } |
| 1550 | |
| 1551 | Value CreateVector(size_t start, size_t vec_len, size_t step, bool typed, |
| 1552 | bool fixed, const Value *keys = nullptr) { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1553 | FLATBUFFERS_ASSERT( |
| 1554 | !fixed || |
| 1555 | typed); // typed=false, fixed=true combination is not supported. |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1556 | // Figure out smallest bit width we can store this vector with. |
| 1557 | auto bit_width = (std::max)(force_min_bit_width_, WidthU(vec_len)); |
| 1558 | auto prefix_elems = 1; |
| 1559 | if (keys) { |
| 1560 | // If this vector is part of a map, we will pre-fix an offset to the keys |
| 1561 | // to this vector. |
| 1562 | bit_width = (std::max)(bit_width, keys->ElemWidth(buf_.size(), 0)); |
| 1563 | prefix_elems += 2; |
| 1564 | } |
| 1565 | Type vector_type = FBT_KEY; |
| 1566 | // Check bit widths and types for all elements. |
| 1567 | for (size_t i = start; i < stack_.size(); i += step) { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1568 | auto elem_width = |
| 1569 | stack_[i].ElemWidth(buf_.size(), i - start + prefix_elems); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1570 | bit_width = (std::max)(bit_width, elem_width); |
| 1571 | if (typed) { |
| 1572 | if (i == start) { |
| 1573 | vector_type = stack_[i].type_; |
| 1574 | } else { |
| 1575 | // If you get this assert, you are writing a typed vector with |
| 1576 | // elements that are not all the same type. |
| 1577 | FLATBUFFERS_ASSERT(vector_type == stack_[i].type_); |
| 1578 | } |
| 1579 | } |
| 1580 | } |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1581 | // If you get this assert, your typed types are not one of: |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1582 | // Int / UInt / Float / Key. |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1583 | FLATBUFFERS_ASSERT(!typed || IsTypedVectorElementType(vector_type)); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1584 | auto byte_width = Align(bit_width); |
| 1585 | // Write vector. First the keys width/offset if available, and size. |
| 1586 | if (keys) { |
| 1587 | WriteOffset(keys->u_, byte_width); |
| 1588 | Write<uint64_t>(1ULL << keys->min_bit_width_, byte_width); |
| 1589 | } |
| 1590 | if (!fixed) Write<uint64_t>(vec_len, byte_width); |
| 1591 | // Then the actual data. |
| 1592 | auto vloc = buf_.size(); |
| 1593 | for (size_t i = start; i < stack_.size(); i += step) { |
| 1594 | WriteAny(stack_[i], byte_width); |
| 1595 | } |
| 1596 | // Then the types. |
| 1597 | if (!typed) { |
| 1598 | for (size_t i = start; i < stack_.size(); i += step) { |
| 1599 | buf_.push_back(stack_[i].StoredPackedType(bit_width)); |
| 1600 | } |
| 1601 | } |
| 1602 | return Value(static_cast<uint64_t>(vloc), |
| 1603 | keys ? FBT_MAP |
| 1604 | : (typed ? ToTypedVector(vector_type, fixed ? vec_len : 0) |
| 1605 | : FBT_VECTOR), |
| 1606 | bit_width); |
| 1607 | } |
| 1608 | |
| 1609 | // You shouldn't really be copying instances of this class. |
| 1610 | Builder(const Builder &); |
| 1611 | Builder &operator=(const Builder &); |
| 1612 | |
| 1613 | std::vector<uint8_t> buf_; |
| 1614 | std::vector<Value> stack_; |
| 1615 | |
| 1616 | bool finished_; |
Austin Schuh | 58b9b47 | 2020-11-25 19:12:44 -0800 | [diff] [blame] | 1617 | bool has_duplicate_keys_; |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1618 | |
| 1619 | BuilderFlag flags_; |
| 1620 | |
| 1621 | BitWidth force_min_bit_width_; |
| 1622 | |
| 1623 | struct KeyOffsetCompare { |
| 1624 | explicit KeyOffsetCompare(const std::vector<uint8_t> &buf) : buf_(&buf) {} |
| 1625 | bool operator()(size_t a, size_t b) const { |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1626 | auto stra = reinterpret_cast<const char *>(buf_->data() + a); |
| 1627 | auto strb = reinterpret_cast<const char *>(buf_->data() + b); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1628 | return strcmp(stra, strb) < 0; |
| 1629 | } |
| 1630 | const std::vector<uint8_t> *buf_; |
| 1631 | }; |
| 1632 | |
| 1633 | typedef std::pair<size_t, size_t> StringOffset; |
| 1634 | struct StringOffsetCompare { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1635 | explicit StringOffsetCompare(const std::vector<uint8_t> &buf) |
| 1636 | : buf_(&buf) {} |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1637 | bool operator()(const StringOffset &a, const StringOffset &b) const { |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1638 | auto stra = buf_->data() + a.first; |
| 1639 | auto strb = buf_->data() + b.first; |
| 1640 | auto cr = memcmp(stra, strb, (std::min)(a.second, b.second) + 1); |
| 1641 | return cr < 0 || (cr == 0 && a.second < b.second); |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1642 | } |
| 1643 | const std::vector<uint8_t> *buf_; |
| 1644 | }; |
| 1645 | |
| 1646 | typedef std::set<size_t, KeyOffsetCompare> KeyOffsetMap; |
| 1647 | typedef std::set<StringOffset, StringOffsetCompare> StringOffsetMap; |
| 1648 | |
| 1649 | KeyOffsetMap key_pool; |
| 1650 | StringOffsetMap string_pool; |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1651 | |
| 1652 | friend class Verifier; |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1653 | }; |
| 1654 | |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1655 | // Helper class to verify the integrity of a FlexBuffer |
| 1656 | class Verifier FLATBUFFERS_FINAL_CLASS { |
| 1657 | public: |
| 1658 | Verifier(const uint8_t *buf, size_t buf_len, |
| 1659 | // Supplying this vector likely results in faster verification |
| 1660 | // of larger buffers with many shared keys/strings, but |
| 1661 | // comes at the cost of using additional memory the same size of |
| 1662 | // the buffer being verified, so it is by default off. |
| 1663 | std::vector<uint8_t> *reuse_tracker = nullptr, |
| 1664 | bool _check_alignment = true, size_t max_depth = 64) |
| 1665 | : buf_(buf), |
| 1666 | size_(buf_len), |
| 1667 | depth_(0), |
| 1668 | max_depth_(max_depth), |
| 1669 | num_vectors_(0), |
| 1670 | max_vectors_(buf_len), |
| 1671 | check_alignment_(_check_alignment), |
| 1672 | reuse_tracker_(reuse_tracker) { |
| 1673 | FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE); |
| 1674 | if (reuse_tracker_) { |
| 1675 | reuse_tracker_->clear(); |
| 1676 | reuse_tracker_->resize(size_, PackedType(BIT_WIDTH_8, FBT_NULL)); |
| 1677 | } |
| 1678 | } |
| 1679 | |
| 1680 | private: |
| 1681 | // Central location where any verification failures register. |
| 1682 | bool Check(bool ok) const { |
| 1683 | // clang-format off |
| 1684 | #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE |
| 1685 | FLATBUFFERS_ASSERT(ok); |
| 1686 | #endif |
| 1687 | // clang-format on |
| 1688 | return ok; |
| 1689 | } |
| 1690 | |
| 1691 | // Verify any range within the buffer. |
| 1692 | bool VerifyFrom(size_t elem, size_t elem_len) const { |
| 1693 | return Check(elem_len < size_ && elem <= size_ - elem_len); |
| 1694 | } |
| 1695 | bool VerifyBefore(size_t elem, size_t elem_len) const { |
| 1696 | return Check(elem_len <= elem); |
| 1697 | } |
| 1698 | |
| 1699 | bool VerifyFromPointer(const uint8_t *p, size_t len) { |
| 1700 | auto o = static_cast<size_t>(p - buf_); |
| 1701 | return VerifyFrom(o, len); |
| 1702 | } |
| 1703 | bool VerifyBeforePointer(const uint8_t *p, size_t len) { |
| 1704 | auto o = static_cast<size_t>(p - buf_); |
| 1705 | return VerifyBefore(o, len); |
| 1706 | } |
| 1707 | |
| 1708 | bool VerifyByteWidth(size_t width) { |
| 1709 | return Check(width == 1 || width == 2 || width == 4 || width == 8); |
| 1710 | } |
| 1711 | |
| 1712 | bool VerifyType(int type) { return Check(type >= 0 && type < FBT_MAX_TYPE); } |
| 1713 | |
| 1714 | bool VerifyOffset(uint64_t off, const uint8_t *p) { |
| 1715 | return Check(off <= static_cast<uint64_t>(size_)) && |
| 1716 | off <= static_cast<uint64_t>(p - buf_); |
| 1717 | } |
| 1718 | |
| 1719 | bool VerifyAlignment(const uint8_t *p, size_t size) const { |
| 1720 | auto o = static_cast<size_t>(p - buf_); |
| 1721 | return Check((o & (size - 1)) == 0 || !check_alignment_); |
| 1722 | } |
| 1723 | |
| 1724 | // Macro, since we want to escape from parent function & use lazy args. |
| 1725 | #define FLEX_CHECK_VERIFIED(P, PACKED_TYPE) \ |
| 1726 | if (reuse_tracker_) { \ |
| 1727 | auto packed_type = PACKED_TYPE; \ |
| 1728 | auto existing = (*reuse_tracker_)[P - buf_]; \ |
| 1729 | if (existing == packed_type) return true; \ |
| 1730 | /* Fail verification if already set with different type! */ \ |
| 1731 | if (!Check(existing == 0)) return false; \ |
| 1732 | (*reuse_tracker_)[P - buf_] = packed_type; \ |
| 1733 | } |
| 1734 | |
| 1735 | bool VerifyVector(Reference r, const uint8_t *p, Type elem_type) { |
| 1736 | // Any kind of nesting goes thru this function, so guard against that |
| 1737 | // here, both with simple nesting checks, and the reuse tracker if on. |
| 1738 | depth_++; |
| 1739 | num_vectors_++; |
| 1740 | if (!Check(depth_ <= max_depth_ && num_vectors_ <= max_vectors_)) |
| 1741 | return false; |
| 1742 | auto size_byte_width = r.byte_width_; |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1743 | if (!VerifyBeforePointer(p, size_byte_width)) return false; |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 1744 | FLEX_CHECK_VERIFIED(p - size_byte_width, |
| 1745 | PackedType(Builder::WidthB(size_byte_width), r.type_)); |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1746 | auto sized = Sized(p, size_byte_width); |
| 1747 | auto num_elems = sized.size(); |
| 1748 | auto elem_byte_width = r.type_ == FBT_STRING || r.type_ == FBT_BLOB |
| 1749 | ? uint8_t(1) |
| 1750 | : r.byte_width_; |
| 1751 | auto max_elems = SIZE_MAX / elem_byte_width; |
| 1752 | if (!Check(num_elems < max_elems)) |
| 1753 | return false; // Protect against byte_size overflowing. |
| 1754 | auto byte_size = num_elems * elem_byte_width; |
| 1755 | if (!VerifyFromPointer(p, byte_size)) return false; |
| 1756 | if (elem_type == FBT_NULL) { |
| 1757 | // Verify type bytes after the vector. |
| 1758 | if (!VerifyFromPointer(p + byte_size, num_elems)) return false; |
| 1759 | auto v = Vector(p, size_byte_width); |
| 1760 | for (size_t i = 0; i < num_elems; i++) |
| 1761 | if (!VerifyRef(v[i])) return false; |
| 1762 | } else if (elem_type == FBT_KEY) { |
| 1763 | auto v = TypedVector(p, elem_byte_width, FBT_KEY); |
| 1764 | for (size_t i = 0; i < num_elems; i++) |
| 1765 | if (!VerifyRef(v[i])) return false; |
| 1766 | } else { |
| 1767 | FLATBUFFERS_ASSERT(IsInline(elem_type)); |
| 1768 | } |
| 1769 | depth_--; |
| 1770 | return true; |
| 1771 | } |
| 1772 | |
| 1773 | bool VerifyKeys(const uint8_t *p, uint8_t byte_width) { |
| 1774 | // The vector part of the map has already been verified. |
| 1775 | const size_t num_prefixed_fields = 3; |
| 1776 | if (!VerifyBeforePointer(p, byte_width * num_prefixed_fields)) return false; |
| 1777 | p -= byte_width * num_prefixed_fields; |
| 1778 | auto off = ReadUInt64(p, byte_width); |
| 1779 | if (!VerifyOffset(off, p)) return false; |
| 1780 | auto key_byte_with = |
| 1781 | static_cast<uint8_t>(ReadUInt64(p + byte_width, byte_width)); |
| 1782 | if (!VerifyByteWidth(key_byte_with)) return false; |
| 1783 | return VerifyVector(Reference(p, byte_width, key_byte_with, FBT_VECTOR_KEY), |
| 1784 | p - off, FBT_KEY); |
| 1785 | } |
| 1786 | |
| 1787 | bool VerifyKey(const uint8_t *p) { |
| 1788 | FLEX_CHECK_VERIFIED(p, PackedType(BIT_WIDTH_8, FBT_KEY)); |
| 1789 | while (p < buf_ + size_) |
| 1790 | if (*p++) return true; |
| 1791 | return false; |
| 1792 | } |
| 1793 | |
| 1794 | #undef FLEX_CHECK_VERIFIED |
| 1795 | |
| 1796 | bool VerifyTerminator(const String &s) { |
| 1797 | return VerifyFromPointer(reinterpret_cast<const uint8_t *>(s.c_str()), |
| 1798 | s.size() + 1); |
| 1799 | } |
| 1800 | |
| 1801 | bool VerifyRef(Reference r) { |
| 1802 | // r.parent_width_ and r.data_ already verified. |
| 1803 | if (!VerifyByteWidth(r.byte_width_) || !VerifyType(r.type_)) { |
| 1804 | return false; |
| 1805 | } |
| 1806 | if (IsInline(r.type_)) { |
| 1807 | // Inline scalars, don't require further verification. |
| 1808 | return true; |
| 1809 | } |
| 1810 | // All remaining types are an offset. |
| 1811 | auto off = ReadUInt64(r.data_, r.parent_width_); |
| 1812 | if (!VerifyOffset(off, r.data_)) return false; |
| 1813 | auto p = r.Indirect(); |
| 1814 | if (!VerifyAlignment(p, r.byte_width_)) return false; |
| 1815 | switch (r.type_) { |
| 1816 | case FBT_INDIRECT_INT: |
| 1817 | case FBT_INDIRECT_UINT: |
| 1818 | case FBT_INDIRECT_FLOAT: return VerifyFromPointer(p, r.byte_width_); |
| 1819 | case FBT_KEY: return VerifyKey(p); |
| 1820 | case FBT_MAP: |
| 1821 | return VerifyVector(r, p, FBT_NULL) && VerifyKeys(p, r.byte_width_); |
| 1822 | case FBT_VECTOR: return VerifyVector(r, p, FBT_NULL); |
| 1823 | case FBT_VECTOR_INT: return VerifyVector(r, p, FBT_INT); |
| 1824 | case FBT_VECTOR_BOOL: |
| 1825 | case FBT_VECTOR_UINT: return VerifyVector(r, p, FBT_UINT); |
| 1826 | case FBT_VECTOR_FLOAT: return VerifyVector(r, p, FBT_FLOAT); |
| 1827 | case FBT_VECTOR_KEY: return VerifyVector(r, p, FBT_KEY); |
| 1828 | case FBT_VECTOR_STRING_DEPRECATED: |
| 1829 | // Use of FBT_KEY here intentional, see elsewhere. |
| 1830 | return VerifyVector(r, p, FBT_KEY); |
| 1831 | case FBT_BLOB: return VerifyVector(r, p, FBT_UINT); |
| 1832 | case FBT_STRING: |
| 1833 | return VerifyVector(r, p, FBT_UINT) && |
| 1834 | VerifyTerminator(String(p, r.byte_width_)); |
| 1835 | case FBT_VECTOR_INT2: |
| 1836 | case FBT_VECTOR_UINT2: |
| 1837 | case FBT_VECTOR_FLOAT2: |
| 1838 | case FBT_VECTOR_INT3: |
| 1839 | case FBT_VECTOR_UINT3: |
| 1840 | case FBT_VECTOR_FLOAT3: |
| 1841 | case FBT_VECTOR_INT4: |
| 1842 | case FBT_VECTOR_UINT4: |
| 1843 | case FBT_VECTOR_FLOAT4: { |
| 1844 | uint8_t len = 0; |
| 1845 | auto vtype = ToFixedTypedVectorElementType(r.type_, &len); |
| 1846 | if (!VerifyType(vtype)) return false; |
| 1847 | return VerifyFromPointer(p, r.byte_width_ * len); |
| 1848 | } |
| 1849 | default: return false; |
| 1850 | } |
| 1851 | } |
| 1852 | |
| 1853 | public: |
| 1854 | bool VerifyBuffer() { |
| 1855 | if (!Check(size_ >= 3)) return false; |
| 1856 | auto end = buf_ + size_; |
| 1857 | auto byte_width = *--end; |
| 1858 | auto packed_type = *--end; |
| 1859 | return VerifyByteWidth(byte_width) && Check(end - buf_ >= byte_width) && |
| 1860 | VerifyRef(Reference(end - byte_width, byte_width, packed_type)); |
| 1861 | } |
| 1862 | |
| 1863 | private: |
| 1864 | const uint8_t *buf_; |
| 1865 | size_t size_; |
| 1866 | size_t depth_; |
| 1867 | const size_t max_depth_; |
| 1868 | size_t num_vectors_; |
| 1869 | const size_t max_vectors_; |
| 1870 | bool check_alignment_; |
| 1871 | std::vector<uint8_t> *reuse_tracker_; |
| 1872 | }; |
| 1873 | |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 1874 | // Utility function that constructs the Verifier for you, see above for |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1875 | // parameters. |
| 1876 | inline bool VerifyBuffer(const uint8_t *buf, size_t buf_len, |
| 1877 | std::vector<uint8_t> *reuse_tracker = nullptr) { |
| 1878 | Verifier verifier(buf, buf_len, reuse_tracker); |
| 1879 | return verifier.VerifyBuffer(); |
| 1880 | } |
| 1881 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1882 | } // namespace flexbuffers |
| 1883 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame] | 1884 | #if defined(_MSC_VER) |
| 1885 | # pragma warning(pop) |
| 1886 | #endif |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1887 | |
| 1888 | #endif // FLATBUFFERS_FLEXBUFFERS_H_ |