1 // Written in the D programming language.
2 /**
3
4 High-level interface for allocators. Implements bundled allocation/creation
5 and destruction/deallocation of data including `struct`s and `class`es,
6 and also array primitives related to allocation. This module is the entry point
7 for both making use of allocators and for their documentation.
8
9 $(SCRIPT inhibitQuickIndex = 1;)
10 $(BOOKTABLE,
11 $(TR $(TH Category) $(TH Functions))
12 $(TR $(TD Make) $(TD
13 $(LREF make)
14 $(LREF makeArray)
15 $(LREF makeMultidimensionalArray)
16 ))
17 $(TR $(TD Dispose) $(TD
18 $(LREF dispose)
19 $(LREF disposeMultidimensionalArray)
20 ))
21 $(TR $(TD Modify) $(TD
22 $(LREF expandArray)
23 $(LREF shrinkArray)
24 ))
25 $(TR $(TD Global) $(TD
26 $(LREF processAllocator)
27 $(LREF theAllocator)
28 ))
29 $(TR $(TD Class interface) $(TD
30 $(LREF allocatorObject)
31 $(LREF CAllocatorImpl)
32 $(LREF IAllocator)
33 ))
34 )
35
36 Synopsis:
37 ---
38 // Allocate an int, initialize it with 42
39 int* p = theAllocator.make!int(42);
40 assert(*p == 42);
41 // Destroy and deallocate it
42 theAllocator.dispose(p);
43
44 // Allocate using the global process allocator
45 p = processAllocator.make!int(100);
46 assert(*p == 100);
47 // Destroy and deallocate
48 processAllocator.dispose(p);
49
50 // Create an array of 50 doubles initialized to -1.0
51 double[] arr = theAllocator.makeArray!double(50, -1.0);
52 // Append two zeros to it
53 theAllocator.expandArray(arr, 2, 0.0);
54 // On second thought, take that back
55 theAllocator.shrinkArray(arr, 2);
56 // Destroy and deallocate
57 theAllocator.dispose(arr);
58 ---
59
60 $(H2 Layered Structure)
61
62 D's allocators have a layered structure in both implementation and documentation:
63
64 $(OL
65 $(LI A high-level, dynamically-typed layer (described further down in this
66 module). It consists of an interface called $(LREF IAllocator), which concret;
67 allocators need to implement. The interface primitives themselves are oblivious
68 to the type of the objects being allocated; they only deal in `void[]`, by
69 necessity of the interface being dynamic (as opposed to type-parameterized).
70 Each thread has a current allocator it uses by default, which is a thread-local
71 variable $(LREF theAllocator) of type $(LREF IAllocator). The process has a
72 global _allocator called $(LREF processAllocator), also of type $(LREF
73 IAllocator). When a new thread is created, $(LREF processAllocator) is copied
74 into $(LREF theAllocator). An application can change the objects to which these
75 references point. By default, at application startup, $(LREF processAllocator)
76 refers to an object that uses D's garbage collected heap. This layer also
77 include high-level functions such as $(LREF make) and $(LREF dispose) that
78 comfortably allocate/create and respectively destroy/deallocate objects. This
79 layer is all needed for most casual uses of allocation primitives.)
80
81 $(LI A mid-level, statically-typed layer for assembling several allocators into
82 one. It uses properties of the type of the objects being created to route
83 allocation requests to possibly specialized allocators. This layer is relatively
84 thin and implemented and documented in the $(MREF
85 std,experimental,_allocator,typed) module. It allows an interested user to e.g.
86 use different allocators for arrays versus fixed-sized objects, to the end of
87 better overall performance.)
88
89 $(LI A low-level collection of highly generic $(I heap building blocks)$(MDASH)
90 Lego-like pieces that can be used to assemble application-specific allocators.
91 The real allocation smarts are occurring at this level. This layer is of
92 interest to advanced applications that want to configure their own allocators.
93 A good illustration of typical uses of these building blocks is module $(MREF
94 std,experimental,_allocator,showcase) which defines a collection of frequently-
95 used preassembled allocator objects. The implementation and documentation entry
96 point is $(MREF std,experimental,_allocator,building_blocks). By design, the
97 primitives of the static interface have the same signatures as the $(LREF
98 IAllocator) primitives but are for the most part optional and driven by static
99 introspection. The parameterized class $(LREF CAllocatorImpl) offers an
100 immediate and useful means to package a static low-level _allocator into an
101 implementation of $(LREF IAllocator).)
102
103 $(LI Core _allocator objects that interface with D's garbage collected heap
104 ($(MREF std,experimental,_allocator,gc_allocator)), the C `malloc` family
105 ($(MREF std,experimental,_allocator,mallocator)), and the OS ($(MREF
106 std,experimental,_allocator,mmap_allocator)). Most custom allocators would
107 ultimately obtain memory from one of these core allocators.)
108 )
109
110 $(H2 Idiomatic Use of $(D std.experimental._allocator))
111
112 As of this time, $(D std.experimental._allocator) is not integrated with D's
113 built-in operators that allocate memory, such as `new`, array literals, or
114 array concatenation operators. That means $(D std.experimental._allocator) is
115 opt-in$(MDASH)applications need to make explicit use of it.
116
117 For casual creation and disposal of dynamically-allocated objects, use $(LREF
118 make), $(LREF dispose), and the array-specific functions $(LREF makeArray),
119 $(LREF expandArray), and $(LREF shrinkArray). These use by default D's garbage
120 collected heap, but open the application to better configuration options. These
121 primitives work either with `theAllocator` but also with any allocator obtained
122 by combining heap building blocks. For example:
123
124 ----
125 void fun(size_t n)
126 {
127 // Use the current allocator
128 int[] a1 = theAllocator.makeArray!int(n);
129 scope(exit) theAllocator.dispose(a1);
130 ...
131 }
132 ----
133
134 To experiment with alternative allocators, set $(LREF theAllocator) for the
135 current thread. For example, consider an application that allocates many 8-byte
136 objects. These are not well supported by the default _allocator, so a
137 $(MREF_ALTTEXT free list _allocator,
138 std,experimental,_allocator,building_blocks,free_list) would be recommended.
139 To install one in `main`, the application would use:
140
141 ----
142 void main()
143 {
144 import std.experimental.allocator.building_blocks.free_list
145 : FreeList;
146 theAllocator = allocatorObject(FreeList!8());
147 ...
148 }
149 ----
150
151 $(H3 Saving the `IAllocator` Reference For Later Use)
152
153 As with any global resource, setting `theAllocator` and `processAllocator`
154 should not be done often and casually. In particular, allocating memory with
155 one allocator and deallocating with another causes undefined behavior.
156 Typically, these variables are set during application initialization phase and
157 last through the application.
158
159 To avoid this, long-lived objects that need to perform allocations,
160 reallocations, and deallocations relatively often may want to store a reference
161 to the _allocator object they use throughout their lifetime. Then, instead of
162 using `theAllocator` for internal allocation-related tasks, they'd use the
163 internally held reference. For example, consider a user-defined hash table:
164
165 ----
166 struct HashTable
167 {
168 private IAllocator _allocator;
169 this(size_t buckets, IAllocator allocator = theAllocator) {
170 this._allocator = allocator;
171 ...
172 }
173 // Getter and setter
174 IAllocator allocator() { return _allocator; }
175 void allocator(IAllocator a) { assert(empty); _allocator = a; }
176 }
177 ----
178
179 Following initialization, the `HashTable` object would consistently use its
180 $(D _allocator) object for acquiring memory. Furthermore, setting
181 $(D HashTable._allocator) to point to a different _allocator should be legal but
182 only if the object is empty; otherwise, the object wouldn't be able to
183 deallocate its existing state.
184
185 $(H3 Using Allocators without `IAllocator`)
186
187 Allocators assembled from the heap building blocks don't need to go through
188 `IAllocator` to be usable. They have the same primitives as `IAllocator` and
189 they work with $(LREF make), $(LREF makeArray), $(LREF dispose) etc. So it
190 suffice to create allocator objects wherever fit and use them appropriately:
191
192 ----
193 void fun(size_t n)
194 {
195 // Use a stack-installed allocator for up to 64KB
196 StackFront!65536 myAllocator;
197 int[] a2 = myAllocator.makeArray!int(n);
198 scope(exit) myAllocator.dispose(a2);
199 ...
200 }
201 ----
202
203 In this case, `myAllocator` does not obey the `IAllocator` interface, but
204 implements its primitives so it can work with `makeArray` by means of duck
205 typing.
206
207 One important thing to note about this setup is that statically-typed assembled
208 allocators are almost always faster than allocators that go through
209 `IAllocator`. An important rule of thumb is: "assemble allocator first, adapt
210 to `IAllocator` after". A good allocator implements intricate logic by means of
211 template assembly, and gets wrapped with `IAllocator` (usually by means of
212 $(LREF allocatorObject)) only once, at client level.
213
214 Copyright: Andrei Alexandrescu 2013-.
215
216 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
217
218 Authors: $(HTTP erdani.com, Andrei Alexandrescu)
219
220 Source: $(PHOBOSSRC std/experimental/_allocator)
221
222 */
223
224 module std.experimental.allocator;
225
226 public import std.experimental.allocator.common,
227 std.experimental.allocator.typed;
228
229 // Example in the synopsis above
230 @system unittest
231 {
232 import std.algorithm.comparison : min, max;
233 import std.experimental.allocator.building_blocks.allocator_list
234 : AllocatorList;
235 import std.experimental.allocator.building_blocks.bitmapped_block
236 : BitmappedBlock;
237 import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;
238 import std.experimental.allocator.building_blocks.free_list : FreeList;
239 import std.experimental.allocator.building_blocks.segregator : Segregator;
240 import std.experimental.allocator.gc_allocator : GCAllocator;
241
242 alias FList = FreeList!(GCAllocator, 0, unbounded);
243 alias A = Segregator!(
244 8, FreeList!(GCAllocator, 0, 8),
245 128, Bucketizer!(FList, 1, 128, 16),
246 256, Bucketizer!(FList, 129, 256, 32),
247 512, Bucketizer!(FList, 257, 512, 64),
248 1024, Bucketizer!(FList, 513, 1024, 128),
249 2048, Bucketizer!(FList, 1025, 2048, 256),
250 3584, Bucketizer!(FList, 2049, 3584, 512),
251 4072 * 1024, AllocatorList!(
252 (n) => BitmappedBlock!(4096)(
253 cast(ubyte[])(GCAllocator.instance.allocate(
254 max(n, 4072 * 1024))))),
255 GCAllocator
256 );
257 A tuMalloc;
258 auto b = tuMalloc.allocate(500);
259 assert(b.length == 500);
260 auto c = tuMalloc.allocate(113);
261 assert(c.length == 113);
262 assert(tuMalloc.expand(c, 14));
263 tuMalloc.deallocate(b);
264 tuMalloc.deallocate(c);
265 }
266
267 import std.range.primitives;
268 import std.traits;
269 import std.typecons;
270
271 /**
272 Dynamic allocator interface. Code that defines allocators ultimately implements
273 this interface. This should be used wherever a uniform type is required for
274 encapsulating various allocator implementations.
275
276 Composition of allocators is not recommended at this level due to
277 inflexibility of dynamic interfaces and inefficiencies caused by cascaded
278 multiple calls. Instead, compose allocators using the static interface defined
279 in $(A std_experimental_allocator_building_blocks.html,
280 `std.experimental.allocator.building_blocks`), then adapt the composed
281 allocator to `IAllocator` (possibly by using $(LREF CAllocatorImpl) below).
282
283 Methods returning $(D Ternary) return $(D Ternary.yes) upon success,
284 $(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not
285 implemented by the allocator instance.
286 */
287 interface IAllocator
288 {
289 /**
290 Returns the alignment offered.
291 */
292 @property uint alignment();
293
294 /**
295 Returns the good allocation size that guarantees zero internal
296 fragmentation.
297 */
298 size_t goodAllocSize(size_t s);
299
300 /**
301 Allocates `n` bytes of memory.
302 */
303 void[] allocate(size_t, TypeInfo ti = null);
304
305 /**
306 Allocates `n` bytes of memory with specified alignment `a`. Implementations
307 that do not support this primitive should always return `null`.
308 */
309 void[] alignedAllocate(size_t n, uint a);
310
311 /**
312 Allocates and returns all memory available to this allocator.
313 Implementations that do not support this primitive should always return
314 `null`.
315 */
316 void[] allocateAll();
317
318 /**
319 Expands a memory block in place and returns `true` if successful.
320 Implementations that don't support this primitive should always return
321 `false`.
322 */
323 bool expand(ref void[], size_t);
324
325 /// Reallocates a memory block.
326 bool reallocate(ref void[], size_t);
327
328 /// Reallocates a memory block with specified alignment.
329 bool alignedReallocate(ref void[] b, size_t size, uint alignment);
330
331 /**
332 Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if
333 the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership
334 cannot be determined. Implementations that don't support this primitive
335 should always return `Ternary.unknown`.
336 */
337 Ternary owns(void[] b);
338
339 /**
340 Resolves an internal pointer to the full block allocated. Implementations
341 that don't support this primitive should always return `Ternary.unknown`.
342 */
343 Ternary resolveInternalPointer(const void* p, ref void[] result);
344
345 /**
346 Deallocates a memory block. Implementations that don't support this
347 primitive should always return `false`. A simple way to check that an
348 allocator supports deallocation is to call $(D deallocate(null)).
349 */
350 bool deallocate(void[] b);
351
352 /**
353 Deallocates all memory. Implementations that don't support this primitive
354 should always return `false`.
355 */
356 bool deallocateAll();
357
358 /**
359 Returns $(D Ternary.yes) if no memory is currently allocated from this
360 allocator, $(D Ternary.no) if some allocations are currently active, or
361 $(D Ternary.unknown) if not supported.
362 */
363 Ternary empty();
364 }
365
366 /**
367 Dynamic shared allocator interface. Code that defines allocators shareable
368 across threads ultimately implements this interface. This should be used
369 wherever a uniform type is required for encapsulating various allocator
370 implementations.
371
372 Composition of allocators is not recommended at this level due to
373 inflexibility of dynamic interfaces and inefficiencies caused by cascaded
374 multiple calls. Instead, compose allocators using the static interface defined
375 in $(A std_experimental_allocator_building_blocks.html,
376 `std.experimental.allocator.building_blocks`), then adapt the composed
377 allocator to `ISharedAllocator` (possibly by using $(LREF CSharedAllocatorImpl) below).
378
379 Methods returning $(D Ternary) return $(D Ternary.yes) upon success,
380 $(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not
381 implemented by the allocator instance.
382 */
383 interface ISharedAllocator
384 {
385 /**
386 Returns the alignment offered.
387 */
388 @property uint alignment() shared;
389
390 /**
391 Returns the good allocation size that guarantees zero internal
392 fragmentation.
393 */
394 size_t goodAllocSize(size_t s) shared;
395
396 /**
397 Allocates `n` bytes of memory.
398 */
399 void[] allocate(size_t, TypeInfo ti = null) shared;
400
401 /**
402 Allocates `n` bytes of memory with specified alignment `a`. Implementations
403 that do not support this primitive should always return `null`.
404 */
405 void[] alignedAllocate(size_t n, uint a) shared;
406
407 /**
408 Allocates and returns all memory available to this allocator.
409 Implementations that do not support this primitive should always return
410 `null`.
411 */
412 void[] allocateAll() shared;
413
414 /**
415 Expands a memory block in place and returns `true` if successful.
416 Implementations that don't support this primitive should always return
417 `false`.
418 */
419 bool expand(ref void[], size_t) shared;
420
421 /// Reallocates a memory block.
422 bool reallocate(ref void[], size_t) shared;
423
424 /// Reallocates a memory block with specified alignment.
425 bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared;
426
427 /**
428 Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if
429 the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership
430 cannot be determined. Implementations that don't support this primitive
431 should always return `Ternary.unknown`.
432 */
433 Ternary owns(void[] b) shared;
434
435 /**
436 Resolves an internal pointer to the full block allocated. Implementations
437 that don't support this primitive should always return `Ternary.unknown`.
438 */
439 Ternary resolveInternalPointer(const void* p, ref void[] result) shared;
440
441 /**
442 Deallocates a memory block. Implementations that don't support this
443 primitive should always return `false`. A simple way to check that an
444 allocator supports deallocation is to call $(D deallocate(null)).
445 */
446 bool deallocate(void[] b) shared;
447
448 /**
449 Deallocates all memory. Implementations that don't support this primitive
450 should always return `false`.
451 */
452 bool deallocateAll() shared;
453
454 /**
455 Returns $(D Ternary.yes) if no memory is currently allocated from this
456 allocator, $(D Ternary.no) if some allocations are currently active, or
457 $(D Ternary.unknown) if not supported.
458 */
459 Ternary empty() shared;
460 }
461
462 private shared ISharedAllocator _processAllocator;
463 private IAllocator _threadAllocator;
464
setupThreadAllocator()465 private IAllocator setupThreadAllocator() nothrow @nogc @safe
466 {
467 /*
468 Forwards the `_threadAllocator` calls to the `processAllocator`
469 */
470 static class ThreadAllocator : IAllocator
471 {
472 override @property uint alignment()
473 {
474 return processAllocator.alignment();
475 }
476
477 override size_t goodAllocSize(size_t s)
478 {
479 return processAllocator.goodAllocSize(s);
480 }
481
482 override void[] allocate(size_t n, TypeInfo ti = null)
483 {
484 return processAllocator.allocate(n, ti);
485 }
486
487 override void[] alignedAllocate(size_t n, uint a)
488 {
489 return processAllocator.alignedAllocate(n, a);
490 }
491
492 override void[] allocateAll()
493 {
494 return processAllocator.allocateAll();
495 }
496
497 override bool expand(ref void[] b, size_t size)
498 {
499 return processAllocator.expand(b, size);
500 }
501
502 override bool reallocate(ref void[] b, size_t size)
503 {
504 return processAllocator.reallocate(b, size);
505 }
506
507 override bool alignedReallocate(ref void[] b, size_t size, uint alignment)
508 {
509 return processAllocator.alignedReallocate(b, size, alignment);
510 }
511
512 override Ternary owns(void[] b)
513 {
514 return processAllocator.owns(b);
515 }
516
517 override Ternary resolveInternalPointer(const void* p, ref void[] result)
518 {
519 return processAllocator.resolveInternalPointer(p, result);
520 }
521
522 override bool deallocate(void[] b)
523 {
524 return processAllocator.deallocate(b);
525 }
526
527 override bool deallocateAll()
528 {
529 return processAllocator.deallocateAll();
530 }
531
532 override Ternary empty()
533 {
534 return processAllocator.empty();
535 }
536 }
537
538 assert(!_threadAllocator);
539 import std.conv : emplace;
540 static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState;
541 _threadAllocator = () @trusted { return emplace!(ThreadAllocator)(_threadAllocatorState[]); } ();
542 return _threadAllocator;
543 }
544
545 /**
546 Gets/sets the allocator for the current thread. This is the default allocator
547 that should be used for allocating thread-local memory. For allocating memory
548 to be shared across threads, use $(D processAllocator) (below). By default,
549 $(D theAllocator) ultimately fetches memory from $(D processAllocator), which
550 in turn uses the garbage collected heap.
551 */
theAllocator()552 nothrow @safe @nogc @property IAllocator theAllocator()
553 {
554 auto p = _threadAllocator;
555 return p !is null ? p : setupThreadAllocator();
556 }
557
558 /// Ditto
theAllocator(IAllocator a)559 nothrow @safe @nogc @property void theAllocator(IAllocator a)
560 {
561 assert(a);
562 _threadAllocator = a;
563 }
564
565 ///
566 @system unittest
567 {
568 // Install a new allocator that is faster for 128-byte allocations.
569 import std.experimental.allocator.building_blocks.free_list : FreeList;
570 import std.experimental.allocator.gc_allocator : GCAllocator;
571 auto oldAllocator = theAllocator;
572 scope(exit) theAllocator = oldAllocator;
573 theAllocator = allocatorObject(FreeList!(GCAllocator, 128)());
574 // Use the now changed allocator to allocate an array
575 const ubyte[] arr = theAllocator.makeArray!ubyte(128);
576 assert(arr.ptr);
577 //...
578 }
579
580 /**
581 Gets/sets the allocator for the current process. This allocator must be used
582 for allocating memory shared across threads. Objects created using this
583 allocator can be cast to $(D shared).
584 */
shared(ISharedAllocator)585 @property shared(ISharedAllocator) processAllocator()
586 {
587 import std.experimental.allocator.gc_allocator : GCAllocator;
588 import std.concurrency : initOnce;
589 return initOnce!_processAllocator(
590 sharedAllocatorObject(GCAllocator.instance));
591 }
592
593 /// Ditto
processAllocator(shared ISharedAllocator a)594 @property void processAllocator(shared ISharedAllocator a)
595 {
596 assert(a);
597 _processAllocator = a;
598 }
599
600 @system unittest
601 {
602 import core.exception : AssertError;
603 import std.exception : assertThrown;
604 import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
605 import std.experimental.allocator.mallocator : Mallocator;
606
607 assert(processAllocator);
608 assert(theAllocator);
609
610 testAllocatorObject(processAllocator);
611 testAllocatorObject(theAllocator);
612
613 shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL;
614 shared ISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL);
615 assert(sharedFLObj);
616 testAllocatorObject(sharedFLObj);
617
618 // Test processAllocator setter
619 shared ISharedAllocator oldProcessAllocator = processAllocator;
620 processAllocator = sharedFLObj;
621 assert(processAllocator is sharedFLObj);
622
623 testAllocatorObject(processAllocator);
624 testAllocatorObject(theAllocator);
625 assertThrown!AssertError(processAllocator = null);
626
627 // Restore initial processAllocator state
628 processAllocator = oldProcessAllocator;
629 assert(processAllocator is oldProcessAllocator);
630
631 shared ISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL);
632 testAllocatorObject(indirectShFLObj);
633
634 IAllocator indirectMallocator = allocatorObject(&Mallocator.instance);
635 testAllocatorObject(indirectMallocator);
636 }
637
638 /**
639 Dynamically allocates (using $(D alloc)) and then creates in the memory
640 allocated an object of type $(D T), using $(D args) (if any) for its
641 initialization. Initialization occurs in the memory allocated and is otherwise
642 semantically the same as $(D T(args)).
643 (Note that using $(D alloc.make!(T[])) creates a pointer to an (empty) array
644 of $(D T)s, not an array. To use an allocator to allocate and initialize an
645 array, use $(D alloc.makeArray!T) described below.)
646
647 Params:
648 T = Type of the object being created.
649 alloc = The allocator used for getting the needed memory. It may be an object
650 implementing the static interface for allocators, or an $(D IAllocator)
651 reference.
652 args = Optional arguments used for initializing the created object. If not
653 present, the object is default constructed.
654
655 Returns: If $(D T) is a class type, returns a reference to the created $(D T)
656 object. Otherwise, returns a $(D T*) pointing to the created object. In all
657 cases, returns $(D null) if allocation failed.
658
659 Throws: If $(D T)'s constructor throws, deallocates the allocated memory and
660 propagates the exception.
661 */
make(T,Allocator,A...)662 auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args)
663 {
664 import std.algorithm.comparison : max;
665 import std.conv : emplace, emplaceRef;
666 auto m = alloc.allocate(max(stateSize!T, 1));
667 if (!m.ptr) return null;
668
669 // make can only be @safe if emplace or emplaceRef is `pure`
670 auto construct()
671 {
672 static if (is(T == class)) return emplace!T(m, args);
673 else
674 {
675 // Assume cast is safe as allocation succeeded for `stateSize!T`
676 auto p = () @trusted { return cast(T*) m.ptr; }();
677 emplaceRef(*p, args);
678 return p;
679 }
680 }
681
682 scope(failure)
683 {
684 static if (is(typeof(() pure { return construct(); })))
685 {
686 // Assume deallocation is safe because:
687 // 1) in case of failure, `m` is the only reference to this memory
688 // 2) `m` is known to originate from `alloc`
689 () @trusted { alloc.deallocate(m); }();
690 }
691 else
692 {
693 alloc.deallocate(m);
694 }
695 }
696
697 return construct();
698 }
699
700 ///
701 @system unittest
702 {
703 // Dynamically allocate one integer
704 const int* p1 = theAllocator.make!int;
705 // It's implicitly initialized with its .init value
706 assert(*p1 == 0);
707 // Dynamically allocate one double, initialize to 42.5
708 const double* p2 = theAllocator.make!double(42.5);
709 assert(*p2 == 42.5);
710
711 // Dynamically allocate a struct
712 static struct Point
713 {
714 int x, y, z;
715 }
716 // Use the generated constructor taking field values in order
717 const Point* p = theAllocator.make!Point(1, 2);
718 assert(p.x == 1 && p.y == 2 && p.z == 0);
719
720 // Dynamically allocate a class object
721 static class Customer
722 {
723 uint id = uint.max;
this()724 this() {}
this(uint id)725 this(uint id) { this.id = id; }
726 // ...
727 }
728 Customer cust = theAllocator.make!Customer;
729 assert(cust.id == uint.max); // default initialized
730 cust = theAllocator.make!Customer(42);
731 assert(cust.id == 42);
732
733 // explicit passing of outer pointer
734 static class Outer
735 {
736 int x = 3;
737 class Inner
738 {
getX()739 auto getX() { return x; }
740 }
741 }
742 auto outer = theAllocator.make!Outer();
743 auto inner = theAllocator.make!(Outer.Inner)(outer);
744 assert(outer.x == inner.getX);
745 }
746
747 @system unittest // bugzilla 15639 & 15772
748 {
749 abstract class Foo {}
750 class Bar: Foo {}
751 static assert(!is(typeof(theAllocator.make!Foo)));
752 static assert( is(typeof(theAllocator.make!Bar)));
753 }
754
755 @system unittest
756 {
test(Allocator)757 void test(Allocator)(auto ref Allocator alloc)
758 {
759 const int* a = alloc.make!int(10);
760 assert(*a == 10);
761
762 struct A
763 {
764 int x;
765 string y;
766 double z;
767 }
768
769 A* b = alloc.make!A(42);
770 assert(b.x == 42);
771 assert(b.y is null);
772 import std.math : isNaN;
773 assert(b.z.isNaN);
774
775 b = alloc.make!A(43, "44", 45);
776 assert(b.x == 43);
777 assert(b.y == "44");
778 assert(b.z == 45);
779
780 static class B
781 {
782 int x;
783 string y;
784 double z;
785 this(int _x, string _y = null, double _z = double.init)
786 {
787 x = _x;
788 y = _y;
789 z = _z;
790 }
791 }
792
793 B c = alloc.make!B(42);
794 assert(c.x == 42);
795 assert(c.y is null);
796 assert(c.z.isNaN);
797
798 c = alloc.make!B(43, "44", 45);
799 assert(c.x == 43);
800 assert(c.y == "44");
801 assert(c.z == 45);
802
803 const parray = alloc.make!(int[]);
804 assert((*parray).empty);
805 }
806
807 import std.experimental.allocator.gc_allocator : GCAllocator;
808 test(GCAllocator.instance);
809 test(theAllocator);
810 }
811
812 // Attribute propagation
813 nothrow @safe @nogc unittest
814 {
815 import std.experimental.allocator.mallocator : Mallocator;
816 alias alloc = Mallocator.instance;
817
test(T,Args...)818 void test(T, Args...)(auto ref Args args)
819 {
820 auto k = alloc.make!T(args);
821 () @trusted { alloc.dispose(k); }();
822 }
823
824 test!int;
825 test!(int*);
826 test!int(0);
827 test!(int*)(null);
828 }
829
830 // should be pure with the GCAllocator
831 /*pure nothrow*/ @safe unittest
832 {
833 import std.experimental.allocator.gc_allocator : GCAllocator;
834
835 alias alloc = GCAllocator.instance;
836
test(T,Args...)837 void test(T, Args...)(auto ref Args args)
838 {
839 auto k = alloc.make!T(args);
840 (a) @trusted { a.dispose(k); }(alloc);
841 }
842
843 test!int();
844 test!(int*);
845 test!int(0);
846 test!(int*)(null);
847 }
848
849 // Verify that making an object by calling an impure constructor is not @safe
850 nothrow @safe @nogc unittest
851 {
852 import std.experimental.allocator.mallocator : Mallocator;
thisPure853 static struct Pure { this(int) pure nothrow @nogc @safe {} }
854
855 cast(void) Mallocator.instance.make!Pure(0);
856
857 static int g = 0;
thisImpure858 static struct Impure { this(int) nothrow @nogc @safe {
859 g++;
860 } }
861 static assert(!__traits(compiles, cast(void) Mallocator.instance.make!Impure(0)));
862 }
863
864 // test failure with a pure, failing struct
865 @safe unittest
866 {
867 import std.exception : assertThrown, enforce;
868
869 // this struct can't be initialized
870 struct InvalidStruct
871 {
thisInvalidStruct872 this(int b)
873 {
874 enforce(1 == 2);
875 }
876 }
877 import std.experimental.allocator.mallocator : Mallocator;
878 assertThrown(make!InvalidStruct(Mallocator.instance, 42));
879 }
880
881 // test failure with an impure, failing struct
882 @system unittest
883 {
884 import std.exception : assertThrown, enforce;
885 static int g;
886 struct InvalidImpureStruct
887 {
thisInvalidImpureStruct888 this(int b)
889 {
890 g++;
891 enforce(1 == 2);
892 }
893 }
894 import std.experimental.allocator.mallocator : Mallocator;
895 assertThrown(make!InvalidImpureStruct(Mallocator.instance, 42));
896 }
897
fillWithMemcpy(T)898 private void fillWithMemcpy(T)(void[] array, auto ref T filler) nothrow
899 {
900 import core.stdc.string : memcpy;
901 import std.algorithm.comparison : min;
902 if (!array.length) return;
903 memcpy(array.ptr, &filler, T.sizeof);
904 // Fill the array from the initialized portion of itself exponentially.
905 for (size_t offset = T.sizeof; offset < array.length; )
906 {
907 size_t extent = min(offset, array.length - offset);
908 memcpy(array.ptr + offset, array.ptr, extent);
909 offset += extent;
910 }
911 }
912
913 @system unittest
914 {
915 int[] a;
916 fillWithMemcpy(a, 42);
917 assert(a.length == 0);
918 a = [ 1, 2, 3, 4, 5 ];
919 fillWithMemcpy(a, 42);
920 assert(a == [ 42, 42, 42, 42, 42]);
921 }
922
uninitializedFillDefault(T)923 private T[] uninitializedFillDefault(T)(T[] array) nothrow
924 {
925 T t = T.init;
926 fillWithMemcpy(array, t);
927 return array;
928 }
929
930 pure nothrow @nogc
931 @system unittest
932 {
933 static struct S { int x = 42; @disable this(this); }
934
935 int[5] expected = [42, 42, 42, 42, 42];
936 S[5] arr = void;
937 uninitializedFillDefault(arr);
938 assert((cast(int*) arr.ptr)[0 .. arr.length] == expected);
939 }
940
941 @system unittest
942 {
943 int[] a = [1, 2, 4];
944 uninitializedFillDefault(a);
945 assert(a == [0, 0, 0]);
946 }
947
948 /**
949 Create an array of $(D T) with $(D length) elements using $(D alloc). The array is either default-initialized, filled with copies of $(D init), or initialized with values fetched from `range`.
950
951 Params:
952 T = element type of the array being created
953 alloc = the allocator used for getting memory
954 length = length of the newly created array
955 init = element used for filling the array
956 range = range used for initializing the array elements
957
958 Returns:
959 The newly-created array, or $(D null) if either $(D length) was $(D 0) or
960 allocation failed.
961
962 Throws:
963 The first two overloads throw only if `alloc`'s primitives do. The
964 overloads that involve copy initialization deallocate memory and propagate the
965 exception if the copy operation throws.
966 */
makeArray(T,Allocator)967 T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length)
968 {
969 if (!length) return null;
970 auto m = alloc.allocate(T.sizeof * length);
971 if (!m.ptr) return null;
972 alias U = Unqual!T;
973 return () @trusted { return cast(T[]) uninitializedFillDefault(cast(U[]) m); }();
974 }
975
976 @system unittest
977 {
test1(A)978 void test1(A)(auto ref A alloc)
979 {
980 int[] a = alloc.makeArray!int(0);
981 assert(a.length == 0 && a.ptr is null);
982 a = alloc.makeArray!int(5);
983 assert(a.length == 5);
984 static immutable cheatsheet = [0, 0, 0, 0, 0];
985 assert(a == cheatsheet);
986 }
987
test2(A)988 void test2(A)(auto ref A alloc)
989 {
990 static struct S { int x = 42; @disable this(this); }
991 S[] arr = alloc.makeArray!S(5);
992 assert(arr.length == 5);
993 int[] arrInt = () @trusted { return (cast(int*) arr.ptr)[0 .. 5]; }();
994 static immutable res = [42, 42, 42, 42, 42];
995 assert(arrInt == res);
996 }
997
998 import std.experimental.allocator.gc_allocator : GCAllocator;
999 import std.experimental.allocator.mallocator : Mallocator;
1000 (alloc) /*pure nothrow*/ @safe { test1(alloc); test2(alloc);} (GCAllocator.instance);
1001 (alloc) nothrow @safe @nogc { test1(alloc); test2(alloc);} (Mallocator.instance);
1002 test2(theAllocator);
1003 }
1004
1005 @system unittest
1006 {
1007 import std.algorithm.comparison : equal;
1008 auto a = theAllocator.makeArray!(shared int)(5);
1009 static assert(is(typeof(a) == shared(int)[]));
1010 assert(a.length == 5);
1011 assert(a.equal([0, 0, 0, 0, 0]));
1012
1013 auto b = theAllocator.makeArray!(const int)(5);
1014 static assert(is(typeof(b) == const(int)[]));
1015 assert(b.length == 5);
1016 assert(b.equal([0, 0, 0, 0, 0]));
1017
1018 auto c = theAllocator.makeArray!(immutable int)(5);
1019 static assert(is(typeof(c) == immutable(int)[]));
1020 assert(c.length == 5);
1021 assert(c.equal([0, 0, 0, 0, 0]));
1022 }
1023
1024 private enum hasPurePostblit(T) = !hasElaborateCopyConstructor!T ||
1025 is(typeof(() pure { T.init.__xpostblit(); }));
1026
1027 private enum hasPureDtor(T) = !hasElaborateDestructor!T ||
1028 is(typeof(() pure { T.init.__xdtor(); }));
1029
1030 // `true` when postblit and destructor of T cannot escape references to itself
1031 private enum canSafelyDeallocPostRewind(T) = hasPurePostblit!T && hasPureDtor!T;
1032
1033 /// Ditto
makeArray(T,Allocator)1034 T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length,
1035 auto ref T init)
1036 {
1037 if (!length) return null;
1038 auto m = alloc.allocate(T.sizeof * length);
1039 if (!m.ptr) return null;
1040 auto result = () @trusted { return cast(T[]) m; } ();
1041 import std.traits : hasElaborateCopyConstructor;
1042 static if (hasElaborateCopyConstructor!T)
1043 {
1044 scope(failure)
1045 {
1046 static if (canSafelyDeallocPostRewind!T)
1047 () @trusted { alloc.deallocate(m); } ();
1048 else
1049 alloc.deallocate(m);
1050 }
1051
1052 size_t i = 0;
1053 static if (hasElaborateDestructor!T)
1054 {
1055 scope (failure)
1056 {
1057 foreach (j; 0 .. i)
1058 {
1059 destroy(result[j]);
1060 }
1061 }
1062 }
1063 import std.conv : emplace;
1064 for (; i < length; ++i)
1065 {
1066 emplace!T(&result[i], init);
1067 }
1068 }
1069 else
1070 {
1071 alias U = Unqual!T;
1072 () @trusted { fillWithMemcpy(cast(U[]) result, *(cast(U*) &init)); }();
1073 }
1074 return result;
1075 }
1076
1077 ///
1078 @system unittest
1079 {
1080 import std.algorithm.comparison : equal;
test(T)1081 static void test(T)()
1082 {
1083 T[] a = theAllocator.makeArray!T(2);
1084 assert(a.equal([0, 0]));
1085 a = theAllocator.makeArray!T(3, 42);
1086 assert(a.equal([42, 42, 42]));
1087 import std.range : only;
1088 a = theAllocator.makeArray!T(only(42, 43, 44));
1089 assert(a.equal([42, 43, 44]));
1090 }
1091 test!int();
1092 test!(shared int)();
1093 test!(const int)();
1094 test!(immutable int)();
1095 }
1096
1097 @system unittest
1098 {
test(A)1099 void test(A)(auto ref A alloc)
1100 {
1101 long[] a = alloc.makeArray!long(0, 42);
1102 assert(a.length == 0 && a.ptr is null);
1103 a = alloc.makeArray!long(5, 42);
1104 assert(a.length == 5);
1105 assert(a == [ 42, 42, 42, 42, 42 ]);
1106 }
1107 import std.experimental.allocator.gc_allocator : GCAllocator;
1108 (alloc) /*pure nothrow*/ @safe { test(alloc); } (GCAllocator.instance);
1109 test(theAllocator);
1110 }
1111
1112 // test failure with a pure, failing struct
1113 @safe unittest
1114 {
1115 import std.exception : assertThrown, enforce;
1116
1117 struct NoCopy
1118 {
1119 @disable this();
1120
thisNoCopy1121 this(int b){}
1122
1123 // can't be copied
thisNoCopy1124 this(this)
1125 {
1126 enforce(1 == 2);
1127 }
1128 }
1129 import std.experimental.allocator.mallocator : Mallocator;
1130 assertThrown(makeArray!NoCopy(Mallocator.instance, 10, NoCopy(42)));
1131 }
1132
1133 // test failure with an impure, failing struct
1134 @system unittest
1135 {
1136 import std.exception : assertThrown, enforce;
1137
1138 static int i = 0;
1139 struct Singleton
1140 {
1141 @disable this();
1142
thisSingleton1143 this(int b){}
1144
1145 // can't be copied
thisSingleton1146 this(this)
1147 {
1148 enforce(i++ == 0);
1149 }
1150
~thisSingleton1151 ~this()
1152 {
1153 i--;
1154 }
1155 }
1156 import std.experimental.allocator.mallocator : Mallocator;
1157 assertThrown(makeArray!Singleton(Mallocator.instance, 10, Singleton(42)));
1158 }
1159
1160 /// Ditto
1161 Unqual!(ElementEncodingType!R)[] makeArray(Allocator, R)(auto ref Allocator alloc, R range)
1162 if (isInputRange!R && !isInfinite!R)
1163 {
1164 alias T = Unqual!(ElementEncodingType!R);
1165 return makeArray!(T, Allocator, R)(alloc, range);
1166 }
1167
1168 /// Ditto
1169 T[] makeArray(T, Allocator, R)(auto ref Allocator alloc, R range)
1170 if (isInputRange!R && !isInfinite!R)
1171 {
1172 static if (isForwardRange!R || hasLength!R)
1173 {
1174 static if (hasLength!R || isNarrowString!R)
1175 immutable length = range.length;
1176 else
1177 immutable length = range.save.walkLength;
1178
1179 if (!length) return null;
1180 auto m = alloc.allocate(T.sizeof * length);
1181 if (!m.ptr) return null;
1182 auto result = () @trusted { return cast(T[]) m; } ();
1183
1184 size_t i = 0;
scope(failure)1185 scope (failure)
1186 {
1187 foreach (j; 0 .. i)
1188 {
1189 auto p = () @trusted { return cast(Unqual!T*) &result[j]; }();
1190 destroy(p);
1191 }
1192
1193 static if (canSafelyDeallocPostRewind!T)
1194 () @trusted { alloc.deallocate(m); } ();
1195 else
1196 alloc.deallocate(m);
1197 }
1198
1199 import std.conv : emplaceRef;
1200 static if (isNarrowString!R || isRandomAccessRange!R)
1201 {
1202 foreach (j; 0 .. range.length)
1203 {
1204 emplaceRef!T(result[i++], range[j]);
1205 }
1206 }
1207 else
1208 {
1209 for (; !range.empty; range.popFront, ++i)
1210 {
1211 emplaceRef!T(result[i], range.front);
1212 }
1213 }
1214
1215 return result;
1216 }
1217 else
1218 {
1219 // Estimated size
1220 size_t estimated = 8;
1221 auto m = alloc.allocate(T.sizeof * estimated);
1222 if (!m.ptr) return null;
1223 auto result = () @trusted { return cast(T[]) m; } ();
1224
1225 size_t initialized = 0;
bailout()1226 void bailout()
1227 {
1228 foreach (i; 0 .. initialized + 1)
1229 {
1230 destroy(result[i]);
1231 }
1232
1233 static if (canSafelyDeallocPostRewind!T)
1234 () @trusted { alloc.deallocate(m); } ();
1235 else
1236 alloc.deallocate(m);
1237 }
1238 scope (failure) bailout;
1239
1240 for (; !range.empty; range.popFront, ++initialized)
1241 {
1242 if (initialized == estimated)
1243 {
1244 // Need to reallocate
1245 static if (hasPurePostblit!T)
1246 auto success = () @trusted { return alloc.reallocate(m, T.sizeof * (estimated *= 2)); } ();
1247 else
1248 auto success = alloc.reallocate(m, T.sizeof * (estimated *= 2));
1249 if (!success)
1250 {
1251 bailout;
1252 return null;
1253 }
1254 result = () @trusted { return cast(T[]) m; } ();
1255 }
1256 import std.conv : emplaceRef;
1257 emplaceRef(result[initialized], range.front);
1258 }
1259
1260 if (initialized < estimated)
1261 {
1262 // Try to shrink memory, no harm if not possible
1263 static if (hasPurePostblit!T)
1264 auto success = () @trusted { return alloc.reallocate(m, T.sizeof * initialized); } ();
1265 else
1266 auto success = alloc.reallocate(m, T.sizeof * initialized);
1267 if (success)
1268 result = () @trusted { return cast(T[]) m; } ();
1269 }
1270
1271 return result[0 .. initialized];
1272 }
1273 }
1274
1275 @system unittest
1276 {
test(A)1277 void test(A)(auto ref A alloc)
1278 {
1279 long[] a = alloc.makeArray!long((int[]).init);
1280 assert(a.length == 0 && a.ptr is null);
1281 a = alloc.makeArray!long([5, 42]);
1282 assert(a.length == 2);
1283 assert(a == [ 5, 42]);
1284
1285 // we can also infer the type
1286 auto b = alloc.makeArray([4.0, 2.0]);
1287 static assert(is(typeof(b) == double[]));
1288 assert(b == [4.0, 2.0]);
1289 }
1290 import std.experimental.allocator.gc_allocator : GCAllocator;
1291 (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
1292 test(theAllocator);
1293 }
1294
1295 // infer types for strings
1296 @system unittest
1297 {
test(A)1298 void test(A)(auto ref A alloc)
1299 {
1300 auto c = alloc.makeArray("fooπ");
1301 static assert(is(typeof(c) == char[]));
1302 assert(c == "fooπ");
1303
1304 auto d = alloc.makeArray("fooπ"d);
1305 static assert(is(typeof(d) == dchar[]));
1306 assert(d == "fooπ");
1307
1308 auto w = alloc.makeArray("fooπ"w);
1309 static assert(is(typeof(w) == wchar[]));
1310 assert(w == "fooπ");
1311 }
1312
1313 import std.experimental.allocator.gc_allocator : GCAllocator;
1314 (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
1315 test(theAllocator);
1316 }
1317
1318 /*pure*/ nothrow @safe unittest
1319 {
1320 import std.algorithm.comparison : equal;
1321 import std.experimental.allocator.gc_allocator : GCAllocator;
1322 import std.internal.test.dummyrange;
1323 import std.range : iota;
foreach(DummyType;AllDummyRanges)1324 foreach (DummyType; AllDummyRanges)
1325 {
1326 (alloc) pure nothrow @safe
1327 {
1328 DummyType d;
1329 auto arr = alloc.makeArray(d);
1330 assert(arr.length == 10);
1331 assert(arr.equal(iota(1, 11)));
1332 } (GCAllocator.instance);
1333 }
1334 }
1335
1336 // test failure with a pure, failing struct
1337 @safe unittest
1338 {
1339 import std.exception : assertThrown, enforce;
1340
1341 struct NoCopy
1342 {
1343 int b;
1344
1345 @disable this();
1346
thisNoCopy1347 this(int b)
1348 {
1349 this.b = b;
1350 }
1351
1352 // can't be copied
thisNoCopy1353 this(this)
1354 {
1355 enforce(b < 3, "there can only be three elements");
1356 }
1357 }
1358 import std.experimental.allocator.mallocator : Mallocator;
1359 auto arr = [NoCopy(1), NoCopy(2), NoCopy(3)];
1360 assertThrown(makeArray!NoCopy(Mallocator.instance, arr));
1361
1362 struct NoCopyRange
1363 {
1364 static j = 0;
emptyNoCopyRange1365 bool empty()
1366 {
1367 return j > 5;
1368 }
1369
frontNoCopyRange1370 auto front()
1371 {
1372 return NoCopy(j);
1373 }
1374
popFrontNoCopyRange1375 void popFront()
1376 {
1377 j++;
1378 }
1379 }
1380 assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange()));
1381 }
1382
1383 // test failure with an impure, failing struct
1384 @system unittest
1385 {
1386 import std.exception : assertThrown, enforce;
1387
1388 static i = 0;
1389 static maxElements = 2;
1390 struct NoCopy
1391 {
1392 int val;
1393 @disable this();
1394
thisNoCopy1395 this(int b){
1396 this.val = i++;
1397 }
1398
1399 // can't be copied
thisNoCopy1400 this(this)
1401 {
1402 enforce(i++ < maxElements, "there can only be four elements");
1403 }
1404 }
1405
1406 import std.experimental.allocator.mallocator : Mallocator;
1407 auto arr = [NoCopy(1), NoCopy(2)];
1408 assertThrown(makeArray!NoCopy(Mallocator.instance, arr));
1409
1410 // allow more copies and thus force reallocation
1411 i = 0;
1412 maxElements = 30;
1413 static j = 0;
1414
1415 struct NoCopyRange
1416 {
emptyNoCopyRange1417 bool empty()
1418 {
1419 return j > 100;
1420 }
1421
frontNoCopyRange1422 auto front()
1423 {
1424 return NoCopy(1);
1425 }
1426
popFrontNoCopyRange1427 void popFront()
1428 {
1429 j++;
1430 }
1431 }
1432 assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange()));
1433
1434 maxElements = 300;
1435 auto arr2 = makeArray!NoCopy(Mallocator.instance, NoCopyRange());
1436
1437 import std.algorithm.comparison : equal;
1438 import std.algorithm.iteration : map;
1439 import std.range : iota;
1440 assert(arr2.map!`a.val`.equal(iota(32, 204, 2)));
1441 }
1442
version(unittest)1443 version (unittest)
1444 {
1445 private struct ForcedInputRange
1446 {
1447 int[]* array;
1448 pure nothrow @safe @nogc:
1449 bool empty() { return !array || (*array).empty; }
1450 ref int front() { return (*array)[0]; }
1451 void popFront() { *array = (*array)[1 .. $]; }
1452 }
1453 }
1454
1455 @system unittest
1456 {
1457 import std.array : array;
1458 import std.range : iota;
1459 int[] arr = iota(10).array;
1460
1461 void test(A)(auto ref A alloc)
1462 {
1463 ForcedInputRange r;
1464 long[] a = alloc.makeArray!long(r);
1465 assert(a.length == 0 && a.ptr is null);
1466 auto arr2 = arr;
1467 r.array = () @trusted { return &arr2; } ();
1468 a = alloc.makeArray!long(r);
1469 assert(a.length == 10);
1470 assert(a == iota(10).array);
1471 }
1472 import std.experimental.allocator.gc_allocator : GCAllocator;
1473 (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
1474 test(theAllocator);
1475 }
1476
1477 /**
1478 Grows $(D array) by appending $(D delta) more elements. The needed memory is
1479 allocated using $(D alloc). The extra elements added are either default-
1480 initialized, filled with copies of $(D init), or initialized with values
1481 fetched from `range`.
1482
1483 Params:
1484 T = element type of the array being created
1485 alloc = the allocator used for getting memory
1486 array = a reference to the array being grown
1487 delta = number of elements to add (upon success the new length of $(D array) is
1488 $(D array.length + delta))
1489 init = element used for filling the array
1490 range = range used for initializing the array elements
1491
1492 Returns:
1493 $(D true) upon success, $(D false) if memory could not be allocated. In the
1494 latter case $(D array) is left unaffected.
1495
1496 Throws:
1497 The first two overloads throw only if `alloc`'s primitives do. The
1498 overloads that involve copy initialization deallocate memory and propagate the
1499 exception if the copy operation throws.
1500 */
1501 bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array,
1502 size_t delta)
1503 {
1504 if (!delta) return true;
1505 if (array is null) return false;
1506 immutable oldLength = array.length;
1507 void[] buf = array;
1508 if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false;
1509 array = cast(T[]) buf;
1510 array[oldLength .. $].uninitializedFillDefault;
1511 return true;
1512 }
1513
1514 @system unittest
1515 {
1516 void test(A)(auto ref A alloc)
1517 {
1518 auto arr = alloc.makeArray!int([1, 2, 3]);
1519 assert(alloc.expandArray(arr, 3));
1520 assert(arr == [1, 2, 3, 0, 0, 0]);
1521 }
1522 import std.experimental.allocator.gc_allocator : GCAllocator;
1523 test(GCAllocator.instance);
1524 test(theAllocator);
1525 }
1526
1527 /// Ditto
1528 bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array,
1529 size_t delta, auto ref T init)
1530 {
1531 if (!delta) return true;
1532 if (array is null) return false;
1533 void[] buf = array;
1534 if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false;
1535 immutable oldLength = array.length;
1536 array = cast(T[]) buf;
1537 scope(failure) array[oldLength .. $].uninitializedFillDefault;
1538 import std.algorithm.mutation : uninitializedFill;
1539 array[oldLength .. $].uninitializedFill(init);
1540 return true;
1541 }
1542
1543 @system unittest
1544 {
1545 void test(A)(auto ref A alloc)
1546 {
1547 auto arr = alloc.makeArray!int([1, 2, 3]);
1548 assert(alloc.expandArray(arr, 3, 1));
1549 assert(arr == [1, 2, 3, 1, 1, 1]);
1550 }
1551 import std.experimental.allocator.gc_allocator : GCAllocator;
1552 test(GCAllocator.instance);
1553 test(theAllocator);
1554 }
1555
1556 /// Ditto
1557 bool expandArray(T, Allocator, R)(auto ref Allocator alloc, ref T[] array,
1558 R range)
1559 if (isInputRange!R)
1560 {
1561 if (array is null) return false;
1562 static if (isForwardRange!R)
1563 {
1564 immutable delta = walkLength(range.save);
1565 if (!delta) return true;
1566 immutable oldLength = array.length;
1567
1568 // Reallocate support memory
1569 void[] buf = array;
1570 if (!alloc.reallocate(buf, buf.length + T.sizeof * delta))
1571 {
1572 return false;
1573 }
1574 array = cast(T[]) buf;
1575 // At this point we're committed to the new length.
1576
1577 auto toFill = array[oldLength .. $];
1578 scope (failure)
1579 {
1580 // Fill the remainder with default-constructed data
1581 toFill.uninitializedFillDefault;
1582 }
1583
1584 for (; !range.empty; range.popFront, toFill.popFront)
1585 {
1586 assert(!toFill.empty);
1587 import std.conv : emplace;
1588 emplace!T(&toFill.front, range.front);
1589 }
1590 assert(toFill.empty);
1591 }
1592 else
1593 {
1594 scope(failure)
1595 {
1596 // The last element didn't make it, fill with default
1597 array[$ - 1 .. $].uninitializedFillDefault;
1598 }
1599 void[] buf = array;
1600 for (; !range.empty; range.popFront)
1601 {
1602 if (!alloc.reallocate(buf, buf.length + T.sizeof))
1603 {
1604 array = cast(T[]) buf;
1605 return false;
1606 }
1607 import std.conv : emplace;
1608 emplace!T(buf[$ - T.sizeof .. $], range.front);
1609 }
1610
1611 array = cast(T[]) buf;
1612 }
1613 return true;
1614 }
1615
1616 ///
1617 @system unittest
1618 {
1619 auto arr = theAllocator.makeArray!int([1, 2, 3]);
1620 assert(theAllocator.expandArray(arr, 2));
1621 assert(arr == [1, 2, 3, 0, 0]);
1622 import std.range : only;
1623 assert(theAllocator.expandArray(arr, only(4, 5)));
1624 assert(arr == [1, 2, 3, 0, 0, 4, 5]);
1625 }
1626
1627 @system unittest
1628 {
1629 auto arr = theAllocator.makeArray!int([1, 2, 3]);
1630 ForcedInputRange r;
1631 int[] b = [ 1, 2, 3, 4 ];
1632 auto temp = b;
1633 r.array = &temp;
1634 assert(theAllocator.expandArray(arr, r));
1635 assert(arr == [1, 2, 3, 1, 2, 3, 4]);
1636 }
1637
1638 /**
1639 Shrinks an array by $(D delta) elements.
1640
1641 If $(D array.length < delta), does nothing and returns `false`. Otherwise,
1642 destroys the last $(D array.length - delta) elements in the array and then
1643 reallocates the array's buffer. If reallocation fails, fills the array with
1644 default-initialized data.
1645
1646 Params:
1647 T = element type of the array being created
1648 alloc = the allocator used for getting memory
1649 array = a reference to the array being shrunk
1650 delta = number of elements to remove (upon success the new length of $(D array) is $(D array.length - delta))
1651
1652 Returns:
1653 `true` upon success, `false` if memory could not be reallocated. In the latter
1654 case, the slice $(D array[$ - delta .. $]) is left with default-initialized
1655 elements.
1656
1657 Throws:
1658 The first two overloads throw only if `alloc`'s primitives do. The
1659 overloads that involve copy initialization deallocate memory and propagate the
1660 exception if the copy operation throws.
1661 */
1662 bool shrinkArray(T, Allocator)(auto ref Allocator alloc,
1663 ref T[] array, size_t delta)
1664 {
1665 if (delta > array.length) return false;
1666
1667 // Destroy elements. If a destructor throws, fill the already destroyed
1668 // stuff with the default initializer.
1669 {
1670 size_t destroyed;
1671 scope(failure)
1672 {
1673 array[$ - delta .. $][0 .. destroyed].uninitializedFillDefault;
1674 }
1675 foreach (ref e; array[$ - delta .. $])
1676 {
1677 e.destroy;
1678 ++destroyed;
1679 }
1680 }
1681
1682 if (delta == array.length)
1683 {
1684 alloc.deallocate(array);
1685 array = null;
1686 return true;
1687 }
1688
1689 void[] buf = array;
1690 if (!alloc.reallocate(buf, buf.length - T.sizeof * delta))
1691 {
1692 // urgh, at least fill back with default
1693 array[$ - delta .. $].uninitializedFillDefault;
1694 return false;
1695 }
1696 array = cast(T[]) buf;
1697 return true;
1698 }
1699
1700 ///
1701 @system unittest
1702 {
1703 int[] a = theAllocator.makeArray!int(100, 42);
1704 assert(a.length == 100);
1705 assert(theAllocator.shrinkArray(a, 98));
1706 assert(a.length == 2);
1707 assert(a == [42, 42]);
1708 }
1709
1710 @system unittest
1711 {
1712 void test(A)(auto ref A alloc)
1713 {
1714 long[] a = alloc.makeArray!long((int[]).init);
1715 assert(a.length == 0 && a.ptr is null);
1716 a = alloc.makeArray!long(100, 42);
1717 assert(alloc.shrinkArray(a, 98));
1718 assert(a.length == 2);
1719 assert(a == [ 42, 42]);
1720 }
1721 import std.experimental.allocator.gc_allocator : GCAllocator;
1722 test(GCAllocator.instance);
1723 test(theAllocator);
1724 }
1725
1726 /**
1727
1728 Destroys and then deallocates (using $(D alloc)) the object pointed to by a
1729 pointer, the class object referred to by a $(D class) or $(D interface)
1730 reference, or an entire array. It is assumed the respective entities had been
1731 allocated with the same allocator.
1732
1733 */
1734 void dispose(A, T)(auto ref A alloc, T* p)
1735 {
1736 static if (hasElaborateDestructor!T)
1737 {
1738 destroy(*p);
1739 }
1740 alloc.deallocate((cast(void*) p)[0 .. T.sizeof]);
1741 }
1742
1743 /// Ditto
1744 void dispose(A, T)(auto ref A alloc, T p)
1745 if (is(T == class) || is(T == interface))
1746 {
1747 if (!p) return;
1748 static if (is(T == interface))
1749 {
1750 version (Windows)
1751 {
1752 import core.sys.windows.unknwn : IUnknown;
1753 static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in "
1754 ~ __PRETTY_FUNCTION__);
1755 }
1756 auto ob = cast(Object) p;
1757 }
1758 else
1759 alias ob = p;
1760 auto support = (cast(void*) ob)[0 .. typeid(ob).initializer.length];
1761 destroy(p);
1762 alloc.deallocate(support);
1763 }
1764
1765 /// Ditto
1766 void dispose(A, T)(auto ref A alloc, T[] array)
1767 {
1768 static if (hasElaborateDestructor!(typeof(array[0])))
1769 {
1770 foreach (ref e; array)
1771 {
1772 destroy(e);
1773 }
1774 }
1775 alloc.deallocate(array);
1776 }
1777
1778 @system unittest
1779 {
1780 static int x;
1781 static interface I
1782 {
1783 void method();
1784 }
1785 static class A : I
1786 {
1787 int y;
1788 override void method() { x = 21; }
1789 ~this() { x = 42; }
1790 }
1791 static class B : A
1792 {
1793 }
1794 auto a = theAllocator.make!A;
1795 a.method();
1796 assert(x == 21);
1797 theAllocator.dispose(a);
1798 assert(x == 42);
1799
1800 B b = theAllocator.make!B;
1801 b.method();
1802 assert(x == 21);
1803 theAllocator.dispose(b);
1804 assert(x == 42);
1805
1806 I i = theAllocator.make!B;
1807 i.method();
1808 assert(x == 21);
1809 theAllocator.dispose(i);
1810 assert(x == 42);
1811
1812 int[] arr = theAllocator.makeArray!int(43);
1813 theAllocator.dispose(arr);
1814 }
1815
1816 @system unittest //bugzilla 15721
1817 {
1818 import std.experimental.allocator.mallocator : Mallocator;
1819
1820 interface Foo {}
1821 class Bar: Foo {}
1822
1823 Bar bar;
1824 Foo foo;
1825 bar = Mallocator.instance.make!Bar;
1826 foo = cast(Foo) bar;
1827 Mallocator.instance.dispose(foo);
1828 }
1829
1830 /**
1831 Allocates a multidimensional array of elements of type T.
1832
1833 Params:
1834 N = number of dimensions
1835 T = element type of an element of the multidimensional arrat
1836 alloc = the allocator used for getting memory
1837 lengths = static array containing the size of each dimension
1838
1839 Returns:
1840 An N-dimensional array with individual elements of type T.
1841 */
1842 auto makeMultidimensionalArray(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...)
1843 {
1844 static if (N == 1)
1845 {
1846 return makeArray!T(alloc, lengths[0]);
1847 }
1848 else
1849 {
1850 alias E = typeof(makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $]));
1851 auto ret = makeArray!E(alloc, lengths[0]);
1852 foreach (ref e; ret)
1853 e = makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $]);
1854 return ret;
1855 }
1856 }
1857
1858 ///
1859 @system unittest
1860 {
1861 import std.experimental.allocator.mallocator : Mallocator;
1862
1863 auto mArray = Mallocator.instance.makeMultidimensionalArray!int(2, 3, 6);
1864
1865 // deallocate when exiting scope
1866 scope(exit)
1867 {
1868 Mallocator.instance.disposeMultidimensionalArray(mArray);
1869 }
1870
1871 assert(mArray.length == 2);
1872 foreach (lvl2Array; mArray)
1873 {
1874 assert(lvl2Array.length == 3);
1875 foreach (lvl3Array; lvl2Array)
1876 assert(lvl3Array.length == 6);
1877 }
1878 }
1879
1880 /**
1881 Destroys and then deallocates a multidimensional array, assuming it was
1882 created with makeMultidimensionalArray and the same allocator was used.
1883
1884 Params:
1885 T = element type of an element of the multidimensional array
1886 alloc = the allocator used for getting memory
1887 array = the multidimensional array that is to be deallocated
1888 */
1889 void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, T[] array)
1890 {
1891 static if (isArray!T)
1892 {
1893 foreach (ref e; array)
1894 disposeMultidimensionalArray(alloc, e);
1895 }
1896
1897 dispose(alloc, array);
1898 }
1899
1900 ///
1901 @system unittest
1902 {
1903 struct TestAllocator
1904 {
1905 import std.experimental.allocator.common : platformAlignment;
1906 import std.experimental.allocator.mallocator : Mallocator;
1907
1908 alias allocator = Mallocator.instance;
1909
1910 private static struct ByteRange
1911 {
1912 void* ptr;
1913 size_t length;
1914 }
1915
1916 private ByteRange[] _allocations;
1917
1918 enum uint alignment = platformAlignment;
1919
1920 void[] allocate(size_t numBytes)
1921 {
1922 auto ret = allocator.allocate(numBytes);
1923 _allocations ~= ByteRange(ret.ptr, ret.length);
1924 return ret;
1925 }
1926
1927 bool deallocate(void[] bytes)
1928 {
1929 import std.algorithm.mutation : remove;
1930 import std.algorithm.searching : canFind;
1931
1932 bool pred(ByteRange other)
1933 { return other.ptr == bytes.ptr && other.length == bytes.length; }
1934
1935 assert(_allocations.canFind!pred);
1936
1937 _allocations = _allocations.remove!pred;
1938 return allocator.deallocate(bytes);
1939 }
1940
1941 ~this()
1942 {
1943 assert(!_allocations.length);
1944 }
1945 }
1946
1947 TestAllocator allocator;
1948
1949 auto mArray = allocator.makeMultidimensionalArray!int(2, 3, 5, 6, 7, 2);
1950
1951 allocator.disposeMultidimensionalArray(mArray);
1952 }
1953
1954 /**
1955
1956 Returns a dynamically-typed $(D CAllocator) built around a given statically-
1957 typed allocator $(D a) of type $(D A). Passing a pointer to the allocator
1958 creates a dynamic allocator around the allocator pointed to by the pointer,
1959 without attempting to copy or move it. Passing the allocator by value or
1960 reference behaves as follows.
1961
1962 $(UL
1963 $(LI If $(D A) has no state, the resulting object is allocated in static
1964 shared storage.)
1965 $(LI If $(D A) has state and is copyable, the result will store a copy of it
1966 within. The result itself is allocated in its own statically-typed allocator.)
1967 $(LI If $(D A) has state and is not copyable, the result will move the
1968 passed-in argument into the result. The result itself is allocated in its own
1969 statically-typed allocator.)
1970 )
1971
1972 */
1973 CAllocatorImpl!A allocatorObject(A)(auto ref A a)
1974 if (!isPointer!A)
1975 {
1976 import std.conv : emplace;
1977 static if (stateSize!A == 0)
1978 {
1979 enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof);
1980 static __gshared ulong[s] state;
1981 static __gshared CAllocatorImpl!A result;
1982 if (!result)
1983 {
1984 // Don't care about a few races
1985 result = emplace!(CAllocatorImpl!A)(state[]);
1986 }
1987 assert(result);
1988 return result;
1989 }
1990 else static if (is(typeof({ A b = a; A c = b; }))) // copyable
1991 {
1992 auto state = a.allocate(stateSize!(CAllocatorImpl!A));
1993 import std.traits : hasMember;
1994 static if (hasMember!(A, "deallocate"))
1995 {
1996 scope(failure) a.deallocate(state);
1997 }
1998 return cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state);
1999 }
2000 else // the allocator object is not copyable
2001 {
2002 // This is sensitive... create on the stack and then move
2003 enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof);
2004 ulong[s] state;
2005 import std.algorithm.mutation : move;
2006 emplace!(CAllocatorImpl!A)(state[], move(a));
2007 auto dynState = a.allocate(stateSize!(CAllocatorImpl!A));
2008 // Bitblast the object in its final destination
2009 dynState[] = state[];
2010 return cast(CAllocatorImpl!A) dynState.ptr;
2011 }
2012 }
2013
2014 /// Ditto
2015 CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa)
2016 {
2017 assert(pa);
2018 import std.conv : emplace;
2019 auto state = pa.allocate(stateSize!(CAllocatorImpl!(A, Yes.indirect)));
2020 import std.traits : hasMember;
2021 static if (hasMember!(A, "deallocate"))
2022 {
2023 scope(failure) pa.deallocate(state);
2024 }
2025 return emplace!(CAllocatorImpl!(A, Yes.indirect))
2026 (state, pa);
2027 }
2028
2029 ///
2030 @system unittest
2031 {
2032 import std.experimental.allocator.mallocator : Mallocator;
2033 IAllocator a = allocatorObject(Mallocator.instance);
2034 auto b = a.allocate(100);
2035 assert(b.length == 100);
2036 assert(a.deallocate(b));
2037
2038 // The in-situ region must be used by pointer
2039 import std.experimental.allocator.building_blocks.region : InSituRegion;
2040 auto r = InSituRegion!1024();
2041 a = allocatorObject(&r);
2042 b = a.allocate(200);
2043 assert(b.length == 200);
2044 // In-situ regions can deallocate the last allocation
2045 assert(a.deallocate(b));
2046 }
2047
2048 /**
2049
2050 Returns a dynamically-typed $(D CSharedAllocator) built around a given statically-
2051 typed allocator $(D a) of type $(D A). Passing a pointer to the allocator
2052 creates a dynamic allocator around the allocator pointed to by the pointer,
2053 without attempting to copy or move it. Passing the allocator by value or
2054 reference behaves as follows.
2055
2056 $(UL
2057 $(LI If $(D A) has no state, the resulting object is allocated in static
2058 shared storage.)
2059 $(LI If $(D A) has state and is copyable, the result will store a copy of it
2060 within. The result itself is allocated in its own statically-typed allocator.)
2061 $(LI If $(D A) has state and is not copyable, the result will move the
2062 passed-in argument into the result. The result itself is allocated in its own
2063 statically-typed allocator.)
2064 )
2065
2066 */
2067 shared(CSharedAllocatorImpl!A) sharedAllocatorObject(A)(auto ref A a)
2068 if (!isPointer!A)
2069 {
2070 import std.conv : emplace;
2071 static if (stateSize!A == 0)
2072 {
2073 enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof);
2074 static __gshared ulong[s] state;
2075 static shared CSharedAllocatorImpl!A result;
2076 if (!result)
2077 {
2078 // Don't care about a few races
2079 result = cast(shared
2080 CSharedAllocatorImpl!A)(emplace!(CSharedAllocatorImpl!A)(state[]));
2081 }
2082 assert(result);
2083 return result;
2084 }
2085 else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable
2086 {
2087 auto state = a.allocate(stateSize!(CSharedAllocatorImpl!A));
2088 import std.traits : hasMember;
2089 static if (hasMember!(A, "deallocate"))
2090 {
2091 scope(failure) a.deallocate(state);
2092 }
2093 return emplace!(shared CSharedAllocatorImpl!A)(state);
2094 }
2095 else // the allocator object is not copyable
2096 {
2097 assert(0, "Not yet implemented");
2098 }
2099 }
2100
2101 /// Ditto
2102 shared(CSharedAllocatorImpl!(A, Yes.indirect)) sharedAllocatorObject(A)(A* pa)
2103 {
2104 assert(pa);
2105 import std.conv : emplace;
2106 auto state = pa.allocate(stateSize!(CSharedAllocatorImpl!(A, Yes.indirect)));
2107 import std.traits : hasMember;
2108 static if (hasMember!(A, "deallocate"))
2109 {
2110 scope(failure) pa.deallocate(state);
2111 }
2112 return emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa);
2113 }
2114
2115
2116 /**
2117
2118 Implementation of `IAllocator` using `Allocator`. This adapts a
2119 statically-built allocator type to `IAllocator` that is directly usable by
2120 non-templated code.
2121
2122 Usually `CAllocatorImpl` is used indirectly by calling $(LREF theAllocator).
2123 */
2124 class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
2125 : IAllocator
2126 {
2127 import std.traits : hasMember;
2128
2129 /**
2130 The implementation is available as a public member.
2131 */
2132 static if (indirect)
2133 {
2134 private Allocator* pimpl;
2135 ref Allocator impl()
2136 {
2137 return *pimpl;
2138 }
2139 this(Allocator* pa)
2140 {
2141 pimpl = pa;
2142 }
2143 }
2144 else
2145 {
2146 static if (stateSize!Allocator) Allocator impl;
2147 else alias impl = Allocator.instance;
2148 }
2149
2150 /// Returns `impl.alignment`.
2151 override @property uint alignment()
2152 {
2153 return impl.alignment;
2154 }
2155
2156 /**
2157 Returns `impl.goodAllocSize(s)`.
2158 */
2159 override size_t goodAllocSize(size_t s)
2160 {
2161 return impl.goodAllocSize(s);
2162 }
2163
2164 /**
2165 Returns `impl.allocate(s)`.
2166 */
2167 override void[] allocate(size_t s, TypeInfo ti = null)
2168 {
2169 return impl.allocate(s);
2170 }
2171
2172 /**
2173 If `impl.alignedAllocate` exists, calls it and returns the result.
2174 Otherwise, always returns `null`.
2175 */
2176 override void[] alignedAllocate(size_t s, uint a)
2177 {
2178 static if (hasMember!(Allocator, "alignedAllocate"))
2179 return impl.alignedAllocate(s, a);
2180 else
2181 return null;
2182 }
2183
2184 /**
2185 If `Allocator` implements `owns`, forwards to it. Otherwise, returns
2186 `Ternary.unknown`.
2187 */
2188 override Ternary owns(void[] b)
2189 {
2190 static if (hasMember!(Allocator, "owns")) return impl.owns(b);
2191 else return Ternary.unknown;
2192 }
2193
2194 /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise.
2195 override bool expand(ref void[] b, size_t s)
2196 {
2197 static if (hasMember!(Allocator, "expand"))
2198 return impl.expand(b, s);
2199 else
2200 return s == 0;
2201 }
2202
2203 /// Returns $(D impl.reallocate(b, s)).
2204 override bool reallocate(ref void[] b, size_t s)
2205 {
2206 return impl.reallocate(b, s);
2207 }
2208
2209 /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise.
2210 bool alignedReallocate(ref void[] b, size_t s, uint a)
2211 {
2212 static if (!hasMember!(Allocator, "alignedAllocate"))
2213 {
2214 return false;
2215 }
2216 else
2217 {
2218 return impl.alignedReallocate(b, s, a);
2219 }
2220 }
2221
2222 // Undocumented for now
2223 Ternary resolveInternalPointer(const void* p, ref void[] result)
2224 {
2225 static if (hasMember!(Allocator, "resolveInternalPointer"))
2226 {
2227 return impl.resolveInternalPointer(p, result);
2228 }
2229 else
2230 {
2231 return Ternary.unknown;
2232 }
2233 }
2234
2235 /**
2236 If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards
2237 the call.
2238 */
2239 override bool deallocate(void[] b)
2240 {
2241 static if (hasMember!(Allocator, "deallocate"))
2242 {
2243 return impl.deallocate(b);
2244 }
2245 else
2246 {
2247 return false;
2248 }
2249 }
2250
2251 /**
2252 Calls `impl.deallocateAll()` and returns the result if defined,
2253 otherwise returns `false`.
2254 */
2255 override bool deallocateAll()
2256 {
2257 static if (hasMember!(Allocator, "deallocateAll"))
2258 {
2259 return impl.deallocateAll();
2260 }
2261 else
2262 {
2263 return false;
2264 }
2265 }
2266
2267 /**
2268 Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`.
2269 */
2270 override Ternary empty()
2271 {
2272 static if (hasMember!(Allocator, "empty"))
2273 {
2274 return Ternary(impl.empty);
2275 }
2276 else
2277 {
2278 return Ternary.unknown;
2279 }
2280 }
2281
2282 /**
2283 Returns `impl.allocateAll()` if present, `null` otherwise.
2284 */
2285 override void[] allocateAll()
2286 {
2287 static if (hasMember!(Allocator, "allocateAll"))
2288 {
2289 return impl.allocateAll();
2290 }
2291 else
2292 {
2293 return null;
2294 }
2295 }
2296 }
2297
2298 /**
2299
2300 Implementation of `ISharedAllocator` using `Allocator`. This adapts a
2301 statically-built, shareable across threads, allocator type to `ISharedAllocator`
2302 that is directly usable by non-templated code.
2303
2304 Usually `CSharedAllocatorImpl` is used indirectly by calling
2305 $(LREF processAllocator).
2306 */
2307 class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
2308 : ISharedAllocator
2309 {
2310 import std.traits : hasMember;
2311
2312 /**
2313 The implementation is available as a public member.
2314 */
2315 static if (indirect)
2316 {
2317 private shared Allocator* pimpl;
2318 ref Allocator impl() shared
2319 {
2320 return *pimpl;
2321 }
2322 this(Allocator* pa) shared
2323 {
2324 pimpl = pa;
2325 }
2326 }
2327 else
2328 {
2329 static if (stateSize!Allocator) shared Allocator impl;
2330 else alias impl = Allocator.instance;
2331 }
2332
2333 /// Returns `impl.alignment`.
2334 override @property uint alignment() shared
2335 {
2336 return impl.alignment;
2337 }
2338
2339 /**
2340 Returns `impl.goodAllocSize(s)`.
2341 */
2342 override size_t goodAllocSize(size_t s) shared
2343 {
2344 return impl.goodAllocSize(s);
2345 }
2346
2347 /**
2348 Returns `impl.allocate(s)`.
2349 */
2350 override void[] allocate(size_t s, TypeInfo ti = null) shared
2351 {
2352 return impl.allocate(s);
2353 }
2354
2355 /**
2356 If `impl.alignedAllocate` exists, calls it and returns the result.
2357 Otherwise, always returns `null`.
2358 */
2359 override void[] alignedAllocate(size_t s, uint a) shared
2360 {
2361 static if (hasMember!(Allocator, "alignedAllocate"))
2362 return impl.alignedAllocate(s, a);
2363 else
2364 return null;
2365 }
2366
2367 /**
2368 If `Allocator` implements `owns`, forwards to it. Otherwise, returns
2369 `Ternary.unknown`.
2370 */
2371 override Ternary owns(void[] b) shared
2372 {
2373 static if (hasMember!(Allocator, "owns")) return impl.owns(b);
2374 else return Ternary.unknown;
2375 }
2376
2377 /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise.
2378 override bool expand(ref void[] b, size_t s) shared
2379 {
2380 static if (hasMember!(Allocator, "expand"))
2381 return impl.expand(b, s);
2382 else
2383 return s == 0;
2384 }
2385
2386 /// Returns $(D impl.reallocate(b, s)).
2387 override bool reallocate(ref void[] b, size_t s) shared
2388 {
2389 return impl.reallocate(b, s);
2390 }
2391
2392 /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise.
2393 bool alignedReallocate(ref void[] b, size_t s, uint a) shared
2394 {
2395 static if (!hasMember!(Allocator, "alignedAllocate"))
2396 {
2397 return false;
2398 }
2399 else
2400 {
2401 return impl.alignedReallocate(b, s, a);
2402 }
2403 }
2404
2405 // Undocumented for now
2406 Ternary resolveInternalPointer(const void* p, ref void[] result) shared
2407 {
2408 static if (hasMember!(Allocator, "resolveInternalPointer"))
2409 {
2410 return impl.resolveInternalPointer(p, result);
2411 }
2412 else
2413 {
2414 return Ternary.unknown;
2415 }
2416 }
2417
2418 /**
2419 If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards
2420 the call.
2421 */
2422 override bool deallocate(void[] b) shared
2423 {
2424 static if (hasMember!(Allocator, "deallocate"))
2425 {
2426 return impl.deallocate(b);
2427 }
2428 else
2429 {
2430 return false;
2431 }
2432 }
2433
2434 /**
2435 Calls `impl.deallocateAll()` and returns the result if defined,
2436 otherwise returns `false`.
2437 */
2438 override bool deallocateAll() shared
2439 {
2440 static if (hasMember!(Allocator, "deallocateAll"))
2441 {
2442 return impl.deallocateAll();
2443 }
2444 else
2445 {
2446 return false;
2447 }
2448 }
2449
2450 /**
2451 Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`.
2452 */
2453 override Ternary empty() shared
2454 {
2455 static if (hasMember!(Allocator, "empty"))
2456 {
2457 return Ternary(impl.empty);
2458 }
2459 else
2460 {
2461 return Ternary.unknown;
2462 }
2463 }
2464
2465 /**
2466 Returns `impl.allocateAll()` if present, `null` otherwise.
2467 */
2468 override void[] allocateAll() shared
2469 {
2470 static if (hasMember!(Allocator, "allocateAll"))
2471 {
2472 return impl.allocateAll();
2473 }
2474 else
2475 {
2476 return null;
2477 }
2478 }
2479 }
2480
2481
2482 // Example in intro above
2483 @system unittest
2484 {
2485 // Allocate an int, initialize it with 42
2486 int* p = theAllocator.make!int(42);
2487 assert(*p == 42);
2488
2489 // Destroy and deallocate it
2490 theAllocator.dispose(p);
2491
2492 // Allocate using the global process allocator
2493 p = processAllocator.make!int(100);
2494 assert(*p == 100);
2495
2496 // Destroy and deallocate
2497 processAllocator.dispose(p);
2498
2499 // Create an array of 50 doubles initialized to -1.0
2500 double[] arr = theAllocator.makeArray!double(50, -1.0);
2501
2502 // Check internal pointer
2503 void[] result;
2504 assert(theAllocator.resolveInternalPointer(null, result) == Ternary.no);
2505 Ternary r = theAllocator.resolveInternalPointer(arr.ptr, result);
2506 assert(result.ptr is arr.ptr && result.length >= arr.length);
2507
2508 // Append two zeros to it
2509 theAllocator.expandArray(arr, 2, 0.0);
2510 // On second thought, take that back
2511 theAllocator.shrinkArray(arr, 2);
2512 // Destroy and deallocate
2513 theAllocator.dispose(arr);
2514 }
2515
2516 __EOF__
2517
2518 /**
2519
2520 Stores an allocator object in thread-local storage (i.e. non-$(D shared) D
2521 global). $(D ThreadLocal!A) is a subtype of $(D A) so it appears to implement
2522 $(D A)'s allocator primitives.
2523
2524 $(D A) must hold state, otherwise $(D ThreadLocal!A) refuses instantiation. This
2525 means e.g. $(D ThreadLocal!Mallocator) does not work because $(D Mallocator)'s
2526 state is not stored as members of $(D Mallocator), but instead is hidden in the
2527 C library implementation.
2528
2529 */
2530 struct ThreadLocal(A)
2531 {
2532 static assert(stateSize!A,
2533 typeof(A).stringof
2534 ~ " does not have state so it cannot be used with ThreadLocal");
2535
2536 /**
2537 The allocator instance.
2538 */
2539 static A instance;
2540
2541 /**
2542 `ThreadLocal!A` is a subtype of `A` so it appears to implement `A`'s
2543 allocator primitives.
2544 */
2545 alias instance this;
2546
2547 /**
2548 `ThreadLocal` disables all constructors. The intended usage is
2549 `ThreadLocal!A.instance`.
2550 */
2551 @disable this();
2552 /// Ditto
2553 @disable this(this);
2554 }
2555
2556 ///
2557 unittest
2558 {
2559 static assert(!is(ThreadLocal!Mallocator));
2560 static assert(!is(ThreadLocal!GCAllocator));
2561 alias ThreadLocal!(FreeList!(GCAllocator, 0, 8)) Allocator;
2562 auto b = Allocator.instance.allocate(5);
2563 static assert(hasMember!(Allocator, "allocate"));
2564 }
2565
2566 /*
2567 (Not public.)
2568
2569 A binary search tree that uses no allocation of its own. Instead, it relies on
2570 user code to allocate nodes externally. Then $(D EmbeddedTree)'s primitives wire
2571 the nodes appropriately.
2572
2573 Warning: currently $(D EmbeddedTree) is not using rebalancing, so it may
2574 degenerate. A red-black tree implementation storing the color with one of the
2575 pointers is planned for the future.
2576 */
2577 private struct EmbeddedTree(T, alias less)
2578 {
2579 static struct Node
2580 {
2581 T payload;
2582 Node* left, right;
2583 }
2584
2585 private Node* root;
2586
2587 private Node* insert(Node* n, ref Node* backref)
2588 {
2589 backref = n;
2590 n.left = n.right = null;
2591 return n;
2592 }
2593
2594 Node* find(Node* data)
2595 {
2596 for (auto n = root; n; )
2597 {
2598 if (less(data, n))
2599 {
2600 n = n.left;
2601 }
2602 else if (less(n, data))
2603 {
2604 n = n.right;
2605 }
2606 else
2607 {
2608 return n;
2609 }
2610 }
2611 return null;
2612 }
2613
2614 Node* insert(Node* data)
2615 {
2616 if (!root)
2617 {
2618 root = data;
2619 data.left = data.right = null;
2620 return root;
2621 }
2622 auto n = root;
2623 for (;;)
2624 {
2625 if (less(data, n))
2626 {
2627 if (!n.left)
2628 {
2629 // Found insertion point
2630 return insert(data, n.left);
2631 }
2632 n = n.left;
2633 }
2634 else if (less(n, data))
2635 {
2636 if (!n.right)
2637 {
2638 // Found insertion point
2639 return insert(data, n.right);
2640 }
2641 n = n.right;
2642 }
2643 else
2644 {
2645 // Found
2646 return n;
2647 }
2648 if (!n) return null;
2649 }
2650 }
2651
2652 Node* remove(Node* data)
2653 {
2654 auto n = root;
2655 Node* parent = null;
2656 for (;;)
2657 {
2658 if (!n) return null;
2659 if (less(data, n))
2660 {
2661 parent = n;
2662 n = n.left;
2663 }
2664 else if (less(n, data))
2665 {
2666 parent = n;
2667 n = n.right;
2668 }
2669 else
2670 {
2671 // Found
2672 remove(n, parent);
2673 return n;
2674 }
2675 }
2676 }
2677
2678 private void remove(Node* n, Node* parent)
2679 {
2680 assert(n);
2681 assert(!parent || parent.left == n || parent.right == n);
2682 Node** referrer = parent
2683 ? (parent.left == n ? &parent.left : &parent.right)
2684 : &root;
2685 if (!n.left)
2686 {
2687 *referrer = n.right;
2688 }
2689 else if (!n.right)
2690 {
2691 *referrer = n.left;
2692 }
2693 else
2694 {
2695 // Find the leftmost child in the right subtree
2696 auto leftmost = n.right;
2697 Node** leftmostReferrer = &n.right;
2698 while (leftmost.left)
2699 {
2700 leftmostReferrer = &leftmost.left;
2701 leftmost = leftmost.left;
2702 }
2703 // Unlink leftmost from there
2704 *leftmostReferrer = leftmost.right;
2705 // Link leftmost in lieu of n
2706 leftmost.left = n.left;
2707 leftmost.right = n.right;
2708 *referrer = leftmost;
2709 }
2710 }
2711
2712 Ternary empty() const
2713 {
2714 return Ternary(!root);
2715 }
2716
2717 void dump()
2718 {
2719 writeln(typeid(this), " @ ", cast(void*) &this);
2720 dump(root, 3);
2721 }
2722
2723 void dump(Node* r, uint indent)
2724 {
2725 write(repeat(' ', indent).array);
2726 if (!r)
2727 {
2728 writeln("(null)");
2729 return;
2730 }
2731 writeln(r.payload, " @ ", cast(void*) r);
2732 dump(r.left, indent + 3);
2733 dump(r.right, indent + 3);
2734 }
2735
2736 void assertSane()
2737 {
2738 static bool isBST(Node* r, Node* lb, Node* ub)
2739 {
2740 if (!r) return true;
2741 if (lb && !less(lb, r)) return false;
2742 if (ub && !less(r, ub)) return false;
2743 return isBST(r.left, lb, r) &&
2744 isBST(r.right, r, ub);
2745 }
2746 if (isBST(root, null, null)) return;
2747 dump;
2748 assert(0);
2749 }
2750 }
2751
2752 unittest
2753 {
2754 alias a = GCAllocator.instance;
2755 alias Tree = EmbeddedTree!(int, (a, b) => a.payload < b.payload);
2756 Tree t;
2757 assert(t.empty);
2758 int[] vals = [ 6, 3, 9, 1, 0, 2, 8, 11 ];
2759 foreach (v; vals)
2760 {
2761 auto n = new Tree.Node(v, null, null);
2762 assert(t.insert(n));
2763 assert(n);
2764 t.assertSane;
2765 }
2766 assert(!t.empty);
2767 foreach (v; vals)
2768 {
2769 Tree.Node n = { v };
2770 assert(t.remove(&n));
2771 t.assertSane;
2772 }
2773 assert(t.empty);
2774 }
2775
2776 /*
2777
2778 $(D InternalPointersTree) adds a primitive on top of another allocator: calling
2779 $(D resolveInternalPointer(p)) returns the block within which the internal
2780 pointer $(D p) lies. Pointers right after the end of allocated blocks are also
2781 considered internal.
2782
2783 The implementation stores three additional words with each allocation (one for
2784 the block size and two for search management).
2785
2786 */
2787 private struct InternalPointersTree(Allocator)
2788 {
2789 alias Tree = EmbeddedTree!(size_t,
2790 (a, b) => cast(void*) a + a.payload < cast(void*) b);
2791 alias Parent = AffixAllocator!(Allocator, Tree.Node);
2792
2793 // Own state
2794 private Tree blockMap;
2795
2796 alias alignment = Parent.alignment;
2797
2798 /**
2799 The implementation is available as a public member.
2800 */
2801 static if (stateSize!Parent) Parent parent;
2802 else alias parent = Parent.instance;
2803
2804 /// Allocator API.
2805 void[] allocate(size_t bytes)
2806 {
2807 auto r = parent.allocate(bytes);
2808 if (!r.ptr) return r;
2809 Tree.Node* n = &parent.prefix(r);
2810 n.payload = bytes;
2811 blockMap.insert(n) || assert(0);
2812 return r;
2813 }
2814
2815 /// Ditto
2816 bool deallocate(void[] b)
2817 {
2818 if (!b.ptr) return;
2819 Tree.Node* n = &parent.prefix(b);
2820 blockMap.remove(n) || assert(false);
2821 parent.deallocate(b);
2822 return true;
2823 }
2824
2825 /// Ditto
2826 static if (hasMember!(Allocator, "reallocate"))
2827 bool reallocate(ref void[] b, size_t s)
2828 {
2829 auto n = &parent.prefix(b);
2830 assert(n.payload == b.length);
2831 blockMap.remove(n) || assert(0);
2832 if (!parent.reallocate(b, s))
2833 {
2834 // Failed, must reinsert the same node in the tree
2835 assert(n.payload == b.length);
2836 blockMap.insert(n) || assert(0);
2837 return false;
2838 }
2839 // Insert the new node
2840 n = &parent.prefix(b);
2841 n.payload = s;
2842 blockMap.insert(n) || assert(0);
2843 return true;
2844 }
2845
2846 /// Ditto
2847 Ternary owns(void[] b)
2848 {
2849 void[] result;
2850 return resolveInternalPointer(b.ptr, result);
2851 }
2852
2853 /// Ditto
2854 Ternary empty()
2855 {
2856 return Ternary(blockMap.empty);
2857 }
2858
2859 /** Returns the block inside which $(D p) resides, or $(D null) if the
2860 pointer does not belong.
2861 */
2862 Ternary resolveInternalPointer(const void* p, ref void[] result)
2863 {
2864 // Must define a custom find
2865 Tree.Node* find()
2866 {
2867 for (auto n = blockMap.root; n; )
2868 {
2869 if (p < n)
2870 {
2871 n = n.left;
2872 }
2873 else if (p > (cast(void*) (n + 1)) + n.payload)
2874 {
2875 n = n.right;
2876 }
2877 else
2878 {
2879 return n;
2880 }
2881 }
2882 return null;
2883 }
2884
2885 auto n = find();
2886 if (!n) return Ternary.no;
2887 result = (cast(void*) (n + 1))[0 .. n.payload];
2888 return Ternary.yes;
2889 }
2890 }
2891
2892 unittest
2893 {
2894 InternalPointersTree!(Mallocator) a;
2895 int[] vals = [ 6, 3, 9, 1, 2, 8, 11 ];
2896 void[][] allox;
2897 foreach (v; vals)
2898 {
2899 allox ~= a.allocate(v);
2900 }
2901 a.blockMap.assertSane;
2902
2903 foreach (b; allox)
2904 {
2905 void[] p;
2906 Ternary r = a.resolveInternalPointer(b.ptr, p);
2907 assert(p.ptr is b.ptr && p.length >= b.length);
2908 r = a.resolveInternalPointer(b.ptr + b.length, p);
2909 assert(p.ptr is b.ptr && p.length >= b.length);
2910 r = a.resolveInternalPointer(b.ptr + b.length / 2, p);
2911 assert(p.ptr is b.ptr && p.length >= b.length);
2912 auto bogus = new void[b.length];
2913 assert(a.resolveInternalPointer(bogus.ptr, p) == Ternary.no);
2914 }
2915
2916 foreach (b; allox.randomCover)
2917 {
2918 a.deallocate(b);
2919 }
2920
2921 assert(a.empty);
2922 }
2923
2924 //version (std_allocator_benchmark)
2925 unittest
2926 {
2927 static void testSpeed(A)()
2928 {
2929 static if (stateSize!A) A a;
2930 else alias a = A.instance;
2931
2932 void[][128] bufs;
2933
2934 import std.random;
2935 foreach (i; 0 .. 100_000)
2936 {
2937 auto j = uniform(0, bufs.length);
2938 switch (uniform(0, 2))
2939 {
2940 case 0:
2941 a.deallocate(bufs[j]);
2942 bufs[j] = a.allocate(uniform(0, 4096));
2943 break;
2944 case 1:
2945 a.deallocate(bufs[j]);
2946 bufs[j] = null;
2947 break;
2948 default:
2949 assert(0);
2950 }
2951 }
2952 }
2953
2954 alias FList = FreeList!(GCAllocator, 0, unbounded);
2955 alias A = Segregator!(
2956 8, FreeList!(GCAllocator, 0, 8),
2957 128, Bucketizer!(FList, 1, 128, 16),
2958 256, Bucketizer!(FList, 129, 256, 32),
2959 512, Bucketizer!(FList, 257, 512, 64),
2960 1024, Bucketizer!(FList, 513, 1024, 128),
2961 2048, Bucketizer!(FList, 1025, 2048, 256),
2962 3584, Bucketizer!(FList, 2049, 3584, 512),
2963 4072 * 1024, AllocatorList!(
2964 (size_t n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate(
2965 max(n, 4072 * 1024)))),
2966 GCAllocator
2967 );
2968
2969 import std.datetime, std.experimental.allocator.null_allocator;
2970 if (false) writeln(benchmark!(
2971 testSpeed!NullAllocator,
2972 testSpeed!Mallocator,
2973 testSpeed!GCAllocator,
2974 testSpeed!(ThreadLocal!A),
2975 testSpeed!(A),
2976 )(20)[].map!(t => t.to!("seconds", double)));
2977 }
2978
2979 unittest
2980 {
2981 auto a = allocatorObject(Mallocator.instance);
2982 auto b = a.allocate(100);
2983 assert(b.length == 100);
2984
2985 FreeList!(GCAllocator, 0, 8) fl;
2986 auto sa = allocatorObject(fl);
2987 b = a.allocate(101);
2988 assert(b.length == 101);
2989
2990 FallbackAllocator!(InSituRegion!(10240, 64), GCAllocator) fb;
2991 // Doesn't work yet...
2992 //a = allocatorObject(fb);
2993 //b = a.allocate(102);
2994 //assert(b.length == 102);
2995 }
2996
2997 ///
2998 unittest
2999 {
3000 /// Define an allocator bound to the built-in GC.
3001 IAllocator alloc = allocatorObject(GCAllocator.instance);
3002 auto b = alloc.allocate(42);
3003 assert(b.length == 42);
3004 assert(alloc.deallocate(b) == Ternary.yes);
3005
3006 // Define an elaborate allocator and bind it to the class API.
3007 // Note that the same variable "alloc" is used.
3008 alias FList = FreeList!(GCAllocator, 0, unbounded);
3009 alias A = ThreadLocal!(
3010 Segregator!(
3011 8, FreeList!(GCAllocator, 0, 8),
3012 128, Bucketizer!(FList, 1, 128, 16),
3013 256, Bucketizer!(FList, 129, 256, 32),
3014 512, Bucketizer!(FList, 257, 512, 64),
3015 1024, Bucketizer!(FList, 513, 1024, 128),
3016 2048, Bucketizer!(FList, 1025, 2048, 256),
3017 3584, Bucketizer!(FList, 2049, 3584, 512),
3018 4072 * 1024, AllocatorList!(
3019 (n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate(
3020 max(n, 4072 * 1024)))),
3021 GCAllocator
3022 )
3023 );
3024
3025 auto alloc2 = allocatorObject(A.instance);
3026 b = alloc.allocate(101);
3027 assert(alloc.deallocate(b) == Ternary.yes);
3028 }
3029