8#include <memory_resource>
38 void*
do_allocate(
size_t bytes,
size_t alignment)
override {
return arena_.allocate(bytes, alignment); }
41 if (
this == &other)
return true;
84 template<class U,
std::enable_if_t<
std::is_convertible_v<U*, T*>,
int> = 0>
87 constexpr void operator()(T* ptr)
const noexcept(
noexcept(ptr->~T())) { ptr->~T(); }
96 constexpr Ref() noexcept = default;
97 constexpr
Ref(
std::nullptr_t) noexcept {}
98 constexpr explicit Ref(T* ptr) noexcept
101 requires std::convertible_to<U*, T*>
constexpr Ref(
Ref<U> ref) noexcept
104 constexpr T*
get() const noexcept {
return ptr_; }
106 constexpr T&
operator*() const noexcept {
return *ptr_; }
107 constexpr explicit operator bool() const noexcept {
return ptr_ !=
nullptr; }
115 using Ptr = std::unique_ptr<T, Deleter<T>>;
116 using State = std::pair<size_t, size_t>;
122 : page_size_(page_size) {
123 pages_.emplace_back();
149 template<
class T,
class... Args>
159 template<
class T,
class... Args>
161 static_assert(std::is_trivially_destructible_v<std::remove_const_t<T>>,
162 "a Ref never destroys - use Arena::mk for a type with a destructor");
163 return Ref<T>(create<T>(std::forward<Args>(args)...));
167 template<std::ranges::input_range R,
class T = std::ranges::range_value_t<R>>
169 static_assert(std::is_trivially_destructible_v<T>);
170 auto n = std::ranges::size(range);
172 std::uninitialized_copy(std::ranges::begin(range), std::ranges::end(range), ptr);
187 if (num_bytes == 0)
return nullptr;
191 if (aligned_index + num_bytes > pages_.back().size) {
192 pages_.emplace_back(std::max(page_size_, num_bytes),
align);
196 auto result = pages_.back().buffer + aligned_index;
197 index_ = aligned_index + num_bytes;
203 return static_cast<T*
>(
allocate(num_elems *
sizeof(T),
alignof(T)));
220 assert(num_bytes <= index_);
223 [[nodiscard]]
State state() const noexcept {
return {pages_.size(), index_}; }
226 assert(
state.first > 0);
227 assert(
state.first <= pages_.size());
228 while (pages_.size() >
state.first)
230 assert(
state.second <= pages_.back().size);
231 index_ =
state.second;
238 swap(a1.pages_, a2.pages_);
239 swap(a1.page_size_, a2.page_size_);
240 swap(a1.index_, a2.index_);
245 static constexpr size_t align(
size_t i,
size_t a)
noexcept {
return (i + (a - 1)) & ~(a - 1); }
249 template<
class T,
class... Args>
250 std::remove_const_t<T>* create(Args&&... args) {
251 using U = std::remove_const_t<T>;
253 static_assert(
sizeof...(Args) >= U::num_vlas(),
"one range per VLA, as the last arguments");
254 constexpr auto n =
sizeof...(Args) - U::num_vlas();
255 return create_vla<U>(std::make_index_sequence<n>(), std::make_index_sequence<U::num_vlas()>(),
256 std::forward_as_tuple(std::forward<Args>(args)...));
259 !
requires {
typename U::VLA_Self; },
260 "this inherits the fe::VLA of a base class, so its arrays would sit at that base's "
261 "offset - only the most derived class may declare VLA_Types");
262 return new (
allocate<U>(1)) U(std::forward<Args>(args)...);
266 template<
class U,
size_t... Hs,
size_t... Ts,
class Tuple>
267 U* create_vla(std::index_sequence<Hs...>, std::index_sequence<Ts...>, Tuple&& tuple) {
268 auto counts = std::array<size_t,
sizeof...(Ts)>{std::ranges::size(std::get<
sizeof...(Hs) + Ts>(tuple))...};
269 auto align = std::max(
alignof(U), U::vla_align());
270 auto ptr =
new (
allocate(U::vla_bytes(counts),
align)) U(std::get<Hs>(std::forward<Tuple>(tuple))...);
271 ptr->fill_vla(std::get<
sizeof...(Hs) + Ts>(tuple)...);
275 Arena&
align(
size_t a)
noexcept {
return index_ =
align(index_, a), *
this; }
278 constexpr Page() noexcept = default;
279 Page(
size_t size,
size_t align)
282 , buffer((
char*)::operator new[](size, std::align_val_t(align))) {}
283 constexpr ~Page() noexcept {
284 if (buffer) ::operator
delete[](buffer, std::align_val_t(align));
287 const size_t size = 0;
288 const size_t align = 0;
289 char* buffer =
nullptr;
292 std::list<Page> pages_;
A memory resource bridge in order to use this Arena for pmr containers.
bool do_is_equal(const std::pmr::memory_resource &other) const noexcept override
MemoryResource(Arena &arena) noexcept
void do_deallocate(void *, size_t, size_t) override
void * do_allocate(size_t bytes, size_t alignment) override
A non-owning pointer into an Arena.
constexpr bool operator==(const Ref &) const noexcept=default
constexpr Ref() noexcept=default
constexpr Ref(T *ptr) noexcept
constexpr T * operator->() const noexcept
constexpr T & operator*() const noexcept
constexpr T * get() const noexcept
constexpr Ref(Ref< U > ref) noexcept
void * allocate(size_t num_bytes, size_t align)
Get n bytes of fresh memory.
void deallocate(State state) noexcept
Ref< T > ref(Args &&... args)
Like Arena::mk, but yields a Ref: nothing will ever destroy the object.
Arena(const Arena &)=delete
Arena(Arena &&other) noexcept
Span< T > copy(const R &range)
An Arena-allocated copy of range.
static constexpr size_t align(size_t i, size_t a) noexcept
Align i to a.
void deallocate(size_t num_bytes) noexcept
Removes num_bytes again.
std::pmr::memory_resource * resource() noexcept
std::pair< size_t, size_t > State
Arena(size_t page_size=Default_Page_Size)
T * allocate(size_t num_elems)
Allocator< T > allocator() noexcept
Create Allocator from Arena.
friend void swap(Arena &a1, Arena &a2) noexcept
Arena & operator=(Arena)=delete
std::unique_ptr< T, Deleter< T > > Ptr
const std::pmr::memory_resource * resource() const noexcept
Ptr< T > mk(Args &&... args)
This is a std::unique_ptr that uses the Arena under the hood and whose Deleter will only invoke the d...
static constexpr size_t Default_Page_Size
1MB.
State state() const noexcept
This is a thin wrapper for std::span<T, N> with the following additional features:
An allocator in order to use this Arena for containers.
constexpr bool operator!=(const Allocator< U > &) const noexcept
constexpr bool operator==(const Allocator< U > &) const noexcept
All Arena::Allocators compare equal.
T * allocate(size_t num_elems)
constexpr void deallocate(T *, size_t) noexcept
constexpr Allocator(const Arena::Allocator< U > &allocator) noexcept
constexpr Allocator(Arena &arena) noexcept
constexpr Deleter() noexcept=default
constexpr void operator()(T *ptr) const noexcept(noexcept(ptr->~T()))