diff options
author | Tor Egge <Tor.Egge@online.no> | 2023-06-22 18:32:17 +0200 |
---|---|---|
committer | Tor Egge <Tor.Egge@online.no> | 2023-06-22 18:32:17 +0200 |
commit | 3bef09482a1fcc1a55ff09c778921ea921028e24 (patch) | |
tree | 6e4dd47797c192350b0178fb88fd8255bbe3bcb1 | |
parent | fec5b4b5837826129e9e06bd68c1488dd93766df (diff) |
Allocate space for allowed buffer underflow.
10 files changed, 88 insertions, 57 deletions
diff --git a/vespalib/src/tests/datastore/array_store/array_store_test.cpp b/vespalib/src/tests/datastore/array_store/array_store_test.cpp index a259fcaa4dc..2674acf1ce9 100644 --- a/vespalib/src/tests/datastore/array_store/array_store_test.cpp +++ b/vespalib/src/tests/datastore/array_store/array_store_test.cpp @@ -280,7 +280,7 @@ TYPED_TEST(NumberStoreTest, control_static_sizes) { EXPECT_EQ(202140u, usage.allocatedBytes()); EXPECT_EQ(197680u, usage.usedBytes()); } else { - EXPECT_EQ(202388u, usage.allocatedBytes()); + EXPECT_EQ(202328u, usage.allocatedBytes()); EXPECT_EQ(197568u, usage.usedBytes()); } } @@ -564,10 +564,10 @@ TYPED_TEST(NumberStoreTest, address_space_usage_is_ratio_between_used_arrays_and * allocated elements = 256 / sizeof(int) = 64. * limit = 64 / 3 = 21. * - * For dynamic buffer 3, we have 16 * 5 * sizeof(int) => 320 -> 512 - * limit = 512 / (5 * 4) = 25 + * For dynamic buffer 3, we have 16 * 5 * sizeof(int) => 320 -> 512 - 64 + * limit = (512 -64) / (5 * 4) = 22 */ - size_t type_id_3_entries = this->simple_buffers() ? 21 : 25; + size_t type_id_3_entries = this->simple_buffers() ? 21 : 22; size_t expLimit = fourgig - 4 * TestFixture::EntryRefType::offsetSize() + 3 * 16 + type_id_3_entries; EXPECT_EQ(static_cast<double>(2)/ expLimit, this->store.addressSpaceUsage().usage()); EXPECT_EQ(expLimit, this->store.addressSpaceUsage().limit()); diff --git a/vespalib/src/tests/datastore/dynamic_array_buffer_type/dynamic_array_buffer_type_test.cpp b/vespalib/src/tests/datastore/dynamic_array_buffer_type/dynamic_array_buffer_type_test.cpp index a703d9b18eb..9c0432a7780 100644 --- a/vespalib/src/tests/datastore/dynamic_array_buffer_type/dynamic_array_buffer_type_test.cpp +++ b/vespalib/src/tests/datastore/dynamic_array_buffer_type/dynamic_array_buffer_type_test.cpp @@ -120,20 +120,23 @@ protected: BufferType _buffer_type; size_t _entry_size; + size_t _buffer_underflow_size; size_t _buf_size; - std::unique_ptr<char[]> _buf; + std::unique_ptr<char[]> _buf_alloc; + char* _buf; }; DynamicArrayBufferTypeTest::DynamicArrayBufferTypeTest() : testing::Test(), _buffer_type(3, ArrayStoreConfig::AllocSpec(0, 10, 0, 0.2), {}), _entry_size(_buffer_type.entry_size()), + _buffer_underflow_size(BufferType::buffer_underflow_size), _buf_size(2 * _entry_size), - _buf(std::make_unique<char[]>(_buf_size)) + _buf_alloc(std::make_unique<char[]>(_buf_size + _buffer_underflow_size)), + _buf(_buf_alloc.get() + _buffer_underflow_size) { // Call initialize_reserved_entries to force construction of empty element - _buffer_type.initialize_reserved_entries(_buf.get(), 1); - memset(_buf.get(), 55, _buf_size); + _buffer_type.initialize_reserved_entries(_buf, 1); // Reset counts after empty element has been constructed counts = Counts(); } @@ -178,7 +181,7 @@ DynamicArrayBufferTypeTest::get_max_vector(const void* buffer, uint32_t offset) void DynamicArrayBufferTypeTest::write_entry1() { - auto e1 = BufferType::get_entry(_buf.get(), 1, _entry_size); + auto e1 = BufferType::get_entry(_buf, 1, _entry_size); BufferType::set_dynamic_array_size(e1, 2); new (static_cast<void *>(e1)) WrapInt32(42); new (static_cast<void *>(e1 + 1)) WrapInt32(47); @@ -204,46 +207,47 @@ TEST_F(DynamicArrayBufferTypeTest, entry_size_is_calculated) TEST_F(DynamicArrayBufferTypeTest, initialize_reserved_entries) { - _buffer_type.initialize_reserved_entries(_buf.get(), 2); - EXPECT_EQ((std::vector<int>{}), get_vector(_buf.get(), 0)); - EXPECT_EQ((std::vector<int>{}), get_vector(_buf.get(), 1)); - EXPECT_EQ((std::vector<int>{0, 0, 0}), get_max_vector(_buf.get(), 0)); - EXPECT_EQ((std::vector<int>{0, 0, 0}), get_max_vector(_buf.get(), 1)); + _buffer_type.initialize_reserved_entries(_buf, 2); + EXPECT_EQ((std::vector<int>{}), get_vector(_buf, 0)); + EXPECT_EQ((std::vector<int>{}), get_vector(_buf, 1)); + EXPECT_EQ((std::vector<int>{0, 0, 0}), get_max_vector(_buf, 0)); + EXPECT_EQ((std::vector<int>{0, 0, 0}), get_max_vector(_buf, 1)); EXPECT_EQ(Counts(0, 0, 6, 0, 0), counts); } TEST_F(DynamicArrayBufferTypeTest, fallback_copy) { - _buffer_type.initialize_reserved_entries(_buf.get(), 1); + _buffer_type.initialize_reserved_entries(_buf, 1); write_entry1(); EXPECT_EQ(Counts(0, 3, 3, 0, 0), counts); - auto buf2 = std::make_unique<char[]>(_buf_size); - _buffer_type.fallback_copy(buf2.get(), _buf.get(), 2); - EXPECT_EQ((std::vector<int>{}), get_vector(buf2.get(), 0)); - EXPECT_EQ((std::vector<int>{42, 47}), get_vector(buf2.get(), 1)); - EXPECT_EQ((std::vector<int>{0, 0, 0}), get_max_vector(buf2.get(), 0)); - EXPECT_EQ((std::vector<int>{42, 47, 49}), get_max_vector(buf2.get(), 1)); + auto buf2_alloc = std::make_unique<char[]>(_buf_size + _buffer_underflow_size); + char* buf2 = buf2_alloc.get() + _buffer_underflow_size; + _buffer_type.fallback_copy(buf2, _buf, 2); + EXPECT_EQ((std::vector<int>{}), get_vector(buf2, 0)); + EXPECT_EQ((std::vector<int>{42, 47}), get_vector(buf2, 1)); + EXPECT_EQ((std::vector<int>{0, 0, 0}), get_max_vector(buf2, 0)); + EXPECT_EQ((std::vector<int>{42, 47, 49}), get_max_vector(buf2, 1)); EXPECT_EQ(Counts(0, 3, 9, 0, 0), counts); } TEST_F(DynamicArrayBufferTypeTest, destroy_entries) { - _buffer_type.initialize_reserved_entries(_buf.get(), 2); + _buffer_type.initialize_reserved_entries(_buf, 2); write_entry1(); - _buffer_type.destroy_entries(_buf.get(), 2); + _buffer_type.destroy_entries(_buf, 2); EXPECT_EQ(Counts(0, 3, 6, 6, 0), counts); } TEST_F(DynamicArrayBufferTypeTest, clean_hold) { - _buffer_type.initialize_reserved_entries(_buf.get(), 1); + _buffer_type.initialize_reserved_entries(_buf, 1); write_entry1(); MyCleanContext clean_context; - _buffer_type.clean_hold(_buf.get(), 1, 1, clean_context); - EXPECT_EQ((std::vector<int>{0, 0}), get_vector(_buf.get(), 1)); - EXPECT_EQ((std::vector<int>{0, 0, 49}), get_max_vector(_buf.get(), 1)); + _buffer_type.clean_hold(_buf, 1, 1, clean_context); + EXPECT_EQ((std::vector<int>{0, 0}), get_vector(_buf, 1)); + EXPECT_EQ((std::vector<int>{0, 0, 49}), get_max_vector(_buf, 1)); EXPECT_EQ(Counts(0, 3, 3, 0, 2), counts); - _buffer_type.clean_hold(_buf.get(), 0, 2, clean_context); + _buffer_type.clean_hold(_buf, 0, 2, clean_context); EXPECT_EQ(Counts(0, 3, 3, 0, 4), counts); } diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_type.cpp b/vespalib/src/vespa/vespalib/datastore/buffer_type.cpp index 90f9334da77..1b087a01c58 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_type.cpp +++ b/vespalib/src/vespa/vespalib/datastore/buffer_type.cpp @@ -27,6 +27,7 @@ BufferTypeBase::CleanContext::extraBytesCleaned(size_t value) } BufferTypeBase::BufferTypeBase(uint32_t entry_size_in, + uint32_t buffer_underflow_size_in, uint32_t arraySize, uint32_t min_entries, uint32_t max_entries, @@ -34,6 +35,7 @@ BufferTypeBase::BufferTypeBase(uint32_t entry_size_in, float allocGrowFactor) noexcept : _entry_size(entry_size_in), _arraySize(arraySize), + _buffer_underflow_size(buffer_underflow_size_in), _min_entries(std::min(min_entries, max_entries)), _max_entries(max_entries), _num_entries_for_new_buffer(std::min(num_entries_for_new_buffer, max_entries)), @@ -46,10 +48,11 @@ BufferTypeBase::BufferTypeBase(uint32_t entry_size_in, } BufferTypeBase::BufferTypeBase(uint32_t entry_size_in, + uint32_t buffer_underflow_size_in, uint32_t arraySize, uint32_t min_entries, uint32_t max_entries) noexcept - : BufferTypeBase(entry_size_in, arraySize, min_entries, max_entries, 0u, DEFAULT_ALLOC_GROW_FACTOR) + : BufferTypeBase(entry_size_in, buffer_underflow_size_in, arraySize, min_entries, max_entries, 0u, DEFAULT_ALLOC_GROW_FACTOR) { } diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_type.h b/vespalib/src/vespa/vespalib/datastore/buffer_type.h index c79651c3ba2..7b23a238ba2 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_type.h +++ b/vespalib/src/vespa/vespalib/datastore/buffer_type.h @@ -39,8 +39,8 @@ public: BufferTypeBase & operator=(const BufferTypeBase &rhs) = delete; BufferTypeBase(BufferTypeBase &&rhs) noexcept = default; BufferTypeBase & operator=(BufferTypeBase &&rhs) noexcept = default; - BufferTypeBase(uint32_t entry_size_in, uint32_t arraySize, uint32_t min_entries, uint32_t max_entries) noexcept; - BufferTypeBase(uint32_t entry_size_in, uint32_t arraySize, uint32_t min_entries, uint32_t max_entries, + BufferTypeBase(uint32_t entry_size_in, uint32_t buffer_underflow_size_in, uint32_t arraySize, uint32_t min_entries, uint32_t max_entries) noexcept; + BufferTypeBase(uint32_t entry_size_in, uint32_t buffer_underflow_size_in, uint32_t arraySize, uint32_t min_entries, uint32_t max_entries, uint32_t num_entries_for_new_buffer, float allocGrowFactor) noexcept; virtual ~BufferTypeBase(); virtual void destroy_entries(void *buffer, EntryCount num_entries) = 0; @@ -57,6 +57,7 @@ public: */ virtual void initialize_reserved_entries(void *buffer, EntryCount reserved_entries) = 0; size_t entry_size() const noexcept { return _entry_size; } + uint32_t buffer_underflow_size() const noexcept { return _buffer_underflow_size; } virtual void clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) = 0; size_t getArraySize() const noexcept { return _arraySize; } virtual void on_active(uint32_t bufferId, std::atomic<EntryCount>* used_entries, std::atomic<EntryCount>* dead_entries, void* buffer); @@ -115,6 +116,16 @@ protected: uint32_t _entry_size; // Number of bytes in an allocation unit uint32_t _arraySize; // Number of elements in an allocation unit + + /* + * Buffer underflow size is the size of an area before the start + * of the logical buffer that is safe to access (part of the same + * memory alloation as the buffer itself). This allows for data + * belonging to an entry to be placed at the end of what is normally + * the last part of the previos entry (e.g. dynamic array size + * for the dynamic array buffer type). + */ + uint32_t _buffer_underflow_size; uint32_t _min_entries; // Minimum number of entries to allocate in a buffer uint32_t _max_entries; // Maximum number of entries to allocate in a buffer // Number of entries needed before allocating a new buffer instead of just resizing the first one diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_type.hpp b/vespalib/src/vespa/vespalib/datastore/buffer_type.hpp index 375c832d9fb..00d642be9bc 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_type.hpp +++ b/vespalib/src/vespa/vespalib/datastore/buffer_type.hpp @@ -8,13 +8,13 @@ namespace vespalib::datastore { template <typename ElemT, typename EmptyT> BufferType<ElemT, EmptyT>::BufferType(uint32_t arraySize, uint32_t min_entries, uint32_t max_entries) noexcept - : BufferTypeBase(arraySize * sizeof(ElemT), arraySize, min_entries, max_entries) + : BufferTypeBase(arraySize * sizeof(ElemT), 0u, arraySize, min_entries, max_entries) { } template <typename ElemT, typename EmptyT> BufferType<ElemT, EmptyT>::BufferType(uint32_t arraySize, uint32_t min_entries, uint32_t max_entries, uint32_t num_entries_for_new_buffer, float allocGrowFactor) noexcept - : BufferTypeBase(arraySize * sizeof(ElemT), arraySize, min_entries, max_entries, num_entries_for_new_buffer, allocGrowFactor) + : BufferTypeBase(arraySize * sizeof(ElemT), 0u, arraySize, min_entries, max_entries, num_entries_for_new_buffer, allocGrowFactor) { } template <typename ElemT, typename EmptyT> diff --git a/vespalib/src/vespa/vespalib/datastore/bufferstate.cpp b/vespalib/src/vespa/vespalib/datastore/bufferstate.cpp index f312596d6f7..e7832a1c4e2 100644 --- a/vespalib/src/vespa/vespalib/datastore/bufferstate.cpp +++ b/vespalib/src/vespa/vespalib/datastore/bufferstate.cpp @@ -64,13 +64,14 @@ calc_allocation(uint32_t bufferId, { size_t alloc_entries = typeHandler.calc_entries_to_alloc(bufferId, free_entries_needed, resizing); size_t entry_size = typeHandler.entry_size(); - size_t allocBytes = roundUpToMatchAllocator(alloc_entries * entry_size); - size_t maxAllocBytes = typeHandler.get_max_entries() * entry_size; + auto buffer_underflow_size = typeHandler.buffer_underflow_size(); + size_t allocBytes = roundUpToMatchAllocator(alloc_entries * entry_size + buffer_underflow_size); + size_t maxAllocBytes = typeHandler.get_max_entries() * entry_size + buffer_underflow_size; if (allocBytes > maxAllocBytes) { // Ensure that allocated bytes does not exceed the maximum handled by this type. allocBytes = maxAllocBytes; } - size_t adjusted_alloc_entries = allocBytes / entry_size; + size_t adjusted_alloc_entries = (allocBytes - buffer_underflow_size) / entry_size; return AllocResult(adjusted_alloc_entries, allocBytes); } @@ -102,7 +103,8 @@ BufferState::on_active(uint32_t bufferId, uint32_t typeId, _buffer = (allocator != nullptr) ? Alloc::alloc_with_allocator(allocator) : Alloc::alloc(0, MemoryAllocator::HUGEPAGE_SIZE); _buffer.create(alloc.bytes).swap(_buffer); assert(_buffer.get() != nullptr || alloc.entries == 0u); - buffer.store(_buffer.get(), std::memory_order_release); + auto buffer_underflow_size = typeHandler->buffer_underflow_size(); + buffer.store(get_buffer(buffer_underflow_size), std::memory_order_release); _stats.set_alloc_entries(alloc.entries); _typeHandler.store(typeHandler, std::memory_order_release); assert(typeId <= std::numeric_limits<uint16_t>::max()); @@ -117,28 +119,30 @@ void BufferState::onHold(uint32_t buffer_id) { assert(getState() == State::ACTIVE); - assert(getTypeHandler() != nullptr); + auto type_handler = getTypeHandler(); + assert(type_handler != nullptr); _state.store(State::HOLD, std::memory_order_release); _compacting = false; assert(_stats.dead_entries() <= size()); assert(_stats.hold_entries() <= (size() - _stats.dead_entries())); _stats.set_dead_entries(0); _stats.set_hold_entries(size()); - getTypeHandler()->on_hold(buffer_id, &_stats.used_entries_ref(), &_stats.dead_entries_ref()); + type_handler->on_hold(buffer_id, &_stats.used_entries_ref(), &_stats.dead_entries_ref()); _free_list.disable(); } void BufferState::onFree(std::atomic<void*>& buffer) { - assert(buffer.load(std::memory_order_relaxed) == _buffer.get()); assert(getState() == State::HOLD); - assert(_typeHandler != nullptr); + auto type_handler = getTypeHandler(); + assert(type_handler != nullptr); + assert(buffer.load(std::memory_order_relaxed) == get_buffer(type_handler->buffer_underflow_size())); assert(_stats.dead_entries() <= size()); assert(_stats.hold_entries() == (size() - _stats.dead_entries())); - getTypeHandler()->destroy_entries(buffer, size()); + type_handler->destroy_entries(buffer, size()); Alloc::alloc().swap(_buffer); - getTypeHandler()->on_free(size()); + type_handler->on_free(size()); buffer.store(nullptr, std::memory_order_release); _stats.clear(); _state.store(State::FREE, std::memory_order_release); @@ -200,9 +204,11 @@ BufferState::free_entries(EntryRef ref, size_t num_entries, size_t ref_offset) } _stats.inc_dead_entries(num_entries); _stats.dec_hold_entries(num_entries); - getTypeHandler()->clean_hold(_buffer.get(), ref_offset, num_entries, - BufferTypeBase::CleanContext(_stats.extra_used_bytes_ref(), - _stats.extra_hold_bytes_ref())); + auto type_handler = getTypeHandler(); + auto buffer_underflow_size = type_handler->buffer_underflow_size(); + type_handler->clean_hold(get_buffer(buffer_underflow_size), ref_offset, num_entries, + BufferTypeBase::CleanContext(_stats.extra_used_bytes_ref(), + _stats.extra_hold_bytes_ref())); } void @@ -212,17 +218,19 @@ BufferState::fallback_resize(uint32_t bufferId, Alloc &holdBuffer) { assert(getState() == State::ACTIVE); - assert(_typeHandler != nullptr); + auto type_handler = getTypeHandler(); + assert(type_handler != nullptr); assert(holdBuffer.get() == nullptr); - AllocResult alloc = calc_allocation(bufferId, *_typeHandler, free_entries_needed, true); + auto buffer_underflow_size = type_handler->buffer_underflow_size(); + AllocResult alloc = calc_allocation(bufferId, *type_handler, free_entries_needed, true); assert(alloc.entries >= size() + free_entries_needed); assert(alloc.entries > capacity()); Alloc newBuffer = _buffer.create(alloc.bytes); - getTypeHandler()->fallback_copy(newBuffer.get(), buffer.load(std::memory_order_relaxed), size()); + type_handler->fallback_copy(get_buffer(newBuffer, buffer_underflow_size), buffer.load(std::memory_order_relaxed), size()); holdBuffer.swap(_buffer); std::atomic_thread_fence(std::memory_order_release); _buffer = std::move(newBuffer); - buffer.store(_buffer.get(), std::memory_order_release); + buffer.store(get_buffer(buffer_underflow_size), std::memory_order_release); _stats.set_alloc_entries(alloc.entries); } diff --git a/vespalib/src/vespa/vespalib/datastore/bufferstate.h b/vespalib/src/vespa/vespalib/datastore/bufferstate.h index 1b9db616888..01439586f5b 100644 --- a/vespalib/src/vespa/vespalib/datastore/bufferstate.h +++ b/vespalib/src/vespa/vespalib/datastore/bufferstate.h @@ -48,6 +48,8 @@ private: bool _disable_entry_hold_list : 1; bool _compacting : 1; + static void *get_buffer(Alloc& buffer, uint32_t buffer_underflow_size) noexcept { return static_cast<char *>(buffer.get()) + buffer_underflow_size; } + void *get_buffer(uint32_t buffer_underflow_size) noexcept { return get_buffer(_buffer, buffer_underflow_size); } public: /** * TODO: Check if per-buffer free lists are useful, or if diff --git a/vespalib/src/vespa/vespalib/datastore/datastorebase.cpp b/vespalib/src/vespa/vespalib/datastore/datastorebase.cpp index 5926132427c..5c88900ae92 100644 --- a/vespalib/src/vespa/vespalib/datastore/datastorebase.cpp +++ b/vespalib/src/vespa/vespalib/datastore/datastorebase.cpp @@ -59,7 +59,8 @@ DataStoreBase::FallbackHold::FallbackHold(size_t bytesSize, BufferState::Alloc & DataStoreBase::FallbackHold::~FallbackHold() { - _typeHandler->destroy_entries(_buffer.get(), _used_entries); + auto buffer_underflow_size = _typeHandler->buffer_underflow_size(); + _typeHandler->destroy_entries(static_cast<char *>(_buffer.get()) + buffer_underflow_size, _used_entries); } class DataStoreBase::BufferHold : public GenerationHeldBase { diff --git a/vespalib/src/vespa/vespalib/datastore/dynamic_array_buffer_type.h b/vespalib/src/vespa/vespalib/datastore/dynamic_array_buffer_type.h index 2f0f0ae0e26..7ee86b44876 100644 --- a/vespalib/src/vespa/vespalib/datastore/dynamic_array_buffer_type.h +++ b/vespalib/src/vespa/vespalib/datastore/dynamic_array_buffer_type.h @@ -26,12 +26,13 @@ class DynamicArrayBufferType : public BufferTypeBase { using AllocSpec = ArrayStoreConfig::AllocSpec; std::shared_ptr<alloc::MemoryAllocator> _memory_allocator; + public: using ElemType = ElemT; static constexpr size_t entry_min_align = std::max(alignof(uint32_t), alignof(ElemT)); using EntryMinAligner = Aligner<entry_min_align>; - static constexpr size_t entry_bias = EntryMinAligner::align(sizeof(uint32_t)); + static constexpr uint32_t buffer_underflow_size = 64u; protected: static const ElemType& empty_entry() noexcept; ElemType* get_entry(void *buffer, size_t offset) noexcept { return get_entry(buffer, offset, entry_size()); } @@ -55,8 +56,8 @@ public: const vespalib::alloc::MemoryAllocator* get_memory_allocator() const override; static size_t calc_entry_size(size_t array_size) noexcept; static size_t calc_array_size(size_t entry_size) noexcept; - static ElemType* get_entry(void* buffer, size_t offset, uint32_t entry_size) noexcept { return reinterpret_cast<ElemType*>(static_cast<char*>(buffer) + offset * entry_size + entry_bias); } - static const ElemType* get_entry(const void* buffer, size_t offset, uint32_t entry_size) noexcept { return reinterpret_cast<const ElemType*>(static_cast<const char*>(buffer) + offset * entry_size + entry_bias); } + static ElemType* get_entry(void* buffer, size_t offset, uint32_t entry_size) noexcept { return reinterpret_cast<ElemType*>(static_cast<char*>(buffer) + offset * entry_size); } + static const ElemType* get_entry(const void* buffer, size_t offset, uint32_t entry_size) noexcept { return reinterpret_cast<const ElemType*>(static_cast<const char*>(buffer) + offset * entry_size); } static uint32_t get_dynamic_array_size(const ElemType* buffer) noexcept { return *(reinterpret_cast<const uint32_t*>(buffer) - 1); } static void set_dynamic_array_size(ElemType* buffer, uint32_t array_size) noexcept { *(reinterpret_cast<uint32_t*>(buffer) - 1) = array_size; } bool is_dynamic_array_buffer_type() const noexcept override; diff --git a/vespalib/src/vespa/vespalib/datastore/dynamic_array_buffer_type.hpp b/vespalib/src/vespa/vespalib/datastore/dynamic_array_buffer_type.hpp index 3ed26db4e7f..57d5b81b0c7 100644 --- a/vespalib/src/vespa/vespalib/datastore/dynamic_array_buffer_type.hpp +++ b/vespalib/src/vespa/vespalib/datastore/dynamic_array_buffer_type.hpp @@ -9,7 +9,7 @@ namespace vespalib::datastore { template <typename ElemT> DynamicArrayBufferType<ElemT>::DynamicArrayBufferType(uint32_t array_size, const AllocSpec& spec, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) noexcept - : BufferTypeBase(calc_entry_size(array_size), array_size, spec.min_entries_in_buffer, spec.max_entries_in_buffer, spec.num_entries_for_new_buffer, spec.allocGrowFactor), + : BufferTypeBase(calc_entry_size(array_size), buffer_underflow_size, array_size, spec.min_entries_in_buffer, spec.max_entries_in_buffer, spec.num_entries_for_new_buffer, spec.allocGrowFactor), _memory_allocator(std::move(memory_allocator)) { } @@ -24,14 +24,15 @@ template <typename ElemT> size_t DynamicArrayBufferType<ElemT>::calc_entry_size(size_t array_size) noexcept { - return EntryMinAligner::align(sizeof(ElemType) * array_size + entry_bias); + auto entry_size = EntryMinAligner::align(sizeof(ElemType) * array_size + sizeof(uint32_t)); + return entry_size; } template <typename ElemT> size_t DynamicArrayBufferType<ElemT>::calc_array_size(size_t entry_size) noexcept { - return (entry_size - entry_bias) / sizeof(ElemType); + return (entry_size - sizeof(uint32_t)) / sizeof(ElemType); } template <typename ElemT> |