James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021 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_FLATBUFFER_BUILDER_H_ |
| 18 | #define FLATBUFFERS_FLATBUFFER_BUILDER_H_ |
| 19 | |
| 20 | #include <functional> |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 21 | #include <initializer_list> |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 22 | |
| 23 | #include "flatbuffers/allocator.h" |
| 24 | #include "flatbuffers/array.h" |
| 25 | #include "flatbuffers/base.h" |
| 26 | #include "flatbuffers/buffer_ref.h" |
| 27 | #include "flatbuffers/default_allocator.h" |
| 28 | #include "flatbuffers/detached_buffer.h" |
| 29 | #include "flatbuffers/stl_emulation.h" |
| 30 | #include "flatbuffers/string.h" |
| 31 | #include "flatbuffers/struct.h" |
| 32 | #include "flatbuffers/table.h" |
| 33 | #include "flatbuffers/vector.h" |
| 34 | #include "flatbuffers/vector_downward.h" |
| 35 | #include "flatbuffers/verifier.h" |
| 36 | |
| 37 | namespace flatbuffers { |
| 38 | |
| 39 | // Converts a Field ID to a virtual table offset. |
| 40 | inline voffset_t FieldIndexToOffset(voffset_t field_id) { |
| 41 | // Should correspond to what EndTable() below builds up. |
| 42 | const int fixed_fields = 2; // Vtable size and Object Size. |
| 43 | return static_cast<voffset_t>((field_id + fixed_fields) * sizeof(voffset_t)); |
| 44 | } |
| 45 | |
| 46 | template<typename T, typename Alloc = std::allocator<T>> |
| 47 | const T *data(const std::vector<T, Alloc> &v) { |
| 48 | // Eventually the returned pointer gets passed down to memcpy, so |
| 49 | // we need it to be non-null to avoid undefined behavior. |
| 50 | static uint8_t t; |
| 51 | return v.empty() ? reinterpret_cast<const T *>(&t) : &v.front(); |
| 52 | } |
| 53 | template<typename T, typename Alloc = std::allocator<T>> |
| 54 | T *data(std::vector<T, Alloc> &v) { |
| 55 | // Eventually the returned pointer gets passed down to memcpy, so |
| 56 | // we need it to be non-null to avoid undefined behavior. |
| 57 | static uint8_t t; |
| 58 | return v.empty() ? reinterpret_cast<T *>(&t) : &v.front(); |
| 59 | } |
| 60 | |
| 61 | /// @addtogroup flatbuffers_cpp_api |
| 62 | /// @{ |
| 63 | /// @class FlatBufferBuilder |
| 64 | /// @brief Helper class to hold data needed in creation of a FlatBuffer. |
| 65 | /// To serialize data, you typically call one of the `Create*()` functions in |
| 66 | /// the generated code, which in turn call a sequence of `StartTable`/ |
| 67 | /// `PushElement`/`AddElement`/`EndTable`, or the builtin `CreateString`/ |
| 68 | /// `CreateVector` functions. Do this is depth-first order to build up a tree to |
| 69 | /// the root. `Finish()` wraps up the buffer ready for transport. |
| 70 | class FlatBufferBuilder { |
| 71 | public: |
| 72 | /// @brief Default constructor for FlatBufferBuilder. |
| 73 | /// @param[in] initial_size The initial size of the buffer, in bytes. Defaults |
| 74 | /// to `1024`. |
| 75 | /// @param[in] allocator An `Allocator` to use. If null will use |
| 76 | /// `DefaultAllocator`. |
| 77 | /// @param[in] own_allocator Whether the builder/vector should own the |
| 78 | /// allocator. Defaults to / `false`. |
| 79 | /// @param[in] buffer_minalign Force the buffer to be aligned to the given |
| 80 | /// minimum alignment upon reallocation. Only needed if you intend to store |
| 81 | /// types with custom alignment AND you wish to read the buffer in-place |
| 82 | /// directly after creation. |
| 83 | explicit FlatBufferBuilder( |
| 84 | size_t initial_size = 1024, Allocator *allocator = nullptr, |
| 85 | bool own_allocator = false, |
| 86 | size_t buffer_minalign = AlignOf<largest_scalar_t>()) |
| 87 | : buf_(initial_size, allocator, own_allocator, buffer_minalign), |
| 88 | num_field_loc(0), |
| 89 | max_voffset_(0), |
| 90 | nested(false), |
| 91 | finished(false), |
| 92 | minalign_(1), |
| 93 | force_defaults_(false), |
| 94 | dedup_vtables_(true), |
| 95 | string_pool(nullptr) { |
| 96 | EndianCheck(); |
| 97 | } |
| 98 | |
| 99 | /// @brief Move constructor for FlatBufferBuilder. |
| 100 | FlatBufferBuilder(FlatBufferBuilder &&other) |
| 101 | : buf_(1024, nullptr, false, AlignOf<largest_scalar_t>()), |
| 102 | num_field_loc(0), |
| 103 | max_voffset_(0), |
| 104 | nested(false), |
| 105 | finished(false), |
| 106 | minalign_(1), |
| 107 | force_defaults_(false), |
| 108 | dedup_vtables_(true), |
| 109 | string_pool(nullptr) { |
| 110 | EndianCheck(); |
| 111 | // Default construct and swap idiom. |
| 112 | // Lack of delegating constructors in vs2010 makes it more verbose than |
| 113 | // needed. |
| 114 | Swap(other); |
| 115 | } |
| 116 | |
| 117 | /// @brief Move assignment operator for FlatBufferBuilder. |
| 118 | FlatBufferBuilder &operator=(FlatBufferBuilder &&other) { |
| 119 | // Move construct a temporary and swap idiom |
| 120 | FlatBufferBuilder temp(std::move(other)); |
| 121 | Swap(temp); |
| 122 | return *this; |
| 123 | } |
| 124 | |
| 125 | void Swap(FlatBufferBuilder &other) { |
| 126 | using std::swap; |
| 127 | buf_.swap(other.buf_); |
| 128 | swap(num_field_loc, other.num_field_loc); |
| 129 | swap(max_voffset_, other.max_voffset_); |
| 130 | swap(nested, other.nested); |
| 131 | swap(finished, other.finished); |
| 132 | swap(minalign_, other.minalign_); |
| 133 | swap(force_defaults_, other.force_defaults_); |
| 134 | swap(dedup_vtables_, other.dedup_vtables_); |
| 135 | swap(string_pool, other.string_pool); |
| 136 | } |
| 137 | |
| 138 | ~FlatBufferBuilder() { |
| 139 | if (string_pool) delete string_pool; |
| 140 | } |
| 141 | |
| 142 | void Reset() { |
| 143 | Clear(); // clear builder state |
| 144 | buf_.reset(); // deallocate buffer |
| 145 | } |
| 146 | |
| 147 | /// @brief Reset all the state in this FlatBufferBuilder so it can be reused |
| 148 | /// to construct another buffer. |
| 149 | void Clear() { |
| 150 | ClearOffsets(); |
| 151 | buf_.clear(); |
| 152 | nested = false; |
| 153 | finished = false; |
| 154 | minalign_ = 1; |
| 155 | if (string_pool) string_pool->clear(); |
| 156 | } |
| 157 | |
| 158 | /// @brief The current size of the serialized buffer, counting from the end. |
| 159 | /// @return Returns an `uoffset_t` with the current size of the buffer. |
| 160 | uoffset_t GetSize() const { return buf_.size(); } |
| 161 | |
| 162 | /// @brief Get the serialized buffer (after you call `Finish()`). |
| 163 | /// @return Returns an `uint8_t` pointer to the FlatBuffer data inside the |
| 164 | /// buffer. |
| 165 | uint8_t *GetBufferPointer() const { |
| 166 | Finished(); |
| 167 | return buf_.data(); |
| 168 | } |
| 169 | |
| 170 | /// @brief Get the serialized buffer (after you call `Finish()`) as a span. |
| 171 | /// @return Returns a constructed flatbuffers::span that is a view over the |
| 172 | /// FlatBuffer data inside the buffer. |
| 173 | flatbuffers::span<uint8_t> GetBufferSpan() const { |
| 174 | Finished(); |
| 175 | return flatbuffers::span<uint8_t>(buf_.data(), buf_.size()); |
| 176 | } |
| 177 | |
| 178 | /// @brief Get a pointer to an unfinished buffer. |
| 179 | /// @return Returns a `uint8_t` pointer to the unfinished buffer. |
| 180 | uint8_t *GetCurrentBufferPointer() const { return buf_.data(); } |
| 181 | |
| 182 | /// @brief Get the released pointer to the serialized buffer. |
| 183 | /// @warning Do NOT attempt to use this FlatBufferBuilder afterwards! |
| 184 | /// @return A `FlatBuffer` that owns the buffer and its allocator and |
| 185 | /// behaves similar to a `unique_ptr` with a deleter. |
| 186 | FLATBUFFERS_ATTRIBUTE([[deprecated("use Release() instead")]]) |
| 187 | DetachedBuffer ReleaseBufferPointer() { |
| 188 | Finished(); |
| 189 | return buf_.release(); |
| 190 | } |
| 191 | |
| 192 | /// @brief Get the released DetachedBuffer. |
| 193 | /// @return A `DetachedBuffer` that owns the buffer and its allocator. |
| 194 | DetachedBuffer Release() { |
| 195 | Finished(); |
| 196 | return buf_.release(); |
| 197 | } |
| 198 | |
| 199 | /// @brief Get the released pointer to the serialized buffer. |
| 200 | /// @param size The size of the memory block containing |
| 201 | /// the serialized `FlatBuffer`. |
| 202 | /// @param offset The offset from the released pointer where the finished |
| 203 | /// `FlatBuffer` starts. |
| 204 | /// @return A raw pointer to the start of the memory block containing |
| 205 | /// the serialized `FlatBuffer`. |
| 206 | /// @remark If the allocator is owned, it gets deleted when the destructor is |
| 207 | /// called.. |
| 208 | uint8_t *ReleaseRaw(size_t &size, size_t &offset) { |
| 209 | Finished(); |
| 210 | return buf_.release_raw(size, offset); |
| 211 | } |
| 212 | |
| 213 | /// @brief get the minimum alignment this buffer needs to be accessed |
| 214 | /// properly. This is only known once all elements have been written (after |
| 215 | /// you call Finish()). You can use this information if you need to embed |
| 216 | /// a FlatBuffer in some other buffer, such that you can later read it |
| 217 | /// without first having to copy it into its own buffer. |
| 218 | size_t GetBufferMinAlignment() const { |
| 219 | Finished(); |
| 220 | return minalign_; |
| 221 | } |
| 222 | |
| 223 | /// @cond FLATBUFFERS_INTERNAL |
| 224 | void Finished() const { |
| 225 | // If you get this assert, you're attempting to get access a buffer |
| 226 | // which hasn't been finished yet. Be sure to call |
| 227 | // FlatBufferBuilder::Finish with your root table. |
| 228 | // If you really need to access an unfinished buffer, call |
| 229 | // GetCurrentBufferPointer instead. |
| 230 | FLATBUFFERS_ASSERT(finished); |
| 231 | } |
| 232 | /// @endcond |
| 233 | |
| 234 | /// @brief In order to save space, fields that are set to their default value |
| 235 | /// don't get serialized into the buffer. |
| 236 | /// @param[in] fd When set to `true`, always serializes default values that |
| 237 | /// are set. Optional fields which are not set explicitly, will still not be |
| 238 | /// serialized. |
| 239 | void ForceDefaults(bool fd) { force_defaults_ = fd; } |
| 240 | |
| 241 | /// @brief By default vtables are deduped in order to save space. |
| 242 | /// @param[in] dedup When set to `true`, dedup vtables. |
| 243 | void DedupVtables(bool dedup) { dedup_vtables_ = dedup; } |
| 244 | |
| 245 | /// @cond FLATBUFFERS_INTERNAL |
| 246 | void Pad(size_t num_bytes) { buf_.fill(num_bytes); } |
| 247 | |
| 248 | void TrackMinAlign(size_t elem_size) { |
| 249 | if (elem_size > minalign_) minalign_ = elem_size; |
| 250 | } |
| 251 | |
| 252 | void Align(size_t elem_size) { |
| 253 | TrackMinAlign(elem_size); |
| 254 | buf_.fill(PaddingBytes(buf_.size(), elem_size)); |
| 255 | } |
| 256 | |
| 257 | void PushFlatBuffer(const uint8_t *bytes, size_t size) { |
| 258 | PushBytes(bytes, size); |
| 259 | finished = true; |
| 260 | } |
| 261 | |
| 262 | void PushBytes(const uint8_t *bytes, size_t size) { buf_.push(bytes, size); } |
| 263 | |
| 264 | void PopBytes(size_t amount) { buf_.pop(amount); } |
| 265 | |
| 266 | template<typename T> void AssertScalarT() { |
| 267 | // The code assumes power of 2 sizes and endian-swap-ability. |
| 268 | static_assert(flatbuffers::is_scalar<T>::value, "T must be a scalar type"); |
| 269 | } |
| 270 | |
| 271 | // Write a single aligned scalar to the buffer |
| 272 | template<typename T> uoffset_t PushElement(T element) { |
| 273 | AssertScalarT<T>(); |
| 274 | Align(sizeof(T)); |
| 275 | buf_.push_small(EndianScalar(element)); |
| 276 | return GetSize(); |
| 277 | } |
| 278 | |
| 279 | template<typename T> uoffset_t PushElement(Offset<T> off) { |
| 280 | // Special case for offsets: see ReferTo below. |
| 281 | return PushElement(ReferTo(off.o)); |
| 282 | } |
| 283 | |
| 284 | // When writing fields, we track where they are, so we can create correct |
| 285 | // vtables later. |
| 286 | void TrackField(voffset_t field, uoffset_t off) { |
| 287 | FieldLoc fl = { off, field }; |
| 288 | buf_.scratch_push_small(fl); |
| 289 | num_field_loc++; |
| 290 | if (field > max_voffset_) { max_voffset_ = field; } |
| 291 | } |
| 292 | |
| 293 | // Like PushElement, but additionally tracks the field this represents. |
| 294 | template<typename T> void AddElement(voffset_t field, T e, T def) { |
| 295 | // We don't serialize values equal to the default. |
| 296 | if (IsTheSameAs(e, def) && !force_defaults_) return; |
| 297 | TrackField(field, PushElement(e)); |
| 298 | } |
| 299 | |
| 300 | template<typename T> void AddElement(voffset_t field, T e) { |
| 301 | TrackField(field, PushElement(e)); |
| 302 | } |
| 303 | |
| 304 | template<typename T> void AddOffset(voffset_t field, Offset<T> off) { |
| 305 | if (off.IsNull()) return; // Don't store. |
| 306 | AddElement(field, ReferTo(off.o), static_cast<uoffset_t>(0)); |
| 307 | } |
| 308 | |
| 309 | template<typename T> void AddStruct(voffset_t field, const T *structptr) { |
| 310 | if (!structptr) return; // Default, don't store. |
| 311 | Align(AlignOf<T>()); |
| 312 | buf_.push_small(*structptr); |
| 313 | TrackField(field, GetSize()); |
| 314 | } |
| 315 | |
| 316 | void AddStructOffset(voffset_t field, uoffset_t off) { |
| 317 | TrackField(field, off); |
| 318 | } |
| 319 | |
| 320 | // Offsets initially are relative to the end of the buffer (downwards). |
| 321 | // This function converts them to be relative to the current location |
| 322 | // in the buffer (when stored here), pointing upwards. |
| 323 | uoffset_t ReferTo(uoffset_t off) { |
| 324 | // Align to ensure GetSize() below is correct. |
| 325 | Align(sizeof(uoffset_t)); |
| 326 | // Offset must refer to something already in buffer. |
| 327 | const uoffset_t size = GetSize(); |
| 328 | FLATBUFFERS_ASSERT(off && off <= size); |
| 329 | return size - off + static_cast<uoffset_t>(sizeof(uoffset_t)); |
| 330 | } |
| 331 | |
| 332 | void NotNested() { |
| 333 | // If you hit this, you're trying to construct a Table/Vector/String |
| 334 | // during the construction of its parent table (between the MyTableBuilder |
| 335 | // and table.Finish(). |
| 336 | // Move the creation of these sub-objects to above the MyTableBuilder to |
| 337 | // not get this assert. |
| 338 | // Ignoring this assert may appear to work in simple cases, but the reason |
| 339 | // it is here is that storing objects in-line may cause vtable offsets |
| 340 | // to not fit anymore. It also leads to vtable duplication. |
| 341 | FLATBUFFERS_ASSERT(!nested); |
| 342 | // If you hit this, fields were added outside the scope of a table. |
| 343 | FLATBUFFERS_ASSERT(!num_field_loc); |
| 344 | } |
| 345 | |
| 346 | // From generated code (or from the parser), we call StartTable/EndTable |
| 347 | // with a sequence of AddElement calls in between. |
| 348 | uoffset_t StartTable() { |
| 349 | NotNested(); |
| 350 | nested = true; |
| 351 | return GetSize(); |
| 352 | } |
| 353 | |
| 354 | // This finishes one serialized object by generating the vtable if it's a |
| 355 | // table, comparing it against existing vtables, and writing the |
| 356 | // resulting vtable offset. |
| 357 | uoffset_t EndTable(uoffset_t start) { |
| 358 | // If you get this assert, a corresponding StartTable wasn't called. |
| 359 | FLATBUFFERS_ASSERT(nested); |
| 360 | // Write the vtable offset, which is the start of any Table. |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 361 | // We fill its value later. |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 362 | auto vtableoffsetloc = PushElement<soffset_t>(0); |
| 363 | // Write a vtable, which consists entirely of voffset_t elements. |
| 364 | // It starts with the number of offsets, followed by a type id, followed |
| 365 | // by the offsets themselves. In reverse: |
| 366 | // Include space for the last offset and ensure empty tables have a |
| 367 | // minimum size. |
| 368 | max_voffset_ = |
| 369 | (std::max)(static_cast<voffset_t>(max_voffset_ + sizeof(voffset_t)), |
| 370 | FieldIndexToOffset(0)); |
| 371 | buf_.fill_big(max_voffset_); |
| 372 | auto table_object_size = vtableoffsetloc - start; |
| 373 | // Vtable use 16bit offsets. |
| 374 | FLATBUFFERS_ASSERT(table_object_size < 0x10000); |
| 375 | WriteScalar<voffset_t>(buf_.data() + sizeof(voffset_t), |
| 376 | static_cast<voffset_t>(table_object_size)); |
| 377 | WriteScalar<voffset_t>(buf_.data(), max_voffset_); |
| 378 | // Write the offsets into the table |
| 379 | for (auto it = buf_.scratch_end() - num_field_loc * sizeof(FieldLoc); |
| 380 | it < buf_.scratch_end(); it += sizeof(FieldLoc)) { |
| 381 | auto field_location = reinterpret_cast<FieldLoc *>(it); |
| 382 | auto pos = static_cast<voffset_t>(vtableoffsetloc - field_location->off); |
| 383 | // If this asserts, it means you've set a field twice. |
| 384 | FLATBUFFERS_ASSERT( |
| 385 | !ReadScalar<voffset_t>(buf_.data() + field_location->id)); |
| 386 | WriteScalar<voffset_t>(buf_.data() + field_location->id, pos); |
| 387 | } |
| 388 | ClearOffsets(); |
| 389 | auto vt1 = reinterpret_cast<voffset_t *>(buf_.data()); |
| 390 | auto vt1_size = ReadScalar<voffset_t>(vt1); |
| 391 | auto vt_use = GetSize(); |
| 392 | // See if we already have generated a vtable with this exact same |
| 393 | // layout before. If so, make it point to the old one, remove this one. |
| 394 | if (dedup_vtables_) { |
| 395 | for (auto it = buf_.scratch_data(); it < buf_.scratch_end(); |
| 396 | it += sizeof(uoffset_t)) { |
| 397 | auto vt_offset_ptr = reinterpret_cast<uoffset_t *>(it); |
| 398 | auto vt2 = reinterpret_cast<voffset_t *>(buf_.data_at(*vt_offset_ptr)); |
| 399 | auto vt2_size = ReadScalar<voffset_t>(vt2); |
| 400 | if (vt1_size != vt2_size || 0 != memcmp(vt2, vt1, vt1_size)) continue; |
| 401 | vt_use = *vt_offset_ptr; |
| 402 | buf_.pop(GetSize() - vtableoffsetloc); |
| 403 | break; |
| 404 | } |
| 405 | } |
| 406 | // If this is a new vtable, remember it. |
| 407 | if (vt_use == GetSize()) { buf_.scratch_push_small(vt_use); } |
| 408 | // Fill the vtable offset we created above. |
| 409 | // The offset points from the beginning of the object to where the |
| 410 | // vtable is stored. |
| 411 | // Offsets default direction is downward in memory for future format |
| 412 | // flexibility (storing all vtables at the start of the file). |
| 413 | WriteScalar(buf_.data_at(vtableoffsetloc), |
| 414 | static_cast<soffset_t>(vt_use) - |
| 415 | static_cast<soffset_t>(vtableoffsetloc)); |
| 416 | |
| 417 | nested = false; |
| 418 | return vtableoffsetloc; |
| 419 | } |
| 420 | |
| 421 | FLATBUFFERS_ATTRIBUTE([[deprecated("call the version above instead")]]) |
| 422 | uoffset_t EndTable(uoffset_t start, voffset_t /*numfields*/) { |
| 423 | return EndTable(start); |
| 424 | } |
| 425 | |
| 426 | // This checks a required field has been set in a given table that has |
| 427 | // just been constructed. |
| 428 | template<typename T> void Required(Offset<T> table, voffset_t field); |
| 429 | |
| 430 | uoffset_t StartStruct(size_t alignment) { |
| 431 | Align(alignment); |
| 432 | return GetSize(); |
| 433 | } |
| 434 | |
| 435 | uoffset_t EndStruct() { return GetSize(); } |
| 436 | |
| 437 | void ClearOffsets() { |
| 438 | buf_.scratch_pop(num_field_loc * sizeof(FieldLoc)); |
| 439 | num_field_loc = 0; |
| 440 | max_voffset_ = 0; |
| 441 | } |
| 442 | |
| 443 | // Aligns such that when "len" bytes are written, an object can be written |
| 444 | // after it with "alignment" without padding. |
| 445 | void PreAlign(size_t len, size_t alignment) { |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 446 | if (len == 0) return; |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 447 | TrackMinAlign(alignment); |
| 448 | buf_.fill(PaddingBytes(GetSize() + len, alignment)); |
| 449 | } |
| 450 | template<typename T> void PreAlign(size_t len) { |
| 451 | AssertScalarT<T>(); |
| 452 | PreAlign(len, sizeof(T)); |
| 453 | } |
| 454 | /// @endcond |
| 455 | |
| 456 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 457 | /// @param[in] str A const char pointer to the data to be stored as a string. |
| 458 | /// @param[in] len The number of bytes that should be stored from `str`. |
| 459 | /// @return Returns the offset in the buffer where the string starts. |
| 460 | Offset<String> CreateString(const char *str, size_t len) { |
| 461 | NotNested(); |
| 462 | PreAlign<uoffset_t>(len + 1); // Always 0-terminated. |
| 463 | buf_.fill(1); |
| 464 | PushBytes(reinterpret_cast<const uint8_t *>(str), len); |
| 465 | PushElement(static_cast<uoffset_t>(len)); |
| 466 | return Offset<String>(GetSize()); |
| 467 | } |
| 468 | |
| 469 | /// @brief Store a string in the buffer, which is null-terminated. |
| 470 | /// @param[in] str A const char pointer to a C-string to add to the buffer. |
| 471 | /// @return Returns the offset in the buffer where the string starts. |
| 472 | Offset<String> CreateString(const char *str) { |
| 473 | return CreateString(str, strlen(str)); |
| 474 | } |
| 475 | |
| 476 | /// @brief Store a string in the buffer, which is null-terminated. |
| 477 | /// @param[in] str A char pointer to a C-string to add to the buffer. |
| 478 | /// @return Returns the offset in the buffer where the string starts. |
| 479 | Offset<String> CreateString(char *str) { |
| 480 | return CreateString(str, strlen(str)); |
| 481 | } |
| 482 | |
| 483 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 484 | /// @param[in] str A const reference to a std::string to store in the buffer. |
| 485 | /// @return Returns the offset in the buffer where the string starts. |
| 486 | Offset<String> CreateString(const std::string &str) { |
| 487 | return CreateString(str.c_str(), str.length()); |
| 488 | } |
| 489 | |
| 490 | // clang-format off |
| 491 | #ifdef FLATBUFFERS_HAS_STRING_VIEW |
| 492 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 493 | /// @param[in] str A const string_view to copy in to the buffer. |
| 494 | /// @return Returns the offset in the buffer where the string starts. |
| 495 | Offset<String> CreateString(flatbuffers::string_view str) { |
| 496 | return CreateString(str.data(), str.size()); |
| 497 | } |
| 498 | #endif // FLATBUFFERS_HAS_STRING_VIEW |
| 499 | // clang-format on |
| 500 | |
| 501 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 502 | /// @param[in] str A const pointer to a `String` struct to add to the buffer. |
| 503 | /// @return Returns the offset in the buffer where the string starts |
| 504 | Offset<String> CreateString(const String *str) { |
| 505 | return str ? CreateString(str->c_str(), str->size()) : 0; |
| 506 | } |
| 507 | |
| 508 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 509 | /// @param[in] str A const reference to a std::string like type with support |
| 510 | /// of T::c_str() and T::length() to store in the buffer. |
| 511 | /// @return Returns the offset in the buffer where the string starts. |
| 512 | template<typename T> Offset<String> CreateString(const T &str) { |
| 513 | return CreateString(str.c_str(), str.length()); |
| 514 | } |
| 515 | |
| 516 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 517 | /// If a string with this exact contents has already been serialized before, |
| 518 | /// instead simply returns the offset of the existing string. This uses a map |
| 519 | /// stored on the heap, but only stores the numerical offsets. |
| 520 | /// @param[in] str A const char pointer to the data to be stored as a string. |
| 521 | /// @param[in] len The number of bytes that should be stored from `str`. |
| 522 | /// @return Returns the offset in the buffer where the string starts. |
| 523 | Offset<String> CreateSharedString(const char *str, size_t len) { |
| 524 | FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); |
| 525 | if (!string_pool) |
| 526 | string_pool = new StringOffsetMap(StringOffsetCompare(buf_)); |
| 527 | auto size_before_string = buf_.size(); |
| 528 | // Must first serialize the string, since the set is all offsets into |
| 529 | // buffer. |
| 530 | auto off = CreateString(str, len); |
| 531 | auto it = string_pool->find(off); |
| 532 | // If it exists we reuse existing serialized data! |
| 533 | if (it != string_pool->end()) { |
| 534 | // We can remove the string we serialized. |
| 535 | buf_.pop(buf_.size() - size_before_string); |
| 536 | return *it; |
| 537 | } |
| 538 | // Record this string for future use. |
| 539 | string_pool->insert(off); |
| 540 | return off; |
| 541 | } |
| 542 | |
| 543 | #ifdef FLATBUFFERS_HAS_STRING_VIEW |
| 544 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 545 | /// If a string with this exact contents has already been serialized before, |
| 546 | /// instead simply returns the offset of the existing string. This uses a map |
| 547 | /// stored on the heap, but only stores the numerical offsets. |
| 548 | /// @param[in] str A const std::string_view to store in the buffer. |
| 549 | /// @return Returns the offset in the buffer where the string starts |
| 550 | Offset<String> CreateSharedString(const flatbuffers::string_view str) { |
| 551 | return CreateSharedString(str.data(), str.size()); |
| 552 | } |
| 553 | #else |
| 554 | /// @brief Store a string in the buffer, which null-terminated. |
| 555 | /// If a string with this exact contents has already been serialized before, |
| 556 | /// instead simply returns the offset of the existing string. This uses a map |
| 557 | /// stored on the heap, but only stores the numerical offsets. |
| 558 | /// @param[in] str A const char pointer to a C-string to add to the buffer. |
| 559 | /// @return Returns the offset in the buffer where the string starts. |
| 560 | Offset<String> CreateSharedString(const char *str) { |
| 561 | return CreateSharedString(str, strlen(str)); |
| 562 | } |
| 563 | |
| 564 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 565 | /// If a string with this exact contents has already been serialized before, |
| 566 | /// instead simply returns the offset of the existing string. This uses a map |
| 567 | /// stored on the heap, but only stores the numerical offsets. |
| 568 | /// @param[in] str A const reference to a std::string to store in the buffer. |
| 569 | /// @return Returns the offset in the buffer where the string starts. |
| 570 | Offset<String> CreateSharedString(const std::string &str) { |
| 571 | return CreateSharedString(str.c_str(), str.length()); |
| 572 | } |
| 573 | #endif |
| 574 | |
| 575 | /// @brief Store a string in the buffer, which can contain any binary data. |
| 576 | /// If a string with this exact contents has already been serialized before, |
| 577 | /// instead simply returns the offset of the existing string. This uses a map |
| 578 | /// stored on the heap, but only stores the numerical offsets. |
| 579 | /// @param[in] str A const pointer to a `String` struct to add to the buffer. |
| 580 | /// @return Returns the offset in the buffer where the string starts |
| 581 | Offset<String> CreateSharedString(const String *str) { |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 582 | return str ? CreateSharedString(str->c_str(), str->size()) : 0; |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 583 | } |
| 584 | |
| 585 | /// @cond FLATBUFFERS_INTERNAL |
| 586 | uoffset_t EndVector(size_t len) { |
| 587 | FLATBUFFERS_ASSERT(nested); // Hit if no corresponding StartVector. |
| 588 | nested = false; |
| 589 | return PushElement(static_cast<uoffset_t>(len)); |
| 590 | } |
| 591 | |
| 592 | void StartVector(size_t len, size_t elemsize) { |
| 593 | NotNested(); |
| 594 | nested = true; |
| 595 | PreAlign<uoffset_t>(len * elemsize); |
| 596 | PreAlign(len * elemsize, elemsize); // Just in case elemsize > uoffset_t. |
| 597 | } |
| 598 | |
| 599 | // Call this right before StartVector/CreateVector if you want to force the |
| 600 | // alignment to be something different than what the element size would |
| 601 | // normally dictate. |
| 602 | // This is useful when storing a nested_flatbuffer in a vector of bytes, |
| 603 | // or when storing SIMD floats, etc. |
| 604 | void ForceVectorAlignment(size_t len, size_t elemsize, size_t alignment) { |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 605 | if (len == 0) return; |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 606 | FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); |
| 607 | PreAlign(len * elemsize, alignment); |
| 608 | } |
| 609 | |
| 610 | // Similar to ForceVectorAlignment but for String fields. |
| 611 | void ForceStringAlignment(size_t len, size_t alignment) { |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 612 | if (len == 0) return; |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 613 | FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); |
| 614 | PreAlign((len + 1) * sizeof(char), alignment); |
| 615 | } |
| 616 | |
| 617 | /// @endcond |
| 618 | |
| 619 | /// @brief Serialize an array into a FlatBuffer `vector`. |
| 620 | /// @tparam T The data type of the array elements. |
| 621 | /// @param[in] v A pointer to the array of type `T` to serialize into the |
| 622 | /// buffer as a `vector`. |
| 623 | /// @param[in] len The number of elements to serialize. |
| 624 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 625 | /// where the vector is stored. |
| 626 | template<typename T> Offset<Vector<T>> CreateVector(const T *v, size_t len) { |
| 627 | // If this assert hits, you're specifying a template argument that is |
| 628 | // causing the wrong overload to be selected, remove it. |
| 629 | AssertScalarT<T>(); |
| 630 | StartVector(len, sizeof(T)); |
| 631 | if (len == 0) { return Offset<Vector<T>>(EndVector(len)); } |
| 632 | // clang-format off |
| 633 | #if FLATBUFFERS_LITTLEENDIAN |
| 634 | PushBytes(reinterpret_cast<const uint8_t *>(v), len * sizeof(T)); |
| 635 | #else |
| 636 | if (sizeof(T) == 1) { |
| 637 | PushBytes(reinterpret_cast<const uint8_t *>(v), len); |
| 638 | } else { |
| 639 | for (auto i = len; i > 0; ) { |
| 640 | PushElement(v[--i]); |
| 641 | } |
| 642 | } |
| 643 | #endif |
| 644 | // clang-format on |
| 645 | return Offset<Vector<T>>(EndVector(len)); |
| 646 | } |
| 647 | |
| 648 | /// @brief Serialize an array like object into a FlatBuffer `vector`. |
| 649 | /// @tparam T The data type of the array elements. |
| 650 | /// @tparam C The type of the array. |
| 651 | /// @param[in] array A reference to an array like object of type `T` to |
| 652 | /// serialize into the buffer as a `vector`. |
| 653 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 654 | /// where the vector is stored. |
| 655 | template<typename T, class C> Offset<Vector<T>> CreateVector(const C &array) { |
| 656 | return CreateVector(array.data(), array.size()); |
| 657 | } |
| 658 | |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 659 | /// @brief Serialize an initializer list into a FlatBuffer `vector`. |
| 660 | /// @tparam T The data type of the initializer list elements. |
| 661 | /// @param[in] v The value of the initializer list. |
| 662 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 663 | /// where the vector is stored. |
| 664 | template<typename T> |
| 665 | Offset<Vector<T>> CreateVector(std::initializer_list<T> v) { |
| 666 | return CreateVector(v.begin(), v.size()); |
| 667 | } |
| 668 | |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 669 | template<typename T> |
| 670 | Offset<Vector<Offset<T>>> CreateVector(const Offset<T> *v, size_t len) { |
| 671 | StartVector(len, sizeof(Offset<T>)); |
| 672 | for (auto i = len; i > 0;) { PushElement(v[--i]); } |
| 673 | return Offset<Vector<Offset<T>>>(EndVector(len)); |
| 674 | } |
| 675 | |
| 676 | /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. |
| 677 | /// @tparam T The data type of the `std::vector` elements. |
| 678 | /// @param v A const reference to the `std::vector` to serialize into the |
| 679 | /// buffer as a `vector`. |
| 680 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 681 | /// where the vector is stored. |
| 682 | template<typename T, typename Alloc = std::allocator<T>> |
| 683 | Offset<Vector<T>> CreateVector(const std::vector<T, Alloc> &v) { |
| 684 | return CreateVector(data(v), v.size()); |
| 685 | } |
| 686 | |
| 687 | // vector<bool> may be implemented using a bit-set, so we can't access it as |
| 688 | // an array. Instead, read elements manually. |
| 689 | // Background: https://isocpp.org/blog/2012/11/on-vectorbool |
| 690 | Offset<Vector<uint8_t>> CreateVector(const std::vector<bool> &v) { |
| 691 | StartVector(v.size(), sizeof(uint8_t)); |
| 692 | for (auto i = v.size(); i > 0;) { |
| 693 | PushElement(static_cast<uint8_t>(v[--i])); |
| 694 | } |
| 695 | return Offset<Vector<uint8_t>>(EndVector(v.size())); |
| 696 | } |
| 697 | |
| 698 | /// @brief Serialize values returned by a function into a FlatBuffer `vector`. |
| 699 | /// This is a convenience function that takes care of iteration for you. |
| 700 | /// @tparam T The data type of the `std::vector` elements. |
| 701 | /// @param f A function that takes the current iteration 0..vector_size-1 and |
| 702 | /// returns any type that you can construct a FlatBuffers vector out of. |
| 703 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 704 | /// where the vector is stored. |
| 705 | template<typename T> |
| 706 | Offset<Vector<T>> CreateVector(size_t vector_size, |
| 707 | const std::function<T(size_t i)> &f) { |
| 708 | FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); |
| 709 | std::vector<T> elems(vector_size); |
| 710 | for (size_t i = 0; i < vector_size; i++) elems[i] = f(i); |
| 711 | return CreateVector(elems); |
| 712 | } |
| 713 | |
| 714 | /// @brief Serialize values returned by a function into a FlatBuffer `vector`. |
| 715 | /// This is a convenience function that takes care of iteration for you. This |
| 716 | /// uses a vector stored on the heap to store the intermediate results of the |
| 717 | /// iteration. |
| 718 | /// @tparam T The data type of the `std::vector` elements. |
| 719 | /// @param f A function that takes the current iteration 0..vector_size-1, |
| 720 | /// and the state parameter returning any type that you can construct a |
| 721 | /// FlatBuffers vector out of. |
| 722 | /// @param state State passed to f. |
| 723 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 724 | /// where the vector is stored. |
| 725 | template<typename T, typename F, typename S> |
| 726 | Offset<Vector<T>> CreateVector(size_t vector_size, F f, S *state) { |
| 727 | FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); |
| 728 | std::vector<T> elems(vector_size); |
| 729 | for (size_t i = 0; i < vector_size; i++) elems[i] = f(i, state); |
| 730 | return CreateVector(elems); |
| 731 | } |
| 732 | |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 733 | /// @brief Serialize a `std::vector<StringType>` into a FlatBuffer `vector`. |
| 734 | /// whereas StringType is any type that is accepted by the CreateString() |
| 735 | /// overloads. |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 736 | /// This is a convenience function for a common case. |
| 737 | /// @param v A const reference to the `std::vector` to serialize into the |
| 738 | /// buffer as a `vector`. |
| 739 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 740 | /// where the vector is stored. |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 741 | template<typename StringType = std::string, |
| 742 | typename Alloc = std::allocator<StringType>> |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 743 | Offset<Vector<Offset<String>>> CreateVectorOfStrings( |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 744 | const std::vector<StringType, Alloc> &v) { |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 745 | return CreateVectorOfStrings(v.cbegin(), v.cend()); |
| 746 | } |
| 747 | |
| 748 | /// @brief Serialize a collection of Strings into a FlatBuffer `vector`. |
| 749 | /// This is a convenience function for a common case. |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 750 | /// @param begin The beginning iterator of the collection |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 751 | /// @param end The ending iterator of the collection |
| 752 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 753 | /// where the vector is stored. |
| 754 | template<class It> |
| 755 | Offset<Vector<Offset<String>>> CreateVectorOfStrings(It begin, It end) { |
| 756 | auto size = std::distance(begin, end); |
| 757 | auto scratch_buffer_usage = size * sizeof(Offset<String>); |
| 758 | // If there is not enough space to store the offsets, there definitely won't |
| 759 | // be enough space to store all the strings. So ensuring space for the |
Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 760 | // scratch region is OK, for if it fails, it would have failed later. |
James Kuszmaul | 8e62b02 | 2022-03-22 09:33:25 -0700 | [diff] [blame] | 761 | buf_.ensure_space(scratch_buffer_usage); |
| 762 | for (auto it = begin; it != end; ++it) { |
| 763 | buf_.scratch_push_small(CreateString(*it)); |
| 764 | } |
| 765 | StartVector(size, sizeof(Offset<String>)); |
| 766 | for (auto i = 1; i <= size; i++) { |
| 767 | // Note we re-evaluate the buf location each iteration to account for any |
| 768 | // underlying buffer resizing that may occur. |
| 769 | PushElement(*reinterpret_cast<Offset<String> *>( |
| 770 | buf_.scratch_end() - i * sizeof(Offset<String>))); |
| 771 | } |
| 772 | buf_.scratch_pop(scratch_buffer_usage); |
| 773 | return Offset<Vector<Offset<String>>>(EndVector(size)); |
| 774 | } |
| 775 | |
| 776 | /// @brief Serialize an array of structs into a FlatBuffer `vector`. |
| 777 | /// @tparam T The data type of the struct array elements. |
| 778 | /// @param[in] v A pointer to the array of type `T` to serialize into the |
| 779 | /// buffer as a `vector`. |
| 780 | /// @param[in] len The number of elements to serialize. |
| 781 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 782 | /// where the vector is stored. |
| 783 | template<typename T> |
| 784 | Offset<Vector<const T *>> CreateVectorOfStructs(const T *v, size_t len) { |
| 785 | StartVector(len * sizeof(T) / AlignOf<T>(), AlignOf<T>()); |
| 786 | if (len > 0) { |
| 787 | PushBytes(reinterpret_cast<const uint8_t *>(v), sizeof(T) * len); |
| 788 | } |
| 789 | return Offset<Vector<const T *>>(EndVector(len)); |
| 790 | } |
| 791 | |
| 792 | /// @brief Serialize an array of native structs into a FlatBuffer `vector`. |
| 793 | /// @tparam T The data type of the struct array elements. |
| 794 | /// @tparam S The data type of the native struct array elements. |
| 795 | /// @param[in] v A pointer to the array of type `S` to serialize into the |
| 796 | /// buffer as a `vector`. |
| 797 | /// @param[in] len The number of elements to serialize. |
| 798 | /// @param[in] pack_func Pointer to a function to convert the native struct |
| 799 | /// to the FlatBuffer struct. |
| 800 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 801 | /// where the vector is stored. |
| 802 | template<typename T, typename S> |
| 803 | Offset<Vector<const T *>> CreateVectorOfNativeStructs( |
| 804 | const S *v, size_t len, T (*const pack_func)(const S &)) { |
| 805 | FLATBUFFERS_ASSERT(pack_func); |
| 806 | auto structs = StartVectorOfStructs<T>(len); |
| 807 | for (size_t i = 0; i < len; i++) { structs[i] = pack_func(v[i]); } |
| 808 | return EndVectorOfStructs<T>(len); |
| 809 | } |
| 810 | |
| 811 | /// @brief Serialize an array of native structs into a FlatBuffer `vector`. |
| 812 | /// @tparam T The data type of the struct array elements. |
| 813 | /// @tparam S The data type of the native struct array elements. |
| 814 | /// @param[in] v A pointer to the array of type `S` to serialize into the |
| 815 | /// buffer as a `vector`. |
| 816 | /// @param[in] len The number of elements to serialize. |
| 817 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 818 | /// where the vector is stored. |
| 819 | template<typename T, typename S> |
| 820 | Offset<Vector<const T *>> CreateVectorOfNativeStructs(const S *v, |
| 821 | size_t len) { |
| 822 | extern T Pack(const S &); |
| 823 | return CreateVectorOfNativeStructs(v, len, Pack); |
| 824 | } |
| 825 | |
| 826 | /// @brief Serialize an array of structs into a FlatBuffer `vector`. |
| 827 | /// @tparam T The data type of the struct array elements. |
| 828 | /// @param[in] filler A function that takes the current iteration |
| 829 | /// 0..vector_size-1 and a pointer to the struct that must be filled. |
| 830 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 831 | /// where the vector is stored. |
| 832 | /// This is mostly useful when flatbuffers are generated with mutation |
| 833 | /// accessors. |
| 834 | template<typename T> |
| 835 | Offset<Vector<const T *>> CreateVectorOfStructs( |
| 836 | size_t vector_size, const std::function<void(size_t i, T *)> &filler) { |
| 837 | T *structs = StartVectorOfStructs<T>(vector_size); |
| 838 | for (size_t i = 0; i < vector_size; i++) { |
| 839 | filler(i, structs); |
| 840 | structs++; |
| 841 | } |
| 842 | return EndVectorOfStructs<T>(vector_size); |
| 843 | } |
| 844 | |
| 845 | /// @brief Serialize an array of structs into a FlatBuffer `vector`. |
| 846 | /// @tparam T The data type of the struct array elements. |
| 847 | /// @param[in] f A function that takes the current iteration 0..vector_size-1, |
| 848 | /// a pointer to the struct that must be filled and the state argument. |
| 849 | /// @param[in] state Arbitrary state to pass to f. |
| 850 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 851 | /// where the vector is stored. |
| 852 | /// This is mostly useful when flatbuffers are generated with mutation |
| 853 | /// accessors. |
| 854 | template<typename T, typename F, typename S> |
| 855 | Offset<Vector<const T *>> CreateVectorOfStructs(size_t vector_size, F f, |
| 856 | S *state) { |
| 857 | T *structs = StartVectorOfStructs<T>(vector_size); |
| 858 | for (size_t i = 0; i < vector_size; i++) { |
| 859 | f(i, structs, state); |
| 860 | structs++; |
| 861 | } |
| 862 | return EndVectorOfStructs<T>(vector_size); |
| 863 | } |
| 864 | |
| 865 | /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector`. |
| 866 | /// @tparam T The data type of the `std::vector` struct elements. |
| 867 | /// @param[in] v A const reference to the `std::vector` of structs to |
| 868 | /// serialize into the buffer as a `vector`. |
| 869 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 870 | /// where the vector is stored. |
| 871 | template<typename T, typename Alloc = std::allocator<T>> |
| 872 | Offset<Vector<const T *>> CreateVectorOfStructs( |
| 873 | const std::vector<T, Alloc> &v) { |
| 874 | return CreateVectorOfStructs(data(v), v.size()); |
| 875 | } |
| 876 | |
| 877 | /// @brief Serialize a `std::vector` of native structs into a FlatBuffer |
| 878 | /// `vector`. |
| 879 | /// @tparam T The data type of the `std::vector` struct elements. |
| 880 | /// @tparam S The data type of the `std::vector` native struct elements. |
| 881 | /// @param[in] v A const reference to the `std::vector` of structs to |
| 882 | /// serialize into the buffer as a `vector`. |
| 883 | /// @param[in] pack_func Pointer to a function to convert the native struct |
| 884 | /// to the FlatBuffer struct. |
| 885 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 886 | /// where the vector is stored. |
| 887 | template<typename T, typename S, typename Alloc = std::allocator<T>> |
| 888 | Offset<Vector<const T *>> CreateVectorOfNativeStructs( |
| 889 | const std::vector<S, Alloc> &v, T (*const pack_func)(const S &)) { |
| 890 | return CreateVectorOfNativeStructs<T, S>(data(v), v.size(), pack_func); |
| 891 | } |
| 892 | |
| 893 | /// @brief Serialize a `std::vector` of native structs into a FlatBuffer |
| 894 | /// `vector`. |
| 895 | /// @tparam T The data type of the `std::vector` struct elements. |
| 896 | /// @tparam S The data type of the `std::vector` native struct elements. |
| 897 | /// @param[in] v A const reference to the `std::vector` of structs to |
| 898 | /// serialize into the buffer as a `vector`. |
| 899 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 900 | /// where the vector is stored. |
| 901 | template<typename T, typename S, typename Alloc = std::allocator<S>> |
| 902 | Offset<Vector<const T *>> CreateVectorOfNativeStructs( |
| 903 | const std::vector<S, Alloc> &v) { |
| 904 | return CreateVectorOfNativeStructs<T, S>(data(v), v.size()); |
| 905 | } |
| 906 | |
| 907 | /// @cond FLATBUFFERS_INTERNAL |
| 908 | template<typename T> struct StructKeyComparator { |
| 909 | bool operator()(const T &a, const T &b) const { |
| 910 | return a.KeyCompareLessThan(&b); |
| 911 | } |
| 912 | }; |
| 913 | /// @endcond |
| 914 | |
| 915 | /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector` |
| 916 | /// in sorted order. |
| 917 | /// @tparam T The data type of the `std::vector` struct elements. |
| 918 | /// @param[in] v A const reference to the `std::vector` of structs to |
| 919 | /// serialize into the buffer as a `vector`. |
| 920 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 921 | /// where the vector is stored. |
| 922 | template<typename T, typename Alloc = std::allocator<T>> |
| 923 | Offset<Vector<const T *>> CreateVectorOfSortedStructs( |
| 924 | std::vector<T, Alloc> *v) { |
| 925 | return CreateVectorOfSortedStructs(data(*v), v->size()); |
| 926 | } |
| 927 | |
| 928 | /// @brief Serialize a `std::vector` of native structs into a FlatBuffer |
| 929 | /// `vector` in sorted order. |
| 930 | /// @tparam T The data type of the `std::vector` struct elements. |
| 931 | /// @tparam S The data type of the `std::vector` native struct elements. |
| 932 | /// @param[in] v A const reference to the `std::vector` of structs to |
| 933 | /// serialize into the buffer as a `vector`. |
| 934 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 935 | /// where the vector is stored. |
| 936 | template<typename T, typename S, typename Alloc = std::allocator<T>> |
| 937 | Offset<Vector<const T *>> CreateVectorOfSortedNativeStructs( |
| 938 | std::vector<S, Alloc> *v) { |
| 939 | return CreateVectorOfSortedNativeStructs<T, S>(data(*v), v->size()); |
| 940 | } |
| 941 | |
| 942 | /// @brief Serialize an array of structs into a FlatBuffer `vector` in sorted |
| 943 | /// order. |
| 944 | /// @tparam T The data type of the struct array elements. |
| 945 | /// @param[in] v A pointer to the array of type `T` to serialize into the |
| 946 | /// buffer as a `vector`. |
| 947 | /// @param[in] len The number of elements to serialize. |
| 948 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 949 | /// where the vector is stored. |
| 950 | template<typename T> |
| 951 | Offset<Vector<const T *>> CreateVectorOfSortedStructs(T *v, size_t len) { |
| 952 | std::stable_sort(v, v + len, StructKeyComparator<T>()); |
| 953 | return CreateVectorOfStructs(v, len); |
| 954 | } |
| 955 | |
| 956 | /// @brief Serialize an array of native structs into a FlatBuffer `vector` in |
| 957 | /// sorted order. |
| 958 | /// @tparam T The data type of the struct array elements. |
| 959 | /// @tparam S The data type of the native struct array elements. |
| 960 | /// @param[in] v A pointer to the array of type `S` to serialize into the |
| 961 | /// buffer as a `vector`. |
| 962 | /// @param[in] len The number of elements to serialize. |
| 963 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 964 | /// where the vector is stored. |
| 965 | template<typename T, typename S> |
| 966 | Offset<Vector<const T *>> CreateVectorOfSortedNativeStructs(S *v, |
| 967 | size_t len) { |
| 968 | extern T Pack(const S &); |
| 969 | auto structs = StartVectorOfStructs<T>(len); |
| 970 | for (size_t i = 0; i < len; i++) { structs[i] = Pack(v[i]); } |
| 971 | std::stable_sort(structs, structs + len, StructKeyComparator<T>()); |
| 972 | return EndVectorOfStructs<T>(len); |
| 973 | } |
| 974 | |
| 975 | /// @cond FLATBUFFERS_INTERNAL |
| 976 | template<typename T> struct TableKeyComparator { |
| 977 | TableKeyComparator(vector_downward &buf) : buf_(buf) {} |
| 978 | TableKeyComparator(const TableKeyComparator &other) : buf_(other.buf_) {} |
| 979 | bool operator()(const Offset<T> &a, const Offset<T> &b) const { |
| 980 | auto table_a = reinterpret_cast<T *>(buf_.data_at(a.o)); |
| 981 | auto table_b = reinterpret_cast<T *>(buf_.data_at(b.o)); |
| 982 | return table_a->KeyCompareLessThan(table_b); |
| 983 | } |
| 984 | vector_downward &buf_; |
| 985 | |
| 986 | private: |
| 987 | FLATBUFFERS_DELETE_FUNC( |
| 988 | TableKeyComparator &operator=(const TableKeyComparator &other)); |
| 989 | }; |
| 990 | /// @endcond |
| 991 | |
| 992 | /// @brief Serialize an array of `table` offsets as a `vector` in the buffer |
| 993 | /// in sorted order. |
| 994 | /// @tparam T The data type that the offset refers to. |
| 995 | /// @param[in] v An array of type `Offset<T>` that contains the `table` |
| 996 | /// offsets to store in the buffer in sorted order. |
| 997 | /// @param[in] len The number of elements to store in the `vector`. |
| 998 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 999 | /// where the vector is stored. |
| 1000 | template<typename T> |
| 1001 | Offset<Vector<Offset<T>>> CreateVectorOfSortedTables(Offset<T> *v, |
| 1002 | size_t len) { |
| 1003 | std::stable_sort(v, v + len, TableKeyComparator<T>(buf_)); |
| 1004 | return CreateVector(v, len); |
| 1005 | } |
| 1006 | |
| 1007 | /// @brief Serialize an array of `table` offsets as a `vector` in the buffer |
| 1008 | /// in sorted order. |
| 1009 | /// @tparam T The data type that the offset refers to. |
| 1010 | /// @param[in] v An array of type `Offset<T>` that contains the `table` |
| 1011 | /// offsets to store in the buffer in sorted order. |
| 1012 | /// @return Returns a typed `Offset` into the serialized data indicating |
| 1013 | /// where the vector is stored. |
| 1014 | template<typename T, typename Alloc = std::allocator<T>> |
| 1015 | Offset<Vector<Offset<T>>> CreateVectorOfSortedTables( |
| 1016 | std::vector<Offset<T>, Alloc> *v) { |
| 1017 | return CreateVectorOfSortedTables(data(*v), v->size()); |
| 1018 | } |
| 1019 | |
| 1020 | /// @brief Specialized version of `CreateVector` for non-copying use cases. |
| 1021 | /// Write the data any time later to the returned buffer pointer `buf`. |
| 1022 | /// @param[in] len The number of elements to store in the `vector`. |
| 1023 | /// @param[in] elemsize The size of each element in the `vector`. |
| 1024 | /// @param[out] buf A pointer to a `uint8_t` pointer that can be |
| 1025 | /// written to at a later time to serialize the data into a `vector` |
| 1026 | /// in the buffer. |
| 1027 | uoffset_t CreateUninitializedVector(size_t len, size_t elemsize, |
| 1028 | uint8_t **buf) { |
| 1029 | NotNested(); |
| 1030 | StartVector(len, elemsize); |
| 1031 | buf_.make_space(len * elemsize); |
| 1032 | auto vec_start = GetSize(); |
| 1033 | auto vec_end = EndVector(len); |
| 1034 | *buf = buf_.data_at(vec_start); |
| 1035 | return vec_end; |
| 1036 | } |
| 1037 | |
| 1038 | /// @brief Specialized version of `CreateVector` for non-copying use cases. |
| 1039 | /// Write the data any time later to the returned buffer pointer `buf`. |
| 1040 | /// @tparam T The data type of the data that will be stored in the buffer |
| 1041 | /// as a `vector`. |
| 1042 | /// @param[in] len The number of elements to store in the `vector`. |
| 1043 | /// @param[out] buf A pointer to a pointer of type `T` that can be |
| 1044 | /// written to at a later time to serialize the data into a `vector` |
| 1045 | /// in the buffer. |
| 1046 | template<typename T> |
| 1047 | Offset<Vector<T>> CreateUninitializedVector(size_t len, T **buf) { |
| 1048 | AssertScalarT<T>(); |
| 1049 | return CreateUninitializedVector(len, sizeof(T), |
| 1050 | reinterpret_cast<uint8_t **>(buf)); |
| 1051 | } |
| 1052 | |
| 1053 | template<typename T> |
| 1054 | Offset<Vector<const T *>> CreateUninitializedVectorOfStructs(size_t len, |
| 1055 | T **buf) { |
| 1056 | return CreateUninitializedVector(len, sizeof(T), |
| 1057 | reinterpret_cast<uint8_t **>(buf)); |
| 1058 | } |
| 1059 | |
| 1060 | // @brief Create a vector of scalar type T given as input a vector of scalar |
| 1061 | // type U, useful with e.g. pre "enum class" enums, or any existing scalar |
| 1062 | // data of the wrong type. |
| 1063 | template<typename T, typename U> |
| 1064 | Offset<Vector<T>> CreateVectorScalarCast(const U *v, size_t len) { |
| 1065 | AssertScalarT<T>(); |
| 1066 | AssertScalarT<U>(); |
| 1067 | StartVector(len, sizeof(T)); |
| 1068 | for (auto i = len; i > 0;) { PushElement(static_cast<T>(v[--i])); } |
| 1069 | return Offset<Vector<T>>(EndVector(len)); |
| 1070 | } |
| 1071 | |
| 1072 | /// @brief Write a struct by itself, typically to be part of a union. |
| 1073 | template<typename T> Offset<const T *> CreateStruct(const T &structobj) { |
| 1074 | NotNested(); |
| 1075 | Align(AlignOf<T>()); |
| 1076 | buf_.push_small(structobj); |
| 1077 | return Offset<const T *>(GetSize()); |
| 1078 | } |
| 1079 | |
| 1080 | /// @brief Finish serializing a buffer by writing the root offset. |
| 1081 | /// @param[in] file_identifier If a `file_identifier` is given, the buffer |
| 1082 | /// will be prefixed with a standard FlatBuffers file header. |
| 1083 | template<typename T> |
| 1084 | void Finish(Offset<T> root, const char *file_identifier = nullptr) { |
| 1085 | Finish(root.o, file_identifier, false); |
| 1086 | } |
| 1087 | |
| 1088 | /// @brief Finish a buffer with a 32 bit size field pre-fixed (size of the |
| 1089 | /// buffer following the size field). These buffers are NOT compatible |
| 1090 | /// with standard buffers created by Finish, i.e. you can't call GetRoot |
| 1091 | /// on them, you have to use GetSizePrefixedRoot instead. |
| 1092 | /// All >32 bit quantities in this buffer will be aligned when the whole |
| 1093 | /// size pre-fixed buffer is aligned. |
| 1094 | /// These kinds of buffers are useful for creating a stream of FlatBuffers. |
| 1095 | template<typename T> |
| 1096 | void FinishSizePrefixed(Offset<T> root, |
| 1097 | const char *file_identifier = nullptr) { |
| 1098 | Finish(root.o, file_identifier, true); |
| 1099 | } |
| 1100 | |
| 1101 | void SwapBufAllocator(FlatBufferBuilder &other) { |
| 1102 | buf_.swap_allocator(other.buf_); |
| 1103 | } |
| 1104 | |
| 1105 | /// @brief The length of a FlatBuffer file header. |
| 1106 | static const size_t kFileIdentifierLength = |
| 1107 | ::flatbuffers::kFileIdentifierLength; |
| 1108 | |
| 1109 | protected: |
| 1110 | // You shouldn't really be copying instances of this class. |
| 1111 | FlatBufferBuilder(const FlatBufferBuilder &); |
| 1112 | FlatBufferBuilder &operator=(const FlatBufferBuilder &); |
| 1113 | |
| 1114 | void Finish(uoffset_t root, const char *file_identifier, bool size_prefix) { |
| 1115 | NotNested(); |
| 1116 | buf_.clear_scratch(); |
| 1117 | // This will cause the whole buffer to be aligned. |
| 1118 | PreAlign((size_prefix ? sizeof(uoffset_t) : 0) + sizeof(uoffset_t) + |
| 1119 | (file_identifier ? kFileIdentifierLength : 0), |
| 1120 | minalign_); |
| 1121 | if (file_identifier) { |
| 1122 | FLATBUFFERS_ASSERT(strlen(file_identifier) == kFileIdentifierLength); |
| 1123 | PushBytes(reinterpret_cast<const uint8_t *>(file_identifier), |
| 1124 | kFileIdentifierLength); |
| 1125 | } |
| 1126 | PushElement(ReferTo(root)); // Location of root. |
| 1127 | if (size_prefix) { PushElement(GetSize()); } |
| 1128 | finished = true; |
| 1129 | } |
| 1130 | |
| 1131 | struct FieldLoc { |
| 1132 | uoffset_t off; |
| 1133 | voffset_t id; |
| 1134 | }; |
| 1135 | |
| 1136 | vector_downward buf_; |
| 1137 | |
| 1138 | // Accumulating offsets of table members while it is being built. |
| 1139 | // We store these in the scratch pad of buf_, after the vtable offsets. |
| 1140 | uoffset_t num_field_loc; |
| 1141 | // Track how much of the vtable is in use, so we can output the most compact |
| 1142 | // possible vtable. |
| 1143 | voffset_t max_voffset_; |
| 1144 | |
| 1145 | // Ensure objects are not nested. |
| 1146 | bool nested; |
| 1147 | |
| 1148 | // Ensure the buffer is finished before it is being accessed. |
| 1149 | bool finished; |
| 1150 | |
| 1151 | size_t minalign_; |
| 1152 | |
| 1153 | bool force_defaults_; // Serialize values equal to their defaults anyway. |
| 1154 | |
| 1155 | bool dedup_vtables_; |
| 1156 | |
| 1157 | struct StringOffsetCompare { |
| 1158 | StringOffsetCompare(const vector_downward &buf) : buf_(&buf) {} |
| 1159 | bool operator()(const Offset<String> &a, const Offset<String> &b) const { |
| 1160 | auto stra = reinterpret_cast<const String *>(buf_->data_at(a.o)); |
| 1161 | auto strb = reinterpret_cast<const String *>(buf_->data_at(b.o)); |
| 1162 | return StringLessThan(stra->data(), stra->size(), strb->data(), |
| 1163 | strb->size()); |
| 1164 | } |
| 1165 | const vector_downward *buf_; |
| 1166 | }; |
| 1167 | |
| 1168 | // For use with CreateSharedString. Instantiated on first use only. |
| 1169 | typedef std::set<Offset<String>, StringOffsetCompare> StringOffsetMap; |
| 1170 | StringOffsetMap *string_pool; |
| 1171 | |
| 1172 | private: |
| 1173 | // Allocates space for a vector of structures. |
| 1174 | // Must be completed with EndVectorOfStructs(). |
| 1175 | template<typename T> T *StartVectorOfStructs(size_t vector_size) { |
| 1176 | StartVector(vector_size * sizeof(T) / AlignOf<T>(), AlignOf<T>()); |
| 1177 | return reinterpret_cast<T *>(buf_.make_space(vector_size * sizeof(T))); |
| 1178 | } |
| 1179 | |
| 1180 | // End the vector of structures in the flatbuffers. |
| 1181 | // Vector should have previously be started with StartVectorOfStructs(). |
| 1182 | template<typename T> |
| 1183 | Offset<Vector<const T *>> EndVectorOfStructs(size_t vector_size) { |
| 1184 | return Offset<Vector<const T *>>(EndVector(vector_size)); |
| 1185 | } |
| 1186 | }; |
| 1187 | /// @} |
| 1188 | |
| 1189 | /// Helpers to get a typed pointer to objects that are currently being built. |
| 1190 | /// @warning Creating new objects will lead to reallocations and invalidates |
| 1191 | /// the pointer! |
| 1192 | template<typename T> |
| 1193 | T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb, Offset<T> offset) { |
| 1194 | return reinterpret_cast<T *>(fbb.GetCurrentBufferPointer() + fbb.GetSize() - |
| 1195 | offset.o); |
| 1196 | } |
| 1197 | |
| 1198 | template<typename T> |
| 1199 | const T *GetTemporaryPointer(FlatBufferBuilder &fbb, Offset<T> offset) { |
| 1200 | return GetMutableTemporaryPointer<T>(fbb, offset); |
| 1201 | } |
| 1202 | |
| 1203 | template<typename T> |
| 1204 | void FlatBufferBuilder::Required(Offset<T> table, voffset_t field) { |
| 1205 | auto table_ptr = reinterpret_cast<const Table *>(buf_.data_at(table.o)); |
| 1206 | bool ok = table_ptr->GetOptionalFieldOffset(field) != 0; |
| 1207 | // If this fails, the caller will show what field needs to be set. |
| 1208 | FLATBUFFERS_ASSERT(ok); |
| 1209 | (void)ok; |
| 1210 | } |
| 1211 | |
| 1212 | } // namespace flatbuffers |
| 1213 | |
| 1214 | #endif // FLATBUFFERS_VECTOR_DOWNWARD_H_ |