1// Written in the D programming language. 2/** 3 4High-level interface for allocators. Implements bundled allocation/creation 5and destruction/deallocation of data including `struct`s and `class`es, 6and also array primitives related to allocation. This module is the entry point 7for 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 36Synopsis: 37--- 38// Allocate an int, initialize it with 42 39int* p = theAllocator.make!int(42); 40assert(*p == 42); 41// Destroy and deallocate it 42theAllocator.dispose(p); 43 44// Allocate using the global process allocator 45p = processAllocator.make!int(100); 46assert(*p == 100); 47// Destroy and deallocate 48processAllocator.dispose(p); 49 50// Create an array of 50 doubles initialized to -1.0 51double[] arr = theAllocator.makeArray!double(50, -1.0); 52// Append two zeros to it 53theAllocator.expandArray(arr, 2, 0.0); 54// On second thought, take that back 55theAllocator.shrinkArray(arr, 2); 56// Destroy and deallocate 57theAllocator.dispose(arr); 58--- 59 60$(H2 Layered Structure) 61 62D'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 66module). It consists of an interface called $(LREF IAllocator), which concrete 67allocators need to implement. The interface primitives themselves are oblivious 68to the type of the objects being allocated; they only deal in `void[]`, by 69necessity of the interface being dynamic (as opposed to type-parameterized). 70Each thread has a current allocator it uses by default, which is a thread-local 71variable $(LREF theAllocator) of type $(LREF IAllocator). The process has a 72global allocator called $(LREF processAllocator), also of type $(LREF 73IAllocator). When a new thread is created, $(LREF processAllocator) is copied 74into $(LREF theAllocator). An application can change the objects to which these 75references point. By default, at application startup, $(LREF processAllocator) 76refers to an object that uses D's garbage collected heap. This layer also 77include high-level functions such as $(LREF make) and $(LREF dispose) that 78comfortably allocate/create and respectively destroy/deallocate objects. This 79layer is all needed for most casual uses of allocation primitives.) 80 81$(LI A mid-level, statically-typed layer for assembling several allocators into 82one. It uses properties of the type of the objects being created to route 83allocation requests to possibly specialized allocators. This layer is relatively 84thin and implemented and documented in the $(MREF 85std,experimental,allocator,typed) module. It allows an interested user to e.g. 86use different allocators for arrays versus fixed-sized objects, to the end of 87better overall performance.) 88 89$(LI A low-level collection of highly generic $(I heap building blocks)$(MDASH) 90Lego-like pieces that can be used to assemble application-specific allocators. 91The real allocation smarts are occurring at this level. This layer is of 92interest to advanced applications that want to configure their own allocators. 93A good illustration of typical uses of these building blocks is module $(MREF 94std,experimental,allocator,showcase) which defines a collection of frequently- 95used preassembled allocator objects. The implementation and documentation entry 96point is $(MREF std,experimental,allocator,building_blocks). By design, the 97primitives of the static interface have the same signatures as the $(LREF 98IAllocator) primitives but are for the most part optional and driven by static 99introspection. The parameterized class $(LREF CAllocatorImpl) offers an 100immediate and useful means to package a static low-level allocator into an 101implementation 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 106std,experimental,allocator,mmap_allocator)). Most custom allocators would 107ultimately obtain memory from one of these core allocators.) 108) 109 110$(H2 Idiomatic Use of `std.experimental.allocator`) 111 112As of this time, `std.experimental.allocator` is not integrated with D's 113built-in operators that allocate memory, such as `new`, array literals, or 114array concatenation operators. That means `std.experimental.allocator` is 115opt-in$(MDASH)applications need to make explicit use of it. 116 117For casual creation and disposal of dynamically-allocated objects, use $(LREF 118make), $(LREF dispose), and the array-specific functions $(LREF makeArray), 119$(LREF expandArray), and $(LREF shrinkArray). These use by default D's garbage 120collected heap, but open the application to better configuration options. These 121primitives work either with `theAllocator` but also with any allocator obtained 122by combining heap building blocks. For example: 123 124---- 125void 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 134To experiment with alternative allocators, set $(LREF theAllocator) for the 135current thread. For example, consider an application that allocates many 8-byte 136objects. These are not well supported by the default allocator, so a 137$(MREF_ALTTEXT free list allocator, 138std,experimental,allocator,building_blocks,free_list) would be recommended. 139To install one in `main`, the application would use: 140 141---- 142void 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 153As with any global resource, setting `theAllocator` and `processAllocator` 154should not be done often and casually. In particular, allocating memory with 155one allocator and deallocating with another causes undefined behavior. 156Typically, these variables are set during application initialization phase and 157last through the application. 158 159To avoid this, long-lived objects that need to perform allocations, 160reallocations, and deallocations relatively often may want to store a reference 161to the allocator object they use throughout their lifetime. Then, instead of 162using `theAllocator` for internal allocation-related tasks, they'd use the 163internally held reference. For example, consider a user-defined hash table: 164 165---- 166struct 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 179Following 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 182only if the object is empty; otherwise, the object wouldn't be able to 183deallocate its existing state. 184 185$(H3 Using Allocators without `IAllocator`) 186 187Allocators 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 189they work with $(LREF make), $(LREF makeArray), $(LREF dispose) etc. So it 190suffice to create allocator objects wherever fit and use them appropriately: 191 192---- 193void 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 203In this case, `myAllocator` does not obey the `IAllocator` interface, but 204implements its primitives so it can work with `makeArray` by means of duck 205typing. 206 207One important thing to note about this setup is that statically-typed assembled 208allocators are almost always faster than allocators that go through 209`IAllocator`. An important rule of thumb is: "assemble allocator first, adapt 210to `IAllocator` after". A good allocator implements intricate logic by means of 211template assembly, and gets wrapped with `IAllocator` (usually by means of 212$(LREF allocatorObject)) only once, at client level. 213 214Copyright: Andrei Alexandrescu 2013-. 215 216License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 217 218Authors: $(HTTP erdani.com, Andrei Alexandrescu) 219 220Source: $(PHOBOSSRC std/experimental/allocator) 221 222*/ 223 224module std.experimental.allocator; 225 226public 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 280import std.range.primitives; 281import std.traits; 282import std.typecons; 283 284/** 285Dynamic allocator interface. Code that defines allocators ultimately implements 286this interface. This should be used wherever a uniform type is required for 287encapsulating various allocator implementations. 288 289Composition of allocators is not recommended at this level due to 290inflexibility of dynamic interfaces and inefficiencies caused by cascaded 291multiple calls. Instead, compose allocators using the static interface defined 292in $(MREF std,experimental,allocator,building_blocks), 293then adapt the composed 294allocator to `IAllocator` (possibly by using $(LREF CAllocatorImpl) below). 295 296Methods returning `Ternary` return `Ternary.yes` upon success, 297`Ternary.no` upon failure, and `Ternary.unknown` if the primitive is not 298implemented by the allocator instance. 299*/ 300interface IAllocator 301{ 302nothrow: 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/** 401A reference counted struct that wraps the dynamic allocator interface. 402This should be used wherever a uniform type is required for encapsulating 403various allocator implementations. 404 405Code that defines allocators ultimately implements the $(LREF IAllocator) 406interface, possibly by using $(LREF CAllocatorImpl) below, and then build a 407`RCIAllocator` out of this. 408 409Composition of allocators is not recommended at this level due to 410inflexibility of dynamic interfaces and inefficiencies caused by cascaded 411multiple calls. Instead, compose allocators using the static interface defined 412in $(A std_experimental_allocator_building_blocks.html, 413`std.experimental.allocator.building_blocks`), then adapt the composed 414allocator to `RCIAllocator` (possibly by using $(LREF allocatorObject) below). 415*/ 416struct RCIAllocator 417{ 418 private IAllocator _alloc; 419 420nothrow: 421 private @nogc pure @safe 422 this(this _)(IAllocator alloc) 423 { 424 assert(alloc); 425 _alloc = alloc; 426 } 427 428 @nogc pure @safe 429 this(this) 430 { 431 if (_alloc !is null) 432 { 433 _alloc.incRef(); 434 } 435 } 436 437 @nogc pure @safe 438 ~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 448 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 464 bool isNull(this _)() 465 { 466 return _alloc is null; 467 } 468 469 @property uint alignment() 470 { 471 assert(_alloc); 472 return _alloc.alignment(); 473 } 474 475 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 487 void[] alignedAllocate(size_t n, uint a) 488 { 489 assert(_alloc); 490 return _alloc.alignedAllocate(n, a); 491 } 492 493 void[] allocateAll() 494 { 495 assert(_alloc); 496 return _alloc.allocateAll(); 497 } 498 499 bool expand(ref void[] b, size_t size) 500 { 501 assert(_alloc); 502 return _alloc.expand(b, size); 503 } 504 505 bool reallocate(ref void[] b, size_t size) 506 { 507 assert(_alloc); 508 return _alloc.reallocate(b, size); 509 } 510 511 bool alignedReallocate(ref void[] b, size_t size, uint alignment) 512 { 513 assert(_alloc); 514 return _alloc.alignedReallocate(b, size, alignment); 515 } 516 517 Ternary owns(void[] b) 518 { 519 assert(_alloc); 520 return _alloc.owns(b); 521 } 522 523 Ternary resolveInternalPointer(const void* p, ref void[] result) 524 { 525 assert(_alloc); 526 return _alloc.resolveInternalPointer(p, result); 527 } 528 529 bool deallocate(void[] b) 530 { 531 assert(_alloc); 532 return _alloc.deallocate(b); 533 } 534 535 bool deallocateAll() 536 { 537 assert(_alloc); 538 return _alloc.deallocateAll(); 539 } 540 541 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/** 627Dynamic shared allocator interface. Code that defines allocators shareable 628across threads ultimately implements this interface. This should be used 629wherever a uniform type is required for encapsulating various allocator 630implementations. 631 632Composition of allocators is not recommended at this level due to 633inflexibility of dynamic interfaces and inefficiencies caused by cascaded 634multiple calls. Instead, compose allocators using the static interface defined 635in $(MREF std,experimental,allocator,building_blocks), 636then adapt the composed 637allocator to `ISharedAllocator` (possibly by using $(LREF CSharedAllocatorImpl) below). 638 639Methods returning `Ternary` return `Ternary.yes` upon success, 640`Ternary.no` upon failure, and `Ternary.unknown` if the primitive is not 641implemented by the allocator instance. 642*/ 643interface ISharedAllocator 644{ 645nothrow: 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/** 746A reference counted struct that wraps the dynamic shared allocator interface. 747This should be used wherever a uniform type is required for encapsulating 748various allocator implementations. 749 750Code 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 753of this. 754 755Composition of allocators is not recommended at this level due to 756inflexibility of dynamic interfaces and inefficiencies caused by cascaded 757multiple calls. Instead, compose allocators using the static interface defined 758in $(A std_experimental_allocator_building_blocks.html, 759`std.experimental.allocator.building_blocks`), then adapt the composed allocator 760to `RCISharedAllocator` (possibly by using $(LREF sharedAllocatorObject) below). 761*/ 762shared struct RCISharedAllocator 763{ 764 private ISharedAllocator _alloc; 765 766nothrow: 767 private @nogc pure @safe 768 this(shared ISharedAllocator alloc) 769 { 770 assert(alloc); 771 _alloc = alloc; 772 } 773 774 @nogc pure @safe 775 this(this) 776 { 777 if (_alloc !is null) 778 { 779 _alloc.incRef(); 780 } 781 } 782 783 @nogc pure @safe 784 ~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 794 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 812 bool isNull(this _)() 813 { 814 return _alloc is null; 815 } 816 817 @property uint alignment() 818 { 819 assert(_alloc); 820 return _alloc.alignment(); 821 } 822 823 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 835 void[] alignedAllocate(size_t n, uint a) 836 { 837 assert(_alloc); 838 return _alloc.alignedAllocate(n, a); 839 } 840 841 void[] allocateAll() 842 { 843 assert(_alloc); 844 return _alloc.allocateAll(); 845 } 846 847 bool expand(ref void[] b, size_t size) 848 { 849 assert(_alloc); 850 return _alloc.expand(b, size); 851 } 852 853 bool reallocate(ref void[] b, size_t size) 854 { 855 assert(_alloc); 856 return _alloc.reallocate(b, size); 857 } 858 859 bool alignedReallocate(ref void[] b, size_t size, uint alignment) 860 { 861 assert(_alloc); 862 return _alloc.alignedReallocate(b, size, alignment); 863 } 864 865 Ternary owns(void[] b) 866 { 867 assert(_alloc); 868 return _alloc.owns(b); 869 } 870 871 Ternary resolveInternalPointer(const void* p, ref void[] result) 872 { 873 assert(_alloc); 874 return _alloc.resolveInternalPointer(p, result); 875 } 876 877 bool deallocate(void[] b) 878 { 879 assert(_alloc); 880 return _alloc.deallocate(b); 881 } 882 883 bool deallocateAll() 884 { 885 assert(_alloc); 886 return _alloc.deallocateAll(); 887 } 888 889 Ternary empty() 890 { 891 assert(_alloc); 892 return _alloc.empty(); 893 } 894} 895 896private RCISharedAllocator _processAllocator; 897private RCIAllocator _threadAllocator; 898 899@nogc nothrow @safe 900private 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/** 1016Gets/sets the allocator for the current thread. This is the default allocator 1017that should be used for allocating thread-local memory. For allocating memory 1018to be shared across threads, use `processAllocator` (below). By default, 1019`theAllocator` ultimately fetches memory from `processAllocator`, which 1020in turn uses the garbage collected heap. 1021*/ 1022@nogc nothrow @safe 1023@property ref RCIAllocator theAllocator() 1024{ 1025 alias p = _threadAllocator; 1026 return !p.isNull() ? p : setupThreadAllocator(); 1027} 1028 1029/// Ditto 1030nothrow @system @nogc 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/** 1053Gets/sets the allocator for the current process. This allocator must be used 1054for allocating memory shared across threads. Objects created using this 1055allocator can be cast to `shared`. 1056*/ 1057@nogc nothrow @trusted 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 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/** 1132Dynamically allocates (using `alloc`) and then creates in the memory 1133allocated an object of type `T`, using `args` (if any) for its 1134initialization. Initialization occurs in the memory allocated and is otherwise 1135semantically the same as `T(args)`. 1136(Note that using `alloc.make!(T[])` creates a pointer to an (empty) array 1137of `T`s, not an array. To use an allocator to allocate and initialize an 1138array, use `alloc.makeArray!T` described below.) 1139 1140Params: 1141T = Type of the object being created. 1142alloc = The allocator used for getting the needed memory. It may be an object 1143implementing the static interface for allocators, or an `IAllocator` 1144reference. 1145args = Optional arguments used for initializing the created object. If not 1146present, the object is default constructed. 1147 1148Returns: If `T` is a class type, returns a reference to the created `T` 1149object. Otherwise, returns a `T*` pointing to the created object. In all 1150cases, returns `null` if allocation failed. 1151 1152Throws: If `T`'s constructor throws, deallocates the allocated memory and 1153propagates the exception. 1154*/ 1155auto 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; 1229 this() {} 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 { 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{ 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 1320nothrow @safe @nogc unittest 1321{ 1322 import std.experimental.allocator.mallocator : Mallocator; 1323 alias alloc = Mallocator.instance; 1324 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 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 1357nothrow @safe @nogc unittest 1358{ 1359 import std.experimental.allocator.mallocator : Mallocator; 1360 static struct Pure { this(int) pure nothrow @nogc @safe {} } 1361 1362 cast(void) Mallocator.instance.make!Pure(0); 1363 1364 static int g = 0; 1365 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 { 1379 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 { 1395 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 { 1428 ubyte[] allocate(size_t) { return []; } 1429 void deallocate(void[]) {} 1430 } 1431 1432 auto x = SomeAllocator().make!S(); 1433} 1434 1435private void fillWithMemcpy(T)(scope void[] array, auto ref T filler) nothrow 1436if (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 1444private void fillWithMemcpy(T)(scope void[] array, auto ref T filler) nothrow 1445if (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 1489private 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 1513pure 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/** 1550Create 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 1552Params: 1553T = element type of the array being created 1554alloc = the allocator used for getting memory 1555length = length of the newly created array 1556init = element used for filling the array 1557range = range used for initializing the array elements 1558 1559Returns: 1560The newly-created array, or `null` if either `length` was `0` or 1561allocation failed. 1562 1563Throws: 1564The first two overloads throw only if `alloc`'s primitives do. The 1565overloads that involve copy initialization deallocate memory and propagate the 1566exception if the copy operation throws. 1567*/ 1568T[] 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{ 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 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 1655private enum hasPurePostblit(T) = !hasElaborateCopyConstructor!T || 1656 is(typeof(() pure { T.init.__xpostblit(); })); 1657 1658private 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 1662private enum canSafelyDeallocPostRewind(T) = hasPurePostblit!T && hasPureDtor!T; 1663 1664/// Ditto 1665T[] 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; 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{ 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{ 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 1763 this(int b){} 1764 1765 // can't be copied 1766 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 1785 this(int b){} 1786 1787 // can't be copied 1788 this(this) 1789 { 1790 enforce(i++ == 0); 1791 } 1792 1793 ~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 1803Unqual!(ElementEncodingType!R)[] makeArray(Allocator, R)(auto ref Allocator alloc, R range) 1804if (isInputRange!R && !isInfinite!R) 1805{ 1806 alias T = Unqual!(ElementEncodingType!R); 1807 return makeArray!(T, Allocator, R)(alloc, range); 1808} 1809 1810/// Ditto 1811T[] makeArray(T, Allocator, R)(auto ref Allocator alloc, R range) 1812if (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; 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; 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{ 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{ 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; 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 1989 this(int b) 1990 { 1991 this.b = b; 1992 } 1993 1994 // can't be copied 1995 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; 2007 bool empty() 2008 { 2009 return j > 5; 2010 } 2011 2012 auto front() 2013 { 2014 return NoCopy(j); 2015 } 2016 2017 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 2037 this(int b){ 2038 this.val = i++; 2039 } 2040 2041 // can't be copied 2042 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 { 2058 bool empty() 2059 { 2060 return j > 100; 2061 } 2062 2063 auto front() 2064 { 2065 return NoCopy(1); 2066 } 2067 2068 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 2078version (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 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/** 2113Grows `array` by appending `delta` more elements. The needed memory is 2114allocated using `alloc`. The extra elements added are either default- 2115initialized, filled with copies of `init`, or initialized with values 2116fetched from `range`. 2117 2118Params: 2119T = element type of the array being created 2120alloc = the allocator used for getting memory 2121array = a reference to the array being grown 2122delta = number of elements to add (upon success the new length of `array` is 2123$(D array.length + delta)) 2124init = element used for filling the array 2125range = range used for initializing the array elements 2126 2127Returns: 2128`true` upon success, `false` if memory could not be allocated. In the 2129latter case `array` is left unaffected. 2130 2131Throws: 2132The first two overloads throw only if `alloc`'s primitives do. The 2133overloads that involve copy initialization deallocate memory and propagate the 2134exception if the copy operation throws. 2135*/ 2136bool 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{ 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 2163bool 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{ 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 2192bool expandArray(T, Allocator, R)(auto ref Allocator alloc, ref T[] array, 2193 R range) 2194if (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 .. $]; 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 { 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{ 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/** 2304Shrinks an array by `delta` elements. 2305 2306If $(D array.length < delta), does nothing and returns `false`. Otherwise, 2307destroys the last $(D array.length - delta) elements in the array and then 2308reallocates the array's buffer. If reallocation fails, fills the array with 2309default-initialized data. 2310 2311Params: 2312T = element type of the array being created 2313alloc = the allocator used for getting memory 2314array = a reference to the array being shrunk 2315delta = number of elements to remove (upon success the new length of `array` is $(D array.length - delta)) 2316 2317Returns: 2318`true` upon success, `false` if memory could not be reallocated. In the latter 2319case, the slice $(D array[$ - delta .. $]) is left with default-initialized 2320elements. 2321 2322Throws: 2323The first two overloads throw only if `alloc`'s primitives do. The 2324overloads that involve copy initialization deallocate memory and propagate the 2325exception if the copy operation throws. 2326*/ 2327bool 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{ 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 2393Destroys and then deallocates (using `alloc`) the object pointed to by a 2394pointer, the class object referred to by a `class` or `interface` 2395reference, or an entire array. It is assumed the respective entities had been 2396allocated with the same allocator. 2397 2398*/ 2399void 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 2411void dispose(A, T)(auto ref A alloc, auto ref T p) 2412if (is(T == class) || is(T == interface)) 2413{ 2414 if (!p) return; 2415 static if (is(T == interface)) 2416 { 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 2435void 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; 2459 override void method() { x = 21; } 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/** 2525Allocates a multidimensional array of elements of type T. 2526 2527Params: 2528N = number of dimensions 2529T = element type of an element of the multidimensional arrat 2530alloc = the allocator used for getting memory 2531lengths = static array containing the size of each dimension 2532 2533Returns: 2534An N-dimensional array with individual elements of type T. 2535*/ 2536auto 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 2560 scope(exit) 2561 { 2562 Mallocator.instance.disposeMultidimensionalArray(mArray); 2563 } 2564 2565 assert(mArray.length == 2); 2566 foreach (lvl2Array; mArray) 2567 { 2568 assert(lvl2Array.length == 3); 2569 foreach (lvl3Array; lvl2Array) 2570 assert(lvl3Array.length == 6); 2571 } 2572} 2573 2574/** 2575Destroys and then deallocates a multidimensional array, assuming it was 2576created with makeMultidimensionalArray and the same allocator was used. 2577 2578Params: 2579T = element type of an element of the multidimensional array 2580alloc = the allocator used for getting memory 2581array = the multidimensional array that is to be deallocated 2582*/ 2583void 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 2616 void[] allocate(size_t numBytes) 2617 { 2618 auto ret = allocator.allocate(numBytes); 2619 _allocations ~= ByteRange(ret.ptr, ret.length); 2620 return ret; 2621 } 2622 2623 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 2637 ~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 2652Returns a dynamically-typed `CAllocator` built around a given statically- 2653typed allocator `a` of type `A`. Passing a pointer to the allocator 2654creates a dynamic allocator around the allocator pointed to by the pointer, 2655without attempting to copy or move it. Passing the allocator by value or 2656reference behaves as follows. 2657 2658$(UL 2659$(LI If `A` has no state, the resulting object is allocated in static 2660shared storage.) 2661$(LI If `A` has state, the result will $(REF move, std,algorithm,mutation) 2662the supplied allocator $(D A a) within. The result itself is allocated in its 2663own statically-typed allocator.) 2664) 2665 2666*/ 2667RCIAllocator allocatorObject(A)(auto ref A a) 2668if (!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 2700RCIAllocator 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 2757Returns a dynamically-typed `CSharedAllocator` built around a given statically- 2758typed allocator `a` of type `A`. Passing a pointer to the allocator 2759creates a dynamic allocator around the allocator pointed to by the pointer, 2760without attempting to copy or move it. Passing the allocator by value or 2761reference behaves as follows. 2762 2763$(UL 2764$(LI If `A` has no state, the resulting object is allocated in static 2765shared 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. 2768The 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 2770passed-in argument into the result. The result itself is allocated in its own 2771statically-typed allocator.) 2772) 2773 2774*/ 2775//nothrow @safe 2776//nothrow @nogc @safe 2777nothrow 2778RCISharedAllocator sharedAllocatorObject(A)(auto ref A a) 2779if (!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 2818RCISharedAllocator 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 2834Implementation of `IAllocator` using `Allocator`. This adapts a 2835statically-built allocator type to `IAllocator` that is directly usable by 2836non-templated code. 2837 2838Usually `CAllocatorImpl` is used indirectly by calling $(LREF theAllocator). 2839*/ 2840class 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 2856 ref Allocator impl() 2857 { 2858 return *pimpl; 2859 } 2860 2861 @nogc pure @safe 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 2873nothrow: 2874 /// Returns `impl.alignment`. 2875 override @property uint alignment() 2876 { 2877 return impl.alignment; 2878 } 2879 2880 /** 2881 Returns `impl.goodAllocSize(s)`. 2882 */ 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 */ 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 */ 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. 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)). 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. 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 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 */ 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 */ 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 */ 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 */ 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 3022 override void incRef() 3023 { 3024 static if (stateSize!Allocator) ++rc; 3025 } 3026 3027 @nogc nothrow pure @trusted 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 3062Implementation of `ISharedAllocator` using `Allocator`. This adapts a 3063statically-built, shareable across threads, allocator type to `ISharedAllocator` 3064that is directly usable by non-templated code. 3065 3066Usually `CSharedAllocatorImpl` is used indirectly by calling 3067$(LREF processAllocator). 3068*/ 3069class 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 3086 ref Allocator impl() shared 3087 { 3088 return *pimpl; 3089 } 3090 3091 @nogc pure @safe 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 3103nothrow: 3104 /// Returns `impl.alignment`. 3105 override @property uint alignment() shared 3106 { 3107 return impl.alignment; 3108 } 3109 3110 /** 3111 Returns `impl.goodAllocSize(s)`. 3112 */ 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 */ 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 */ 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. 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)). 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. 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 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 */ 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 */ 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 */ 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 */ 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 3252 override void incRef() shared 3253 { 3254 static if (stateSize!Allocator) atomicOp!"+="(rc, 1); 3255 } 3256 3257 @nogc nothrow pure @trusted 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 3328Stores an allocator object in thread-local storage (i.e. non-`shared` D 3329global). `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 3333means e.g. `ThreadLocal!Mallocator` does not work because `Mallocator`'s 3334state is not stored as members of `Mallocator`, but instead is hidden in the 3335C library implementation. 3336 3337*/ 3338struct 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 3366unittest 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 3382A binary search tree that uses no allocation of its own. Instead, it relies on 3383user code to allocate nodes externally. Then `EmbeddedTree`'s primitives wire 3384the nodes appropriately. 3385 3386Warning: currently `EmbeddedTree` is not using rebalancing, so it may 3387degenerate. A red-black tree implementation storing the color with one of the 3388pointers is planned for the future. 3389*/ 3390private 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 3571unittest 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 ]; 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); 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 3601pointer `p` lies. Pointers right after the end of allocated blocks are also 3602considered internal. 3603 3604The implementation stores three additional words with each allocation (one for 3605the block size and two for search management). 3606 3607*/ 3608private 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 3717unittest 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; 3725 foreach (v; vals) 3726 { 3727 allox ~= a.allocate(v); 3728 } 3729 a.blockMap.assertSane; 3730 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 3762unittest 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 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 3832unittest 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 3858unittest 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