1 /************************************************************************************************************************
2 It's internal code.
3 
4 Module contain memory allocator.
5 
6 Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
7 License: BSD 3-clause, see LICENSE file in project root folder.
8 */
9 module bubel.ecs.block_allocator;
10 
11 import bubel.ecs.manager;
12 import bubel.ecs.std;
13 
14 /************************************************************************************************************************
15 Allocator allocate large blocks and return smaller blocks. When there is no more blocks then next large block is allocated.
16 By default freeing memory only returns it to allocator. To free large memory chunks freeMemory function is used.
17 freeMemory function return to system memory even if chunk blocks wasn't freed.
18 */
19 struct BlockAllocator
20 {
21     /************************************************************************************************************************
22     Get new block. Allocator automatically allocate next memory chunk if needed.
23     */
24     void* getBlock() nothrow @nogc
25     {
26         if (next_block is null)
27             allocBlock();
28         void* ret = next_block;
29         next_block = *cast(void**) next_block;
30         return ret;
31     }
32 
33     /************************************************************************************************************************
34     Return block to allocator for further use.
35     */
36     void freeBlock(void* block) nothrow @nogc
37     {
38         *cast(void**) block = next_block;
39         next_block = block;
40     }
41 
42     /************************************************************************************************************************
43     Free whole used memory. This function return to system all memory chunks even if not every black was freed.
44     */
45     void freeMemory() nothrow @nogc
46     {
47         while (pointers)
48         {
49             foreach (i; 0 .. pointers.numberof)
50             {
51                 Mallocator.alignDispose(pointers.blocks[i]);
52             }
53             BlockPointers* next_pointers = pointers.next_pointers;
54             Mallocator.dispose(pointers);
55             pointers = next_pointers;
56         }
57         next_block = null;
58     }
59 
60 private:
61 
62     void allocBlock() nothrow @nogc
63     {
64         next_block = cast(void*) Mallocator.alignAlloc(block_size * blocks_in_allocation,
65                 block_size);
66         if (next_block is null)
67             assert(0);
68 
69         if (pointers is null)
70             pointers = Mallocator.make!BlockPointers;
71         if (pointers.numberof >= 32)
72         {
73             BlockPointers* new_pointers = Mallocator.make!BlockPointers;
74             new_pointers.next_pointers = pointers;
75             pointers = new_pointers;
76         }
77         pointers.blocks[pointers.numberof++] = next_block;
78 
79         foreach (i; 0 .. blocks_in_allocation - 1)
80         {
81             void** pointer = cast(void**)(next_block + i * block_size);
82             *pointer = next_block + (i + 1) * block_size;
83         }
84         void** pointer = cast(void**)(next_block + (blocks_in_allocation - 1) * block_size);
85         *pointer = null;
86     }
87 
88     struct BlockPointers
89     {
90         void*[32] blocks;
91         uint numberof = 0;
92         BlockPointers* next_pointers = null;
93     }
94 
95     uint block_size;
96     uint blocks_in_allocation;
97 
98     void* next_block = null;
99     BlockPointers* pointers = null;
100 }