Add reserve_capacity to buffer
This commit is contained in:
2
.bazelrc
2
.bazelrc
@ -7,6 +7,8 @@ build:linux --repo_env=CC=clang
|
||||
build:windows --cxxopt=-Xclang=-std=c++20
|
||||
build:linux --cxxopt=-std=c++20
|
||||
|
||||
common --incompatible_autoload_externally=+@rules_python
|
||||
|
||||
build --cxxopt=-Wall
|
||||
build --cxxopt=-Wno-c++98-compat
|
||||
build --cxxopt=-Wno-c++98-compat-pedantic
|
||||
|
@ -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,12 +101,72 @@ 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
|
||||
const T* data() const
|
||||
{
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user