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 concrete
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 `std.experimental.allocator`)
111
112 As of this time, `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 `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 `allocator` object for acquiring memory. Furthermore, setting
181 `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 // Fix https://issues.dlang.org/show_bug.cgi?id=17806
230 // this should always be the first unittest in this module in order to ensure
231 // that we use the `processAllocator` setter before the getter
232 @system unittest
233 {
234 import std.experimental.allocator.mallocator : Mallocator;
235 import std.experimental.allocator.gc_allocator : GCAllocator;
236 auto newAlloc = sharedAllocatorObject(Mallocator.instance);
237 processAllocator = newAlloc;
238 assert(processAllocator is newAlloc);
239 processAllocator = sharedAllocatorObject(GCAllocator.instance);
240 }
241
242 // Example in the synopsis above
243 @system unittest
244 {
245 import std.algorithm.comparison : min, max;
246 import std.experimental.allocator.building_blocks.allocator_list
247 : AllocatorList;
248 import std.experimental.allocator.building_blocks.bitmapped_block
249 : BitmappedBlock;
250 import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;
251 import std.experimental.allocator.building_blocks.free_list : FreeList;
252 import std.experimental.allocator.building_blocks.segregator : Segregator;
253 import std.experimental.allocator.gc_allocator : GCAllocator;
254
255 alias FList = FreeList!(GCAllocator, 0, unbounded);
256 alias A = Segregator!(
257 8, FreeList!(GCAllocator, 0, 8),
258 128, Bucketizer!(FList, 1, 128, 16),
259 256, Bucketizer!(FList, 129, 256, 32),
260 512, Bucketizer!(FList, 257, 512, 64),
261 1024, Bucketizer!(FList, 513, 1024, 128),
262 2048, Bucketizer!(FList, 1025, 2048, 256),
263 3584, Bucketizer!(FList, 2049, 3584, 512),
264 4072 * 1024, AllocatorList!(
265 (n) => BitmappedBlock!(4096)(
266 cast(ubyte[])(GCAllocator.instance.allocate(
267 max(n, 4072 * 1024))))),
268 GCAllocator
269 );
270 A tuMalloc;
271 auto b = tuMalloc.allocate(500);
272 assert(b.length == 500);
273 auto c = tuMalloc.allocate(113);
274 assert(c.length == 113);
275 assert(tuMalloc.expand(c, 14));
276 tuMalloc.deallocate(b);
277 tuMalloc.deallocate(c);
278 }
279
280 import std.range.primitives;
281 import std.traits;
282 import std.typecons;
283
284 /**
285 Dynamic allocator interface. Code that defines allocators ultimately implements
286 this interface. This should be used wherever a uniform type is required for
287 encapsulating various allocator implementations.
288
289 Composition of allocators is not recommended at this level due to
290 inflexibility of dynamic interfaces and inefficiencies caused by cascaded
291 multiple calls. Instead, compose allocators using the static interface defined
292 in $(MREF std,experimental,allocator,building_blocks),
293 then adapt the composed
294 allocator to `IAllocator` (possibly by using $(LREF CAllocatorImpl) below).
295
296 Methods returning `Ternary` return `Ternary.yes` upon success,
297 `Ternary.no` upon failure, and `Ternary.unknown` if the primitive is not
298 implemented by the allocator instance.
299 */
300 interface IAllocator
301 {
302 nothrow:
303 /**
304 Returns the alignment offered.
305 */
306 @property uint alignment();
307
308 /**
309 Returns the good allocation size that guarantees zero internal
310 fragmentation.
311 */
312 size_t goodAllocSize(size_t s);
313
314 /**
315 Allocates `n` bytes of memory.
316 */
317 void[] allocate(size_t, TypeInfo ti = null);
318
319 /**
320 Allocates `n` bytes of memory with specified alignment `a`. Implementations
321 that do not support this primitive should always return `null`.
322 */
323 void[] alignedAllocate(size_t n, uint a);
324
325 /**
326 Allocates and returns all memory available to this allocator.
327 Implementations that do not support this primitive should always return
328 `null`.
329 */
330 void[] allocateAll();
331
332 /**
333 Expands a memory block in place and returns `true` if successful.
334 Implementations that don't support this primitive should always return
335 `false`.
336 */
337 bool expand(ref void[], size_t);
338
339 /// Reallocates a memory block.
340 bool reallocate(ref void[], size_t);
341
342 /// Reallocates a memory block with specified alignment.
343 bool alignedReallocate(ref void[] b, size_t size, uint alignment);
344
345 /**
346 Returns `Ternary.yes` if the allocator owns `b`, `Ternary.no` if
347 the allocator doesn't own `b`, and `Ternary.unknown` if ownership
348 cannot be determined. Implementations that don't support this primitive
349 should always return `Ternary.unknown`.
350 */
351 Ternary owns(void[] b);
352
353 /**
354 Resolves an internal pointer to the full block allocated. Implementations
355 that don't support this primitive should always return `Ternary.unknown`.
356 */
357 Ternary resolveInternalPointer(const void* p, ref void[] result);
358
359 /**
360 Deallocates a memory block. Implementations that don't support this
361 primitive should always return `false`. A simple way to check that an
362 allocator supports deallocation is to call `deallocate(null)`.
363 */
364 bool deallocate(void[] b);
365
366 /**
367 Deallocates all memory. Implementations that don't support this primitive
368 should always return `false`.
369 */
370 bool deallocateAll();
371
372 /**
373 Returns `Ternary.yes` if no memory is currently allocated from this
374 allocator, `Ternary.no` if some allocations are currently active, or
375 `Ternary.unknown` if not supported.
376 */
377 Ternary empty();
378
379 /**
380 Increases the reference count of the concrete class that implements this
381 interface.
382
383 For stateless allocators, this does nothing.
384 */
385 @safe @nogc pure
386 void incRef();
387
388 /**
389 Decreases the reference count of the concrete class that implements this
390 interface.
391 When the reference count is `0`, the object self-destructs.
392
393 Returns: `true` if the reference count is greater than `0` and `false` when
394 it hits `0`. For stateless allocators, it always returns `true`.
395 */
396 @safe @nogc pure
397 bool decRef();
398 }
399
400 /**
401 A reference counted struct that wraps the dynamic allocator interface.
402 This should be used wherever a uniform type is required for encapsulating
403 various allocator implementations.
404
405 Code that defines allocators ultimately implements the $(LREF IAllocator)
406 interface, possibly by using $(LREF CAllocatorImpl) below, and then build a
407 `RCIAllocator` out of this.
408
409 Composition of allocators is not recommended at this level due to
410 inflexibility of dynamic interfaces and inefficiencies caused by cascaded
411 multiple calls. Instead, compose allocators using the static interface defined
412 in $(A std_experimental_allocator_building_blocks.html,
413 `std.experimental.allocator.building_blocks`), then adapt the composed
414 allocator to `RCIAllocator` (possibly by using $(LREF allocatorObject) below).
415 */
416 struct RCIAllocator
417 {
418 private IAllocator _alloc;
419
420 nothrow:
421 private @nogc pure @safe
thisRCIAllocator422 this(this _)(IAllocator alloc)
423 {
424 assert(alloc);
425 _alloc = alloc;
426 }
427
428 @nogc pure @safe
thisRCIAllocator429 this(this)
430 {
431 if (_alloc !is null)
432 {
433 _alloc.incRef();
434 }
435 }
436
437 @nogc pure @safe
~thisRCIAllocator438 ~this()
439 {
440 if (_alloc !is null)
441 {
442 bool isLast = !_alloc.decRef();
443 if (isLast) _alloc = null;
444 }
445 }
446
447 @nogc pure @safe
opAssignRCIAllocator448 auto ref opAssign()(typeof(this) rhs)
449 {
450 if (_alloc is rhs._alloc)
451 {
452 return this;
453 }
454 // incRef was allready called by rhs posblit, so we're just moving
455 // calling dtor is the equivalent of decRef
456 __dtor();
457 _alloc = rhs._alloc;
458 // move
459 rhs._alloc = null;
460 return this;
461 }
462
463 @nogc pure @safe
isNullRCIAllocator464 bool isNull(this _)()
465 {
466 return _alloc is null;
467 }
468
alignmentRCIAllocator469 @property uint alignment()
470 {
471 assert(_alloc);
472 return _alloc.alignment();
473 }
474
goodAllocSizeRCIAllocator475 size_t goodAllocSize(size_t s)
476 {
477 assert(_alloc);
478 return _alloc.goodAllocSize(s);
479 }
480
481 void[] allocate(size_t n, TypeInfo ti = null)
482 {
483 assert(_alloc);
484 return _alloc.allocate(n, ti);
485 }
486
alignedAllocateRCIAllocator487 void[] alignedAllocate(size_t n, uint a)
488 {
489 assert(_alloc);
490 return _alloc.alignedAllocate(n, a);
491 }
492
allocateAllRCIAllocator493 void[] allocateAll()
494 {
495 assert(_alloc);
496 return _alloc.allocateAll();
497 }
498
expandRCIAllocator499 bool expand(ref void[] b, size_t size)
500 {
501 assert(_alloc);
502 return _alloc.expand(b, size);
503 }
504
reallocateRCIAllocator505 bool reallocate(ref void[] b, size_t size)
506 {
507 assert(_alloc);
508 return _alloc.reallocate(b, size);
509 }
510
alignedReallocateRCIAllocator511 bool alignedReallocate(ref void[] b, size_t size, uint alignment)
512 {
513 assert(_alloc);
514 return _alloc.alignedReallocate(b, size, alignment);
515 }
516
ownsRCIAllocator517 Ternary owns(void[] b)
518 {
519 assert(_alloc);
520 return _alloc.owns(b);
521 }
522
resolveInternalPointerRCIAllocator523 Ternary resolveInternalPointer(const void* p, ref void[] result)
524 {
525 assert(_alloc);
526 return _alloc.resolveInternalPointer(p, result);
527 }
528
deallocateRCIAllocator529 bool deallocate(void[] b)
530 {
531 assert(_alloc);
532 return _alloc.deallocate(b);
533 }
534
deallocateAllRCIAllocator535 bool deallocateAll()
536 {
537 assert(_alloc);
538 return _alloc.deallocateAll();
539 }
540
emptyRCIAllocator541 Ternary empty()
542 {
543 assert(_alloc);
544 return _alloc.empty();
545 }
546 }
547
548 @system unittest
549 {
550 import std.experimental.allocator.building_blocks.region : Region;
551 import std.conv : emplace;
552
553 auto reg = Region!()(new ubyte[1024]);
554 auto state = reg.allocate(stateSize!(CAllocatorImpl!(Region!(), Yes.indirect)));
555 auto regObj = emplace!(CAllocatorImpl!(Region!(), Yes.indirect))(state, ®);
556
557 auto rcalloc = RCIAllocator(regObj);
558 auto b = rcalloc.allocate(10);
559 assert(b.length == 10);
560
561 // The reference counting is zero based
562 assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
563 {
564 auto rca2 = rcalloc;
565 assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 2);
566 }
567 assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
568 }
569
570 @system unittest
571 {
572 import std.conv;
573 import std.experimental.allocator.mallocator;
574 import std.experimental.allocator.building_blocks.stats_collector;
575
576 alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
577 SCAlloc statsCollectorAlloc;
578
579 ulong bytesUsed = statsCollectorAlloc.bytesUsed;
580 assert(bytesUsed == 0);
581
582 {
583 auto _allocator = allocatorObject(&statsCollectorAlloc);
584 bytesUsed = statsCollectorAlloc.bytesUsed;
585 assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc, Yes.indirect)));
586 }
587
588 bytesUsed = statsCollectorAlloc.bytesUsed;
589 assert(bytesUsed == 0, "RCIAllocator leaks memory; leaked "
590 ~ to!string(bytesUsed) ~ " bytes");
591 }
592
593 @system unittest
594 {
595 import std.conv;
596 import std.experimental.allocator.mallocator;
597 import std.experimental.allocator.building_blocks.stats_collector;
598
599 alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
600 SCAlloc statsCollectorAlloc;
601
602 ulong bytesUsed = statsCollectorAlloc.bytesUsed;
603 assert(bytesUsed == 0);
604
605 {
606 auto _allocator = allocatorObject(statsCollectorAlloc);
607
608 // Ensure that the allocator was passed through in CAllocatorImpl
609 // This allocator was used to allocate the chunk that holds the
610 // CAllocatorImpl object; which is it's own wrapper
611 bytesUsed = (cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed;
612 assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)),
613 "RCIAllocator leaks memory; leaked " ~ to!string(bytesUsed) ~ " bytes");
614 _allocator.allocate(1);
615 bytesUsed = (cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed;
616 assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)) + 1,
617 "RCIAllocator leaks memory; leaked " ~ to!string(bytesUsed) ~ " bytes");
618 }
619
620 bytesUsed = statsCollectorAlloc.bytesUsed;
621 assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)),
622 "RCIAllocator leaks memory; leaked "
623 ~ to!string(bytesUsed) ~ " bytes");
624 }
625
626 /**
627 Dynamic shared allocator interface. Code that defines allocators shareable
628 across threads ultimately implements this interface. This should be used
629 wherever a uniform type is required for encapsulating various allocator
630 implementations.
631
632 Composition of allocators is not recommended at this level due to
633 inflexibility of dynamic interfaces and inefficiencies caused by cascaded
634 multiple calls. Instead, compose allocators using the static interface defined
635 in $(MREF std,experimental,allocator,building_blocks),
636 then adapt the composed
637 allocator to `ISharedAllocator` (possibly by using $(LREF CSharedAllocatorImpl) below).
638
639 Methods returning `Ternary` return `Ternary.yes` upon success,
640 `Ternary.no` upon failure, and `Ternary.unknown` if the primitive is not
641 implemented by the allocator instance.
642 */
643 interface ISharedAllocator
644 {
645 nothrow:
646 /**
647 Returns the alignment offered.
648 */
649 @property uint alignment() shared;
650
651 /**
652 Returns the good allocation size that guarantees zero internal
653 fragmentation.
654 */
655 size_t goodAllocSize(size_t s) shared;
656
657 /**
658 Allocates `n` bytes of memory.
659 */
660 void[] allocate(size_t, TypeInfo ti = null) shared;
661
662 /**
663 Allocates `n` bytes of memory with specified alignment `a`. Implementations
664 that do not support this primitive should always return `null`.
665 */
666 void[] alignedAllocate(size_t n, uint a) shared;
667
668 /**
669 Allocates and returns all memory available to this allocator.
670 Implementations that do not support this primitive should always return
671 `null`.
672 */
673 void[] allocateAll() shared;
674
675 /**
676 Expands a memory block in place and returns `true` if successful.
677 Implementations that don't support this primitive should always return
678 `false`.
679 */
680 bool expand(ref void[], size_t) shared;
681
682 /// Reallocates a memory block.
683 bool reallocate(ref void[], size_t) shared;
684
685 /// Reallocates a memory block with specified alignment.
686 bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared;
687
688 /**
689 Returns `Ternary.yes` if the allocator owns `b`, `Ternary.no` if
690 the allocator doesn't own `b`, and `Ternary.unknown` if ownership
691 cannot be determined. Implementations that don't support this primitive
692 should always return `Ternary.unknown`.
693 */
694 Ternary owns(void[] b) shared;
695
696 /**
697 Resolves an internal pointer to the full block allocated. Implementations
698 that don't support this primitive should always return `Ternary.unknown`.
699 */
700 Ternary resolveInternalPointer(const void* p, ref void[] result) shared;
701
702 /**
703 Deallocates a memory block. Implementations that don't support this
704 primitive should always return `false`. A simple way to check that an
705 allocator supports deallocation is to call `deallocate(null)`.
706 */
707 bool deallocate(void[] b) shared;
708
709 /**
710 Deallocates all memory. Implementations that don't support this primitive
711 should always return `false`.
712 */
713 bool deallocateAll() shared;
714
715 /**
716 Returns `Ternary.yes` if no memory is currently allocated from this
717 allocator, `Ternary.no` if some allocations are currently active, or
718 `Ternary.unknown` if not supported.
719 */
720 Ternary empty() shared;
721
722 /**
723 Increases the reference count of the concrete class that implements this
724 interface.
725
726 For stateless allocators, this does nothing.
727 */
728 @safe @nogc pure
729 void incRef() shared;
730
731 /**
732 Decreases the reference count of the concrete class that implements this
733 interface.
734 When the reference count is `0`, the object self-destructs.
735
736 For stateless allocators, this does nothing.
737
738 Returns: `true` if the reference count is greater than `0` and `false` when
739 it hits `0`. For stateless allocators, it always returns `true`.
740 */
741 @safe @nogc pure
742 bool decRef() shared;
743 }
744
745 /**
746 A reference counted struct that wraps the dynamic shared allocator interface.
747 This should be used wherever a uniform type is required for encapsulating
748 various allocator implementations.
749
750 Code that defines allocators shareable across threads ultimately implements the
751 $(LREF ISharedAllocator) interface, possibly by using
752 $(LREF CSharedAllocatorImpl) below, and then build a `RCISharedAllocator` out
753 of this.
754
755 Composition of allocators is not recommended at this level due to
756 inflexibility of dynamic interfaces and inefficiencies caused by cascaded
757 multiple calls. Instead, compose allocators using the static interface defined
758 in $(A std_experimental_allocator_building_blocks.html,
759 `std.experimental.allocator.building_blocks`), then adapt the composed allocator
760 to `RCISharedAllocator` (possibly by using $(LREF sharedAllocatorObject) below).
761 */
762 shared struct RCISharedAllocator
763 {
764 private ISharedAllocator _alloc;
765
766 nothrow:
767 private @nogc pure @safe
thisRCISharedAllocator768 this(shared ISharedAllocator alloc)
769 {
770 assert(alloc);
771 _alloc = alloc;
772 }
773
774 @nogc pure @safe
thisRCISharedAllocator775 this(this)
776 {
777 if (_alloc !is null)
778 {
779 _alloc.incRef();
780 }
781 }
782
783 @nogc pure @safe
~thisRCISharedAllocator784 ~this()
785 {
786 if (_alloc !is null)
787 {
788 bool isLast = !_alloc.decRef();
789 if (isLast) _alloc = null;
790 }
791 }
792
793 @nogc pure @safe
opAssignRCISharedAllocator794 auto ref opAssign()(RCISharedAllocator rhs)
795 {
796 if (_alloc is rhs._alloc)
797 {
798 return this;
799 }
800 // incRef was allready called by rhs posblit, so we're just moving
801 if (_alloc !is null)
802 {
803 _alloc.decRef();
804 }
805 _alloc = rhs._alloc;
806 // move
807 rhs._alloc = null;
808 return this;
809 }
810
811 @nogc pure @safe
isNullRCISharedAllocator812 bool isNull(this _)()
813 {
814 return _alloc is null;
815 }
816
alignmentRCISharedAllocator817 @property uint alignment()
818 {
819 assert(_alloc);
820 return _alloc.alignment();
821 }
822
goodAllocSizeRCISharedAllocator823 size_t goodAllocSize(size_t s)
824 {
825 assert(_alloc);
826 return _alloc.goodAllocSize(s);
827 }
828
829 void[] allocate(size_t n, TypeInfo ti = null)
830 {
831 assert(_alloc);
832 return _alloc.allocate(n, ti);
833 }
834
alignedAllocateRCISharedAllocator835 void[] alignedAllocate(size_t n, uint a)
836 {
837 assert(_alloc);
838 return _alloc.alignedAllocate(n, a);
839 }
840
allocateAllRCISharedAllocator841 void[] allocateAll()
842 {
843 assert(_alloc);
844 return _alloc.allocateAll();
845 }
846
expandRCISharedAllocator847 bool expand(ref void[] b, size_t size)
848 {
849 assert(_alloc);
850 return _alloc.expand(b, size);
851 }
852
reallocateRCISharedAllocator853 bool reallocate(ref void[] b, size_t size)
854 {
855 assert(_alloc);
856 return _alloc.reallocate(b, size);
857 }
858
alignedReallocateRCISharedAllocator859 bool alignedReallocate(ref void[] b, size_t size, uint alignment)
860 {
861 assert(_alloc);
862 return _alloc.alignedReallocate(b, size, alignment);
863 }
864
ownsRCISharedAllocator865 Ternary owns(void[] b)
866 {
867 assert(_alloc);
868 return _alloc.owns(b);
869 }
870
resolveInternalPointerRCISharedAllocator871 Ternary resolveInternalPointer(const void* p, ref void[] result)
872 {
873 assert(_alloc);
874 return _alloc.resolveInternalPointer(p, result);
875 }
876
deallocateRCISharedAllocator877 bool deallocate(void[] b)
878 {
879 assert(_alloc);
880 return _alloc.deallocate(b);
881 }
882
deallocateAllRCISharedAllocator883 bool deallocateAll()
884 {
885 assert(_alloc);
886 return _alloc.deallocateAll();
887 }
888
emptyRCISharedAllocator889 Ternary empty()
890 {
891 assert(_alloc);
892 return _alloc.empty();
893 }
894 }
895
896 private RCISharedAllocator _processAllocator;
897 private RCIAllocator _threadAllocator;
898
899 @nogc nothrow @safe
setupThreadAllocator()900 private ref RCIAllocator setupThreadAllocator()
901 {
902 /*
903 Forwards the `_threadAllocator` calls to the `processAllocator`
904 */
905 static class ThreadAllocator : IAllocator
906 {
907 nothrow:
908 private RCISharedAllocator _allocator;
909
910 @nogc @safe
911 this(ref RCISharedAllocator procAlloc)
912 {
913 _allocator = procAlloc;
914 }
915
916 override @property uint alignment()
917 {
918 return _allocator.alignment();
919 }
920
921 override size_t goodAllocSize(size_t s)
922 {
923 return _allocator.goodAllocSize(s);
924 }
925
926 override void[] allocate(size_t n, TypeInfo ti = null)
927 {
928 return _allocator.allocate(n, ti);
929 }
930
931 override void[] alignedAllocate(size_t n, uint a)
932 {
933 return _allocator.alignedAllocate(n, a);
934 }
935
936 override void[] allocateAll()
937 {
938 return _allocator.allocateAll();
939 }
940
941 override bool expand(ref void[] b, size_t size)
942 {
943 return _allocator.expand(b, size);
944 }
945
946 override bool reallocate(ref void[] b, size_t size)
947 {
948 return _allocator.reallocate(b, size);
949 }
950
951 override bool alignedReallocate(ref void[] b, size_t size, uint alignment)
952 {
953 return _allocator.alignedReallocate(b, size, alignment);
954 }
955
956 override Ternary owns(void[] b)
957 {
958 return _allocator.owns(b);
959 }
960
961 override Ternary resolveInternalPointer(const void* p, ref void[] result)
962 {
963 return _allocator.resolveInternalPointer(p, result);
964 }
965
966 override bool deallocate(void[] b)
967 {
968 return _allocator.deallocate(b);
969 }
970
971 override bool deallocateAll()
972 {
973 return _allocator.deallocateAll();
974 }
975
976 override Ternary empty()
977 {
978 return _allocator.empty();
979 }
980
981 @nogc pure @safe
982 override void incRef()
983 {
984 _allocator._alloc.incRef();
985 }
986
987 @nogc pure @safe
988 override bool decRef()
989 {
990 return _allocator._alloc.decRef();
991 }
992 }
993
994 assert(_threadAllocator.isNull);
995 import core.lifetime : emplace;
996 static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState;
997 () @trusted {
998 _threadAllocator = RCIAllocator(emplace!(ThreadAllocator)(_threadAllocatorState[], processAllocator()));
999 }();
1000 return _threadAllocator;
1001 }
1002
1003 // Fix threadAllocator bug: the threadAllocator should hold an internal reference
1004 // to the processAllocator that it's using
1005 @system unittest
1006 {
1007 import std.experimental.allocator.mallocator : Mallocator;
1008
1009 auto a = sharedAllocatorObject(Mallocator.instance);
1010 auto buf = theAllocator.allocate(42);
1011 processAllocator = a;
1012 theAllocator.deallocate(buf);
1013 }
1014
1015 /**
1016 Gets/sets the allocator for the current thread. This is the default allocator
1017 that should be used for allocating thread-local memory. For allocating memory
1018 to be shared across threads, use `processAllocator` (below). By default,
1019 `theAllocator` ultimately fetches memory from `processAllocator`, which
1020 in turn uses the garbage collected heap.
1021 */
1022 @nogc nothrow @safe
theAllocator()1023 @property ref RCIAllocator theAllocator()
1024 {
1025 alias p = _threadAllocator;
1026 return !p.isNull() ? p : setupThreadAllocator();
1027 }
1028
1029 /// Ditto
1030 nothrow @system @nogc
theAllocator(RCIAllocator a)1031 @property void theAllocator(RCIAllocator a)
1032 {
1033 assert(!a.isNull);
1034 _threadAllocator = a;
1035 }
1036
1037 ///
1038 @system unittest
1039 {
1040 // Install a new allocator that is faster for 128-byte allocations.
1041 import std.experimental.allocator.building_blocks.free_list : FreeList;
1042 import std.experimental.allocator.gc_allocator : GCAllocator;
1043 auto oldAllocator = theAllocator;
1044 scope(exit) theAllocator = oldAllocator;
1045 theAllocator = allocatorObject(FreeList!(GCAllocator, 128)());
1046 // Use the now changed allocator to allocate an array
1047 const ubyte[] arr = theAllocator.makeArray!ubyte(128);
1048 assert(arr.ptr);
1049 //...
1050 }
1051
1052 /**
1053 Gets/sets the allocator for the current process. This allocator must be used
1054 for allocating memory shared across threads. Objects created using this
1055 allocator can be cast to `shared`.
1056 */
1057 @nogc nothrow @trusted
processAllocator()1058 @property ref RCISharedAllocator processAllocator()
1059 {
1060 import std.experimental.allocator.gc_allocator : GCAllocator;
1061 import std.concurrency : initOnce;
1062
1063 static RCISharedAllocator* forceAttributes()
1064 {
1065 return &initOnce!_processAllocator(
1066 sharedAllocatorObject(GCAllocator.instance));
1067 }
1068
1069 return *(cast(RCISharedAllocator* function() @nogc nothrow)(&forceAttributes))();
1070 }
1071
1072 /// Ditto
1073 @nogc nothrow @system
processAllocator(ref RCISharedAllocator a)1074 @property void processAllocator(ref RCISharedAllocator a)
1075 {
1076 assert(!a.isNull);
1077 processAllocator() = a;
1078 }
1079
1080 @system unittest
1081 {
1082 import core.exception : AssertError;
1083 import std.exception : assertThrown;
1084 import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
1085 import std.experimental.allocator.mallocator : Mallocator;
1086
1087 assert(!processAllocator.isNull);
1088 assert(!theAllocator.isNull);
1089
1090 testAllocatorObject(processAllocator);
1091 testAllocatorObject(theAllocator);
1092
1093 shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL;
1094 RCISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL);
1095 alias SharedAllocT = CSharedAllocatorImpl!(
1096 shared SharedFreeList!(
1097 Mallocator, chooseAtRuntime, chooseAtRuntime));
1098
1099 assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 1);
1100 assert(!sharedFLObj.isNull);
1101 testAllocatorObject(sharedFLObj);
1102
1103 // Test processAllocator setter
1104 RCISharedAllocator oldProcessAllocator = processAllocator;
1105 processAllocator = sharedFLObj;
1106 assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 2);
1107 assert(processAllocator._alloc is sharedFLObj._alloc);
1108
1109 testAllocatorObject(processAllocator);
1110 testAllocatorObject(theAllocator);
1111 assertThrown!AssertError(processAllocator = RCISharedAllocator(null));
1112
1113 // Restore initial processAllocator state
1114 processAllocator = oldProcessAllocator;
1115 assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 1);
1116 assert(processAllocator is oldProcessAllocator);
1117
1118 RCISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL);
1119 testAllocatorObject(indirectShFLObj);
1120 alias IndirectSharedAllocT = CSharedAllocatorImpl!(
1121 shared SharedFreeList!(
1122 Mallocator, chooseAtRuntime, chooseAtRuntime)
1123 , Yes.indirect);
1124
1125 assert((cast(IndirectSharedAllocT)(indirectShFLObj._alloc)).rc == 1);
1126
1127 RCIAllocator indirectMallocator = allocatorObject(&Mallocator.instance);
1128 testAllocatorObject(indirectMallocator);
1129 }
1130
1131 /**
1132 Dynamically allocates (using `alloc`) and then creates in the memory
1133 allocated an object of type `T`, using `args` (if any) for its
1134 initialization. Initialization occurs in the memory allocated and is otherwise
1135 semantically the same as `T(args)`.
1136 (Note that using `alloc.make!(T[])` creates a pointer to an (empty) array
1137 of `T`s, not an array. To use an allocator to allocate and initialize an
1138 array, use `alloc.makeArray!T` described below.)
1139
1140 Params:
1141 T = Type of the object being created.
1142 alloc = The allocator used for getting the needed memory. It may be an object
1143 implementing the static interface for allocators, or an `IAllocator`
1144 reference.
1145 args = Optional arguments used for initializing the created object. If not
1146 present, the object is default constructed.
1147
1148 Returns: If `T` is a class type, returns a reference to the created `T`
1149 object. Otherwise, returns a `T*` pointing to the created object. In all
1150 cases, returns `null` if allocation failed.
1151
1152 Throws: If `T`'s constructor throws, deallocates the allocated memory and
1153 propagates the exception.
1154 */
make(T,Allocator,A...)1155 auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args)
1156 {
1157 import std.algorithm.comparison : max;
1158 static if (!is(T == class) && !is(T == interface) && A.length == 0
1159 && __traits(compiles, {T t;}) && __traits(isZeroInit, T)
1160 && is(typeof(alloc.allocateZeroed(size_t.max))))
1161 {
1162 auto m = alloc.allocateZeroed(max(T.sizeof, 1));
1163 return (() @trusted => cast(T*) m.ptr)();
1164 }
1165 else
1166 {
1167 import core.internal.lifetime : emplaceRef;
1168 import core.lifetime : emplace;
1169
1170 auto m = alloc.allocate(max(stateSize!T, 1));
1171 if (!m.ptr) return null;
1172
1173 // make can only be @safe if emplace or emplaceRef is `pure`
1174 auto construct()
1175 {
1176 static if (is(T == class)) return emplace!T(m, args);
1177 else
1178 {
1179 // Assume cast is safe as allocation succeeded for `stateSize!T`
1180 auto p = () @trusted { return cast(T*) m.ptr; }();
1181 emplaceRef!T(*p, args);
1182 return p;
1183 }
1184 }
1185
1186 scope(failure)
1187 {
1188 static if (is(typeof(() pure { return construct(); })))
1189 {
1190 // Assume deallocation is safe because:
1191 // 1) in case of failure, `m` is the only reference to this memory
1192 // 2) `m` is known to originate from `alloc`
1193 () @trusted { alloc.deallocate(m); }();
1194 }
1195 else
1196 {
1197 alloc.deallocate(m);
1198 }
1199 }
1200
1201 return construct();
1202 }
1203 }
1204
1205 ///
1206 @system unittest
1207 {
1208 // Dynamically allocate one integer
1209 const int* p1 = theAllocator.make!int;
1210 // It's implicitly initialized with its .init value
1211 assert(*p1 == 0);
1212 // Dynamically allocate one double, initialize to 42.5
1213 const double* p2 = theAllocator.make!double(42.5);
1214 assert(*p2 == 42.5);
1215
1216 // Dynamically allocate a struct
1217 static struct Point
1218 {
1219 int x, y, z;
1220 }
1221 // Use the generated constructor taking field values in order
1222 const Point* p = theAllocator.make!Point(1, 2);
1223 assert(p.x == 1 && p.y == 2 && p.z == 0);
1224
1225 // Dynamically allocate a class object
1226 static class Customer
1227 {
1228 uint id = uint.max;
this()1229 this() {}
this(uint id)1230 this(uint id) { this.id = id; }
1231 // ...
1232 }
1233 Customer cust = theAllocator.make!Customer;
1234 assert(cust.id == uint.max); // default initialized
1235 cust = theAllocator.make!Customer(42);
1236 assert(cust.id == 42);
1237
1238 // explicit passing of outer pointer
1239 static class Outer
1240 {
1241 int x = 3;
1242 class Inner
1243 {
getX()1244 auto getX() { return x; }
1245 }
1246 }
1247 auto outer = theAllocator.make!Outer();
1248 auto inner = theAllocator.make!(Outer.Inner)(outer);
1249 assert(outer.x == inner.getX);
1250 }
1251
1252 // https://issues.dlang.org/show_bug.cgi?id=15639
1253 // https://issues.dlang.org/show_bug.cgi?id=15772
1254 @system unittest
1255 {
1256 abstract class Foo {}
1257 class Bar: Foo {}
1258 static assert(!is(typeof(theAllocator.make!Foo)));
1259 static assert( is(typeof(theAllocator.make!Bar)));
1260 }
1261
1262 @system unittest
1263 {
test(Allocator)1264 void test(Allocator)(auto ref Allocator alloc)
1265 {
1266 const int* a = alloc.make!int(10);
1267 assert(*a == 10);
1268
1269 struct A
1270 {
1271 int x;
1272 string y;
1273 double z;
1274 }
1275
1276 A* b = alloc.make!A(42);
1277 assert(b.x == 42);
1278 assert(b.y is null);
1279 import std.math.traits : isNaN;
1280 assert(b.z.isNaN);
1281
1282 b = alloc.make!A(43, "44", 45);
1283 assert(b.x == 43);
1284 assert(b.y == "44");
1285 assert(b.z == 45);
1286
1287 static class B
1288 {
1289 int x;
1290 string y;
1291 double z;
1292 this(int _x, string _y = null, double _z = double.init)
1293 {
1294 x = _x;
1295 y = _y;
1296 z = _z;
1297 }
1298 }
1299
1300 B c = alloc.make!B(42);
1301 assert(c.x == 42);
1302 assert(c.y is null);
1303 assert(c.z.isNaN);
1304
1305 c = alloc.make!B(43, "44", 45);
1306 assert(c.x == 43);
1307 assert(c.y == "44");
1308 assert(c.z == 45);
1309
1310 const parray = alloc.make!(int[]);
1311 assert((*parray).empty);
1312 }
1313
1314 import std.experimental.allocator.gc_allocator : GCAllocator;
1315 test(GCAllocator.instance);
1316 test(theAllocator);
1317 }
1318
1319 // Attribute propagation
1320 nothrow @safe @nogc unittest
1321 {
1322 import std.experimental.allocator.mallocator : Mallocator;
1323 alias alloc = Mallocator.instance;
1324
test(T,Args...)1325 void test(T, Args...)(auto ref Args args)
1326 {
1327 auto k = alloc.make!T(args);
1328 () @trusted { alloc.dispose(k); }();
1329 }
1330
1331 test!int;
1332 test!(int*);
1333 test!int(0);
1334 test!(int*)(null);
1335 }
1336
1337 // should be pure with the GCAllocator
1338 /*pure nothrow*/ @safe unittest
1339 {
1340 import std.experimental.allocator.gc_allocator : GCAllocator;
1341
1342 alias alloc = GCAllocator.instance;
1343
test(T,Args...)1344 void test(T, Args...)(auto ref Args args)
1345 {
1346 auto k = alloc.make!T(args);
1347 (a) @trusted { a.dispose(k); }(alloc);
1348 }
1349
1350 test!int();
1351 test!(int*);
1352 test!int(0);
1353 test!(int*)(null);
1354 }
1355
1356 // Verify that making an object by calling an impure constructor is not @safe
1357 nothrow @safe @nogc unittest
1358 {
1359 import std.experimental.allocator.mallocator : Mallocator;
thisPure1360 static struct Pure { this(int) pure nothrow @nogc @safe {} }
1361
1362 cast(void) Mallocator.instance.make!Pure(0);
1363
1364 static int g = 0;
thisImpure1365 static struct Impure { this(int) nothrow @nogc @safe {
1366 g++;
1367 } }
1368 static assert(!__traits(compiles, cast(void) Mallocator.instance.make!Impure(0)));
1369 }
1370
1371 // test failure with a pure, failing struct
1372 @safe unittest
1373 {
1374 import std.exception : assertThrown, enforce;
1375
1376 // this struct can't be initialized
1377 struct InvalidStruct
1378 {
thisInvalidStruct1379 this(int b)
1380 {
1381 enforce(1 == 2);
1382 }
1383 }
1384 import std.experimental.allocator.mallocator : Mallocator;
1385 assertThrown(make!InvalidStruct(Mallocator.instance, 42));
1386 }
1387
1388 // test failure with an impure, failing struct
1389 @system unittest
1390 {
1391 import std.exception : assertThrown, enforce;
1392 static int g;
1393 struct InvalidImpureStruct
1394 {
thisInvalidImpureStruct1395 this(int b)
1396 {
1397 g++;
1398 enforce(1 == 2);
1399 }
1400 }
1401 import std.experimental.allocator.mallocator : Mallocator;
1402 assertThrown(make!InvalidImpureStruct(Mallocator.instance, 42));
1403 }
1404
1405 // Don't allow zero-ctor-args `make` for structs with `@disable this();`
1406 @system unittest
1407 {
1408 struct NoDefaultCtor
1409 {
1410 int i;
1411 @disable this();
1412 }
1413 import std.experimental.allocator.mallocator : Mallocator;
1414 static assert(!__traits(compiles, make!NoDefaultCtor(Mallocator.instance)),
1415 "Don't allow zero-ctor-args `make` for structs with `@disable this();`");
1416 }
1417
1418 // https://issues.dlang.org/show_bug.cgi?id=18937
1419 @safe unittest
1420 {
1421 static struct S
1422 {
1423 ubyte[16 * 1024] data;
1424 }
1425
1426 static struct SomeAllocator
1427 {
allocate(size_t)1428 ubyte[] allocate(size_t) { return []; }
deallocate(void[])1429 void deallocate(void[]) {}
1430 }
1431
1432 auto x = SomeAllocator().make!S();
1433 }
1434
1435 private void fillWithMemcpy(T)(scope void[] array, auto ref T filler) nothrow
1436 if (T.sizeof == 1)
1437 {
1438 import core.stdc.string : memset;
1439 import std.traits : CopyConstness;
1440 if (!array.length) return;
1441 memset(array.ptr, *cast(CopyConstness!(T*, ubyte*)) &filler, array.length);
1442 }
1443
1444 private void fillWithMemcpy(T)(scope void[] array, auto ref T filler) nothrow
1445 if (T.sizeof != 1)
1446 {
1447 import core.stdc.string : memcpy;
1448 import std.algorithm.comparison : min;
1449 if (!array.length) return;
1450 memcpy(array.ptr, &filler, T.sizeof);
1451 // Fill the array from the initialized portion of itself exponentially.
1452 for (size_t offset = T.sizeof; offset < array.length; )
1453 {
1454 size_t extent = min(offset, array.length - offset);
1455 memcpy(array.ptr + offset, array.ptr, extent);
1456 offset += extent;
1457 }
1458 }
1459
1460 @system unittest
1461 {
1462 // Test T.sizeof == 1 path of fillWithMemcpy.
1463 ubyte[] a;
1464 fillWithMemcpy(a, ubyte(42));
1465 assert(a.length == 0);
1466 a = [ 1, 2, 3, 4, 5 ];
1467 fillWithMemcpy(a, ubyte(42));
1468 assert(a == [ 42, 42, 42, 42, 42]);
1469 }
1470
1471 @system unittest
1472 {
1473 int[] a;
1474 fillWithMemcpy(a, 42);
1475 assert(a.length == 0);
1476 a = [ 1, 2, 3, 4, 5 ];
1477 fillWithMemcpy(a, 42);
1478 assert(a == [ 42, 42, 42, 42, 42]);
1479 }
1480
1481 //Make shared object
1482 @system unittest
1483 {
1484 import core.atomic : atomicLoad;
1485 auto psi = theAllocator.make!(shared(int))(10);
1486 assert(10 == (*psi).atomicLoad());
1487 }
1488
uninitializedFillDefault(T)1489 private T[] uninitializedFillDefault(T)(T[] array) nothrow
1490 {
1491 static if (__traits(isZeroInit, T))
1492 {
1493 import core.stdc.string : memset;
1494 if (array !is null)
1495 memset(array.ptr, 0, T.sizeof * array.length);
1496 return array;
1497 }
1498 else static if (is(immutable T == immutable char) || is(immutable T == immutable wchar))
1499 {
1500 import core.stdc.string : memset;
1501 if (array !is null)
1502 memset(array.ptr, 0xff, T.sizeof * array.length);
1503 return array;
1504 }
1505 else
1506 {
1507 T t = T.init;
1508 fillWithMemcpy(array, t);
1509 return array;
1510 }
1511 }
1512
1513 pure nothrow @nogc
1514 @system unittest
1515 {
1516 static struct S { int x = 42; @disable this(this); }
1517
1518 int[5] expected = [42, 42, 42, 42, 42];
1519 S[5] arr = void;
1520 uninitializedFillDefault(arr);
1521 assert((cast(int*) arr.ptr)[0 .. arr.length] == expected);
1522 }
1523
1524 @system unittest
1525 {
1526 int[] a = [1, 2, 4];
1527 uninitializedFillDefault(a);
1528 assert(a == [0, 0, 0]);
1529
1530 char[] b = [1, 2, 4];
1531 uninitializedFillDefault(b);
1532 assert(b == [0xff, 0xff, 0xff]);
1533
1534 wchar[] c = [1, 2, 4];
1535 uninitializedFillDefault(c);
1536 assert(c == [0xffff, 0xffff, 0xffff]);
1537 }
1538
1539 @system unittest
1540 {
1541 static struct P { float x = 0; float y = 0; }
1542
1543 static assert(__traits(isZeroInit, P));
1544 P[] a = [P(10, 11), P(20, 21), P(40, 41)];
1545 uninitializedFillDefault(a);
1546 assert(a == [P.init, P.init, P.init]);
1547 }
1548
1549 /**
1550 Create an array of `T` with `length` elements using `alloc`. The array is either default-initialized, filled with copies of `init`, or initialized with values fetched from `range`.
1551
1552 Params:
1553 T = element type of the array being created
1554 alloc = the allocator used for getting memory
1555 length = length of the newly created array
1556 init = element used for filling the array
1557 range = range used for initializing the array elements
1558
1559 Returns:
1560 The newly-created array, or `null` if either `length` was `0` or
1561 allocation failed.
1562
1563 Throws:
1564 The first two overloads throw only if `alloc`'s primitives do. The
1565 overloads that involve copy initialization deallocate memory and propagate the
1566 exception if the copy operation throws.
1567 */
makeArray(T,Allocator)1568 T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length)
1569 {
1570 if (!length) return null;
1571 static if (T.sizeof <= 1)
1572 {
1573 const nAlloc = length * T.sizeof;
1574 }
1575 else
1576 {
1577 import core.checkedint : mulu;
1578 bool overflow;
1579 const nAlloc = mulu(length, T.sizeof, overflow);
1580 if (overflow) return null;
1581 }
1582
1583 static if (__traits(isZeroInit, T) && hasMember!(Allocator, "allocateZeroed"))
1584 {
1585 auto m = alloc.allocateZeroed(nAlloc);
1586 return (() @trusted => cast(T[]) m)();
1587 }
1588 else
1589 {
1590 auto m = alloc.allocate(nAlloc);
1591 if (!m.ptr) return null;
1592 alias U = Unqual!T;
1593 return () @trusted { return cast(T[]) uninitializedFillDefault(cast(U[]) m); }();
1594 }
1595 }
1596
1597 @system unittest
1598 {
test1(A)1599 void test1(A)(auto ref A alloc)
1600 {
1601 int[] a = alloc.makeArray!int(0);
1602 assert(a.length == 0 && a.ptr is null);
1603 a = alloc.makeArray!int(5);
1604 assert(a.length == 5);
1605 static immutable cheatsheet = [0, 0, 0, 0, 0];
1606 assert(a == cheatsheet);
1607 }
1608
test2(A)1609 void test2(A)(auto ref A alloc)
1610 {
1611 static struct S { int x = 42; @disable this(this); }
1612 S[] arr = alloc.makeArray!S(5);
1613 assert(arr.length == 5);
1614 int[] arrInt = () @trusted { return (cast(int*) arr.ptr)[0 .. 5]; }();
1615 static immutable res = [42, 42, 42, 42, 42];
1616 assert(arrInt == res);
1617 }
1618
1619 import std.experimental.allocator.gc_allocator : GCAllocator;
1620 import std.experimental.allocator.mallocator : Mallocator;
1621 (alloc) /*pure nothrow*/ @safe { test1(alloc); test2(alloc);} (GCAllocator.instance);
1622 (alloc) nothrow @safe @nogc { test1(alloc); test2(alloc);} (Mallocator.instance);
1623 test2(theAllocator);
1624 }
1625
1626 @system unittest
1627 {
1628 import std.algorithm.comparison : equal;
1629 auto a = theAllocator.makeArray!(shared int)(5);
1630 static assert(is(typeof(a) == shared(int)[]));
1631 assert(a.length == 5);
1632 assert(a.equal([0, 0, 0, 0, 0]));
1633
1634 auto b = theAllocator.makeArray!(const int)(5);
1635 static assert(is(typeof(b) == const(int)[]));
1636 assert(b.length == 5);
1637 assert(b.equal([0, 0, 0, 0, 0]));
1638
1639 auto c = theAllocator.makeArray!(immutable int)(5);
1640 static assert(is(typeof(c) == immutable(int)[]));
1641 assert(c.length == 5);
1642 assert(c.equal([0, 0, 0, 0, 0]));
1643 }
1644
1645 // https://issues.dlang.org/show_bug.cgi?id=19085 - makeArray with void
1646 @system unittest
1647 {
1648 auto b = theAllocator.makeArray!void(5);
1649 scope(exit) theAllocator.dispose(b);
1650 auto c = cast(ubyte[]) b;
1651 assert(c.length == 5);
1652 assert(c == [0, 0, 0, 0, 0]); // default initialization
1653 }
1654
1655 private enum hasPurePostblit(T) = !hasElaborateCopyConstructor!T ||
1656 is(typeof(() pure { T.init.__xpostblit(); }));
1657
1658 private enum hasPureDtor(T) = !hasElaborateDestructor!T ||
1659 is(typeof(() pure { T.init.__xdtor(); }));
1660
1661 // `true` when postblit and destructor of T cannot escape references to itself
1662 private enum canSafelyDeallocPostRewind(T) = hasPurePostblit!T && hasPureDtor!T;
1663
1664 /// Ditto
makeArray(T,Allocator)1665 T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length, T init)
1666 {
1667 if (!length) return null;
1668 auto m = alloc.allocate(T.sizeof * length);
1669 if (!m.ptr) return null;
1670 auto result = () @trusted { return cast(T[]) m; } ();
1671 import std.traits : hasElaborateCopyConstructor;
1672 static if (hasElaborateCopyConstructor!T)
1673 {
1674 scope(failure)
1675 {
1676 static if (canSafelyDeallocPostRewind!T)
1677 () @trusted { alloc.deallocate(m); } ();
1678 else
1679 alloc.deallocate(m);
1680 }
1681
1682 size_t i = 0;
1683 static if (hasElaborateDestructor!T)
1684 {
1685 scope (failure)
1686 {
1687 foreach (j; 0 .. i)
1688 {
1689 destroy(result[j]);
1690 }
1691 }
1692 }
1693 import core.lifetime : emplace;
1694 for (; i < length; ++i)
1695 {
1696 emplace!T(&result[i], init);
1697 }
1698 }
1699 else
1700 {
1701 alias U = Unqual!T;
1702 () @trusted { fillWithMemcpy(cast(U[]) result, *(cast(U*) &init)); }();
1703 }
1704 return result;
1705 }
1706
1707 ///
1708 @system unittest
1709 {
1710 import std.algorithm.comparison : equal;
test(T)1711 static void test(T)()
1712 {
1713 T[] a = theAllocator.makeArray!T(2);
1714 assert(a.equal([0, 0]));
1715 a = theAllocator.makeArray!T(3, 42);
1716 assert(a.equal([42, 42, 42]));
1717 import std.range : only;
1718 a = theAllocator.makeArray!T(only(42, 43, 44));
1719 assert(a.equal([42, 43, 44]));
1720 }
1721 test!int();
1722 test!(shared int)();
1723 test!(const int)();
1724 test!(immutable int)();
1725 }
1726
1727 @system unittest
1728 {
test(T)1729 void test(T)(in T initialValue)
1730 {
1731 auto t = theAllocator.makeArray!T(100, initialValue);
1732 //auto t = theAllocator.makeArray(100, initialValue); // works well with the old code
1733 }
1734
1735 const int init = 3;
1736 test(init);
1737 }
1738
1739 @system unittest
1740 {
test(A)1741 void test(A)(auto ref A alloc)
1742 {
1743 long[] a = alloc.makeArray!long(0, 42);
1744 assert(a.length == 0 && a.ptr is null);
1745 a = alloc.makeArray!long(5, 42);
1746 assert(a.length == 5);
1747 assert(a == [ 42, 42, 42, 42, 42 ]);
1748 }
1749 import std.experimental.allocator.gc_allocator : GCAllocator;
1750 (alloc) /*pure nothrow*/ @safe { test(alloc); } (GCAllocator.instance);
1751 test(theAllocator);
1752 }
1753
1754 // test failure with a pure, failing struct
1755 @safe unittest
1756 {
1757 import std.exception : assertThrown, enforce;
1758
1759 struct NoCopy
1760 {
1761 @disable this();
1762
thisNoCopy1763 this(int b){}
1764
1765 // can't be copied
thisNoCopy1766 this(this)
1767 {
1768 enforce(1 == 2);
1769 }
1770 }
1771 import std.experimental.allocator.mallocator : Mallocator;
1772 assertThrown(makeArray!NoCopy(Mallocator.instance, 10, NoCopy(42)));
1773 }
1774
1775 // test failure with an impure, failing struct
1776 @system unittest
1777 {
1778 import std.exception : assertThrown, enforce;
1779
1780 static int i = 0;
1781 struct Singleton
1782 {
1783 @disable this();
1784
thisSingleton1785 this(int b){}
1786
1787 // can't be copied
thisSingleton1788 this(this)
1789 {
1790 enforce(i++ == 0);
1791 }
1792
~thisSingleton1793 ~this()
1794 {
1795 i--;
1796 }
1797 }
1798 import std.experimental.allocator.mallocator : Mallocator;
1799 assertThrown(makeArray!Singleton(Mallocator.instance, 10, Singleton(42)));
1800 }
1801
1802 /// Ditto
1803 Unqual!(ElementEncodingType!R)[] makeArray(Allocator, R)(auto ref Allocator alloc, R range)
1804 if (isInputRange!R && !isInfinite!R)
1805 {
1806 alias T = Unqual!(ElementEncodingType!R);
1807 return makeArray!(T, Allocator, R)(alloc, range);
1808 }
1809
1810 /// Ditto
1811 T[] makeArray(T, Allocator, R)(auto ref Allocator alloc, R range)
1812 if (isInputRange!R && !isInfinite!R)
1813 {
1814 static if (isForwardRange!R || hasLength!R)
1815 {
1816 static if (hasLength!R || isNarrowString!R)
1817 immutable length = range.length;
1818 else
1819 immutable length = range.save.walkLength;
1820
1821 if (!length) return null;
1822 auto m = alloc.allocate(T.sizeof * length);
1823 if (!m.ptr) return null;
1824 auto result = () @trusted { return cast(T[]) m; } ();
1825
1826 size_t i = 0;
scope(failure)1827 scope (failure)
1828 {
1829 foreach (j; 0 .. i)
1830 {
1831 auto p = () @trusted { return cast(Unqual!T*) &result[j]; }();
1832 destroy(p);
1833 }
1834
1835 static if (canSafelyDeallocPostRewind!T)
1836 () @trusted { alloc.deallocate(m); } ();
1837 else
1838 alloc.deallocate(m);
1839 }
1840
1841 import core.internal.lifetime : emplaceRef;
1842 static if (isNarrowString!R || isRandomAccessRange!R)
1843 {
1844 foreach (j; 0 .. range.length)
1845 {
1846 emplaceRef!T(result[i++], range[j]);
1847 }
1848 }
1849 else
1850 {
1851 for (; !range.empty; range.popFront, ++i)
1852 {
1853 emplaceRef!T(result[i], range.front);
1854 }
1855 }
1856
1857 return result;
1858 }
1859 else
1860 {
1861 // Estimated size
1862 size_t estimated = 8;
1863 auto m = alloc.allocate(T.sizeof * estimated);
1864 if (!m.ptr) return null;
1865 auto result = () @trusted { return cast(T[]) m; } ();
1866
1867 size_t initialized = 0;
bailout()1868 void bailout()
1869 {
1870 foreach (i; 0 .. initialized + 1)
1871 {
1872 destroy(result[i]);
1873 }
1874
1875 static if (canSafelyDeallocPostRewind!T)
1876 () @trusted { alloc.deallocate(m); } ();
1877 else
1878 alloc.deallocate(m);
1879 }
1880 scope (failure) bailout;
1881
1882 for (; !range.empty; range.popFront, ++initialized)
1883 {
1884 if (initialized == estimated)
1885 {
1886 // Need to reallocate
1887 static if (hasPurePostblit!T)
1888 auto success = () @trusted { return alloc.reallocate(m, T.sizeof * (estimated *= 2)); } ();
1889 else
1890 auto success = alloc.reallocate(m, T.sizeof * (estimated *= 2));
1891 if (!success)
1892 {
1893 bailout;
1894 return null;
1895 }
1896 result = () @trusted { return cast(T[]) m; } ();
1897 }
1898 import core.internal.lifetime : emplaceRef;
1899 emplaceRef(result[initialized], range.front);
1900 }
1901
1902 if (initialized < estimated)
1903 {
1904 // Try to shrink memory, no harm if not possible
1905 static if (hasPurePostblit!T)
1906 auto success = () @trusted { return alloc.reallocate(m, T.sizeof * initialized); } ();
1907 else
1908 auto success = alloc.reallocate(m, T.sizeof * initialized);
1909 if (success)
1910 result = () @trusted { return cast(T[]) m; } ();
1911 }
1912
1913 return result[0 .. initialized];
1914 }
1915 }
1916
1917 @system unittest
1918 {
test(A)1919 void test(A)(auto ref A alloc)
1920 {
1921 long[] a = alloc.makeArray!long((int[]).init);
1922 assert(a.length == 0 && a.ptr is null);
1923 a = alloc.makeArray!long([5, 42]);
1924 assert(a.length == 2);
1925 assert(a == [ 5, 42]);
1926
1927 // we can also infer the type
1928 auto b = alloc.makeArray([4.0, 2.0]);
1929 static assert(is(typeof(b) == double[]));
1930 assert(b == [4.0, 2.0]);
1931 }
1932 import std.experimental.allocator.gc_allocator : GCAllocator;
1933 (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
1934 test(theAllocator);
1935 }
1936
1937 // infer types for strings
1938 @system unittest
1939 {
test(A)1940 void test(A)(auto ref A alloc)
1941 {
1942 auto c = alloc.makeArray("fooπ");
1943 static assert(is(typeof(c) == char[]));
1944 assert(c == "fooπ");
1945
1946 auto d = alloc.makeArray("fooπ"d);
1947 static assert(is(typeof(d) == dchar[]));
1948 assert(d == "fooπ");
1949
1950 auto w = alloc.makeArray("fooπ"w);
1951 static assert(is(typeof(w) == wchar[]));
1952 assert(w == "fooπ");
1953 }
1954
1955 import std.experimental.allocator.gc_allocator : GCAllocator;
1956 (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
1957 test(theAllocator);
1958 }
1959
1960 /*pure*/ nothrow @safe unittest
1961 {
1962 import std.algorithm.comparison : equal;
1963 import std.experimental.allocator.gc_allocator : GCAllocator;
1964 import std.internal.test.dummyrange;
1965 import std.range : iota;
foreach(DummyType;AllDummyRanges)1966 foreach (DummyType; AllDummyRanges)
1967 {
1968 (alloc) pure nothrow @safe
1969 {
1970 DummyType d;
1971 auto arr = alloc.makeArray(d);
1972 assert(arr.length == 10);
1973 assert(arr.equal(iota(1, 11)));
1974 } (GCAllocator.instance);
1975 }
1976 }
1977
1978 // test failure with a pure, failing struct
1979 @safe unittest
1980 {
1981 import std.exception : assertThrown, enforce;
1982
1983 struct NoCopy
1984 {
1985 int b;
1986
1987 @disable this();
1988
thisNoCopy1989 this(int b)
1990 {
1991 this.b = b;
1992 }
1993
1994 // can't be copied
thisNoCopy1995 this(this)
1996 {
1997 enforce(b < 3, "there can only be three elements");
1998 }
1999 }
2000 import std.experimental.allocator.mallocator : Mallocator;
2001 auto arr = [NoCopy(1), NoCopy(2), NoCopy(3)];
2002 assertThrown(makeArray!NoCopy(Mallocator.instance, arr));
2003
2004 struct NoCopyRange
2005 {
2006 static j = 0;
emptyNoCopyRange2007 bool empty()
2008 {
2009 return j > 5;
2010 }
2011
frontNoCopyRange2012 auto front()
2013 {
2014 return NoCopy(j);
2015 }
2016
popFrontNoCopyRange2017 void popFront()
2018 {
2019 j++;
2020 }
2021 }
2022 makeArray!NoCopy(Mallocator.instance, NoCopyRange()); // rvalue elements are forwarded/moved
2023 }
2024
2025 // test failure with an impure, failing struct
2026 @system unittest
2027 {
2028 import std.exception : assertThrown, enforce;
2029
2030 static i = 0;
2031 static maxElements = 2;
2032 struct NoCopy
2033 {
2034 int val;
2035 @disable this();
2036
thisNoCopy2037 this(int b){
2038 this.val = i++;
2039 }
2040
2041 // can't be copied
thisNoCopy2042 this(this)
2043 {
2044 enforce(i++ < maxElements, "there can only be four elements");
2045 }
2046 }
2047
2048 import std.experimental.allocator.mallocator : Mallocator;
2049 auto arr = [NoCopy(1), NoCopy(2)];
2050 assertThrown(makeArray!NoCopy(Mallocator.instance, arr));
2051
2052 i = 0;
2053 maxElements = 0; // disallow any postblit
2054 static j = 0;
2055
2056 struct NoCopyRange
2057 {
emptyNoCopyRange2058 bool empty()
2059 {
2060 return j > 100;
2061 }
2062
frontNoCopyRange2063 auto front()
2064 {
2065 return NoCopy(1);
2066 }
2067
popFrontNoCopyRange2068 void popFront()
2069 {
2070 j++;
2071 }
2072 }
2073
2074 auto arr2 = makeArray!NoCopy(Mallocator.instance, NoCopyRange());
2075 assert(i == j && i == 101); // all 101 rvalue elements forwarded/moved
2076 }
2077
version(StdUnittest)2078 version (StdUnittest)
2079 {
2080 private struct ForcedInputRange(T)
2081 {
2082 T[]* array;
2083 pure nothrow @safe @nogc:
2084 bool empty() { return !array || (*array).empty; }
2085 ref T front() { return (*array)[0]; }
2086 void popFront() { *array = (*array)[1 .. $]; }
2087 }
2088 }
2089
2090 @system unittest
2091 {
2092 import std.array : array;
2093 import std.range : iota;
2094 int[] arr = iota(10).array;
2095
test(A)2096 void test(A)(auto ref A alloc)
2097 {
2098 ForcedInputRange!int r;
2099 long[] a = alloc.makeArray!long(r);
2100 assert(a.length == 0 && a.ptr is null);
2101 auto arr2 = arr;
2102 r.array = () @trusted { return &arr2; } ();
2103 a = alloc.makeArray!long(r);
2104 assert(a.length == 10);
2105 assert(a == iota(10).array);
2106 }
2107 import std.experimental.allocator.gc_allocator : GCAllocator;
2108 (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
2109 test(theAllocator);
2110 }
2111
2112 /**
2113 Grows `array` by appending `delta` more elements. The needed memory is
2114 allocated using `alloc`. The extra elements added are either default-
2115 initialized, filled with copies of `init`, or initialized with values
2116 fetched from `range`.
2117
2118 Params:
2119 T = element type of the array being created
2120 alloc = the allocator used for getting memory
2121 array = a reference to the array being grown
2122 delta = number of elements to add (upon success the new length of `array` is
2123 $(D array.length + delta))
2124 init = element used for filling the array
2125 range = range used for initializing the array elements
2126
2127 Returns:
2128 `true` upon success, `false` if memory could not be allocated. In the
2129 latter case `array` is left unaffected.
2130
2131 Throws:
2132 The first two overloads throw only if `alloc`'s primitives do. The
2133 overloads that involve copy initialization deallocate memory and propagate the
2134 exception if the copy operation throws.
2135 */
expandArray(T,Allocator)2136 bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array,
2137 size_t delta)
2138 {
2139 if (!delta) return true;
2140 if (array is null) return false;
2141 immutable oldLength = array.length;
2142 void[] buf = array;
2143 if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false;
2144 array = cast(T[]) buf;
2145 array[oldLength .. $].uninitializedFillDefault;
2146 return true;
2147 }
2148
2149 @system unittest
2150 {
test(A)2151 void test(A)(auto ref A alloc)
2152 {
2153 auto arr = alloc.makeArray!int([1, 2, 3]);
2154 assert(alloc.expandArray(arr, 3));
2155 assert(arr == [1, 2, 3, 0, 0, 0]);
2156 }
2157 import std.experimental.allocator.gc_allocator : GCAllocator;
2158 test(GCAllocator.instance);
2159 test(theAllocator);
2160 }
2161
2162 /// Ditto
expandArray(T,Allocator)2163 bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array,
2164 size_t delta, auto ref T init)
2165 {
2166 if (!delta) return true;
2167 if (array is null) return false;
2168 void[] buf = array;
2169 if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false;
2170 immutable oldLength = array.length;
2171 array = cast(T[]) buf;
2172 scope(failure) array[oldLength .. $].uninitializedFillDefault;
2173 import std.algorithm.mutation : uninitializedFill;
2174 array[oldLength .. $].uninitializedFill(init);
2175 return true;
2176 }
2177
2178 @system unittest
2179 {
test(A)2180 void test(A)(auto ref A alloc)
2181 {
2182 auto arr = alloc.makeArray!int([1, 2, 3]);
2183 assert(alloc.expandArray(arr, 3, 1));
2184 assert(arr == [1, 2, 3, 1, 1, 1]);
2185 }
2186 import std.experimental.allocator.gc_allocator : GCAllocator;
2187 test(GCAllocator.instance);
2188 test(theAllocator);
2189 }
2190
2191 /// Ditto
2192 bool expandArray(T, Allocator, R)(auto ref Allocator alloc, ref T[] array,
2193 R range)
2194 if (isInputRange!R)
2195 {
2196 if (array is null) return false;
2197 static if (isForwardRange!R)
2198 {
2199 immutable delta = walkLength(range.save);
2200 if (!delta) return true;
2201 immutable oldLength = array.length;
2202
2203 // Reallocate support memory
2204 void[] buf = array;
2205 if (!alloc.reallocate(buf, buf.length + T.sizeof * delta))
2206 {
2207 return false;
2208 }
2209 array = cast(T[]) buf;
2210 // At this point we're committed to the new length.
2211
2212 auto toFill = array[oldLength .. $];
scope(failure)2213 scope (failure)
2214 {
2215 // Fill the remainder with default-constructed data
2216 toFill.uninitializedFillDefault;
2217 }
2218
2219 for (; !range.empty; range.popFront, toFill = toFill[1 .. $])
2220 {
2221 assert(toFill.length > 0);
2222 import core.lifetime : emplace;
2223 emplace!T(&toFill[0], range.front);
2224 }
2225 assert(toFill.length == 0);
2226 }
2227 else
2228 {
scope(failure)2229 scope(failure)
2230 {
2231 // The last element didn't make it, fill with default
2232 array[$ - 1 .. $].uninitializedFillDefault;
2233 }
2234 void[] buf = array;
2235 for (; !range.empty; range.popFront)
2236 {
2237 if (!alloc.reallocate(buf, buf.length + T.sizeof))
2238 {
2239 array = cast(T[]) buf;
2240 return false;
2241 }
2242 import core.lifetime : emplace;
2243 emplace!T(buf[$ - T.sizeof .. $], range.front);
2244 }
2245
2246 array = cast(T[]) buf;
2247 }
2248 return true;
2249 }
2250
2251 ///
2252 @system unittest
2253 {
2254 auto arr = theAllocator.makeArray!int([1, 2, 3]);
2255 assert(theAllocator.expandArray(arr, 2));
2256 assert(arr == [1, 2, 3, 0, 0]);
2257 import std.range : only;
2258 assert(theAllocator.expandArray(arr, only(4, 5)));
2259 assert(arr == [1, 2, 3, 0, 0, 4, 5]);
2260 }
2261
2262 @system unittest
2263 {
2264 auto arr = theAllocator.makeArray!int([1, 2, 3]);
2265 ForcedInputRange!int r;
2266 int[] b = [ 1, 2, 3, 4 ];
2267 auto temp = b;
2268 r.array = &temp;
2269 assert(theAllocator.expandArray(arr, r));
2270 assert(arr == [1, 2, 3, 1, 2, 3, 4]);
2271 }
2272
2273 // Regression test for https://issues.dlang.org/show_bug.cgi?id=20929
2274 @system unittest
2275 {
test(Char,Allocator)2276 static void test(Char, Allocator)(auto ref Allocator alloc)
2277 {
2278 auto arr = alloc.makeArray!Char(1, Char('f'));
2279
2280 import std.utf : byUTF;
2281 auto forwardRange = "oo".byUTF!Char();
2282 static assert(isForwardRange!(typeof(forwardRange)));
2283 // Test the forward-range code-path.
2284 assert(alloc.expandArray(arr, forwardRange));
2285
2286 assert(arr == "foo");
2287
2288 immutable(Char)[] temp = "bar";
2289 auto inputRange = ForcedInputRange!(immutable(Char))(&temp);
2290 // Test the input-range code-path.
2291 assert(alloc.expandArray(arr, inputRange));
2292
2293 assert(arr == "foobar");
2294 }
2295
2296 import std.experimental.allocator.gc_allocator : GCAllocator;
2297 test!char(GCAllocator.instance);
2298 test!wchar(GCAllocator.instance);
2299 test!char(theAllocator);
2300 test!wchar(theAllocator);
2301 }
2302
2303 /**
2304 Shrinks an array by `delta` elements.
2305
2306 If $(D array.length < delta), does nothing and returns `false`. Otherwise,
2307 destroys the last $(D array.length - delta) elements in the array and then
2308 reallocates the array's buffer. If reallocation fails, fills the array with
2309 default-initialized data.
2310
2311 Params:
2312 T = element type of the array being created
2313 alloc = the allocator used for getting memory
2314 array = a reference to the array being shrunk
2315 delta = number of elements to remove (upon success the new length of `array` is $(D array.length - delta))
2316
2317 Returns:
2318 `true` upon success, `false` if memory could not be reallocated. In the latter
2319 case, the slice $(D array[$ - delta .. $]) is left with default-initialized
2320 elements.
2321
2322 Throws:
2323 The first two overloads throw only if `alloc`'s primitives do. The
2324 overloads that involve copy initialization deallocate memory and propagate the
2325 exception if the copy operation throws.
2326 */
shrinkArray(T,Allocator)2327 bool shrinkArray(T, Allocator)(auto ref Allocator alloc,
2328 ref T[] array, size_t delta)
2329 {
2330 if (delta > array.length) return false;
2331
2332 // Destroy elements. If a destructor throws, fill the already destroyed
2333 // stuff with the default initializer.
2334 {
2335 size_t destroyed;
2336 scope(failure)
2337 {
2338 array[$ - delta .. $][0 .. destroyed].uninitializedFillDefault;
2339 }
2340 foreach (ref e; array[$ - delta .. $])
2341 {
2342 e.destroy;
2343 ++destroyed;
2344 }
2345 }
2346
2347 if (delta == array.length)
2348 {
2349 alloc.deallocate(array);
2350 array = null;
2351 return true;
2352 }
2353
2354 void[] buf = array;
2355 if (!alloc.reallocate(buf, buf.length - T.sizeof * delta))
2356 {
2357 // urgh, at least fill back with default
2358 array[$ - delta .. $].uninitializedFillDefault;
2359 return false;
2360 }
2361 array = cast(T[]) buf;
2362 return true;
2363 }
2364
2365 ///
2366 @system unittest
2367 {
2368 int[] a = theAllocator.makeArray!int(100, 42);
2369 assert(a.length == 100);
2370 assert(theAllocator.shrinkArray(a, 98));
2371 assert(a.length == 2);
2372 assert(a == [42, 42]);
2373 }
2374
2375 @system unittest
2376 {
test(A)2377 void test(A)(auto ref A alloc)
2378 {
2379 long[] a = alloc.makeArray!long((int[]).init);
2380 assert(a.length == 0 && a.ptr is null);
2381 a = alloc.makeArray!long(100, 42);
2382 assert(alloc.shrinkArray(a, 98));
2383 assert(a.length == 2);
2384 assert(a == [ 42, 42]);
2385 }
2386 import std.experimental.allocator.gc_allocator : GCAllocator;
2387 test(GCAllocator.instance);
2388 test(theAllocator);
2389 }
2390
2391 /**
2392
2393 Destroys and then deallocates (using `alloc`) the object pointed to by a
2394 pointer, the class object referred to by a `class` or `interface`
2395 reference, or an entire array. It is assumed the respective entities had been
2396 allocated with the same allocator.
2397
2398 */
dispose(A,T)2399 void dispose(A, T)(auto ref A alloc, auto ref T* p)
2400 {
2401 static if (hasElaborateDestructor!T)
2402 {
2403 destroy(*p);
2404 }
2405 alloc.deallocate((cast(void*) p)[0 .. T.sizeof]);
2406 static if (__traits(isRef, p))
2407 p = null;
2408 }
2409
2410 /// Ditto
2411 void dispose(A, T)(auto ref A alloc, auto ref T p)
2412 if (is(T == class) || is(T == interface))
2413 {
2414 if (!p) return;
2415 static if (is(T == interface))
2416 {
version(Windows)2417 version (Windows)
2418 {
2419 import core.sys.windows.unknwn : IUnknown;
2420 static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in "
2421 ~ __PRETTY_FUNCTION__);
2422 }
2423 auto ob = cast(Object) p;
2424 }
2425 else
2426 alias ob = p;
2427 auto support = (cast(void*) ob)[0 .. typeid(ob).initializer.length];
2428 destroy(p);
2429 alloc.deallocate(support);
2430 static if (__traits(isRef, p))
2431 p = null;
2432 }
2433
2434 /// Ditto
dispose(A,T)2435 void dispose(A, T)(auto ref A alloc, auto ref T[] array)
2436 {
2437 static if (hasElaborateDestructor!(typeof(array[0])))
2438 {
2439 foreach (ref e; array)
2440 {
2441 destroy(e);
2442 }
2443 }
2444 alloc.deallocate(array);
2445 static if (__traits(isRef, array))
2446 array = null;
2447 }
2448
2449 @system unittest
2450 {
2451 static int x;
2452 static interface I
2453 {
2454 void method();
2455 }
2456 static class A : I
2457 {
2458 int y;
method()2459 override void method() { x = 21; }
~this()2460 ~this() { x = 42; }
2461 }
2462 static class B : A
2463 {
2464 }
2465 auto a = theAllocator.make!A;
2466 a.method();
2467 assert(x == 21);
2468 theAllocator.dispose(a);
2469 assert(x == 42);
2470
2471 B b = theAllocator.make!B;
2472 b.method();
2473 assert(x == 21);
2474 theAllocator.dispose(b);
2475 assert(x == 42);
2476
2477 I i = theAllocator.make!B;
2478 i.method();
2479 assert(x == 21);
2480 theAllocator.dispose(i);
2481 assert(x == 42);
2482
2483 int[] arr = theAllocator.makeArray!int(43);
2484 theAllocator.dispose(arr);
2485 }
2486
2487 // https://issues.dlang.org/show_bug.cgi?id=16512
2488 @system unittest
2489 {
2490 import std.experimental.allocator.mallocator : Mallocator;
2491
2492 int* i = Mallocator.instance.make!int(0);
2493 Mallocator.instance.dispose(i);
2494 assert(i is null);
2495
2496 Object o = Mallocator.instance.make!Object();
2497 Mallocator.instance.dispose(o);
2498 assert(o is null);
2499
2500 uint* u = Mallocator.instance.make!uint(0);
2501 Mallocator.instance.dispose((){return u;}());
2502 assert(u !is null);
2503
2504 uint[] ua = Mallocator.instance.makeArray!uint([0,1,2]);
2505 Mallocator.instance.dispose(ua);
2506 assert(ua is null);
2507 }
2508
2509 // https://issues.dlang.org/show_bug.cgi?id=15721
2510 @system unittest
2511 {
2512 import std.experimental.allocator.mallocator : Mallocator;
2513
2514 interface Foo {}
2515 class Bar: Foo {}
2516
2517 Bar bar;
2518 Foo foo;
2519 bar = Mallocator.instance.make!Bar;
2520 foo = cast(Foo) bar;
2521 Mallocator.instance.dispose(foo);
2522 }
2523
2524 /**
2525 Allocates a multidimensional array of elements of type T.
2526
2527 Params:
2528 N = number of dimensions
2529 T = element type of an element of the multidimensional arrat
2530 alloc = the allocator used for getting memory
2531 lengths = static array containing the size of each dimension
2532
2533 Returns:
2534 An N-dimensional array with individual elements of type T.
2535 */
makeMultidimensionalArray(T,Allocator,size_t N)2536 auto makeMultidimensionalArray(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...)
2537 {
2538 static if (N == 1)
2539 {
2540 return makeArray!T(alloc, lengths[0]);
2541 }
2542 else
2543 {
2544 alias E = typeof(makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $]));
2545 auto ret = makeArray!E(alloc, lengths[0]);
2546 foreach (ref e; ret)
2547 e = makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $]);
2548 return ret;
2549 }
2550 }
2551
2552 ///
2553 @system unittest
2554 {
2555 import std.experimental.allocator.mallocator : Mallocator;
2556
2557 auto mArray = Mallocator.instance.makeMultidimensionalArray!int(2, 3, 6);
2558
2559 // deallocate when exiting scope
scope(exit)2560 scope(exit)
2561 {
2562 Mallocator.instance.disposeMultidimensionalArray(mArray);
2563 }
2564
2565 assert(mArray.length == 2);
foreach(lvl2Array;mArray)2566 foreach (lvl2Array; mArray)
2567 {
2568 assert(lvl2Array.length == 3);
2569 foreach (lvl3Array; lvl2Array)
2570 assert(lvl3Array.length == 6);
2571 }
2572 }
2573
2574 /**
2575 Destroys and then deallocates a multidimensional array, assuming it was
2576 created with makeMultidimensionalArray and the same allocator was used.
2577
2578 Params:
2579 T = element type of an element of the multidimensional array
2580 alloc = the allocator used for getting memory
2581 array = the multidimensional array that is to be deallocated
2582 */
disposeMultidimensionalArray(T,Allocator)2583 void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, auto ref T[] array)
2584 {
2585 static if (isArray!T)
2586 {
2587 foreach (ref e; array)
2588 disposeMultidimensionalArray(alloc, e);
2589 }
2590
2591 dispose(alloc, array);
2592 static if (__traits(isRef, array))
2593 array = null;
2594 }
2595
2596 ///
2597 @system unittest
2598 {
2599 struct TestAllocator
2600 {
2601 import std.experimental.allocator.common : platformAlignment;
2602 import std.experimental.allocator.mallocator : Mallocator;
2603
2604 alias allocator = Mallocator.instance;
2605
2606 private static struct ByteRange
2607 {
2608 void* ptr;
2609 size_t length;
2610 }
2611
2612 private ByteRange[] _allocations;
2613
2614 enum uint alignment = platformAlignment;
2615
allocateTestAllocator2616 void[] allocate(size_t numBytes)
2617 {
2618 auto ret = allocator.allocate(numBytes);
2619 _allocations ~= ByteRange(ret.ptr, ret.length);
2620 return ret;
2621 }
2622
deallocateTestAllocator2623 bool deallocate(void[] bytes)
2624 {
2625 import std.algorithm.mutation : remove;
2626 import std.algorithm.searching : canFind;
2627
2628 bool pred(ByteRange other)
2629 { return other.ptr == bytes.ptr && other.length == bytes.length; }
2630
2631 assert(_allocations.canFind!pred);
2632
2633 _allocations = _allocations.remove!pred;
2634 return allocator.deallocate(bytes);
2635 }
2636
~thisTestAllocator2637 ~this()
2638 {
2639 assert(!_allocations.length);
2640 }
2641 }
2642
2643 TestAllocator allocator;
2644
2645 auto mArray = allocator.makeMultidimensionalArray!int(2, 3, 5, 6, 7, 2);
2646
2647 allocator.disposeMultidimensionalArray(mArray);
2648 }
2649
2650 /**
2651
2652 Returns a dynamically-typed `CAllocator` built around a given statically-
2653 typed allocator `a` of type `A`. Passing a pointer to the allocator
2654 creates a dynamic allocator around the allocator pointed to by the pointer,
2655 without attempting to copy or move it. Passing the allocator by value or
2656 reference behaves as follows.
2657
2658 $(UL
2659 $(LI If `A` has no state, the resulting object is allocated in static
2660 shared storage.)
2661 $(LI If `A` has state, the result will $(REF move, std,algorithm,mutation)
2662 the supplied allocator $(D A a) within. The result itself is allocated in its
2663 own statically-typed allocator.)
2664 )
2665
2666 */
2667 RCIAllocator allocatorObject(A)(auto ref A a)
2668 if (!isPointer!A)
2669 {
2670 import core.lifetime : emplace;
2671 static if (stateSize!A == 0)
2672 {
2673 enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof);
2674 __gshared ulong[s] state;
2675 __gshared RCIAllocator result;
2676 if (result.isNull)
2677 {
2678 // Don't care about a few races
2679 result = RCIAllocator(emplace!(CAllocatorImpl!A)(state[]));
2680 }
2681 assert(!result.isNull);
2682 return result;
2683 }
2684 else
2685 {
2686 auto state = a.allocate(stateSize!(CAllocatorImpl!A));
2687 import std.algorithm.mutation : move;
2688 import std.traits : hasMember;
2689 static if (hasMember!(A, "deallocate"))
2690 {
2691 scope(failure) a.deallocate(state);
2692 }
2693 auto tmp = cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state);
2694 move(a, tmp.impl);
2695 return RCIAllocator(tmp);
2696 }
2697 }
2698
2699 /// Ditto
allocatorObject(A)2700 RCIAllocator allocatorObject(A)(A* pa)
2701 {
2702 assert(pa);
2703 import core.lifetime : emplace;
2704 auto state = pa.allocate(stateSize!(CAllocatorImpl!(A, Yes.indirect)));
2705 import std.traits : hasMember;
2706 static if (hasMember!(A, "deallocate"))
2707 {
2708 scope(failure) pa.deallocate(state);
2709 }
2710 return RCIAllocator(emplace!(CAllocatorImpl!(A, Yes.indirect))
2711 (state, pa));
2712 }
2713
2714 ///
2715 @system unittest
2716 {
2717 import std.experimental.allocator.mallocator : Mallocator;
2718
2719 RCIAllocator a = allocatorObject(Mallocator.instance);
2720 auto b = a.allocate(100);
2721 assert(b.length == 100);
2722 assert(a.deallocate(b));
2723
2724 // The in-situ region must be used by pointer
2725 import std.experimental.allocator.building_blocks.region : InSituRegion;
2726 auto r = InSituRegion!1024();
2727 a = allocatorObject(&r);
2728 b = a.allocate(200);
2729 assert(b.length == 200);
2730 // In-situ regions can deallocate the last allocation
2731 assert(a.deallocate(b));
2732 }
2733
2734 @system unittest
2735 {
2736 import std.conv;
2737 import std.experimental.allocator.mallocator;
2738 import std.experimental.allocator.building_blocks.stats_collector;
2739
2740 alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
2741 SCAlloc statsCollectorAlloc;
2742 assert(statsCollectorAlloc.bytesUsed == 0);
2743
2744 auto _allocator = allocatorObject(statsCollectorAlloc);
2745 // Ensure that the allocator was passed through in CAllocatorImpl
2746 // This allocator was used to allocate the chunk that holds the
2747 // CAllocatorImpl object; which is it's own wrapper
2748 assert((cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed
2749 == stateSize!(CAllocatorImpl!(SCAlloc)));
2750 _allocator.allocate(1);
2751 assert((cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed
2752 == stateSize!(CAllocatorImpl!(SCAlloc)) + 1);
2753 }
2754
2755 /**
2756
2757 Returns a dynamically-typed `CSharedAllocator` built around a given statically-
2758 typed allocator `a` of type `A`. Passing a pointer to the allocator
2759 creates a dynamic allocator around the allocator pointed to by the pointer,
2760 without attempting to copy or move it. Passing the allocator by value or
2761 reference behaves as follows.
2762
2763 $(UL
2764 $(LI If `A` has no state, the resulting object is allocated in static
2765 shared storage.)
2766 $(LI If `A` has state and is copyable, the result will
2767 $(REF move, std,algorithm,mutation) the supplied allocator $(D A a) within.
2768 The result itself is allocated in its own statically-typed allocator.)
2769 $(LI If `A` has state and is not copyable, the result will move the
2770 passed-in argument into the result. The result itself is allocated in its own
2771 statically-typed allocator.)
2772 )
2773
2774 */
2775 //nothrow @safe
2776 //nothrow @nogc @safe
2777 nothrow
2778 RCISharedAllocator sharedAllocatorObject(A)(auto ref A a)
2779 if (!isPointer!A)
2780 {
2781 import core.lifetime : emplace;
2782 static if (stateSize!A == 0)
2783 {
2784 enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof);
2785 static shared ulong[s] state;
2786 static RCISharedAllocator result;
2787 if (result.isNull)
2788 {
2789 // Don't care about a few races
2790 result = RCISharedAllocator(
2791 (cast(shared CSharedAllocatorImpl!A)(
2792 emplace!(CSharedAllocatorImpl!A)(
2793 (() @trusted => cast(ulong[]) state[])()))));
2794 }
2795 assert(!result.isNull);
2796 return result;
2797 }
2798 else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable
2799 {
2800 auto state = a.allocate(stateSize!(CSharedAllocatorImpl!A));
2801 import std.algorithm.mutation : move;
2802 import std.traits : hasMember;
2803 static if (hasMember!(A, "deallocate"))
2804 {
2805 scope(failure) a.deallocate(state);
2806 }
2807 auto tmp = emplace!(shared CSharedAllocatorImpl!A)(state);
2808 move(a, tmp.impl);
2809 return RCISharedAllocator(tmp);
2810 }
2811 else // the allocator object is not copyable
2812 {
2813 assert(0, "Not yet implemented");
2814 }
2815 }
2816
2817 /// Ditto
sharedAllocatorObject(A)2818 RCISharedAllocator sharedAllocatorObject(A)(A* pa)
2819 {
2820 assert(pa);
2821 import core.lifetime : emplace;
2822 auto state = pa.allocate(stateSize!(CSharedAllocatorImpl!(A, Yes.indirect)));
2823 import std.traits : hasMember;
2824 static if (hasMember!(A, "deallocate"))
2825 {
2826 scope(failure) pa.deallocate(state);
2827 }
2828 return RCISharedAllocator(emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa));
2829 }
2830
2831
2832 /**
2833
2834 Implementation of `IAllocator` using `Allocator`. This adapts a
2835 statically-built allocator type to `IAllocator` that is directly usable by
2836 non-templated code.
2837
2838 Usually `CAllocatorImpl` is used indirectly by calling $(LREF theAllocator).
2839 */
2840 class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
2841 : IAllocator
2842 {
2843 import std.traits : hasMember;
2844
2845 static if (stateSize!Allocator) private size_t rc = 1;
2846
2847 /**
2848 The implementation is available as a public member.
2849 */
2850 static if (indirect)
2851 {
2852 nothrow:
2853 private Allocator* pimpl;
2854
2855 @nogc pure @safe
impl()2856 ref Allocator impl()
2857 {
2858 return *pimpl;
2859 }
2860
2861 @nogc pure @safe
this(Allocator * pa)2862 this(Allocator* pa)
2863 {
2864 pimpl = pa;
2865 }
2866 }
2867 else
2868 {
2869 static if (stateSize!Allocator) Allocator impl;
2870 else alias impl = Allocator.instance;
2871 }
2872
2873 nothrow:
2874 /// Returns `impl.alignment`.
alignment()2875 override @property uint alignment()
2876 {
2877 return impl.alignment;
2878 }
2879
2880 /**
2881 Returns `impl.goodAllocSize(s)`.
2882 */
goodAllocSize(size_t s)2883 override size_t goodAllocSize(size_t s)
2884 {
2885 return impl.goodAllocSize(s);
2886 }
2887
2888 /**
2889 Returns `impl.allocate(s)`.
2890 */
2891 override void[] allocate(size_t s, TypeInfo ti = null)
2892 {
2893 return impl.allocate(s);
2894 }
2895
2896 /**
2897 If `impl.alignedAllocate` exists, calls it and returns the result.
2898 Otherwise, always returns `null`.
2899 */
alignedAllocate(size_t s,uint a)2900 override void[] alignedAllocate(size_t s, uint a)
2901 {
2902 static if (hasMember!(Allocator, "alignedAllocate"))
2903 return impl.alignedAllocate(s, a);
2904 else
2905 return null;
2906 }
2907
2908 /**
2909 If `Allocator` implements `owns`, forwards to it. Otherwise, returns
2910 `Ternary.unknown`.
2911 */
owns(void[]b)2912 override Ternary owns(void[] b)
2913 {
2914 static if (hasMember!(Allocator, "owns")) return impl.owns(b);
2915 else return Ternary.unknown;
2916 }
2917
2918 /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise.
expand(ref void[]b,size_t s)2919 override bool expand(ref void[] b, size_t s)
2920 {
2921 static if (hasMember!(Allocator, "expand"))
2922 return impl.expand(b, s);
2923 else
2924 return s == 0;
2925 }
2926
2927 /// Returns $(D impl.reallocate(b, s)).
reallocate(ref void[]b,size_t s)2928 override bool reallocate(ref void[] b, size_t s)
2929 {
2930 return impl.reallocate(b, s);
2931 }
2932
2933 /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise.
alignedReallocate(ref void[]b,size_t s,uint a)2934 bool alignedReallocate(ref void[] b, size_t s, uint a)
2935 {
2936 static if (!hasMember!(Allocator, "alignedAllocate"))
2937 {
2938 return false;
2939 }
2940 else
2941 {
2942 return impl.alignedReallocate(b, s, a);
2943 }
2944 }
2945
2946 // Undocumented for now
resolveInternalPointer(const void * p,ref void[]result)2947 Ternary resolveInternalPointer(const void* p, ref void[] result)
2948 {
2949 static if (hasMember!(Allocator, "resolveInternalPointer"))
2950 {
2951 return impl.resolveInternalPointer(p, result);
2952 }
2953 else
2954 {
2955 return Ternary.unknown;
2956 }
2957 }
2958
2959 /**
2960 If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards
2961 the call.
2962 */
deallocate(void[]b)2963 override bool deallocate(void[] b)
2964 {
2965 static if (hasMember!(Allocator, "deallocate"))
2966 {
2967 return impl.deallocate(b);
2968 }
2969 else
2970 {
2971 return false;
2972 }
2973 }
2974
2975 /**
2976 Calls `impl.deallocateAll()` and returns the result if defined,
2977 otherwise returns `false`.
2978 */
deallocateAll()2979 override bool deallocateAll()
2980 {
2981 static if (hasMember!(Allocator, "deallocateAll"))
2982 {
2983 return impl.deallocateAll();
2984 }
2985 else
2986 {
2987 return false;
2988 }
2989 }
2990
2991 /**
2992 Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`.
2993 */
empty()2994 override Ternary empty()
2995 {
2996 static if (hasMember!(Allocator, "empty"))
2997 {
2998 return Ternary(impl.empty);
2999 }
3000 else
3001 {
3002 return Ternary.unknown;
3003 }
3004 }
3005
3006 /**
3007 Returns `impl.allocateAll()` if present, `null` otherwise.
3008 */
allocateAll()3009 override void[] allocateAll()
3010 {
3011 static if (hasMember!(Allocator, "allocateAll"))
3012 {
3013 return impl.allocateAll();
3014 }
3015 else
3016 {
3017 return null;
3018 }
3019 }
3020
3021 @nogc nothrow pure @safe
incRef()3022 override void incRef()
3023 {
3024 static if (stateSize!Allocator) ++rc;
3025 }
3026
3027 @nogc nothrow pure @trusted
decRef()3028 override bool decRef()
3029 {
3030 static if (stateSize!Allocator)
3031 {
3032 import core.stdc.string : memcpy;
3033
3034 if (rc == 1)
3035 {
3036 static if (indirect)
3037 {
3038 Allocator* tmp = pimpl;
3039 }
3040 else
3041 {
3042 Allocator tmp;
3043 memcpy(&tmp, &this.impl, Allocator.sizeof);
3044 }
3045 void[] support = (cast(void*) this)[0 .. stateSize!(typeof(this))];
3046 tmp.deallocate(support);
3047 return false;
3048 }
3049
3050 --rc;
3051 return true;
3052 }
3053 else
3054 {
3055 return true;
3056 }
3057 }
3058 }
3059
3060 /**
3061
3062 Implementation of `ISharedAllocator` using `Allocator`. This adapts a
3063 statically-built, shareable across threads, allocator type to `ISharedAllocator`
3064 that is directly usable by non-templated code.
3065
3066 Usually `CSharedAllocatorImpl` is used indirectly by calling
3067 $(LREF processAllocator).
3068 */
3069 class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
3070 : ISharedAllocator
3071 {
3072 import std.traits : hasMember;
3073 import core.atomic : atomicOp, atomicLoad;
3074
3075 static if (stateSize!Allocator) shared size_t rc = 1;
3076
3077 /**
3078 The implementation is available as a public member.
3079 */
3080 static if (indirect)
3081 {
3082 nothrow:
3083 private shared Allocator* pimpl;
3084
3085 @nogc pure @safe
impl()3086 ref Allocator impl() shared
3087 {
3088 return *pimpl;
3089 }
3090
3091 @nogc pure @safe
this(Allocator * pa)3092 this(Allocator* pa) shared
3093 {
3094 pimpl = pa;
3095 }
3096 }
3097 else
3098 {
3099 static if (stateSize!Allocator) shared Allocator impl;
3100 else alias impl = Allocator.instance;
3101 }
3102
3103 nothrow:
3104 /// Returns `impl.alignment`.
alignment()3105 override @property uint alignment() shared
3106 {
3107 return impl.alignment;
3108 }
3109
3110 /**
3111 Returns `impl.goodAllocSize(s)`.
3112 */
goodAllocSize(size_t s)3113 override size_t goodAllocSize(size_t s) shared
3114 {
3115 return impl.goodAllocSize(s);
3116 }
3117
3118 /**
3119 Returns `impl.allocate(s)`.
3120 */
3121 override void[] allocate(size_t s, TypeInfo ti = null) shared
3122 {
3123 return impl.allocate(s);
3124 }
3125
3126 /**
3127 If `impl.alignedAllocate` exists, calls it and returns the result.
3128 Otherwise, always returns `null`.
3129 */
alignedAllocate(size_t s,uint a)3130 override void[] alignedAllocate(size_t s, uint a) shared
3131 {
3132 static if (hasMember!(Allocator, "alignedAllocate"))
3133 return impl.alignedAllocate(s, a);
3134 else
3135 return null;
3136 }
3137
3138 /**
3139 If `Allocator` implements `owns`, forwards to it. Otherwise, returns
3140 `Ternary.unknown`.
3141 */
owns(void[]b)3142 override Ternary owns(void[] b) shared
3143 {
3144 static if (hasMember!(Allocator, "owns")) return impl.owns(b);
3145 else return Ternary.unknown;
3146 }
3147
3148 /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise.
expand(ref void[]b,size_t s)3149 override bool expand(ref void[] b, size_t s) shared
3150 {
3151 static if (hasMember!(Allocator, "expand"))
3152 return impl.expand(b, s);
3153 else
3154 return s == 0;
3155 }
3156
3157 /// Returns $(D impl.reallocate(b, s)).
reallocate(ref void[]b,size_t s)3158 override bool reallocate(ref void[] b, size_t s) shared
3159 {
3160 return impl.reallocate(b, s);
3161 }
3162
3163 /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise.
alignedReallocate(ref void[]b,size_t s,uint a)3164 bool alignedReallocate(ref void[] b, size_t s, uint a) shared
3165 {
3166 static if (!hasMember!(Allocator, "alignedAllocate"))
3167 {
3168 return false;
3169 }
3170 else
3171 {
3172 return impl.alignedReallocate(b, s, a);
3173 }
3174 }
3175
3176 // Undocumented for now
resolveInternalPointer(const void * p,ref void[]result)3177 Ternary resolveInternalPointer(const void* p, ref void[] result) shared
3178 {
3179 static if (hasMember!(Allocator, "resolveInternalPointer"))
3180 {
3181 return impl.resolveInternalPointer(p, result);
3182 }
3183 else
3184 {
3185 return Ternary.unknown;
3186 }
3187 }
3188
3189 /**
3190 If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards
3191 the call.
3192 */
deallocate(void[]b)3193 override bool deallocate(void[] b) shared
3194 {
3195 static if (hasMember!(Allocator, "deallocate"))
3196 {
3197 return impl.deallocate(b);
3198 }
3199 else
3200 {
3201 return false;
3202 }
3203 }
3204
3205 /**
3206 Calls `impl.deallocateAll()` and returns the result if defined,
3207 otherwise returns `false`.
3208 */
deallocateAll()3209 override bool deallocateAll() shared
3210 {
3211 static if (hasMember!(Allocator, "deallocateAll"))
3212 {
3213 return impl.deallocateAll();
3214 }
3215 else
3216 {
3217 return false;
3218 }
3219 }
3220
3221 /**
3222 Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`.
3223 */
empty()3224 override Ternary empty() shared
3225 {
3226 static if (hasMember!(Allocator, "empty"))
3227 {
3228 return Ternary(impl.empty);
3229 }
3230 else
3231 {
3232 return Ternary.unknown;
3233 }
3234 }
3235
3236 /**
3237 Returns `impl.allocateAll()` if present, `null` otherwise.
3238 */
allocateAll()3239 override void[] allocateAll() shared
3240 {
3241 static if (hasMember!(Allocator, "allocateAll"))
3242 {
3243 return impl.allocateAll();
3244 }
3245 else
3246 {
3247 return null;
3248 }
3249 }
3250
3251 @nogc nothrow pure @safe
incRef()3252 override void incRef() shared
3253 {
3254 static if (stateSize!Allocator) atomicOp!"+="(rc, 1);
3255 }
3256
3257 @nogc nothrow pure @trusted
decRef()3258 override bool decRef() shared
3259 {
3260 static if (stateSize!Allocator)
3261 {
3262 import core.stdc.string : memcpy;
3263
3264 // rc starts as 1 to avoid comparing with size_t(0) - 1
3265 if (atomicOp!"-="(rc, 1) == 0)
3266 {
3267 static if (indirect)
3268 {
3269 Allocator* tmp = pimpl;
3270 }
3271 else
3272 {
3273 Allocator tmp;
3274 memcpy(cast(void*) &tmp, cast(void*) &this.impl, Allocator.sizeof);
3275 Allocator empty;
3276 memcpy(cast(void*) &this.impl, cast(void*) &empty, Allocator.sizeof);
3277 }
3278 void[] support = (cast(void*) this)[0 .. stateSize!(typeof(this))];
3279 (cast(bool delegate(void[]) @nogc nothrow pure)(&tmp.deallocate))(support);
3280 return false;
3281 }
3282 return true;
3283 }
3284 else
3285 {
3286 return true;
3287 }
3288 }
3289 }
3290
3291
3292 // Example in intro above
3293 @system unittest
3294 {
3295 // Allocate an int, initialize it with 42
3296 int* p = theAllocator.make!int(42);
3297 assert(*p == 42);
3298
3299 // Destroy and deallocate it
3300 theAllocator.dispose(p);
3301
3302 // Allocate using the global process allocator
3303 p = processAllocator.make!int(100);
3304 assert(*p == 100);
3305
3306 // Destroy and deallocate
3307 processAllocator.dispose(p);
3308
3309 // Create an array of 50 doubles initialized to -1.0
3310 double[] arr = theAllocator.makeArray!double(50, -1.0);
3311
3312 // Check internal pointer
3313 void[] result;
3314 assert(theAllocator.resolveInternalPointer(null, result) == Ternary.no);
3315 Ternary r = theAllocator.resolveInternalPointer(arr.ptr, result);
3316 assert(result.ptr is arr.ptr && result.length >= arr.length);
3317
3318 // Append two zeros to it
3319 theAllocator.expandArray(arr, 2, 0.0);
3320 // On second thought, take that back
3321 theAllocator.shrinkArray(arr, 2);
3322 // Destroy and deallocate
3323 theAllocator.dispose(arr);
3324 }
3325
3326 /**
3327
3328 Stores an allocator object in thread-local storage (i.e. non-`shared` D
3329 global). `ThreadLocal!A` is a subtype of `A` so it appears to implement
3330 `A`'s allocator primitives.
3331
3332 `A` must hold state, otherwise `ThreadLocal!A` refuses instantiation. This
3333 means e.g. `ThreadLocal!Mallocator` does not work because `Mallocator`'s
3334 state is not stored as members of `Mallocator`, but instead is hidden in the
3335 C library implementation.
3336
3337 */
ThreadLocal(A)3338 struct ThreadLocal(A)
3339 {
3340 static assert(stateSize!A,
3341 typeof(A).stringof
3342 ~ " does not have state so it cannot be used with ThreadLocal");
3343
3344 /**
3345 The allocator instance.
3346 */
3347 static A instance;
3348
3349 /**
3350 `ThreadLocal!A` is a subtype of `A` so it appears to implement `A`'s
3351 allocator primitives.
3352 */
3353 alias instance this;
3354
3355 /**
3356 `ThreadLocal` disables all constructors. The intended usage is
3357 `ThreadLocal!A.instance`.
3358 */
3359 @disable this();
3360 /// Ditto
3361 @disable this(this);
3362 }
3363
3364 ///
3365 @system
3366 unittest
3367 {
3368 import std.experimental.allocator.building_blocks.free_list : FreeList;
3369 import std.experimental.allocator.gc_allocator : GCAllocator;
3370 import std.experimental.allocator.mallocator : Mallocator;
3371
3372 static assert(!is(ThreadLocal!Mallocator));
3373 static assert(!is(ThreadLocal!GCAllocator));
3374 alias Allocator = ThreadLocal!(FreeList!(GCAllocator, 0, 8));
3375 auto b = Allocator.instance.allocate(5);
3376 static assert(__traits(hasMember, Allocator, "allocate"));
3377 }
3378
3379 /*
3380 (Not public.)
3381
3382 A binary search tree that uses no allocation of its own. Instead, it relies on
3383 user code to allocate nodes externally. Then `EmbeddedTree`'s primitives wire
3384 the nodes appropriately.
3385
3386 Warning: currently `EmbeddedTree` is not using rebalancing, so it may
3387 degenerate. A red-black tree implementation storing the color with one of the
3388 pointers is planned for the future.
3389 */
EmbeddedTree(T,alias less)3390 private struct EmbeddedTree(T, alias less)
3391 {
3392 static struct Node
3393 {
3394 T payload;
3395 Node* left, right;
3396 }
3397
3398 private Node* root;
3399
3400 private Node* insert(Node* n, ref Node* backref)
3401 {
3402 backref = n;
3403 n.left = n.right = null;
3404 return n;
3405 }
3406
3407 Node* find(Node* data)
3408 {
3409 for (auto n = root; n; )
3410 {
3411 if (less(data, n))
3412 {
3413 n = n.left;
3414 }
3415 else if (less(n, data))
3416 {
3417 n = n.right;
3418 }
3419 else
3420 {
3421 return n;
3422 }
3423 }
3424 return null;
3425 }
3426
3427 Node* insert(Node* data)
3428 {
3429 if (!root)
3430 {
3431 root = data;
3432 data.left = data.right = null;
3433 return root;
3434 }
3435 auto n = root;
3436 for (;;)
3437 {
3438 if (less(data, n))
3439 {
3440 if (!n.left)
3441 {
3442 // Found insertion point
3443 return insert(data, n.left);
3444 }
3445 n = n.left;
3446 }
3447 else if (less(n, data))
3448 {
3449 if (!n.right)
3450 {
3451 // Found insertion point
3452 return insert(data, n.right);
3453 }
3454 n = n.right;
3455 }
3456 else
3457 {
3458 // Found
3459 return n;
3460 }
3461 if (!n) return null;
3462 }
3463 }
3464
3465 Node* remove(Node* data)
3466 {
3467 auto n = root;
3468 Node* parent = null;
3469 for (;;)
3470 {
3471 if (!n) return null;
3472 if (less(data, n))
3473 {
3474 parent = n;
3475 n = n.left;
3476 }
3477 else if (less(n, data))
3478 {
3479 parent = n;
3480 n = n.right;
3481 }
3482 else
3483 {
3484 // Found
3485 remove(n, parent);
3486 return n;
3487 }
3488 }
3489 }
3490
3491 private void remove(Node* n, Node* parent)
3492 {
3493 assert(n);
3494 assert(!parent || parent.left == n || parent.right == n);
3495 Node** referrer = parent
3496 ? (parent.left == n ? &parent.left : &parent.right)
3497 : &root;
3498 if (!n.left)
3499 {
3500 *referrer = n.right;
3501 }
3502 else if (!n.right)
3503 {
3504 *referrer = n.left;
3505 }
3506 else
3507 {
3508 // Find the leftmost child in the right subtree
3509 auto leftmost = n.right;
3510 Node** leftmostReferrer = &n.right;
3511 while (leftmost.left)
3512 {
3513 leftmostReferrer = &leftmost.left;
3514 leftmost = leftmost.left;
3515 }
3516 // Unlink leftmost from there
3517 *leftmostReferrer = leftmost.right;
3518 // Link leftmost in lieu of n
3519 leftmost.left = n.left;
3520 leftmost.right = n.right;
3521 *referrer = leftmost;
3522 }
3523 }
3524
3525 Ternary empty() const
3526 {
3527 return Ternary(!root);
3528 }
3529
3530 void dump()
3531 {
3532 import std.stdio : writeln;
3533 writeln(typeid(this), " @ ", cast(void*) &this);
3534 dump(root, 3);
3535 }
3536
3537 void dump(Node* r, uint indent)
3538 {
3539 import std.stdio : write, writeln;
3540 import std.range : repeat;
3541 import std.array : array;
3542
3543 write(repeat(' ', indent).array);
3544 if (!r)
3545 {
3546 writeln("(null)");
3547 return;
3548 }
3549 writeln(r.payload, " @ ", cast(void*) r);
3550 dump(r.left, indent + 3);
3551 dump(r.right, indent + 3);
3552 }
3553
3554 void assertSane()
3555 {
3556 static bool isBST(Node* r, Node* lb, Node* ub)
3557 {
3558 if (!r) return true;
3559 if (lb && !less(lb, r)) return false;
3560 if (ub && !less(r, ub)) return false;
3561 return isBST(r.left, lb, r) &&
3562 isBST(r.right, r, ub);
3563 }
3564 if (isBST(root, null, null)) return;
3565 dump;
3566 assert(0);
3567 }
3568 }
3569
3570 @system
3571 unittest
3572 {
3573 import std.experimental.allocator.gc_allocator : GCAllocator;
3574
3575 alias a = GCAllocator.instance;
3576 alias Tree = EmbeddedTree!(int, (a, b) => a.payload < b.payload);
3577 Tree t;
3578 assert(t.empty == Ternary.yes);
3579 int[] vals = [ 6, 3, 9, 1, 0, 2, 8, 11 ];
foreach(v;vals)3580 foreach (v; vals)
3581 {
3582 auto n = new Tree.Node(v, null, null);
3583 assert(t.insert(n));
3584 assert(n);
3585 t.assertSane;
3586 }
3587 assert(t.empty != Ternary.yes);
foreach(v;vals)3588 foreach (v; vals)
3589 {
3590 Tree.Node n = { v };
3591 assert(t.remove(&n));
3592 t.assertSane;
3593 }
3594 assert(t.empty == Ternary.yes);
3595 }
3596
3597 /*
3598
3599 `InternalPointersTree` adds a primitive on top of another allocator: calling
3600 `resolveInternalPointer(p)` returns the block within which the internal
3601 pointer `p` lies. Pointers right after the end of allocated blocks are also
3602 considered internal.
3603
3604 The implementation stores three additional words with each allocation (one for
3605 the block size and two for search management).
3606
3607 */
InternalPointersTree(Allocator)3608 private struct InternalPointersTree(Allocator)
3609 {
3610 import std.experimental.allocator.building_blocks.affix_allocator : AffixAllocator;
3611
3612 alias Tree = EmbeddedTree!(size_t,
3613 (a, b) => cast(void*) a + a.payload < cast(void*) b);
3614 alias Parent = AffixAllocator!(Allocator, Tree.Node);
3615
3616 // Own state
3617 private Tree blockMap;
3618
3619 alias alignment = Parent.alignment;
3620
3621 /**
3622 The implementation is available as a public member.
3623 */
3624 static if (stateSize!Parent) Parent parent;
3625 else alias parent = Parent.instance;
3626
3627 /// Allocator API.
3628 void[] allocate(size_t bytes)
3629 {
3630 auto r = parent.allocate(bytes);
3631 if (!r.ptr) return r;
3632 Tree.Node* n = &parent.prefix(r);
3633 n.payload = bytes;
3634 blockMap.insert(n) || assert(0);
3635 return r;
3636 }
3637
3638 /// Ditto
3639 bool deallocate(void[] b)
3640 {
3641 if (!b.ptr) return true;
3642 Tree.Node* n = &parent.prefix(b);
3643 blockMap.remove(n) || assert(false);
3644 parent.deallocate(b);
3645 return true;
3646 }
3647
3648 /// Ditto
3649 static if (hasMember!(Allocator, "reallocate"))
3650 bool reallocate(ref void[] b, size_t s)
3651 {
3652 auto n = &parent.prefix(b);
3653 assert(n.payload == b.length);
3654 blockMap.remove(n) || assert(0);
3655 if (!parent.reallocate(b, s))
3656 {
3657 // Failed, must reinsert the same node in the tree
3658 assert(n.payload == b.length);
3659 blockMap.insert(n) || assert(0);
3660 return false;
3661 }
3662 // Insert the new node
3663 n = &parent.prefix(b);
3664 n.payload = s;
3665 blockMap.insert(n) || assert(0);
3666 return true;
3667 }
3668
3669 /// Ditto
3670 Ternary owns(void[] b)
3671 {
3672 void[] result;
3673 return resolveInternalPointer(b.ptr, result);
3674 }
3675
3676 /// Ditto
3677 Ternary empty()
3678 {
3679 return Ternary(blockMap.empty);
3680 }
3681
3682 /** Returns the block inside which `p` resides, or `null` if the
3683 pointer does not belong.
3684 */
3685 pure nothrow @safe @nogc
3686 Ternary resolveInternalPointer(const void* p, ref void[] result)
3687 {
3688 // Must define a custom find
3689 Tree.Node* find()
3690 {
3691 for (auto n = blockMap.root; n; )
3692 {
3693 if (p < n)
3694 {
3695 n = n.left;
3696 }
3697 else if ((() @trusted => p > (cast(void*) (n + 1)) + n.payload)())
3698 {
3699 n = n.right;
3700 }
3701 else
3702 {
3703 return n;
3704 }
3705 }
3706 return null;
3707 }
3708
3709 auto n = find();
3710 if (!n) return Ternary.no;
3711 result = (() @trusted => (cast(void*) (n + 1))[0 .. n.payload])();
3712 return Ternary.yes;
3713 }
3714 }
3715
3716 @system
3717 unittest
3718 {
3719 import std.experimental.allocator.mallocator : Mallocator;
3720 import std.random : randomCover;
3721
3722 InternalPointersTree!(Mallocator) a;
3723 int[] vals = [ 6, 3, 9, 1, 2, 8, 11 ];
3724 void[][] allox;
foreach(v;vals)3725 foreach (v; vals)
3726 {
3727 allox ~= a.allocate(v);
3728 }
3729 a.blockMap.assertSane;
3730
foreach(b;allox)3731 foreach (b; allox)
3732 {
3733 () pure nothrow @safe {
3734 void[] p;
3735 Ternary r = (() @nogc => a.resolveInternalPointer(&b[0], p))();
3736 assert(&p[0] == &b[0] && p.length >= b.length);
3737 r = a.resolveInternalPointer((() @trusted => &b[0] + b.length)(), p);
3738
3739 /* This line randomly fails on MacOS 12.x x64
3740 * https://issues.dlang.org/show_bug.cgi?id=22660
3741 * Commenting it out until someone can fix it.
3742 */
3743 //assert(&p[0] == &b[0] && p.length >= b.length);
3744
3745 r = a.resolveInternalPointer((() @trusted => &b[0] + b.length / 2)(), p);
3746 assert(&p[0] == &b[0] && p.length >= b.length);
3747 auto bogus = new void[b.length];
3748 assert(a.resolveInternalPointer(&bogus[0], p) == Ternary.no);
3749 }();
3750 }
3751
3752 foreach (b; allox.randomCover)
3753 {
3754 () nothrow @nogc { a.deallocate(b); }();
3755 }
3756
3757 assert(a.empty == Ternary.yes);
3758 }
3759
3760 //version (std_allocator_benchmark)
3761 @system
3762 unittest
3763 {
3764 import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
3765 import std.experimental.allocator.building_blocks.allocator_list : AllocatorList;
3766 import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
3767 import std.experimental.allocator.building_blocks.segregator : Segregator;
3768 import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;
3769 import std.experimental.allocator.building_blocks.free_list : FreeList;
3770 import std.experimental.allocator.gc_allocator : GCAllocator;
3771 import std.experimental.allocator.mallocator : Mallocator;
3772
testSpeed(A)3773 static void testSpeed(A)()
3774 {
3775 static if (stateSize!A) A a;
3776 else alias a = A.instance;
3777
3778 void[][128] bufs;
3779
3780 import std.random;
3781 foreach (i; 0 .. 100_000)
3782 {
3783 auto j = uniform(0, bufs.length);
3784 switch (uniform(0, 2))
3785 {
3786 case 0:
3787 () nothrow @nogc { a.deallocate(bufs[j]); }();
3788 bufs[j] = a.allocate(uniform(0, 4096));
3789 break;
3790 case 1:
3791 () nothrow @nogc { a.deallocate(bufs[j]); }();
3792 bufs[j] = null;
3793 break;
3794 default:
3795 assert(0);
3796 }
3797 }
3798 }
3799
3800 import std.algorithm.comparison : max;
3801
3802 alias FList = FreeList!(GCAllocator, 0, unbounded);
3803 alias A = Segregator!(
3804 8, FreeList!(GCAllocator, 0, 8),
3805 128, Bucketizer!(FList, 1, 128, 16),
3806 256, Bucketizer!(FList, 129, 256, 32),
3807 512, Bucketizer!(FList, 257, 512, 64),
3808 1024, Bucketizer!(FList, 513, 1024, 128),
3809 2048, Bucketizer!(FList, 1025, 2048, 256),
3810 3584, Bucketizer!(FList, 2049, 3584, 512),
3811 4072 * 1024, AllocatorList!(
3812 (size_t n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate(
3813 max(n, 4072 * 1024)))),
3814 GCAllocator
3815 );
3816
3817 import std.stdio;
3818 import std.conv : to;
3819 import std.datetime.stopwatch;
3820 import std.algorithm.iteration : map;
3821
3822 if (false) writeln(benchmark!(
3823 testSpeed!NullAllocator,
3824 testSpeed!Mallocator,
3825 testSpeed!GCAllocator,
3826 testSpeed!(ThreadLocal!A),
3827 testSpeed!(A),
3828 )(20)[].map!(t => t.to!Duration));
3829 }
3830
3831 @system
3832 unittest
3833 {
3834 import std.experimental.allocator.building_blocks.free_list : FreeList;
3835 import std.experimental.allocator.building_blocks.region : InSituRegion;
3836 import std.experimental.allocator.building_blocks.fallback_allocator : FallbackAllocator;
3837 import std.experimental.allocator.gc_allocator : GCAllocator;
3838 import std.experimental.allocator.mallocator : Mallocator;
3839
3840 auto a = allocatorObject(Mallocator.instance);
3841 auto b = a.allocate(100);
3842 assert(b.length == 100);
3843
3844 FreeList!(GCAllocator, 0, 8) fl;
3845 auto sa = allocatorObject(fl);
3846 b = a.allocate(101);
3847 assert(b.length == 101);
3848
3849 FallbackAllocator!(InSituRegion!(10240, 64), GCAllocator) fb;
3850 // Doesn't work yet...
3851 //a = allocatorObject(fb);
3852 //b = a.allocate(102);
3853 //assert(b.length == 102);
3854 }
3855
3856 ///
3857 @system
3858 unittest
3859 {
3860 import std.experimental.allocator.building_blocks.allocator_list : AllocatorList;
3861 import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
3862 import std.experimental.allocator.building_blocks.segregator : Segregator;
3863 import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;
3864 import std.experimental.allocator.building_blocks.free_list : FreeList;
3865 import std.experimental.allocator.gc_allocator : GCAllocator;
3866
3867 /// Define an allocator bound to the built-in GC.
3868 auto alloc = allocatorObject(GCAllocator.instance);
3869 auto b = alloc.allocate(42);
3870 assert(b.length == 42);
3871 assert(alloc.deallocate(b));
3872
3873 import std.algorithm.comparison : max;
3874 // Define an elaborate allocator and bind it to the class API.
3875 alias FList = FreeList!(GCAllocator, 0, unbounded);
3876 alias A = ThreadLocal!(
3877 Segregator!(
3878 8, FreeList!(GCAllocator, 0, 8),
3879 128, Bucketizer!(FList, 1, 128, 16),
3880 256, Bucketizer!(FList, 129, 256, 32),
3881 512, Bucketizer!(FList, 257, 512, 64),
3882 1024, Bucketizer!(FList, 513, 1024, 128),
3883 2048, Bucketizer!(FList, 1025, 2048, 256),
3884 3584, Bucketizer!(FList, 2049, 3584, 512),
3885 4072 * 1024, AllocatorList!(
3886 (n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate(
3887 max(n, 4072 * 1024)))),
3888 GCAllocator
3889 )
3890 );
3891
3892 auto alloc2 = allocatorObject(A.instance);
3893 b = alloc2.allocate(101);
3894 assert(alloc2.deallocate(b));
3895 }
3896