diff options
author | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-12-12 00:17:02 +0100 |
---|---|---|
committer | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2024-12-20 15:35:58 +0100 |
commit | 7c9e871eb66de64e7a1861fd1faebcd5524fed96 (patch) | |
tree | 63bfcf8a2163c724b39e67f3786f0daab94a8ea1 /asl | |
parent | b509ebcdc5858a88af4b23499a964305da484b8d (diff) |
Add reserve_capacity to buffer
Diffstat (limited to 'asl')
-rw-r--r-- | asl/buffer.hpp | 92 | ||||
-rw-r--r-- | asl/tests/buffer_tests.cpp | 25 | ||||
-rw-r--r-- | asl/utility.hpp | 17 |
3 files changed, 124 insertions, 10 deletions
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_t>(size) | kOnHeapMask;
+ }
+
static constexpr isize_t decode_size(size_t size_encoded)
{
if constexpr (kInlineCapacity == 0)
@@ -55,7 +66,12 @@ class buffer : static_cast<isize_t>(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<isize_t>(round_up_pow2(static_cast<uint64_t>(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<T>(old_capacity);
+ auto new_layout = layout::array<T>(new_capacity);
+
+ if (currently_on_heap && trivially_copyable<T>)
+ {
+ m_data = reinterpret_cast<T*>(m_allocator.realloc(m_data, old_layout, new_layout));
+ m_capacity = new_capacity;
+ return;
+ }
+
+ T* new_data = reinterpret_cast<T*>(m_allocator.alloc(new_layout));
+
+ // @Todo Move this logic somewhere else. Make move/destruct/etc. abstractions.
+
+ if constexpr (trivially_copyable<T>)
+ {
+ auto init_layout = layout::array<T>(current_size);
+ memcpy(new_data, old_data, init_layout.size);
+ }
+ else
+ {
+ static_assert(move_constructible<T>);
+ 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<T>)
+ {
+ 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<const T*>(this);
+ return is_on_heap() ? m_data : reinterpret_cast<const T*>(this);
}
}
@@ -114,9 +188,7 @@ public: }
else
{
- return is_on_heap(load_size_encoded())
- ? m_data
- : reinterpret_cast<T*>(this);
+ return is_on_heap() ? m_data : reinterpret_cast<T*>(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<int32_t> 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<decltype(expr_)>&&>(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;
|