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