1 /// Very fast and very not thread-safe fixed data segment allocator.
2 module tern.experimental.ds_allocator;
3 
4 import tern.traits;
5 import tern.memory;
6 import tern.meta : random;
7 import std.range : iota;
8 import std.conv;
9 
10 private pure string generateCases(size_t r)()
11 {
12     string str = "switch (size) {";
13     static foreach (i; iota(0, 20_000, size_t.sizeof))
14     {
15         str ~= "case "~i.to!string~":
16         static ubyte["~i.to!string~"] data"~r.to!string~";
17      *cast(size_t*)&data"~r.to!string~" = size;
18         T arr;
19         (cast(size_t*)&arr)[0] = length;
20         (cast(void**)&arr)[1] = cast(void*)&data"~r.to!string~" + size_t.sizeof;
21         return arr;";
22     }
23     str ~= "default: assert(0); }";
24     return str;
25 }
26 
27 public:
28 static:
29 @nogc:
30 /**
31  * Creates an array of type `T` on the ds with a specified initial length.
32  * 
33  * Params:
34  *  T = The element type of the array.
35  *  length = The initial length of the array.
36  *
37  * Returns:
38  *  A new array of type `T`.
39  */
40 T dsNew(T, uint R0 = __LINE__, string R1 = __TIMESTAMP__, string R2 = __FILE_FULL_PATH__, string R3 = __FUNCTION__)(size_t length)
41     if (isDynamicArray!T)
42 {
43     enum elem = cast(size_t)(ElementType!T.sizeof * 1.5) + (cast(size_t)(ElementType!T.sizeof * 1.5) == 8 ? 0 : (size_t.sizeof - (cast(size_t)(ElementType!T.sizeof * 1.5) % size_t.sizeof)));
44     const size_t size = elem * length + size_t.sizeof;
45     mixin(generateCases!(random!(size_t, 0, size_t.max, uint.max, R0, R1, R2, R3)));
46 }
47 
48 /**
49  * Resizes a previously allocated array in the data segment to the specified length.
50  * 
51  * Params:
52  *  T = The array type.
53  *  arr = The array to be resized.
54  *  length = The new length of the array.
55  *
56  * Remarks:
57  *  No validation is done that `arr` was previously allocated with `dsNew`, stay vigilant.
58  */
59 void dsResize(T : U[], U)(ref T arr, size_t length)
60 {
61     const size_t size = U.sizeof * length;
62     const size_t curSize = *cast(size_t*)(cast(void*)arr.ptr - size_t.sizeof);
63     if (curSize >= size)
64     {
65         enum elem = cast(size_t)(U.sizeof * 1.5) + (cast(size_t)(U.sizeof * 1.5) == size_t.sizeof ? 0 : (size_t.sizeof - (cast(size_t)(U.sizeof * 1.5) % size_t.sizeof)));
66         (cast(size_t*)&arr)[0] = length;
67         (cast(size_t*)arr.ptr)[-1] = elem * length + size_t.sizeof;
68     }
69     else
70     {
71         T tarr = dsNew!T(length);
72         memcpy(cast(void*)arr.ptr, cast(void*)tarr.ptr, size);
73         (cast(size_t*)&arr)[0] = length;
74         (cast(void**)&arr)[1] = cast(void*)tarr.ptr;
75     }
76 }
77 
78 /**
79  * Resizes a previously allocated array in the data segment to the specified length from beneath.
80  * New elements will become [0..length] (or removed.)
81  * 
82  * Params:
83  *  T = The array type.
84  *  arr = The array to be resized.
85  *  length = The new length of the array.
86  *
87  * Remarks:
88  *  No validation is done that `arr` was previously allocated with `dsNew`, stay vigilant.
89  */
90 void dsResizeBeneath(T : U[], U)(ref T arr, size_t length)
91 {
92     const size_t size = U.sizeof * length;
93     const size_t curSize = *cast(size_t*)(cast(void*)arr.ptr - size_t.sizeof);
94     const size_t offset = U.sizeof * cast(size_t)((curSize - (curSize - (cast(size_t)(U.sizeof * 1.5) == size_t.sizeof ? 0 : (size_t.sizeof - (cast(size_t)(U.sizeof * 1.5) % size_t.sizeof))))) / 1.5);
95     if (curSize >= size)
96     {
97         (cast(size_t*)&arr)[0] = length;
98         (cast(void**)&arr)[1] += offset;
99         (cast(size_t*)arr.ptr)[-1] = size;
100     }
101     else
102     {
103         T tarr = dsNew!T(length);
104         memcpy(cast(void*)arr.ptr + offset, cast(void*)tarr.ptr, size);
105         (cast(size_t*)&arr)[0] = length;
106         (cast(void**)&arr)[1] = cast(void*)tarr.ptr;
107     }
108 }
109 
110 /**
111  * Allocates `T` in the data segment, this will just create `T` normally for value types.
112  *
113  * Params:
114  *   T = The type to be allocated.
115  *
116  * Returns:
117  *   A new instance of `T` allocated in the data segment.
118  *
119  * Example:
120  *   ```d
121  *   B a = dsNew!B;
122  *   writeln(a); // tern.main.B
123  *   ```
124  */
125 T dsNew(T, uint R0 = __LINE__, string R1 = __TIMESTAMP__, string R2 = __FILE_FULL_PATH__, string R3 = __FUNCTION__)()
126     if (!isDynamicArray!T)
127 {
128     static if (!is(T == class))
129     {
130         static if (hasConstructor!T)
131             return T(args);
132         else
133             return T.init;
134     }
135     else
136     {
137         enum rand = random!(size_t, 0, size_t.max, uint.max, R0, R1, R2, R3).to!string;
138         mixin("static ubyte[sizeof!T] bytes"~rand~";");
139         foreach (field; Fields!T)
140         {
141             auto init = getChild!(T, field).init;
142             enum offset = getChild!(T, field).offsetof;
143             mixin("bytes"~rand~"[offset..(offset + TypeOf!(T, field).sizeof)] = (cast(ubyte*)&init)[0..TypeOf!(T, field).sizeof];");
144         }
145         // 8 bytes after this are __monitor, but we don't need to create one 
146         mixin("(cast(void**)bytes"~rand~".ptr)[0] = T.classinfo.vtbl.ptr;");
147         mixin("T ret = cast(T)bytes"~rand~".ptr;");
148         ret.__ctor();
149         return ret;
150     }
151 }
152 
153 T dsbNew(T, bool ctor = true)()
154     if (!is(T : U[], U))
155 {
156     static if (!is(T == class))
157     {
158         static if (hasConstructor!T)
159             T ret = T(args);
160         else
161             T ret;
162         return ret;
163     }
164     else
165     {
166         enum capacity = 100_194_304 - sizeof!T;
167         static size_t offset;
168         static ubyte[100_194_304] data;
169         assert(offset < capacity, "DSB allocator ran out of available memory!");
170 
171         static foreach (field; Fields!T)
172         {
173             {
174                 auto init = getChild!(T, field).init;
175                 size_t _offset = getChild!(T, field).offsetof + offset;
176                 data[_offset..(_offset + TypeOf!(T, field).sizeof)] = (cast(ubyte*)&init)[0..TypeOf!(T, field).sizeof];
177             }
178         }
179 
180         // 8 bytes after this are __monitor, but we don't need to create one 
181         (cast(void**)(cast(ubyte*)&data + offset))[0] = T.classinfo.vtbl.ptr;
182         T ret = cast(T)(cast(ubyte*)&data + offset);
183         static if (ctor)
184             ret.__ctor();
185         offset += sizeof!T;
186         return ret;
187     }
188 }