OpenMoHAA 0.82.0
Loading...
Searching...
No Matches
mem_blockalloc.h
1/*
2===========================================================================
3Copyright (C) 2015 the OpenMoHAA team
4
5This file is part of OpenMoHAA source code.
6
7OpenMoHAA source code is free software; you can redistribute it
8and/or modify it under the terms of the GNU General Public License as
9published by the Free Software Foundation; either version 2 of the License,
10or (at your option) any later version.
11
12OpenMoHAA source code is distributed in the hope that it will be
13useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with OpenMoHAA source code; if not, write to the Free Software
19Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20===========================================================================
21*/
22
23// mem_blockalloc.h: Fast block memory manager
24
25#pragma once
26
27#include "Linklist.h"
28#include "q_shared.h"
29
30#include <cstddef>
31#include <type_traits>
32#include <new>
33
34void *MEM_Alloc(int size);
35void MEM_Free(void *ptr);
36
37#ifdef _DEBUG_MEM
38# define _DEBUG_MEMBLOCK 1
39#endif
40
41static constexpr size_t DefaultBlock = 256;
42
43enum class alloc_source_e {
44 SourceBlock = 174,
45 SourceMalloc
46};
47
48template<typename aclass, size_t blocksize>
50
51template<size_t bits>
53
54template<>
56 using type = uint8_t;
57};
58
59template<>
61 using type = uint16_t;
62};
63
64template<>
66 using type = uint32_t;
67};
68
69template<>
71 using type = uint64_t;
72};
73
74template<typename aclass, size_t blocksize>
75class block_s
76{
77private:
78 static constexpr size_t bitsNeeded = blocksize <= 0x80 ? 8
79 : blocksize <= 0x8000 ? 16
80 : blocksize <= 0x80000000 ? 32
81 : 64;
82
83public:
84 block_s();
85
86#if !_DEBUG_MEMBLOCK
87 bool usedDataAvailable() const;
88 bool freeDataAvailable() const;
89#endif
90
91public:
92 using offset_t = typename block_selectType_t<bitsNeeded>::type;
93
94 struct info_t {
95 offset_t index;
96 alloc_source_e source;
97 static constexpr uint16_t typeSize = sizeof(aclass);
98 alignas(alignof(aclass)) char data[sizeof(aclass)];
99 };
100
101public:
102#if !_DEBUG_MEMBLOCK
103 info_t data[blocksize];
104 offset_t prev_data[blocksize];
105 offset_t next_data[blocksize];
106
107 offset_t free_data;
108 offset_t used_data;
109 bool has_free_data : 1;
110 bool has_used_data : 1;
111#else
112 offset_t data[sizeof(aclass)];
113#endif
114
115 block_s<aclass, blocksize> *prev_block;
116 block_s<aclass, blocksize> *next_block;
117
118public:
119 static constexpr size_t headersize = offsetof(info_t, data);
120 static constexpr size_t dataoffset = 0;
121 static constexpr size_t datasize = sizeof(info_t);
122};
123
124template<typename aclass, size_t blocksize>
125block_s<aclass, blocksize>::block_s()
126#if !_DEBUG_MEMBLOCK
127{
128 info_t *header;
129 offset_t curr;
130 for (curr = 0; curr < blocksize - 1; ++curr) {
131 offset_t next = curr + 1;
132 header = &data[curr];
133 header->source = alloc_source_e::SourceBlock;
134 header->index = curr;
135 prev_data[next] = curr;
136 next_data[curr] = next;
137 }
138
139 header = &data[curr];
140 header->source = alloc_source_e::SourceBlock;
141 header->index = blocksize - 1;
142 prev_data[0] = blocksize - 1;
143 next_data[blocksize - 1] = 0;
144 free_data = 0;
145 prev_block = next_block = nullptr;
146
147 has_free_data = true;
148 has_used_data = false;
149}
150#else
151 : prev_block(nullptr)
152 , next_block(nullptr)
153{}
154#endif
155
156#if !_DEBUG_MEMBLOCK
157template<typename aclass, size_t blocksize>
158bool block_s<aclass, blocksize>::usedDataAvailable() const
159{
160 return has_used_data;
161}
162
163template<typename aclass, size_t blocksize>
164bool block_s<aclass, blocksize>::freeDataAvailable() const
165{
166 return has_free_data;
167}
168#endif
169
170template<typename aclass, size_t blocksize = DefaultBlock>
171class MEM_BlockAlloc
172{
173 static_assert(blocksize >= 2, "Minimum 2x class preallocation required!!");
174
175public:
176 MEM_BlockAlloc();
177 ~MEM_BlockAlloc();
178
179 void *Alloc();
180 void Free(void *ptr) noexcept;
181 void FreeAll() noexcept;
182 size_t Count();
183 size_t BlockCount();
184 size_t BlockMemory();
185
186private:
187 friend class MEM_BlockAlloc_enum<aclass, blocksize>;
188 using block_t = block_s<aclass, blocksize>;
189 using block_offset_t = typename block_t::offset_t;
190
191#if !_DEBUG_MEMBLOCK
192 block_t *m_FreeBlock;
193 block_t *m_StartUsedBlock;
194 block_t *m_StartFullBlock;
195#else
196 block_t *m_Block;
197#endif
198 size_t m_BlockCount;
199
200private:
201 void *TakeFree(block_t *block, uintptr_t free_data);
202 size_t Count(const block_t *block);
203};
204
205template<typename aclass, size_t blocksize = DefaultBlock>
206class MEM_BlockAlloc_enum
207{
208public:
209 MEM_BlockAlloc_enum(MEM_BlockAlloc<aclass, blocksize>& owner);
210
211 aclass *NextElement();
212 aclass *CurrentElement();
213
214 enum blockType_e {
215 used,
216 full
217 };
218
219private:
220 using block_t = block_s<aclass, blocksize>;
221 using offset_t = typename block_t::offset_t;
222
224 block_t *m_CurrentBlock;
225
226#if !_DEBUG_MEMBLOCK
227 offset_t m_CurrentData;
228 blockType_e m_CurrentBlockType;
229#endif
230};
231
232template<typename a, size_t b>
233MEM_BlockAlloc<a, b>::MEM_BlockAlloc()
234#if !_DEBUG_MEMBLOCK
235 : m_StartUsedBlock()
236 , m_StartFullBlock()
237{
238 m_FreeBlock = nullptr;
239 m_BlockCount = 0;
240}
241#else
242 : m_Block()
243{
244 m_BlockCount = 0;
245}
246#endif
247
248template<typename a, size_t b>
249MEM_BlockAlloc<a, b>::~MEM_BlockAlloc()
250{
251 // due to the randomized order of initialization and destruction
252 // MEM_BlockAlloc shouldn't automatically free memory
253 // because con_set and other stuff could handle destruction
254 // after memory was freed
255 //FreeAll();
256}
257
258template<typename a, size_t b>
259void *MEM_BlockAlloc<a, b>::Alloc()
260{
261#if _DEBUG_MEMBLOCK
262 block_t *block = new (MEM_Alloc(sizeof(block_t))) block_t();
263
264 LL_SafeAddFirst(m_Block, block, next_block, prev_block);
265
266 m_BlockCount++;
267 return (void *)block->data;
268#else
269 block_t *used_block;
270 block_offset_t free_data;
271 block_offset_t next_data;
272
273 if (m_StartUsedBlock) {
274 used_block = m_StartUsedBlock;
275
276 free_data = used_block->free_data;
277 next_data = used_block->next_data[free_data];
278
279 if (next_data == free_data) {
280 // Move the block to the next block list as there is no space
281 // available
282 m_StartUsedBlock = used_block->next_block;
283
284 LL_SafeRemoveRoot(m_StartUsedBlock, used_block, next_block, prev_block);
285 LL_SafeAddFirst(m_StartFullBlock, used_block, next_block, prev_block);
286
287 used_block->has_free_data = false;
288 return TakeFree(used_block, free_data);
289 }
290 } else {
291 if (m_FreeBlock) {
292 // start from the free block
293 used_block = m_FreeBlock;
294 m_FreeBlock = nullptr;
295 free_data = used_block->free_data;
296 next_data = used_block->next_data[free_data];
297 } else {
298 m_BlockCount++;
299 // allocate and construct a new block
300 used_block = new (MEM_Alloc(sizeof(block_t))) block_t();
301
302 free_data = 0;
303 next_data = 1;
304 }
305
306 LL_SafeAddFirst(m_StartUsedBlock, used_block, next_block, prev_block);
307 }
308
309 const block_offset_t prev_data = used_block->prev_data[free_data];
310
311 used_block->next_data[prev_data] = next_data;
312 used_block->prev_data[next_data] = prev_data;
313 used_block->free_data = next_data;
314 used_block->has_free_data = true;
315
316 if (!used_block->usedDataAvailable()) {
317 used_block->used_data = free_data;
318 used_block->has_used_data = true;
319 used_block->next_data[free_data] = free_data;
320 used_block->prev_data[free_data] = free_data;
321 return used_block->data[free_data].data;
322 }
323
324 return TakeFree(used_block, free_data);
325#endif
326}
327
328template<typename aclass, size_t blocksize>
329void *MEM_BlockAlloc<aclass, blocksize>::TakeFree(block_t *block, uintptr_t free_data)
330{
331 const block_offset_t used_data = block->used_data;
332 const block_offset_t prev_data = block->prev_data[used_data];
333
334 block->next_data[prev_data] = (block_offset_t)free_data;
335 block->prev_data[used_data] = (block_offset_t)free_data;
336 block->next_data[free_data] = used_data;
337 block->prev_data[free_data] = prev_data;
338 return block->data[free_data].data;
339}
340
341template<typename a, size_t b>
342void MEM_BlockAlloc<a, b>::Free(void *ptr) noexcept
343{
344#if _DEBUG_MEMBLOCK
345 block_t *block = (block_t*)ptr - offsetof(block_t, data);
346
347 LL_SafeRemoveRoot(m_Block, block, next_block, prev_block);
348
349 m_BlockCount--;
350 MEM_Free(block);
351#else
352 // get the header of the pointer
353 typename block_t::info_t *header =
354 reinterpret_cast<typename block_t::info_t *>(static_cast<unsigned char *>(ptr) - block_t::headersize);
355 const block_offset_t used_data = header->index;
356 // get the block from the header
357 block_t *const block = (block_t *)((uint8_t *)header - used_data * block_t::datasize - block_t::dataoffset);
358 const block_offset_t next_data = block->next_data[used_data];
359 if (next_data == used_data) {
360 LL_SafeRemoveRoot(m_StartUsedBlock, block, next_block, prev_block);
361
362 if (m_FreeBlock) {
363 // deallocate the free block because of another deallocation
364 --m_BlockCount;
365 MEM_Free(m_FreeBlock);
366 m_FreeBlock = nullptr;
367 }
368
369 m_FreeBlock = block;
370 block->has_used_data = false;
371
372 const block_offset_t free_data = block->free_data;
373 const block_offset_t prev_data = block->prev_data[free_data];
374
375 block->next_data[prev_data] = used_data;
376 block->prev_data[free_data] = used_data;
377 block->next_data[used_data] = free_data;
378 block->prev_data[used_data] = prev_data;
379 } else {
380 const block_offset_t prev_data = block->prev_data[used_data];
381
382 block->next_data[prev_data] = next_data;
383 block->prev_data[next_data] = prev_data;
384 block->used_data = next_data;
385 block->has_used_data = true;
386
387 if (block->freeDataAvailable()) {
388 const block_offset_t free_data = block->free_data;
389 const block_offset_t prev_data = block->prev_data[free_data];
390
391 block->next_data[prev_data] = used_data;
392 block->prev_data[free_data] = used_data;
393 block->next_data[used_data] = free_data;
394 block->prev_data[used_data] = prev_data;
395 return;
396 }
397
398 if (m_StartFullBlock == block) {
399 m_StartFullBlock = block->next_block;
400 }
401
402 LL_SafeRemoveRoot(m_StartFullBlock, block, next_block, prev_block);
403 LL_SafeAddFirst(m_StartUsedBlock, block, next_block, prev_block);
404
405 block->free_data = used_data;
406 block->has_free_data = true;
407 block->prev_data[used_data] = used_data;
408 block->next_data[used_data] = used_data;
409 }
410#endif
411}
412
413template<typename a, size_t b>
414void MEM_BlockAlloc<a, b>::FreeAll() noexcept
415{
416 block_t *block;
417#if _DEBUG_MEMBLOCK
418 block_t *next = m_Block;
419 for (block = next; block; block = next) {
420 next = block->next_block;
421 m_BlockCount--;
422 a *ptr = (a *)block->data;
423 ptr->~a();
424 MEM_Free(block);
425 }
426
427 m_Block = NULL;
428#else
429 while ((block = m_StartFullBlock) != nullptr) {
430 if (block->usedDataAvailable()) {
431 a *ptr = (a *)block->data[block->used_data].data;
432 ptr->~a();
433 Free(ptr);
434 block = m_StartFullBlock;
435 }
436 }
437
438 while ((block = m_StartUsedBlock) != nullptr) {
439 if (block->usedDataAvailable()) {
440 a *ptr = (a *)block->data[block->used_data].data;
441 ptr->~a();
442 Free(ptr);
443 }
444 }
445
446 if (m_FreeBlock) {
447 m_BlockCount--;
448 MEM_Free(m_FreeBlock);
449 m_FreeBlock = nullptr;
450 }
451#endif
452}
453
454template<typename a, size_t b>
455size_t MEM_BlockAlloc<a, b>::Count(const block_t *list)
456{
457 int count = 0;
458#if _DEBUG_MEMBLOCK
459 for (const block_t *block = list; block; block = block->next_block) {
460 count++;
461 }
462 return count;
463#else
464
465 for (const block_t *block = list; block; block = block->next_block) {
466 if (!block->usedDataAvailable()) {
467 continue;
468 }
469
470 const block_offset_t used_data = block->used_data;
471 block_offset_t current_used_data = used_data;
472
473 do {
474 count++;
475 current_used_data = block->next_data[current_used_data];
476 } while (current_used_data != used_data);
477 }
478
479 return count;
480#endif
481}
482
483template<typename a, size_t b>
484size_t MEM_BlockAlloc<a, b>::Count()
485{
486#if _DEBUG_MEMBLOCK
487 return Count(m_Block);
488#else
489 return Count(m_StartFullBlock) + Count(m_StartUsedBlock);
490#endif
491}
492
493template<typename a, size_t b>
494size_t MEM_BlockAlloc<a, b>::BlockCount()
495{
496 return m_BlockCount;
497}
498
499template<typename a, size_t b>
500size_t MEM_BlockAlloc<a, b>::BlockMemory()
501{
502 return sizeof(block_s<a, b>);
503}
504
505template<typename a, size_t b>
506MEM_BlockAlloc_enum<a, b>::MEM_BlockAlloc_enum(MEM_BlockAlloc<a, b>& owner)
507{
508 m_Owner = &owner;
509 m_CurrentBlock = nullptr;
510#if !_DEBUG_MEMBLOCK
511 m_CurrentBlockType = MEM_BlockAlloc_enum::used;
512#endif
513}
514
515template<typename a, size_t b>
516a *MEM_BlockAlloc_enum<a, b>::NextElement()
517{
518#if _DEBUG_MEMBLOCK
519 if (!m_CurrentBlock) {
520 m_CurrentBlock = m_Owner->m_Block;
521 } else {
522 m_CurrentBlock = m_CurrentBlock->next_block;
523 }
524 return (a *)m_CurrentBlock;
525#else
526 // search for a valid block type
527 while (!m_CurrentBlock) {
528 switch (m_CurrentBlockType) {
529 case blockType_e::used:
530 m_CurrentBlock = m_Owner->m_StartUsedBlock;
531 break;
532 case blockType_e::full:
533 m_CurrentBlock = m_Owner->m_StartFullBlock;
534 break;
535 default:
536 return nullptr;
537 }
538
539 reinterpret_cast<uint8_t&>(m_CurrentBlockType)++;
540
541 _label:
542 for (; m_CurrentBlock; m_CurrentBlock = m_CurrentBlock->next_block) {
543 if (m_CurrentBlock->usedDataAvailable()) {
544 m_CurrentData = m_CurrentBlock->used_data;
545 return reinterpret_cast<a *>(m_CurrentBlock->data[m_CurrentData].data);
546 }
547 }
548 }
549
550 m_CurrentData = m_CurrentBlock->next_data[m_CurrentData];
551
552 if (m_CurrentData == m_CurrentBlock->used_data) {
553 // found an object
554 m_CurrentBlock = m_CurrentBlock->next_block;
555 goto _label;
556 }
557
558 return reinterpret_cast<a *>(m_CurrentBlock->data[m_CurrentData].data);
559#endif
560}
561
562template<typename a, size_t b>
563a *MEM_BlockAlloc_enum<a, b>::CurrentElement()
564{
565 return m_CurrentBlock;
566}
567
568template<typename a, size_t b>
569void *operator new(size_t, MEM_BlockAlloc<a, b>& allocator)
570{
571 return allocator.Alloc();
572}
573
574template<typename a, size_t b>
575void operator delete(void *ptr, MEM_BlockAlloc<a, b>& allocator) noexcept
576{
577 return allocator.Free(ptr);
578}
Definition mem_blockalloc.h:207
Definition mem_blockalloc.h:172
Definition mem_blockalloc.h:76
Definition mem_blockalloc.h:94
Definition mem_blockalloc.h:52