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;
86 constexpr void operator()(T* ptr)
const noexcept(
noexcept(ptr->~T())) { ptr->~T(); }
95 constexpr Ref() noexcept = default;
96 constexpr
Ref(
std::nullptr_t) noexcept {}
97 constexpr explicit Ref(T* ptr) noexcept
100 requires std::convertible_to<U*, T*>
constexpr Ref(
Ref<U> ref) noexcept
103 constexpr T*
get() const noexcept {
return ptr_; }
105 constexpr T&
operator*() const noexcept {
return *ptr_; }
106 constexpr explicit operator bool() const noexcept {
return ptr_ !=
nullptr; }
114 using Ptr = std::unique_ptr<T, Deleter<T>>;
115 using State = std::pair<size_t, size_t>;
121 : page_size_(page_size) {
122 pages_.emplace_back();
148 template<
class T,
class... Args>
158 template<
class T,
class... Args>
160 static_assert(std::is_trivially_destructible_v<std::remove_const_t<T>>,
161 "a Ref never destroys - use Arena::mk for a type with a destructor");
162 return Ref<T>(create<T>(std::forward<Args>(args)...));
166 template<std::ranges::input_range R,
class T = std::ranges::range_value_t<R>>
168 static_assert(std::is_trivially_destructible_v<T>);
169 auto n = std::ranges::size(range);
171 std::uninitialized_copy(std::ranges::begin(range), std::ranges::end(range), ptr);
181 if (num_bytes == 0)
return nullptr;
185 auto base =
reinterpret_cast<uintptr_t
>(pages_.back().buffer);
187 if (aligned_index + num_bytes > pages_.back().size) {
188 pages_.emplace_back(std::max(page_size_, num_bytes),
align);
192 auto result = pages_.back().buffer + aligned_index;
193 index_ = aligned_index + num_bytes;
199 return static_cast<T*
>(
allocate(num_elems *
sizeof(T),
alignof(T)));
216 assert(num_bytes <= index_);
219 [[nodiscard]]
State state() const noexcept {
return {pages_.size(), index_}; }
222 assert(
state.first > 0);
223 assert(
state.first <= pages_.size());
224 while (pages_.size() >
state.first)
226 assert(
state.second <= pages_.back().size);
227 index_ =
state.second;
234 swap(a1.pages_, a2.pages_);
235 swap(a1.page_size_, a2.page_size_);
236 swap(a1.index_, a2.index_);
241 static constexpr size_t align(
size_t i,
size_t a)
noexcept {
return (i + (a - 1)) & ~(a - 1); }
245 template<
class T,
class... Args>
246 std::remove_const_t<T>* create(Args&&... args) {
247 using U = std::remove_const_t<T>;
249 if constexpr (
sizeof...(Args) < U::num_vlas()) {
250 static_assert(
false,
"one range per VLA, as the last arguments");
252 constexpr auto n =
sizeof...(Args) - U::num_vlas();
253 return create_vla<U>(std::make_index_sequence<n>(), std::make_index_sequence<U::num_vlas()>(),
254 std::forward_as_tuple(std::forward<Args>(args)...));
258 !
requires {
typename U::VLA_Self; },
259 "this inherits the fe::VLA of a base class, so its arrays would sit at that base's "
260 "offset - only the most derived class may declare VLA_Types");
261 return new (
allocate<U>(1)) U(std::forward<Args>(args)...);
265 template<
class U,
size_t... Hs,
size_t... Ts,
class Tuple>
266 U* create_vla(std::index_sequence<Hs...>, std::index_sequence<Ts...>, Tuple&& tuple) {
267 auto counts = std::array<size_t,
sizeof...(Ts)>{std::ranges::size(std::get<
sizeof...(Hs) + Ts>(tuple))...};
268 auto align = std::max(
alignof(U), U::vla_align());
269 auto ptr =
new (
allocate(U::vla_bytes(counts),
align)) U(std::get<Hs>(std::forward<Tuple>(tuple))...);
270 ptr->fill_vla(std::get<
sizeof...(Hs) + Ts>(tuple)...);
275 constexpr Page() noexcept = default;
276 Page(
size_t size,
size_t align)
279 , buffer((
char*)::operator new[](size, std::align_val_t(align))) {}
280 constexpr ~Page() noexcept {
281 if (buffer) ::operator
delete[](buffer, std::align_val_t(align));
284 const size_t size = 0;
285 const size_t align = 0;
286 char* buffer =
nullptr;
289 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
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()))