stellarlib 0.1.0
Loading...
Searching...
No Matches
memory.hpp
1/* clang-format off */
2
3/*
4 stellarlib
5 Copyright (C) 2025-2026 Domán Zana
6
7 This software is provided 'as-is', without any express or implied
8 warranty. In no event will the authors be held liable for any damages
9 arising from the use of this software.
10
11 Permission is granted to anyone to use this software for any purpose,
12 including commercial applications, and to alter it and redistribute it
13 freely, subject to the following restrictions:
14
15 1. The origin of this software must not be misrepresented; you must not
16 claim that you wrote the original software. If you use this software
17 in a product, an acknowledgment in the product documentation would be
18 appreciated but is not required.
19 2. Altered source versions must be plainly marked as such, and must not be
20 misrepresented as being the original software.
21 3. This notice may not be removed or altered from any source distribution.
22*/
23
24#ifndef STELLARLIB_EXT_MEMORY_HPP
25#define STELLARLIB_EXT_MEMORY_HPP
26
27#include <stellarlib/ext/type_traits.hpp>
28
29#include <SDL3/SDL_stdinc.h>
30
31#include <bit>
32#include <cstddef>
33#include <cstdlib>
34#include <memory>
35#include <ranges>
36#include <utility>
37
38/**
39 * @brief Standard library extensions
40 */
41namespace stellarlib::ext
42{
43/**
44 * @brief Linear memory allocator optimized for vectors
45 * @tparam T Value type of the allocator
46 * @tparam SizeType Size type of the allocator
47 */
48template <typename T, typename SizeType = std::size_t>
49class vector_allocator : std::allocator<T>
50{
51public:
52 /**
53 * @brief Value type of the allocator
54 */
55 using value_type = std::allocator<T>::value_type;
56
57 /**
58 * @brief Size type of the allocator
59 */
60 using size_type = SizeType;
61
62 /**
63 * @brief Difference type of the allocator
64 */
65 using difference_type = std::allocator<value_type>::difference_type;
66
67 /**
68 * @brief Whether the allocator can be propagated on container move assignment
69 */
70 using propagate_on_container_move_assignment = std::allocator<value_type>::propagate_on_container_move_assignment;
71
72 /**
73 * @brief Default constructor
74 */
75 [[nodiscard]]
76 constexpr vector_allocator() noexcept = default;
77
78 /**
79 * @brief Copy constructor
80 * @param other Other instance
81 */
82 [[nodiscard]]
83 constexpr vector_allocator(const vector_allocator &other) noexcept = default;
84
85 /**
86 * @brief Move constructor
87 * @param other Other instance
88 */
89 [[nodiscard]]
90 constexpr vector_allocator(vector_allocator &&other) noexcept = default;
91
92 /**
93 * @brief Copy assignment operator
94 * @param other Other instance
95 * @return Current instance
96 */
97 constexpr auto operator=(const vector_allocator &other) noexcept
98 -> vector_allocator & = default;
99
100 /**
101 * @brief Move assignment operator
102 * @param other Other instance
103 * @return Current instance
104 */
105 constexpr auto operator=(vector_allocator &&other) noexcept
106 -> vector_allocator & = default;
107
108 /**
109 * @brief Destructor
110 */
111 constexpr ~vector_allocator() noexcept = default;
112
113 /**
114 * @brief Allocates uninitialized memory for N instances of type T
115 * @param begin Updated to point to the allocated memory
116 * @param capacity Number of instances to allocate
117 */
118 constexpr void allocate(value_type *&begin, const size_type capacity) const noexcept
119 {
120 begin = static_cast<value_type *>(std::malloc(capacity * sizeof(value_type)));
121 }
122
123 /**
124 * @brief Reallocates uninitialized memory for N instances of type T via bit-wise relocation
125 * @param begin Pointer for existing memory; Updated to point to the reallocated memory
126 * @param capacity Number of instances to allocate
127 */
128 constexpr void reallocate(value_type *&begin, const size_type capacity) const noexcept
129 {
130 begin = static_cast<value_type *>(std::realloc(begin, capacity * sizeof(value_type)));
131 }
132
133 /**
134 * @brief Reallocates uninitialized memory for at least N instances of type T via element-wise move
135 * @param begin Pointer for existing memory; Updated to point to the reallocated memory
136 * @param size Number of initialized instances in existing memory
137 * @param capacity Requested capacity; Updated to the actual allocated capacity
138 */
139 constexpr void reallocate(value_type *&begin, const size_type size, size_type &capacity) const noexcept
140 {
142 capacity += capacity / 4;
143 reallocate(begin, capacity);
144 }
145 else {
146 capacity = std::bit_ceil(capacity);
147 const auto dst{static_cast<value_type *>(std::malloc(capacity * sizeof(value_type)))};
148
149 for (const auto src : std::views::iota(begin, begin + size)) {
150 std::construct_at(src + (dst - begin), std::move(*src));
151 std::destroy_at(src);
152 }
153
154 deallocate(begin);
155 begin = dst;
156 }
157 }
158
159 /**
160 * @brief Comparison operator
161 * @param other Other instance
162 * @return Result of the comparison
163 */
164 [[nodiscard]]
165 constexpr auto operator==(const vector_allocator &other) const noexcept
166 -> bool = default;
167
168 /**
169 * @brief Deallocates uninitialized memory
170 * @param begin Pointer for existing memory
171 */
172 constexpr void deallocate(value_type *begin) const noexcept
173 {
174 std::free(begin);
175 }
176};
177
178/**
179 * @brief Fixed-size, page-aligned memory arena
180 */
181class arena : std::allocator<void>
182{
183public:
184 /**
185 * @brief Value type of the allocator
186 */
187 using value_type = std::allocator<void>::value_type;
188
189 /**
190 * @brief Size type of the allocator
191 */
192 using size_type = std::allocator<void>::size_type;
193
194 /**
195 * @brief Difference type of the allocator
196 */
197 using difference_type = std::allocator<void>::difference_type;
198
199 /**
200 * @brief Whether the allocator can be propagated on container move assignment
201 */
202 using propagate_on_container_move_assignment = std::allocator<void>::propagate_on_container_move_assignment;
203
204 /**
205 * @brief Parameterized constructor
206 * @param capacity Requested capacity of the arena in bytes
207 */
208 [[nodiscard]]
209 explicit arena(size_type capacity) noexcept;
210
211 /**
212 * @brief Deleted copy constructor
213 */
214 [[nodiscard]]
215 constexpr arena(const arena &) noexcept = delete;
216
217 /**
218 * @brief Move constructor
219 * @param other Other instance
220 */
221 [[nodiscard]]
222 arena(arena &&other) noexcept;
223
224 /**
225 * @brief Deleted copy assignment operator
226 */
227 constexpr auto operator=(const arena &) noexcept
228 -> arena & = delete;
229
230 /**
231 * @brief Move assignment operator
232 * @param other Other instance
233 * @return Current instance
234 */
235 auto operator=(arena &&other) noexcept
236 -> arena &;
237
238 /**
239 * @brief Destructor
240 */
241 ~arena() noexcept;
242
243 /**
244 * @brief Returns the remaining capacity of the arena in bytes
245 * @return Remaining capacity of the arena in bytes
246 */
247 [[nodiscard]]
248 auto capacity() const noexcept
249 -> size_type;
250
251 /**
252 * @brief Returns uninitialized memory for type T
253 * @tparam T Type to determine alignment and size
254 * @return Uninitialized memory for type T, or nullptr if arena is exhausted
255 */
256 template <typename T>
257 [[nodiscard]]
258 constexpr auto allocate() noexcept
259 {
260 if (const auto ptr{std::align(alignof(T), sizeof(T), _cursor, _size)}) {
261 _cursor = static_cast<T *>(_cursor) + 1;
262 _size -= sizeof(T);
263 return static_cast<T *>(ptr);
264 }
265
266 return static_cast<T *>(nullptr);
267 }
268
269 /**
270 * @brief Comparison operator
271 * @param other Other instance
272 * @return Result of the comparison
273 */
274 [[nodiscard]]
275 auto operator==(const arena &other) const noexcept
276 -> bool;
277
278 /**
279 * @brief Resets the arena
280 */
281 void deallocate() noexcept;
282
283private:
284 static const size_type page_capacity;
285 static const size_type page_alignment;
286 size_type _capacity;
287 value_type *_cursor{SDL_aligned_alloc(page_alignment, _capacity)};
288 size_type _size{_capacity};
289 value_type *_begin{_cursor};
290};
291
292/**
293 * @brief Dynamic arena allocator
294 */
295class arena_allocator : std::allocator<void>
296{
297public:
298 /**
299 * @brief Value type of the allocator
300 */
301 using value_type = std::allocator<void>::value_type;
302
303 /**
304 * @brief Size type of the allocator
305 */
306 using size_type = std::allocator<void>::size_type;
307
308 /**
309 * @brief Difference type of the allocator
310 */
311 using difference_type = std::allocator<void>::difference_type;
312
313 /**
314 * @brief Whether the allocator can be propagated on container move assignment
315 */
316 using propagate_on_container_move_assignment = std::allocator<void>::propagate_on_container_move_assignment;
317
318 /**
319 * @brief Default constructor
320 */
321 [[nodiscard]]
322 arena_allocator() noexcept;
323
324 /**
325 * @brief Deleted copy constructor
326 */
327 [[nodiscard]]
328 constexpr arena_allocator(const arena_allocator &) noexcept = delete;
329
330 /**
331 * @brief Move constructor
332 * @param other Other instance
333 */
334 [[nodiscard]]
336
337 /**
338 * @brief Deleted copy assignment operator
339 */
340 constexpr auto operator=(const arena_allocator &) noexcept
341 -> arena_allocator & = delete;
342
343 /**
344 * @brief Move assignment operator
345 * @param other Other instance
346 * @return Current instance
347 */
348 auto operator=(arena_allocator &&other) noexcept
349 -> arena_allocator &;
350
351 /**
352 * @brief Destructor
353 */
354 ~arena_allocator() noexcept;
355
356 /**
357 * @brief Returns uninitialized memory for type T
358 * @tparam T Type to determine alignment and size
359 * @return Uninitialized memory for type T
360 */
361 template <typename T>
362 [[nodiscard]]
363 constexpr auto allocate() noexcept
364 {
365 if (const auto ptr{_begin[_cursor].allocate<T>()}) {
366 return ptr;
367 }
368
369 _begin = static_cast<arena *>(std::realloc(_begin, (++_cursor + 1) * sizeof(arena)));
370 std::construct_at(_begin + _cursor, sizeof(T) <= _capacity ? _capacity : sizeof(T));
371 _capacity += _begin[_cursor].capacity();
372 return _begin[_cursor].allocate<T>();
373 }
374
375 /**
376 * @brief Comparison operator
377 * @param other Other instance
378 * @return Result of the comparison
379 */
380 [[nodiscard]]
381 auto operator==(const arena_allocator &other) const noexcept
382 -> bool;
383
384 /**
385 * @brief Resets the arena
386 */
387 void deallocate() noexcept;
388
389private:
390 arena *_begin{std::construct_at(static_cast<arena *>(std::malloc(sizeof(arena))), 0)};
391 size_type _cursor{};
392 size_type _capacity{_begin->capacity()};
393};
394}
395
396#endif
constexpr auto allocate() noexcept
Returns uninitialized memory for type T.
Definition memory.hpp:363
std::allocator< void >::propagate_on_container_move_assignment propagate_on_container_move_assignment
Whether the allocator can be propagated on container move assignment.
Definition memory.hpp:316
auto operator==(const arena_allocator &other) const noexcept -> bool
Comparison operator.
std::allocator< void >::value_type value_type
Value type of the allocator.
Definition memory.hpp:301
std::allocator< void >::size_type size_type
Size type of the allocator.
Definition memory.hpp:306
void deallocate() noexcept
Resets the arena.
std::allocator< void >::difference_type difference_type
Difference type of the allocator.
Definition memory.hpp:311
arena_allocator() noexcept
Default constructor.
Fixed-size, page-aligned memory arena.
Definition memory.hpp:182
arena(size_type capacity) noexcept
Parameterized constructor.
constexpr auto allocate() noexcept
Returns uninitialized memory for type T.
Definition memory.hpp:258
void deallocate() noexcept
Resets the arena.
auto operator==(const arena &other) const noexcept -> bool
Comparison operator.
auto operator=(arena &&other) noexcept -> arena &
Move assignment operator.
std::allocator< void >::size_type size_type
Size type of the allocator.
Definition memory.hpp:192
arena(arena &&other) noexcept
Move constructor.
std::allocator< void >::value_type value_type
Value type of the allocator.
Definition memory.hpp:187
constexpr arena(const arena &) noexcept=delete
Deleted copy constructor.
~arena() noexcept
Destructor.
constexpr auto operator=(const arena &) noexcept -> arena &=delete
Deleted copy assignment operator.
std::allocator< void >::difference_type difference_type
Difference type of the allocator.
Definition memory.hpp:197
std::allocator< void >::propagate_on_container_move_assignment propagate_on_container_move_assignment
Whether the allocator can be propagated on container move assignment.
Definition memory.hpp:202
auto capacity() const noexcept -> size_type
Returns the remaining capacity of the arena in bytes.
std::allocator< T >::value_type value_type
Value type of the allocator.
Definition memory.hpp:55
constexpr void reallocate(value_type *&begin, const size_type capacity) const noexcept
Reallocates uninitialized memory for N instances of type T via bit-wise relocation.
Definition memory.hpp:128
constexpr void reallocate(value_type *&begin, const size_type size, size_type &capacity) const noexcept
Reallocates uninitialized memory for at least N instances of type T via element-wise move.
Definition memory.hpp:139
constexpr void allocate(value_type *&begin, const size_type capacity) const noexcept
Allocates uninitialized memory for N instances of type T.
Definition memory.hpp:118
constexpr vector_allocator() noexcept=default
Default constructor.
constexpr void deallocate(value_type *begin) const noexcept
Deallocates uninitialized memory.
Definition memory.hpp:172
std::allocator< value_type >::difference_type difference_type
Difference type of the allocator.
Definition memory.hpp:65
SizeType size_type
Size type of the allocator.
Definition memory.hpp:60
constexpr auto operator==(const vector_allocator &other) const noexcept -> bool=default
Comparison operator.
std::allocator< value_type >::propagate_on_container_move_assignment propagate_on_container_move_assignment
Whether the allocator can be propagated on container move assignment.
Definition memory.hpp:70
Standard library extensions.
Definition bit.hpp:33
constexpr bool is_trivially_relocatable_v
Evaluates whether T is eligible for bit-wise relocation.
Definition type_traits.hpp:65