WPILibC++ 2025.1.1
Loading...
Searching...
No Matches
iteration_allocator.hpp
Go to the documentation of this file.
1// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
2// SPDX-License-Identifier: Zlib
3
4#ifndef WPI_MEMORY_ITERATION_ALLOCATOR_HPP_INCLUDED
5#define WPI_MEMORY_ITERATION_ALLOCATOR_HPP_INCLUDED
6
7/// \file
8/// Class template \ref wpi::memory::iteration_allocator.
9
12#include "default_allocator.hpp"
13#include "error.hpp"
14#include "memory_arena.hpp"
15
16namespace wpi
17{
18 namespace memory
19 {
20 namespace detail
21 {
22 template <class BlockOrRawAllocator>
25 } // namespace detail
26
27 /// A stateful RawAllocator that is designed for allocations in a loop.
28 /// It uses `N` stacks for the allocation, one of them is always active.
29 /// Allocation uses the currently active stack.
30 /// Calling \ref iteration_allocator::next_iteration() at the end of the loop,
31 /// will make the next stack active for allocation,
32 /// effectively releasing all of its memory.
33 /// Any memory allocated will thus be usable for `N` iterations of the loop.
34 /// This type of allocator is a generalization of the double frame allocator.
35 /// \ingroup memory_allocator
36 template <std::size_t N, class BlockOrRawAllocator = default_allocator>
38 : WPI_EBO(detail::iteration_block_allocator<BlockOrRawAllocator>)
39 {
40 public:
42
43 /// \effects Creates it with a given initial block size and and other constructor arguments for the BlockAllocator.
44 /// It will allocate the first (and only) block and evenly divide it on all the stacks it uses.
45 template <typename... Args>
46 explicit iteration_allocator(std::size_t block_size, Args&&... args)
47 : allocator_type(block_size, detail::forward<Args>(args)...), cur_(0u)
48 {
49 block_ = get_allocator().allocate_block();
50 auto cur = static_cast<char*>(block_.memory);
51 auto size_each = block_.size / N;
52 for (auto i = 0u; i != N; ++i)
53 {
54 stacks_[i] = detail::fixed_memory_stack(cur);
55 cur += size_each;
56 }
57 }
58
61 block_(other.block_),
62 cur_(detail::move(other.cur_))
63 {
64 for (auto i = 0u; i != N; ++i)
65 stacks_[i] = detail::move(other.stacks_[i]);
66
67 other.cur_ = N;
68 }
69
71 {
72 if (cur_ < N)
73 get_allocator().deallocate_block(block_);
74 }
75
77 {
78 allocator_type::operator=(detail::move(other));
79 block_ = other.block_;
80 cur_ = other.cur_;
81
82 for (auto i = 0u; i != N; ++i)
83 stacks_[i] = detail::move(other.stacks_[i]);
84
85 other.cur_ = N;
86
87 return *this;
88 }
89
90 /// \effects Allocates a memory block of given size and alignment.
91 /// It simply moves the top marker of the currently active stack.
92 /// \returns A node with given size and alignment.
93 /// \throws \ref out_of_fixed_memory if the current stack does not have any memory left.
94 /// \requires \c size and \c alignment must be valid.
95 void* allocate(std::size_t size, std::size_t alignment)
96 {
97 auto& stack = stacks_[cur_];
98
99 auto fence = detail::debug_fence_size;
100 auto offset = detail::align_offset(stack.top() + fence, alignment);
101 if (!stack.top()
102 || (fence + offset + size + fence > std::size_t(block_end(cur_) - stack.top())))
103 WPI_THROW(out_of_fixed_memory(info(), size));
104 return stack.allocate_unchecked(size, offset);
105 }
106
107 /// \effects Allocates a memory block of given size and alignment
108 /// similar to \ref allocate().
109 /// \returns A node with given size and alignment
110 /// or `nullptr` if the current stack does not have any memory left.
111 void* try_allocate(std::size_t size, std::size_t alignment) noexcept
112 {
113 auto& stack = stacks_[cur_];
114 return stack.allocate(block_end(cur_), size, alignment);
115 }
116
117 /// \effects Goes to the next internal stack.
118 /// This will clear the stack whose \ref max_iterations() lifetime has reached,
119 /// and use it for all allocations in this iteration.
120 /// \note This function should be called at the end of the loop.
121 void next_iteration() noexcept
122 {
123 WPI_MEMORY_ASSERT_MSG(cur_ != N, "moved-from allocator");
124 cur_ = (cur_ + 1) % N;
125 stacks_[cur_].unwind(block_start(cur_));
126 }
127
128 /// \returns The number of iteration each allocation will live.
129 /// This is the template parameter `N`.
130 static std::size_t max_iterations() noexcept
131 {
132 return N;
133 }
134
135 /// \returns The index of the current iteration.
136 /// This is modulo \ref max_iterations().
137 std::size_t cur_iteration() const noexcept
138 {
139 return cur_;
140 }
141
142 /// \returns A reference to the BlockAllocator used for managing the memory.
143 /// \requires It is undefined behavior to move this allocator out into another object.
145 {
146 return *this;
147 }
148
149 /// \returns The amount of memory remaining in the stack with the given index.
150 /// This is the number of bytes that are available for allocation.
151 std::size_t capacity_left(std::size_t i) const noexcept
152 {
153 return std::size_t(block_end(i) - stacks_[i].top());
154 }
155
156 /// \returns The amount of memory remaining in the currently active stack.
157 std::size_t capacity_left() const noexcept
158 {
160 }
161
162 private:
163 allocator_info info() const noexcept
164 {
165 return {WPI_MEMORY_LOG_PREFIX "::iteration_allocator", this};
166 }
167
168 char* block_start(std::size_t i) const noexcept
169 {
170 WPI_MEMORY_ASSERT_MSG(i <= N, "moved from state");
171 auto ptr = static_cast<char*>(block_.memory);
172 return ptr + (i * block_.size / N);
173 }
174
175 char* block_end(std::size_t i) const noexcept
176 {
177 WPI_MEMORY_ASSERT_MSG(i < N, "moved from state");
178 return block_start(i + 1);
179 }
180
181 detail::fixed_memory_stack stacks_[N];
182 memory_block block_;
183 std::size_t cur_;
184
185 friend allocator_traits<iteration_allocator<N, BlockOrRawAllocator>>;
186 friend composable_allocator_traits<iteration_allocator<N, BlockOrRawAllocator>>;
187 };
188
189 /// An alias for \ref iteration_allocator for two iterations.
190 /// \ingroup memory_allocator
191 template <class BlockOrRawAllocator = default_allocator>
192 WPI_ALIAS_TEMPLATE(double_frame_allocator,
194
195#if WPI_MEMORY_EXTERN_TEMPLATE
196 extern template class iteration_allocator<2>;
197#endif
198
199 /// Specialization of the \ref allocator_traits for \ref iteration_allocator.
200 /// \note It is not allowed to mix calls through the specialization and through the member functions,
201 /// i.e. \ref memory_stack::allocate() and this \c allocate_node().
202 /// \ingroup memory_allocator
203 template <std::size_t N, class BlockAllocator>
205 {
206 public:
208 using is_stateful = std::true_type;
209
210 /// \returns The result of \ref iteration_allocator::allocate().
211 static void* allocate_node(allocator_type& state, std::size_t size,
212 std::size_t alignment)
213 {
214 return state.allocate(size, alignment);
215 }
216
217 /// \returns The result of \ref memory_stack::allocate().
218 static void* allocate_array(allocator_type& state, std::size_t count, std::size_t size,
219 std::size_t alignment)
220 {
221 return allocate_node(state, count * size, alignment);
222 }
223
224 /// @{
225 /// \effects Does nothing.
226 /// Actual deallocation can only be done via \ref memory_stack::unwind().
227 static void deallocate_node(allocator_type&, void*, std::size_t, std::size_t) noexcept
228 {
229 }
230
231 static void deallocate_array(allocator_type&, void*, std::size_t, std::size_t,
232 std::size_t) noexcept
233 {
234 }
235 /// @}
236
237 /// @{
238 /// \returns The maximum size which is \ref iteration_allocator::capacity_left().
239 static std::size_t max_node_size(const allocator_type& state) noexcept
240 {
241 return state.capacity_left();
242 }
243
244 static std::size_t max_array_size(const allocator_type& state) noexcept
245 {
246 return state.capacity_left();
247 }
248 /// @}
249
250 /// \returns The maximum possible value since there is no alignment restriction
251 /// (except indirectly through \ref memory_stack::next_capacity()).
252 static std::size_t max_alignment(const allocator_type&) noexcept
253 {
254 return std::size_t(-1);
255 }
256 };
257
258 /// Specialization of the \ref composable_allocator_traits for \ref iteration_allocator classes.
259 /// \ingroup memory_allocator
260 template <std::size_t N, class BlockAllocator>
262 {
263 public:
265
266 /// \returns The result of \ref memory_stack::try_allocate().
267 static void* try_allocate_node(allocator_type& state, std::size_t size,
268 std::size_t alignment) noexcept
269 {
270 return state.try_allocate(size, alignment);
271 }
272
273 /// \returns The result of \ref memory_stack::try_allocate().
274 static void* try_allocate_array(allocator_type& state, std::size_t count,
275 std::size_t size, std::size_t alignment) noexcept
276 {
277 return state.try_allocate(count * size, alignment);
278 }
279
280 /// @{
281 /// \effects Does nothing.
282 /// \returns Whether the memory will be deallocated by \ref memory_stack::unwind().
283 static bool try_deallocate_node(allocator_type& state, void* ptr, std::size_t,
284 std::size_t) noexcept
285 {
286 return state.block_.contains(ptr);
287 }
288
289 static bool try_deallocate_array(allocator_type& state, void* ptr, std::size_t count,
290 std::size_t size, std::size_t alignment) noexcept
291 {
292 return try_deallocate_node(state, ptr, count * size, alignment);
293 }
294 /// @}
295 };
296
297#if WPI_MEMORY_EXTERN_TEMPLATE
298 extern template class allocator_traits<iteration_allocator<2>>;
299 extern template class composable_allocator_traits<iteration_allocator<2>>;
300#endif
301 } // namespace memory
302} // namespace wpi
303
304#endif // WPI_MEMORY_ITERATION_ALLOCATOR_HPP_INCLUDED
static void * allocate_array(allocator_type &state, std::size_t count, std::size_t size, std::size_t alignment)
Definition iteration_allocator.hpp:218
static void deallocate_node(allocator_type &, void *, std::size_t, std::size_t) noexcept
Definition iteration_allocator.hpp:227
static std::size_t max_node_size(const allocator_type &state) noexcept
Definition iteration_allocator.hpp:239
std::true_type is_stateful
Definition iteration_allocator.hpp:208
static std::size_t max_alignment(const allocator_type &) noexcept
Definition iteration_allocator.hpp:252
static void * allocate_node(allocator_type &state, std::size_t size, std::size_t alignment)
Definition iteration_allocator.hpp:211
static void deallocate_array(allocator_type &, void *, std::size_t, std::size_t, std::size_t) noexcept
Definition iteration_allocator.hpp:231
static std::size_t max_array_size(const allocator_type &state) noexcept
Definition iteration_allocator.hpp:244
The default specialization of the allocator_traits for a RawAllocator.
Definition allocator_traits.hpp:292
static void * try_allocate_node(allocator_type &state, std::size_t size, std::size_t alignment) noexcept
Definition iteration_allocator.hpp:267
static bool try_deallocate_array(allocator_type &state, void *ptr, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition iteration_allocator.hpp:289
static void * try_allocate_array(allocator_type &state, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition iteration_allocator.hpp:274
static bool try_deallocate_node(allocator_type &state, void *ptr, std::size_t, std::size_t) noexcept
Definition iteration_allocator.hpp:283
The default specialization of the composable_allocator_traits for a ComposableAllocator.
Definition allocator_traits.hpp:500
static bool try_deallocate_node(allocator_type &state, void *node, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:522
Definition memory_stack.hpp:22
void unwind(char *top) noexcept
Definition memory_stack.hpp:100
A stateful RawAllocator that is designed for allocations in a loop.
Definition iteration_allocator.hpp:39
std::size_t cur_iteration() const noexcept
Definition iteration_allocator.hpp:137
void * allocate(std::size_t size, std::size_t alignment)
Definition iteration_allocator.hpp:95
std::size_t capacity_left(std::size_t i) const noexcept
Definition iteration_allocator.hpp:151
void * try_allocate(std::size_t size, std::size_t alignment) noexcept
Definition iteration_allocator.hpp:111
static std::size_t max_iterations() noexcept
Definition iteration_allocator.hpp:130
detail::iteration_block_allocator< BlockOrRawAllocator > allocator_type
Definition iteration_allocator.hpp:41
void next_iteration() noexcept
Definition iteration_allocator.hpp:121
~iteration_allocator() noexcept
Definition iteration_allocator.hpp:70
iteration_allocator(std::size_t block_size, Args &&... args)
Definition iteration_allocator.hpp:46
std::size_t capacity_left() const noexcept
Definition iteration_allocator.hpp:157
allocator_type & get_allocator() noexcept
Definition iteration_allocator.hpp:144
iteration_allocator(iteration_allocator &&other) noexcept
Definition iteration_allocator.hpp:59
iteration_allocator & operator=(iteration_allocator &&other) noexcept
Definition iteration_allocator.hpp:76
A special case of out_of_memory errors thrown when a low-level allocator with a fixed size runs out o...
Definition error.hpp:121
Alias template for an STL container that uses a certain RawAllocator.
Definition container.hpp:184
#define WPI_MEMORY_LOG_PREFIX
Definition config.hpp:46
#define WPI_THROW(Ex)
Definition config.hpp:33
#define WPI_ALIAS_TEMPLATE(Name,...)
Definition config.hpp:73
The typedef wpi::memory::default_allocator.
The exception classes.
auto ptr(T p) -> const void *
Converts p to const void* for pointer formatting.
Definition format.h:3821
implementation_defined make_block_allocator_t
Takes either a BlockAllocator or a RawAllocator.
Definition memory_arena.hpp:622
Class wpi::memory::memory_arena and related functionality regarding BlockAllocators.
detail namespace with internal helper functions
Definition input_adapters.h:32
std::size_t align_offset(std::uintptr_t address, std::size_t alignment) noexcept
Definition align.hpp:26
std::remove_reference< T >::type && move(T &&arg) noexcept
Definition utility.hpp:25
constexpr std::size_t debug_fence_size
Definition debug_helpers.hpp:22
make_block_allocator_t< BlockOrRawAllocator, fixed_block_allocator > iteration_block_allocator
Definition iteration_allocator.hpp:23
Memory namespace.
Definition heap_allocator.hpp:20
Foonathan namespace.
Definition ntcore_cpp.h:26
Contains information about an allocator.
Definition error.hpp:23
std::size_t size
The size of the memory block (might be 0).
Definition memory_arena.hpp:30
void * memory
The address of the memory block (might be nullptr).
Definition memory_arena.hpp:29
#define WPI_MEMORY_ASSERT_MSG(Expr, Msg)
Definition assert.hpp:47