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 import core.stdc.string : memcpy;
9 import core.stdc.stdlib : malloc, free, realloc;
10 
11 import std.conv : emplace;
12 import std.traits;
13 import std.algorithm: swap;
14 
15 
16 /// Returns: next pointer aligned with alignment bytes.
17 @nogc void* nextAlignedPointer(void* start, size_t alignment) pure nothrow
18 {
19     return cast(void*)nextMultipleOf(cast(size_t)(start), alignment);
20 }
21 
22 /// Allocates an aligned memory chunk.
23 /// Functionally equivalent to Visual C++ _aligned_malloc.
24 @nogc void* alignedMalloc(size_t size, size_t alignment) nothrow
25 {
26     if (size == 0)
27         return null;
28 
29     size_t request = requestedSize(size, alignment);
30     void* raw = malloc(request);
31 
32     static if( __VERSION__ > 2067 ) // onOutOfMemoryError wasn't nothrow before July 2014
33     {
34         if (request > 0 && raw == null) // malloc(0) can validly return anything
35             onOutOfMemoryError();
36     }
37 
38     return storeRawPointerPlusSizeAndReturnAligned(raw, size, alignment);
39 }
40 
41 /// Frees aligned memory allocated by alignedMalloc or alignedRealloc.
42 /// Functionally equivalent to Visual C++ _aligned_free.
43 @nogc void alignedFree(void* aligned) nothrow
44 {
45     // support for free(NULL)
46     if (aligned is null)
47         return;
48 
49     void** rawLocation = cast(void**)(cast(char*)aligned - size_t.sizeof);
50     free(*rawLocation);
51 }
52 
53 /// Reallocates an aligned memory chunk allocated by alignedMalloc or alignedRealloc.
54 /// Functionally equivalent to Visual C++ _aligned_realloc.
55 @nogc void* alignedRealloc(void* aligned, size_t size, size_t alignment) nothrow
56 {
57     if (aligned is null)
58         return alignedMalloc(size, alignment);
59 
60     if (size == 0)
61     {
62         alignedFree(aligned);
63         return null;
64     }
65 
66     size_t previousSize = *cast(size_t*)(cast(char*)aligned - size_t.sizeof * 2);
67 
68 
69     void* raw = *cast(void**)(cast(char*)aligned - size_t.sizeof);
70     size_t request = requestedSize(size, alignment);
71 
72     // Heuristic: if new requested size is within 50% to 100% of what is already allocated
73     //            then exit with the same pointer
74     if ( (previousSize < request * 4) && (request <= previousSize) )
75         return aligned;
76 
77     void* newRaw = malloc(request);
78     static if( __VERSION__ > 2067 ) // onOutOfMemoryError wasn't nothrow before July 2014
79     {
80         if (request > 0 && newRaw == null) // realloc(0) can validly return anything
81             onOutOfMemoryError();
82     }
83 
84     void* newAligned = storeRawPointerPlusSizeAndReturnAligned(newRaw, request, alignment);
85     size_t minSize = size < previousSize ? size : previousSize;
86     memcpy(newAligned, aligned, minSize);
87 
88     // Free previous data
89     alignedFree(aligned);
90     return newAligned;
91 }
92 
93 private
94 {
95     // Returns number of bytes to actually allocate when asking
96     // for a particular alignement
97     @nogc size_t requestedSize(size_t askedSize, size_t alignment) pure nothrow
98     {
99         enum size_t pointerSize = size_t.sizeof;
100         return askedSize + alignment - 1 + pointerSize * 2;
101     }
102 
103     // Store pointer given my malloc, and size in bytes initially requested (alignedRealloc needs it)
104     @nogc void* storeRawPointerPlusSizeAndReturnAligned(void* raw, size_t size, size_t alignment) nothrow
105     {
106         enum size_t pointerSize = size_t.sizeof;
107         char* start = cast(char*)raw + pointerSize * 2;
108         void* aligned = nextAlignedPointer(start, alignment);
109         void** rawLocation = cast(void**)(cast(char*)aligned - pointerSize);
110         *rawLocation = raw;
111         size_t* sizeLocation = cast(size_t*)(cast(char*)aligned - 2 * pointerSize);
112         *sizeLocation = size;
113         return aligned;
114     }
115 
116     // Returns: x, multiple of powerOfTwo, so that x >= n.
117     @nogc size_t nextMultipleOf(size_t n, size_t powerOfTwo) pure nothrow
118     {
119         // check power-of-two
120         assert( (powerOfTwo != 0) && ((powerOfTwo & (powerOfTwo - 1)) == 0));
121 
122         size_t mask = ~(powerOfTwo - 1);
123         return (n + powerOfTwo - 1) & mask;
124     }
125 }
126 
127 unittest
128 {
129     assert(nextMultipleOf(0, 4) == 0);
130     assert(nextMultipleOf(1, 4) == 4);
131     assert(nextMultipleOf(2, 4) == 4);
132     assert(nextMultipleOf(3, 4) == 4);
133     assert(nextMultipleOf(4, 4) == 4);
134     assert(nextMultipleOf(5, 4) == 8);
135 
136     {
137         void* p = alignedMalloc(23, 16);
138         assert(p !is null);
139         assert(((cast(size_t)p) & 0xf) == 0);
140 
141         alignedFree(p);
142     }
143 
144     assert(alignedMalloc(0, 16) == null);
145     alignedFree(null);
146 
147     {
148         int alignment = 16;
149         int* p = null;
150 
151         // check if growing keep values in place
152         foreach(int i; 0..100)
153         {
154             p = cast(int*) alignedRealloc(p, (i + 1) * int.sizeof, alignment);
155             p[i] = i;
156         }
157 
158         foreach(int i; 0..100)
159             assert(p[i] == i);
160 
161 
162         p = cast(int*) alignedRealloc(p, 0, alignment);
163         assert(p is null);
164     }
165 }
166 
167 
168 /// Destructors called by the GC enjoy a variety of limitations and
169 /// relying on them is dangerous.
170 /// See_also: $(WEB p0nce.github.io/d-idioms/#The-trouble-with-class-destructors)
171 /// Example:
172 /// ---
173 /// class Resource
174 /// {
175 ///     ~this()
176 ///     {
177 ///         if (!alreadyClosed)
178 ///         {
179 ///             if (isCalledByGC())
180 ///                 assert(false, "Resource release relies on Garbage Collection");
181 ///             alreadyClosed = true;
182 ///             releaseResource();
183 ///         }
184 ///     }
185 /// }
186 /// ---
187 bool isCalledByGC() nothrow
188 {
189     import core.exception;
190     try
191     {
192         import core.memory;
193         cast(void) GC.malloc(1); // not ideal since it allocates
194         return false;
195     }
196     catch(InvalidMemoryOperationError e)
197     {
198         return true;
199     }
200 }
201 
202 unittest
203 {
204     import std.stdio;
205     class A
206     {
207         ~this()
208         {
209             assert(!isCalledByGC());
210         }
211     }
212     import std.typecons;
213     auto a = scoped!A();
214 }
215 
216 /// Crash if the GC is running.
217 /// Useful in destructors to avoid reliance GC resource release.
218 /// See_also: $(WEB p0nce.github.io/d-idioms/#GC-proof-resource-class)
219 void ensureNotInGC(string resourceName = null) nothrow
220 {
221     import core.exception;
222     try
223     {
224         import core.memory;
225         cast(void) GC.malloc(1); // not ideal since it allocates
226         return;
227     }
228     catch(InvalidMemoryOperationError e)
229     {
230         import core.stdc.stdio;
231         fprintf(stderr, "Error: clean-up of %s incorrectly depends on destructors called by the GC.\n",
232                         resourceName ? resourceName.ptr : "a resource".ptr);
233         assert(false); // crash
234     }
235 }
236 
237 
238 /// Allocates and construct a struct or class object.
239 /// Returns: Newly allocated object.
240 auto mallocEmplace(T, Args...)(Args args)
241 {
242     static if (is(T == class))
243         immutable size_t allocSize = __traits(classInstanceSize, T);
244     else
245         immutable size_t allocSize = T.sizeof;
246 
247     void* rawMemory = malloc(allocSize);
248     if (!rawMemory)
249         onOutOfMemoryError();
250 
251     static if (is(T == class))
252     {
253         T obj = emplace!T(rawMemory[0 .. allocSize], args);
254     }
255     else
256     {
257         T* obj = cast(T*)rawMemory;
258         emplace!T(obj, args);
259     }
260 
261     static if (hasIndirections!T)
262         GC.addRange(rawMemory, allocSize);
263 
264     return obj;
265 }
266 
267 /// Destroys and frees a class object created with $(D mallocEmplace).
268 void destroyFree(T)(T p) if (is(T == class))
269 {
270     if (p !is null)
271     {
272         destroy(p);
273 
274         static if (hasIndirections!T)
275             GC.removeRange(cast(void*)p);
276 
277         free(cast(void*)p);
278     }
279 }
280 
281 /// Destroys and frees a non-class object created with $(D mallocEmplace).
282 void destroyFree(T)(T* p) if (!is(T == class))
283 {
284     if (p !is null)
285     {
286         destroy(p);
287 
288         static if (hasIndirections!T)
289             GC.removeRange(cast(void*)p);
290 
291         free(cast(void*)p);
292     }
293 }
294 
295 unittest
296 {
297     class A
298     {
299         int _i;
300         this(int i)
301         {
302             _i = i;
303         }
304     }
305 
306     struct B
307     {
308         int i;
309     }
310 
311     void testMallocEmplace()
312     {
313         A a = mallocEmplace!A(4);
314         destroyFree(a);
315 
316         B* b = mallocEmplace!B(5);
317         destroyFree(b);
318     }
319 
320     testMallocEmplace();
321 }
322 
323 version( D_InlineAsm_X86 )
324 {
325     version = AsmX86;
326 }
327 else version( D_InlineAsm_X86_64 )
328 {
329     version = AsmX86;
330 }
331 
332 /// Inserts a breakpoint instruction. useful to trigger the debugger.
333 void debugBreak() nothrow @nogc
334 {
335     version( AsmX86 )
336     {
337         asm nothrow @nogc 
338         {
339             int 3; 
340         }
341     }
342     else version( GNU )
343     {
344         // __builtin_trap() is not the same thing unfortunately
345         asm
346         {
347             "int $0x03" : : : ;
348         }
349     }
350     else
351     {
352         static assert(false, "No debugBreak() for this compiler");
353     }
354 }
355 
356 auto assumeNoGC(T) (T t) if (isFunctionPointer!T || isDelegate!T)
357 {
358     enum attrs = functionAttributes!T | FunctionAttribute.nogc;
359     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
360 }
361 
362 auto assumeNothrow(T) (T t) if (isFunctionPointer!T || isDelegate!T)
363 {
364     enum attrs = functionAttributes!T | FunctionAttribute.nothrow_;
365     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
366 }
367 
368 auto assumeNothrowNoGC(T) (T t) if (isFunctionPointer!T || isDelegate!T)
369 {
370     enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_;
371     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
372 }
373 
374 unittest
375 {
376     void funcThatDoesGC()
377     {
378         throw new Exception("hello!");
379     }
380 
381     void anotherFunction() nothrow @nogc
382     {
383         assumeNothrowNoGC( (){ funcThatDoesGC(); } )();
384     }
385 
386     void aThirdFunction() @nogc
387     {
388         assumeNoGC( () { funcThatDoesGC(); } )();
389     }
390 }
391 
392 /// Must return -1 if a < b
393 ///              0 if a == b
394 ///              1 if a > b
395 alias nogcComparisonFunction(T) = int delegate(in T a, in T b) nothrow @nogc;
396 
397 /// @nogc quicksort
398 /// From the excellent: http://codereview.stackexchange.com/a/77788
399 void nogc_qsort(T)(T[] array, nogcComparisonFunction!T comparison) nothrow @nogc
400 {
401     if (array.length < 2)
402         return;
403 
404     int partition(T* arr, int left, int right) nothrow @nogc
405     {
406         immutable int mid = left + (right - left) / 2;
407         immutable T pivot = arr[mid];
408         // move the mid point value to the front.
409         swap(arr[mid],arr[left]);
410         int i = left + 1;
411         int j = right;
412         while (i <= j)
413         {
414             while(i <= j && comparison(arr[i], pivot) <= 0 )
415                 i++;
416 
417             while(i <= j && comparison(arr[j], pivot) > 0)
418                 j--;
419 
420             if (i < j)
421                 swap(arr[i], arr[j]);
422         }
423         swap(arr[i - 1], arr[left]);
424         return i - 1;
425     }
426 
427     void doQsort(T* array, int left, int right) nothrow @nogc
428     {
429         if (left >= right)
430             return;
431 
432         int part = partition(array, left, right);
433         doQsort(array, left, part - 1);
434         doQsort(array, part + 1, right);
435     }
436 
437     doQsort(array.ptr, 0, cast(int)(array.length) - 1);
438 }
439 
440 unittest
441 {
442     int[] testData = [110, 5, 10, 3, 22, 100, 1, 23];
443     nogc_qsort!int(testData, (a, b) => (a - b));
444     assert(testData == [1, 3, 5, 10, 22, 23, 100, 110]);
445 }