1 module libd.memory.allocator.common; 2 3 import libd.meta.ctassert : ctassert; 4 import libd.memory.ptr : MaybeNullPtr, MaybeNullSlice, NotNullSlice, maybeNull, NotNullPtr, notNull; 5 import libd.memory.funcs : emplaceCtor, dtorSliceIfNeeded, emplaceInit; 6 import libd.meta.traits : isInstanceOf, isCopyable; 7 8 enum isSimpleAllocator(alias T) = 9 __traits(hasMember, T, "alloc") 10 && __traits(hasMember, T, "free") 11 && __traits(hasMember, T, "Tag") 12 && __traits(hasMember, T, "realloc"); 13 14 enum isStaticAllocator(alias T) = 15 isSimpleAllocator!T 16 && __traits(hasMember, T, "AllocIsStatic"); 17 18 enum isAllocatorWrapper(alias T) = isInstanceOf!(Allocator, T); 19 20 template AllocatorWrapperOf(alias AllocOrWrapperT) 21 { 22 static if(isAllocatorWrapper!AllocatorWrapperOf) 23 alias AllocatorWrapperOf = AllocOrWrapperT; 24 else 25 alias AllocatorWrapperOf = Allocator!AllocOrWrapperT; 26 } 27 28 @nogc nothrow 29 struct Allocator(alias AllocT) 30 if(ctassert!(isSimpleAllocator!AllocT, "Type `"~AllocT.stringof~"` is not an allocator.")) 31 { 32 static assert(isAllocatorWrapper!(typeof(this))); 33 static immutable string Tag = AllocT.Tag; 34 35 alias Alloc = AllocT; 36 alias isStatic = isStaticAllocator!Alloc; 37 38 static if(isStatic) 39 { 40 alias instance = AllocT; 41 42 this(_...)(_){} // To make generic code slightly easier, we'll just eat anything passed to the ctor in this case. 43 this(_...)(_) shared {} 44 } 45 else 46 { 47 AllocT* instance; 48 invariant(instance !is null, "Non-static allocator `"~AllocT.stringof~"` must be provided a solid instance before use."); 49 50 @safe 51 this(AllocT* instance) 52 { 53 assert(instance !is null, "Instance cannot be null."); 54 this.instance = instance; 55 } 56 57 @safe shared 58 this(AllocT* instance) 59 { 60 assert(instance !is null, "Instance cannot be null."); 61 this.instance = instance; 62 } 63 } 64 65 @nogc nothrow: 66 67 MaybeNullPtr!(T, Tag) make(T, Params...)(scope auto ref Params params) 68 { 69 auto slice = instance.alloc!T(1); 70 if(slice is null) 71 return typeof(return)(null); 72 emplaceCtor(*slice.ptr, params); 73 return typeof(return)(slice.ptr); 74 } 75 76 // idk how to DRY this since the type system constantly gets in my way. 77 shared 78 MaybeNullPtr!(T, Tag) make(T, Params...)(scope auto ref Params params) 79 { 80 auto slice = instance.alloc!T(1); 81 if(slice is null) 82 return typeof(return)(null); 83 emplaceCtor(*slice.ptr, params); 84 return typeof(return)(slice.ptr); 85 } 86 87 MaybeNullSlice!(T, Tag) makeArray(T, Params...)(const size_t amount, scope auto ref Params params) 88 { 89 auto slice = instance.alloc!T(amount); 90 if(slice is null) 91 return typeof(return)(null); 92 93 foreach(ref item; slice) 94 emplaceCtor(item, params); 95 return slice; 96 } 97 98 shared 99 MaybeNullSlice!(T, Tag) makeArray(T, Params...)(const size_t amount, scope auto ref Params params) 100 { 101 auto slice = instance.alloc!T(amount); 102 if(slice is null) 103 return typeof(return)(null); 104 105 foreach(ref item; slice) 106 emplaceCtor(item, params); 107 return slice; 108 } 109 110 // TODO: Check if reallocation causes the base ptr to change, and if it has, perform an internal pointer update 111 // on any types marked with `OnMove.callUpdateInternalPointers`. 112 113 MaybeNullSlice!(T, Tag) growArray(T)(const size_t to, scope auto ref NotNullSlice!(T, Tag) slice) 114 { 115 if(to == slice.length) 116 return slice.maybeNull; 117 118 assert(to > slice.length, "`to` is not greater or equal to the given slice."); 119 const oldLen = slice.length; 120 const diff = to - slice.length; 121 auto ptr = instance.realloc!T(slice, to); 122 if(ptr is null) 123 return typeof(return)(null); 124 125 static if(!__traits(isZeroInit, T)) 126 { 127 static if(isCopyable!T) 128 { 129 foreach(ref value; ptr[oldLen..oldLen+diff]) 130 value = T.init; 131 } 132 else 133 { 134 foreach(ref value; ptr[oldLen..oldLen+diff]) 135 emplaceInit(value); 136 } 137 } 138 return typeof(return)(ptr[0..to]); 139 } 140 141 MaybeNullSlice!(T, Tag) shrinkArray(T)(const size_t to, scope auto ref NotNullSlice!(T, Tag) slice) 142 { 143 if(to == slice.length) 144 return slice.maybeNull; 145 146 assert(to < slice.length, "`to` is not less than or eqaul to the given slice."); 147 slice[to..$].notNull.dtorSliceIfNeeded(); 148 149 auto ptr = instance.realloc!T(slice, to); 150 if(ptr is null) 151 return typeof(return)(null); 152 else 153 return typeof(return)(ptr[0..to]); 154 } 155 156 void dispose(T)(scope auto ref NotNullPtr!(T, Tag) ptr) 157 { 158 static if(__traits(hasMember, T, "__xdtor")) 159 ptr.ptr.__xdtor(); 160 instance.free!T(ptr); 161 } 162 163 void dispose(T)(scope auto ref NotNullSlice!(T, Tag) slice) 164 { 165 slice[0..$].notNull.dtorSliceIfNeeded(); 166 instance.free!T(slice); 167 } 168 169 void dispose(T)(scope auto ref T* ptr) // Trusted that the user knows what they're doing. 170 { 171 auto wrapped = NotNullPtr!(T, Tag)(ptr); 172 this.dispose(wrapped); 173 ptr = null; 174 } 175 176 shared 177 void dispose(T)(scope auto ref NotNullPtr!(T, Tag) ptr) 178 { 179 static if(__traits(hasMember, T, "__xdtor")) 180 ptr.ptr.__xdtor(); 181 instance.free!T(ptr); 182 } 183 184 shared 185 void dispose(T)(scope auto ref NotNullSlice!(T, Tag) slice) 186 { 187 slice[0..$].notNull.dtorSliceIfNeeded(); 188 instance.free!T(slice); 189 } 190 191 shared 192 void dispose(T)(scope auto ref T* ptr) // Trusted that the user knows what they're doing. 193 { 194 auto wrapped = NotNullPtr!(T, Tag)(ptr); 195 this.dispose(wrapped); 196 ptr = null; 197 } 198 } 199 200 @nogc nothrow 201 size_t memoryPageSize() 202 { 203 version(Windows) 204 { 205 import runtime.system.windows; 206 SYSTEM_INFO sysInfo; 207 GetSystemInfo(&sysInfo); 208 return sysInfo.dwPageSize; 209 } 210 else version(Posix) 211 { 212 import runtime.system.posix; 213 return g_posixPageSize; 214 } 215 else assert(false); 216 }