1 module memutils.utils; 2 3 import memutils.allocators; 4 import memutils.constants; 5 import memutils.vector : Array; 6 import memutils.helpers : UnConst, memset, memcpy; 7 import memutils.memory; 8 9 import std.traits : hasMember, isPointer, hasIndirections, hasElaborateDestructor, isArray, ReturnType; 10 11 struct ThreadMem { 12 nothrow: 13 @trusted: 14 mixin ConvenienceAllocators!(LocklessFreeList, ThreadMem); 15 } 16 17 // Reserved for containers 18 struct Malloc { 19 enum ident = Mallocator; 20 } 21 // Reserved for containers 22 struct CTFE { 23 24 } 25 26 // overloaded for AppMem, otherwise uses ThreadMem 27 @trusted extern(C) nothrow { 28 void[] FL_allocate(size_t n); 29 void[] FL_reallocate(void[] mem, size_t n); 30 void FL_deallocate(void[] mem); 31 } 32 /* 33 template PoolAllocator(T) 34 { 35 nothrow: 36 enum ElemSize = AllocSize!T; 37 Pool m_allocator; 38 39 this(Pool allocator) { 40 m_allocator = allocator; 41 } 42 43 44 alias TR = RefTypeOf!T; 45 46 TR alloc(ARGS...)(auto ref ARGS args) 47 { 48 auto mem = m_allocator.alloc(ElemSize); 49 TR omem = cast(TR)mem; 50 51 logTrace("PoolObjectAllocator.alloc initialize"); 52 *omem = T(args); 53 return omem; 54 55 } 56 57 void free(TR obj) 58 { 59 60 TR objc = obj; 61 import memutils.helpers : destructRecurse; 62 static if (is(TR == T*) && hasElaborateDestructor!T) { 63 logTrace("ObjectAllocator.free Pointer destr ", T.stringof); 64 destructRecurse(*objc); 65 } 66 else static if (hasElaborateDestructor!TR) { 67 logTrace("ObjectAllocator.free other destr ", T.stringof); 68 destructRecurse(objc); 69 } 70 71 m_allocator.free((cast(void*)obj)[0 .. ElemSize]); 72 73 } 74 } 75 */ 76 77 nothrow: 78 79 struct ObjectAllocator(T, ALLOC = ThreadMem) 80 { 81 nothrow: 82 enum ElemSize = AllocSize!T; 83 84 static if (ALLOC.stringof == "PoolStack") { 85 ReturnType!(ALLOC.top) function() m_getAlloc = &ALLOC.top; 86 } else static if (!hasMember!(ALLOC, "ident") && ALLOC.stringof != "void") { 87 ALLOC* m_allocator; 88 this(ALLOC* base) { 89 m_allocator = base; 90 } 91 } 92 enum NOGC = true; 93 94 alias TR = RefTypeOf!T; 95 TR alloc(ARGS...)(auto ref ARGS args) 96 { 97 if (__ctfe) { 98 assert(__ctfe); 99 return new T(args); 100 } else { 101 static if (ALLOC.stringof == "PoolStack") { 102 auto mem = m_getAlloc().alloc(ElemSize); 103 } 104 else static if (ALLOC.stringof == "void") { 105 auto mem = FL_allocate(ElemSize); 106 } else { 107 static if (hasMember!(ALLOC, "ident")) 108 auto allocator_ = getAllocator!(ALLOC.ident)(false); 109 else 110 auto allocator_ = m_allocator; 111 auto mem = allocator_.alloc(ElemSize); 112 } 113 TR omem = cast(TR)mem; 114 115 logTrace("ObjectAllocator.alloc initialize"); 116 *omem = T(args); 117 return omem; 118 } 119 120 } 121 122 void free(TR obj) 123 { 124 125 if (!__ctfe) { 126 TR objc = obj; 127 import memutils.helpers : destructRecurse; 128 static if (is(TR == T*) && hasElaborateDestructor!T) { 129 logTrace("ObjectAllocator.free Pointer destr ", T.stringof); 130 destructRecurse(*objc); 131 } 132 else static if (hasElaborateDestructor!TR) { 133 logTrace("ObjectAllocator.free other destr ", T.stringof); 134 destructRecurse(objc); 135 } 136 137 static if (ALLOC.stringof == "CTFE") { 138 return; 139 } else static if (ALLOC.stringof == "PoolStack") { 140 m_getAlloc().free((cast(void*)obj)[0 .. ElemSize]); 141 } 142 else static if (ALLOC.stringof == "void") { 143 FL_deallocate((cast(void*)obj)[0 .. ElemSize]); 144 } 145 else { 146 static if (hasMember!(ALLOC, "ident")) 147 auto a = getAllocator!(ALLOC.ident)(true); 148 else 149 auto a = m_allocator; 150 a.free((cast(void*)obj)[0 .. ElemSize]); 151 } 152 } 153 154 } 155 } 156 157 /// Allocates an array without touching the memory. 158 T[] allocArray(T, ALLOC = ThreadMem)(size_t n, ALLOC* base = null) 159 { 160 static enum TSize = T.sizeof; 161 162 if (__ctfe) { 163 assert(__ctfe); 164 return new T[n]; 165 } else { 166 static if (ALLOC.stringof == "void") { 167 auto mem = FL_allocate(TSize * n); 168 return (cast(T*)mem.ptr)[0 .. n]; 169 } else { 170 static if (ALLOC.stringof == "PoolStack") 171 auto allocator = ALLOC.top; 172 else static if (hasMember!(ALLOC, "ident")) 173 auto allocator = getAllocator!(ALLOC.ident)(false); 174 else static if (ALLOC.stringof != "void") 175 auto allocator = base; 176 auto mem = allocator.alloc(TSize * n); 177 // logTrace("alloc ", T.stringof, ": ", mem.ptr); 178 auto ret = (cast(T*)mem.ptr)[0 .. n]; 179 // logTrace("alloc ", ALLOC.stringof, ": ", mem.ptr, ":", mem.length); 180 181 // don't touch the memory - all practical uses of this function will handle initialization. 182 return ret; 183 } 184 } 185 186 } 187 188 T[] reallocArray(T, ALLOC = ThreadMem)(T[] array, size_t n, ALLOC* base = null) { 189 static enum TSize = T.sizeof; 190 assert(n > array.length, "Cannot reallocate to smaller sizes"); 191 192 if (__ctfe) { 193 assert(__ctfe); 194 auto arr = new T[n]; 195 arr[0 .. array.length] = array[0 .. $]; 196 return arr; 197 } else { 198 static if (ALLOC.stringof == "void") { 199 auto mem = FL_reallocate((cast(void*)array.ptr)[0 .. array.length * TSize], TSize * n); 200 return (cast(T*)mem.ptr)[0 .. n]; 201 } else { 202 static if (ALLOC.stringof == "PoolStack") 203 auto allocator = ALLOC.top; 204 else static if (hasMember!(ALLOC, "ident")) 205 auto allocator = getAllocator!(ALLOC.ident)(false); 206 else 207 auto allocator = base; 208 209 // logTrace("realloc before ", ALLOC.stringof, ": ", cast(void*)array.ptr, ":", array.length); 210 211 //logTrace("realloc fre ", T.stringof, ": ", array.ptr); 212 auto mem = allocator.realloc((cast(void*)array.ptr)[0 .. array.length * TSize], TSize * n); 213 //logTrace("realloc ret ", T.stringof, ": ", mem.ptr); 214 auto ret = (cast(T*)mem.ptr)[0 .. n]; 215 // logTrace("realloc after ", ALLOC.stringof, ": ", mem.ptr, ":", mem.length); 216 217 218 return ret; 219 } 220 } 221 } 222 223 nothrow void freeArray(T, ALLOC = ThreadMem)(auto ref T[] array, size_t max_destroy = size_t.max, size_t offset = 0, ALLOC* base = null) 224 { 225 static enum TSize = T.sizeof; 226 227 static if (hasElaborateDestructor!T) { // calls destructors, but not for indirections... 228 size_t i; 229 foreach (ref e; array) { 230 if (i < offset) { i++; continue; } 231 if (i + offset == max_destroy) break; 232 import memutils.helpers : destructRecurse; 233 destructRecurse(e); 234 i++; 235 } 236 } 237 238 static if (ALLOC.stringof == "void") { 239 FL_deallocate((cast(void*)array.ptr)[0 .. array.length * TSize]); 240 } 241 else static if (ALLOC.stringof != "CTFE") { 242 static if (ALLOC.stringof == "PoolStack") 243 auto allocator = ALLOC.top; 244 else static if (hasMember!(ALLOC, "ident")) { 245 auto allocator = getAllocator!(ALLOC.ident)(true); 246 if (allocator == typeof(allocator).init) return; 247 } else { 248 auto allocator = base; 249 } 250 251 allocator.free((cast(void*)array.ptr)[0 .. array.length * TSize]); 252 } 253 254 array = null; 255 256 } 257 258 mixin template ConvenienceAllocators(alias ALLOC, alias THIS) { 259 package enum ident = ALLOC; 260 nothrow: 261 static: 262 // objects 263 auto alloc(T, ARGS...)(auto ref ARGS args) 264 if (!isArray!T) 265 { 266 return ObjectAllocator!(T, THIS)().alloc(args); 267 } 268 269 void free(T)(auto ref T* obj) 270 if (!isArray!T && !is(T : Object)) 271 { 272 scope(exit) obj = null; 273 ObjectAllocator!(T, THIS)().free(obj); 274 } 275 276 void free(T)(auto ref T obj) 277 if (!isArray!T && is(T : Object)) 278 { 279 scope(exit) obj = null; 280 ObjectAllocator!(T, THIS)().free(obj); 281 } 282 283 /// arrays 284 auto alloc(T)(size_t n) 285 if (isArray!T) 286 { 287 static if (is(T == void[])) { 288 return getAllocator!ALLOC().alloc(n); 289 } else { 290 alias ElType = UnConst!(typeof(T.init[0])); 291 return allocArray!(ElType, THIS)(n); 292 } 293 } 294 295 auto copy(T)(auto ref T arr) 296 if (isArray!T) 297 { 298 alias ElType = UnConst!(typeof(arr[0])); 299 enum ElSize = ElType.sizeof; 300 auto arr_copy = allocArray!(ElType, THIS)(arr.length); 301 memcpy(arr_copy.ptr, arr.ptr, arr.length * ElSize); 302 303 return cast(T)arr_copy; 304 } 305 306 auto realloc(T)(auto ref T arr, size_t n, bool zeroise_mem = true) 307 if (isArray!T) 308 { 309 static if (is(T == void[])) { 310 return getAllocator!ALLOC().realloc(arr, n, zeroise_mem); 311 } else { 312 alias ElType = UnConst!(typeof(arr[0])); 313 scope(exit) arr = null; 314 auto arr_copy = reallocArray!(typeof(arr[0]), THIS)(arr, n); 315 return cast(T) arr_copy; 316 } 317 } 318 319 void free(T)(auto ref T arr, bool zeroise_mem = false) 320 if (isArray!T) 321 { 322 static if (is(T == void[])) { 323 return getAllocator!ALLOC().free(arr, zeroise_mem); 324 } else { 325 alias ElType = typeof(arr[0]); 326 scope(exit) arr = null; 327 freeArray!(ElType, THIS)(arr); 328 } 329 } 330 331 }