1 /**
2   This module provide support for aligned memory.
3  */
4 module gfm.core.memory;
5 
6 import core.memory : GC;
7 import core.exception : onOutOfMemoryError;
8 
9 import std.c.stdlib : malloc, free, realloc;
10 import std.conv : emplace;
11 import std.traits;
12 import std.algorithm: swap;
13 
14 
15 static if( __VERSION__ < 2066 ) private enum nogc = 1;
16 
17 /// Returns: next pointer aligned with alignment bytes.
18 @nogc void* nextAlignedPointer(void* start, size_t alignment) pure nothrow
19 {
20     return cast(void*)nextMultipleOf(cast(size_t)(start), alignment);
21 }
22 
23 /// Allocates an aligned memory chunk.
24 /// Functionally equivalent to Visual C++ _aligned_malloc.
25 @nogc void* alignedMalloc(size_t size, size_t alignment) nothrow
26 {
27     if (size == 0)
28         return null;
29 
30     size_t request = requestedSize(size, alignment);
31     void* raw = malloc(request);
32 
33     static if( __VERSION__ > 2067 ) // onOutOfMemoryError wasn't nothrow before July 2014
34     {
35         if (request > 0 && raw == null) // malloc(0) can validly return anything
36             onOutOfMemoryError();
37     }
38 
39     return storeRawPointerAndReturnAligned(raw, alignment);
40 }
41 
42 /// Frees aligned memory allocated by alignedMalloc or alignedRealloc.
43 /// Functionally equivalent to Visual C++ _aligned_free.
44 @nogc void alignedFree(void* aligned) nothrow
45 {
46     // support for free(NULL)
47     if (aligned is null)
48         return;
49 
50     void** rawLocation = cast(void**)(cast(char*)aligned - size_t.sizeof);
51     free(*rawLocation);
52 }
53 
54 /// Reallocates an aligned memory chunk allocated by alignedMalloc or alignedRealloc.
55 /// Functionally equivalent to Visual C++ _aligned_realloc.
56 @nogc void* alignedRealloc(void* aligned, size_t size, size_t alignment) nothrow
57 {
58     if (aligned is null)
59         return alignedMalloc(size, alignment);
60 
61     if (size == 0)
62     {
63         alignedFree(aligned);
64         return null;
65     }
66 
67     void* raw = *cast(void**)(cast(char*)aligned - size_t.sizeof);
68 
69     size_t request = requestedSize(size, alignment);
70     void* newRaw = realloc(raw, request);
71 
72     static if( __VERSION__ > 2067 ) // onOutOfMemoryError wasn't nothrow before July 2014
73     {
74         if (request > 0 && newRaw == null) // realloc(0) can validly return anything
75             onOutOfMemoryError();
76     }
77 
78     // if newRaw is raw, nothing to do
79     if (raw is newRaw)
80         return aligned;
81 
82     // else write raw at the new location
83     return storeRawPointerAndReturnAligned(newRaw, alignment);
84 }
85 
86 private
87 {
88     // Returns number of bytes to actually allocate when asking
89     // for a particular alignement
90     @nogc size_t requestedSize(size_t askedSize, size_t alignment) pure nothrow
91     {
92         enum size_t pointerSize = size_t.sizeof;
93         return askedSize + alignment - 1 + pointerSize;
94     }
95 
96     @nogc void* storeRawPointerAndReturnAligned(void* raw, size_t alignment) nothrow
97     {
98         enum size_t pointerSize = size_t.sizeof;
99         char* start = cast(char*)raw + pointerSize;
100         void* aligned = nextAlignedPointer(start, alignment);
101         void** rawLocation = cast(void**)(cast(char*)aligned - pointerSize);
102         *rawLocation = raw;
103         return aligned;
104     }
105 
106     // Returns: x, multiple of powerOfTwo, so that x >= n.
107     @nogc size_t nextMultipleOf(size_t n, size_t powerOfTwo) pure nothrow
108     {
109         // check power-of-two
110         assert( (powerOfTwo != 0) && ((powerOfTwo & (powerOfTwo - 1)) == 0));
111 
112         size_t mask = ~(powerOfTwo - 1);
113         return (n + powerOfTwo - 1) & mask;
114     }
115 }
116 
117 unittest
118 {
119     assert(nextMultipleOf(0, 4) == 0);
120     assert(nextMultipleOf(1, 4) == 4);
121     assert(nextMultipleOf(2, 4) == 4);
122     assert(nextMultipleOf(3, 4) == 4);
123     assert(nextMultipleOf(4, 4) == 4);
124     assert(nextMultipleOf(5, 4) == 8);
125 
126     {
127         void* p = alignedMalloc(23, 16);
128         assert(p !is null);
129         assert(((cast(size_t)p) & 0xf) == 0);
130 
131         alignedFree(p);
132     }
133 
134     assert(alignedMalloc(0, 16) == null);
135     alignedFree(null);
136 
137     {
138         int alignment = 16;
139         int* p = null;
140 
141         // check if growing keep values in place
142         foreach(int i; 0..100)
143         {
144             p = cast(int*) alignedRealloc(p, (i + 1) * int.sizeof, alignment);
145             p[i] = i;
146         }
147 
148         foreach(int i; 0..100)
149             assert(p[i] == i);
150 
151 
152         p = cast(int*) alignedRealloc(p, 0, alignment);
153         assert(p is null);
154     }
155 }
156 
157 
158 /// Allocates and construct a struct or class object.
159 /// Returns: Newly allocated object.
160 auto mallocEmplace(T, Args...)(Args args)
161 {
162     static if (is(T == class))
163         immutable size_t allocSize = __traits(classInstanceSize, T);
164     else
165         immutable size_t allocSize = T.sizeof;
166 
167     void* rawMemory = malloc(allocSize);
168     if (!rawMemory)
169         onOutOfMemoryError();
170 
171     static if (is(T == class))
172     {
173         T obj = emplace!T(rawMemory[0 .. allocSize], args);
174     }
175     else
176     {
177         T* obj = cast(T*)rawMemory;
178         emplace!T(obj, args);
179     }
180 
181     static if (hasIndirections!T)
182         GC.addRange(rawMemory, allocSize);
183 
184     return obj;
185 }
186 
187 /// Destroys and frees a class object created with $(D mallocEmplace).
188 void destroyFree(T)(T p) if (is(T == class))
189 {
190     if (p !is null)
191     {
192         destroy(p);
193 
194         static if (hasIndirections!T)
195             GC.removeRange(cast(void*)p);
196 
197         free(cast(void*)p);
198     }
199 }
200 
201 /// Destroys and frees a non-class object created with $(D mallocEmplace).
202 void destroyFree(T)(T* p) if (!is(T == class))
203 {
204     if (p !is null)
205     {
206         destroy(p);
207 
208         static if (hasIndirections!T)
209             GC.removeRange(cast(void*)p);
210 
211         free(cast(void*)p);
212     }
213 }
214 
215 unittest
216 {
217     class A
218     {
219         int _i;
220         this(int i)
221         {
222             _i = i;
223         }
224     }
225 
226     struct B
227     {
228         int i;
229     }
230 
231     void testMallocEmplace()
232     {
233         A a = mallocEmplace!A(4);
234         destroyFree(a);
235 
236         B* b = mallocEmplace!B(5);
237         destroyFree(b);
238     }
239 
240     testMallocEmplace();
241 }
242 
243 version( D_InlineAsm_X86 )
244 {
245     version = AsmX86;
246 }
247 else version( D_InlineAsm_X86_64 )
248 {
249     version = AsmX86;
250 }
251 
252 /// Inserts a breakpoint instruction. useful to trigger the debugger.
253 void debugBreak() nothrow @nogc
254 {
255     version( AsmX86 )
256     {
257         static if( __VERSION__ >= 2067 )
258         {
259             mixin("asm nothrow @nogc { int 3; }");
260         }
261         else
262         {
263             mixin("asm { int 3; }");
264         }
265     }
266     else version( GNU )
267     {
268         // __builtin_trap() is not the same thing unfortunately
269         asm 
270         {
271             "int $0x03" : : : ;
272         }
273     }
274     else
275     {
276         static assert(false, "No debugBreak() for this compiler");
277     }
278 }
279 
280 auto assumeNoGC(T) (T t) if (isFunctionPointer!T || isDelegate!T)
281 {
282     enum attrs = functionAttributes!T | FunctionAttribute.nogc;
283     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
284 }
285 
286 auto assumeNothrowNoGC(T) (T t) if (isFunctionPointer!T || isDelegate!T)
287 {
288     enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_;
289     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
290 }
291 
292 unittest
293 {
294     void funcThatDoesGC()
295     {
296         throw new Exception("hello!");
297     }
298 
299     void anotherFunction() nothrow @nogc
300     {
301         assumeNothrowNoGC( (){ funcThatDoesGC(); } )();
302     }
303 
304     void aThirdFunction() @nogc
305     {
306         assumeNoGC( () { funcThatDoesGC(); } )();
307     }
308 }
309 
310 /// Must return -1 if a < b
311 ///              0 if a == b
312 ///              1 if a > b  
313 alias nogcComparisonFunction(T) = int delegate(in T a, in T b) nothrow @nogc;
314 
315 /// @nogc quicksort
316 /// From the excellent: http://codereview.stackexchange.com/a/77788
317 void nogc_qsort(T)(T[] array, nogcComparisonFunction!T comparison) nothrow @nogc
318 {
319     if (array.length < 2)
320         return;
321 
322     int partition(T* arr, int left, int right) nothrow @nogc
323     {
324         immutable int mid = left + (right - left) / 2;
325         immutable T pivot = arr[mid];
326         // move the mid point value to the front.
327         swap(arr[mid],arr[left]);
328         int i = left + 1;
329         int j = right;
330         while (i <= j) 
331         {
332             while(i <= j && comparison(arr[i], pivot) <= 0 )
333                 i++;
334 
335             while(i <= j && comparison(arr[j], pivot) > 0)
336                 j--;
337 
338             if (i < j)
339                 swap(arr[i], arr[j]);
340         }
341         swap(arr[i - 1], arr[left]);
342         return i - 1;
343     }
344 
345     void doQsort(T* array, int left, int right) nothrow @nogc
346     {
347         if (left >= right)
348             return;
349 
350         int part = partition(array, left, right);
351         doQsort(array, left, part - 1);
352         doQsort(array, part + 1, right);
353     }
354 
355     doQsort(array.ptr, 0, cast(int)(array.length) - 1);
356 }
357 
358 unittest
359 {
360     int[] testData = [110, 5, 10, 3, 22, 100, 1, 23];
361     nogc_qsort!int(testData, (a, b) => (a - b));
362     assert(testData == [1, 3, 5, 10, 22, 23, 100, 110]);
363 }