Max OS 0.3
Loading...
Searching...
No Matches
bump.cpp
1//
2// Created by 98max on 8/07/2026.
3//
4
5#include <memory/allocator/bump.h>
6
7using namespace MaxOS;
8using namespace MaxOS::memory;
9using namespace MaxOS::memory::allocator;
10
15 static_assert(CHUNK_HEADER_SIZE == sizeof(chunk_header_t));
16};
17
22
30void* BumpAllocator::allocate_extra_space(size_t requested_size, size_t& actual_size) {
31 return nullptr;
32}
33
40 return canary == EXPECTED_CANARY;
41}
42
51
52 // Make sure the chunk is not poiting to garbage
53 if (!is_valid())
54 ASSERT(false, "Invalid memory chunk detected (possible buffer overflow)\n");
55
56 return prev;
57}
58
59
68
69 // Make sure the chunk is not poiting to garbage
70 if (!is_valid())
71 ASSERT(false, "Invalid memory chunk detected (possible buffer overflow)\n");
72
73 return next;
74}
75
82void* BumpAllocator::expand_heap(size_t size) {
83
84 // Create a new chunk of memory
85 size_t actual_size;
86 auto* chunk = (chunk_header_t*)allocate_extra_space(CHUNK_HEADER_SIZE + size, actual_size);
87 if (chunk == nullptr)
88 return nullptr;
89
90 // Set the chunk's properties
91 chunk->allocated = false;
92 chunk->size = actual_size - CHUNK_HEADER_SIZE;
93 chunk->next = nullptr;
94 chunk->canary = EXPECTED_CANARY;
95
96 // Insert the chunk into the linked list
97 m_last_memory_chunk->next = chunk;
98 chunk->prev = m_last_memory_chunk;
99 m_last_memory_chunk = chunk;
100
101 // If it is possible to merge the new chunk with the previous chunk then do so (note: this happens if the
102 // previous chunk is free but cant contain the size required)
103 chunk_header_t* prev_chunk = chunk->prev_chunk();
104 bool adjacent = (uintptr_t)prev_chunk + CHUNK_HEADER_SIZE + prev_chunk->size == (uintptr_t)chunk;
105 if (!prev_chunk->allocated && adjacent) {
106
107 // The new chunk gets merged into the previous one (its header stops existing), so hand back the merged chunk
108 chunk_header_t* merged = chunk->prev_chunk();
109 unallocate((void*)((size_t)chunk + CHUNK_HEADER_SIZE));
110 return merged;
111 }
112
113 return chunk;
114}
115
122void BumpAllocator::setup_region(uintptr_t address, size_t length) {
123
124 // Create a free chunk that covers the entire region
125 this->m_first_memory_chunk = (chunk_header_t*)address;
126 m_first_memory_chunk->allocated = false;
127 m_first_memory_chunk->prev = nullptr;
128 m_first_memory_chunk->next = nullptr;
129 m_first_memory_chunk->size = length - CHUNK_HEADER_SIZE;
130 m_first_memory_chunk->canary = EXPECTED_CANARY;
131
132 m_last_memory_chunk = m_first_memory_chunk;
133 m_setup = true;
134
135}
136
137
144void* BumpAllocator::allocate(size_t size) {
145
146 chunk_header_t* result = nullptr;
147
148 // Nothing to allocate
149 if (size == 0)
150 return nullptr;
151
152 size = align(size);
153
154 // Find the next free chunk that is big enough
155 for (chunk_header_t* chunk = m_first_memory_chunk; chunk != nullptr && result == nullptr; chunk = chunk->next_chunk()) {
156
157 ASSERT(chunk->is_valid(),"Invalid allocator chunk canary\n");
158 ASSERT(chunk->allocated <= 1, "Invalid allocator allocation state\n");
159
160 if (chunk->size >= size && chunk->allocated == 0)
161 result = chunk;
162 }
163
164 // If there is no free chunk then make more room
165 if (result == nullptr)
166 result = (chunk_header_t*)expand_heap(size);
167
168 // No space to expand heap
169 if (result == nullptr)
170 return nullptr;
171
172 // If there is not left over space to store extra chunks there is no need to split the chunk
173 if (result->size < size + CHUNK_HEADER_SIZE + CHUNK_ALIGNMENT) {
174 result->allocated = true;
175 void* p = (void*)(((size_t)result) + CHUNK_HEADER_SIZE);
176 return p;
177 }
178
179 // Split the chunk into: what was requested + free overflow space for future allocates
180 // - This prevents waste in the event that a big free chunk was found but the requested size would only use a portion of that
181 auto* extra = (chunk_header_t*)((size_t)result + CHUNK_HEADER_SIZE + size);
182 extra->allocated = false;
183 extra->size = result->size - size - CHUNK_HEADER_SIZE;
184 extra->prev = result;
185 extra->canary = EXPECTED_CANARY;
186
187 // Add to the linked list
188 extra->next = result->next;
189 if (extra->next != nullptr)
190 extra->next->prev = extra;
191
192 // Requested chunk is now allocated exactly to the size requested and points to the free (split) block of memory that
193 // it did not use
194 result->size = size;
195 result->allocated = true;
196 result->next = extra;
197 result->canary = EXPECTED_CANARY;
198
199 // Update the last memory chunk if necessary
200 if (result == m_last_memory_chunk)
201 m_last_memory_chunk = extra;
202
203 return (void*)(((size_t)result) + CHUNK_HEADER_SIZE);
204}
205
206
212void BumpAllocator::unallocate(void* pointer) {
213
214 // Cant free unallocated memory
215 if (pointer == nullptr)
216 return;
217
218 // Check bounds
219 auto heap_start = (uintptr_t)m_first_memory_chunk;
220 auto heap_end = (uintptr_t)m_last_memory_chunk + CHUNK_HEADER_SIZE + m_last_memory_chunk->size;
221 if ((uintptr_t)pointer < heap_start || (uintptr_t)pointer > heap_end)
222 return;
223
224 // Get the chunk information from the pointer
225 auto* chunk = (chunk_header_t*)((size_t)pointer - CHUNK_HEADER_SIZE);
226 chunk->allocated = false;
227
228 // If there is a free chunk before this chunk then merge them
229 if (chunk->prev_chunk() != nullptr && !chunk->prev_chunk()->allocated && (uintptr_t)chunk->prev_chunk() + CHUNK_HEADER_SIZE + chunk->prev_chunk()->size == (uintptr_t)chunk) {
230
231 // If this chunk is the last one then the one it merges into is now the last one
232 if (chunk == m_last_memory_chunk)
233 m_last_memory_chunk = chunk->prev_chunk();
234
235 // Grow the chunk behind this one so that it now contains the freed one
236 chunk->prev_chunk()->size += chunk->size + CHUNK_HEADER_SIZE;
237 chunk->prev_chunk()->next = chunk->next_chunk();
238
239 // The chunk in front of the freed one now needs to point to the merged chunk
240 if (chunk->next_chunk() != nullptr)
241 chunk->next_chunk()->prev = chunk->prev_chunk();
242
243 // Freed chunk doesn't exist anymore so now working with the merged chunk
244 chunk = chunk->prev_chunk();
245
246 }
247
248 // If there is a free chunk after this chunk then merge them
249 if (chunk->next_chunk() != nullptr && !chunk->next_chunk()->allocated && (uintptr_t)chunk + CHUNK_HEADER_SIZE + chunk->size == (uintptr_t)chunk->next_chunk()) {
250
251 // Merging with the last mem chunk means this chunk is now the last mem chunk
252 if (chunk->next_chunk() == m_last_memory_chunk)
253 m_last_memory_chunk = chunk;
254
255 // Grow this chunk so that it now contains the free chunk in front of the old (now freed) one
256 chunk->size += chunk->next_chunk()->size + CHUNK_HEADER_SIZE;
257
258 // Now that this chunk contains the next one, it has to point to the one in front of what has just been merged
259 // and that has to point to this
260 chunk->next = chunk->next_chunk()->next_chunk();
261 if (chunk->next_chunk() != nullptr)
262 chunk->next_chunk()->prev = chunk;
263
264 }
265}
266
273
274 size_t result = 0;
275
276 // Loop through all the chunks and add up the size of the allocated chunks
277 for (chunk_header_t* chunk = m_first_memory_chunk; chunk != nullptr; chunk = chunk->next_chunk())
278 if (chunk->allocated)
279 result += chunk->size;
280
281 return result;
282}
283
290size_t BumpAllocator::align(size_t size) {
291
292 return (size + CHUNK_ALIGNMENT - 1) / CHUNK_ALIGNMENT * CHUNK_ALIGNMENT;
293}
void unallocate(void *pointer) final
Frees a block of memory.
Definition bump.cpp:212
virtual ~BumpAllocator()
Destructs a Bump memory allocator.
size_t amount_used() final
Returns the amount of memory used.
Definition bump.cpp:272
size_t align(size_t size) final
Aligns the size to the chunk alignment.
Definition bump.cpp:290
void * allocate(size_t size) final
Allocates a block of memory.
Definition bump.cpp:144
BumpAllocator()
Creates a new bump memory allocator.
Definition bump.cpp:14
void setup_region(uintptr_t address, size_t length) final
Setup a region to be bump memory managed, creates a free chunk represent the whole region.
Definition bump.cpp:122
A span of memory in the heap, can be allocated or free. Used as a node in a doubly linked list.
Definition bump.h:25
ChunkHeader * next
Pointer to the chunk after this one in the list.
Definition bump.h:29
uint8_t allocated
Whether this chunk is in use or can be allocated.
Definition bump.h:32
ChunkHeader * prev
Pointer to the chunk before this one in the list.
Definition bump.h:30
size_t size
The size of this span of memory (not including the MemoryChunk struct itself)
Definition bump.h:33
uint16_t canary
Trip value to detect buffer overflows.
Definition bump.h:27