1 /// 2 module stdx.allocator.mallocator; 3 import stdx.allocator.common; 4 5 /** 6 The C heap allocator. 7 */ 8 struct Mallocator 9 { 10 @system unittest { testAllocator!(() => Mallocator.instance); } 11 12 /** 13 The alignment is a static constant equal to $(D platformAlignment), which 14 ensures proper alignment for any D data type. 15 */ 16 enum uint alignment = platformAlignment; 17 18 /** 19 Standard allocator methods per the semantics defined above. The 20 $(D deallocate) and $(D reallocate) methods are $(D @system) because they 21 may move memory around, leaving dangling pointers in user code. Somewhat 22 paradoxically, $(D malloc) is $(D @safe) but that's only useful to safe 23 programs that can afford to leak memory allocated. 24 */ 25 @trusted @nogc nothrow 26 static void[] allocate()(size_t bytes) 27 { 28 import core.stdc.stdlib : malloc; 29 if (!bytes) return null; 30 auto p = malloc(bytes); 31 return p ? p[0 .. bytes] : null; 32 } 33 34 /// Ditto 35 @system @nogc nothrow 36 static bool deallocate()(void[] b) 37 { 38 import core.stdc.stdlib : free; 39 free(b.ptr); 40 return true; 41 } 42 43 /// Ditto 44 @system @nogc nothrow 45 static bool reallocate()(ref void[] b, size_t s) 46 { 47 import core.stdc.stdlib : realloc; 48 if (!s) 49 { 50 // fuzzy area in the C standard, see http://goo.gl/ZpWeSE 51 // so just deallocate and nullify the pointer 52 deallocate(b); 53 b = null; 54 return true; 55 } 56 auto p = cast(ubyte*) realloc(b.ptr, s); 57 if (!p) return false; 58 b = p[0 .. s]; 59 return true; 60 } 61 62 /** 63 Returns the global instance of this allocator type. The C heap allocator is 64 thread-safe, therefore all of its methods are $(D static) and `instance` itself is 65 $(D shared). 66 */ 67 enum Mallocator instance = Mallocator(); 68 } 69 70 /// 71 @nogc nothrow 72 @system unittest 73 { 74 auto buffer = Mallocator.instance.allocate(1024 * 1024 * 4); 75 scope(exit) Mallocator.instance.deallocate(buffer); 76 //... 77 } 78 79 @nogc nothrow 80 @system unittest 81 { 82 @nogc nothrow 83 static void test(A)() 84 { 85 int* p = null; 86 p = cast(int*) A.instance.allocate(int.sizeof); 87 scope(exit) A.instance.deallocate(p[0 .. int.sizeof]); 88 *p = 42; 89 assert(*p == 42); 90 } 91 test!Mallocator(); 92 } 93 94 @nogc nothrow 95 @system unittest 96 { 97 static void test(A)() 98 { 99 import stdx.allocator : make; 100 Object p = null; 101 p = A.instance.make!Object(); 102 assert(p !is null); 103 } 104 105 test!Mallocator(); 106 } 107 108 version (Posix) 109 @nogc nothrow 110 private extern(C) int posix_memalign(void**, size_t, size_t); 111 112 version (Windows) 113 { 114 // DMD Win 32 bit, DigitalMars C standard library misses the _aligned_xxx 115 // functions family (snn.lib) 116 version(CRuntime_DigitalMars) 117 { 118 // Helper to cast the infos written before the aligned pointer 119 // this header keeps track of the size (required to realloc) and of 120 // the base ptr (required to free). 121 private struct AlignInfo 122 { 123 void* basePtr; 124 size_t size; 125 126 @nogc nothrow 127 static AlignInfo* opCall()(void* ptr) 128 { 129 return cast(AlignInfo*) (ptr - AlignInfo.sizeof); 130 } 131 } 132 133 @nogc nothrow 134 private void* _aligned_malloc()(size_t size, size_t alignment) 135 { 136 import core.stdc.stdlib : malloc; 137 size_t offset = alignment + size_t.sizeof * 2 - 1; 138 139 // unaligned chunk 140 void* basePtr = malloc(size + offset); 141 if (!basePtr) return null; 142 143 // get aligned location within the chunk 144 void* alignedPtr = cast(void**)((cast(size_t)(basePtr) + offset) 145 & ~(alignment - 1)); 146 147 // write the header before the aligned pointer 148 AlignInfo* head = AlignInfo(alignedPtr); 149 head.basePtr = basePtr; 150 head.size = size; 151 152 return alignedPtr; 153 } 154 155 @nogc nothrow 156 private void* _aligned_realloc()(void* ptr, size_t size, size_t alignment) 157 { 158 import core.stdc.stdlib : free; 159 import core.stdc..string : memcpy; 160 161 if (!ptr) return _aligned_malloc(size, alignment); 162 163 // gets the header from the exising pointer 164 AlignInfo* head = AlignInfo(ptr); 165 166 // gets a new aligned pointer 167 void* alignedPtr = _aligned_malloc(size, alignment); 168 if (!alignedPtr) 169 { 170 //to https://msdn.microsoft.com/en-us/library/ms235462.aspx 171 //see Return value: in this case the original block is unchanged 172 return null; 173 } 174 175 // copy exising data 176 memcpy(alignedPtr, ptr, head.size); 177 free(head.basePtr); 178 179 return alignedPtr; 180 } 181 182 @nogc nothrow 183 private void _aligned_free()(void *ptr) 184 { 185 import core.stdc.stdlib : free; 186 if (!ptr) return; 187 AlignInfo* head = AlignInfo(ptr); 188 free(head.basePtr); 189 } 190 191 } 192 // DMD Win 64 bit, uses microsoft standard C library which implements them 193 else 194 { 195 @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t); 196 @nogc nothrow private extern(C) void _aligned_free(void *memblock); 197 @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t); 198 } 199 } 200 201 /** 202 Aligned allocator using OS-specific primitives, under a uniform API. 203 */ 204 version (WebAssembly) {} else version = HasMemAlign; 205 206 version (HasMemAlign) 207 struct AlignedMallocator 208 { 209 @system unittest { testAllocator!(() => typeof(this).instance); } 210 211 /** 212 The default alignment is $(D platformAlignment). 213 */ 214 enum uint alignment = platformAlignment; 215 216 /** 217 Forwards to $(D alignedAllocate(bytes, platformAlignment)). 218 */ 219 @trusted @nogc nothrow 220 static void[] allocate()(size_t bytes) 221 { 222 if (!bytes) return null; 223 return alignedAllocate(bytes, alignment); 224 } 225 226 /** 227 Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, 228 $(D posix_memalign)) on Posix and 229 $(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx, 230 $(D __aligned_malloc)) on Windows. 231 */ 232 version(Posix) 233 @trusted @nogc nothrow 234 static void[] alignedAllocate()(size_t bytes, uint a) 235 { 236 import core.stdc.errno : ENOMEM, EINVAL; 237 assert(a.isGoodDynamicAlignment); 238 void* result; 239 auto code = posix_memalign(&result, a, bytes); 240 if (code == ENOMEM) 241 return null; 242 243 else if (code == EINVAL) 244 { 245 assert(0, "AlignedMallocator.alignment is not a power of two " 246 ~"multiple of (void*).sizeof, according to posix_memalign!"); 247 } 248 else if (code != 0) 249 assert(0, "posix_memalign returned an unknown code!"); 250 251 else 252 return result[0 .. bytes]; 253 } 254 else version(Windows) 255 @trusted @nogc nothrow 256 static void[] alignedAllocate()(size_t bytes, uint a) 257 { 258 auto result = _aligned_malloc(bytes, a); 259 return result ? result[0 .. bytes] : null; 260 } 261 else static assert(0); 262 263 /** 264 Calls $(D free(b.ptr)) on Posix and 265 $(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx, 266 $(D __aligned_free(b.ptr))) on Windows. 267 */ 268 version (Posix) 269 @system @nogc nothrow 270 static bool deallocate()(void[] b) 271 { 272 import core.stdc.stdlib : free; 273 free(b.ptr); 274 return true; 275 } 276 else version (Windows) 277 @system @nogc nothrow 278 static bool deallocate()(void[] b) 279 { 280 _aligned_free(b.ptr); 281 return true; 282 } 283 else static assert(0); 284 285 /** 286 On Posix, forwards to $(D realloc). On Windows, forwards to 287 $(D alignedReallocate(b, newSize, platformAlignment)). 288 */ 289 version (Posix) 290 @system @nogc nothrow 291 static bool reallocate()(ref void[] b, size_t newSize) 292 { 293 return Mallocator.instance.reallocate(b, newSize); 294 } 295 version (Windows) 296 @system @nogc nothrow 297 static bool reallocate()(ref void[] b, size_t newSize) 298 { 299 return alignedReallocate(b, newSize, alignment); 300 } 301 302 /** 303 On Posix, uses $(D alignedAllocate) and copies data around because there is 304 no realloc for aligned memory. On Windows, calls 305 $(HTTP msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx, 306 $(D __aligned_realloc(b.ptr, newSize, a))). 307 */ 308 version (Windows) 309 @system @nogc nothrow 310 static bool alignedReallocate()(ref void[] b, size_t s, uint a) 311 { 312 if (!s) 313 { 314 deallocate(b); 315 b = null; 316 return true; 317 } 318 auto p = cast(ubyte*) _aligned_realloc(b.ptr, s, a); 319 if (!p) return false; 320 b = p[0 .. s]; 321 return true; 322 } 323 324 /** 325 Returns the global instance of this allocator type. The C heap allocator is 326 thread-safe, therefore all of its methods are $(D static) and `instance` itself is 327 $(D shared). 328 */ 329 enum AlignedMallocator instance = AlignedMallocator(); 330 } 331 332 /// 333 @nogc nothrow 334 @system unittest 335 { 336 auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4, 337 128); 338 scope(exit) AlignedMallocator.instance.deallocate(buffer); 339 //... 340 } 341 342 version(unittest) version(CRuntime_DigitalMars) 343 @nogc nothrow 344 size_t addr(ref void* ptr) { return cast(size_t) ptr; } 345 346 version(CRuntime_DigitalMars) 347 @nogc nothrow 348 @system unittest 349 { 350 void* m; 351 352 m = _aligned_malloc(16, 0x10); 353 if (m) 354 { 355 assert((m.addr & 0xF) == 0); 356 _aligned_free(m); 357 } 358 359 m = _aligned_malloc(16, 0x100); 360 if (m) 361 { 362 assert((m.addr & 0xFF) == 0); 363 _aligned_free(m); 364 } 365 366 m = _aligned_malloc(16, 0x1000); 367 if (m) 368 { 369 assert((m.addr & 0xFFF) == 0); 370 _aligned_free(m); 371 } 372 373 m = _aligned_malloc(16, 0x10); 374 if (m) 375 { 376 assert((cast(size_t) m & 0xF) == 0); 377 m = _aligned_realloc(m, 32, 0x10000); 378 if (m) assert((m.addr & 0xFFFF) == 0); 379 _aligned_free(m); 380 } 381 382 m = _aligned_malloc(8, 0x10); 383 if (m) 384 { 385 *cast(ulong*) m = 0X01234567_89ABCDEF; 386 m = _aligned_realloc(m, 0x800, 0x1000); 387 if (m) assert(*cast(ulong*) m == 0X01234567_89ABCDEF); 388 _aligned_free(m); 389 } 390 }