/
auroraos
/
mirror_flatbuffers
Обзор
Документация
Войти
/
auroraos
/
mirror_flatbuffers
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
master
include/flatbuffers/flatbuffer_builder.h
1 518 строк
62 KB
Derek Bailey
Run clang-format -i **/*.cpp (#8865)
19 дек 2025, 21:42
Не верифицирован
19 дек 2025, 21:42
fb55e0c
Код
Авторство
О чём код?
/* * Copyright 2021 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FLATBUFFERS_FLATBUFFER_BUILDER_H_ #define FLATBUFFERS_FLATBUFFER_BUILDER_H_ #include <algorithm> #include <cstdint> #include <functional> #include <initializer_list> #include <type_traits> #include "flatbuffers/allocator.h" #include "flatbuffers/array.h" #include "flatbuffers/base.h" #include "flatbuffers/buffer.h" #include "flatbuffers/buffer_ref.h" #include "flatbuffers/default_allocator.h" #include "flatbuffers/detached_buffer.h" #include "flatbuffers/stl_emulation.h" #include "flatbuffers/string.h" #include "flatbuffers/struct.h" #include "flatbuffers/table.h" #include "flatbuffers/vector.h" #include "flatbuffers/vector_downward.h" #include "flatbuffers/verifier.h" namespace flatbuffers { // Converts a Field ID to a virtual table offset. inline voffset_t FieldIndexToOffset(voffset_t field_id) { // Should correspond to what EndTable() below builds up. const voffset_t fixed_fields = 2 * sizeof(voffset_t); // Vtable size and Object Size. size_t offset = fixed_fields + field_id * sizeof(voffset_t); FLATBUFFERS_ASSERT(offset < std::numeric_limits<voffset_t>::max()); return static_cast<voffset_t>(offset); } template <typename T, typename Alloc = std::allocator<T>> const T* data(const std::vector<T, Alloc>& v) { // Eventually the returned pointer gets passed down to memcpy, so // we need it to be non-null to avoid undefined behavior. static uint8_t t; return v.empty() ? reinterpret_cast<const T*>(&t) : &v.front(); } template <typename T, typename Alloc = std::allocator<T>> T* data(std::vector<T, Alloc>& v) { // Eventually the returned pointer gets passed down to memcpy, so // we need it to be non-null to avoid undefined behavior. static uint8_t t; return v.empty() ? reinterpret_cast<T*>(&t) : &v.front(); } /// @addtogroup flatbuffers_cpp_api /// @{ /// @class FlatBufferBuilder /// @brief Helper class to hold data needed in creation of a FlatBuffer. /// To serialize data, you typically call one of the `Create*()` functions in /// the generated code, which in turn call a sequence of `StartTable`/ /// `PushElement`/`AddElement`/`EndTable`, or the builtin `CreateString`/ /// `CreateVector` functions. Do this is depth-first order to build up a tree to /// the root. `Finish()` wraps up the buffer ready for transport. template <bool Is64Aware = false> class FlatBufferBuilderImpl { public: // This switches the size type of the builder, based on if its 64-bit aware // (uoffset64_t) or not (uoffset_t). typedef typename std::conditional<Is64Aware, uoffset64_t, uoffset_t>::type SizeT; /// @brief Default constructor for FlatBufferBuilder. /// @param[in] initial_size The initial size of the buffer, in bytes. Defaults /// to `1024`. /// @param[in] allocator An `Allocator` to use. If null will use /// `DefaultAllocator`. /// @param[in] own_allocator Whether the builder/vector should own the /// allocator. Defaults to / `false`. /// @param[in] buffer_minalign Force the buffer to be aligned to the given /// minimum alignment upon reallocation. Only needed if you intend to store /// types with custom alignment AND you wish to read the buffer in-place /// directly after creation. explicit FlatBufferBuilderImpl( size_t initial_size = 1024, Allocator* allocator = nullptr, bool own_allocator = false, size_t buffer_minalign = AlignOf<largest_scalar_t>()) : buf_(initial_size, allocator, own_allocator, buffer_minalign, static_cast<SizeT>(Is64Aware ? FLATBUFFERS_MAX_64_BUFFER_SIZE : FLATBUFFERS_MAX_BUFFER_SIZE)), num_field_loc(0), max_voffset_(0), length_of_64_bit_region_(0), nested(false), finished(false), minalign_(1), force_defaults_(false), dedup_vtables_(true), string_pool(nullptr) { EndianCheck(); } /// @brief Move constructor for FlatBufferBuilder. FlatBufferBuilderImpl(FlatBufferBuilderImpl&& other) noexcept : buf_(1024, nullptr, false, AlignOf<largest_scalar_t>(), static_cast<SizeT>(Is64Aware ? FLATBUFFERS_MAX_64_BUFFER_SIZE : FLATBUFFERS_MAX_BUFFER_SIZE)), num_field_loc(0), max_voffset_(0), length_of_64_bit_region_(0), nested(false), finished(false), minalign_(1), force_defaults_(false), dedup_vtables_(true), string_pool(nullptr) { EndianCheck(); // Default construct and swap idiom. // Lack of delegating constructors in vs2010 makes it more verbose than // needed. Swap(other); } /// @brief Move assignment operator for FlatBufferBuilder. FlatBufferBuilderImpl& operator=(FlatBufferBuilderImpl&& other) noexcept { // Move construct a temporary and swap idiom FlatBufferBuilderImpl temp(std::move(other)); Swap(temp); return *this; } void Swap(FlatBufferBuilderImpl& other) { using std::swap; buf_.swap(other.buf_); swap(num_field_loc, other.num_field_loc); swap(max_voffset_, other.max_voffset_); swap(length_of_64_bit_region_, other.length_of_64_bit_region_); swap(nested, other.nested); swap(finished, other.finished); swap(minalign_, other.minalign_); swap(force_defaults_, other.force_defaults_); swap(dedup_vtables_, other.dedup_vtables_); swap(string_pool, other.string_pool); } ~FlatBufferBuilderImpl() { if (string_pool) delete string_pool; } void Reset() { Clear(); // clear builder state buf_.reset(); // deallocate buffer } /// @brief Reset all the state in this FlatBufferBuilder so it can be reused /// to construct another buffer. void Clear() { ClearOffsets(); buf_.clear(); nested = false; finished = false; minalign_ = 1; length_of_64_bit_region_ = 0; if (string_pool) string_pool->clear(); } /// @brief The current size of the serialized buffer, counting from the end. /// @return Returns an `SizeT` with the current size of the buffer. SizeT GetSize() const { return buf_.size(); } /// @brief The current size of the serialized buffer relative to the end of /// the 32-bit region. /// @return Returns an `uoffset_t` with the current size of the buffer. template <bool is_64 = Is64Aware> // Only enable this method for the 64-bit builder, as only that builder is // concerned with the 32/64-bit boundary, and should be the one to bare any // run time costs. typename std::enable_if<is_64, uoffset_t>::type GetSizeRelative32BitRegion() const { //[32-bit region][64-bit region] // [XXXXXXXXXXXXXXXXXXX] GetSize() // [YYYYYYYYYYYYY] length_of_64_bit_region_ // [ZZZZ] return size return static_cast<uoffset_t>(GetSize() - length_of_64_bit_region_); } template <bool is_64 = Is64Aware> // Only enable this method for the 32-bit builder. typename std::enable_if<!is_64, uoffset_t>::type GetSizeRelative32BitRegion() const { return static_cast<uoffset_t>(GetSize()); } /// @brief Get the serialized buffer (after you call `Finish()`). /// @return Returns an `uint8_t` pointer to the FlatBuffer data inside the /// buffer. uint8_t* GetBufferPointer() const { Finished(); return buf_.data(); } /// @brief Get the serialized buffer (after you call `Finish()`) as a span. /// @return Returns a constructed flatbuffers::span that is a view over the /// FlatBuffer data inside the buffer. flatbuffers::span<uint8_t> GetBufferSpan() const { Finished(); return flatbuffers::span<uint8_t>(buf_.data(), buf_.size()); } /// @brief Get a pointer to an unfinished buffer. /// @return Returns a `uint8_t` pointer to the unfinished buffer. uint8_t* GetCurrentBufferPointer() const { return buf_.data(); } /// @brief Get the released DetachedBuffer. /// @return A `DetachedBuffer` that owns the buffer and its allocator. DetachedBuffer Release() { Finished(); DetachedBuffer buffer = buf_.release(); Clear(); return buffer; } /// @brief Get the released pointer to the serialized buffer. /// @param size The size of the memory block containing /// the serialized `FlatBuffer`. /// @param offset The offset from the released pointer where the finished /// `FlatBuffer` starts. /// @return A raw pointer to the start of the memory block containing /// the serialized `FlatBuffer`. /// @remark If the allocator is owned, it gets deleted when the destructor is /// called. uint8_t* ReleaseRaw(size_t& size, size_t& offset) { Finished(); uint8_t* raw = buf_.release_raw(size, offset); Clear(); return raw; } /// @brief get the minimum alignment this buffer needs to be accessed /// properly. This is only known once all elements have been written (after /// you call Finish()). You can use this information if you need to embed /// a FlatBuffer in some other buffer, such that you can later read it /// without first having to copy it into its own buffer. size_t GetBufferMinAlignment() const { Finished(); return minalign_; } /// @cond FLATBUFFERS_INTERNAL void Finished() const { // If you get this assert, you're attempting to get access a buffer // which hasn't been finished yet. Be sure to call // FlatBufferBuilder::Finish with your root table. // If you really need to access an unfinished buffer, call // GetCurrentBufferPointer instead. FLATBUFFERS_ASSERT(finished); } /// @endcond /// @brief In order to save space, fields that are set to their default value /// don't get serialized into the buffer. /// @param[in] fd When set to `true`, always serializes default values that /// are set. Optional fields which are not set explicitly, will still not be /// serialized. void ForceDefaults(bool fd) { force_defaults_ = fd; } /// @brief By default vtables are deduped in order to save space. /// @param[in] dedup When set to `true`, dedup vtables. void DedupVtables(bool dedup) { dedup_vtables_ = dedup; } /// @cond FLATBUFFERS_INTERNAL void Pad(size_t num_bytes) { buf_.fill(num_bytes); } void TrackMinAlign(size_t elem_size) { if (elem_size > minalign_) minalign_ = elem_size; } void Align(size_t elem_size) { TrackMinAlign(elem_size); buf_.fill(PaddingBytes(buf_.size(), elem_size)); } void PushFlatBuffer(const uint8_t* bytes, size_t size) { PushBytes(bytes, size); finished = true; } void PushBytes(const uint8_t* bytes, size_t size) { buf_.push(bytes, size); } void PopBytes(size_t amount) { buf_.pop(amount); } template <typename T> void AssertScalarT() { // The code assumes power of 2 sizes and endian-swap-ability. static_assert(flatbuffers::is_scalar<T>::value, "T must be a scalar type"); } // Write a single aligned scalar to the buffer template <typename T, typename ReturnT = uoffset_t> ReturnT PushElement(T element) { AssertScalarT<T>(); Align(sizeof(T)); buf_.push_small(EndianScalar(element)); return CalculateOffset<ReturnT>(); } template <typename T, template <typename> class OffsetT = Offset> uoffset_t PushElement(OffsetT<T> off) { // Special case for offsets: see ReferTo below. return PushElement(ReferTo(off.o)); } // When writing fields, we track where they are, so we can create correct // vtables later. void TrackField(voffset_t field, uoffset_t off) { FieldLoc fl = {off, field}; buf_.scratch_push_small(fl); num_field_loc++; if (field > max_voffset_) { max_voffset_ = field; } } // Like PushElement, but additionally tracks the field this represents. template <typename T> void AddElement(voffset_t field, T e, T def) { // We don't serialize values equal to the default. if (IsTheSameAs(e, def) && !force_defaults_) return; TrackField(field, PushElement(e)); } template <typename T> void AddElement(voffset_t field, T e) { TrackField(field, PushElement(e)); } template <typename T> void AddOffset(voffset_t field, Offset<T> off) { if (off.IsNull()) return; // Don't store. AddElement(field, ReferTo(off.o), static_cast<uoffset_t>(0)); } template <typename T> void AddOffset(voffset_t field, Offset64<T> off) { if (off.IsNull()) return; // Don't store. AddElement(field, ReferTo(off.o), static_cast<uoffset64_t>(0)); } template <typename T> void AddStruct(voffset_t field, const T* structptr) { if (!structptr) return; // Default, don't store. Align(AlignOf<T>()); buf_.push_small(*structptr); TrackField(field, CalculateOffset<uoffset_t>()); } void AddStructOffset(voffset_t field, uoffset_t off) { TrackField(field, off); } // Offsets initially are relative to the end of the buffer (downwards). // This function converts them to be relative to the current location // in the buffer (when stored here), pointing upwards. uoffset_t ReferTo(uoffset_t off) { // Align to ensure GetSizeRelative32BitRegion() below is correct. Align(sizeof(uoffset_t)); // 32-bit offsets are relative to the tail of the 32-bit region of the // buffer. For most cases (without 64-bit entities) this is equivalent to // size of the whole buffer (e.g. GetSize()) return ReferTo(off, GetSizeRelative32BitRegion()); } uoffset64_t ReferTo(uoffset64_t off) { // Align to ensure GetSize() below is correct. Align(sizeof(uoffset64_t)); // 64-bit offsets are relative to tail of the whole buffer return ReferTo(off, GetSize()); } template <typename T, typename T2> T ReferTo(const T off, const T2 size) { FLATBUFFERS_ASSERT(off && off <= size); return size - off + static_cast<T>(sizeof(T)); } template <typename T> T ReferTo(const T off, const T size) { FLATBUFFERS_ASSERT(off && off <= size); return size - off + static_cast<T>(sizeof(T)); } void NotNested() { // If you hit this, you're trying to construct a Table/Vector/String // during the construction of its parent table (between the MyTableBuilder // and table.Finish(). // Move the creation of these sub-objects to above the MyTableBuilder to // not get this assert. // Ignoring this assert may appear to work in simple cases, but the reason // it is here is that storing objects in-line may cause vtable offsets // to not fit anymore. It also leads to vtable duplication. FLATBUFFERS_ASSERT(!nested); // If you hit this, fields were added outside the scope of a table. FLATBUFFERS_ASSERT(!num_field_loc); } // From generated code (or from the parser), we call StartTable/EndTable // with a sequence of AddElement calls in between. uoffset_t StartTable() { NotNested(); nested = true; return GetSizeRelative32BitRegion(); } // This finishes one serialized object by generating the vtable if it's a // table, comparing it against existing vtables, and writing the // resulting vtable offset. uoffset_t EndTable(uoffset_t start) { // If you get this assert, a corresponding StartTable wasn't called. FLATBUFFERS_ASSERT(nested); // Write the vtable offset, which is the start of any Table. // We fill its value later. // This is relative to the end of the 32-bit region. const uoffset_t vtable_offset_loc = static_cast<uoffset_t>(PushElement<soffset_t>(0)); // Write a vtable, which consists entirely of voffset_t elements. // It starts with the number of offsets, followed by a type id, followed // by the offsets themselves. In reverse: // Include space for the last offset and ensure empty tables have a // minimum size. max_voffset_ = (std::max)(static_cast<voffset_t>(max_voffset_ + sizeof(voffset_t)), FieldIndexToOffset(0)); buf_.fill_big(max_voffset_); const uoffset_t table_object_size = vtable_offset_loc - start; // Vtable use 16bit offsets. FLATBUFFERS_ASSERT(table_object_size < 0x10000); WriteScalar<voffset_t>(buf_.data() + sizeof(voffset_t), static_cast<voffset_t>(table_object_size)); WriteScalar<voffset_t>(buf_.data(), max_voffset_); // Write the offsets into the table for (auto it = buf_.scratch_end() - num_field_loc * sizeof(FieldLoc); it < buf_.scratch_end(); it += sizeof(FieldLoc)) { auto field_location = reinterpret_cast<FieldLoc*>(it); const voffset_t pos = static_cast<voffset_t>(vtable_offset_loc - field_location->off); // If this asserts, it means you've set a field twice. FLATBUFFERS_ASSERT( !ReadScalar<voffset_t>(buf_.data() + field_location->id)); WriteScalar<voffset_t>(buf_.data() + field_location->id, pos); } ClearOffsets(); auto vt1 = reinterpret_cast<voffset_t*>(buf_.data()); auto vt1_size = ReadScalar<voffset_t>(vt1); auto vt_use = GetSizeRelative32BitRegion(); // See if we already have generated a vtable with this exact same // layout before. If so, make it point to the old one, remove this one. if (dedup_vtables_) { for (auto it = buf_.scratch_data(); it < buf_.scratch_end(); it += sizeof(uoffset_t)) { auto vt_offset_ptr = reinterpret_cast<uoffset_t*>(it); auto vt2 = reinterpret_cast<voffset_t*>( buf_.data_at(*vt_offset_ptr + length_of_64_bit_region_)); auto vt2_size = ReadScalar<voffset_t>(vt2); if (vt1_size != vt2_size || 0 != memcmp(vt2, vt1, vt1_size)) continue; vt_use = *vt_offset_ptr; buf_.pop(GetSizeRelative32BitRegion() - vtable_offset_loc); break; } } // If this is a new vtable, remember it. if (vt_use == GetSizeRelative32BitRegion()) { buf_.scratch_push_small(vt_use); } // Fill the vtable offset we created above. // The offset points from the beginning of the object to where the vtable is // stored. // Offsets default direction is downward in memory for future format // flexibility (storing all vtables at the start of the file). WriteScalar(buf_.data_at(vtable_offset_loc + length_of_64_bit_region_), static_cast<soffset_t>(vt_use) - static_cast<soffset_t>(vtable_offset_loc)); nested = false; return vtable_offset_loc; } FLATBUFFERS_ATTRIBUTE([[deprecated("call the version above instead")]]) uoffset_t EndTable(uoffset_t start, voffset_t /*numfields*/) { return EndTable(start); } // This checks a required field has been set in a given table that has // just been constructed. template <typename T> void Required(Offset<T> table, voffset_t field) { auto table_ptr = reinterpret_cast<const Table*>(buf_.data_at(table.o)); bool ok = table_ptr->GetOptionalFieldOffset(field) != 0; // If this fails, the caller will show what field needs to be set. FLATBUFFERS_ASSERT(ok); (void)ok; } uoffset_t StartStruct(size_t alignment) { Align(alignment); return GetSizeRelative32BitRegion(); } uoffset_t EndStruct() { return GetSizeRelative32BitRegion(); } void ClearOffsets() { buf_.scratch_pop(num_field_loc * sizeof(FieldLoc)); num_field_loc = 0; max_voffset_ = 0; } // Aligns such that when "len" bytes are written, an object can be written // after it (forward in the buffer) with "alignment" without padding. void PreAlign(size_t len, size_t alignment) { if (len == 0) return; TrackMinAlign(alignment); buf_.fill(PaddingBytes(GetSize() + len, alignment)); } // Aligns such than when "len" bytes are written, an object of type `AlignT` // can be written after it (forward in the buffer) without padding. template <typename AlignT> void PreAlign(size_t len) { AssertScalarT<AlignT>(); PreAlign(len, AlignOf<AlignT>()); } /// @endcond /// @brief Store a string in the buffer, which can contain any binary data. /// @param[in] str A const char pointer to the data to be stored as a string. /// @param[in] len The number of bytes that should be stored from `str`. /// @return Returns the offset in the buffer where the string starts. template <template <typename> class OffsetT = Offset> OffsetT<String> CreateString(const char* str, size_t len) { CreateStringImpl(str, len); return OffsetT<String>( CalculateOffset<typename OffsetT<String>::offset_type>()); } /// @brief Store a string in the buffer, which is null-terminated. /// @param[in] str A const char pointer to a C-string to add to the buffer. /// @return Returns the offset in the buffer where the string starts. template <template <typename> class OffsetT = Offset> OffsetT<String> CreateString(const char* str) { return CreateString<OffsetT>(str, strlen(str)); } /// @brief Store a string in the buffer, which is null-terminated. /// @param[in] str A char pointer to a C-string to add to the buffer. /// @return Returns the offset in the buffer where the string starts. template <template <typename> class OffsetT = Offset> OffsetT<String> CreateString(char* str) { return CreateString<OffsetT>(str, strlen(str)); } /// @brief Store a string in the buffer, which can contain any binary data. /// @param[in] str A const reference to a std::string to store in the buffer. /// @return Returns the offset in the buffer where the string starts. template <template <typename> class OffsetT = Offset> OffsetT<String> CreateString(const std::string& str) { return CreateString<OffsetT>(str.c_str(), str.length()); } // clang-format off #ifdef FLATBUFFERS_HAS_STRING_VIEW /// @brief Store a string in the buffer, which can contain any binary data. /// @param[in] str A const string_view to copy in to the buffer. /// @return Returns the offset in the buffer where the string starts. template<template <typename> class OffsetT = Offset> OffsetT<String>CreateString(flatbuffers::string_view str) { return CreateString<OffsetT>(str.data(), str.size()); } #endif // FLATBUFFERS_HAS_STRING_VIEW // clang-format on /// @brief Store a string in the buffer, which can contain any binary data. /// @param[in] str A const pointer to a `String` struct to add to the buffer. /// @return Returns the offset in the buffer where the string starts template <template <typename> class OffsetT = Offset> OffsetT<String> CreateString(const String* str) { return str ? CreateString<OffsetT>(str->c_str(), str->size()) : 0; } /// @brief Store a string in the buffer, which can contain any binary data. /// @param[in] str A const reference to a std::string like type with support /// of T::data() and T::length() to store in the buffer. /// @return Returns the offset in the buffer where the string starts. template <template <typename> class OffsetT = Offset, // No need to explicitly declare the T type, let the compiler deduce // it. int&... ExplicitArgumentBarrier, typename T> OffsetT<String> CreateString(const T& str) { return CreateString<OffsetT>(str.data(), str.length()); } /// @brief Store a string in the buffer, which can contain any binary data. /// If a string with this exact contents has already been serialized before, /// instead simply returns the offset of the existing string. This uses a map /// stored on the heap, but only stores the numerical offsets. /// @param[in] str A const char pointer to the data to be stored as a string. /// @param[in] len The number of bytes that should be stored from `str`. /// @return Returns the offset in the buffer where the string starts. Offset<String> CreateSharedString(const char* str, size_t len) { FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); if (!string_pool) { string_pool = new StringOffsetMap(StringOffsetCompare(buf_)); } const size_t size_before_string = buf_.size(); // Must first serialize the string, since the set is all offsets into // buffer. const Offset<String> off = CreateString<Offset>(str, len); auto it = string_pool->find(off); // If it exists we reuse existing serialized data! if (it != string_pool->end()) { // We can remove the string we serialized. buf_.pop(buf_.size() - size_before_string); return *it; } // Record this string for future use. string_pool->insert(off); return off; } #ifdef FLATBUFFERS_HAS_STRING_VIEW /// @brief Store a string in the buffer, which can contain any binary data. /// If a string with this exact contents has already been serialized before, /// instead simply returns the offset of the existing string. This uses a map /// stored on the heap, but only stores the numerical offsets. /// @param[in] str A const std::string_view to store in the buffer. /// @return Returns the offset in the buffer where the string starts Offset<String> CreateSharedString(const flatbuffers::string_view str) { return CreateSharedString(str.data(), str.size()); } #else /// @brief Store a string in the buffer, which null-terminated. /// If a string with this exact contents has already been serialized before, /// instead simply returns the offset of the existing string. This uses a map /// stored on the heap, but only stores the numerical offsets. /// @param[in] str A const char pointer to a C-string to add to the buffer. /// @return Returns the offset in the buffer where the string starts. Offset<String> CreateSharedString(const char* str) { return CreateSharedString(str, strlen(str)); } /// @brief Store a string in the buffer, which can contain any binary data. /// If a string with this exact contents has already been serialized before, /// instead simply returns the offset of the existing string. This uses a map /// stored on the heap, but only stores the numerical offsets. /// @param[in] str A const reference to a std::string to store in the buffer. /// @return Returns the offset in the buffer where the string starts. Offset<String> CreateSharedString(const std::string& str) { return CreateSharedString(str.c_str(), str.length()); } #endif /// @brief Store a string in the buffer, which can contain any binary data. /// If a string with this exact contents has already been serialized before, /// instead simply returns the offset of the existing string. This uses a map /// stored on the heap, but only stores the numerical offsets. /// @param[in] str A const pointer to a `String` struct to add to the buffer. /// @return Returns the offset in the buffer where the string starts Offset<String> CreateSharedString(const String* str) { return str ? CreateSharedString(str->c_str(), str->size()) : 0; } /// @cond FLATBUFFERS_INTERNAL template <typename LenT = uoffset_t, typename ReturnT = uoffset_t> ReturnT EndVector(size_t len) { FLATBUFFERS_ASSERT(nested); // Hit if no corresponding StartVector. nested = false; return PushElement<LenT, ReturnT>(static_cast<LenT>(len)); } template <template <typename> class OffsetT = Offset, typename LenT = uint32_t> void StartVector(size_t len, size_t elemsize, size_t alignment) { NotNested(); nested = true; // Align to the Length type of the vector (either 32-bit or 64-bit), so // that the length of the buffer can be added without padding. PreAlign<LenT>(len * elemsize); PreAlign(len * elemsize, alignment); // Just in case elemsize > uoffset_t. } template <typename T, template <typename> class OffsetT = Offset, typename LenT = uint32_t> void StartVector(size_t len) { return StartVector<OffsetT, LenT>(len, sizeof(T), AlignOf<T>()); } // Call this right before StartVector/CreateVector if you want to force the // alignment to be something different than what the element size would // normally dictate. // This is useful when storing a nested_flatbuffer in a vector of bytes, // or when storing SIMD floats, etc. void ForceVectorAlignment(const size_t len, const size_t elemsize, const size_t alignment) { if (len == 0) return; FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); PreAlign(len * elemsize, alignment); } template <bool is_64 = Is64Aware> typename std::enable_if<is_64, void>::type ForceVectorAlignment64( const size_t len, const size_t elemsize, const size_t alignment) { // If you hit this assertion, you are trying to force alignment on a // vector with offset64 after serializing a 32-bit offset. FLATBUFFERS_ASSERT(GetSize() == length_of_64_bit_region_); // Call through. ForceVectorAlignment(len, elemsize, alignment); // Update the 64 bit region. length_of_64_bit_region_ = GetSize(); } // Similar to ForceVectorAlignment but for String fields. void ForceStringAlignment(size_t len, size_t alignment) { if (len == 0) return; FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); PreAlign((len + 1) * sizeof(char), alignment); } /// @endcond /// @brief Serialize an array into a FlatBuffer `vector`. /// @tparam T The data type of the array elements. /// @tparam OffsetT the type of offset to return /// @tparam VectorT the type of vector to cast to. /// @param[in] v A pointer to the array of type `T` to serialize into the /// buffer as a `vector`. /// @param[in] len The number of elements to serialize. /// @return Returns a typed `TOffset` into the serialized data indicating /// where the vector is stored. template <typename T, template <typename...> class OffsetT = Offset, template <typename...> class VectorT = Vector> OffsetT<VectorT<T>> CreateVector(const T* v, size_t len) { // The type of the length field in the vector. typedef typename VectorT<T>::size_type LenT; typedef typename OffsetT<VectorT<T>>::offset_type offset_type; // If this assert hits, you're specifying a template argument that is // causing the wrong overload to be selected, remove it. AssertScalarT<T>(); StartVector<T, OffsetT, LenT>(len); if (len > 0) { // clang-format off #if FLATBUFFERS_LITTLEENDIAN PushBytes(reinterpret_cast<const uint8_t *>(v), len * sizeof(T)); #else if (sizeof(T) == 1) { PushBytes(reinterpret_cast<const uint8_t *>(v), len); } else { for (auto i = len; i > 0; ) { PushElement(v[--i]); } } #endif // clang-format on } return OffsetT<VectorT<T>>(EndVector<LenT, offset_type>(len)); } /// @brief Serialize an array like object into a FlatBuffer `vector`. /// @tparam T The data type of the array elements. /// @tparam C The type of the array. /// @param[in] array A reference to an array like object of type `T` to /// serialize into the buffer as a `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, class C> Offset<Vector<T>> CreateVector(const C& array) { return CreateVector(array.data(), array.size()); } /// @brief Serialize an initializer list into a FlatBuffer `vector`. /// @tparam T The data type of the initializer list elements. /// @param[in] v The value of the initializer list. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T> Offset<Vector<T>> CreateVector(std::initializer_list<T> v) { return CreateVector(v.begin(), v.size()); } template <typename T> Offset<Vector<Offset<T>>> CreateVector(const Offset<T>* v, size_t len) { StartVector<Offset<T>>(len); for (auto i = len; i > 0;) { PushElement(v[--i]); } return Offset<Vector<Offset<T>>>(EndVector(len)); } /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. /// @tparam T The data type of the `std::vector` elements. /// @param v A const reference to the `std::vector` to serialize into the /// buffer as a `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename Alloc = std::allocator<T>> Offset<Vector<T>> CreateVector(const std::vector<T, Alloc>& v) { return CreateVector(data(v), v.size()); } template <template <typename...> class VectorT = Vector64, int&... ExplicitArgumentBarrier, typename T> Offset64<VectorT<T>> CreateVector64(const std::vector<T>& v) { return CreateVector<T, Offset64, VectorT>(data(v), v.size()); } // vector<bool> may be implemented using a bit-set, so we can't access it as // an array. Instead, read elements manually. // Background: https://isocpp.org/blog/2012/11/on-vectorbool Offset<Vector<uint8_t>> CreateVector(const std::vector<bool>& v) { StartVector<uint8_t>(v.size()); for (auto i = v.size(); i > 0;) { PushElement(static_cast<uint8_t>(v[--i])); } return Offset<Vector<uint8_t>>(EndVector(v.size())); } Offset64<Vector64<uint8_t>> CreateVector64(const std::vector<bool>& v) { StartVector<uint8_t, Offset64, Vector64<uint8_t>::size_type>(v.size()); for (auto i = v.size(); i > 0;) { PushElement(static_cast<uint8_t>(v[--i])); } return Offset64<Vector64<uint8_t>>( EndVector<Vector64<uint8_t>::size_type, Offset64<Vector64<uint8_t>>::offset_type>(v.size())); } /// @brief Serialize values returned by a function into a FlatBuffer `vector`. /// This is a convenience function that takes care of iteration for you. /// @tparam T The data type of the `std::vector` elements. /// @param f A function that takes the current iteration 0..vector_size-1 and /// returns any type that you can construct a FlatBuffers vector out of. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T> Offset<Vector<T>> CreateVector(size_t vector_size, const std::function<T(size_t i)>& f) { FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); std::vector<T> elems(vector_size); for (size_t i = 0; i < vector_size; i++) elems[i] = f(i); return CreateVector(elems); } /// @brief Serialize values returned by a function into a FlatBuffer `vector`. /// This is a convenience function that takes care of iteration for you. This /// uses a vector stored on the heap to store the intermediate results of the /// iteration. /// @tparam T The data type of the `std::vector` elements. /// @param f A function that takes the current iteration 0..vector_size-1, /// and the state parameter returning any type that you can construct a /// FlatBuffers vector out of. /// @param state State passed to f. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename F, typename S> Offset<Vector<T>> CreateVector(size_t vector_size, F f, S* state) { FLATBUFFERS_ASSERT(FLATBUFFERS_GENERAL_HEAP_ALLOC_OK); std::vector<T> elems(vector_size); for (size_t i = 0; i < vector_size; i++) elems[i] = f(i, state); return CreateVector(elems); } /// @brief Serialize a `std::vector<StringType>` into a FlatBuffer `vector`. /// whereas StringType is any type that is accepted by the CreateString() /// overloads. /// This is a convenience function for a common case. /// @param v A const reference to the `std::vector` to serialize into the /// buffer as a `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename StringType = std::string, typename Alloc = std::allocator<StringType>> Offset<Vector<Offset<String>>> CreateVectorOfStrings( const std::vector<StringType, Alloc>& v) { return CreateVectorOfStrings(v.cbegin(), v.cend()); } /// @brief Serialize a collection of Strings into a FlatBuffer `vector`. /// This is a convenience function for a common case. /// @param begin The beginning iterator of the collection /// @param end The ending iterator of the collection /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <class It> Offset<Vector<Offset<String>>> CreateVectorOfStrings(It begin, It end) { auto distance = std::distance(begin, end); FLATBUFFERS_ASSERT(distance >= 0); auto size = static_cast<size_t>(distance); auto scratch_buffer_usage = size * sizeof(Offset<String>); // If there is not enough space to store the offsets, there definitely won't // be enough space to store all the strings. So ensuring space for the // scratch region is OK, for if it fails, it would have failed later. buf_.ensure_space(scratch_buffer_usage); for (auto it = begin; it != end; ++it) { buf_.scratch_push_small(CreateString(*it)); } StartVector<Offset<String>>(size); for (size_t i = 1; i <= size; i++) { // Note we re-evaluate the buf location each iteration to account for any // underlying buffer resizing that may occur. PushElement(*reinterpret_cast<Offset<String>*>( buf_.scratch_end() - i * sizeof(Offset<String>))); } buf_.scratch_pop(scratch_buffer_usage); return Offset<Vector<Offset<String>>>(EndVector(size)); } /// @brief Serialize an array of structs into a FlatBuffer `vector`. /// @tparam T The data type of the struct array elements. /// @param[in] v A pointer to the array of type `T` to serialize into the /// buffer as a `vector`. /// @param[in] len The number of elements to serialize. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, template <typename...> class OffsetT = Offset, template <typename...> class VectorT = Vector> OffsetT<VectorT<const T*>> CreateVectorOfStructs(const T* v, size_t len) { // The type of the length field in the vector. typedef typename VectorT<T>::size_type LenT; typedef typename OffsetT<VectorT<const T*>>::offset_type offset_type; StartVector<OffsetT, LenT>(len, sizeof(T), AlignOf<T>()); if (len > 0) { PushBytes(reinterpret_cast<const uint8_t*>(v), sizeof(T) * len); } return OffsetT<VectorT<const T*>>(EndVector<LenT, offset_type>(len)); } /// @brief Serialize an array of structs into a FlatBuffer `vector`. /// @tparam T The data type of the struct array elements. /// @param[in] filler A function that takes the current iteration /// 0..vector_size-1 and a pointer to the struct that must be filled. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. /// This is mostly useful when flatbuffers are generated with mutation /// accessors. template <typename T> Offset<Vector<const T*>> CreateVectorOfStructs( size_t vector_size, const std::function<void(size_t i, T*)>& filler) { T* structs = StartVectorOfStructs<T>(vector_size); for (size_t i = 0; i < vector_size; i++) { filler(i, structs); structs++; } return EndVectorOfStructs<T>(vector_size); } /// @brief Serialize an array of structs into a FlatBuffer `vector`. /// @tparam T The data type of the struct array elements. /// @param[in] f A function that takes the current iteration 0..vector_size-1, /// a pointer to the struct that must be filled and the state argument. /// @param[in] state Arbitrary state to pass to f. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. /// This is mostly useful when flatbuffers are generated with mutation /// accessors. template <typename T, typename F, typename S> Offset<Vector<const T*>> CreateVectorOfStructs(size_t vector_size, F f, S* state) { T* structs = StartVectorOfStructs<T>(vector_size); for (size_t i = 0; i < vector_size; i++) { f(i, structs, state); structs++; } return EndVectorOfStructs<T>(vector_size); } /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector`. /// @tparam T The data type of the `std::vector` struct elements. /// @param[in] v A const reference to the `std::vector` of structs to /// serialize into the buffer as a `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, template <typename...> class OffsetT = Offset, template <typename...> class VectorT = Vector, typename Alloc = std::allocator<T>> OffsetT<VectorT<const T*>> CreateVectorOfStructs( const std::vector<T, Alloc>& v) { return CreateVectorOfStructs<T, OffsetT, VectorT>(data(v), v.size()); } template <template <typename...> class VectorT = Vector64, int&..., typename T> Offset64<VectorT<const T*>> CreateVectorOfStructs64(const std::vector<T>& v) { return CreateVectorOfStructs<T, Offset64, VectorT>(data(v), v.size()); } /// @brief Serialize an array of native structs into a FlatBuffer `vector`. /// @tparam T The data type of the struct array elements. /// @tparam S The data type of the native struct array elements. /// @param[in] v A pointer to the array of type `S` to serialize into the /// buffer as a `vector`. /// @param[in] len The number of elements to serialize. /// @param[in] pack_func Pointer to a function to convert the native struct /// to the FlatBuffer struct. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename S> Offset<Vector<const T*>> CreateVectorOfNativeStructs( const S* v, size_t len, T (*const pack_func)(const S&)) { FLATBUFFERS_ASSERT(pack_func); auto structs = StartVectorOfStructs<T>(len); for (size_t i = 0; i < len; i++) { structs[i] = pack_func(v[i]); } return EndVectorOfStructs<T>(len); } /// @brief Serialize an array of native structs into a FlatBuffer `vector`. /// @tparam T The data type of the struct array elements. /// @tparam S The data type of the native struct array elements. /// @param[in] v A pointer to the array of type `S` to serialize into the /// buffer as a `vector`. /// @param[in] len The number of elements to serialize. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename S> Offset<Vector<const T*>> CreateVectorOfNativeStructs(const S* v, size_t len) { extern T Pack(const S&); return CreateVectorOfNativeStructs(v, len, Pack); } /// @brief Serialize a `std::vector` of native structs into a FlatBuffer /// `vector`. /// @tparam T The data type of the `std::vector` struct elements. /// @tparam S The data type of the `std::vector` native struct elements. /// @param[in] v A const reference to the `std::vector` of structs to /// serialize into the buffer as a `vector`. /// @param[in] pack_func Pointer to a function to convert the native struct /// to the FlatBuffer struct. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename S, typename Alloc = std::allocator<T>> Offset<Vector<const T*>> CreateVectorOfNativeStructs( const std::vector<S, Alloc>& v, T (*const pack_func)(const S&)) { return CreateVectorOfNativeStructs<T, S>(data(v), v.size(), pack_func); } /// @brief Serialize a `std::vector` of native structs into a FlatBuffer /// `vector`. /// @tparam T The data type of the `std::vector` struct elements. /// @tparam S The data type of the `std::vector` native struct elements. /// @param[in] v A const reference to the `std::vector` of structs to /// serialize into the buffer as a `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename S, typename Alloc = std::allocator<S>> Offset<Vector<const T*>> CreateVectorOfNativeStructs( const std::vector<S, Alloc>& v) { return CreateVectorOfNativeStructs<T, S>(data(v), v.size()); } /// @cond FLATBUFFERS_INTERNAL template <typename T> struct StructKeyComparator { bool operator()(const T& a, const T& b) const { return a.KeyCompareLessThan(&b); } }; /// @endcond /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector` /// in sorted order. /// @tparam T The data type of the `std::vector` struct elements. /// @param[in] v A const reference to the `std::vector` of structs to /// serialize into the buffer as a `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename Alloc = std::allocator<T>> Offset<Vector<const T*>> CreateVectorOfSortedStructs( std::vector<T, Alloc>* v) { return CreateVectorOfSortedStructs(data(*v), v->size()); } /// @brief Serialize a `std::vector` of native structs into a FlatBuffer /// `vector` in sorted order. /// @tparam T The data type of the `std::vector` struct elements. /// @tparam S The data type of the `std::vector` native struct elements. /// @param[in] v A const reference to the `std::vector` of structs to /// serialize into the buffer as a `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename S, typename Alloc = std::allocator<T>> Offset<Vector<const T*>> CreateVectorOfSortedNativeStructs( std::vector<S, Alloc>* v) { return CreateVectorOfSortedNativeStructs<T, S>(data(*v), v->size()); } /// @brief Serialize an array of structs into a FlatBuffer `vector` in sorted /// order. /// @tparam T The data type of the struct array elements. /// @param[in] v A pointer to the array of type `T` to serialize into the /// buffer as a `vector`. /// @param[in] len The number of elements to serialize. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T> Offset<Vector<const T*>> CreateVectorOfSortedStructs(T* v, size_t len) { std::stable_sort(v, v + len, StructKeyComparator<T>()); return CreateVectorOfStructs(v, len); } /// @brief Serialize an array of native structs into a FlatBuffer `vector` in /// sorted order. /// @tparam T The data type of the struct array elements. /// @tparam S The data type of the native struct array elements. /// @param[in] v A pointer to the array of type `S` to serialize into the /// buffer as a `vector`. /// @param[in] len The number of elements to serialize. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename S> Offset<Vector<const T*>> CreateVectorOfSortedNativeStructs(S* v, size_t len) { extern T Pack(const S&); auto structs = StartVectorOfStructs<T>(len); for (size_t i = 0; i < len; i++) { structs[i] = Pack(v[i]); } std::stable_sort(structs, structs + len, StructKeyComparator<T>()); return EndVectorOfStructs<T>(len); } /// @cond FLATBUFFERS_INTERNAL template <typename T> struct TableKeyComparator { explicit TableKeyComparator(vector_downward<SizeT>& buf) : buf_(buf) {} TableKeyComparator(const TableKeyComparator& other) : buf_(other.buf_) {} bool operator()(const Offset<T>& a, const Offset<T>& b) const { auto table_a = reinterpret_cast<T*>(buf_.data_at(a.o)); auto table_b = reinterpret_cast<T*>(buf_.data_at(b.o)); return table_a->KeyCompareLessThan(table_b); } vector_downward<SizeT>& buf_; private: FLATBUFFERS_DELETE_FUNC( TableKeyComparator& operator=(const TableKeyComparator& other)); }; /// @endcond /// @brief Serialize an array of `table` offsets as a `vector` in the buffer /// in sorted order. /// @tparam T The data type that the offset refers to. /// @param[in] v An array of type `Offset<T>` that contains the `table` /// offsets to store in the buffer in sorted order. /// @param[in] len The number of elements to store in the `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T> Offset<Vector<Offset<T>>> CreateVectorOfSortedTables(Offset<T>* v, size_t len) { std::stable_sort(v, v + len, TableKeyComparator<T>(buf_)); return CreateVector(v, len); } /// @brief Serialize an array of `table` offsets as a `vector` in the buffer /// in sorted order. /// @tparam T The data type that the offset refers to. /// @param[in] v An array of type `Offset<T>` that contains the `table` /// offsets to store in the buffer in sorted order. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template <typename T, typename Alloc = std::allocator<T>> Offset<Vector<Offset<T>>> CreateVectorOfSortedTables( std::vector<Offset<T>, Alloc>* v) { return CreateVectorOfSortedTables(data(*v), v->size()); } /// @brief Specialized version of `CreateVector` for non-copying use cases. /// Write the data any time later to the returned buffer pointer `buf`. /// @param[in] len The number of elements to store in the `vector`. /// @param[in] elemsize The size of each element in the `vector`. /// @param[out] buf A pointer to a `uint8_t` pointer that can be /// written to at a later time to serialize the data into a `vector` /// in the buffer. uoffset_t CreateUninitializedVector(size_t len, size_t elemsize, size_t alignment, uint8_t** buf) { NotNested(); StartVector(len, elemsize, alignment); buf_.make_space(len * elemsize); const uoffset_t vec_start = GetSizeRelative32BitRegion(); auto vec_end = EndVector(len); *buf = buf_.data_at(vec_start); return vec_end; } FLATBUFFERS_ATTRIBUTE([[deprecated("call the version above instead")]]) uoffset_t CreateUninitializedVector(size_t len, size_t elemsize, uint8_t** buf) { return CreateUninitializedVector(len, elemsize, elemsize, buf); } /// @brief Specialized version of `CreateVector` for non-copying use cases. /// Write the data any time later to the returned buffer pointer `buf`. /// @tparam T The data type of the data that will be stored in the buffer /// as a `vector`. /// @param[in] len The number of elements to store in the `vector`. /// @param[out] buf A pointer to a pointer of type `T` that can be /// written to at a later time to serialize the data into a `vector` /// in the buffer. template <typename T> Offset<Vector<T>> CreateUninitializedVector(size_t len, T** buf) { AssertScalarT<T>(); return CreateUninitializedVector(len, sizeof(T), AlignOf<T>(), reinterpret_cast<uint8_t**>(buf)); } template <typename T> Offset<Vector<const T*>> CreateUninitializedVectorOfStructs(size_t len, T** buf) { return CreateUninitializedVector(len, sizeof(T), AlignOf<T>(), reinterpret_cast<uint8_t**>(buf)); } // @brief Create a vector of scalar type T given as input a vector of scalar // type U, useful with e.g. pre "enum class" enums, or any existing scalar // data of the wrong type. template <typename T, typename U> Offset<Vector<T>> CreateVectorScalarCast(const U* v, size_t len) { AssertScalarT<T>(); AssertScalarT<U>(); StartVector<T>(len); for (auto i = len; i > 0;) { PushElement(static_cast<T>(v[--i])); } return Offset<Vector<T>>(EndVector(len)); } /// @brief Write a struct by itself, typically to be part of a union. template <typename T> Offset<const T*> CreateStruct(const T& structobj) { NotNested(); Align(AlignOf<T>()); buf_.push_small(structobj); return Offset<const T*>( CalculateOffset<typename Offset<const T*>::offset_type>()); } /// @brief Finish serializing a buffer by writing the root offset. /// @param[in] file_identifier If a `file_identifier` is given, the buffer /// will be prefixed with a standard FlatBuffers file header. template <typename T> void Finish(Offset<T> root, const char* file_identifier = nullptr) { Finish(root.o, file_identifier, false); } /// @brief Finish a buffer with a 32 bit size field pre-fixed (size of the /// buffer following the size field). These buffers are NOT compatible /// with standard buffers created by Finish, i.e. you can't call GetRoot /// on them, you have to use GetSizePrefixedRoot instead. /// All >32 bit quantities in this buffer will be aligned when the whole /// size pre-fixed buffer is aligned. /// These kinds of buffers are useful for creating a stream of FlatBuffers. template <typename T> void FinishSizePrefixed(Offset<T> root, const char* file_identifier = nullptr) { Finish(root.o, file_identifier, true); } void SwapBufAllocator(FlatBufferBuilderImpl& other) { buf_.swap_allocator(other.buf_); } /// @brief The length of a FlatBuffer file header. static const size_t kFileIdentifierLength = ::flatbuffers::kFileIdentifierLength; protected: // You shouldn't really be copying instances of this class. FlatBufferBuilderImpl(const FlatBufferBuilderImpl&); FlatBufferBuilderImpl& operator=(const FlatBufferBuilderImpl&); void Finish(uoffset_t root, const char* file_identifier, bool size_prefix) { // A buffer can only be finished once. To reuse a builder use `clear()`. FLATBUFFERS_ASSERT(!finished); NotNested(); buf_.clear_scratch(); const size_t prefix_size = size_prefix ? sizeof(SizeT) : 0; // Make sure we track the alignment of the size prefix. TrackMinAlign(prefix_size); const size_t root_offset_size = sizeof(uoffset_t); const size_t file_id_size = file_identifier ? kFileIdentifierLength : 0; // This will cause the whole buffer to be aligned. PreAlign(prefix_size + root_offset_size + file_id_size, minalign_); if (file_identifier) { FLATBUFFERS_ASSERT(strlen(file_identifier) == kFileIdentifierLength); PushBytes(reinterpret_cast<const uint8_t*>(file_identifier), kFileIdentifierLength); } PushElement(ReferTo(root)); // Location of root. if (size_prefix) { PushElement(GetSize()); } finished = true; } struct FieldLoc { uoffset_t off; voffset_t id; }; vector_downward<SizeT> buf_; // Accumulating offsets of table members while it is being built. // We store these in the scratch pad of buf_, after the vtable offsets. uoffset_t num_field_loc; // Track how much of the vtable is in use, so we can output the most compact // possible vtable. voffset_t max_voffset_; // This is the length of the 64-bit region of the buffer. The buffer supports // 64-bit offsets by forcing serialization of those elements in the "tail" // region of the buffer (i.e. "64-bit region"). To properly keep track of // offsets that are referenced from the tail of the buffer to not overflow // their size (e.g. Offset is a uint32_t type), the boundary of the 32-/64-bit // regions must be tracked. // // [ Complete FlatBuffer ] // [32-bit region][64-bit region] // ^ ^ // | Tail of the buffer. // | // Tail of the 32-bit region of the buffer. // // This keeps track of the size of the 64-bit region so that the tail of the // 32-bit region can be calculated as `GetSize() - length_of_64_bit_region_`. // // This will remain 0 if no 64-bit offset types are added to the buffer. size_t length_of_64_bit_region_; // Ensure objects are not nested. bool nested; // Ensure the buffer is finished before it is being accessed. bool finished; size_t minalign_; bool force_defaults_; // Serialize values equal to their defaults anyway. bool dedup_vtables_; struct StringOffsetCompare { explicit StringOffsetCompare(const vector_downward<SizeT>& buf) : buf_(&buf) {} bool operator()(const Offset<String>& a, const Offset<String>& b) const { auto stra = reinterpret_cast<const String*>(buf_->data_at(a.o)); auto strb = reinterpret_cast<const String*>(buf_->data_at(b.o)); return StringLessThan(stra->data(), stra->size(), strb->data(), strb->size()); } const vector_downward<SizeT>* buf_; }; // For use with CreateSharedString. Instantiated on first use only. typedef std::set<Offset<String>, StringOffsetCompare> StringOffsetMap; StringOffsetMap* string_pool; private: void CanAddOffset64() { // If you hit this assertion, you are attempting to add a 64-bit offset to // a 32-bit only builder. This is because the builder has overloads that // differ only on the offset size returned: e.g.: // // FlatBufferBuilder builder; // Offset64<String> string_offset = builder.CreateString<Offset64>(); // // Either use a 64-bit aware builder, or don't try to create an Offset64 // return type. // // TODO(derekbailey): we can probably do more enable_if to avoid this // looking like its possible to the user. static_assert(Is64Aware, "cannot add 64-bit offset to a 32-bit builder"); // If you hit this assertion, you are attempting to add an 64-bit offset // item after already serializing a 32-bit item. All 64-bit offsets have to // added to the tail of the buffer before any 32-bit items can be added. // Otherwise some items might not be addressable due to the maximum range of // the 32-bit offset. FLATBUFFERS_ASSERT(GetSize() == length_of_64_bit_region_); } /// @brief Store a string in the buffer, which can contain any binary data. /// @param[in] str A const char pointer to the data to be stored as a string. /// @param[in] len The number of bytes that should be stored from `str`. /// @return Returns the offset in the buffer where the string starts. void CreateStringImpl(const char* str, size_t len) { NotNested(); PreAlign<uoffset_t>(len + 1); // Always 0-terminated. buf_.fill(1); PushBytes(reinterpret_cast<const uint8_t*>(str), len); PushElement(static_cast<uoffset_t>(len)); } // Allocates space for a vector of structures. // Must be completed with EndVectorOfStructs(). template <typename T, template <typename> class OffsetT = Offset> T* StartVectorOfStructs(size_t vector_size) { StartVector<OffsetT>(vector_size, sizeof(T), AlignOf<T>()); return reinterpret_cast<T*>(buf_.make_space(vector_size * sizeof(T))); } // End the vector of structures in the flatbuffers. // Vector should have previously be started with StartVectorOfStructs(). template <typename T, template <typename> class OffsetT = Offset> OffsetT<Vector<const T*>> EndVectorOfStructs(size_t vector_size) { return OffsetT<Vector<const T*>>( EndVector<typename Vector<const T*>::size_type, typename OffsetT<Vector<const T*>>::offset_type>( vector_size)); } template <typename T> typename std::enable_if<std::is_same<T, uoffset_t>::value, T>::type CalculateOffset() { // Default to the end of the 32-bit region. This may or may not be the end // of the buffer, depending on if any 64-bit offsets have been added. return GetSizeRelative32BitRegion(); } // Specializations to handle the 64-bit CalculateOffset, which is relative to // end of the buffer. template <typename T> typename std::enable_if<std::is_same<T, uoffset64_t>::value, T>::type CalculateOffset() { // This should never be compiled in when not using a 64-bit builder. static_assert(Is64Aware, "invalid 64-bit offset in 32-bit builder"); // Store how big the 64-bit region of the buffer is, so we can determine // where the 32/64 bit boundary is. length_of_64_bit_region_ = GetSize(); return length_of_64_bit_region_; } }; /// @} // Hack to `FlatBufferBuilder` mean `FlatBufferBuilder<false>` or // `FlatBufferBuilder<>`, where the template < > syntax is required. using FlatBufferBuilder = FlatBufferBuilderImpl<false>; using FlatBufferBuilder64 = FlatBufferBuilderImpl<true>; // These are external due to GCC not allowing them in the class. // See: https://stackoverflow.com/q/8061456/868247 template <> template <> inline Offset64<String> FlatBufferBuilder64::CreateString(const char* str, size_t len) { CanAddOffset64(); CreateStringImpl(str, len); return Offset64<String>( CalculateOffset<typename Offset64<String>::offset_type>()); } // Used to distinguish from real Offsets. template <typename T = void> struct EmptyOffset {}; // TODO(derekbailey): it would be nice to combine these two methods. template <> template <> inline void FlatBufferBuilder64::StartVector<Offset64, uint32_t>( size_t len, size_t elemsize, size_t alignment) { CanAddOffset64(); StartVector<EmptyOffset, uint32_t>(len, elemsize, alignment); } template <> template <> inline void FlatBufferBuilder64::StartVector<Offset64, uint64_t>( size_t len, size_t elemsize, size_t alignment) { CanAddOffset64(); StartVector<EmptyOffset, uint64_t>(len, elemsize, alignment); } /// Helpers to get a typed pointer to objects that are currently being built. /// @warning Creating new objects will lead to reallocations and invalidates /// the pointer! template <typename T> T* GetMutableTemporaryPointer(FlatBufferBuilder& fbb, Offset<T> offset) { return reinterpret_cast<T*>(fbb.GetCurrentBufferPointer() + fbb.GetSize() - offset.o); } template <typename T> const T* GetTemporaryPointer(const FlatBufferBuilder& fbb, Offset<T> offset) { return reinterpret_cast<const T*>(fbb.GetCurrentBufferPointer() + fbb.GetSize() - offset.o); } } // namespace flatbuffers #endif // FLATBUFFERS_FLATBUFFER_BUILDER_H_