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:windows --cxxopt=-Xclang=-std=c++20
|
||||||
build:linux --cxxopt=-std=c++20
|
build:linux --cxxopt=-std=c++20
|
||||||
|
|
||||||
|
common --incompatible_autoload_externally=+@rules_python
|
||||||
|
|
||||||
build --cxxopt=-Wall
|
build --cxxopt=-Wall
|
||||||
build --cxxopt=-Wno-c++98-compat
|
build --cxxopt=-Wno-c++98-compat
|
||||||
build --cxxopt=-Wno-c++98-compat-pedantic
|
build --cxxopt=-Wno-c++98-compat-pedantic
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "asl/allocator.hpp"
|
#include "asl/allocator.hpp"
|
||||||
#include "asl/annotations.hpp"
|
#include "asl/annotations.hpp"
|
||||||
#include "asl/memory.hpp"
|
#include "asl/memory.hpp"
|
||||||
|
#include "asl/assert.hpp"
|
||||||
|
|
||||||
namespace asl
|
namespace asl
|
||||||
{
|
{
|
||||||
@ -35,11 +36,21 @@ class buffer
|
|||||||
return s;
|
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)
|
static constexpr bool is_on_heap(size_t size_encoded)
|
||||||
{
|
{
|
||||||
return (size_encoded & kOnHeapMask) != 0;
|
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)
|
static constexpr isize_t decode_size(size_t size_encoded)
|
||||||
{
|
{
|
||||||
if constexpr (kInlineCapacity == 0)
|
if constexpr (kInlineCapacity == 0)
|
||||||
@ -56,6 +67,11 @@ class buffer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr bool is_on_heap() const
|
||||||
|
{
|
||||||
|
return is_on_heap(load_size_encoded());
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static constexpr isize_t kInlineCapacity = []() {
|
static constexpr isize_t kInlineCapacity = []() {
|
||||||
@ -85,12 +101,72 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return is_on_heap(load_size_encoded())
|
return is_on_heap() ? m_capacity : kInlineCapacity;
|
||||||
? 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
|
// @Todo(C++23) Use deducing this
|
||||||
const T* data() const
|
const T* data() const
|
||||||
{
|
{
|
||||||
@ -100,9 +176,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return is_on_heap(load_size_encoded())
|
return is_on_heap() ? m_data : reinterpret_cast<const T*>(this);
|
||||||
? m_data
|
|
||||||
: reinterpret_cast<const T*>(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,9 +188,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return is_on_heap(load_size_encoded())
|
return is_on_heap() ? m_data : reinterpret_cast<T*>(this);
|
||||||
? m_data
|
|
||||||
: reinterpret_cast<T*>(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -24,3 +24,28 @@ ASL_TEST(default_size)
|
|||||||
ASL_TEST_EXPECT(b2.capacity() == 0);
|
ASL_TEST_EXPECT(b2.capacity() == 0);
|
||||||
ASL_TEST_EXPECT(b2.data() == nullptr);
|
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/meta.hpp"
|
||||||
#include "asl/layout.hpp"
|
#include "asl/layout.hpp"
|
||||||
|
#include "asl/assert.hpp"
|
||||||
|
|
||||||
#define ASL_MOVE(expr_) (static_cast<::asl::un_ref_t<decltype(expr_)>&&>(expr_))
|
#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;
|
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) \
|
#define ASL_DELETE_COPY(T) \
|
||||||
T(const T&) = delete; \
|
T(const T&) = delete; \
|
||||||
T& operator=(const T&) = delete;
|
T& operator=(const T&) = delete;
|
||||||
|
Reference in New Issue
Block a user