1 module memutils.utils; 2 3 import core.thread : Fiber; 4 import std.traits : isPointer, hasIndirections, hasElaborateDestructor; 5 import std.conv : emplace; 6 import std.c.string : memset, memcpy; 7 import memutils.allocators; 8 import std.algorithm : startsWith; 9 import memutils.constants; 10 import memutils.vector : Array; 11 import std.traits : isArray; 12 import std.range : ElementType; 13 import memutils.helpers : UnConst; 14 15 struct AppMem { 16 mixin ConvenienceAllocators!(NativeGC, typeof(this)); 17 } 18 19 struct ThreadMem { 20 mixin ConvenienceAllocators!(LocklessFreeList, typeof(this)); 21 } 22 23 struct SecureMem { 24 mixin ConvenienceAllocators!(CryptoSafe, typeof(this)); 25 } 26 27 package struct Malloc { 28 enum ident = Mallocator; 29 } 30 31 package: 32 33 template ObjectAllocator(T, ALLOC) 34 { 35 import std.traits : ReturnType; 36 import core.memory : GC; 37 enum ElemSize = AllocSize!T; 38 39 static if (ALLOC.stringof == "PoolStack") { 40 ReturnType!(ALLOC.top) function() m_getAlloc = &ALLOC.top; 41 } 42 static if (__traits(hasMember, T, "NOGC")) enum NOGC = T.NOGC; 43 else enum NOGC = false; 44 45 alias TR = RefTypeOf!T; 46 47 48 TR alloc(ARGS...)(auto ref ARGS args) 49 { 50 static if (ALLOC.stringof != "PoolStack") 51 auto mem = getAllocator!(ALLOC.ident)().alloc(ElemSize); 52 else 53 auto mem = m_getAlloc().alloc(ElemSize); 54 static if ( ALLOC.stringof != "AppMem" && hasIndirections!T && !NOGC) 55 { 56 static if (__traits(compiles, { GC.addRange(null, 0, typeid(string)); }())) 57 GC.addRange(mem.ptr, ElemSize, typeid(T)); 58 else 59 GC.addRange(mem.ptr, ElemSize); 60 } 61 return emplace!T(mem, args); 62 63 } 64 65 void free(TR obj) 66 { 67 auto objc = obj; 68 static if (is(TR == T*)) .destroy(*objc); 69 else .destroy(objc); 70 71 static if( ALLOC.stringof != "AppMem" && hasIndirections!T && !NOGC) GC.removeRange(cast(void*)obj); 72 73 static if (ALLOC.stringof != "PoolStack") 74 getAllocator!(ALLOC.ident)().free((cast(void*)obj)[0 .. ElemSize]); 75 else 76 m_getAlloc().free((cast(void*)obj)[0 .. ElemSize]); 77 78 } 79 } 80 81 /// Allocates an array without touching the memory. 82 T[] allocArray(T, ALLOC = ThreadMem)(size_t n) 83 { 84 import core.memory : GC; 85 mixin(translateAllocator()); 86 auto allocator = thisAllocator(); 87 88 auto mem = allocator.alloc(T.sizeof * n); 89 // logTrace("alloc ", T.stringof, ": ", mem.ptr); 90 auto ret = cast(T[])mem; 91 // logTrace("alloc ", ALLOC.stringof, ": ", mem.ptr, ":", mem.length); 92 static if (__traits(hasMember, T, "NOGC")) enum NOGC = T.NOGC; 93 else enum NOGC = false; 94 95 static if( ALLOC.stringof != "AppMem" && hasIndirections!T && !NOGC) { 96 // TODO: Do I need to add range for GC.malloc too? 97 static if (__traits(compiles, { GC.addRange(null, 0, typeid(string)); }())) 98 GC.addRange(mem.ptr, mem.length, typeid(T)); 99 else 100 GC.addRange(mem.ptr, mem.length); 101 } 102 103 // don't touch the memory - all practical uses of this function will handle initialization. 104 return ret; 105 } 106 107 T[] reallocArray(T, ALLOC = ThreadMem)(T[] array, size_t n) { 108 import core.memory : GC; 109 assert(n > array.length, "Cannot reallocate to smaller sizes"); 110 mixin(translateAllocator()); 111 auto allocator = thisAllocator(); 112 // logTrace("realloc before ", ALLOC.stringof, ": ", cast(void*)array.ptr, ":", array.length); 113 114 //logTrace("realloc fre ", T.stringof, ": ", array.ptr); 115 auto mem = allocator.realloc(cast(void[]) array, T.sizeof * n); 116 //logTrace("realloc ret ", T.stringof, ": ", mem.ptr); 117 auto ret = cast(T[])mem; 118 // logTrace("realloc after ", ALLOC.stringof, ": ", mem.ptr, ":", mem.length); 119 120 static if (__traits(hasMember, T, "NOGC")) enum NOGC = T.NOGC; 121 else enum NOGC = false; 122 123 static if (hasIndirections!T && !NOGC) { 124 GC.removeRange(array.ptr); 125 static if (__traits(compiles, { GC.addRange(null, 0, typeid(string)); }())) 126 GC.addRange(mem.ptr, mem.length, typeid(T)); 127 else 128 GC.addRange(mem.ptr, mem.length); 129 // Zero out unused capacity to prevent gc from seeing false pointers 130 memset(mem.ptr + (array.length * T.sizeof), 0, (n - array.length) * T.sizeof); 131 } 132 133 return ret; 134 } 135 136 void freeArray(T, ALLOC = ThreadMem)(auto ref T[] array, size_t max_destroy = size_t.max) 137 { 138 import core.memory : GC; 139 mixin(translateAllocator()); 140 auto allocator = thisAllocator(); 141 142 // logTrace("free ", ALLOC.stringof, ": ", cast(void*)array.ptr, ":", array.length); 143 static if (__traits(hasMember, T, "NOGC")) enum NOGC = T.NOGC; 144 else enum NOGC = false; 145 146 static if (ALLOC.stringof != "AppMem" && hasIndirections!T && !NOGC) { 147 GC.removeRange(array.ptr); 148 } 149 150 static if (hasElaborateDestructor!T) { // calls destructors, but not for indirections... 151 size_t i; 152 foreach (ref e; array) { 153 if (i == max_destroy) break; 154 static if (is(T == struct) && !isPointer!T) .destroy(e); 155 i++; 156 } 157 } 158 allocator.free(cast(void[])array); 159 array = null; 160 } 161 162 mixin template ConvenienceAllocators(alias ALLOC, alias THIS) { 163 package enum ident = ALLOC; 164 static: 165 // objects 166 auto alloc(T, ARGS...)(auto ref ARGS args) 167 if (!isArray!T) 168 { 169 return ObjectAllocator!(T, THIS).alloc(args); 170 } 171 172 void free(T)(auto ref T* obj) 173 if (!isArray!T && !is(T : Object)) 174 { 175 scope(exit) obj = null; 176 ObjectAllocator!(T, THIS).free(obj); 177 } 178 179 void free(T)(auto ref T obj) 180 if (!isArray!T && is(T : Object)) 181 { 182 scope(exit) obj = null; 183 ObjectAllocator!(T, THIS).free(obj); 184 } 185 186 /// arrays 187 auto alloc(T)(size_t n) 188 if (isArray!T) 189 { 190 alias ElType = UnConst!(typeof(T.init[0])); 191 return allocArray!(ElType, THIS)(n); 192 } 193 194 auto copy(T)(auto ref T arr) 195 if (isArray!T) 196 { 197 alias ElType = UnConst!(typeof(arr[0])); 198 auto arr_copy = allocArray!(ElType, THIS)(arr.length); 199 memcpy(arr_copy.ptr, arr.ptr, arr.length * ElType.sizeof); 200 201 return cast(T)arr_copy; 202 } 203 204 auto realloc(T)(auto ref T arr, size_t n) 205 if (isArray!T) 206 { 207 alias ElType = UnConst!(typeof(arr[0])); 208 scope(exit) arr = null; 209 auto arr_copy = reallocArray!(typeof(arr[0]), THIS)(arr, n); 210 return cast(T) arr_copy; 211 } 212 213 void free(T)(auto ref T arr) 214 if (isArray!T) 215 { 216 alias ElType = typeof(arr[0]); 217 scope(exit) arr = null; 218 freeArray!(ElType, THIS)(arr); 219 } 220 221 } 222 223 string translateAllocator() { /// requires (ALLOC) template parameter 224 return ` 225 static if (ALLOC.stringof != "PoolStack") { 226 ReturnType!(getAllocator!(ALLOC.ident)) thisAllocator() { 227 return getAllocator!(ALLOC.ident)(); 228 } 229 } 230 else { 231 ReturnType!(ALLOC.top) function() thisAllocator = &ALLOC.top; 232 } 233 `; 234 }