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