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 }