From 7c9e871eb66de64e7a1861fd1faebcd5524fed96 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Thu, 12 Dec 2024 00:17:02 +0100 Subject: Add reserve_capacity to buffer --- asl/buffer.hpp | 92 +++++++++++++++++++++++++++++++++++++++++----- asl/tests/buffer_tests.cpp | 25 +++++++++++++ asl/utility.hpp | 17 +++++++++ 3 files changed, 124 insertions(+), 10 deletions(-) (limited to 'asl') diff --git a/asl/buffer.hpp b/asl/buffer.hpp index da931cb..04444f0 100644 --- a/asl/buffer.hpp +++ b/asl/buffer.hpp @@ -4,6 +4,7 @@ #include "asl/allocator.hpp" #include "asl/annotations.hpp" #include "asl/memory.hpp" +#include "asl/assert.hpp" namespace asl { @@ -35,11 +36,21 @@ class buffer return s; } + constexpr void store_size_encoded(size_t encoded) + { + asl::memcpy(&m_size_encoded_, &encoded, sizeof(size_t)); + } + static constexpr bool is_on_heap(size_t size_encoded) { return (size_encoded & kOnHeapMask) != 0; } + static constexpr size_t encode_size_heap(isize_t size) + { + return static_cast(size) | kOnHeapMask; + } + static constexpr isize_t decode_size(size_t size_encoded) { if constexpr (kInlineCapacity == 0) @@ -55,7 +66,12 @@ class buffer : static_cast(size_encoded >> 56); } } - + + constexpr bool is_on_heap() const + { + return is_on_heap(load_size_encoded()); + } + public: static constexpr isize_t kInlineCapacity = []() { @@ -85,10 +101,70 @@ public: } else { - return is_on_heap(load_size_encoded()) - ? m_capacity - : kInlineCapacity; + return is_on_heap() ? m_capacity : kInlineCapacity; + } + } + + void reserve_capacity(isize_t new_capacity) + { + ASL_ASSERT(new_capacity >= 0); + ASL_ASSERT_RELEASE(new_capacity <= 0x4000'0000'0000'0000); + + if (new_capacity <= capacity()) { return; } + ASL_ASSERT(new_capacity > kInlineCapacity); + + new_capacity = static_cast(round_up_pow2(static_cast(new_capacity))); + + T* old_data = data(); + const isize_t old_capacity = capacity(); + const isize_t current_size = size(); + const bool currently_on_heap = is_on_heap(); + + auto old_layout = layout::array(old_capacity); + auto new_layout = layout::array(new_capacity); + + if (currently_on_heap && trivially_copyable) + { + m_data = reinterpret_cast(m_allocator.realloc(m_data, old_layout, new_layout)); + m_capacity = new_capacity; + return; + } + + T* new_data = reinterpret_cast(m_allocator.alloc(new_layout)); + + // @Todo Move this logic somewhere else. Make move/destruct/etc. abstractions. + + if constexpr (trivially_copyable) + { + auto init_layout = layout::array(current_size); + memcpy(new_data, old_data, init_layout.size); + } + else + { + static_assert(move_constructible); + for (isize_t i = 0; i < current_size; ++i) + { + // NOLINTNEXTLINE(*-pointer-arithmetic) + new(new_data + i) T(ASL_MOVE(old_data[i])); + } } + + if constexpr (!trivially_destructible) + { + for (isize_t i = 0; i < current_size; ++i) + { + (old_data + i)->~T(); + } + } + + if (currently_on_heap) + { + m_allocator.dealloc(old_data, old_layout); + } + + m_data = new_data; + m_capacity = new_capacity; + store_size_encoded(encode_size_heap(current_size)); } // @Todo(C++23) Use deducing this @@ -100,9 +176,7 @@ public: } else { - return is_on_heap(load_size_encoded()) - ? m_data - : reinterpret_cast(this); + return is_on_heap() ? m_data : reinterpret_cast(this); } } @@ -114,9 +188,7 @@ public: } else { - return is_on_heap(load_size_encoded()) - ? m_data - : reinterpret_cast(this); + return is_on_heap() ? m_data : reinterpret_cast(this); } } }; diff --git a/asl/tests/buffer_tests.cpp b/asl/tests/buffer_tests.cpp index b1a59ff..32f6682 100644 --- a/asl/tests/buffer_tests.cpp +++ b/asl/tests/buffer_tests.cpp @@ -24,3 +24,28 @@ ASL_TEST(default_size) ASL_TEST_EXPECT(b2.capacity() == 0); ASL_TEST_EXPECT(b2.data() == nullptr); } + +// @Todo Make test allocator that counts allocations + +ASL_TEST(reserve_capacity) +{ + asl::buffer b; + ASL_TEST_EXPECT(b.size() == 0); + ASL_TEST_EXPECT(b.capacity() == 5); + + b.reserve_capacity(4); + ASL_TEST_EXPECT(b.size() == 0); + ASL_TEST_EXPECT(b.capacity() == 5); + + b.reserve_capacity(12); + ASL_TEST_EXPECT(b.size() == 0); + ASL_TEST_EXPECT(b.capacity() >= 12); + + b.reserve_capacity(13); + ASL_TEST_EXPECT(b.size() == 0); + ASL_TEST_EXPECT(b.capacity() >= 13); + + b.reserve_capacity(130); + ASL_TEST_EXPECT(b.size() == 0); + ASL_TEST_EXPECT(b.capacity() >= 130); +} diff --git a/asl/utility.hpp b/asl/utility.hpp index 7740eff..451a1e3 100644 --- a/asl/utility.hpp +++ b/asl/utility.hpp @@ -2,6 +2,7 @@ #include "asl/meta.hpp" #include "asl/layout.hpp" +#include "asl/assert.hpp" #define ASL_MOVE(expr_) (static_cast<::asl::un_ref_t&&>(expr_)) @@ -30,6 +31,22 @@ T min(T a, T b) return (a <= b) ? a : b; } +constexpr uint64_t round_up_pow2(uint64_t v) +{ + ASL_ASSERT(v <= 0x8000'0000'0000'0000); + + v -= 1; + + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + + return v + 1; +} + #define ASL_DELETE_COPY(T) \ T(const T&) = delete; \ T& operator=(const T&) = delete; -- cgit