diff options
| author | Pimyn Girgis <pimyn@google.com> | 2025-12-02 12:28:10 +0000 |
|---|---|---|
| committer | Tahuti <pimyn@google.com> | 2025-12-03 09:02:54 +0000 |
| commit | 1cfbf16e320ca9bdadd9c24eb1d2d68b25369ba6 (patch) | |
| tree | be0ce827f327c8a76125307e64047e01fa449d4c /executor/_include/flatbuffers/flatbuffer_builder.h | |
| parent | 42a04216dd856c12e723e48930e85d63998e4eda (diff) | |
executor: update flatbuffers
Update flatbuffers to v23.5.26, which matches the compiler version in the new env container.
Diffstat (limited to 'executor/_include/flatbuffers/flatbuffer_builder.h')
| -rw-r--r-- | executor/_include/flatbuffers/flatbuffer_builder.h | 571 |
1 files changed, 411 insertions, 160 deletions
diff --git a/executor/_include/flatbuffers/flatbuffer_builder.h b/executor/_include/flatbuffers/flatbuffer_builder.h index f9432338f..0a38b4ac3 100644 --- a/executor/_include/flatbuffers/flatbuffer_builder.h +++ b/executor/_include/flatbuffers/flatbuffer_builder.h @@ -17,12 +17,16 @@ #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" @@ -39,8 +43,9 @@ 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 int fixed_fields = 2; // Vtable size and Object Size. - return static_cast<voffset_t>((field_id + fixed_fields) * sizeof(voffset_t)); + const voffset_t fixed_fields = + 2 * sizeof(voffset_t); // Vtable size and Object Size. + return fixed_fields + field_id * sizeof(voffset_t); } template<typename T, typename Alloc = std::allocator<T>> @@ -67,8 +72,13 @@ T *data(std::vector<T, Alloc> &v) { /// `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. -class FlatBufferBuilder { +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`. @@ -80,13 +90,16 @@ class FlatBufferBuilder { /// 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 FlatBufferBuilder( + 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), + : 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), @@ -97,10 +110,13 @@ class FlatBufferBuilder { } /// @brief Move constructor for FlatBufferBuilder. - FlatBufferBuilder(FlatBufferBuilder &&other) - : buf_(1024, nullptr, false, AlignOf<largest_scalar_t>()), + 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), @@ -115,18 +131,19 @@ class FlatBufferBuilder { } /// @brief Move assignment operator for FlatBufferBuilder. - FlatBufferBuilder &operator=(FlatBufferBuilder &&other) { + FlatBufferBuilderImpl &operator=(FlatBufferBuilderImpl &&other) noexcept { // Move construct a temporary and swap idiom - FlatBufferBuilder temp(std::move(other)); + FlatBufferBuilderImpl temp(std::move(other)); Swap(temp); return *this; } - void Swap(FlatBufferBuilder &other) { + 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_); @@ -135,7 +152,7 @@ class FlatBufferBuilder { swap(string_pool, other.string_pool); } - ~FlatBufferBuilder() { + ~FlatBufferBuilderImpl() { if (string_pool) delete string_pool; } @@ -152,12 +169,36 @@ class FlatBufferBuilder { 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. - uoffset_t GetSize() const { return buf_.size(); } + 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 @@ -269,14 +310,16 @@ class FlatBufferBuilder { } // Write a single aligned scalar to the buffer - template<typename T> uoffset_t PushElement(T element) { + template<typename T, typename ReturnT = uoffset_t> + ReturnT PushElement(T element) { AssertScalarT<T>(); Align(sizeof(T)); buf_.push_small(EndianScalar(element)); - return GetSize(); + return CalculateOffset<ReturnT>(); } - template<typename T> uoffset_t PushElement(Offset<T> off) { + 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)); } @@ -306,11 +349,16 @@ class FlatBufferBuilder { 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, GetSize()); + TrackField(field, CalculateOffset<uoffset_t>()); } void AddStructOffset(voffset_t field, uoffset_t off) { @@ -321,12 +369,29 @@ class FlatBufferBuilder { // 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 GetSize() below is correct. + // Align to ensure GetSizeRelative32BitRegion() below is correct. Align(sizeof(uoffset_t)); - // Offset must refer to something already in buffer. - const uoffset_t size = GetSize(); + // 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<uoffset_t>(sizeof(uoffset_t)); + return size - off + static_cast<T>(sizeof(T)); } void NotNested() { @@ -348,7 +413,7 @@ class FlatBufferBuilder { uoffset_t StartTable() { NotNested(); nested = true; - return GetSize(); + return GetSizeRelative32BitRegion(); } // This finishes one serialized object by generating the vtable if it's a @@ -359,7 +424,9 @@ class FlatBufferBuilder { FLATBUFFERS_ASSERT(nested); // Write the vtable offset, which is the start of any Table. // We fill its value later. - auto vtableoffsetloc = PushElement<soffset_t>(0); + // 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: @@ -369,7 +436,7 @@ class FlatBufferBuilder { (std::max)(static_cast<voffset_t>(max_voffset_ + sizeof(voffset_t)), FieldIndexToOffset(0)); buf_.fill_big(max_voffset_); - auto table_object_size = vtableoffsetloc - start; + 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), @@ -379,7 +446,8 @@ class FlatBufferBuilder { 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); - auto pos = static_cast<voffset_t>(vtableoffsetloc - field_location->off); + 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)); @@ -388,7 +456,7 @@ class FlatBufferBuilder { ClearOffsets(); auto vt1 = reinterpret_cast<voffset_t *>(buf_.data()); auto vt1_size = ReadScalar<voffset_t>(vt1); - auto vt_use = GetSize(); + 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_) { @@ -399,23 +467,24 @@ class FlatBufferBuilder { 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(GetSize() - vtableoffsetloc); + buf_.pop(GetSizeRelative32BitRegion() - vtable_offset_loc); break; } } // If this is a new vtable, remember it. - if (vt_use == GetSize()) { buf_.scratch_push_small(vt_use); } + 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. + // 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(vtableoffsetloc), + WriteScalar(buf_.data_at(vtable_offset_loc + length_of_64_bit_region_), static_cast<soffset_t>(vt_use) - - static_cast<soffset_t>(vtableoffsetloc)); - + static_cast<soffset_t>(vtable_offset_loc)); nested = false; - return vtableoffsetloc; + return vtable_offset_loc; } FLATBUFFERS_ATTRIBUTE([[deprecated("call the version above instead")]]) @@ -425,14 +494,20 @@ class FlatBufferBuilder { // 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); + 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 GetSize(); + return GetSizeRelative32BitRegion(); } - uoffset_t EndStruct() { return GetSize(); } + uoffset_t EndStruct() { return GetSizeRelative32BitRegion(); } void ClearOffsets() { buf_.scratch_pop(num_field_loc * sizeof(FieldLoc)); @@ -441,15 +516,18 @@ class FlatBufferBuilder { } // Aligns such that when "len" bytes are written, an object can be written - // after it with "alignment" without padding. + // 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)); } - template<typename T> void PreAlign(size_t len) { - AssertScalarT<T>(); - PreAlign(len, sizeof(T)); + + // 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 @@ -457,34 +535,35 @@ class FlatBufferBuilder { /// @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> CreateString(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)); - return Offset<String>(GetSize()); + 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. - Offset<String> CreateString(const char *str) { - return CreateString(str, strlen(str)); + 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. - Offset<String> CreateString(char *str) { - return CreateString(str, strlen(str)); + 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. - Offset<String> CreateString(const std::string &str) { - return CreateString(str.c_str(), str.length()); + template<template<typename> class OffsetT = Offset> + OffsetT<String> CreateString(const std::string &str) { + return CreateString<OffsetT>(str.c_str(), str.length()); } // clang-format off @@ -492,8 +571,9 @@ class FlatBufferBuilder { /// @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. - Offset<String> CreateString(flatbuffers::string_view str) { - return CreateString(str.data(), str.size()); + 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 @@ -501,16 +581,21 @@ class FlatBufferBuilder { /// @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 - Offset<String> CreateString(const String *str) { - return str ? CreateString(str->c_str(), str->size()) : 0; + 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::c_str() and T::length() to store in the buffer. /// @return Returns the offset in the buffer where the string starts. - template<typename T> Offset<String> CreateString(const T &str) { - return CreateString(str.c_str(), str.length()); + 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.c_str(), str.length()); } /// @brief Store a string in the buffer, which can contain any binary data. @@ -522,12 +607,14 @@ class FlatBufferBuilder { /// @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) + if (!string_pool) { string_pool = new StringOffsetMap(StringOffsetCompare(buf_)); - auto size_before_string = buf_.size(); + } + + const size_t size_before_string = buf_.size(); // Must first serialize the string, since the set is all offsets into // buffer. - auto off = CreateString(str, len); + 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()) { @@ -583,17 +670,27 @@ class FlatBufferBuilder { } /// @cond FLATBUFFERS_INTERNAL - uoffset_t EndVector(size_t len) { + 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(static_cast<uoffset_t>(len)); + return PushElement<LenT, ReturnT>(static_cast<LenT>(len)); } - void StartVector(size_t len, size_t elemsize) { + template<template<typename> class OffsetT = Offset, typename LenT = uint32_t> + void StartVector(size_t len, size_t elemsize, size_t alignment) { NotNested(); nested = true; - PreAlign<uoffset_t>(len * elemsize); - PreAlign(len * elemsize, elemsize); // Just in case elemsize > uoffset_t. + // 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 @@ -618,31 +715,39 @@ class FlatBufferBuilder { /// @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 `Offset` into the serialized data indicating + /// @return Returns a typed `TOffset` into the serialized data indicating /// where the vector is stored. - template<typename T> Offset<Vector<T>> CreateVector(const T *v, size_t len) { + 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(len, sizeof(T)); - if (len == 0) { return Offset<Vector<T>>(EndVector(len)); } - // 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]); + 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 Offset<Vector<T>>(EndVector(len)); + #endif + // clang-format on + } + return OffsetT<VectorT<T>>(EndVector<LenT, offset_type>(len)); } /// @brief Serialize an array like object into a FlatBuffer `vector`. @@ -668,7 +773,7 @@ class FlatBufferBuilder { template<typename T> Offset<Vector<Offset<T>>> CreateVector(const Offset<T> *v, size_t len) { - StartVector(len, sizeof(Offset<T>)); + StartVector<Offset<T>>(len); for (auto i = len; i > 0;) { PushElement(v[--i]); } return Offset<Vector<Offset<T>>>(EndVector(len)); } @@ -684,11 +789,17 @@ class FlatBufferBuilder { 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(v.size(), sizeof(uint8_t)); + StartVector<uint8_t>(v.size()); for (auto i = v.size(); i > 0;) { PushElement(static_cast<uint8_t>(v[--i])); } @@ -762,7 +873,7 @@ class FlatBufferBuilder { for (auto it = begin; it != end; ++it) { buf_.scratch_push_small(CreateString(*it)); } - StartVector(size, sizeof(Offset<String>)); + StartVector<Offset<String>>(size); for (auto i = 1; i <= size; i++) { // Note we re-evaluate the buf location each iteration to account for any // underlying buffer resizing that may occur. @@ -780,47 +891,19 @@ class FlatBufferBuilder { /// @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 *>> CreateVectorOfStructs(const T *v, size_t len) { - StartVector(len * sizeof(T) / AlignOf<T>(), AlignOf<T>()); + 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>(), sizeof(T), + AlignOf<T>()); if (len > 0) { PushBytes(reinterpret_cast<const uint8_t *>(v), sizeof(T) * len); } - return Offset<Vector<const T *>>(EndVector(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. - /// @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); + return OffsetT<VectorT<const T *>>(EndVector<LenT, offset_type>(len)); } /// @brief Serialize an array of structs into a FlatBuffer `vector`. @@ -868,10 +951,52 @@ class FlatBufferBuilder { /// 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 *>> CreateVectorOfStructs( + 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(data(v), v.size()); + 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 @@ -974,14 +1099,14 @@ class FlatBufferBuilder { /// @cond FLATBUFFERS_INTERNAL template<typename T> struct TableKeyComparator { - TableKeyComparator(vector_downward &buf) : buf_(buf) {} + 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 &buf_; + vector_downward<SizeT> &buf_; private: FLATBUFFERS_DELETE_FUNC( @@ -1025,16 +1150,22 @@ class FlatBufferBuilder { /// 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, - uint8_t **buf) { + size_t alignment, uint8_t **buf) { NotNested(); - StartVector(len, elemsize); + StartVector(len, elemsize, alignment); buf_.make_space(len * elemsize); - auto vec_start = GetSize(); + 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 @@ -1046,14 +1177,14 @@ class FlatBufferBuilder { template<typename T> Offset<Vector<T>> CreateUninitializedVector(size_t len, T **buf) { AssertScalarT<T>(); - return CreateUninitializedVector(len, sizeof(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), + return CreateUninitializedVector(len, sizeof(T), AlignOf<T>(), reinterpret_cast<uint8_t **>(buf)); } @@ -1064,7 +1195,7 @@ class FlatBufferBuilder { Offset<Vector<T>> CreateVectorScalarCast(const U *v, size_t len) { AssertScalarT<T>(); AssertScalarT<U>(); - StartVector(len, sizeof(T)); + StartVector<T>(len); for (auto i = len; i > 0;) { PushElement(static_cast<T>(v[--i])); } return Offset<Vector<T>>(EndVector(len)); } @@ -1074,7 +1205,8 @@ class FlatBufferBuilder { NotNested(); Align(AlignOf<T>()); buf_.push_small(structobj); - return Offset<const T *>(GetSize()); + return Offset<const T *>( + CalculateOffset<typename Offset<const T *>::offset_type>()); } /// @brief Finish serializing a buffer by writing the root offset. @@ -1098,7 +1230,7 @@ class FlatBufferBuilder { Finish(root.o, file_identifier, true); } - void SwapBufAllocator(FlatBufferBuilder &other) { + void SwapBufAllocator(FlatBufferBuilderImpl &other) { buf_.swap_allocator(other.buf_); } @@ -1108,16 +1240,23 @@ class FlatBufferBuilder { protected: // You shouldn't really be copying instances of this class. - FlatBufferBuilder(const FlatBufferBuilder &); - FlatBufferBuilder &operator=(const FlatBufferBuilder &); + FlatBufferBuilderImpl(const FlatBufferBuilderImpl &); + FlatBufferBuilderImpl &operator=(const FlatBufferBuilderImpl &); void Finish(uoffset_t root, const char *file_identifier, bool size_prefix) { 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((size_prefix ? sizeof(uoffset_t) : 0) + sizeof(uoffset_t) + - (file_identifier ? kFileIdentifierLength : 0), - minalign_); + 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), @@ -1133,7 +1272,7 @@ class FlatBufferBuilder { voffset_t id; }; - vector_downward buf_; + 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. @@ -1142,6 +1281,26 @@ class FlatBufferBuilder { // 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; @@ -1155,14 +1314,15 @@ class FlatBufferBuilder { bool dedup_vtables_; struct StringOffsetCompare { - StringOffsetCompare(const vector_downward &buf) : buf_(&buf) {} + 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 *buf_; + const vector_downward<SizeT> *buf_; }; // For use with CreateSharedString. Instantiated on first use only. @@ -1170,22 +1330,122 @@ class FlatBufferBuilder { 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> T *StartVectorOfStructs(size_t vector_size) { - StartVector(vector_size * sizeof(T) / AlignOf<T>(), AlignOf<T>()); + template<typename T, template<typename> class OffsetT = Offset> + T *StartVectorOfStructs(size_t vector_size) { + StartVector<OffsetT>(vector_size * sizeof(T) / AlignOf<T>(), 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> - Offset<Vector<const T *>> EndVectorOfStructs(size_t vector_size) { - return Offset<Vector<const T *>>(EndVector(vector_size)); + 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! @@ -1200,15 +1460,6 @@ const T *GetTemporaryPointer(FlatBufferBuilder &fbb, Offset<T> offset) { return GetMutableTemporaryPointer<T>(fbb, offset); } -template<typename T> -void FlatBufferBuilder::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; -} - } // namespace flatbuffers -#endif // FLATBUFFERS_VECTOR_DOWNWARD_H_ +#endif // FLATBUFFERS_FLATBUFFER_BUILDER_H_ |
