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 }