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