1 // Written in the D programming language. 2 /** 3 The C heap allocator. 4 5 Source: $(PHOBOSSRC std/experimental/allocator/mallocator.d) 6 */ 7 module std.experimental.allocator.mallocator; 8 import std.experimental.allocator.common; 9 10 /** 11 The C heap allocator. 12 */ 13 struct Mallocator 14 { 15 version (StdUnittest) @system unittest { testAllocator!(() => Mallocator.instance); } 16 17 /** 18 The alignment is a static constant equal to `platformAlignment`, which 19 ensures proper alignment for any D data type. 20 */ 21 enum uint alignment = platformAlignment; 22 23 /** 24 Standard allocator methods per the semantics defined above. The 25 `deallocate` and `reallocate` methods are `@system` because they 26 may move memory around, leaving dangling pointers in user code. Somewhat 27 paradoxically, `malloc` is `@safe` but that's only useful to safe 28 programs that can afford to leak memory allocated. 29 */ 30 @trusted @nogc nothrow pure 31 void[] allocate(size_t bytes) shared const 32 { 33 import core.memory : pureMalloc; 34 if (!bytes) return null; 35 auto p = pureMalloc(bytes); 36 return p ? p[0 .. bytes] : null; 37 } 38 39 /// Ditto 40 @system @nogc nothrow pure 41 bool deallocate(void[] b) shared const 42 { 43 import core.memory : pureFree; 44 pureFree(b.ptr); 45 return true; 46 } 47 48 /// Ditto 49 @system @nogc nothrow pure 50 bool reallocate(ref void[] b, size_t s) shared const 51 { 52 import core.memory : pureRealloc; 53 if (!s) 54 { 55 // fuzzy area in the C standard, see https://stackoverflow.com/questions/6502077/malloc-and-realloc-functions 56 // so just deallocate and nullify the pointer 57 deallocate(b); 58 b = null; 59 return true; 60 } 61 auto p = cast(ubyte*) pureRealloc(b.ptr, s); 62 if (!p) return false; 63 b = p[0 .. s]; 64 return true; 65 } 66 67 @trusted @nogc nothrow pure 68 package void[] allocateZeroed()(size_t bytes) shared const 69 { 70 import core.memory : pureCalloc; 71 if (!bytes) return null; 72 auto p = pureCalloc(1, bytes); 73 return p ? p[0 .. bytes] : null; 74 } 75 76 /** 77 Returns the global instance of this allocator type. The C heap allocator is 78 thread-safe, therefore all of its methods and `it` itself are 79 `shared`. 80 */ 81 static shared Mallocator instance; 82 } 83 84 /// 85 @nogc @system nothrow unittest 86 { 87 auto buffer = Mallocator.instance.allocate(1024 * 1024 * 4); 88 scope(exit) Mallocator.instance.deallocate(buffer); 89 //... 90 } 91 92 @nogc @system nothrow pure unittest 93 { 94 @nogc nothrow pure 95 static void test(A)() 96 { 97 int* p = null; 98 p = cast(int*) A.instance.allocate(int.sizeof); 99 scope(exit) () nothrow @nogc { A.instance.deallocate(p[0 .. int.sizeof]); }(); 100 *p = 42; 101 assert(*p == 42); 102 } 103 test!Mallocator(); 104 } 105 106 @nogc @system nothrow pure unittest 107 { 108 static void test(A)() 109 { 110 import std.experimental.allocator : make; 111 Object p = null; 112 p = A.instance.make!Object(); 113 assert(p !is null); 114 } 115 116 test!Mallocator(); 117 } 118 119 version (CRuntime_Microsoft) 120 { 121 @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t); 122 @nogc nothrow private extern(C) void _aligned_free(void *memblock); 123 @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t); 124 } 125 126 /** 127 Aligned allocator using OS-specific primitives, under a uniform API. 128 */ 129 struct AlignedMallocator 130 { 131 version (StdUnittest) @system unittest { testAllocator!(() => typeof(this).instance); } 132 133 /** 134 The default alignment is `platformAlignment`. 135 */ 136 enum uint alignment = platformAlignment; 137 138 /** 139 Forwards to $(D alignedAllocate(bytes, platformAlignment)). 140 */ 141 @trusted @nogc nothrow 142 void[] allocate(size_t bytes) shared 143 { 144 if (!bytes) return null; 145 return alignedAllocate(bytes, alignment); 146 } 147 148 /** 149 Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, 150 `posix_memalign`) on Posix and 151 $(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx, 152 `__aligned_malloc`) on Windows. 153 */ 154 version (Posix) 155 @trusted @nogc nothrow 156 void[] alignedAllocate(size_t bytes, uint a) shared 157 { 158 import core.stdc.errno : ENOMEM, EINVAL; 159 import core.sys.posix.stdlib : posix_memalign; 160 assert(a.isGoodDynamicAlignment); 161 void* result; 162 auto code = posix_memalign(&result, a, bytes); 163 164 version (OSX) 165 version (LDC_AddressSanitizer) 166 { 167 // The return value with AddressSanitizer may be -1 instead of ENOMEM 168 // or EINVAL. See https://bugs.llvm.org/show_bug.cgi?id=36510 169 if (code == -1) 170 return null; 171 } 172 if (code == ENOMEM) 173 return null; 174 175 else if (code == EINVAL) 176 { 177 assert(0, "AlignedMallocator.alignment is not a power of two " 178 ~"multiple of (void*).sizeof, according to posix_memalign!"); 179 } 180 else if (code != 0) 181 assert(0, "posix_memalign returned an unknown code!"); 182 183 else 184 return result[0 .. bytes]; 185 } 186 else version (Windows) 187 @trusted @nogc nothrow 188 void[] alignedAllocate(size_t bytes, uint a) shared 189 { 190 auto result = _aligned_malloc(bytes, a); 191 return result ? result[0 .. bytes] : null; 192 } 193 else static assert(0); 194 195 /** 196 Calls `free(b.ptr)` on Posix and 197 $(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx, 198 `__aligned_free(b.ptr)`) on Windows. 199 */ 200 version (Posix) 201 @system @nogc nothrow 202 bool deallocate(void[] b) shared 203 { 204 import core.stdc.stdlib : free; 205 free(b.ptr); 206 return true; 207 } 208 else version (Windows) 209 @system @nogc nothrow 210 bool deallocate(void[] b) shared 211 { 212 _aligned_free(b.ptr); 213 return true; 214 } 215 else static assert(0); 216 217 /** 218 Forwards to $(D alignedReallocate(b, newSize, platformAlignment)). 219 Should be used with blocks obtained with `allocate` otherwise the custom 220 alignment passed with `alignedAllocate` can be lost. 221 */ 222 @system @nogc nothrow 223 bool reallocate(ref void[] b, size_t newSize) shared 224 { 225 return alignedReallocate(b, newSize, alignment); 226 } 227 228 /** 229 On Posix there is no `realloc` for aligned memory, so `alignedReallocate` emulates 230 the needed behavior by using `alignedAllocate` to get a new block. The existing 231 block is copied to the new block and then freed. 232 On Windows, calls $(HTTPS msdn.microsoft.com/en-us/library/y69db7sx.aspx, 233 $(D __aligned_realloc(b.ptr, newSize, a))). 234 */ 235 version (Windows) 236 @system @nogc nothrow 237 bool alignedReallocate(ref void[] b, size_t s, uint a) shared 238 { 239 if (!s) 240 { 241 deallocate(b); 242 b = null; 243 return true; 244 } 245 auto p = cast(ubyte*) _aligned_realloc(b.ptr, s, a); 246 if (!p) return false; 247 b = p[0 .. s]; 248 return true; 249 } 250 251 /// ditto 252 version (Posix) 253 @system @nogc nothrow 254 bool alignedReallocate(ref void[] b, size_t s, uint a) shared 255 { 256 if (!s) 257 { 258 deallocate(b); 259 b = null; 260 return true; 261 } 262 auto p = alignedAllocate(s, a); 263 if (!p.ptr) 264 { 265 return false; 266 } 267 import std.algorithm.comparison : min; 268 const upTo = min(s, b.length); 269 p[0 .. upTo] = b[0 .. upTo]; 270 deallocate(b); 271 b = p; 272 return true; 273 } 274 275 /** 276 Returns the global instance of this allocator type. The C heap allocator is 277 thread-safe, therefore all of its methods and `instance` itself are 278 `shared`. 279 */ 280 static shared AlignedMallocator instance; 281 } 282 283 /// 284 @nogc @system nothrow unittest 285 { 286 auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4, 287 128); 288 scope(exit) AlignedMallocator.instance.deallocate(buffer); 289 //... 290 } 291 292 version (Posix) 293 @nogc @system nothrow unittest 294 { 295 // https://issues.dlang.org/show_bug.cgi?id=16398 296 // test the "pseudo" alignedReallocate for Posix 297 void[] b = AlignedMallocator.instance.alignedAllocate(16, 32); 298 (cast(ubyte[]) b)[] = ubyte(1); 299 AlignedMallocator.instance.alignedReallocate(b, 32, 32); 300 ubyte[16] o; 301 o[] = 1; 302 assert((cast(ubyte[]) b)[0 .. 16] == o); 303 AlignedMallocator.instance.alignedReallocate(b, 4, 32); 304 assert((cast(ubyte[]) b)[0 .. 3] == o[0 .. 3]); 305 AlignedMallocator.instance.alignedReallocate(b, 128, 32); 306 assert((cast(ubyte[]) b)[0 .. 3] == o[0 .. 3]); 307 AlignedMallocator.instance.deallocate(b); 308 309 void[] c; 310 AlignedMallocator.instance.alignedReallocate(c, 32, 32); 311 assert(c.ptr); 312 313 version (LDC_AddressSanitizer) {} else // AddressSanitizer does not support such large memory allocations (0x10000000000 max) 314 version (DragonFlyBSD) {} else /* FIXME: Malloc on DragonFly does not return NULL when allocating more than UINTPTR_MAX 315 * $(LINK: https://bugs.dragonflybsd.org/issues/3114, dragonfly bug report) 316 * $(LINK: https://github.com/dlang/druntime/pull/1999#discussion_r157536030, PR Discussion) */ 317 assert(!AlignedMallocator.instance.alignedReallocate(c, size_t.max, 4096)); 318 AlignedMallocator.instance.deallocate(c); 319 }