Files
flatbuffers-bigfoot/include/flatbuffers/vector_downward.h
Derek Bailey 63b7b25289 FlatBuffers 64 for C++ (#7935)
* First working hack of adding 64-bit. Don't judge :)

* Made vector_downward work on 64 bit types

* vector_downward uses size_t, added offset64 to reflection

* cleaned up adding offset64 in parser

* Add C++ testing skeleton for 64-bit

* working test for CreateVector64

* working >2 GiB buffers

* support for large strings

* simplified CreateString<> to just provide the offset type

* generalize CreateVector template

* update test_64.afb due to upstream format change

* Added Vector64 type, which is just an alias for vector ATM

* Switch to Offset64 for Vector64

* Update for reflection bfbs output change

* Starting to add support for vector64 type in C++

* made a generic CreateVector that can handle different offsets and vector types

* Support for 32-vector with 64-addressing

* Vector64 basic builder + tests working

* basic support for json vector64 support

* renamed fields in test_64bit.fbs to better reflect their use

* working C++ vector64 builder

* Apply --annotate-sparse-vector to 64-bit tests

* Enable Vector64 for --annotate-sparse-vectors

* Merged from upstream

* Add `near_string` field for testing 32-bit offsets alongside

* keep track of where the 32-bit and 64-bit regions are for flatbufferbuilder

* move template<> outside class body for GCC

* update run.sh to build and run tests

* basic assertion for adding 64-bit offset at the wrong time

* started to separate `FlatBufferBuilder` into two classes, 1 64-bit aware, the other not

* add test for nested flatbuffer vector64, fix bug in alignment of big vectors

* fixed CreateDirect method by iterating by Offset64 first

* internal refactoring of flatbufferbuilder

* block not supported languages in the parser from using 64-bit

* evolution tests for adding a vector64 field

* conformity tests for adding/removing offset64 attributes

* ensure test is for a big buffer

* add parser error tests for `offset64` and `vector64` attributes

* add missing static that GCC only complains about

* remove stdint-uintn.h header that gets automatically added

* move 64-bit CalculateOffset internal

* fixed return size of EndVector

* various fixes on windows

* add SizeT to vector_downward

* minimze range of size changes in vector and builder

* reworked how tracking if 64-offsets are added

* Add ReturnT to EndVector

* small cleanups

* remove need for second Array definition

* combine IndirectHelpers into one definition

* started support for vector of struct

* Support for 32/64-vectors of structs + Offset64

* small cleanups

* add verification for vector64

* add sized prefix for 64-bit buffers

* add fuzzer for 64-bit

* add example of adding many vectors using a wrapper table

* run the new -bfbs-gen-embed logic on the 64-bit tests

* remove run.sh and fix cmakelist issue

* fixed bazel rules

* fixed some PR comments

* add 64-bit tests to cmakelist
2023-05-09 09:16:30 -07:00

289 lines
8.5 KiB
C++

/*
* 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_VECTOR_DOWNWARD_H_
#define FLATBUFFERS_VECTOR_DOWNWARD_H_
#include <cstdint>
#include <algorithm>
#include "flatbuffers/base.h"
#include "flatbuffers/default_allocator.h"
#include "flatbuffers/detached_buffer.h"
namespace flatbuffers {
// This is a minimal replication of std::vector<uint8_t> functionality,
// except growing from higher to lower addresses. i.e. push_back() inserts data
// in the lowest address in the vector.
// Since this vector leaves the lower part unused, we support a "scratch-pad"
// that can be stored there for temporary data, to share the allocated space.
// Essentially, this supports 2 std::vectors in a single buffer.
template<typename SizeT = uoffset_t> class vector_downward {
public:
explicit vector_downward(size_t initial_size, Allocator *allocator,
bool own_allocator, size_t buffer_minalign,
const SizeT max_size = FLATBUFFERS_MAX_BUFFER_SIZE)
: allocator_(allocator),
own_allocator_(own_allocator),
initial_size_(initial_size),
max_size_(max_size),
buffer_minalign_(buffer_minalign),
reserved_(0),
size_(0),
buf_(nullptr),
cur_(nullptr),
scratch_(nullptr) {}
vector_downward(vector_downward &&other) noexcept
// clang-format on
: allocator_(other.allocator_),
own_allocator_(other.own_allocator_),
initial_size_(other.initial_size_),
max_size_(other.max_size_),
buffer_minalign_(other.buffer_minalign_),
reserved_(other.reserved_),
size_(other.size_),
buf_(other.buf_),
cur_(other.cur_),
scratch_(other.scratch_) {
// No change in other.allocator_
// No change in other.initial_size_
// No change in other.buffer_minalign_
other.own_allocator_ = false;
other.reserved_ = 0;
other.buf_ = nullptr;
other.cur_ = nullptr;
other.scratch_ = nullptr;
}
vector_downward &operator=(vector_downward &&other) noexcept {
// Move construct a temporary and swap idiom
vector_downward temp(std::move(other));
swap(temp);
return *this;
}
~vector_downward() {
clear_buffer();
clear_allocator();
}
void reset() {
clear_buffer();
clear();
}
void clear() {
if (buf_) {
cur_ = buf_ + reserved_;
} else {
reserved_ = 0;
cur_ = nullptr;
}
size_ = 0;
clear_scratch();
}
void clear_scratch() { scratch_ = buf_; }
void clear_allocator() {
if (own_allocator_ && allocator_) { delete allocator_; }
allocator_ = nullptr;
own_allocator_ = false;
}
void clear_buffer() {
if (buf_) Deallocate(allocator_, buf_, reserved_);
buf_ = nullptr;
}
// Relinquish the pointer to the caller.
uint8_t *release_raw(size_t &allocated_bytes, size_t &offset) {
auto *buf = buf_;
allocated_bytes = reserved_;
offset = vector_downward::offset();
// release_raw only relinquishes the buffer ownership.
// Does not deallocate or reset the allocator. Destructor will do that.
buf_ = nullptr;
clear();
return buf;
}
// Relinquish the pointer to the caller.
DetachedBuffer release() {
// allocator ownership (if any) is transferred to DetachedBuffer.
DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_,
size());
if (own_allocator_) {
allocator_ = nullptr;
own_allocator_ = false;
}
buf_ = nullptr;
clear();
return fb;
}
size_t ensure_space(size_t len) {
FLATBUFFERS_ASSERT(cur_ >= scratch_ && scratch_ >= buf_);
// If the length is larger than the unused part of the buffer, we need to
// grow.
if (len > unused_buffer_size()) { reallocate(len); }
FLATBUFFERS_ASSERT(size() < max_size_);
return len;
}
inline uint8_t *make_space(size_t len) {
if (len) {
ensure_space(len);
cur_ -= len;
size_ += static_cast<SizeT>(len);
}
return cur_;
}
// Returns nullptr if using the DefaultAllocator.
Allocator *get_custom_allocator() { return allocator_; }
// The current offset into the buffer.
size_t offset() const { return cur_ - buf_; }
// The total size of the vector (both the buffer and scratch parts).
inline SizeT size() const { return size_; }
// The size of the buffer part of the vector that is currently unused.
SizeT unused_buffer_size() const { return static_cast<SizeT>(cur_ - scratch_); }
// The size of the scratch part of the vector.
SizeT scratch_size() const { return static_cast<SizeT>(scratch_ - buf_); }
size_t capacity() const { return reserved_; }
uint8_t *data() const {
FLATBUFFERS_ASSERT(cur_);
return cur_;
}
uint8_t *scratch_data() const {
FLATBUFFERS_ASSERT(buf_);
return buf_;
}
uint8_t *scratch_end() const {
FLATBUFFERS_ASSERT(scratch_);
return scratch_;
}
uint8_t *data_at(size_t offset) const { return buf_ + reserved_ - offset; }
void push(const uint8_t *bytes, size_t num) {
if (num > 0) { memcpy(make_space(num), bytes, num); }
}
// Specialized version of push() that avoids memcpy call for small data.
template<typename T> void push_small(const T &little_endian_t) {
make_space(sizeof(T));
*reinterpret_cast<T *>(cur_) = little_endian_t;
}
template<typename T> void scratch_push_small(const T &t) {
ensure_space(sizeof(T));
*reinterpret_cast<T *>(scratch_) = t;
scratch_ += sizeof(T);
}
// fill() is most frequently called with small byte counts (<= 4),
// which is why we're using loops rather than calling memset.
void fill(size_t zero_pad_bytes) {
make_space(zero_pad_bytes);
for (size_t i = 0; i < zero_pad_bytes; i++) cur_[i] = 0;
}
// Version for when we know the size is larger.
// Precondition: zero_pad_bytes > 0
void fill_big(size_t zero_pad_bytes) {
memset(make_space(zero_pad_bytes), 0, zero_pad_bytes);
}
void pop(size_t bytes_to_remove) {
cur_ += bytes_to_remove;
size_ -= static_cast<SizeT>(bytes_to_remove);
}
void scratch_pop(size_t bytes_to_remove) { scratch_ -= bytes_to_remove; }
void swap(vector_downward &other) {
using std::swap;
swap(allocator_, other.allocator_);
swap(own_allocator_, other.own_allocator_);
swap(initial_size_, other.initial_size_);
swap(buffer_minalign_, other.buffer_minalign_);
swap(reserved_, other.reserved_);
swap(size_, other.size_);
swap(max_size_, other.max_size_);
swap(buf_, other.buf_);
swap(cur_, other.cur_);
swap(scratch_, other.scratch_);
}
void swap_allocator(vector_downward &other) {
using std::swap;
swap(allocator_, other.allocator_);
swap(own_allocator_, other.own_allocator_);
}
private:
// You shouldn't really be copying instances of this class.
FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &));
FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &));
Allocator *allocator_;
bool own_allocator_;
size_t initial_size_;
// The maximum size the vector can be.
SizeT max_size_;
size_t buffer_minalign_;
size_t reserved_;
SizeT size_;
uint8_t *buf_;
uint8_t *cur_; // Points at location between empty (below) and used (above).
uint8_t *scratch_; // Points to the end of the scratchpad in use.
void reallocate(size_t len) {
auto old_reserved = reserved_;
auto old_size = size();
auto old_scratch_size = scratch_size();
reserved_ +=
(std::max)(len, old_reserved ? old_reserved / 2 : initial_size_);
reserved_ = (reserved_ + buffer_minalign_ - 1) & ~(buffer_minalign_ - 1);
if (buf_) {
buf_ = ReallocateDownward(allocator_, buf_, old_reserved, reserved_,
old_size, old_scratch_size);
} else {
buf_ = Allocate(allocator_, reserved_);
}
cur_ = buf_ + reserved_ - old_size;
scratch_ = buf_ + old_scratch_size;
}
};
} // namespace flatbuffers
#endif // FLATBUFFERS_VECTOR_DOWNWARD_H_