FE 0.6.0
A header-only C++ library for writing frontends
Loading...
Searching...
No Matches
arena.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <list>
5#include <memory>
6
7#include "fe/assert.h"
8
9namespace fe {
10
11/// An arena pre-allocates so-called *pages* of size Arena::page_size_.
12/// You can use Arena::allocate to obtain memory from this.
13/// When a page runs out of memory, the next page will be (pre-)allocated.
14/// You cannot directly release memory obtained via this method.
15/// Instead, *all* memory acquired via this Arena will be released as soon as this Arena will be destroyed.
16/// As an exception, you can Arena::deallocate memory that just as been acquired.
17class Arena {
18public:
19 static constexpr size_t Default_Page_Size = 1024 * 1024; ///< 1MB.
20
21 /// @name Allocator
22 /// An [allocator](https://en.cppreference.com/w/cpp/named_req/Allocator) in order to use this Arena for
23 /// [containers](https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer).
24 /// Construct it via Arena::allocator.
25 ///@{
26 template<class T> struct Allocator {
27 using value_type = T;
28
29 Allocator() = delete;
30 template<class U>
32 : arena(allocator.arena) {}
34 : arena(arena) {}
35
36 [[nodiscard]] T* allocate(size_t num_elems) { return arena.allocate<T>(num_elems); }
37
38 void deallocate(T*, size_t) noexcept {}
39
40 template<class U> bool operator==(const Allocator<U>& a) const noexcept { return &arena == &a.arena; }
41 template<class U> bool operator!=(const Allocator<U>& a) const noexcept { return &arena != &a.arena; }
42
44 };
45
46 /// Create Allocator from Arena.
47 template<class T> Allocator<T> allocator() { return Allocator<T>(*this); }
48 ///@}
49
50 /// @name Smart Pointer
51 /// This is a [std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr)
52 /// that uses the Arena under the hood
53 /// and whose deleter will *only* invoke the destructor but *not* `delete` anything;
54 /// memory will be released upon destruction of the Arena.
55 ///
56 /// Use like this:
57 /// ```
58 /// auto ptr = arena.mk<Foo>(a, b, c); // new Foo(a, b, c) placed into arena
59 /// ```
60 ///@{
61 template<class T> struct Deleter {
62 constexpr Deleter() noexcept = default;
63 template<class U> constexpr Deleter(const Deleter<U>&) noexcept {}
64 void operator()(T* ptr) { ptr->~T(); }
65 };
66
67 template<class T> using Ptr = std::unique_ptr<T, Deleter<T>>;
68 template<class T, class... Args> Ptr<T> mk(Args&&... args) {
69 auto ptr = new (allocate(sizeof(T))) T(std::forward<Args&&>(args)...);
70 return Ptr<T>(ptr, Deleter<T>());
71 }
72 ///@}
73
74 /// @name Construction/Destruction
75 ///@{
76 Arena(size_t page_size = Default_Page_Size)
77 : page_size_(page_size) {
78 pages_.emplace_back(page_size);
79 }
80 Arena(const Arena&) = delete;
81 Arena(Arena&& other) noexcept
82 : Arena() {
83 swap(*this, other);
84 }
85 Arena& operator=(Arena) = delete;
86 ///@}
87
88 /// @name Allocate
89 ///@{
90
91 /// Align next allocate(size_t) to @p a.
92 Arena& align(size_t a) { return index_ = (index_ + (a - 1)) & ~(a - 1), *this; }
93
94 /// Get @p n bytes of fresh memory.
95 [[nodiscard]] void* allocate(size_t num_bytes) {
96 if (index_ + num_bytes > pages_.back().size) {
97 pages_.emplace_back(std::max(page_size_, num_bytes));
98 index_ = 0;
99 }
100
101 auto result = pages_.back().buffer.get() + index_;
102 index_ += num_bytes;
103 return result;
104 }
105
106 template<class T> [[nodiscard]] T* allocate(size_t num_elems) {
107 align(alignof(T));
108 return static_cast<T*>(allocate(num_elems * std::max(sizeof(T), alignof(T))));
109 }
110 ///@}
111
112 /// @name Deallocate
113 /// Deallocate memory again in reverse order.
114 ///@{
115 /// Removes @p num_bytes again.
116 void deallocate(size_t num_bytes) { index_ -= num_bytes; }
117
118 /// Goes back to @p state in Arena.
119 /// Use like this:
120 /// ```
121 /// auto state = arena.state();
122 /// auto ptr = arena.allocate(n);
123 /// if (/* I don't want that */) arena.deallocate(state);
124 /// ```
125 /// @warning Only use, if you really know what you are doing.
126 using State = std::pair<size_t, size_t>;
127 State state() const { return {pages_.size(), index_}; }
129 if (state.first == pages_.size())
130 index_ = state.second; // don't care otherwise
131 else
132 index_ = 0;
133 }
134 ///@}
135
136 friend void swap(Arena& a1, Arena& a2) noexcept {
137 using std::swap;
138 // clang-format off
139 swap(a1.pages_, a2.pages_);
140 swap(a1.page_size_, a2.page_size_);
141 swap(a1.index_, a2.index_);
142 // clang-format on
143 }
144
145private:
146 struct Page {
147 Page(size_t size)
148 : size(size)
149 , buffer(std::make_unique<char[]>(size)) {}
150 const size_t size;
151 std::unique_ptr<char[]> buffer;
152 };
153
154 std::list<Page> pages_;
155 size_t page_size_;
156 size_t index_ = 0;
157};
158
159} // namespace fe
An arena pre-allocates so-called pages of size Arena::page_size_.
Definition arena.h:17
void deallocate(State state)
Definition arena.h:128
Arena(const Arena &)=delete
Arena & align(size_t a)
Align next allocate(size_t) to a.
Definition arena.h:92
void deallocate(size_t num_bytes)
Definition arena.h:116
Arena(Arena &&other) noexcept
Definition arena.h:81
std::pair< size_t, size_t > State
Goes back to state in Arena.
Definition arena.h:126
void * allocate(size_t num_bytes)
Get n bytes of fresh memory.
Definition arena.h:95
Arena(size_t page_size=Default_Page_Size)
Definition arena.h:76
Allocator< T > allocator()
Create Allocator from Arena.
Definition arena.h:47
T * allocate(size_t num_elems)
Definition arena.h:106
friend void swap(Arena &a1, Arena &a2) noexcept
Definition arena.h:136
Arena & operator=(Arena)=delete
std::unique_ptr< T, Deleter< T > > Ptr
Definition arena.h:67
Ptr< T > mk(Args &&... args)
Definition arena.h:68
static constexpr size_t Default_Page_Size
1MB.
Definition arena.h:19
State state() const
Definition arena.h:127
Definition arena.h:9
bool operator!=(const Allocator< U > &a) const noexcept
Definition arena.h:41
T * allocate(size_t num_elems)
Definition arena.h:36
void deallocate(T *, size_t) noexcept
Definition arena.h:38
bool operator==(const Allocator< U > &a) const noexcept
Definition arena.h:40
Allocator(Arena &arena) noexcept
Definition arena.h:33
Allocator(const Arena::Allocator< U > &allocator) noexcept
Definition arena.h:31
constexpr Deleter() noexcept=default
void operator()(T *ptr)
Definition arena.h:64