1 /** 2 Utility and ancillary artifacts of `stdx.collections`. 3 */ 4 module stdx.collections.common; 5 import std.range: isInputRange; 6 7 auto tail(Collection)(Collection collection) 8 if (isInputRange!Collection) 9 { 10 collection.popFront(); 11 return collection; 12 } 13 14 struct Mutable(T) 15 { 16 import std.experimental.allocator : RCIAllocator, RCISharedAllocator, 17 theAllocator, processAllocator, dispose, stateSize; 18 import std.experimental.allocator.building_blocks.affix_allocator; 19 import std.variant : Algebraic; 20 import core.atomic : atomicOp; 21 import std.algorithm.mutation : move; 22 23 private static struct RefCountedMutable 24 { 25 DualAllocatorU!(RCIAllocator, RCISharedAllocator) _alloc; 26 T _payload; 27 size_t _rc; 28 } 29 30 private void[] _mutableSupport; 31 32 alias LocalAllocT = AffixAllocator!(RCIAllocator, RefCountedMutable); 33 alias SharedAllocT = shared AffixAllocator!(RCISharedAllocator, RefCountedMutable); 34 //alias AllocT = Algebraic!(LocalAllocT, SharedAllocT); 35 //alias AllocT = DualAllocatorU!(LocalAllocT, SharedAllocT); 36 //pragma(msg, AllocT.stringof); 37 //private AllocT _mutableAllocator; 38 39 this(this Q)(T theMutable) 40 { 41 static if (is(Q == immutable) || is(Q == const)) 42 { 43 this(processAllocator, theMutable); 44 } 45 else 46 { 47 this(theAllocator, theMutable); 48 } 49 } 50 51 this(A, this Q)(A alloc, T theMutable) 52 if (((is(Q == immutable) || is(Q == const)) && is(A == RCISharedAllocator)) 53 || (!(is(Q == immutable) || is(Q == const)) && is(A == RCIAllocator))) 54 { 55 static if (is(A == RCIAllocator)) 56 { 57 auto t = LocalAllocT(alloc); 58 } 59 else 60 { 61 auto t = SharedAllocT(alloc); 62 } 63 auto tSupport = (() @trusted => t.allocate(1))(); 64 () @trusted { 65 // TODO: this is opAssign 66 t.prefix(tSupport)._alloc = DualAllocatorU!(RCIAllocator, RCISharedAllocator)(alloc); 67 //move(DualAllocatorU!(RCIAllocator, RCISharedAllocator)(alloc), t.prefix(tSupport)._alloc); 68 t.prefix(tSupport)._payload = theMutable; 69 }(); 70 _mutableSupport = (() @trusted => cast(typeof(_mutableSupport))(tSupport))(); 71 } 72 73 this(this) @trusted 74 { 75 if (_mutableSupport !is null) 76 { 77 addRef(_mutableSupport); 78 } 79 } 80 81 @trusted void addRef(SupportQ, this Q)(SupportQ support) 82 { 83 assert(support !is null); 84 static if (is(Q == immutable) || is(Q == const)) 85 { 86 auto p = cast(shared uint*)(&SharedAllocT.prefix(support)._rc); 87 atomicOp!"+="(*p, 1); 88 } 89 else 90 { 91 ++LocalAllocT.prefix(support)._rc; 92 } 93 } 94 95 ~this() @trusted 96 { 97 if (_mutableSupport !is null) 98 { 99 if (LocalAllocT.prefix(_mutableSupport)._rc == 0) 100 { 101 // Workaround for disabled postblit, though I think move is the 102 // correct behaviour 103 alias AT = typeof(LocalAllocT.prefix(_mutableSupport)._alloc); 104 AT origAlloc; 105 move(LocalAllocT.prefix(_mutableSupport)._alloc, origAlloc); 106 if (!origAlloc.get!(RCISharedAllocator).isNull) 107 { 108 auto disposer = SharedAllocT(origAlloc.get!(RCISharedAllocator)); 109 disposer.dispose(_mutableSupport); 110 } 111 else 112 { 113 auto disposer = LocalAllocT(origAlloc.get!(RCIAllocator)); 114 disposer.dispose(_mutableSupport); 115 } 116 } 117 else 118 { 119 --LocalAllocT.prefix(_mutableSupport)._rc; 120 } 121 } 122 } 123 124 auto ref opAssign()(auto ref typeof(this) rhs) 125 { 126 if (rhs._mutableSupport !is null 127 && _mutableSupport is rhs._mutableSupport) 128 { 129 return this; 130 } 131 if (rhs._mutableSupport !is null) 132 { 133 addRef(rhs._mutableSupport); 134 } 135 __dtor(); 136 _mutableSupport = rhs._mutableSupport; 137 return this; 138 } 139 140 bool isNull(this _)() 141 { 142 return _mutableSupport is null; 143 } 144 145 auto ref get(this Q)() 146 { 147 static if (is(Q == immutable) || is(Q == const)) 148 { 149 alias PayloadType = typeof(allocator.prefix(_mutableSupport)._payload); 150 //pragma(msg, "pld type is " ~ typeof(PayloadType).stringof); 151 return cast(shared PayloadType)(SharedAllocT.prefix(_mutableSupport)._payload); 152 } 153 else 154 { 155 return LocalAllocT.prefix(_mutableSupport)._payload; 156 } 157 } 158 159 void set(T v) 160 { 161 LocalAllocT.prefix(_mutableSupport)._payload = v; 162 } 163 } 164 165 @safe unittest 166 { 167 import std.experimental.allocator : RCIAllocator, RCISharedAllocator, 168 theAllocator, processAllocator, dispose; 169 import std.experimental.allocator.building_blocks.affix_allocator; 170 import std.variant : Algebraic; 171 172 auto a = Mutable!(RCIAllocator)(theAllocator); 173 //auto a = immutable Mutable!(RCISharedAllocator)(processAllocator); 174 //auto a = shared Mutable!(RCIAllocator)(theAllocator, theAllocator); 175 } 176 177 struct DualAllocatorU(LocalAllocT, SharedAllocT) 178 { 179 import std.experimental.allocator : RCIAllocator, RCISharedAllocator; 180 import std.traits : Unqual; 181 182 private union LAllocator 183 { 184 LocalAllocT alloc; 185 } 186 187 private union SAllocator 188 { 189 SharedAllocT alloc; 190 } 191 192 bool _isShared = false; 193 LAllocator _localAlloc; 194 SAllocator _sharedAlloc; 195 196 this(A, this Q)(A alloc) @trusted 197 { 198 static if (is(Q == immutable)) 199 { 200 _isShared = true; 201 _sharedAlloc.alloc = cast(typeof(_sharedAlloc.alloc)) alloc; 202 } 203 else 204 { 205 _localAlloc.alloc = alloc; 206 } 207 } 208 209 @disable this(this); 210 //this(this) 211 //{ 212 //_localAlloc.alloc.__xpostblit(); 213 //_sharedAlloc.alloc.__xpostblit(); 214 //} 215 216 @disable this(shared this) shared; 217 //this(this) shared 218 //{ 219 //assert(_isShared); 220 //_localAlloc.alloc.__xpostblit(); 221 //_sharedAlloc.alloc.__xpostblit(); 222 //} 223 224 ~this() 225 { 226 if(_isShared) 227 { 228 _sharedAlloc.alloc.__xdtor(); 229 } 230 else 231 { 232 _localAlloc.alloc.__xdtor(); 233 } 234 } 235 236 auto ref get(T, this _)() 237 if (is(T == LocalAllocT) || is(T == SharedAllocT)) 238 { 239 static if (is(T == SharedAllocT)) 240 { 241 return _sharedAlloc.alloc; 242 } 243 else 244 { 245 return _localAlloc.alloc; 246 } 247 } 248 249 //Forward to thread local allocator 250 251 @property uint alignment() 252 { 253 return _localAlloc.alloc.alignment; 254 } 255 256 //size_t goodAllocSize(size_t s) 257 //{ 258 //return _localAlloc.alloc.goodAllocSize(s); 259 //} 260 261 void[] allocate(size_t n) 262 { 263 return _localAlloc.alloc.allocate(n); 264 } 265 266 //void[] alignedAllocate(size_t n, uint a) 267 //{ 268 //return _localAlloc.alloc.alignedAllocate(n, a); 269 //} 270 271 //void[] allocateAll() 272 //{ 273 //return _localAlloc.alloc.allocateAll(); 274 //} 275 276 //bool expand(ref void[] b, size_t size) 277 //{ 278 //return _localAlloc.alloc.expand(b, size); 279 //} 280 281 //bool reallocate(ref void[] b, size_t size) 282 //{ 283 //return _localAlloc.alloc.reallocate(b, size); 284 //} 285 286 //bool alignedReallocate(ref void[] b, size_t size, uint alignment) 287 //{ 288 //return _localAlloc.alloc.alignedReallocate(b, size, alignment); 289 //} 290 291 //Ternary owns(void[] b) 292 //{ 293 //return _localAlloc.alloc.owns(b); 294 //} 295 296 //Ternary resolveInternalPointer(const void* p, ref void[] result) 297 //{ 298 //return _localAlloc.alloc.resolveInternalPointer(p, result); 299 //} 300 301 bool deallocate(void[] b) 302 { 303 return _localAlloc.alloc.deallocate(b); 304 } 305 306 //bool deallocateAll() 307 //{ 308 //return _localAlloc.alloc.deallocateAll(); 309 //} 310 311 //Ternary empty() 312 //{ 313 //return _localAlloc.alloc.empty(); 314 //} 315 316 317 // Forward to shared allocator 318 319 @property uint alignment() shared 320 { 321 return _sharedAlloc.alloc.alignment; 322 } 323 324 //size_t goodAllocSize(size_t s) shared 325 //{ 326 //return _sharedAlloc.alloc.goodAllocSize(s); 327 //} 328 329 void[] allocate(size_t n) shared 330 { 331 return _sharedAlloc.alloc.allocate(n); 332 } 333 334 //void[] alignedAllocate(size_t n, uint a) shared 335 //{ 336 //return _sharedAlloc.alloc.alignedAllocate(n, a); 337 //} 338 339 //void[] allocateAll() shared 340 //{ 341 //return _sharedAlloc.alloc.allocateAll(); 342 //} 343 344 //bool expand(ref void[] b, size_t size) shared 345 //{ 346 //return _sharedAlloc.alloc.expand(b, size); 347 //} 348 349 //bool reallocate(ref void[] b, size_t size) shared 350 //{ 351 //return _sharedAlloc.alloc.reallocate(b, size); 352 //} 353 354 //bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared 355 //{ 356 //return _sharedAlloc.alloc.alignedReallocate(b, size, alignment); 357 //} 358 359 //Ternary owns(void[] b) shared 360 //{ 361 //return _sharedAlloc.alloc.owns(b); 362 //} 363 364 //Ternary resolveInternalPointer(const void* p, ref void[] result) shared 365 //{ 366 //return _sharedAlloc.alloc.resolveInternalPointer(p, result); 367 //} 368 369 bool deallocate(void[] b) shared 370 { 371 return _sharedAlloc.alloc.deallocate(b); 372 } 373 374 //bool deallocateAll() shared 375 //{ 376 //return _sharedAlloc.alloc.deallocateAll(); 377 //} 378 379 //Ternary empty() shared 380 //{ 381 //return _sharedAlloc.alloc.empty(); 382 //} 383 } 384 385 @safe unittest 386 { 387 import std.experimental.allocator : RCIAllocator, RCISharedAllocator, 388 theAllocator, processAllocator, dispose; 389 import std.experimental.allocator.building_blocks.affix_allocator; 390 391 struct RefCountedMutable(T) 392 { 393 DualAllocator!(RCIAllocator, RCISharedAllocator) _alloc; 394 T _payload; 395 size_t _rc; 396 } 397 398 //alias SharedAllocT = shared AffixAllocator!(RCISharedAllocator, RefCountedMutable!RCISharedAllocator); 399 //pragma(msg, is(RCISharedAllocator == shared)); 400 //SharedAllocT a = SharedAllocT(processAllocator); 401 //() @trusted shared { 402 //auto buf = a.allocate(10); 403 //assert(buf.length == 10); 404 //}(); 405 406 alias T = RCIAllocator; 407 alias LocalAllocT = AffixAllocator!(RCIAllocator, RefCountedMutable!T); 408 alias TT = RCISharedAllocator; 409 alias SharedAllocT = shared AffixAllocator!(RCISharedAllocator, RefCountedMutable!TT); 410 //alias AllocT = Algebraic!(LocalAllocT, SharedAllocT); 411 //alias AllocT = DualAllocator!(LocalAllocT, SharedAllocT); 412 alias AllocT = shared DualAllocatorU!(LocalAllocT, SharedAllocT); 413 AllocT _mutableAllocator; 414 415 AffixAllocator!(AllocT, RefCountedMutable!T) a; 416 //AffixAllocator!(AllocT, int) a; 417 } 418 419 @safe unittest 420 { 421 import std.experimental.allocator : RCIAllocator, RCISharedAllocator, 422 theAllocator, processAllocator, dispose; 423 import std.exception : enforce; 424 425 auto a = DualAllocatorU!(RCIAllocator, RCISharedAllocator)(theAllocator); 426 assert(!a._isShared); 427 assert(a.get!(RCISharedAllocator).isNull); 428 429 auto b = immutable DualAllocatorU!(RCIAllocator, RCISharedAllocator)(processAllocator); 430 assert(b._isShared); 431 assert(!b.get!(RCISharedAllocator).isNull); 432 assert(b.get!(RCIAllocator).isNull); 433 434 DualAllocatorU!(RCIAllocator, RCISharedAllocator) c; 435 assert(c.get!(RCIAllocator).isNull); 436 assert(c.get!(RCISharedAllocator).isNull); 437 } 438 439 struct DualAllocator(LocalAllocT, SharedAllocT) 440 { 441 import std.experimental.allocator : RCIAllocator, RCISharedAllocator; 442 import std.typecons : Ternary; 443 import std.traits : Unqual; 444 445 private LocalAllocT localAlloc; 446 private SharedAllocT sharedAlloc; 447 448 this(A, this Q)(A alloc) @trusted 449 { 450 static if (is(Q == immutable)) 451 { 452 sharedAlloc = cast(typeof(sharedAlloc)) alloc; 453 //sharedAlloc = alloc; 454 } 455 else 456 { 457 localAlloc = alloc; 458 } 459 } 460 461 this(this) inout {} 462 463 auto ref opAssign(A)(A rhs) 464 if (is(A == LocalAllocT) || is(A == SharedAllocT)) 465 { 466 static if (is(A == shared)) 467 { 468 pragma(msg, "JAAAAA"); 469 sharedAlloc = rhs; 470 } 471 else 472 { 473 localAlloc = rhs; 474 } 475 return this; 476 } 477 478 // Only works for stateful allocators 479 inout(T)* peek(T)() inout @trusted 480 if (is(T == LocalAllocT) || is(T == SharedAllocT)) 481 { 482 static if (is(T == SharedAllocT)) 483 { 484 alias ST = typeof(sharedAlloc); 485 return sharedAlloc == ST.init ? null : &sharedAlloc; 486 } 487 else 488 { 489 alias LT = typeof(localAlloc); 490 return localAlloc == LT.init ? null : &localAlloc; 491 } 492 } 493 494 auto ref get(T, this _)() 495 if (is(T == LocalAllocT) || is(T == SharedAllocT)) 496 { 497 static if (is(T == SharedAllocT)) 498 { 499 return sharedAlloc; 500 } 501 else 502 { 503 return localAlloc; 504 } 505 } 506 507 //Forward to thread local allocator 508 509 //@property uint alignment() 510 //{ 511 //return localAlloc.alignment(); 512 //} 513 514 //size_t goodAllocSize(size_t s) 515 //{ 516 //return localAlloc.goodAllocSize(s); 517 //} 518 519 void[] allocate(size_t n) 520 { 521 return localAlloc.allocate(n); 522 } 523 524 //void[] alignedAllocate(size_t n, uint a) 525 //{ 526 //return localAlloc.alignedAllocate(n, a); 527 //} 528 529 //void[] allocateAll() 530 //{ 531 //return localAlloc.allocateAll(); 532 //} 533 534 //bool expand(ref void[] b, size_t size) 535 //{ 536 //return localAlloc.expand(b, size); 537 //} 538 539 //bool reallocate(ref void[] b, size_t size) 540 //{ 541 //return localAlloc.reallocate(b, size); 542 //} 543 544 //bool alignedReallocate(ref void[] b, size_t size, uint alignment) 545 //{ 546 //return localAlloc.alignedReallocate(b, size, alignment); 547 //} 548 549 //Ternary owns(void[] b) 550 //{ 551 //return localAlloc.owns(b); 552 //} 553 554 //Ternary resolveInternalPointer(const void* p, ref void[] result) 555 //{ 556 //return localAlloc.resolveInternalPointer(p, result); 557 //} 558 559 bool deallocate(void[] b) 560 { 561 return localAlloc.deallocate(b); 562 } 563 564 //bool deallocateAll() 565 //{ 566 //return localAlloc.deallocateAll(); 567 //} 568 569 //Ternary empty() 570 //{ 571 //return localAlloc.empty(); 572 //} 573 574 575 // Forward to shared allocator 576 577 //@property uint alignment() shared 578 //{ 579 //return sharedAlloc.alignment(); 580 //} 581 582 //size_t goodAllocSize(size_t s) shared 583 //{ 584 //return sharedAlloc.goodAllocSize(s); 585 //} 586 587 void[] allocate(size_t n) shared 588 { 589 return sharedAlloc.allocate(n); 590 } 591 592 //void[] alignedAllocate(size_t n, uint a) shared 593 //{ 594 //return sharedAlloc.alignedAllocate(n, a); 595 //} 596 597 //void[] allocateAll() shared 598 //{ 599 //return sharedAlloc.allocateAll(); 600 //} 601 602 //bool expand(ref void[] b, size_t size) shared 603 //{ 604 //return sharedAlloc.expand(b, size); 605 //} 606 607 //bool reallocate(ref void[] b, size_t size) shared 608 //{ 609 //return sharedAlloc.reallocate(b, size); 610 //} 611 612 //bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared 613 //{ 614 //return sharedAlloc.alignedReallocate(b, size, alignment); 615 //} 616 617 //Ternary owns(void[] b) shared 618 //{ 619 //return sharedAlloc.owns(b); 620 //} 621 622 //Ternary resolveInternalPointer(const void* p, ref void[] result) shared 623 //{ 624 //return sharedAlloc.resolveInternalPointer(p, result); 625 //} 626 627 bool deallocate(void[] b) shared 628 { 629 return sharedAlloc.deallocate(b); 630 } 631 632 //bool deallocateAll() shared 633 //{ 634 //return sharedAlloc.deallocateAll(); 635 //} 636 637 //Ternary empty() shared 638 //{ 639 //return sharedAlloc.empty(); 640 //} 641 } 642 643 @safe unittest 644 { 645 import std.experimental.allocator : RCIAllocator, RCISharedAllocator, 646 theAllocator, processAllocator, dispose; 647 import std.exception : enforce; 648 649 auto a = DualAllocator!(RCIAllocator, RCISharedAllocator)(theAllocator); 650 assert(a.peek!(RCISharedAllocator) is null); 651 assert(a.peek!(RCIAllocator) !is null); 652 assert(a.get!(RCISharedAllocator).isNull); 653 assert(!a.get!(RCIAllocator).isNull); 654 655 auto b = immutable DualAllocatorU!(RCIAllocator, RCISharedAllocator)(processAllocator); 656 assert(b._isShared); 657 assert(!b.get!(RCISharedAllocator).isNull); 658 assert(b.get!(RCIAllocator).isNull); 659 660 DualAllocatorU!(RCIAllocator, RCISharedAllocator) c; 661 assert(c.get!(RCIAllocator).isNull); 662 assert(c.get!(RCISharedAllocator).isNull); 663 } 664 665 struct MutableAlloc(Alloc) 666 { 667 import std.experimental.allocator : RCIAllocator, RCISharedAllocator, 668 theAllocator, processAllocator, dispose, stateSize; 669 import std.experimental.allocator.building_blocks.affix_allocator; 670 import std.variant : Algebraic; 671 import core.atomic : atomicOp; 672 import std.algorithm.mutation : move; 673 674 private static struct RefCountedMutable 675 { 676 //DualAllocatorU!(RCIAllocator, RCISharedAllocator) _alloc; 677 Alloc _alloc; 678 //T _payload; 679 size_t _rc; 680 } 681 682 private void[] _mutableSupport; 683 684 static if (is(Alloc == shared)) { 685 alias LocalAllocT = shared AffixAllocator!(Alloc, RefCountedMutable); 686 } else { 687 alias LocalAllocT = AffixAllocator!(Alloc, RefCountedMutable); 688 } 689 //alias LocalAllocT = AffixAllocator!(Alloc, RefCountedMutable); 690 //alias SharedAllocT = shared AffixAllocator!(Alloc, RefCountedMutable); 691 692 this(this Q)(Alloc alloc) 693 { 694 auto t = LocalAllocT(alloc); 695 auto tSupport = (() @trusted => t.allocate(1))(); 696 () @trusted { 697 // TODO: this is opAssign 698 t.prefix(tSupport)._alloc = alloc; 699 }(); 700 _mutableSupport = (() @trusted => cast(typeof(_mutableSupport))(tSupport))(); 701 } 702 703 this(this) @trusted 704 { 705 if (_mutableSupport !is null) 706 { 707 addRef(_mutableSupport); 708 } 709 } 710 711 @trusted void addRef(SupportQ, this Q)(SupportQ support) 712 { 713 assert(support !is null); 714 static if (is(Q == immutable) || is(Q == const)) 715 { 716 auto p = cast(shared uint*)(&LocalAllocT.prefix(support)._rc); 717 atomicOp!"+="(*p, 1); 718 } 719 else 720 { 721 ++LocalAllocT.prefix(support)._rc; 722 } 723 } 724 725 ~this() @trusted 726 { 727 if (_mutableSupport !is null) 728 { 729 if (LocalAllocT.prefix(_mutableSupport)._rc == 0) 730 { 731 // Workaround for disabled postblit, though I think move is the 732 // correct behaviour 733 alias AT = typeof(LocalAllocT.prefix(_mutableSupport)._alloc); 734 //AT origAlloc; 735 //move(LocalAllocT.prefix(_mutableSupport)._alloc, origAlloc); 736 pragma(msg, is(AT == shared)); 737 738 AT origAlloc = LocalAllocT.prefix(_mutableSupport)._alloc; 739 //AT origAlloc; 740 //move(LocalAllocT.prefix(_mutableSupport)._alloc, origAlloc); 741 auto disposer = LocalAllocT(origAlloc); 742 pragma(msg, typeof(disposer).stringof); 743 pragma(msg, typeof(_mutableSupport).stringof); 744 disposer.deallocate(_mutableSupport); 745 } 746 else 747 { 748 --LocalAllocT.prefix(_mutableSupport)._rc; 749 } 750 } 751 } 752 753 auto ref opAssign()(auto ref typeof(this) rhs) 754 { 755 if (rhs._mutableSupport !is null 756 && _mutableSupport is rhs._mutableSupport) 757 { 758 return this; 759 } 760 if (rhs._mutableSupport !is null) 761 { 762 addRef(rhs._mutableSupport); 763 } 764 __dtor(); 765 _mutableSupport = rhs._mutableSupport; 766 return this; 767 } 768 769 bool isNull(this _)() 770 { 771 return _mutableSupport is null; 772 } 773 774 auto ref get(this Q)() 775 { 776 static if (is(Q == immutable) || is(Q == const)) 777 { 778 alias PayloadType = typeof(allocator.prefix(_mutableSupport)._alloc); 779 //pragma(msg, "pld type is " ~ typeof(PayloadType).stringof); 780 return cast(shared PayloadType)(LocalAllocT.prefix(_mutableSupport)._alloc); 781 } 782 else 783 { 784 return LocalAllocT.prefix(_mutableSupport)._alloc; 785 } 786 } 787 788 void set(Alloc v) 789 { 790 LocalAllocT.prefix(_mutableSupport)._alloc = v; 791 } 792 } 793 794 @safe unittest 795 { 796 import std.experimental.allocator : RCIAllocator, RCISharedAllocator, 797 theAllocator, processAllocator, dispose; 798 import std.experimental.allocator.building_blocks.affix_allocator; 799 import std.variant : Algebraic; 800 801 auto a = MutableAlloc!(RCIAllocator)(theAllocator); 802 auto a2 = immutable MutableAlloc!(RCISharedAllocator)(processAllocator); 803 auto a3 = MutableAlloc!(DualAllocatorU!(RCIAllocator, RCISharedAllocator)) 804 (theAllocator); 805 } 806