1 module mage.util.mem;
2 import std.algorithm : max;
3 
4 auto KiB(size_t i) { return i * 1024; }
5 auto MiB(size_t i) { return KiB(i) * 1024; }
6 auto GiB(size_t i) { return MiB(i) * 1024; }
7 auto TiB(size_t i) { return GiB(i) * 1024; }
8 
9 struct Mallocator
10 {
11   @disable this(this);
12 }
13 
14 static shared Mallocator mallocator;
15 
16 @trusted void[] allocate(shared ref Mallocator a, size_t size)
17 {
18   import core.stdc.stdlib : malloc;
19   if(!size) {
20     return null;
21   }
22   auto ptr = malloc(size);
23   return ptr ? ptr[0..size] : null;
24 }
25 
26 @system void deallocate(shared ref Mallocator a, void[] mem)
27 {
28   import core.stdc.stdlib : free;
29   free(mem.ptr);
30 }
31 
32 unittest {
33   auto p = mallocator.allocate(32);
34   scope(exit) mallocator.deallocate(p);
35   assert(p !is null);
36   assert(mallocator.allocate(0) is null);
37 }
38 
39 auto allocate(T, A, Args...)(ref A allocator, Args args)
40 {
41   import std.conv : emplace;
42   return emplace!(T, Args)(allocator.allocate(T.sizeof), args);
43 }
44 
45 void deallocate(A, T)(ref A allocator, T instance)
46 {
47   allocator.deallocate(cast(void[])(&instance)[0..T.sizeof]);
48 }
49 
50 unittest {
51   struct X { int a = 42; int b = 1337; }
52   auto px = mallocator.allocate!X;
53   scope(exit) mallocator.deallocate(px);
54   assert(px.a == 42);
55   assert(px.b == 1337);
56 }
57 
58 struct Block(size_t N = 4.KiB)
59 {
60   void[N] mem;
61   size_t ap = 0; // Allocation pointer.
62 }
63 
64 void[] allocate(size_t N)(ref Block!N b, size_t size)
65 {
66   if(!size || b.ap + size > N) {
67     return null;
68   }
69   auto mem = b.mem[b.ap .. b.ap + size];
70   b.ap += size;
71   return mem;
72 }
73 
74 void deallocate(size_t N)(ref Block!N b)
75 {
76   b.ap = 0;
77 }
78 
79 unittest {
80   auto block = Block!(16)();
81   auto m1 = block.allocate(8);
82   assert(m1 !is null);
83   auto m2 = block.allocate(4);
84   assert(m1 !is null);
85   auto m3 = block.allocate(8);
86   assert(m1 !is null);
87 }
88 
89 unittest {
90   auto block = Block!(16)();
91   struct S { int a = 42; int b = 1337; }
92   import mage;
93   auto ps = block.allocate!S();
94   assert(ps.a == 42);
95   assert(ps.b == 1337);
96   ps = block.allocate!S(1, 2);
97   assert(ps.a == 1);
98   assert(ps.b == 2);
99 }
100 
101 /// Uses a dynamic array of fixed sized blocks to allocate memory.
102 struct BlockArray(size_t N)
103 {
104   Block!N[] blocks;
105 }
106 
107 void[] allocate(size_t N)(ref BlockArray!N dyn, size_t size)
108 {
109   import std.range;
110   
111   if(dyn.blocks.empty) {
112     ++dyn.blocks.length;
113   }
114   auto ptr = dyn.blocks[$-1].allocate(size);
115   if(ptr is null)
116   {
117     ++dyn.blocks.length;
118     ptr = dyn.blocks[$-1].allocate(size);
119   }
120   return ptr;
121 }