CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
allocator_pool.inl
Go to the documentation of this file.
1
9namespace CppCommon {
10
11template <class TAuxMemoryManager>
12inline PoolMemoryManager<TAuxMemoryManager>::PoolMemoryManager(TAuxMemoryManager& auxiliary, size_t page, size_t pages)
13 : _allocated(0),
14 _allocations(0),
15 _auxiliary(auxiliary),
16 _external(false),
17 _max_pages(0),
18 _pages(0),
19 _page(0),
20 _current(nullptr),
21 _free_block(nullptr)
22{
24}
25
26template <class TAuxMemoryManager>
27inline PoolMemoryManager<TAuxMemoryManager>::PoolMemoryManager(TAuxMemoryManager& auxiliary, void* buffer, size_t capacity)
28 : _allocated(0),
29 _allocations(0),
30 _auxiliary(auxiliary),
31 _external(false),
32 _max_pages(0),
33 _pages(0),
34 _page(0),
35 _current(nullptr),
36 _free_block(nullptr)
37{
38 reset(buffer, capacity);
39}
40
41template <class TAuxMemoryManager>
42inline void* PoolMemoryManager<TAuxMemoryManager>::malloc(size_t size, size_t alignment)
43{
44 assert((size > 0) && "Allocated block size must be greater than zero!");
45 assert(Memory::IsValidAlignment(alignment) && "Alignment must be valid!");
46
47 // Allocate huge blocks using the auxiliary memory manager
48 if (size > _page)
49 {
50 void* result = _auxiliary.malloc(size, alignment);
51 if (result != nullptr)
52 {
53 // Update allocation statistics
54 _allocated += size;
55 ++_allocations;
56 }
57 return result;
58 }
59
60 bool allocate = true;
61 FreeBlock* prev_free_block = nullptr;
62 FreeBlock* current_free_block = _free_block;
63
64 while ((current_free_block != nullptr) || allocate)
65 {
66 // Try to allocate a new memory pool page
67 if ((current_free_block == nullptr) && allocate)
68 {
69 if ((_current != nullptr) && (_current->next != nullptr))
70 {
71 // Use the next memory pool page
72 _current = _current->next;
73
74 // Update the current free block
75 current_free_block = (FreeBlock*)_current->buffer;
76 current_free_block->size = _page;
77 current_free_block->next = nullptr;
78
79 // Update the free blocks list
80 if (prev_free_block != nullptr)
81 prev_free_block->next = current_free_block;
82 else
83 _free_block = current_free_block;
84 }
85 else
86 {
87 // Check if there is enough pages to create
88 if (_pages > 0)
89 {
90 // Allocate a new memory pool page
91 Page* current = AllocateMemoryPool(_page, _current);
92 if (current != nullptr)
93 {
94 // Use the new memory pool page
95 _current = current;
96
97 // Update the current free block
98 current_free_block = (FreeBlock*)_current->buffer;
99 current_free_block->size = _page;
100 current_free_block->next = nullptr;
101
102 // Update the free blocks list
103 if (prev_free_block != nullptr)
104 prev_free_block->next = current_free_block;
105 else
106 _free_block = current_free_block;
107
108 // Update created memory pool pages count
109 --_pages;
110 }
111 }
112 }
113
114 // Stop the allocation attempts
115 allocate = false;
116 continue;
117 }
118
119 // Reset the allocation attempts
120 allocate = true;
121
122 // Calculate memory adjustment including the allocation header
123 size_t adjustment = AlignAdjustment(current_free_block, alignment, sizeof(AllocBlock));
124
125 // Calculate aligned block size
126 size_t aligned_size = size + adjustment;
127
128 // Concatenate joint free blocks
129 while (current_free_block->next != nullptr)
130 {
131 FreeBlock* base_free_block = current_free_block;
132 FreeBlock* next_free_block = current_free_block->next;
133
134 // Right joint
135 if (((uint8_t*)base_free_block + base_free_block->size) == (uint8_t*)next_free_block)
136 {
137 current_free_block->size += next_free_block->size;
138 current_free_block->next = next_free_block->next;
139 }
140 else
141 break;
142 }
143
144 // If there is no enough free space in the current free block use the next one
145 if (current_free_block->size < aligned_size)
146 {
147 // Optimization: Skip the last too small peace of memory in page
148 if (current_free_block->next != nullptr)
149 prev_free_block = current_free_block;
150 current_free_block = current_free_block->next;
151 continue;
152 }
153
154 static_assert(sizeof(AllocBlock) >= sizeof(FreeBlock), "Allocated block structure size must not be less than free block structure size!");
155
156 // Allocate from the remaining memory in the current free block
157 if ((current_free_block->size - aligned_size) <= sizeof(AllocBlock))
158 {
159 //Increase allocation size instead of creating a new free block
160 aligned_size = current_free_block->size;
161
162 // Update the free blocks list
163 if (prev_free_block != nullptr)
164 prev_free_block->next = current_free_block->next;
165 else
166 _free_block = current_free_block->next;
167 }
168 else
169 {
170 // Create a new free block containing remaining memory
171 FreeBlock* next_free_block = (FreeBlock*)((uint8_t*)current_free_block + aligned_size);
172 next_free_block->size = current_free_block->size - aligned_size;
173 next_free_block->next = current_free_block->next;
174
175 // Update the free blocks list
176 if (prev_free_block != nullptr)
177 prev_free_block->next = next_free_block;
178 else
179 _free_block = next_free_block;
180 }
181
182 // Calculate the aligned address
183 uint8_t* aligned = (uint8_t*)current_free_block + adjustment;
184
185 // Create a new allocated block
186 AllocBlock* alloc_block = (AllocBlock*)(aligned - sizeof(AllocBlock));
187 alloc_block->size = aligned_size;
188 alloc_block->adjustment = adjustment;
189
190 // Update allocation statistics
191 _allocated += size;
192 ++_allocations;
193
194 return aligned;
195 }
196
197 // Out of memory...
198 return nullptr;
199}
200
201template <class TAuxMemoryManager>
202inline void PoolMemoryManager<TAuxMemoryManager>::free(void* ptr, size_t size)
203{
204 assert((ptr != nullptr) && "Deallocated block must be valid!");
205
206 // Deallocate huge blocks using the auxiliary memory manager
207 if (size > _page)
208 {
209 _auxiliary.free(ptr, size);
210
211 // Update allocation statistics
212 _allocated -= size;
213 --_allocations;
214
215 return;
216 }
217
218 AllocBlock* alloc_block = (AllocBlock*)((uint8_t*)ptr - sizeof(AllocBlock));
219
220 uint8_t* block_start = (uint8_t*)ptr - alloc_block->adjustment;
221 size_t block_size = alloc_block->size;
222 uint8_t* block_end = block_start + block_size;
223
224 // Insert a new free block in the begin of the list
225 FreeBlock* old_free_block = _free_block;
226 FreeBlock* new_free_block = (FreeBlock*)block_start;
227 new_free_block->size = block_size;
228 new_free_block->next = old_free_block;
229 _free_block = new_free_block;
230
231 // Concatenate two joint free blocks
232 if (old_free_block != nullptr)
233 {
234 // Left joint
235 if (((uint8_t*)old_free_block + old_free_block->size) == block_start)
236 {
237 old_free_block->size += new_free_block->size;
238 _free_block = old_free_block;
239 }
240 // Right joint
241 else if ((uint8_t*)old_free_block == block_end)
242 {
243 new_free_block->size += old_free_block->size;
244 new_free_block->next = old_free_block->next;
245 _free_block = new_free_block;
246 }
247 }
248
249 // Update allocation statistics
250 _allocated -= size;
251 --_allocations;
252}
253
254template <class TAuxMemoryManager>
256{
257 assert((_allocated == 0) && "Memory leak detected! Allocated memory size must be zero!");
258 assert((_allocations == 0) && "Memory leak detected! Count of active memory allocations must be zero!");
259
260 if (_current != nullptr)
261 {
262 // Reset the current memory pool page
263 while (_current->prev != nullptr)
264 _current = _current->prev;
265
266 // Reset the free block pointer
267 _free_block = (FreeBlock*)_current->buffer;
268 _free_block->size = _page;
269 _free_block->next = nullptr;
270 }
271}
272
273template <class TAuxMemoryManager>
274inline void PoolMemoryManager<TAuxMemoryManager>::reset(size_t page, size_t pages)
275{
276 assert((page >= (sizeof(Page) + sizeof(AllocBlock))) && "Memory pool page must be big enough to fit at least one allocation block!");
277
278 assert((_allocated == 0) && "Memory leak detected! Allocated memory size must be zero!");
279 assert((_allocations == 0) && "Memory leak detected! Count of active memory allocations must be zero!");
280
281 // Clear previous allocations
282 clear();
283
284 // Initialize the memory pool
285 _external = false;
286 _max_pages = (pages > 0) ? pages : std::numeric_limits<size_t>::max();
287 _pages = _max_pages;
288 _page = page;
289
290 // Allocate a new memory pool page
291 Page* current = AllocateMemoryPool(page, _current);
292 if (current != nullptr)
293 {
294 // Use the new memory pool page
295 _current = current;
296
297 // Initialize the free block pointer
298 _free_block = (FreeBlock*)_current->buffer;
299 _free_block->size = page;
300 _free_block->next = nullptr;
301
302 // Update created memory pool pages count
303 --_pages;
304 }
305}
306
307template <class TAuxMemoryManager>
308inline void PoolMemoryManager<TAuxMemoryManager>::reset(void* buffer, size_t capacity)
309{
310 assert((buffer != nullptr) && "Memory pool buffer must be valid!");
311 assert((capacity >= (sizeof(Page) + sizeof(AllocBlock))) && "Memory pool buffer capacity must be big enough to fit at least one allocation block!");
312
313 assert((_allocated == 0) && "Memory leak detected! Allocated memory size must be zero!");
314 assert((_allocations == 0) && "Memory leak detected! Count of active memory allocations must be zero!");
315
316 // Clear previous allocations
317 clear();
318
319 // Initialize the external memory pool
320 _external = true;
321 _max_pages = 1;
322 _pages = 0;
323 _page = capacity;
324 _current = (Page*)buffer;
325 _current->buffer = (uint8_t*)buffer + sizeof(Page);
326 _current->prev = nullptr;
327 _current->next = nullptr;
328
329 // Initialize the free block pointer
330 _free_block = (FreeBlock*)_current->buffer;
331 _free_block->size = capacity - sizeof(Page);
332 _free_block->next = nullptr;
333}
334
335template <class TAuxMemoryManager>
337{
338 assert((_allocated == 0) && "Memory leak detected! Allocated memory size must be zero!");
339 assert((_allocations == 0) && "Memory leak detected! Count of active memory allocations must be zero!");
340
341 // Clear memory pool
342 ClearMemoryPool();
343
344 // Clear memory pool buffer
345 _external = false;
346 _max_pages = 0;
347 _pages = 0;
348 _page = 0;
349 _current = nullptr;
350
351 // Reset the free block pointer
352 _free_block = nullptr;
353}
354
355template <class TAuxMemoryManager>
356inline size_t PoolMemoryManager<TAuxMemoryManager>::AlignAdjustment(const void* address, size_t alignment)
357{
358 return (uint8_t*)Memory::Align(address, alignment) - (uint8_t*)address;
359}
360
361template <class TAuxMemoryManager>
362inline size_t PoolMemoryManager<TAuxMemoryManager>::AlignAdjustment(const void* address, size_t alignment, size_t header)
363{
364 size_t adjustment = AlignAdjustment(address, alignment);
365
366 size_t required = header;
367 if (adjustment < required)
368 {
369 required -= adjustment;
370 adjustment += alignment * (required / alignment);
371 if ((required % alignment) > 0)
372 adjustment += alignment;
373 }
374
375 return adjustment;
376}
377
378template <class TAuxMemoryManager>
379inline typename PoolMemoryManager<TAuxMemoryManager>::Page* PoolMemoryManager<TAuxMemoryManager>::AllocateMemoryPool(size_t capacity, Page* prev)
380{
381 // Allocate a new memory pool page
382 uint8_t* buffer = (uint8_t*)_auxiliary.malloc(sizeof(Page) + capacity + alignof(std::max_align_t));
383 Page* page = (Page*)buffer;
384 if (page != nullptr)
385 {
386 // Prepare and return a new memory pool page
387 page->buffer = buffer + sizeof(Page);
388 page->prev = prev;
389 page->next = nullptr;
390 if (prev != nullptr)
391 prev->next = page;
392 return page;
393 }
394
395 // Out of memory...
396 return nullptr;
397}
398
399template <class TAuxMemoryManager>
400inline void PoolMemoryManager<TAuxMemoryManager>::ClearMemoryPool()
401{
402 if (!_external)
403 {
404 // Clear all memory pool pages
405 while (_current != nullptr)
406 {
407 Page* prev = _current->prev;
408 Page* next = _current->next;
409 _auxiliary.free(_current, sizeof(Page) + _page + alignof(std::max_align_t));
410 if (prev != nullptr)
411 {
412 prev->next = next;
413 _current = prev;
414 }
415 else if (next != nullptr)
416 {
417 next->prev = prev;
418 _current = next;
419 }
420 else
421 _current = nullptr;
422 }
423
424 // Reset the free block pointer
425 _free_block = nullptr;
426 }
427}
428
429} // namespace CppCommon
static bool IsValidAlignment(size_t alignment) noexcept
Is the given alignment valid?
Definition memory.inl:13
static T * Align(const T *address, size_t alignment=alignof(T), bool upwards=true) noexcept
Align pointer (upwards or downwards)
Definition memory.inl:29
Memory pool manager class.
size_t pages() const noexcept
Max pages size.
void reset()
Reset the memory manager.
PoolMemoryManager(TAuxMemoryManager &auxiliary)
Initialize memory pool manager with an auxiliary memory manager.
void * malloc(size_t size, size_t alignment=alignof(std::max_align_t))
Allocate a new memory block of the given size.
void clear()
Clear memory pool.
size_t page() const noexcept
Page size in bytes.
void free(void *ptr, size_t size)
Free the previously allocated memory block.
C++ Common project definitions.