Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
allocator_traits.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/core_pch.hpp>
4
5#include <algorithm>
6#include <concepts>
7#include <cstddef>
8#include <cstdint>
9#include <memory>
10#include <type_traits>
11
12namespace helios::memory {
13
14/**
15 * @brief Statistics for tracking allocator usage.
16 * @details Provides metrics about memory usage, allocation counts, and fragmentation.
17 */
19 size_t total_allocated = 0; ///< Total bytes currently allocated
20 size_t total_freed = 0; ///< Total bytes freed
21 size_t peak_usage = 0; ///< Peak memory usage
22 size_t allocation_count = 0; ///< Number of active allocations
23 size_t total_allocations = 0; ///< Total number of allocations made
24 size_t total_deallocations = 0; ///< Total number of deallocations made
25 size_t alignment_waste = 0; ///< Bytes wasted due to alignment
26};
27
28/**
29 * @brief Result type for allocation operations.
30 * @details Contains pointer and actual allocated size, or error code.
31 */
33 void* ptr = nullptr; ///< Pointer to allocated memory, nullptr on failure
34 size_t allocated_size = 0; ///< Actual size allocated (may be larger than requested)
35
36 /**
37 * @brief Checks if the allocation was successful.
38 * @return True if ptr is not nullptr, false otherwise
39 */
40 [[nodiscard]] constexpr bool Valid() const noexcept { return ptr != nullptr && allocated_size > 0; }
41};
42
43/**
44 * @brief Concept for basic allocator interface.
45 * @details Allocators must provide allocate and deallocate methods.
46 * Deallocate signature may vary between allocators (e.g., frame allocators don't need parameters).
47 * @tparam T Type to check for allocator requirements
48 */
49template <typename T>
50concept Allocator = requires(T allocator, size_t size, size_t alignment) {
51 { allocator.Allocate(size, alignment) } -> std::same_as<AllocationResult>;
52 // Note: Deallocate signature varies by allocator type, so not checked here
53};
54
55/**
56 * @brief Concept for allocators that can be reset/cleared.
57 * @details Frame allocators and stack allocators typically support this operation.
58 * @tparam T Type to check for resettable allocator requirements
59 */
60template <typename T>
61concept ResettableAllocator = Allocator<T> && requires(T allocator) {
62 { allocator.Reset() } -> std::same_as<void>;
63};
64
65/**
66 * @brief Concept for allocators that provide statistics.
67 * @details Most allocators should provide statistics for debugging and profiling.
68 * @tparam T Type to check for stats support
69 */
70template <typename T>
71concept AllocatorWithStats = Allocator<T> && requires(const T allocator) {
72 { allocator.Stats() } -> std::same_as<AllocatorStats>;
73};
74
75/**
76 * @brief Default alignment for allocations (cache line size for most modern CPUs).
77 */
78constexpr size_t kDefaultAlignment = 64;
79
80/**
81 * @brief Minimum alignment for any allocation.
82 */
83constexpr size_t kMinAlignment = alignof(std::max_align_t);
84
85/**
86 * @brief Helper function to align a size up to the given alignment.
87 * @param size Size to align
88 * @param alignment Alignment boundary (must be power of 2)
89 * @return Aligned size
90 */
91[[nodiscard]] constexpr size_t AlignUp(size_t size, size_t alignment) noexcept {
92 return (size + alignment - 1) & ~(alignment - 1);
93}
94
95/**
96 * @brief Helper function to align a pointer up to the given alignment.
97 * @param ptr Pointer to align
98 * @param alignment Alignment boundary (must be power of 2)
99 * @return Aligned pointer
100 */
101[[nodiscard]] inline void* AlignUpPtr(void* ptr, size_t alignment) noexcept {
102 const auto addr = reinterpret_cast<uintptr_t>(ptr);
103 const auto aligned_addr = (addr + alignment - 1) & ~(alignment - 1);
104 return reinterpret_cast<void*>(aligned_addr);
105}
106
107/**
108 * @brief Helper function to check if a pointer is aligned.
109 * @param ptr Pointer to check
110 * @param alignment Alignment boundary
111 * @return True if pointer is aligned
112 */
113[[nodiscard]] inline bool IsAligned(const void* ptr, size_t alignment) noexcept {
114 return (reinterpret_cast<uintptr_t>(ptr) & (alignment - 1)) == 0;
115}
116
117/**
118 * @brief Helper function to check if a size is a power of 2.
119 * @param size Size to check
120 * @return True if size is power of 2
121 */
122[[nodiscard]] constexpr bool IsPowerOfTwo(size_t size) noexcept {
123 return size != 0 && (size & (size - 1)) == 0;
124}
125
126/**
127 * @brief Calculate padding needed for alignment.
128 * @param ptr Current pointer
129 * @param alignment Desired alignment
130 * @return Padding needed in bytes
131 */
132[[nodiscard]] inline size_t CalculatePadding(const void* ptr, size_t alignment) noexcept {
133 const auto addr = reinterpret_cast<uintptr_t>(ptr);
134 const auto aligned_addr = (addr + alignment - 1) & ~(alignment - 1);
135 return aligned_addr - addr;
136}
137
138/**
139 * @brief Calculate padding with header for alignment.
140 * @param ptr Current pointer
141 * @param alignment Desired alignment
142 * @param header_size Size of header to include
143 * @return Padding needed in bytes
144 */
145[[nodiscard]] inline size_t CalculatePaddingWithHeader(const void* ptr, size_t alignment, size_t header_size) noexcept {
146 const auto addr = reinterpret_cast<uintptr_t>(ptr);
147 const auto aligned_addr = (addr + alignment - 1) & ~(alignment - 1);
148 size_t padding = aligned_addr - addr;
149
150 // Check if we need more padding to fit the header
151 if (padding < header_size) {
152 header_size -= padding;
153 // Align header_size to alignment
154 if (header_size % alignment > 0) {
155 padding += alignment * (1 + (header_size / alignment));
156 } else {
157 padding += alignment * (header_size / alignment);
158 }
159 }
160
161 return padding;
162}
163
164/**
165 * @brief Allocates memory for a single object of type T.
166 * @details Convenience function that calculates size and alignment from the type.
167 * The returned memory is uninitialized - use placement new to construct the object.
168 * @tparam T Type to allocate memory for
169 * @tparam Alloc Allocator type (deduced)
170 * @param allocator Allocator to use for allocation
171 * @return Pointer to allocated memory, or nullptr on failure
172 *
173 * @example
174 * @code
175 * FrameAllocator alloc(1024);
176 * int* ptr = Allocate<int>(alloc);
177 * if (ptr != nullptr) {
178 * new (ptr) int(42); // Placement new
179 * }
180 * @endcode
181 */
182template <typename T, Allocator Alloc>
183[[nodiscard]] constexpr T* Allocate(Alloc& allocator) noexcept {
184 constexpr size_t size = sizeof(T);
185 constexpr size_t alignment = std::max(alignof(T), kMinAlignment);
186 auto result = allocator.Allocate(size, alignment);
187 return static_cast<T*>(result.ptr);
188}
189
190/**
191 * @brief Allocates memory for an array of objects of type T.
192 * @details Convenience function that calculates size and alignment from the type.
193 * The returned memory is uninitialized - use placement new to construct objects.
194 * @tparam T Type to allocate memory for
195 * @tparam Alloc Allocator type (deduced)
196 * @param allocator Allocator to use for allocation
197 * @param count Number of objects to allocate space for
198 * @return Pointer to allocated memory, or nullptr on failure
199 *
200 * @example
201 * @code
202 * FrameAllocator alloc(1024);
203 * int* arr = Allocate<int>(alloc, 10);
204 * if (arr != nullptr) {
205 * for (size_t i = 0; i < 10; ++i) {
206 * new (&arr[i]) int(static_cast<int>(i)); // Placement new
207 * }
208 * }
209 * @endcode
210 */
211template <typename T, Allocator Alloc>
212[[nodiscard]] constexpr T* Allocate(Alloc& allocator, size_t count) noexcept {
213 if (count == 0) [[unlikely]] {
214 return nullptr;
215 }
216 constexpr size_t alignment = std::max(alignof(T), kMinAlignment);
217 const size_t size = sizeof(T) * count;
218 auto result = allocator.Allocate(size, alignment);
219 return static_cast<T*>(result.ptr);
220}
221
222/**
223 * @brief Allocates and constructs a single object of type T.
224 * @details Convenience function that allocates memory and constructs the object in-place.
225 * Uses placement new internally.
226 * @tparam T Type to allocate and construct
227 * @tparam Alloc Allocator type (deduced)
228 * @tparam Args Constructor argument types
229 * @param allocator Allocator to use for allocation
230 * @param args Arguments to forward to T's constructor
231 * @return Pointer to constructed object, or nullptr on allocation failure
232 *
233 * @example
234 * @code
235 * FrameAllocator alloc(1024);
236 * auto* vec = AllocateAndConstruct<MyVec3>(alloc, 1.0f, 2.0f, 3.0f);
237 * @endcode
238 */
239template <typename T, Allocator Alloc, typename... Args>
240 requires std::constructible_from<T, Args...>
241[[nodiscard]] constexpr T* AllocateAndConstruct(Alloc& allocator,
242 Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>) {
243 T* ptr = Allocate<T>(allocator);
244 if (ptr != nullptr) [[likely]] {
245 std::construct_at(ptr, std::forward<Args>(args)...);
246 }
247 return ptr;
248}
249
250/**
251 * @brief Allocates and default-constructs an array of objects of type T.
252 * @details Convenience function that allocates memory and default-constructs objects in-place.
253 * Uses placement new internally.
254 * @tparam T Type to allocate and construct (must be default constructible)
255 * @tparam Alloc Allocator type (deduced)
256 * @param allocator Allocator to use for allocation
257 * @param count Number of objects to allocate and construct
258 * @return Pointer to first constructed object, or nullptr on allocation failure
259 *
260 * @example
261 * @code
262 * FrameAllocator alloc(1024);
263 * auto* arr = AllocateAndConstructArray<MyType>(alloc, 10);
264 * @endcode
265 */
266template <typename T, Allocator Alloc>
267 requires std::default_initializable<T>
268[[nodiscard]] constexpr T* AllocateAndConstructArray(Alloc& allocator, size_t count) noexcept(
269 std::is_nothrow_default_constructible_v<T>) {
270 T* ptr = Allocate<T>(allocator, count);
271 if (ptr != nullptr) [[likely]] {
272 for (size_t i = 0; i < count; ++i) {
273 std::construct_at(ptr + i);
274 }
275 }
276 return ptr;
277}
278
279} // namespace helios::memory
Concept for allocators that provide statistics.
Concept for basic allocator interface.
Concept for allocators that can be reset/cleared.
size_t CalculatePadding(const void *ptr, size_t alignment) noexcept
Calculate padding needed for alignment.
constexpr size_t kDefaultAlignment
Default alignment for allocations (cache line size for most modern CPUs).
bool IsAligned(const void *ptr, size_t alignment) noexcept
Helper function to check if a pointer is aligned.
constexpr size_t AlignUp(size_t size, size_t alignment) noexcept
Helper function to align a size up to the given alignment.
constexpr bool IsPowerOfTwo(size_t size) noexcept
Helper function to check if a size is a power of 2.
constexpr size_t kMinAlignment
Minimum alignment for any allocation.
constexpr T * AllocateAndConstructArray(Alloc &allocator, size_t count) noexcept(std::is_nothrow_default_constructible_v< T >)
constexpr T * Allocate(Alloc &allocator) noexcept
void * AlignUpPtr(void *ptr, size_t alignment) noexcept
Helper function to align a pointer up to the given alignment.
constexpr T * AllocateAndConstruct(Alloc &allocator, Args &&... args) noexcept(std::is_nothrow_constructible_v< T, Args... >)
size_t CalculatePaddingWithHeader(const void *ptr, size_t alignment, size_t header_size) noexcept
Calculate padding with header for alignment.
Result type for allocation operations.
constexpr bool Valid() const noexcept
Checks if the allocation was successful.
void * ptr
Pointer to allocated memory, nullptr on failure.
size_t allocated_size
Actual size allocated (may be larger than requested)
Statistics for tracking allocator usage.
size_t total_freed
Total bytes freed.
size_t peak_usage
Peak memory usage.
size_t alignment_waste
Bytes wasted due to alignment.
size_t allocation_count
Number of active allocations.
size_t total_allocations
Total number of allocations made.
size_t total_allocated
Total bytes currently allocated.
size_t total_deallocations
Total number of deallocations made.