1 /** 2 Common code shared with other `btl.autoptr` modules . 3 4 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 Authors: Adam Búš 6 */ 7 module btl.autoptr.common; 8 9 import std.meta : AliasSeq; 10 11 import btl.traits.common; 12 import btl.traits.assume; 13 import btl.lifetime; 14 15 import btl.internal.traits; 16 import btl.internal.allocator; 17 import btl.internal.gc; 18 19 20 21 /** 22 Type used as parameter for function pointer returned from `DestructorType`. 23 */ 24 public alias Evoid = btl.lifetime.Evoid; 25 26 27 /** 28 Type used in forward constructors. 29 */ 30 alias Forward = btl.traits.common.Forward; 31 32 33 34 /**/ 35 package struct SmartPtr{} 36 37 38 39 /** 40 Default `ControlBlock` for `SharedPtr` and `RcPtr`. 41 */ 42 public alias SharedControlBlock = ControlBlock!(int, int); 43 public deprecated alias SharedControlType = SharedControlBlock; 44 45 46 47 /** 48 Default `ControlBlock` for `UniquePtr`. 49 */ 50 public alias UniqueControlBlock = ControlBlock!void; 51 public deprecated alias UniqueControlType = UniqueControlBlock; 52 53 54 55 /** 56 Default allcoator for `SharedPtr.make`, `RcPtr.make`, `UniquePtr.make` and `IntrusivePtr.make`. 57 */ 58 public alias DefaultAllocator = Mallocator; 59 60 61 62 63 /** 64 Check if type `Type` is of type destructor type (is(void function(Evoid* )pure nothrow @safe @nogc : Type)) 65 */ 66 alias isDestructorType = btl.lifetime.isDtorType; 67 68 69 /// 70 unittest{ 71 static assert(isDestructorType!(void function(Evoid* )pure)); 72 static assert(isDestructorType!(DestructorType!long)); 73 static assert(!isDestructorType!(long)); 74 } 75 76 77 78 /** 79 Destructor type of destructors of types `Types` ( void function(Evoid*)@destructor_attributes ). 80 */ 81 public template DestructorType(Types...){ 82 import std.traits : Unqual, isDynamicArray, BaseClassesTuple; 83 import std.range : ElementEncodingType; 84 import std.meta : AliasSeq; 85 86 static void fn_body(Evoid*)pure nothrow @safe @nogc{} 87 88 static void impl()(Evoid*){ 89 90 static foreach(alias Type; Types){ 91 static if(is(Type == class)){ 92 { 93 ClassDtorType!Type fn = &fn_body; 94 fn(null); 95 } 96 } 97 else static if(isDynamicArray!Type){ 98 { 99 ElementEncodingType!(Unqual!Type) tmp; 100 } 101 } 102 else static if(is(void function(Evoid*)pure nothrow @safe @nogc : Unqual!Type)){ 103 { 104 Unqual!Type fn = &fn_body; 105 fn(null); 106 } 107 } 108 else{ 109 { 110 DtorType!Type fn = &fn_body; 111 fn(null); 112 } 113 } 114 } 115 } 116 117 alias DestructorType = typeof(&impl!()); 118 } 119 120 121 /// 122 unittest{ 123 static assert(is(DestructorType!long == void function(Evoid*)pure nothrow @safe @nogc)); 124 125 126 static struct Struct{ 127 ~this()nothrow @system{ 128 } 129 } 130 static assert(is(DestructorType!Struct == void function(Evoid*)nothrow @system)); 131 132 133 version(D_BetterC) 134 static extern(C)class Class{ 135 ~this()pure @trusted{ 136 137 } 138 } 139 else 140 static class Class{ 141 ~this()pure @trusted{ 142 143 } 144 } 145 146 static assert(is(DestructorType!Class == void function(Evoid*)pure @safe)); 147 148 //multiple types: 149 static assert(is(DestructorType!(Class, Struct, long) == void function(Evoid*)@system)); 150 151 static assert(is( 152 DestructorType!(Class, DestructorType!long, DestructorType!Struct) == DestructorType!(Class, Struct, long) 153 )); 154 } 155 156 157 158 /** 159 Similiar to `DestructorType` but returns destructor attributes of type `Deleter` and attributes necessary to call variable of type `Deleter` with parameter of type `T`. 160 */ 161 public template DestructorDeleterType(T, Deleter){ 162 import std.traits : isCallable; 163 164 static assert(isCallable!Deleter); 165 166 static assert(__traits(compiles, (ElementReferenceTypeImpl!T elm){ 167 cast(void)Deleter.init(elm); 168 })); 169 170 alias Get(T) = T; 171 172 static void impl()(Evoid*){ 173 ElementReferenceTypeImpl!T elm; 174 175 Deleter deleter; 176 177 cast(void)deleter(elm); 178 } 179 180 alias DestructorDeleterType = typeof(&impl!()); 181 182 } 183 184 185 186 /** 187 Similiar to `DestructorType` but returns destructor attributes of type `Allocator` and attributes of methods `void[] allocate(size_t)` and `void deallocate(void[])`. 188 189 If method `allocate` is `@safe`/`@trusted` then method `deallocate` is assumed to be `@trusted` even if doesn't have `@safe`/`@trusted` attribute. 190 */ 191 public template DestructorAllocatorType(Allocator){ 192 import std.traits : Unqual, isPointer, PointerTarget, isAggregateType, hasMember; 193 import std.range : ElementEncodingType; 194 import std.meta : AliasSeq; 195 196 static if(isPointer!Allocator) 197 alias AllocatorType = PointerTarget!Allocator; 198 else 199 alias AllocatorType = Allocator; 200 201 static assert(isAggregateType!AllocatorType); 202 203 static assert(hasMember!(AllocatorType, "deallocate")); 204 static assert(hasMember!(AllocatorType, "allocate")); 205 206 static if(isStatelessAllocator!Allocator){ 207 static assert(__traits(compiles, (){ 208 const size_t size; 209 void[] data = statelessAllcoator!Allocator.allocate(size); 210 }())); 211 212 static assert(__traits(compiles, (){ 213 void[] data; 214 statelessAllcoator!Allocator.deallocate(data); 215 }())); 216 } 217 else{ 218 static assert(__traits(compiles, (){ 219 const size_t size; 220 Allocator.init.allocate(size); 221 }())); 222 223 static assert(__traits(compiles, (){ 224 void[] data; 225 Allocator.init.deallocate(data); 226 }())); 227 } 228 229 230 alias Get(T) = T; 231 232 static void impl()(Evoid*){ 233 static if(!isStatelessAllocator!Allocator){ 234 { 235 Allocator allocator; 236 } 237 } 238 void[] data; 239 const size_t size; 240 241 static if(isStatelessAllocator!Allocator){ 242 enum bool safe_alloc = __traits(compiles, ()@safe{ 243 const size_t size; 244 statelessAllcoator!Allocator.allocate(size); 245 }()); 246 247 data = statelessAllcoator!Allocator.allocate(size); 248 249 static if(safe_alloc) 250 function(void[] data)@trusted{ 251 statelessAllcoator!Allocator.deallocate(data); 252 }(data); 253 else 254 statelessAllcoator!Allocator.deallocate(data); 255 } 256 else{ 257 enum bool safe_alloc = __traits(compiles, ()@safe{ 258 const size_t size; 259 Allocator.init.allocate(size); 260 }()); 261 262 data = Allocator.init.allocate(size); 263 264 static if(safe_alloc) 265 function(void[] data)@trusted{ 266 Allocator.init.deallocate(data); 267 }(data); 268 else 269 Allocator.init.deallocate(data); 270 } 271 272 } 273 274 alias DestructorAllocatorType = typeof(&impl!()); 275 } 276 277 278 279 /** 280 This template deduce `ControlType` shared qualifier in `SharedPtr`, `RcPtr` and `UniquePtr`. 281 282 If `Type` is shared then `ControlType` is shared too (atomic counting). 283 */ 284 public template ControlBlockDeduction(Type, ControlType){ 285 import std.traits : Select; 286 287 alias impl = Select!( 288 is(Type == shared), /+|| is(Type == immutable)+/ 289 shared(ControlType), 290 ControlType 291 ); 292 293 alias ControlBlockDeduction = impl; 294 } 295 deprecated alias ControlTypeDeduction = ControlBlockDeduction; 296 297 298 299 /// 300 unittest{ 301 alias CB = ControlBlock!(int, int); 302 303 static assert(is(ControlBlockDeduction!(long, CB) == CB)); 304 static assert(is(ControlBlockDeduction!(void, CB) == CB)); 305 static assert(is(ControlBlockDeduction!(shared double, CB) == shared CB)); 306 static assert(is(ControlBlockDeduction!(const int, CB) == CB)); 307 static assert(is(ControlBlockDeduction!(shared const int, CB) == shared CB)); 308 309 static assert(is(ControlBlockDeduction!(immutable int, CB) == CB)); 310 311 static assert(is(ControlBlockDeduction!(shared int[], CB) == shared CB)); 312 static assert(is(ControlBlockDeduction!(shared(int)[], CB) == CB)); 313 } 314 315 316 /** 317 Check if type `T` is of type `ControlBlock!(...)`. 318 */ 319 public template isControlBlock(T...) 320 if(T.length == 1){ 321 import std.traits : Unqual, isMutable; 322 323 enum bool isControlBlock = is( 324 Unqual!(T[0]) == ControlBlock!Args, Args... 325 ); 326 } 327 328 /// 329 unittest{ 330 static assert(!isControlBlock!long); 331 static assert(!isControlBlock!(void*)); 332 static assert(isControlBlock!(ControlBlock!long)); 333 static assert(isControlBlock!(ControlBlock!(int, int))); 334 } 335 336 337 /** 338 Control block for `SharedPtr`, `RcPtr`, `UniquePtr` and `IntrusivePtr`. 339 340 Contains ref counting and dynamic dispatching for destruction and dealocation of managed object. 341 342 Template parameters: 343 344 `_Shared` signed integer for ref counting of `SharedPtr` or void if ref counting is not necessary (`UniquePtr` doesn't need ref counting). 345 346 `_Weak` signed integer for weak ref counting of `SharedPtr` or void if weak pointer is not necessary. 347 348 */ 349 public template ControlBlock(_Shared, _Weak = void){ 350 import std.traits : Unqual, isUnsigned, isIntegral, isMutable; 351 import core.atomic; 352 353 static assert((isIntegral!_Shared && !isUnsigned!_Shared) || is(_Shared == void)); 354 static assert(is(Unqual!_Shared == _Shared)); 355 356 static assert((isIntegral!_Weak && !isUnsigned!_Weak) || is(_Weak == void)); 357 static assert(is(Unqual!_Weak == _Weak)); 358 359 struct ControlBlock{ 360 /** 361 Signed integer for ref counting of `SharedPtr` or void if ref counting is not necessary (`UniquePtr`). 362 */ 363 public alias Shared = _Shared; 364 365 /** 366 Signed integer for weak ref counting of `SharedPtr` or void if weak counting is not necessary (`UniquePtr` or `SharedPtr` without weak ptr). 367 */ 368 public alias Weak = _Weak; 369 370 /** 371 `true` if `ControlBlock` has ref counting (`Shared != void`). 372 */ 373 public enum bool hasSharedCounter = !is(_Shared == void); 374 375 /** 376 `true` if `ControlBlock` has weak ref counting (`Weak != void`). 377 */ 378 public enum bool hasWeakCounter = !is(_Weak == void); 379 380 /** 381 Copy constructor is @disabled. 382 */ 383 public @disable this(scope ref const typeof(this) )scope pure nothrow @safe @nogc; 384 385 /** 386 Assign is @disabled. 387 */ 388 public @disable void opAssign(scope ref const typeof(this) )scope pure nothrow @safe @nogc; 389 390 391 //necessary for intrusive ptr 392 package void initialize(this This)(immutable Vtable* vptr)scope pure nothrow @trusted @nogc{ 393 (cast(Unqual!This*)&this).vptr = vptr; 394 } 395 396 static assert(hasSharedCounter >= hasWeakCounter); 397 398 package static struct Vtable{ 399 400 static if(hasSharedCounter) 401 void function(ControlBlock*)pure nothrow @safe @nogc on_zero_shared; 402 403 static if(hasWeakCounter) 404 void function(ControlBlock*)pure nothrow @safe @nogc on_zero_weak; 405 406 void function(ControlBlock*, bool)pure nothrow @safe @nogc manual_destroy; 407 408 bool initialized()const pure nothrow @safe @nogc{ 409 return manual_destroy !is null; 410 } 411 412 bool valid()const pure nothrow @safe @nogc{ 413 bool result = true; 414 static if(hasSharedCounter){ 415 if(on_zero_shared is null) 416 return false; 417 } 418 static if(hasWeakCounter){ 419 if(on_zero_weak is null) 420 return false; 421 } 422 423 return manual_destroy !is null; 424 } 425 } 426 427 private immutable(Vtable)* vptr; 428 429 static if(hasSharedCounter) 430 private Shared shared_count = 0; 431 432 static if(hasWeakCounter) 433 private Weak weak_count = 0; 434 435 package this(this This)(immutable Vtable* vptr)pure nothrow @safe @nogc{ 436 assert(vptr !is null); 437 this.vptr = vptr; 438 } 439 440 package final auto count(bool weak, this This)()scope const pure nothrow @safe @nogc{ 441 static if(weak){ 442 static if(hasWeakCounter){ 443 static if(is(This == shared)) 444 return atomicLoad(this.weak_count); 445 else 446 return this.weak_count; 447 } 448 else 449 return int.init; 450 } 451 else{ 452 static if(hasSharedCounter){ 453 static if(is(This == shared)) 454 return atomicLoad(this.shared_count); 455 else 456 return this.shared_count; 457 } 458 else 459 return int.max; 460 } 461 462 } 463 464 465 package final void add(bool weak, this This)()scope @trusted pure nothrow @nogc 466 if(isMutable!This){ 467 enum bool atomic = is(This == shared); 468 469 static if(weak){ 470 static if(hasWeakCounter){ 471 rc_increment!atomic(this.weak_count); 472 } 473 } 474 else{ 475 static if(hasSharedCounter){ 476 rc_increment!atomic(this.shared_count); 477 } 478 } 479 } 480 481 public final void release(bool weak, this This)()@trusted pure nothrow @nogc{ 482 enum bool atomic = is(This == shared); 483 auto self = cast(Unconst!This*)&this; 484 485 static if(is(This == immutable)){ 486 static if(hasSharedCounter) 487 assert(this.count!(false) == 0); 488 static if(hasWeakCounter) 489 assert(this.count!(true) == 0); 490 } 491 492 static if(!hasSharedCounter){ 493 static assert(is(This == immutable)); 494 self.manual_destroy(true); ///TODO 495 } 496 else static if(weak){ 497 static if(hasWeakCounter){ 498 static if(atomic){ 499 if(atomicLoad!(MemoryOrder.acq)(self.weak_count) == 0) 500 self.on_zero_weak(); 501 502 else if(rc_decrement!atomic(self.weak_count) == -1) 503 self.on_zero_weak(); 504 } 505 else{ 506 if(this.weak_count == 0) 507 self.on_zero_weak(); 508 else if(rc_decrement!atomic(self.weak_count) == -1) 509 self.on_zero_weak(); 510 } 511 } 512 } 513 else{ 514 static assert(hasSharedCounter); 515 516 if(rc_decrement!atomic(self.shared_count) == -1){ 517 //auto tmp = &this; 518 //auto self = &this; 519 self.on_zero_shared(); 520 521 self.release!true; 522 } 523 } 524 } 525 526 static if(hasSharedCounter){ 527 package final bool add_shared_if_exists()@trusted pure nothrow @nogc{ 528 529 if(this.shared_count == -1){ 530 return false; 531 } 532 this.shared_count += 1; 533 return true; 534 } 535 536 package final bool add_shared_if_exists()shared @trusted pure nothrow @nogc{ 537 auto owners = atomicLoad(this.shared_count); 538 539 while(owners != -1){ 540 import core.atomic; 541 if(casWeak(&this.shared_count, 542 &owners, 543 cast(Shared)(owners + 1) 544 )){ 545 return true; 546 } 547 } 548 549 return false; 550 } 551 } 552 553 static if(hasSharedCounter) 554 package void on_zero_shared(this This)()pure nothrow @nogc @trusted{ 555 this.vptr.on_zero_shared(cast(ControlBlock*)&this); 556 } 557 558 static if(hasWeakCounter) 559 package void on_zero_weak(this This)()pure nothrow @nogc @trusted{ 560 this.vptr.on_zero_weak(cast(ControlBlock*)&this); 561 } 562 563 package void manual_destroy(this This)(bool dealocate)pure nothrow @nogc @trusted{ 564 this.vptr.manual_destroy(cast(ControlBlock*)&this, dealocate); 565 } 566 } 567 } 568 569 570 /// 571 unittest{ 572 static assert(is(ControlBlock!(int, long).Shared == int)); 573 static assert(is(ControlBlock!(int, long).Weak == long)); 574 static assert(is(ControlBlock!int.Shared == int)); 575 static assert(is(ControlBlock!int.Weak == void)); 576 577 static assert(ControlBlock!(int, int).hasSharedCounter); 578 static assert(ControlBlock!(int, int).hasWeakCounter); 579 580 static assert(is(ControlBlock!int == ControlBlock!(int, void))); 581 static assert(ControlBlock!int.hasSharedCounter); 582 static assert(ControlBlock!int.hasWeakCounter == false); 583 584 static assert(ControlBlock!void.hasSharedCounter == false); 585 static assert(ControlBlock!void.hasWeakCounter == false); 586 } 587 588 589 590 /** 591 Return number of `ControlBlock`s in type `Type`. 592 593 `IntrusivePtr` need exact `1` control block. 594 */ 595 public template isIntrusive(Type){ 596 static if(is(Type == struct)){ 597 enum size_t impl = isIntrusiveStruct!(Type)(); 598 } 599 else static if(is(Type == class)){ 600 enum size_t impl = isIntrusiveClass!(Type, false)(); 601 } 602 else{ 603 enum size_t impl = 0; 604 } 605 606 enum size_t isIntrusive = impl; 607 } 608 609 /// 610 unittest{ 611 static assert(isIntrusive!long == 0); 612 613 static assert(isIntrusive!(ControlBlock!int) == 0); 614 615 static class Foo{ 616 long x; 617 ControlBlock!int control; 618 } 619 620 static assert(isIntrusive!Foo == 1); 621 622 static struct Struct{ 623 long x; 624 ControlBlock!int control; 625 Foo foo; 626 } 627 static assert(isIntrusive!Struct == 1); 628 629 630 static class Bar : Foo{ 631 const ControlBlock!int control2; 632 Struct s; 633 634 } 635 static assert(isIntrusive!Bar == 2); 636 637 638 static class Zee{ 639 long l; 640 double x; 641 Struct str; 642 } 643 static assert(isIntrusive!Zee == 0); 644 645 } 646 647 private size_t isIntrusiveClass(Type, bool ignoreBase)()pure nothrow @trusted @nogc 648 if(is(Type == class)){ 649 import std.traits : BaseClassesTuple; 650 651 Type ty = null; 652 653 size_t result = 0; 654 655 static foreach(alias T; typeof(ty.tupleof)){ 656 static if(is(T == struct) && isControlBlock!T) 657 result += 1; 658 } 659 660 static if(!ignoreBase) 661 static foreach(alias T; BaseClassesTuple!Type){ 662 result += isIntrusiveClass!(T, true); 663 } 664 665 return result; 666 667 } 668 669 private size_t isIntrusiveStruct(Type)()pure nothrow @trusted @nogc 670 if(is(Type == struct)){ 671 Type* ty = null; 672 673 size_t result = 0; 674 675 static foreach(alias T; typeof((*ty).tupleof)){ 676 static if(is(T == struct) && isControlBlock!T) 677 result += 1; 678 } 679 680 return result; 681 } 682 683 684 685 /** 686 Alias to `ControlBlock` including qualifiers contained by `Type`. 687 688 If `mutable` is `true`, then result type alias is mutable (can be shared). 689 */ 690 public template IntrusiveControlBlock(Type, bool mutable = false){ 691 692 static if(is(Type == class)) 693 alias PtrControlBlock = typeof(intrusivControlBlock(Type.init)); 694 else static if(is(Type == struct)) 695 alias PtrControlBlock = typeof(intrusivControlBlock(*cast(Type*)null)); 696 else 697 alias PtrControlBlock = void*; 698 699 700 import std.traits : CopyTypeQualifiers, PointerTarget, Unconst; 701 702 static if(mutable && is(PtrControlBlock == shared)) 703 alias impl = shared(Unconst!(PointerTarget!PtrControlBlock)); 704 else 705 alias impl = PointerTarget!PtrControlBlock; 706 707 alias IntrusiveControlBlock = impl; 708 709 } 710 711 /// 712 unittest{ 713 714 static class Foo{ 715 ControlBlock!int c; 716 } 717 718 static assert(is( 719 IntrusiveControlBlock!(Foo) == ControlBlock!int 720 )); 721 static assert(is( 722 IntrusiveControlBlock!(const Foo) == const ControlBlock!int 723 )); 724 static assert(is( 725 IntrusiveControlBlock!(shared Foo) == shared ControlBlock!int 726 )); 727 static assert(is( 728 IntrusiveControlBlock!(const shared Foo) == const shared ControlBlock!int 729 )); 730 static assert(is( 731 IntrusiveControlBlock!(immutable Foo) == immutable ControlBlock!int 732 )); 733 734 735 736 static class Bar{ 737 shared ControlBlock!int c; 738 } 739 740 static assert(is( 741 IntrusiveControlBlock!(Bar) == shared ControlBlock!int 742 )); 743 static assert(is( 744 IntrusiveControlBlock!(const Bar) == const shared ControlBlock!int 745 )); 746 static assert(is( 747 IntrusiveControlBlock!(shared Bar) == shared ControlBlock!int 748 )); 749 static assert(is( 750 IntrusiveControlBlock!(const shared Bar) == const shared ControlBlock!int 751 )); 752 static assert(is( 753 IntrusiveControlBlock!(immutable Bar) == immutable ControlBlock!int 754 )); 755 756 757 758 static assert(is( 759 IntrusiveControlBlock!(long) == void 760 )); 761 762 } 763 764 765 766 767 /** 768 Check if `T` is smart pointer of type `btl.autoptr.shared_ptr.SharedPtr`, `btl.autoptr.rc_ptr.RcPtr` or `btl.autoptr.intrusive_ptr.IntrusivePtr`. 769 */ 770 template isSmartPtr(T){ 771 /+import btl.autoptr.shared_ptr : isSharedPtr; 772 import btl.autoptr.rc_ptr : isRcPtr; 773 import btl.autoptr.intrusive_ptr : isIntrusivePtr; 774 775 enum bool isSmartPtr = false 776 || isSharedPtr!T 777 || isRcPtr!T 778 || isIntrusivePtr!T;+/ 779 enum bool isSmartPtr = is(T.SmartPtr : .SmartPtr); 780 } 781 782 783 784 import std.meta : allSatisfy, staticMap, AliasSeq; 785 import std.traits : isMutable; 786 import core.lifetime : forward, move; 787 788 /** 789 Sefly dereference all `args` of types `btl.autoptr.shared_ptr.SharedPtr`, `btl.autoptr.rc_ptr.RcPtr` and `btl.autoptr.intrusive_ptr.IntrusivePtr` and forward them to callable alias `fn`. 790 791 Ref args are copyied and non ref args are moved. 792 793 */ 794 public template apply(alias fn){ 795 auto impl(Args...)(scope Args args){ 796 pragma(inline, true); @property auto elm(alias arg)()@trusted{ 797 return arg.element(); 798 } 799 800 return fn(staticMap!(elm, args)); 801 } 802 803 auto apply(Args...)(scope auto ref Args args) 804 if( allSatisfy!(isSmartPtr, Args) 805 && allSatisfy!(isMutable, staticMap!(GetControlType, Args)) 806 ){ 807 pragma(inline, true); @property auto ref param(alias arg)()@trusted{ 808 static assert(!is(typeof(arg) == shared)); 809 810 static if(arg.isWeak) 811 return arg.lock(); 812 else 813 return forward!arg; 814 } 815 return impl(staticMap!(param, args)); 816 } 817 } 818 819 820 821 public auto mutablePtr(P)(scope auto ref P ptr)@system //@system 822 if(true 823 && isSmartPtr!P //(isIntrusivePtr!P || isSharedPtr!P || isRcPtr!P) 824 && !is(P == immutable) && !is(P == inout) && !is(P.ControlType == immutable) 825 ){ 826 import std.traits : CopyTypeQualifiers, Unconst, isMutable, TemplateOf; 827 828 static assert(isMutable!(P.ControlType)); 829 830 alias Ptr = TemplateOf!P; 831 832 alias Self = Ptr!( 833 CopyTypeQualifiers!(P, P.ElementType), 834 P.DestructorType, 835 P.ControlType, 836 P.isWeak 837 ); 838 839 static if(__traits(isRef, ptr)) 840 return *cast(Self*)&ptr; 841 else 842 return move(*cast(Self*)&ptr); 843 } 844 845 846 /// 847 @safe pure nothrow @nogc unittest{ 848 import btl.autoptr.shared_ptr; 849 import btl.autoptr.rc_ptr; 850 import btl.autoptr.intrusive_ptr; 851 852 import core.lifetime : move; 853 854 static class Foo{ 855 ControlBlock!(int, int) c; 856 int i; 857 858 this(int i)pure nothrow @safe @nogc{ 859 this.i = i; 860 } 861 862 ~this()pure nothrow @safe @nogc{ 863 i = -1; 864 } 865 } 866 867 ()@safe{ 868 auto a = SharedPtr!long.make(42); 869 auto b = RcPtr!float.make(3.14); 870 auto c = IntrusivePtr!Foo.make(123); 871 872 assert(a.useCount == 1); 873 assert(b.useCount == 1); 874 assert(c.useCount == 1); 875 876 int i = apply!((scope long* x, scope float* y, scope Foo z){ 877 assert(a.useCount == 2); 878 assert(b.useCount == 2); 879 880 a = null; 881 b = null; 882 c = null; 883 884 assert(z.i != -1); 885 //x, y, z are still valid until end of the scope. 886 887 return z.i; 888 })(a, b, move(c)); 889 890 assert(i == 123); 891 }(); 892 893 } 894 895 896 897 package template weakLock(From, To){ 898 enum weakLock = (From.isWeak && !To.isWeak); 899 } 900 901 package template GetControlType(Ptr){ 902 import std.traits : CopyTypeQualifiers; 903 904 alias GetControlType = CopyTypeQualifiers!(Ptr, Ptr.ControlType); 905 } 906 907 package template GetElementType(Ptr){ 908 import std.traits : CopyTypeQualifiers; 909 910 alias GetElementType = CopyTypeQualifiers!(Ptr, Ptr.ElementType); 911 } 912 913 package template GetElementReferenceType(Ptr){ 914 import std.traits : CopyTypeQualifiers; 915 916 alias GetElementReferenceType = ElementReferenceTypeImpl!(GetElementType!Ptr); 917 } 918 919 920 package template UnqualSmartPtr(Ptr){ 921 import std.traits : TemplateOf; 922 923 alias SmartPtr = TemplateOf!Ptr; 924 925 alias UnqualSmartPtr = SmartPtr!( 926 GetElementType!Ptr, 927 Ptr.DestructorType, 928 GetControlType!Ptr, 929 Ptr.isWeak 930 ); 931 } 932 933 package template ElementReferenceTypeImpl(T){ 934 import std.traits : Select, isDynamicArray; 935 import std.range : ElementEncodingType; 936 937 938 static if(false 939 || is(T == class) || is(T == interface) 940 || is(T == function) || is(T == delegate) 941 || is(T : U*, U) 942 ){ 943 alias ElementReferenceTypeImpl = T; 944 } 945 else static if(isDynamicArray!T){ 946 alias ElementReferenceTypeImpl = ElementEncodingType!T[]; 947 } 948 else{ 949 alias ElementReferenceTypeImpl = T*; 950 } 951 } 952 953 package template ElementPointerTypeImpl(T){ 954 import std.traits : Select, isDynamicArray; 955 import std.range : ElementEncodingType; 956 957 958 static if(false 959 || is(T == class) || is(T == interface) 960 //|| is(T == function) || is(T == delegate) 961 //|| is(T : U*, U) 962 ){ 963 alias ElementPointerTypeImpl = T; 964 } 965 else static if(isDynamicArray!T){ 966 alias ElementPointerTypeImpl = ElementEncodingType!T*; 967 } 968 else{ 969 alias ElementPointerTypeImpl = T*; 970 } 971 } 972 973 974 package static auto lockSmartPtr(alias fn, Ptr, Args...) 975 (scope auto ref shared Ptr ptr, scope auto ref Args args){ 976 import std.traits : CopyConstness, CopyTypeQualifiers, Unqual; 977 import core.lifetime : forward; 978 import btl.internal.mutex : getMutex; 979 980 shared mutex = getMutex(ptr); 981 982 mutex.lock(); 983 scope(exit)mutex.unlock(); 984 985 alias Result = UnqualSmartPtr!(shared Ptr); 986 987 988 return fn( 989 *(()@trusted => cast(Result*)&ptr )(), 990 forward!args 991 ); 992 } 993 994 995 import std.traits : BaseClassesTuple, Unqual, Unconst, CopyTypeQualifiers; 996 import std.meta : AliasSeq; 997 998 /* 999 Return pointer to qualified control block. 1000 Pointer is mutable and can be shared if control block is shared. 1001 For example if control block is immutable, then return type can be immtuable(ControlBlock)* or shared(immutable(ControlBlock)*). 1002 If result pointer is shared then atomic ref counting is necessary. 1003 */ 1004 package auto intrusivControlBlock(Type)(return auto ref Type elm)pure nothrow @trusted @nogc{ 1005 1006 static if(is(Type == struct)){ 1007 static if(isControlBlock!Type){ 1008 static if(is(Type == shared)) 1009 return cast(CopyTypeQualifiers!(shared(void), Type*))&elm; 1010 else 1011 return &elm; 1012 } 1013 else{ 1014 static assert(isIntrusive!(Unqual!Type) == 1); 1015 1016 foreach(ref x; (*cast(Unqual!(typeof(elm))*)&elm).tupleof){ 1017 static if(isControlBlock!(typeof(x))){ 1018 auto control = intrusivControlBlock(*cast(CopyTypeQualifiers!(Type, typeof(x))*)&x); 1019 1020 static if(is(Type == shared) || is(typeof(x) == shared)) 1021 return cast(CopyTypeQualifiers!(shared(void), typeof(control)))control; 1022 else 1023 return control; 1024 } 1025 } 1026 } 1027 } 1028 else static if(is(Type == class)){ 1029 static assert(isIntrusive!(Unqual!Type) == 1); 1030 1031 static if(isIntrusiveClass!(Type, true)){ 1032 foreach(ref x; (cast(Unqual!(typeof(elm)))elm).tupleof){ 1033 static if(isControlBlock!(typeof(x))){ 1034 auto control = intrusivControlBlock(*cast(CopyTypeQualifiers!(Type, typeof(x))*)&x); 1035 1036 static if(is(Type == shared) || is(typeof(x) == shared)) 1037 return cast(CopyTypeQualifiers!(shared(void), typeof(control)))control; 1038 else 1039 return control; 1040 } 1041 } 1042 1043 } 1044 else static foreach(alias T; BaseClassesTuple!Type){ 1045 static if(isIntrusiveClass!(T, true)){ 1046 1047 foreach(ref x; (cast(Unqual!T)elm).tupleof){ 1048 static if(isControlBlock!(typeof(x))){ 1049 auto control = intrusivControlBlock(*cast(CopyTypeQualifiers!(Type, typeof(x))*)&x); 1050 1051 static if(is(Type == shared) || is(typeof(x) == shared)) 1052 return cast(CopyTypeQualifiers!(shared(void), typeof(control)))control; 1053 else 1054 return control; 1055 } 1056 } 1057 1058 } 1059 } 1060 } 1061 else{ 1062 return cast(void*)null; 1063 } 1064 1065 } 1066 1067 //control block 1068 unittest{ 1069 import std.traits : lvalueOf; 1070 static struct Foo{ 1071 ControlBlock!int c; 1072 } 1073 1074 static assert(is( 1075 typeof(intrusivControlBlock(lvalueOf!Foo)) == ControlBlock!int* 1076 )); 1077 static assert(is( 1078 typeof(intrusivControlBlock(lvalueOf!(shared Foo))) == shared ControlBlock!int* 1079 )); 1080 static assert(is( 1081 typeof(intrusivControlBlock(lvalueOf!(const Foo))) == const(ControlBlock!int)* 1082 )); 1083 static assert(is( 1084 typeof(intrusivControlBlock(lvalueOf!(shared const Foo))) == shared const(ControlBlock!int)* 1085 )); 1086 static assert(is( 1087 typeof(intrusivControlBlock(lvalueOf!(immutable Foo))) == immutable(ControlBlock!int)* 1088 )); 1089 static assert(is( 1090 typeof(intrusivControlBlock(lvalueOf!(long))) == void* 1091 )); 1092 } 1093 1094 //shared control block 1095 unittest{ 1096 import std.traits : lvalueOf; 1097 static struct Foo{ 1098 shared ControlBlock!int c; 1099 } 1100 1101 static assert(is( 1102 typeof(intrusivControlBlock(lvalueOf!Foo)) == shared ControlBlock!int* 1103 )); 1104 static assert(is( 1105 typeof(intrusivControlBlock(lvalueOf!(shared Foo))) == shared ControlBlock!int* 1106 )); 1107 static assert(is( 1108 typeof(intrusivControlBlock(lvalueOf!(const Foo))) == shared const(ControlBlock!int)* 1109 )); 1110 static assert(is( 1111 typeof(intrusivControlBlock(lvalueOf!(shared const Foo))) == shared const(ControlBlock!int)* 1112 )); 1113 static assert(is( 1114 typeof(intrusivControlBlock(lvalueOf!(immutable Foo))) == shared immutable(ControlBlock!int)* 1115 )); 1116 } 1117 1118 1119 //std dynamic cast. 1120 package auto dynCastElement(To, From)(return From from)pure nothrow @trusted @nogc 1121 if(isClassOrInterface!From && isClassOrInterface!To){ 1122 import std.traits : CopyTypeQualifiers, Unqual; 1123 1124 alias Result = CopyTypeQualifiers!(From, To); 1125 1126 return (from is null) 1127 ? Result.init 1128 : cast(Result)cast(Unqual!To)cast(Unqual!From)from; 1129 } 1130 1131 1132 //Return offset of intrusive control block in Type. 1133 package size_t intrusivControlBlockOffset(Type)()pure nothrow @safe @nogc{ 1134 static assert(isIntrusive!(Unqual!Type) == 1); 1135 1136 static if(is(Type == struct)){ 1137 static foreach(alias var; Type.tupleof){ 1138 static if(isControlBlock!(typeof(var))) 1139 return var.offsetof; 1140 } 1141 } 1142 else static if(is(Type == class)){ 1143 static if(isIntrusiveClass!(Type, true)){ 1144 static foreach(alias var; Type.tupleof){ 1145 static if(isControlBlock!(typeof(var))) 1146 return var.offsetof; 1147 1148 } 1149 } 1150 else static foreach(alias T; BaseClassesTuple!Type){ 1151 static if(isIntrusiveClass!(T, true)){ 1152 static foreach(alias var; T.tupleof){ 1153 static if(isControlBlock!(typeof(var))) 1154 return var.offsetof; 1155 } 1156 } 1157 } 1158 } 1159 else static assert(0, "no impl"); 1160 } 1161 1162 unittest{ 1163 static assert(isIntrusive!long == 0); 1164 static assert(isIntrusive!(ControlBlock!int) == 0); 1165 1166 static class Foo{ 1167 long x; 1168 ControlBlock!int control; 1169 } 1170 1171 1172 { 1173 Foo foo; 1174 auto control = intrusivControlBlock(foo); 1175 } 1176 1177 1178 static assert(isIntrusive!Foo == 1); 1179 static assert(Foo.control.offsetof == intrusivControlBlockOffset!Foo()); 1180 1181 static struct Struct{ 1182 long x; 1183 ControlBlock!int control; 1184 Foo foo; 1185 } 1186 1187 static assert(isIntrusive!Struct == 1); 1188 static assert(Struct.control.offsetof == intrusivControlBlockOffset!Struct()); 1189 1190 1191 static class Bar : Foo{ 1192 ControlBlock!int control2; 1193 Struct s; 1194 1195 } 1196 1197 static assert(isIntrusive!Bar == 2); 1198 } 1199 1200 1201 /* 1202 same as core.lifetime.emplace but limited for intrusive class and struct. 1203 initialize vptr for intrusive control block before calling ctor of class/struct. 1204 */ 1205 package void emplaceIntrusive(T, Vptr, Args...)(auto ref T chunk, immutable Vptr* vptr, auto ref Args args) 1206 if(isIntrusive!T){ 1207 static assert(is(T == struct) || is(T == class)); 1208 assert(vptr !is null); 1209 1210 ()@trusted{ 1211 static if(is(T == struct)){ 1212 import core.internal.lifetime : emplaceInitializer; 1213 1214 emplaceInitializer(*cast(Unqual!T*)&chunk); 1215 } 1216 else static if(is(T == class)){ 1217 // Initialize the object in its pre-ctor state 1218 enum classSize = __traits(classInstanceSize, T); 1219 (cast(void*) chunk)[0 .. classSize] = typeid(T).initializer[]; //(() @trusted => (cast(void*) chunk)[0 .. classSize] = typeid(T).initializer[])(); 1220 } 1221 else static assert(0, "no impl"); 1222 1223 }(); 1224 1225 auto control = intrusivControlBlock(chunk); 1226 control.initialize(vptr); 1227 1228 import core.lifetime : forward, emplace; 1229 1230 1231 static if (args.length == 0 && !is(T == class)){ 1232 static assert(is(typeof({static T i;})), 1233 "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~ 1234 ".this() is annotated with @disable." 1235 ); 1236 1237 //emplaceInitializer(chunk); 1238 } 1239 1240 // Call the ctor if any 1241 else static if (is(typeof(chunk.__ctor(forward!args)))){ 1242 // T defines a genuine constructor accepting args 1243 // Go the classic route: write .init first, then call ctor 1244 chunk.__ctor(forward!args); 1245 } 1246 else{ 1247 static assert(args.length == 0 && !is(typeof(&T.__ctor)), 1248 "Don't know how to initialize an object of type " 1249 ~ T.stringof ~ " with arguments " ~ typeof(args).stringof); 1250 } 1251 1252 } 1253 1254 package template MakeEmplace(_Type, _DestructorType, _ControlType, _AllocatorType, bool supportGC){ 1255 import core.lifetime : emplace; 1256 import std.traits: hasIndirections, isAbstractClass, isMutable, isDynamicArray, 1257 Select, CopyTypeQualifiers, 1258 Unqual, Unconst, PointerTarget; 1259 1260 ///static assert(isIntrusive!_Type == 0); 1261 1262 static assert(!isAbstractClass!_Type, 1263 "cannot create object of abstract class" ~ Unqual!_Type.stringof 1264 ); 1265 static assert(!is(_Type == interface), 1266 "cannot create object of interface type " ~ Unqual!_Type.stringof 1267 ); 1268 1269 static if(!isDynamicArray!_Type) 1270 static assert(is(DestructorType!_Type : _DestructorType)); 1271 1272 static assert(is(DestructorAllocatorType!_AllocatorType : _DestructorType), 1273 "allocator attributes `" ~ DestructorAllocatorType!_AllocatorType.stringof ~ "`" ~ 1274 "doesn't support destructor attributes `" ~ _DestructorType.stringof 1275 ); 1276 1277 enum bool hasStatelessAllocator = isStatelessAllocator!_AllocatorType; 1278 1279 enum bool hasWeakCounter = _ControlType.hasWeakCounter; 1280 1281 enum bool hasSharedCounter = _ControlType.hasSharedCounter; 1282 1283 enum bool allocatorGCRange = supportGC 1284 && !hasStatelessAllocator 1285 && hasIndirections!_AllocatorType; 1286 1287 enum bool dataGCRange = supportGC 1288 && (false 1289 || classHasIndirections!_Type 1290 || hasIndirections!_Type 1291 || (is(_Type == class) && is(Unqual!_Type : Object)) 1292 ); 1293 1294 alias Vtable = _ControlType.Vtable; 1295 1296 1297 struct MakeEmplace{ 1298 private static immutable Vtable vtable; 1299 1300 private _ControlType control; 1301 private void[instanceSize!_Type] data; 1302 1303 static if(!hasStatelessAllocator) 1304 private _AllocatorType allocator; 1305 1306 static assert(control.offsetof + typeof(control).sizeof == data.offsetof); 1307 1308 version(D_BetterC) 1309 private static void shared_static_this()pure nothrow @safe @nogc{ 1310 assumePure(()@trusted{ 1311 Vtable* vptr = cast(Vtable*)&vtable; 1312 1313 static if(hasSharedCounter) 1314 vptr.on_zero_shared = &virtual_on_zero_shared; 1315 1316 static if(hasWeakCounter) 1317 vptr.on_zero_weak = &virtual_on_zero_weak; 1318 1319 vptr.manual_destroy = &virtual_manual_destroy; 1320 })(); 1321 1322 } 1323 else 1324 shared static this()nothrow @safe @nogc{ 1325 static if(hasWeakCounter){ 1326 vtable = Vtable( 1327 &virtual_on_zero_shared, 1328 &virtual_on_zero_weak, 1329 &virtual_manual_destroy 1330 ); 1331 } 1332 else static if(hasSharedCounter){ 1333 vtable = Vtable( 1334 &virtual_on_zero_shared, 1335 &virtual_manual_destroy 1336 ); 1337 } 1338 else vtable = Vtable( 1339 &virtual_manual_destroy 1340 ); 1341 } 1342 1343 @disable public this(this)pure nothrow @safe @nogc; 1344 1345 public @property _ControlType* base()return pure nothrow @trusted @nogc{ 1346 //static assert(this.control.offsetof == 0); 1347 /+return function _ControlType*(ref _ControlType ct)@trusted{ 1348 return &ct; 1349 }(this.control);+/ 1350 return &this.control; 1351 } 1352 1353 public @property Select!(isClassOrInterface!_Type, _Type, _Type*) get()pure nothrow @trusted @nogc{ 1354 return cast(typeof(return))this.data.ptr; 1355 } 1356 1357 1358 1359 1360 public static MakeEmplace* make(Args...)(_AllocatorType a, auto ref Args args){ 1361 import std.traits: hasIndirections; 1362 import core.lifetime : forward, emplace; 1363 1364 static assert(!isAbstractClass!_Type, 1365 "cannot create object of abstract class" ~ Unqual!_Type.stringof 1366 ); 1367 static assert(!is(_Type == interface), 1368 "cannot create object of interface type " ~ Unqual!_Type.stringof 1369 ); 1370 1371 1372 static if(hasStatelessAllocator) 1373 void[] raw = statelessAllcoator!_AllocatorType.allocate(typeof(this).sizeof); 1374 else 1375 void[] raw = a.allocate(typeof(this).sizeof); 1376 1377 if(raw.length == 0) 1378 return null; 1379 1380 smart_ptr_allocate(raw[]); 1381 1382 MakeEmplace* result = (()@trusted => cast(MakeEmplace*)raw.ptr)(); 1383 1384 static if(dataGCRange){ 1385 static assert(supportGC); 1386 static if(!hasStatelessAllocator) 1387 static assert(typeof(this).data.offsetof < typeof(this).allocator.offsetof); 1388 1389 static if(allocatorGCRange) 1390 enum size_t gc_range_size = typeof(this).allocator.offsetof 1391 - typeof(this).data.offsetof 1392 + typeof(this.allocator).sizeof; 1393 else 1394 enum size_t gc_range_size = data.length; 1395 1396 gcAddRange( 1397 (()@trusted => cast(void*)result.data.ptr)(), 1398 gc_range_size 1399 ); 1400 } 1401 else static if(allocatorGCRange){ 1402 static assert(supportGC); 1403 static assert(!dataGCRange); 1404 1405 gcAddRange( 1406 cast(void*)&result.allocator, 1407 _AllocatorType.sizeof 1408 ); 1409 } 1410 1411 //debug new MakeEmplace(forward!(a, args)); 1412 return emplace(result, forward!(a, args)); 1413 } 1414 1415 public this(this This, Args...)(_AllocatorType a, auto ref Args args) 1416 if(isMutable!This){ 1417 version(D_BetterC){ 1418 if(!vtable.initialized()) 1419 shared_static_this(); 1420 } 1421 else 1422 assert(vtable.initialized()); 1423 1424 import core.lifetime : forward, emplace; 1425 1426 static if(!hasStatelessAllocator){ 1427 static if(isMoveConstructableElement!_AllocatorType) 1428 this.allocator = forward!a; 1429 else 1430 this.allocator = a; 1431 } 1432 1433 import std.traits : isStaticArray; 1434 import std.range : ElementEncodingType; 1435 1436 assert(vtable.valid, "vtables are not initialized"); 1437 this.control = _ControlType(&vtable); //this.control.initialize(&vtable); 1438 1439 static if(is(Unqual!_Type == void)){ 1440 //nothing 1441 } 1442 else static if(isStaticArray!_Type){ 1443 static if(args.length == 1 && is(Args[0] : _Type)){ 1444 //cast(void)emplace!(_Type)(this.data, forward!args); 1445 cast(void)emplace( 1446 ((ref data)@trusted => cast(_Type*)data.ptr)(this.data), 1447 forward!args 1448 ); 1449 } 1450 else{ 1451 _Type* data = cast(_Type*)this.data.ptr; 1452 1453 foreach(ref ElementEncodingType!_Type d; (*data)[]){ 1454 1455 static if(isClassOrInterface!(ElementEncodingType!_Type)){ 1456 static if(args.length == 0) 1457 d = null; 1458 else static if(args.length == 1) 1459 d = args[0]; 1460 else static assert(0, "no impl"); 1461 1462 } 1463 else{ 1464 cast(void)emplace(&d, args); 1465 } 1466 } 1467 } 1468 } 1469 else{ 1470 static if(isClassOrInterface!_Type) 1471 auto data = ((ref data)@trusted => cast(_Type)data.ptr)(this.data); 1472 else 1473 auto data = ((ref data)@trusted => cast(_Type*)data.ptr)(this.data); 1474 1475 cast(void)emplace( 1476 data, 1477 forward!args 1478 ); 1479 } 1480 1481 1482 1483 smart_ptr_construct(); 1484 } 1485 1486 1487 1488 static if(hasSharedCounter){ 1489 public static void virtual_on_zero_shared(Unqual!_ControlType* control)pure nothrow @nogc @trusted{ 1490 auto self = get_offset_this(control); 1491 self.destruct(); 1492 1493 static if(!hasWeakCounter) 1494 self.deallocate(); 1495 } 1496 } 1497 1498 static if(hasWeakCounter){ 1499 public static void virtual_on_zero_weak(Unqual!_ControlType* control)pure nothrow @nogc @trusted{ 1500 auto self = get_offset_this(control); 1501 self.deallocate(); 1502 } 1503 } 1504 1505 public static void virtual_manual_destroy(Unqual!_ControlType* control, bool dealocate)pure nothrow @nogc @trusted{ 1506 auto self = get_offset_this(control); 1507 self.destruct(); 1508 if(dealocate) 1509 self.deallocate(); 1510 1511 } 1512 1513 private static inout(MakeEmplace)* get_offset_this(inout(Unqual!_ControlType)* control)pure nothrow @system @nogc{ 1514 assert(control !is null); 1515 1516 return cast(typeof(return))((cast(void*)control) - MakeEmplace.control.offsetof); 1517 } 1518 1519 1520 private void destruct()pure nothrow @system @nogc{ 1521 1522 static if(is(_Type == struct) || is(_Type == class)){ 1523 void* data_ptr = this.data.ptr; 1524 1525 //btl.internal.lifetime.destruct!(_Type, DestructorType!void)(data_ptr); 1526 static if(is(_Type == struct)){ 1527 _Type* data = ((ref data)@trusted => cast(_Type*)data.ptr)(this.data); 1528 destructImpl!(false, DtorType!void)(*data); 1529 } 1530 else static if(is(_Type == class)){ 1531 _Type data = ((ref data)@trusted => cast(_Type)data.ptr)(this.data); 1532 destructClassImpl!(false, DtorType!void)(data); 1533 } 1534 else static assert(0, "no impl"); 1535 1536 static if(!allocatorGCRange && dataGCRange){ 1537 gcRemoveRange(data_ptr); 1538 } 1539 1540 } 1541 else static if(is(_Type == interface)){ 1542 assert(0, "no impl"); 1543 } 1544 else{ 1545 // nothing 1546 } 1547 1548 smart_ptr_destruct(); 1549 } 1550 1551 private void deallocate()pure nothrow @system @nogc{ 1552 void* self = cast(void*)&this; 1553 destructImpl!(false, DtorType!void)(this);//btl.internal.lifetime.destruct!(typeof(this), DestructorType!void)(self); 1554 1555 void[] raw = self[0 .. typeof(this).sizeof]; 1556 1557 1558 static if(hasStatelessAllocator) 1559 assumePureNoGcNothrow(function(void[] raw)@trusted => statelessAllcoator!_AllocatorType.deallocate(raw))(raw); 1560 else 1561 assumePureNoGcNothrow(function(void[] raw, ref typeof(this.allocator) allo)@trusted => allo.deallocate(raw))(raw, this.allocator); 1562 1563 1564 static if(allocatorGCRange){ 1565 static if(dataGCRange) 1566 gcRemoveRange(this.data.ptr); 1567 else 1568 gcRemoveRange(&this.allocator); 1569 } 1570 1571 smart_ptr_deallocate(raw[]); 1572 } 1573 1574 } 1575 } 1576 1577 package template MakeDynamicArray(_Type, _DestructorType, _ControlType, _AllocatorType, bool supportGC){ 1578 import std.traits: hasIndirections, isAbstractClass, isDynamicArray, Unqual; 1579 import std.range : ElementEncodingType; 1580 1581 static assert(isDynamicArray!_Type); 1582 1583 static assert(is(DestructorType!_Type : _DestructorType)); 1584 1585 static assert(is(DestructorAllocatorType!_AllocatorType : _DestructorType), 1586 "allocator attributes `" ~ DestructorAllocatorType!_AllocatorType.stringof ~ "`" ~ 1587 "doesn't support destructor attributes `" ~ _DestructorType.stringof 1588 ); 1589 1590 enum bool hasStatelessAllocator = isStatelessAllocator!_AllocatorType; 1591 1592 enum bool hasWeakCounter = _ControlType.hasWeakCounter; 1593 1594 enum bool hasSharedCounter = _ControlType.hasSharedCounter; 1595 1596 //enum bool referenceElementType = isClassOrInterface!_Type; 1597 1598 enum bool allocatorGCRange = supportGC 1599 && !hasStatelessAllocator 1600 && hasIndirections!_AllocatorType; 1601 1602 enum bool dataGCRange = supportGC 1603 && hasIndirections!(ElementEncodingType!_Type); 1604 1605 alias Vtable = _ControlType.Vtable; 1606 1607 struct MakeDynamicArray{ 1608 static if(!hasStatelessAllocator) 1609 private _AllocatorType allocator; 1610 1611 private size_t length; 1612 private _ControlType control; 1613 private ElementEncodingType!_Type[0] data_impl; 1614 1615 static assert(control.offsetof + typeof(control).sizeof == data_impl.offsetof); 1616 1617 @property inout(ElementEncodingType!_Type)[] data()return inout pure nothrow @trusted @nogc{ 1618 return data_impl.ptr[0 .. this.length]; 1619 } 1620 1621 private static immutable Vtable vtable; 1622 1623 version(D_BetterC) 1624 private static void shared_static_this()pure nothrow @safe @nogc{ 1625 assumePure(()@trusted{ 1626 Vtable* vptr = cast(Vtable*)&vtable; 1627 1628 static if(hasSharedCounter) 1629 vptr.on_zero_shared = &virtual_on_zero_shared; 1630 1631 static if(hasWeakCounter) 1632 vptr.on_zero_weak = &virtual_on_zero_weak; 1633 1634 vptr.manual_destroy = &virtual_manual_destroy; 1635 })(); 1636 1637 } 1638 else 1639 shared static this()nothrow @safe @nogc{ 1640 static if(hasWeakCounter){ 1641 vtable = Vtable( 1642 &virtual_on_zero_shared, 1643 &virtual_on_zero_weak, 1644 &virtual_manual_destroy 1645 ); 1646 } 1647 else static if(hasSharedCounter){ 1648 vtable = Vtable( 1649 &virtual_on_zero_shared, 1650 &virtual_manual_destroy 1651 ); 1652 } 1653 else vtable = Vtable( 1654 &virtual_manual_destroy 1655 ); 1656 } 1657 1658 @disable public this(this)pure nothrow @safe @nogc; 1659 1660 public @property _ControlType* base()return pure nothrow @trusted @nogc{ 1661 return &this.control; 1662 } 1663 1664 public @property auto get()return pure nothrow @trusted @nogc{ 1665 return this.data; 1666 } 1667 1668 1669 1670 1671 public static MakeDynamicArray* make(Args...)(_AllocatorType a, const size_t n, auto ref Args args){ 1672 import std.traits: hasIndirections; 1673 import core.lifetime : forward, emplace; 1674 1675 const size_t arraySize = (ElementEncodingType!_Type.sizeof * n); 1676 1677 static if(hasStatelessAllocator) 1678 void[] raw = statelessAllcoator!_AllocatorType.allocate(typeof(this).sizeof + arraySize); 1679 else 1680 void[] raw = a.allocate(typeof(this).sizeof + arraySize); 1681 1682 if(raw.length == 0) 1683 return null; 1684 1685 smart_ptr_allocate(raw[]); 1686 1687 MakeDynamicArray* result = (()@trusted => cast(MakeDynamicArray*)raw.ptr)(); 1688 1689 1690 static if(allocatorGCRange){ 1691 static assert(supportGC); 1692 static assert(typeof(this).length.offsetof >= typeof(this).allocator.offsetof); 1693 1694 static if(dataGCRange) 1695 const size_t gc_range_size = typeof(this).sizeof 1696 - typeof(this).allocator.offsetof 1697 + arraySize; 1698 else 1699 enum size_t gc_range_size = _AllocatorType.sizeof; 1700 1701 gcAddRange( 1702 cast(void*)&result.allocator, 1703 gc_range_size 1704 ); 1705 } 1706 else static if(dataGCRange){ 1707 static assert(supportGC); 1708 static assert(!allocatorGCRange); 1709 1710 gcAddRange( 1711 (()@trusted => result.data.ptr)(), 1712 arraySize //result.data.length * _Type.sizeof 1713 ); 1714 } 1715 1716 return emplace!MakeDynamicArray(result, forward!(a, n, args)); 1717 } 1718 1719 1720 public this(Args...)(_AllocatorType a, const size_t n, auto ref Args args){ 1721 version(D_BetterC){ 1722 if(!vtable.initialized()) 1723 shared_static_this(); 1724 } 1725 else 1726 assert(vtable.initialized()); 1727 1728 this.control = _ControlType(&vtable); 1729 assert(vtable.valid, "vtables are not initialized"); 1730 1731 static if(!hasStatelessAllocator){ 1732 static if(isMoveConstructableElement!_AllocatorType) 1733 this.allocator = forward!a; 1734 else 1735 this.allocator = a; 1736 } 1737 1738 this.length = n; 1739 1740 import core.lifetime : emplace; 1741 1742 foreach(ref d; this.data[]) 1743 emplace((()@trusted => &d)(), args); 1744 1745 smart_ptr_construct(); 1746 } 1747 1748 1749 static if(hasSharedCounter) 1750 private static void virtual_on_zero_shared(Unqual!_ControlType* control)pure nothrow @nogc @trusted{ 1751 auto self = get_offset_this(control); 1752 self.destruct(); 1753 1754 static if(!hasWeakCounter) 1755 self.deallocate(); 1756 } 1757 1758 static if(hasWeakCounter) 1759 private static void virtual_on_zero_weak(Unqual!_ControlType* control)pure nothrow @nogc @trusted{ 1760 auto self = get_offset_this(control); 1761 self.deallocate(); 1762 } 1763 1764 private static void virtual_manual_destroy(Unqual!_ControlType* control, bool deallocate)pure nothrow @trusted @nogc{ 1765 auto self = get_offset_this(control); 1766 self.destruct(); 1767 1768 if(deallocate) 1769 self.deallocate(); 1770 } 1771 1772 private static inout(MakeDynamicArray)* get_offset_this(inout(Unqual!_ControlType)* control)pure nothrow @system @nogc{ 1773 assert(control !is null); 1774 return cast(typeof(return))((cast(void*)control) - MakeDynamicArray.control.offsetof); 1775 } 1776 1777 private void destruct()pure nothrow @system @nogc{ 1778 destructRangeImpl!(false, DtorType!void)(this.data); 1779 1780 static if(!allocatorGCRange && dataGCRange){ 1781 gcRemoveRange(this.data.ptr); 1782 } 1783 1784 smart_ptr_destruct(); 1785 } 1786 1787 private void deallocate()pure nothrow @system @nogc{ 1788 1789 const size_t data_length = ElementEncodingType!_Type.sizeof * this.data.length; 1790 void* self = cast(void*)&this; 1791 destructImpl!(false, DtorType!void)(this); //btl.internal.lifetime.destruct!(typeof(this), DestructorType!void)(self); 1792 1793 1794 void[] raw = self[0 .. typeof(this).sizeof + data_length]; 1795 1796 1797 1798 static if(hasStatelessAllocator) 1799 assumePureNoGcNothrow(function(void[] raw)@trusted => statelessAllcoator!_AllocatorType.deallocate(raw))(raw); 1800 else 1801 assumePureNoGcNothrow(function(void[] raw, ref typeof(this.allocator) allo)@trusted => allo.deallocate(raw))(raw, this.allocator); 1802 1803 1804 static if(allocatorGCRange){ 1805 gcRemoveRange(&this.allocator); 1806 } 1807 1808 smart_ptr_deallocate(raw[]); 1809 } 1810 1811 } 1812 } 1813 1814 package template MakeIntrusive(_Type/+, _DestructorType+/, _AllocatorType, bool supportGC) 1815 if(isIntrusive!_Type == 1){ 1816 import core.lifetime : emplace; 1817 import std.traits: hasIndirections, isAbstractClass, isMutable, 1818 Select, CopyTypeQualifiers, 1819 Unqual, Unconst, PointerTarget, BaseClassesTuple; 1820 1821 static assert(is(_Type == struct) || is(_Type == class)); 1822 1823 static assert(!isAbstractClass!_Type, 1824 "cannot create object of abstract class" ~ Unqual!_Type.stringof 1825 ); 1826 1827 static assert(!is(_Type == interface), 1828 "cannot create object of interface type " ~ Unqual!_Type.stringof 1829 ); 1830 1831 static assert(is(DestructorAllocatorType!_AllocatorType : .DestructorType!_Type), 1832 "allocator attributes `" ~ DestructorAllocatorType!_AllocatorType.stringof ~ "`" ~ 1833 "doesn't support destructor attributes `" ~ .DestructorType!_Type.stringof 1834 ); 1835 1836 static if(is(_Type == class)) 1837 static foreach(alias Base; BaseClassesTuple!_Type){ 1838 static if(!is(Base == Object)) 1839 static assert(is(.DestructorType!_Type : .DestructorType!Base)); 1840 } 1841 1842 alias ControlType = IntrusiveControlBlock!_Type; 1843 1844 enum bool hasStatelessAllocator = isStatelessAllocator!_AllocatorType; 1845 1846 enum bool hasWeakCounter = ControlType.hasWeakCounter; 1847 1848 enum bool hasSharedCounter = ControlType.hasSharedCounter; 1849 1850 enum bool allocatorGCRange = supportGC 1851 && !hasStatelessAllocator 1852 && hasIndirections!_AllocatorType; 1853 1854 enum bool dataGCRange = supportGC 1855 && (false 1856 || classHasIndirections!_Type 1857 || hasIndirections!_Type 1858 || (is(_Type == class) && is(Unqual!_Type : Object)) 1859 ); 1860 1861 alias Vtable = ControlType.Vtable; 1862 1863 1864 struct MakeIntrusive{ 1865 private static immutable Vtable vtable; 1866 1867 1868 private @property ref auto control()return pure nothrow @trusted @nogc{ 1869 static if(isClassOrInterface!_Type) 1870 auto control = intrusivControlBlock(cast(_Type)this.data.ptr); 1871 else 1872 auto control = intrusivControlBlock(*cast(_Type*)this.data.ptr); 1873 1874 alias ControlPtr = typeof(control); 1875 1876 static if(is(typeof(control) == shared)) 1877 alias MutableControl = shared(Unconst!(PointerTarget!ControlPtr)*); 1878 else 1879 alias MutableControl = Unconst!(PointerTarget!ControlPtr)*; 1880 1881 //static assert(!is(typeof(*control) == immutable), "intrusive control block cannot be immutable"); 1882 return *cast(MutableControl)control; 1883 } 1884 1885 private void[instanceSize!_Type] data; 1886 1887 static if(!hasStatelessAllocator) 1888 private _AllocatorType allocator; 1889 1890 version(D_BetterC) 1891 private static void shared_static_this()pure nothrow @safe @nogc{ 1892 assumePure(()@trusted{ 1893 Vtable* vptr = cast(Vtable*)&vtable; 1894 1895 static if(hasSharedCounter) 1896 vptr.on_zero_shared = &virtual_on_zero_shared; 1897 1898 static if(hasWeakCounter) 1899 vptr.on_zero_weak = &virtual_on_zero_weak; 1900 1901 vptr.manual_destroy = &virtual_manual_destroy; 1902 })(); 1903 1904 } 1905 else 1906 shared static this()nothrow @safe @nogc{ 1907 static if(hasWeakCounter){ 1908 vtable = Vtable( 1909 &virtual_on_zero_shared, 1910 &virtual_on_zero_weak, 1911 &virtual_manual_destroy 1912 ); 1913 } 1914 else static if(hasSharedCounter){ 1915 vtable = Vtable( 1916 &virtual_on_zero_shared, 1917 &virtual_manual_destroy 1918 ); 1919 } 1920 else vtable = Vtable( 1921 &virtual_manual_destroy 1922 ); 1923 } 1924 1925 public @property Select!(isClassOrInterface!_Type, _Type, _Type*) get()return pure nothrow @trusted @nogc{ 1926 return cast(typeof(return))this.data.ptr; 1927 } 1928 1929 1930 1931 1932 public static MakeIntrusive* make(Args...)(_AllocatorType a, auto ref Args args){ 1933 import std.traits: hasIndirections; 1934 import core.lifetime : forward, emplace; 1935 1936 static if(hasStatelessAllocator) 1937 void[] raw = statelessAllcoator!_AllocatorType.allocate(typeof(this).sizeof); 1938 else 1939 void[] raw = a.allocate(typeof(this).sizeof); 1940 1941 if(raw.length == 0) 1942 return null; 1943 1944 smart_ptr_allocate(raw[]); 1945 1946 MakeIntrusive* result = (()@trusted => cast(MakeIntrusive*)raw.ptr)(); 1947 1948 static if(dataGCRange){ 1949 static assert(supportGC); 1950 1951 static if(!hasStatelessAllocator) 1952 static assert(typeof(this).data.offsetof < typeof(this).allocator.offsetof); 1953 1954 static if(allocatorGCRange) 1955 enum size_t gc_range_size = typeof(this).allocator.offsetof 1956 - typeof(this).data.offsetof 1957 + typeof(this.allocator).sizeof; 1958 else 1959 enum size_t gc_range_size = data.length; 1960 1961 gcAddRange( 1962 (()@trusted => cast(void*)result.data.ptr)(), 1963 gc_range_size 1964 ); 1965 } 1966 else static if(allocatorGCRange){ 1967 static assert(supportGC); 1968 static assert(!dataGCRange); 1969 1970 gcAddRange( 1971 cast(void*)&result.allocator, 1972 _AllocatorType.sizeof 1973 ); 1974 } 1975 1976 return emplace(result, forward!(a, args)); 1977 } 1978 1979 1980 public this(this This, Args...)(_AllocatorType a, auto ref Args args) 1981 if(isMutable!This){ 1982 version(D_BetterC){ 1983 if(!vtable.initialized()) 1984 shared_static_this(); 1985 } 1986 else 1987 assert(vtable.initialized()); 1988 1989 import core.lifetime : forward, emplace; 1990 1991 static if(!hasStatelessAllocator){ 1992 static if(isMoveConstructableElement!_AllocatorType) 1993 this.allocator = forward!a; 1994 else 1995 this.allocator = a; 1996 } 1997 1998 import std.traits : isStaticArray; 1999 import std.range : ElementEncodingType; 2000 2001 assert(vtable.valid, "vtables are not initialized"); 2002 2003 static if(is(_Type == class)){ 2004 _Type data = ((ref data)@trusted => cast(_Type)data.ptr)(this.data); 2005 emplaceIntrusive(data, &vtable, forward!args); 2006 //emplace(data, forward!args); 2007 //intrusivControlBlock(data).initialize(&vtable); 2008 } 2009 else static if(is(_Type == struct)){ 2010 _Type* data = ((ref data)@trusted => cast(_Type*)data.ptr)(this.data); 2011 emplaceIntrusive(*data, &vtable, forward!args); 2012 //emplace(data, forward!args); 2013 //intrusivControlBlock(*data).initialize(&vtable); 2014 } 2015 else static assert(0, "no impl"); 2016 2017 2018 2019 smart_ptr_construct(); 2020 } 2021 2022 2023 2024 static if(hasSharedCounter){ 2025 public static void virtual_on_zero_shared(Unqual!ControlType* control)pure nothrow @nogc @trusted{ 2026 auto self = get_offset_this(control); 2027 self.destruct(); 2028 2029 static if(!hasWeakCounter) 2030 self.deallocate(); 2031 } 2032 } 2033 2034 static if(hasWeakCounter){ 2035 public static void virtual_on_zero_weak(Unqual!ControlType* control)pure nothrow @nogc @trusted{ 2036 auto self = get_offset_this(control); 2037 self.deallocate(); 2038 } 2039 } 2040 2041 public static void virtual_manual_destroy(Unqual!ControlType* control, bool dealocate)pure nothrow @nogc @trusted{ 2042 auto self = get_offset_this(control); 2043 self.destruct(); 2044 if(dealocate) 2045 self.deallocate(); 2046 2047 } 2048 2049 private static inout(MakeIntrusive)* get_offset_this(inout(Unqual!ControlType)* control)pure nothrow @system @nogc{ 2050 assert(control !is null); 2051 2052 enum size_t offset = data.offsetof + intrusivControlBlockOffset!_Type; 2053 return cast(typeof(return))((cast(void*)control) - offset); 2054 } 2055 2056 2057 private void destruct()pure nothrow @system @nogc{ 2058 2059 static if(is(_Type == struct) || is(_Type == class)){ 2060 void* data_ptr = this.data.ptr; 2061 //btl.internal.lifetime.destruct!(_Type, DestructorType!void)(data_ptr); 2062 2063 static if(is(_Type == struct)){ 2064 _Type* data = ((ref data)@trusted => cast(_Type*)data.ptr)(this.data); 2065 destructImpl!(false, DtorType!void)(*data); 2066 } 2067 else static if(is(_Type == class)){ 2068 _Type data = ((ref data)@trusted => cast(_Type)data.ptr)(this.data); 2069 destructClassImpl!(false, DtorType!void)(data); 2070 } 2071 else static assert(0, "no impl"); 2072 2073 static if(!allocatorGCRange && dataGCRange){ 2074 gcRemoveRange(data_ptr); 2075 } 2076 2077 } 2078 else static if(is(_Type == interface)){ 2079 assert(0, "no impl"); 2080 } 2081 else{ 2082 // nothing 2083 } 2084 2085 smart_ptr_destruct(); 2086 } 2087 2088 private void deallocate()pure nothrow @system @nogc{ 2089 void* self = cast(void*)&this; 2090 destructImpl!(false, DtorType!void)(this);//btl.internal.lifetime.destruct!(typeof(this), DestructorType!void)(self); 2091 2092 void[] raw = self[0 .. typeof(this).sizeof]; 2093 2094 2095 static if(hasStatelessAllocator) 2096 assumePureNoGcNothrow(function(void[] raw)@trusted => statelessAllcoator!_AllocatorType.deallocate(raw))(raw); 2097 else 2098 assumePureNoGcNothrow(function(void[] raw, ref typeof(this.allocator) allo)@trusted => allo.deallocate(raw))(raw, this.allocator); 2099 2100 2101 static if(allocatorGCRange){ 2102 static if(dataGCRange) 2103 gcRemoveRange(this.data.ptr); 2104 else 2105 gcRemoveRange(&this.allocator); 2106 } 2107 2108 smart_ptr_deallocate(raw[]); 2109 } 2110 2111 } 2112 } 2113 2114 package template MakeDeleter(_Type, _DestructorType, _ControlType, DeleterType, _AllocatorType, bool supportGC){ 2115 import std.traits: hasIndirections, isAbstractClass, isDynamicArray, Unqual; 2116 2117 static if(!isDynamicArray!_Type) 2118 static assert(is(DestructorType!_Type : _DestructorType)); 2119 2120 static assert(is(DestructorAllocatorType!_AllocatorType : _DestructorType), 2121 "allocator attributes `" ~ DestructorAllocatorType!_AllocatorType.stringof ~ "`" ~ 2122 "doesn't support destructor attributes `" ~ _DestructorType.stringof 2123 ); 2124 2125 static assert(is(.DestructorDeleterType!(_Type, DeleterType) : _DestructorType), 2126 "deleter attributes '" ~ DestructorDeleterType!(_Type, DeleterType).stringof ~ 2127 "' doesn't support destructor attributes " ~ _DestructorType.stringof 2128 ); 2129 2130 enum bool hasStatelessAllocator = isStatelessAllocator!_AllocatorType; 2131 2132 enum bool hasWeakCounter = _ControlType.hasWeakCounter; 2133 2134 enum bool hasSharedCounter = _ControlType.hasSharedCounter; 2135 2136 enum bool allocatorGCRange = supportGC 2137 && !hasStatelessAllocator 2138 && hasIndirections!_AllocatorType; 2139 2140 enum bool deleterGCRange = supportGC 2141 && hasIndirections!DeleterType; 2142 2143 enum bool dataGCRange = supportGC; 2144 2145 alias Vtable = _ControlType.Vtable; 2146 2147 alias ElementReferenceType = ElementReferenceTypeImpl!_Type; 2148 2149 struct MakeDeleter{ 2150 static assert(control.offsetof == 0); 2151 2152 private _ControlType control; 2153 2154 static if(!hasStatelessAllocator) 2155 private _AllocatorType allocator; 2156 2157 private DeleterType deleter; 2158 package ElementReferenceType data; 2159 2160 private static immutable Vtable vtable; 2161 2162 version(D_BetterC) 2163 private static void shared_static_this()pure nothrow @safe @nogc{ 2164 assumePure(()@trusted{ 2165 Vtable* vptr = cast(Vtable*)&vtable; 2166 2167 static if(hasSharedCounter) 2168 vptr.on_zero_shared = &virtual_on_zero_shared; 2169 2170 static if(hasWeakCounter) 2171 vptr.on_zero_weak = &virtual_on_zero_weak; 2172 2173 vptr.manual_destroy = &virtual_manual_destroy; 2174 })(); 2175 2176 } 2177 else 2178 shared static this()nothrow @safe @nogc{ 2179 static if(hasWeakCounter){ 2180 vtable = Vtable( 2181 &virtual_on_zero_shared, 2182 &virtual_on_zero_weak, 2183 &virtual_manual_destroy 2184 ); 2185 } 2186 else static if(hasSharedCounter){ 2187 vtable = Vtable( 2188 &virtual_on_zero_shared, 2189 &virtual_manual_destroy 2190 ); 2191 } 2192 else vtable = Vtable( 2193 &virtual_manual_destroy 2194 ); 2195 } 2196 2197 2198 @disable public this(this)pure nothrow @safe @nogc; 2199 2200 public _ControlType* base()pure nothrow @safe @nogc{ 2201 return &this.control; 2202 } 2203 2204 public alias get = data; 2205 2206 2207 public static MakeDeleter* make(Args...) 2208 (_AllocatorType a, DeleterType deleter, ElementReferenceType data){ 2209 import std.traits: hasIndirections; 2210 import core.lifetime : forward, emplace; 2211 2212 static assert(!isAbstractClass!_Type, 2213 "cannot create object of abstract class" ~ Unqual!_Type.stringof 2214 ); 2215 static assert(!is(_Type == interface), 2216 "cannot create object of interface type " ~ Unqual!_Type.stringof 2217 ); 2218 2219 2220 static if(hasStatelessAllocator) 2221 void[] raw = statelessAllcoator!_AllocatorType.allocate(typeof(this).sizeof); 2222 else 2223 void[] raw = a.allocate(typeof(this).sizeof); 2224 2225 if(raw.length == 0) 2226 return null; 2227 2228 2229 smart_ptr_allocate(raw[]); 2230 2231 MakeDeleter* result = (()@trusted => cast(MakeDeleter*)raw.ptr)(); 2232 2233 static if(allocatorGCRange){ 2234 static assert(supportGC); 2235 static assert(typeof(this).data.offsetof >= typeof(this).deleter.offsetof); 2236 static assert(typeof(this).deleter.offsetof >= typeof(this).allocator.offsetof); 2237 2238 static if(dataGCRange) 2239 enum size_t gc_range_size = typeof(this).data.offsetof 2240 - typeof(this).allocator.offsetof 2241 + typeof(this.data).sizeof; 2242 else static if(deleterGCRange) 2243 enum size_t gc_range_size = typeof(this).deleter.offsetof 2244 - typeof(this).allocator.offsetof 2245 + typeof(this.deleter).sizeof; 2246 else 2247 enum size_t gc_range_size = _AllocatorType.sizeof; 2248 2249 gcAddRange( 2250 cast(void*)&result.allocator, 2251 gc_range_size 2252 ); 2253 } 2254 else static if(deleterGCRange){ 2255 static assert(supportGC); 2256 static assert(!allocatorGCRange); 2257 static assert(typeof(this).data.offsetof >= typeof(this).deleter.offsetof); 2258 2259 static if(dataGCRange) 2260 enum size_t gc_range_size = typeof(this).data.offsetof 2261 - typeof(this).deleter.offsetof 2262 + typeof(this.data).sizeof; 2263 else 2264 enum size_t gc_range_size = _DeleterType.sizeof; 2265 2266 gcAddRange( 2267 cast(void*)&result.deleter, 2268 gc_range_size 2269 ); 2270 } 2271 else static if(dataGCRange){ 2272 static assert(supportGC); 2273 static assert(!allocatorGCRange); 2274 static assert(!deleterGCRange); 2275 2276 gcAddRange( 2277 &result.data, 2278 ElementReferenceType.sizeof 2279 ); 2280 } 2281 2282 return emplace(result, forward!(a, deleter, data)); 2283 } 2284 2285 2286 public this(Args...)(_AllocatorType a, DeleterType deleter, ElementReferenceType data){ 2287 import core.lifetime : forward, emplace; 2288 2289 smart_ptr_construct(); 2290 2291 version(D_BetterC){ 2292 if(!vtable.initialized()) 2293 shared_static_this(); 2294 } 2295 else 2296 assert(vtable.initialized()); 2297 2298 this.control = _ControlType(&vtable); 2299 assert(vtable.valid, "vtables are not initialized"); 2300 2301 static if(!hasStatelessAllocator){ 2302 static if(isMoveConstructableElement!_AllocatorType) 2303 this.allocator = forward!a; 2304 else 2305 this.allocator = a; 2306 } 2307 2308 this.deleter = forward!deleter; 2309 this.data = data; 2310 } 2311 2312 2313 static if(hasSharedCounter){ 2314 public static void virtual_on_zero_shared(Unqual!_ControlType* control)pure nothrow @nogc @trusted{ 2315 auto self = get_offset_this(control); 2316 self.destruct(); 2317 2318 static if(!hasWeakCounter) 2319 self.deallocate(); 2320 } 2321 } 2322 2323 static if(hasWeakCounter){ 2324 public static void virtual_on_zero_weak(Unqual!_ControlType* control)pure nothrow @nogc @trusted{ 2325 auto self = get_offset_this(control); 2326 self.deallocate(); 2327 } 2328 } 2329 2330 public static void virtual_manual_destroy(Unqual!_ControlType* control, bool dealocate)pure nothrow @nogc @trusted{ 2331 auto self = get_offset_this(control); 2332 self.destruct(); 2333 if(dealocate) 2334 self.deallocate(); 2335 2336 } 2337 2338 private static inout(MakeDeleter)* get_offset_this(inout(Unqual!_ControlType)* control)pure nothrow @system @nogc{ 2339 assert(control !is null); 2340 return cast(typeof(return))((cast(void*)control) - MakeDeleter.control.offsetof); 2341 } 2342 2343 private void destruct()pure nothrow @system @nogc{ 2344 assumePureNoGcNothrow((ref DeleterType deleter, ElementReferenceType data){ 2345 deleter(data); 2346 })(this.deleter, this.data); 2347 2348 static if(!allocatorGCRange && !deleterGCRange && dataGCRange){ 2349 static assert(supportGC); 2350 2351 gcRemoveRange(&this.data); 2352 } 2353 2354 smart_ptr_destruct(); 2355 } 2356 2357 private void deallocate()pure nothrow @system @nogc{ 2358 void* self = cast(void*)&this; 2359 destructImpl!(false, DtorType!void)(this); //btl.internal.lifetime.destruct!(typeof(this), DestructorType!void)(self); 2360 2361 void[] raw = self[0 .. typeof(this).sizeof]; 2362 2363 2364 static if(hasStatelessAllocator) 2365 assumePureNoGcNothrow(function(void[] raw) => statelessAllcoator!_AllocatorType.deallocate(raw))(raw); 2366 else 2367 assumePureNoGcNothrow(function(void[] raw, ref typeof(this.allocator) allo) => allo.deallocate(raw))(raw, this.allocator); 2368 2369 2370 2371 static if(allocatorGCRange){ 2372 static assert(supportGC); 2373 2374 gcRemoveRange(&this.allocator); 2375 } 2376 else static if(deleterGCRange){ 2377 static assert(supportGC); 2378 static assert(!allocatorGCRange); 2379 2380 gcRemoveRange(&this.deleter); 2381 } 2382 2383 smart_ptr_deallocate(raw[]); 2384 } 2385 } 2386 } 2387 2388 2389 2390 2391 version(BTL_AUTOPTR_COUNT_ALLOCATIONS) 2392 public __gshared int _conter_allocations = 0; 2393 2394 version(BTL_AUTOPTR_COUNT_CONSTRUCTIONS) 2395 public __gshared int _conter_constructs = 0; 2396 2397 version(BTL_AUTOPTR_COUNT_ALLOCATIONS) 2398 enum bool BTL_AUTOPTR_COUNT = true; 2399 else version(BTL_AUTOPTR_COUNT_CONSTRUCTIONS) 2400 enum bool BTL_AUTOPTR_COUNT = true; 2401 else 2402 enum bool BTL_AUTOPTR_COUNT = false; 2403 2404 2405 static if(BTL_AUTOPTR_COUNT){ 2406 2407 private void shared_static_dtor_impl(){ 2408 version(BTL_AUTOPTR_COUNT_ALLOCATIONS) 2409 if(_conter_allocations != 0){ 2410 version(D_BetterC){ 2411 assert(0, "_conter_allocations != 0"); 2412 } 2413 else{ 2414 import std.conv; 2415 assert(0, "_conter_allocations: " ~ _conter_allocations.to!string); 2416 } 2417 } 2418 2419 version(BTL_AUTOPTR_COUNT_CONSTRUCTIONS) 2420 if(_conter_constructs != 0){ 2421 version(D_BetterC){ 2422 assert(0, "_conter_constructs != 0"); 2423 } 2424 else{ 2425 import std.conv; 2426 assert(0, "_conter_constructs: " ~ _conter_constructs.to!string); 2427 } 2428 } 2429 } 2430 2431 version(D_BetterC){ 2432 pragma(crt_destructor) 2433 extern(C) void shared_static_this(){ 2434 shared_static_dtor_impl(); 2435 } 2436 } 2437 else 2438 shared static ~this(){ 2439 shared_static_dtor_impl(); 2440 2441 } 2442 2443 2444 2445 } 2446 2447 2448 package void smart_ptr_allocate(scope const void[] data)pure nothrow @safe @nogc{ 2449 version(BTL_AUTOPTR_COUNT_ALLOCATIONS){ 2450 import core.atomic; 2451 2452 assumePure(function void()@trusted{ 2453 atomicFetchAdd!(MemoryOrder.raw)(_conter_allocations, 1); 2454 })(); 2455 } 2456 } 2457 package void smart_ptr_construct()pure nothrow @safe @nogc{ 2458 version(BTL_AUTOPTR_COUNT_CONSTRUCTIONS){ 2459 import core.atomic; 2460 2461 assumePure(function void()@trusted{ 2462 atomicFetchAdd!(MemoryOrder.raw)(_conter_constructs, 1); 2463 })(); 2464 } 2465 } 2466 package void smart_ptr_deallocate(scope const void[] data)pure nothrow @safe @nogc{ 2467 version(BTL_AUTOPTR_COUNT_ALLOCATIONS){ 2468 import core.atomic; 2469 2470 assumePure(function void()@trusted{ 2471 atomicFetchSub!(MemoryOrder.raw)(_conter_allocations, 1); 2472 })(); 2473 } 2474 } 2475 package void smart_ptr_destruct()pure nothrow @safe @nogc{ 2476 version(BTL_AUTOPTR_COUNT_CONSTRUCTIONS){ 2477 import core.atomic; 2478 2479 assumePure(function void()@trusted{ 2480 atomicFetchSub!(MemoryOrder.raw)(_conter_constructs, 1); 2481 })(); 2482 } 2483 } 2484 2485 2486 //increment counter and return new value, if counter is shared then atomic increment is used. 2487 private static T rc_increment(bool atomic, T)(ref T counter){ 2488 static if(atomic || is(T == shared)){ 2489 import core.atomic; 2490 2491 debug{ 2492 import std.traits : Unqual; 2493 2494 auto tmp1 = cast(Unqual!T)counter; 2495 auto result1 = (tmp1 += 1); 2496 2497 auto tmp2 = cast(Unqual!T)counter; 2498 auto result2 = atomicFetchAdd!(MemoryOrder.raw)(tmp2, 1) + 1; 2499 2500 assert(result1 == result2); 2501 } 2502 2503 return atomicFetchAdd!(MemoryOrder.raw)(counter, 1) + 1; 2504 } 2505 else{ 2506 auto result = counter += 1; 2507 2508 result += 0; 2509 return result; 2510 } 2511 } 2512 2513 unittest{ 2514 import core.atomic; 2515 2516 const int counter = 0; 2517 int tmp1 = counter; 2518 int result1 = (tmp1 += 1); 2519 assert(result1 == 1); 2520 2521 int tmp2 = counter; 2522 int result2 = atomicFetchAdd!(MemoryOrder.raw)(tmp2, 1) + 1; 2523 assert(result2 == 1); 2524 2525 assert(result1 == result2); 2526 } 2527 2528 //decrement counter and return new value, if counter is shared then atomic increment is used. 2529 private static T rc_decrement(bool atomic, T)(ref T counter){ 2530 static if(atomic || is(T == shared)){ 2531 import core.atomic; 2532 2533 debug{ 2534 import std.traits : Unqual; 2535 2536 auto tmp1 = cast(Unqual!T)counter; 2537 auto result1 = (tmp1 -= 1); 2538 2539 auto tmp2 = cast(Unqual!T)counter; 2540 auto result2 = atomicFetchSub!(MemoryOrder.acq_rel)(tmp2, 1) - 1; 2541 2542 assert(result1 == result2); 2543 2544 } 2545 2546 //return atomicFetchAdd!(MemoryOrder.acq_rel)(counter, -1); 2547 return atomicFetchSub!(MemoryOrder.acq_rel)(counter, 1) - 1; 2548 } 2549 else{ 2550 return counter -= 1; 2551 } 2552 } 2553 2554 unittest{ 2555 import core.atomic; 2556 2557 const int counter = 0; 2558 int tmp1 = counter; 2559 int result1 = (tmp1 -= 1); 2560 assert(result1 == -1); 2561 2562 int tmp2 = counter; 2563 int result2 = atomicFetchSub!(MemoryOrder.acq_rel)(tmp2, 1) - 1; 2564 assert(result2 == -1); 2565 2566 assert(result1 == result2); 2567 } 2568 2569 package template isMoveCtor(T, alias arg){ 2570 enum bool isMoveCtor = is(T == struct) 2571 && !isRef!arg 2572 && is(immutable T == immutable typeof(arg)); 2573 }