intnslib 0.1
A library to hold common functionality used across multiple projects.
Loading...
Searching...
No Matches
StackAllocator.hpp
1#ifndef INTNS_MEMORY_STACKALLOCATOR_HPP
2#define INTNS_MEMORY_STACKALLOCATOR_HPP
3
4#include <cstddef>
5#include <cstdint>
6#include <optional>
7#include <stdexcept>
8
9#include "Alignment.hpp"
10
11namespace intns::memory {
12
34 public:
35 using marker_type = std::uintptr_t;
36 using checkpoint_t = marker_type;
37 using size_type = std::size_t;
38
39 // Validate our assumptions
40 static_assert(sizeof(marker_type) >= sizeof(void*),
41 "marker_type must be able to hold pointer values");
42 static_assert(std::is_same_v<marker_type, std::uintptr_t>,
43 "marker_type should be uintptr_t for pointer arithmetic");
44 static_assert(sizeof(size_type) >= sizeof(std::size_t),
45 "size_type must accommodate all possible allocation sizes");
46
53 StackAllocator(size_type size = 1000);
54
64 StackAllocator(void* memory, size_type size);
65
72
73 // Don't allow moving or copying
74 StackAllocator(const StackAllocator&) = delete;
75 StackAllocator& operator=(const StackAllocator&) = delete;
77 StackAllocator& operator=(StackAllocator&&) = delete;
78
96 template <typename T>
97 [[nodiscard]] T* alloc_t() noexcept {
98 static_assert(!std::is_reference_v<T>,
99 "Cannot allocate storage for reference types");
100 static_assert(!std::is_void_v<T>, "Cannot allocate storage for void");
101 static_assert(std::is_destructible_v<T>, "Type must be destructible");
102 static_assert(sizeof(T) > 0, "Type must have non-zero size");
103
104 constexpr size_type type_size = sizeof(T);
105 constexpr size_type align_req = alignof(T); // Always power of 2 per spec
106
107 if (type_size > capacity_) [[unlikely]] {
108 return nullptr;
109 }
110
111 const auto aligned_pos = align_address(active_marker_, align_req);
112
113 // This calculates 'type_size' BACK from the end, where are we last valid?
114 const auto last_valid_start_pos = (start_marker_ + capacity_) - type_size;
115 if (aligned_pos < start_marker_ || aligned_pos > last_valid_start_pos) {
116 return nullptr;
117 }
118
119 // Get aligned address and advance the marker
120 T* new_addr = reinterpret_cast<T*>(aligned_pos);
121 active_marker_ = aligned_pos + type_size;
122 return new_addr;
123 }
124
141 [[nodiscard]] void* alloc(size_type size, size_type alignment = alignof(
142 std::max_align_t)) noexcept {
143 if (size == 0 || size > capacity_) {
144 return nullptr;
145 }
146
147 // If anything is awry with the alignment, bail
148 if (!is_power_of_two(alignment) || alignment > capacity_) [[unlikely]] {
149 return nullptr;
150 }
151
152 const auto aligned_pos = align_address(active_marker_, alignment);
153
154 // This calculates 'size' BACK from the end, where are we last valid?
155 const auto last_valid_start_pos = (start_marker_ + capacity_) - size;
156 if (aligned_pos < start_marker_ || aligned_pos > last_valid_start_pos) {
157 return nullptr;
158 }
159
160 // Get aligned address and advance the marker
161 void* new_addr = reinterpret_cast<void*>(aligned_pos);
162 active_marker_ = aligned_pos + size;
163 return new_addr;
164 }
165
170 [[nodiscard]] checkpoint_t save_checkpoint() const noexcept {
171 return active_marker_;
172 }
173
180 void restore_checkpoint(checkpoint_t checkpoint) {
181 // Check the bounds of the checkpoint: [S] checkpoint lives here [E]
182 if (checkpoint < start_marker_ || checkpoint > start_marker_ + capacity_)
183 [[unlikely]] {
184 throw std::runtime_error(
185 "StackAllocator::restore_checkpoint: Invalid checkpoint");
186 }
187
188 active_marker_ = checkpoint;
189 }
190
196 [[nodiscard]] size_type bytes_used() const noexcept {
197 return active_marker_ - start_marker_;
198 }
199
205 [[nodiscard]] size_type bytes_remaining() const noexcept {
206 return capacity_ - bytes_used();
207 }
208
214 [[nodiscard]] size_type capacity() const noexcept { return capacity_; }
215
220 void reset() noexcept { active_marker_ = start_marker_; }
221
222 private:
223 // Marker to track the beginning of the stack
224 marker_type start_marker_ = 0;
225
226 // Marker to track the current position of the stack
227 marker_type active_marker_ = 0;
228
229 // The full size of the stack
230 size_type capacity_ = 0;
231
232 // Tracks ownership of the start marker
233 bool owns_memory_ = true;
234};
235
247 // The parent allocator
248 StackAllocator& allocator_;
249
250 // The position at object creation, to be restored on destruction
251 StackAllocator::checkpoint_t saved_ = 0;
252
253 public:
259 : allocator_(a), saved_(a.save_checkpoint()) {}
260
267 ~StackCheckpoint() { allocator_.restore_checkpoint(saved_); }
268
269 // Don't allow moving or copying - this is a scoped checkpoint!
270 StackCheckpoint(const StackCheckpoint&) = delete;
271 StackCheckpoint& operator=(const StackCheckpoint&) = delete;
273 StackCheckpoint& operator=(StackCheckpoint&&) = delete;
274};
275
276} // namespace intns::memory
277
278#endif
A fast, linear stack-based memory allocator for temporary allocations.
Definition StackAllocator.hpp:33
T * alloc_t() noexcept
Allocates memory for a single object of type T.
Definition StackAllocator.hpp:97
~StackAllocator()
Destructor for the StackAllocator class.
Definition StackAllocator.cpp:53
size_type bytes_remaining() const noexcept
Returns the number of bytes remaining in the stack allocator.
Definition StackAllocator.hpp:205
size_type bytes_used() const noexcept
Returns the number of bytes currently used by the stack allocator.
Definition StackAllocator.hpp:196
size_type capacity() const noexcept
Returns the total capacity of the stack allocator.
Definition StackAllocator.hpp:214
void reset() noexcept
Resets the stack allocator to its initial state.
Definition StackAllocator.hpp:220
void restore_checkpoint(checkpoint_t checkpoint)
Restores the stack allocator to a previously saved checkpoint.
Definition StackAllocator.hpp:180
checkpoint_t save_checkpoint() const noexcept
Saves the current state of the stack allocator.
Definition StackAllocator.hpp:170
void * alloc(size_type size, size_type alignment=alignof(std::max_align_t)) noexcept
Allocates a block of memory from the stack allocator with the specified size and alignment.
Definition StackAllocator.hpp:141
RAII wrapper for StackAllocator to manage scoped memory checkpoints.
Definition StackAllocator.hpp:246
StackCheckpoint(StackAllocator &a)
Constructor that saves current state of the given StackAllocator.
Definition StackAllocator.hpp:258
~StackCheckpoint()
Destructor for the StackCheckpoint class.
Definition StackAllocator.hpp:267