]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/src/std/experimental/allocator/mallocator.d
libphobos: Merge phobos and druntime with upstream.
[thirdparty/gcc.git] / libphobos / src / std / experimental / allocator / mallocator.d
1 ///
2 module std.experimental.allocator.mallocator;
3 import std.experimental.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 void[] allocate(size_t bytes) shared
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 bool deallocate(void[] b) shared
37 {
38 import core.stdc.stdlib : free;
39 free(b.ptr);
40 return true;
41 }
42
43 /// Ditto
44 @system @nogc nothrow
45 bool reallocate(ref void[] b, size_t s) shared
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 and `it` itself are
65 $(D shared).
66 */
67 static shared Mallocator instance;
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 std.experimental.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 (Windows)
109 {
110 // DMD Win 32 bit, DigitalMars C standard library misses the _aligned_xxx
111 // functions family (snn.lib)
112 version (CRuntime_DigitalMars)
113 {
114 // Helper to cast the infos written before the aligned pointer
115 // this header keeps track of the size (required to realloc) and of
116 // the base ptr (required to free).
117 private struct AlignInfo
118 {
119 void* basePtr;
120 size_t size;
121
122 @nogc nothrow
123 static AlignInfo* opCall(void* ptr)
124 {
125 return cast(AlignInfo*) (ptr - AlignInfo.sizeof);
126 }
127 }
128
129 @nogc nothrow
130 private void* _aligned_malloc(size_t size, size_t alignment)
131 {
132 import core.stdc.stdlib : malloc;
133 size_t offset = alignment + size_t.sizeof * 2 - 1;
134
135 // unaligned chunk
136 void* basePtr = malloc(size + offset);
137 if (!basePtr) return null;
138
139 // get aligned location within the chunk
140 void* alignedPtr = cast(void**)((cast(size_t)(basePtr) + offset)
141 & ~(alignment - 1));
142
143 // write the header before the aligned pointer
144 AlignInfo* head = AlignInfo(alignedPtr);
145 head.basePtr = basePtr;
146 head.size = size;
147
148 return alignedPtr;
149 }
150
151 @nogc nothrow
152 private void* _aligned_realloc(void* ptr, size_t size, size_t alignment)
153 {
154 import core.stdc.stdlib : free;
155 import core.stdc.string : memcpy;
156
157 if (!ptr) return _aligned_malloc(size, alignment);
158
159 // gets the header from the exising pointer
160 AlignInfo* head = AlignInfo(ptr);
161
162 // gets a new aligned pointer
163 void* alignedPtr = _aligned_malloc(size, alignment);
164 if (!alignedPtr)
165 {
166 //to https://msdn.microsoft.com/en-us/library/ms235462.aspx
167 //see Return value: in this case the original block is unchanged
168 return null;
169 }
170
171 // copy exising data
172 memcpy(alignedPtr, ptr, head.size);
173 free(head.basePtr);
174
175 return alignedPtr;
176 }
177
178 @nogc nothrow
179 private void _aligned_free(void *ptr)
180 {
181 import core.stdc.stdlib : free;
182 if (!ptr) return;
183 AlignInfo* head = AlignInfo(ptr);
184 free(head.basePtr);
185 }
186
187 }
188 // DMD Win 64 bit, uses microsoft standard C library which implements them
189 else
190 {
191 @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t);
192 @nogc nothrow private extern(C) void _aligned_free(void *memblock);
193 @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t);
194 }
195 }
196
197 /**
198 Aligned allocator using OS-specific primitives, under a uniform API.
199 */
200 struct AlignedMallocator
201 {
202 @system unittest { testAllocator!(() => typeof(this).instance); }
203
204 /**
205 The default alignment is $(D platformAlignment).
206 */
207 enum uint alignment = platformAlignment;
208
209 /**
210 Forwards to $(D alignedAllocate(bytes, platformAlignment)).
211 */
212 @trusted @nogc nothrow
213 void[] allocate(size_t bytes) shared
214 {
215 if (!bytes) return null;
216 return alignedAllocate(bytes, alignment);
217 }
218
219 /**
220 Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html,
221 $(D posix_memalign)) on Posix and
222 $(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx,
223 $(D __aligned_malloc)) on Windows.
224 */
225 version (Posix)
226 @trusted @nogc nothrow
227 void[] alignedAllocate(size_t bytes, uint a) shared
228 {
229 import core.stdc.errno : ENOMEM, EINVAL;
230 import core.sys.posix.stdlib : posix_memalign;
231 assert(a.isGoodDynamicAlignment);
232 void* result;
233 auto code = posix_memalign(&result, a, bytes);
234 if (code == ENOMEM)
235 return null;
236
237 else if (code == EINVAL)
238 {
239 assert(0, "AlignedMallocator.alignment is not a power of two "
240 ~"multiple of (void*).sizeof, according to posix_memalign!");
241 }
242 else if (code != 0)
243 assert(0, "posix_memalign returned an unknown code!");
244
245 else
246 return result[0 .. bytes];
247 }
248 else version (Windows)
249 @trusted @nogc nothrow
250 void[] alignedAllocate(size_t bytes, uint a) shared
251 {
252 auto result = _aligned_malloc(bytes, a);
253 return result ? result[0 .. bytes] : null;
254 }
255 else static assert(0);
256
257 /**
258 Calls $(D free(b.ptr)) on Posix and
259 $(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx,
260 $(D __aligned_free(b.ptr))) on Windows.
261 */
262 version (Posix)
263 @system @nogc nothrow
264 bool deallocate(void[] b) shared
265 {
266 import core.stdc.stdlib : free;
267 free(b.ptr);
268 return true;
269 }
270 else version (Windows)
271 @system @nogc nothrow
272 bool deallocate(void[] b) shared
273 {
274 _aligned_free(b.ptr);
275 return true;
276 }
277 else static assert(0);
278
279 /**
280 On Posix, forwards to $(D realloc). On Windows, forwards to
281 $(D alignedReallocate(b, newSize, platformAlignment)).
282 */
283 version (Posix)
284 @system @nogc nothrow
285 bool reallocate(ref void[] b, size_t newSize) shared
286 {
287 return Mallocator.instance.reallocate(b, newSize);
288 }
289 version (Windows)
290 @system @nogc nothrow
291 bool reallocate(ref void[] b, size_t newSize) shared
292 {
293 return alignedReallocate(b, newSize, alignment);
294 }
295
296 /**
297 On Posix, uses $(D alignedAllocate) and copies data around because there is
298 no realloc for aligned memory. On Windows, calls
299 $(HTTP msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx,
300 $(D __aligned_realloc(b.ptr, newSize, a))).
301 */
302 version (Windows)
303 @system @nogc nothrow
304 bool alignedReallocate(ref void[] b, size_t s, uint a) shared
305 {
306 if (!s)
307 {
308 deallocate(b);
309 b = null;
310 return true;
311 }
312 auto p = cast(ubyte*) _aligned_realloc(b.ptr, s, a);
313 if (!p) return false;
314 b = p[0 .. s];
315 return true;
316 }
317
318 /**
319 Returns the global instance of this allocator type. The C heap allocator is
320 thread-safe, therefore all of its methods and `instance` itself are
321 $(D shared).
322 */
323 static shared AlignedMallocator instance;
324 }
325
326 ///
327 @nogc nothrow
328 @system unittest
329 {
330 auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4,
331 128);
332 scope(exit) AlignedMallocator.instance.deallocate(buffer);
333 //...
334 }
335
336 version (unittest) version (CRuntime_DigitalMars)
337 @nogc nothrow
338 size_t addr(ref void* ptr) { return cast(size_t) ptr; }
339
340 version (CRuntime_DigitalMars)
341 @nogc nothrow
342 @system unittest
343 {
344 void* m;
345
346 m = _aligned_malloc(16, 0x10);
347 if (m)
348 {
349 assert((m.addr & 0xF) == 0);
350 _aligned_free(m);
351 }
352
353 m = _aligned_malloc(16, 0x100);
354 if (m)
355 {
356 assert((m.addr & 0xFF) == 0);
357 _aligned_free(m);
358 }
359
360 m = _aligned_malloc(16, 0x1000);
361 if (m)
362 {
363 assert((m.addr & 0xFFF) == 0);
364 _aligned_free(m);
365 }
366
367 m = _aligned_malloc(16, 0x10);
368 if (m)
369 {
370 assert((cast(size_t) m & 0xF) == 0);
371 m = _aligned_realloc(m, 32, 0x10000);
372 if (m) assert((m.addr & 0xFFFF) == 0);
373 _aligned_free(m);
374 }
375
376 m = _aligned_malloc(8, 0x10);
377 if (m)
378 {
379 *cast(ulong*) m = 0X01234567_89ABCDEF;
380 m = _aligned_realloc(m, 0x800, 0x1000);
381 if (m) assert(*cast(ulong*) m == 0X01234567_89ABCDEF);
382 _aligned_free(m);
383 }
384 }