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 deprecated("Use dplug:core instead") @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 deprecated("Use dplug:core instead") @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 deprecated("Use dplug:core instead") @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 deprecated("Use dplug:core instead") @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     deprecated("Use dplug:core instead") @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     deprecated("Use dplug:core instead") @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     deprecated("Use dplug:core instead") @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 
128 /// Allocates and construct a struct or class object.
129 /// Returns: Newly allocated object.
130 deprecated("Use dplug:core instead") auto mallocEmplace(T, Args...)(Args args)
131 {
132     static if (is(T == class))
133         immutable size_t allocSize = __traits(classInstanceSize, T);
134     else
135         immutable size_t allocSize = T.sizeof;
136 
137     void* rawMemory = malloc(allocSize);
138     if (!rawMemory)
139         onOutOfMemoryError();
140 
141     static if (hasIndirections!T)
142         GC.addRange(rawMemory, allocSize);
143 
144     static if (is(T == class))
145     {
146         T obj = emplace!T(rawMemory[0 .. allocSize], args);
147     }
148     else
149     {
150         T* obj = cast(T*)rawMemory;
151         emplace!T(obj, args);
152     }
153 
154     return obj;
155 }
156 
157 /// Destroys and frees a class object created with $(D mallocEmplace).
158 deprecated("Use dplug:core instead") void destroyFree(T)(T p) if (is(T == class))
159 {
160     if (p !is null)
161     {
162         .destroy(p);
163 
164         static if (hasIndirections!T)
165             GC.removeRange(cast(void*)p);
166 
167         free(cast(void*)p);
168     }
169 }
170 
171 /// Destroys and frees a non-class object created with $(D mallocEmplace).
172 deprecated("Use dplug:core instead") void destroyFree(T)(T* p) if (!is(T == class))
173 {
174     if (p !is null)
175     {
176         .destroy(p);
177 
178         static if (hasIndirections!T)
179             GC.removeRange(cast(void*)p);
180 
181         free(cast(void*)p);
182     }
183 }
184 
185 version( D_InlineAsm_X86 )
186 {
187     version = AsmX86;
188 }
189 else version( D_InlineAsm_X86_64 )
190 {
191     version = AsmX86;
192 }
193 
194 /// Inserts a breakpoint instruction. useful to trigger the debugger.
195 deprecated("Use dplug:core instead") void debugBreak() nothrow @nogc
196 {
197     version( AsmX86 )
198     {
199         asm nothrow @nogc
200         {
201             int 3;
202         }
203     }
204     else version( GNU )
205     {
206         // __builtin_trap() is not the same thing unfortunately
207         asm
208         {
209             "int $0x03" : : : ;
210         }
211     }
212     else
213     {
214         static assert(false, "No debugBreak() for this compiler");
215     }
216 }
217 
218 deprecated("Use dplug:core instead") auto assumeNoGC(T) (T t) if (isFunctionPointer!T || isDelegate!T)
219 {
220     enum attrs = functionAttributes!T | FunctionAttribute.nogc;
221     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
222 }
223 
224 deprecated("Use dplug:core instead") auto assumeNothrow(T) (T t) if (isFunctionPointer!T || isDelegate!T)
225 {
226     enum attrs = functionAttributes!T | FunctionAttribute.nothrow_;
227     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
228 }
229 
230 deprecated("Use dplug:core instead") auto assumeNothrowNoGC(T) (T t) if (isFunctionPointer!T || isDelegate!T)
231 {
232     enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_;
233     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
234 }
235 
236 /// Stresses the GC for a collect to occur, can be useful to reproduce bugs
237 deprecated void stressGC() pure nothrow
238 {
239     class A { }
240     A[] a;
241     for (int i = 0; i < 1000; ++i)
242     {
243         a ~= new A;
244     }
245 }
246 
247 /// Must return -1 if a < b
248 ///              0 if a == b
249 ///              1 if a > b
250 alias nogcComparisonFunction(T) = int delegate(in T a, in T b) nothrow @nogc;
251 
252 /// @nogc quicksort
253 /// From the excellent: http://codereview.stackexchange.com/a/77788
254 deprecated("Use dplug:core instead") void nogc_qsort(T)(T[] array, nogcComparisonFunction!T comparison) nothrow @nogc
255 {
256     if (array.length < 2)
257         return;
258 
259     int partition(T* arr, int left, int right) nothrow @nogc
260     {
261         immutable int mid = left + (right - left) / 2;
262         T pivot = arr[mid];
263         // move the mid point value to the front.
264         swap(arr[mid],arr[left]);
265         int i = left + 1;
266         int j = right;
267         while (i <= j)
268         {
269             while(i <= j && comparison(arr[i], pivot) <= 0 )
270                 i++;
271 
272             while(i <= j && comparison(arr[j], pivot) > 0)
273                 j--;
274 
275             if (i < j)
276                 swap(arr[i], arr[j]);
277         }
278         swap(arr[i - 1], arr[left]);
279         return i - 1;
280     }
281 
282     void doQsort(T* array, int left, int right) nothrow @nogc
283     {
284         if (left >= right)
285             return;
286 
287         int part = partition(array, left, right);
288         doQsort(array, left, part - 1);
289         doQsort(array, part + 1, right);
290     }
291 
292     doQsort(array.ptr, 0, cast(int)(array.length) - 1);
293 }