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 }