1 module carbon.memory; 2 3 import carbon.functional; 4 5 import core.exception; 6 import core.stdc.stdlib; 7 import core.memory; 8 9 import std.algorithm; 10 import std.conv; 11 import std.stdio; 12 import std.exception; 13 import std.range; 14 import std.traits; 15 import std.typecons; 16 17 18 void fastPODCopy(R1, R2)(R1 src, R2 dst) 19 if(isInputRange!R1 && isInputRange!R2 && hasAssignableElements!R2) 20 { 21 alias E1 = ElementType!R1; 22 alias E2 = ElementType!R2; 23 24 static if(isArray!R1 && is(Unqual!R1 == Unqual!R2)) 25 { 26 dst[] = src[]; 27 } 28 else static if(isArray!R1 && isArray!R2 && E1.sizeof == E2.sizeof) 29 { 30 auto ub1 = cast(ubyte[])src; 31 auto ub2 = cast(ubyte[])dst; 32 ub2[] = ub1[]; 33 } 34 else 35 { 36 import std.algorithm : copy; 37 copy(src, dst); 38 } 39 } 40 41 42 void callAllPostblit(T)(ref T obj) 43 if(is(T == struct)) 44 { 45 foreach(ref e; obj.tupleof) 46 callAllPostblit(e); 47 48 static if(is(typeof(obj.__postblit()))) 49 obj.__postblit(); 50 } 51 52 53 void callAllPostblit(T)(ref T obj) 54 if(!is(T == struct)) 55 {} 56 57 58 void callAllDtor(T)(ref T obj) 59 if(is(T == struct)) 60 { 61 static if(is(typeof(obj.__dtor()))) 62 obj.__dtor(); 63 64 foreach(ref e; obj.tupleof) 65 callAllDtor(e); 66 } 67 68 69 void callAllDtor(T)(ref T obj) 70 if(!is(T == struct)) 71 {} 72 73 74 private 75 E[] uninitializedCHeapArray(E)(size_t n) @trusted nothrow 76 { 77 if(n){ 78 auto p = cast(E*)core.stdc.stdlib.malloc(n * E.sizeof); 79 if(p is null) 80 onOutOfMemoryError(); 81 82 static if(hasIndirections!E) 83 { 84 core.stdc..string.memset(p, 0, n * E.sizeof); 85 GC.addRange(p, n * E.sizeof); 86 } 87 88 return p[0 .. n]; 89 }else 90 return null; 91 } 92 93 94 private 95 void destroyCHeapArray(E)(ref E[] arr) nothrow @nogc 96 { 97 static if(hasElaborateDestructor!E) 98 foreach(ref e; arr) 99 callAllDtor(e); 100 101 static if(hasIndirections!E) 102 GC.removeRange(arr.ptr); 103 104 assumeTrusted!(core.stdc.stdlib.free)(arr.ptr); 105 arr = null; 106 } 107 108 109 private 110 void reallocCHeapArray(E)(ref E[] arr, size_t n) 111 { 112 if(n <= arr.length) // n == 0 なら常に return される 113 return; 114 115 immutable oldLen = arr.length; 116 117 static if(hasIndirections!T) 118 { 119 auto newarr = uninitializedCHeapArray!E(n); 120 assumeTrusted!(core.stdc..string.memcpy)(newarr.ptr, arr.ptr, E.sizeof * oldLen); 121 assumeTrusted!(core.stdc..string.memset)(assumeTrusted!"a+b"(newarr.ptr + oldLen), 0, (n - oldLen) * E.sizeof); 122 GC.addRange(newarr.ptr, n * E.sizeof); 123 GC.removeRange(arr.ptr); 124 assumeTrusted!(core.stdc.stdlib.free)(arr.ptr); 125 126 arr = newarr; 127 } 128 else 129 { 130 void* p = assumeTrusted!(core.stdc.stdlib.realloc)(arr.ptr, n * E.sizeof); 131 if(p is null) 132 onOutOfMemoryError(); 133 134 arr = assumeTrusted!((a, b) => (cast(E*)a)[0 .. b])(p, n); 135 } 136 } 137 138 139 auto onGCMemory(T)(T obj) 140 { 141 static struct Result { 142 alias _val this; 143 ref inout(T) _val() inout pure nothrow @safe @property { return *_v; } 144 T* _v; 145 } 146 147 Result res = Result(new T); 148 *res._v = obj; 149 return res; 150 } 151 152 unittest 153 { 154 auto r = [1, 2, 3, 4, 5].map!"a+2".onGCMemory; 155 156 static void popN(R)(R r, size_t n) 157 { 158 foreach(i; 0 .. n) r.popFront(); 159 } 160 161 assert(equal(r.save, [3, 4, 5, 6, 7])); 162 popN(r, 2); 163 assert(equal(r.save, [5, 6, 7])); 164 } 165 166 167 168 /** 169 */ 170 struct UninitializedTemporaryBuffer(T) 171 { 172 import core.stdc.stdlib; 173 174 @disable 175 this(this); 176 177 178 this(size_t n) @trusted 179 { 180 slice = uninitializedCHeapArray!T(n); 181 } 182 183 184 ~this() @trusted 185 { 186 destroyCHeapArray(slice); 187 } 188 189 190 T[] slice; 191 alias slice this; 192 } 193 194 195 /** 196 Defines a reference-counted object containing a $(D T) value as 197 payload. $(D RefCountedNoGC) keeps track of all references of an object, 198 and when the reference count goes down to zero, frees the underlying 199 store. $(D RefCountedNoGC) uses $(D malloc) and $(D free) for operation. 200 201 $(D RefCountedNoGC) is unsafe and should be used with care. No references 202 to the payload should be escaped outside the $(D RefCountedNoGC) object. 203 204 The $(D autoInit) option makes the object ensure the store is 205 automatically initialized. Leaving $(D autoInit == 206 RefCountedAutoInitialize.yes) (the default option) is convenient but 207 has the cost of a test whenever the payload is accessed. If $(D 208 autoInit == RefCountedAutoInitialize.no), user code must call either 209 $(D refCountedStore.isInitialized) or $(D refCountedStore.ensureInitialized) 210 before attempting to access the payload. Not doing so results in null 211 pointer dereference. 212 213 Example: 214 ---- 215 // A pair of an $(D int) and a $(D size_t) - the latter being the 216 // reference count - will be dynamically allocated 217 auto rc1 = RefCountedNoGC!int(5); 218 assert(rc1 == 5); 219 // No more allocation, add just one extra reference count 220 auto rc2 = rc1; 221 // Reference semantics 222 rc2 = 42; 223 assert(rc1 == 42); 224 // the pair will be freed when rc1 and rc2 go out of scope 225 ---- 226 */ 227 struct RefCountedNoGC(T, RefCountedAutoInitialize autoInit = 228 RefCountedAutoInitialize.yes) 229 if (!is(T == class)) 230 { 231 /// $(D RefCountedNoGC) storage implementation. 232 struct RefCountedStore 233 { 234 private struct Impl 235 { 236 T _payload; 237 size_t _count; 238 } 239 240 private Impl* _store; 241 242 private void initialize(A...)(auto ref A args) 243 { 244 import core.memory : GC; 245 import core.stdc.stdlib : malloc; 246 import std.conv : emplace; 247 import std.exception : enforce; 248 249 _store = cast(Impl*)malloc(Impl.sizeof); 250 if(!_store) 251 onOutOfMemoryError(); 252 253 static if (hasIndirections!T) 254 GC.addRange(&_store._payload, T.sizeof); 255 emplace(&_store._payload, args); 256 _store._count = 1; 257 } 258 259 /** 260 Returns $(D true) if and only if the underlying store has been 261 allocated and initialized. 262 */ 263 @property nothrow @safe @nogc 264 bool isInitialized() const 265 { 266 return _store !is null; 267 } 268 269 /** 270 Returns underlying reference count if it is allocated and initialized 271 (a positive integer), and $(D 0) otherwise. 272 */ 273 @property nothrow @safe @nogc 274 size_t refCount() const 275 { 276 return isInitialized ? _store._count : 0; 277 } 278 279 /** 280 Makes sure the payload was properly initialized. Such a 281 call is typically inserted before using the payload. 282 */ 283 void ensureInitialized() 284 { 285 if (!isInitialized) initialize(); 286 } 287 288 } 289 RefCountedStore _refCounted; 290 291 /// Returns storage implementation struct. 292 @property nothrow @safe 293 ref inout(RefCountedStore) refCountedStore() inout 294 { 295 return _refCounted; 296 } 297 298 /** 299 Constructor that initializes the payload. 300 301 Postcondition: $(D refCountedStore.isInitialized) 302 */ 303 this(A...)(auto ref A args) if (A.length > 0) 304 { 305 _refCounted.initialize(args); 306 } 307 308 /** 309 Constructor that tracks the reference count appropriately. If $(D 310 !refCountedStore.isInitialized), does nothing. 311 */ 312 this(this) 313 { 314 if (!_refCounted.isInitialized) return; 315 ++_refCounted._store._count; 316 } 317 318 /** 319 Destructor that tracks the reference count appropriately. If $(D 320 !refCountedStore.isInitialized), does nothing. When the reference count goes 321 down to zero, calls $(D destroy) agaist the payload and calls $(D free) 322 to deallocate the corresponding resource. 323 */ 324 ~this() 325 { 326 if (!_refCounted.isInitialized) return; 327 assert(_refCounted._store._count > 0); 328 if (--_refCounted._store._count) 329 return; 330 // Done, deallocate 331 static if(hasElaborateDestructor!T) 332 callAllDtor(_refCounted._store._payload); 333 334 static if (hasIndirections!T) 335 { 336 import core.memory : GC; 337 GC.removeRange(&_refCounted._store._payload); 338 } 339 import core.stdc.stdlib : free; 340 free(_refCounted._store); 341 _refCounted._store = null; 342 } 343 344 /** 345 Assignment operators 346 */ 347 void opAssign(typeof(this) rhs) 348 { 349 import std.algorithm : swap; 350 351 swap(_refCounted._store, rhs._refCounted._store); 352 } 353 354 /// Ditto 355 void opAssign(T rhs) 356 { 357 import std.algorithm : move; 358 359 static if (autoInit == RefCountedAutoInitialize.yes) 360 { 361 _refCounted.ensureInitialized(); 362 } 363 else 364 { 365 assert(_refCounted.isInitialized); 366 } 367 move(rhs, _refCounted._store._payload); 368 } 369 370 //version to have a single properly ddoc'ed function (w/ correct sig) 371 version(StdDdoc) 372 { 373 /** 374 Returns a reference to the payload. If (autoInit == 375 RefCountedAutoInitialize.yes), calls $(D 376 refCountedStore.ensureInitialized). Otherwise, just issues $(D 377 assert(refCountedStore.isInitialized)). Used with $(D alias 378 refCountedPayload this;), so callers can just use the $(D RefCountedNoGC) 379 object as a $(D T). 380 381 $(BLUE The first overload exists only if $(D autoInit == RefCountedAutoInitialize.yes).) 382 So if $(D autoInit == RefCountedAutoInitialize.no) 383 or called for a constant or immutable object, then 384 $(D refCountedPayload) will also be qualified as safe and nothrow 385 (but will still assert if not initialized). 386 */ 387 @property 388 ref T refCountedPayload(); 389 390 /// ditto 391 @property nothrow @safe 392 ref inout(T) refCountedPayload() inout; 393 } 394 else 395 { 396 static if (autoInit == RefCountedAutoInitialize.yes) 397 { 398 //Can't use inout here because of potential mutation 399 @property 400 ref T refCountedPayload() 401 { 402 _refCounted.ensureInitialized(); 403 return _refCounted._store._payload; 404 } 405 } 406 407 @property nothrow @safe 408 ref inout(T) refCountedPayload() inout 409 { 410 assert(_refCounted.isInitialized, "Attempted to access an uninitialized payload."); 411 return _refCounted._store._payload; 412 } 413 } 414 415 /** 416 Returns a reference to the payload. If (autoInit == 417 RefCountedAutoInitialize.yes), calls $(D 418 refCountedStore.ensureInitialized). Otherwise, just issues $(D 419 assert(refCountedStore.isInitialized)). 420 */ 421 alias refCountedPayload this; 422 } 423 424 425 auto toRefCounted(T)(T obj) 426 { 427 return RefCountedNoGC!T(obj); 428 } 429 430 431 @nogc unittest 432 { 433 auto r = only(1, 2, 3, 4, 5).map!"a+2".toRefCounted; 434 435 static void popN(R)(R r, size_t n) 436 { 437 foreach(i; 0 .. n) r.popFront(); 438 } 439 440 assert(equal(r.save, only(3, 4, 5, 6, 7))); 441 popN(r, 2); 442 assert(equal(r.save, only(5, 6, 7))); 443 } 444 445 446 __EOF__ 447 448 449 struct UniqueArray(E, bool bMini = false) 450 { 451 private static 452 size_t allocateSize(size_t n) pure nothrow @safe @nogc 453 { 454 static if(bMini) 455 return n; 456 else 457 return 1 << (core.bitop.bsr(n) + 1); 458 } 459 460 461 /** 462 */ 463 this(size_t n) nothrow @trusted 464 { 465 immutable elemN = allocateSize(n); 466 467 auto arr = uninitializedCHeapArray!(Unqual!(E))(elemN); 468 foreach(ref e; arr) 469 .emplace(&e); 470 471 _array = cast(E[])arr; 472 _s = 0; 473 _e = n; 474 } 475 476 477 static if(is(typeof((Unqual!E v){ E x = v; }))) 478 { 479 this(bool b)(UniqueArray!(Unqual!E, b) unique) @trusted 480 { 481 _array = cast(E[])unique._array; 482 _s = unique._s; 483 _e = unique._e; 484 485 unique._array = null; 486 } 487 488 489 void opAssign(bool b)(UniqueArray!(const(E), b) unique) @trusted 490 { 491 //.destroy(this); 492 callAllDtor(this); 493 494 _array = cast(E[])unique._array; 495 _s = unique._s; 496 _e = unique._e; 497 498 unique._array = null; 499 } 500 } 501 502 503 static if(is(typeof((const E v){ E x = v; }))) 504 { 505 this(bool b)(UniqueArray!(const(E), b) unique) @trusted 506 { 507 _array = cast(E[])unique._array; 508 _s = unique._s; 509 _e = unique._e; 510 511 unique._array = null; 512 } 513 514 515 void opAssign(bool b)(UniqueArray!(const(E), b) unique) @trusted 516 { 517 //.destroy(this); 518 callAllDtor(this); 519 520 _array = cast(E[])unique._array; 521 _s = unique._s; 522 _e = unique._e; 523 524 unique._array = null; 525 } 526 } 527 528 529 static if(is(typeof((immutable E v){ E x = v; }))) 530 { 531 this(bool b)(UniqueArray!(immutable(E), b) unique) @trusted 532 { 533 _array = cast(E[])unique._array; 534 _s = unique._s; 535 _e = unique._e; 536 537 unique._array = null; 538 } 539 540 541 void opAssign(bool b)(UniqueArray!(immutable(E), b) unique) @trusted 542 { 543 //.destroy(this); 544 callAllDtor(this); 545 546 _array = cast(E[])unique._array; 547 _s = unique._s; 548 _e = unique._e; 549 550 unique._array = null; 551 } 552 } 553 554 555 ~this() 556 { 557 if(_array !is null){ 558 static if(hasElaborateDestructor!E) 559 foreach(ref e; _array) 560 callAllDtor(e); 561 //.destroy(e); 562 563 static if(hasIndirections!E) 564 GC.removeRange(_array.ptr); 565 566 core.stdc.stdlib.free(cast(void*)_array.ptr); 567 _array = null; 568 } 569 } 570 571 // can copy 572 static if(is(typeof((E v){ E x = v; }))) 573 { 574 this(this) 575 { 576 immutable elemN = (_s == 0 && _e == _array.length) ? _array.length : allocateSize(this.length); 577 578 auto newarr = uninitializedCHeapArray!(Unqual!E)(elemN); 579 assumeTrusted!((p){ 580 foreach(i, ref e; this.view) 581 emplace(p + i, e); 582 })(newarr.ptr); 583 584 _array = cast(E[])newarr; 585 _e -= _s; 586 _s = 0; 587 } 588 589 590 void opAssign(typeof(this) unique) 591 { 592 swap(this, unique); 593 } 594 } 595 else{ 596 @disable this(this); 597 @disable void opAssign(typeof(this) unique); 598 } 599 600 601 size_t length() const pure nothrow @safe @nogc @property 602 { 603 return _e - _s; 604 } 605 606 607 size_t capacity() const pure nothrow @safe @nogc @property 608 { 609 return _array.length - _e; 610 } 611 612 613 size_t reserve(size_t n) @nogc 614 { 615 if(_array is null){ 616 auto newArr = typeof(this)(n); 617 this._array = newArr._array; 618 this._s = newArr._s; 619 this._e = newArr._e; 620 newArr._array = null; 621 622 return this.capacity; 623 } 624 else if(n <= _array.length - _s) 625 return this.capacity; 626 else{ 627 immutable newElemN = allocateSize(n); 628 immutable newAllocN = newElemN * E.sizeof; 629 630 auto p = cast(Unqual!E*)core.stdc.stdlib.malloc(newAllocN); 631 if(!p) 632 onOutOfMemoryError(); 633 634 core.stdc..string.memcpy(p, _array.ptr + _s, this.length * E.sizeof); 635 636 static if(hasIndirections!E) 637 { 638 core.stdc..string.memset(p + this.length, 0, (newElemN - this.length) * E.sizeof); 639 GC.addRange(p, newAllocN); 640 GC.removeRange(_array.ptr); 641 } 642 643 core.stdc.stdlib.free(cast(Unqual!E*)_array.ptr); 644 _array = (cast(E*)p)[0 .. newElemN]; 645 _e -= _s; 646 _s = 0; 647 648 return this.capacity; 649 } 650 } 651 652 653 E[] view() pure nothrow @safe @nogc 654 { 655 return _array; 656 } 657 658 659 private: 660 E[] _array ; 661 size_t _s, _e; 662 } 663 664 665 unittest{ 666 UniqueArray!int arr; 667 668 auto rc = RefCountedNoGC!(UniqueArray!int)(3); 669 } 670 671 672 struct RefCountedArrayImpl(E) 673 { 674 this(size_t n) nothrow @trusted @nogc 675 { 676 _refCnt = uninitializedCHeapArray!size_t(1).ptr; 677 *_refCnt = 1; 678 _array = newCHeapArray!E(n); 679 } 680 681 682 bool isInitialized() const pure nothrow @safe @nogc @property 683 { 684 return _refCnt !is null; 685 } 686 687 688 size_t refCount() const pure nothrow @safe @nogc @property 689 { 690 return isInitialized ? *_refCnt : 0; 691 } 692 693 694 this(this) pure nothrow @safe @nogc 695 { 696 if(isInitialized) 697 ++*_refCnt; 698 } 699 700 701 static if(is(typeof(() @safe { E e; }))) 702 { 703 ~this() @trusted 704 { dtorImpl(); } 705 } 706 else 707 { 708 ~this() 709 { dtorImpl(); } 710 } 711 712 713 void dtorImpl() 714 { 715 if(isInitialized){ 716 --*_refCnt; 717 if(refCount == 0){ 718 size_t[] dummy = _refCnt[0 .. 1]; 719 720 dummy.destroyCHeapArray(); // nothrow 721 _refCnt = null; 722 723 _array.destroyCHeapArray(); // maybe throw 724 } 725 } 726 } 727 728 729 void reallocate(size_t n) 730 { 731 _array.reallocCHeapArray(n); 732 } 733 734 735 private: 736 size_t* _refCnt; 737 E[] _array; 738 } 739 740 741 /** 742 Cヒープ上に配列を作成し、その配列を参照カウント方式で管理します。 743 配列への追加は、スライスと同様にCopy On Write方式で管理されます。 744 つまり、複数のオブジェクトが一つの配列を参照している場合に配列への要素の追加を行うと、必ず配列は新たに確保されます。 745 */ 746 struct RefCountedArray(E) 747 { 748 alias Impl = RefCountedArrayImpl; 749 750 751 /** 752 大きさを指定して配列を作成します。 753 */ 754 this(size_t n) 755 { 756 _impl = Impl!E(allocateSize!E(n)); 757 _s = 0; 758 _e = n; 759 } 760 761 762 /** 763 保持している参照を解除します。 764 よって、参照カウントは一つ減ります。 765 */ 766 void release() 767 { 768 _impl = Impl!E.init; 769 _s = 0; 770 _e = 0; 771 } 772 773 774 /** 775 */ 776 void clear() 777 { 778 if(_impl.refCount == 1){ 779 _s = 0; 780 _e = 0; 781 }else 782 release(); 783 } 784 785 786 /** 787 */ 788 size_t capacity() const pure nothrow @safe @nogc @property 789 { 790 if(_impl.refCount == 1) 791 return _impl._array.length - _s; 792 else 793 return 0; 794 } 795 796 797 /** 798 */ 799 size_t reserve(size_t n) 800 { 801 if(n <= this.length) 802 goto Lreturn; 803 804 if(!_impl.isInitialized) 805 this = typeof(this)(n); 806 else if(_impl.refCount == 1 && _impl._array.length < n + _s){ 807 immutable bool doesMoveToFront = (_s * 2 >= allocateSize!E(n) - _impl._array.length); 808 809 if(!doesMoveToFront || _impl._array.length < n) 810 _impl.reallocate(allocateSize!E(n)); 811 812 if(doesMoveToFront){ 813 foreach(i; 0 .. this.length){ 814 static if(is(E == struct) && !__traits(isPOD, E)) 815 _impl._array[i] = std.algorithm.move(_impl._array[_s + i]); 816 else 817 _impl._array[i] = _impl._array[_s + i]; 818 } 819 820 _e -= _s; 821 _s = 0; 822 } 823 } 824 else if(_impl.refCount != 1){ 825 auto newarr = typeof(this)(n); 826 newarr._impl._array[0 .. this.length] = this._impl._array[_s .. _e]; 827 this = newarr; 828 } 829 830 Lreturn: 831 return this.capacity; 832 } 833 834 835 /** 836 */ 837 bool isNull() const pure nothrow @safe @nogc @property 838 { 839 return !_impl.isInitialized; 840 } 841 842 843 /** 844 代入演算子です。 845 このオブジェクトに&(D null)を代入すると、$(D release)を呼び出したことと等価になります。 846 */ 847 void opAssign(typeof(null)) 848 { 849 release(); 850 } 851 852 853 /// array, range primitives 854 ref inout(E) front() inout pure nothrow @safe @nogc @property 855 { 856 return _impl._array[_s]; 857 } 858 859 860 /// ditto 861 ref inout(E) back() inout pure nothrow @safe @nogc @property 862 { 863 return _impl._array[_e-1]; 864 } 865 866 867 /// ditto 868 bool empty() const pure nothrow @safe @nogc @property 869 { 870 return !_impl.isInitialized || _s == _e; 871 } 872 873 874 /// ditto 875 void popFront() 876 { 877 ++_s; 878 879 if(this.empty) 880 this.clear(); 881 } 882 883 884 /// ditto 885 void popBack() 886 { 887 --_e; 888 889 if(this.empty) 890 this.clear(); 891 } 892 893 894 /// ditto 895 auto save() @property 896 { 897 return this; 898 } 899 900 901 /// ditto 902 ref inout(E) opIndex(size_t i) inout pure nothrow @safe @nogc 903 in{ 904 assert(i < this.length); 905 } 906 body{ 907 return _impl._array[_s + i]; 908 } 909 910 911 /// ditto 912 auto opSlice() 913 { 914 return this; 915 } 916 917 918 /// ditto 919 size_t length() const pure nothrow @safe @nogc @property 920 { 921 return _e - _s; 922 } 923 924 925 /// ditto 926 alias opDollar = length; 927 928 929 /// ditto 930 void length(size_t n) @property 931 { 932 reserve(n); 933 _e = _s + n; 934 } 935 936 937 /// ditto 938 RefCountedArray!E dup() @property 939 { 940 auto dst = typeof(return)(this.length); 941 dst._impl._array[0 .. this.length] = this._impl._array[_s .. _e]; 942 return dst; 943 } 944 945 946 static if(is(const(E) : E)) 947 { 948 /// ditto 949 RefCountedArray!E dup() const @property 950 { 951 auto dst = typeof(return)(this.length); 952 dst._impl._array[0 .. this.length] = this._impl._array[_s .. _e]; 953 return dst; 954 } 955 } 956 957 958 /// ditto 959 inout(E)[] view() inout pure nothrow @safe @nogc @property 960 { 961 return _impl._array[_s .. _e]; 962 } 963 964 965 /// ditto 966 auto opSlice(size_t i, size_t j) 967 in{ 968 immutable len = this.length; 969 assert(i <= len); 970 assert(j <= len); 971 assert(i <= j); 972 } 973 body{ 974 auto dst = this; 975 dst._s += i; 976 dst._e = dst._s + (j - i); 977 return dst; 978 } 979 980 981 /// ditto 982 void opOpAssign(string op : "~")(E v) 983 { 984 this.length = this.length + 1; 985 this.back = v; 986 } 987 988 989 /// ditto 990 typeof(this) opBinary(string op : "~")(E v) 991 { 992 if(_impl.refCount == 1 && this.capacity != this.length){ 993 auto dst = this; 994 dst._e += 1; 995 dst.back = v; 996 return dst; 997 } 998 else{ 999 auto dst = this; 1000 dst ~= v; 1001 return dst; 1002 } 1003 } 1004 1005 1006 /// ditto 1007 typeof(this) opBinaryRight(string op : "~")(E v) 1008 { 1009 if(_impl.refCount == 1 && _s >= 1){ 1010 auto dst = this; 1011 dst._s -= 1; 1012 dst.front = v; 1013 return dst; 1014 } 1015 else{ 1016 typeof(this) dst; 1017 dst.reserve(1 + this.length); 1018 dst ~= v; 1019 dst ~= this.view; 1020 return dst; 1021 } 1022 } 1023 1024 1025 /// ditto 1026 void opOpAssign(string op : "~")(E[] arr) 1027 { 1028 immutable oldLen = this.length; 1029 this.length = oldLen + arr.length; 1030 this._impl._array[oldLen .. this.length] = arr[0 .. $]; 1031 } 1032 1033 1034 /// ditto 1035 typeof(this) opBinary(string op : "~")(E[] arr) 1036 { 1037 immutable len = arr.length; 1038 if(_impl.refCount <= 1 && this.capacity >= this.length + len){ 1039 auto dst = this; 1040 dst._e += len; 1041 dst._impl._array[this._e .. dst._e] = arr[0 .. $]; 1042 return dst; 1043 } 1044 else{ 1045 auto dst = this; 1046 dst ~= arr; 1047 return dst; 1048 } 1049 } 1050 1051 1052 /// ditto 1053 typeof(this) opBinaryRight(string op : "~")(E[] arr) 1054 { 1055 immutable len = arr.length; 1056 if(_impl.refCount == 1 && _s >= len){ 1057 auto dst = this; 1058 dst._s -= len; 1059 dst._impl._array[dst._s .. this._s] = arr[0 .. $]; 1060 return dst; 1061 } 1062 else{ 1063 typeof(this) dst; 1064 dst.reserve(len + this.length); 1065 dst ~= arr; 1066 dst ~= this.view; 1067 return dst; 1068 } 1069 } 1070 1071 1072 /// ditto 1073 void opOpAssign(string op : "~")(typeof(this) src) 1074 { 1075 this ~= src.view; 1076 } 1077 1078 1079 /// ditto 1080 typeof(this) opBinary(string op : "~")(typeof(this) src) 1081 { 1082 return this ~ src.view; 1083 } 1084 1085 1086 /// ditto 1087 void opOpAssign(string op : "~", R)(R range) 1088 if(isInputRange!R && is(ElementType!R : E) && !isInfinite!R) 1089 { 1090 static if(hasLength!R) 1091 { 1092 immutable oldLen = this.length, 1093 rangeLen = range.length; 1094 this.length = oldLen + rangeLen; 1095 1096 foreach(i; 0 .. rangeLen){ 1097 assert(!range.empty); 1098 _impl._array[_s + oldLen + i] = range.front; 1099 range.popFront(); 1100 } 1101 } 1102 else 1103 { 1104 if(_impl.isInitialized) 1105 this = this.dup; 1106 else 1107 this = typeof(this)(0); 1108 1109 assert(this._impl.refCount == 1); 1110 assert(this._s == 0); 1111 1112 while(!range.empty){ 1113 immutable remN = this._impl._array.length - _e; 1114 1115 size_t cnt; 1116 foreach(i; 0 .. remN){ 1117 if(range.empty) 1118 break; 1119 1120 this._impl._array[_e + i] = range.front; 1121 ++cnt; 1122 range.popFront(); 1123 } 1124 _e += cnt; 1125 1126 if(cnt == remN && !range.empty) 1127 reserve(_e + 1); 1128 } 1129 } 1130 } 1131 1132 1133 /// ditto 1134 typeof(this) opBinary(string op : "~", R)(R range) 1135 if(isInputRange!R && is(ElementType!R : E) && !isInfinite!R) 1136 { 1137 static if(hasLength!R) 1138 { 1139 immutable len = range.length; 1140 if(_impl.refCount <= 1 && this.capacity >= this.length + len){ 1141 auto dst = this; 1142 dst._e += len; 1143 foreach(i; 0 .. len){ 1144 dst._impl._array[this._e + i] = range.front; 1145 range.popFront(); 1146 } 1147 return dst; 1148 } 1149 } 1150 1151 auto dst = this; 1152 dst ~= range; 1153 return this ~ dst; 1154 } 1155 1156 1157 /// ditto 1158 typeof(this) opBinaryRight(string op : "~", R)(R range) 1159 if(isInputRange!R && is(ElementType!R : E) && !isInfinite!R && !is(typeof(range.opBinary!"~"(this)))) 1160 { 1161 static if(hasLength!R) 1162 { 1163 immutable len = range.length; 1164 if(_impl.refCount <= 1 && _s >= len){ 1165 auto dst = this; 1166 dst._s -= len; 1167 foreach(i; 0 .. len){ 1168 dst._impl._array[dst._s + i] = range.front; 1169 range.popFront(); 1170 } 1171 return dst; 1172 } 1173 1174 typeof(this) dst; 1175 dst.reserve(this.length + len); 1176 dst ~= range; 1177 dst ~= this.view; 1178 return dst; 1179 } 1180 else 1181 { 1182 typeof(this) dst; 1183 dst ~= range; 1184 return this ~ dst; 1185 } 1186 } 1187 1188 1189 /// ditto 1190 int opCmp(R)(auto ref R r) 1191 if(isForwardRange!R && is(typeof(r.front < this.front))) 1192 { 1193 return std.algorithm.cmp(this.view, r.save); 1194 } 1195 1196 1197 /// ditto 1198 bool opEquals(R)(auto ref R r) 1199 if(isForwardRange!R && is(typeof(r.front == this.front))) 1200 { 1201 return std.algorithm.equal(this.view, r.save); 1202 } 1203 1204 1205 private: 1206 Impl!E _impl; 1207 size_t _s, _e; 1208 } 1209 1210 1211 /*nothrow @safe @nogc*/ unittest{ 1212 auto arr1 = RefCountedArray!int(0); 1213 static assert(isInputRange!(typeof(arr1))); 1214 static assert(isForwardRange!(typeof(arr1))); 1215 static assert(isRandomAccessRange!(typeof(arr1))); 1216 static assert(hasLength!(typeof(arr1))); 1217 static assert(hasSlicing!(typeof(arr1))); 1218 1219 assert(arr1.length == 0); 1220 assert(equal(arr1, cast(int[])[])); 1221 assert(!arr1.isNull); 1222 1223 arr1 ~= 1; 1224 assert(arr1.length == 1); 1225 assert(equal(arr1, only(1))); 1226 assert(!arr1.isNull); 1227 1228 arr1 ~= only(2, 3); 1229 assert(arr1.length == 3); 1230 assert(equal(arr1, only(1, 2, 3))); 1231 assert(!arr1.isNull); 1232 1233 arr1 ~= arr1; 1234 assert(arr1.length == 6); 1235 assert(equal(arr1, only(1, 2, 3, 1, 2, 3))); 1236 assert(!arr1.isNull); 1237 1238 arr1 = arr1[2 .. $-1]; 1239 assert(arr1.length == 3); 1240 assert(equal(arr1, only(3, 1, 2))); 1241 assert(!arr1.isNull); 1242 1243 arr1 = arr1.dup; 1244 assert(arr1.length == 3); 1245 assert(equal(arr1, only(3, 1, 2))); 1246 assert(!arr1.isNull); 1247 1248 arr1 = null; 1249 assert(arr1.isNull); 1250 1251 arr1 ~= only(1, 2, 3, 4); 1252 assert(arr1.length == 4); 1253 assert(equal(arr1, only(1, 2, 3, 4))); 1254 assert(!arr1.isNull); 1255 1256 assert(equal(arr1, arr1.retro.retro)); 1257 1258 { 1259 auto arr2 = arr1.retro; 1260 foreach_reverse(e; arr1){ 1261 assert(e == arr2.front); 1262 arr2.popFront(); 1263 } 1264 } 1265 1266 1267 static assert(isForwardRange!(typeof(only(1, 2, 3)))); 1268 1269 1270 arr1.release(); 1271 arr1 ~= only(1, 2, 3); 1272 assert(arr1 < only(2)); 1273 assert(arr1 < only(1, 2, 3, 4)); 1274 assert(arr1 < only(1, 2, 4)); 1275 1276 assert(arr1 <= arr1); 1277 1278 assert(arr1 >= arr1); 1279 assert(arr1 == arr1); 1280 1281 assert(arr1 > only(1)); 1282 assert(arr1 > only(1, 2)); 1283 assert(arr1 > only(1, 2, 2)); 1284 assert(arr1 > only(1, 2, 2, 4)); 1285 1286 assert(arr1 == only(1, 2, 3)); 1287 assert(arr1 != only(1, 2, 3, 4)); 1288 1289 1290 // length, capacity, reserve test 1291 assert(arr1.length == 3); 1292 assert(arr1._impl.refCount == 1); 1293 assert(arr1.capacity == 4); 1294 foreach(i; 0 .. arr1.capacity - arr1.length) 1295 arr1 ~= i; 1296 1297 assert(arr1.length == 4); 1298 assert(arr1.capacity - arr1.length == 0); 1299 1300 foreach(i; 0 .. 3) 1301 arr1.popFront(); 1302 1303 assert(arr1.length == 1); 1304 assert(arr1.capacity - arr1.length == 0); 1305 1306 arr1.reserve(arr1.length + 1); // all elements moved to front. 1307 assert(arr1._s == 0); 1308 assert(arr1._e == arr1.length); 1309 assert(arr1.capacity - arr1.length == 3); 1310 } 1311 1312 1313 unittest{ 1314 import std.typecons; 1315 1316 auto arr = RefCountedArray!(RefCountedNoGC!int)(0); 1317 assert(arr.length == 0); 1318 1319 arr ~= RefCountedNoGC!int(1); 1320 assert(arr.length == 1); 1321 assert(arr.front == 1); 1322 assert(arr.front.refCountedStore.refCount == 1); 1323 1324 auto e = arr.front; 1325 assert(arr.front.refCountedStore.refCount == 2); 1326 assert(e.refCountedStore.refCount == 2); 1327 arr.popFront(); 1328 arr.release(); 1329 assert(e.refCountedStore.refCount == 1); 1330 1331 arr ~= RefCountedNoGC!int(1); 1332 e = arr.front; 1333 assert(arr.front.refCountedStore.refCount == 2); 1334 assert(e.refCountedStore.refCount == 2); 1335 1336 arr[0] = RefCountedNoGC!int(2); 1337 assert(arr.front.refCountedStore.refCount == 1); 1338 assert(e.refCountedStore.refCount == 1); 1339 1340 auto arr2 = arr.dup; 1341 assert(arr.front.refCountedStore.refCount == 2); 1342 assert(e.refCountedStore.refCount == 1); 1343 } 1344 1345 1346 /+ 1347 unittest{ 1348 static void func(T)() 1349 { 1350 T a; 1351 1352 a ~= 1; 1353 foreach(i; 0 .. 15) 1354 a ~= a; 1355 1356 T b; 1357 foreach(e; a[0 .. 1024 * 32]) 1358 b ~= e; 1359 } 1360 1361 static void funcAppender() 1362 { 1363 import std.array; 1364 auto b = appender!(int[])(); 1365 foreach(e; recurrence!"a[n-1]+a[n-2]"(1, 1).take(32 * 1024)) 1366 b ~= e; 1367 } 1368 1369 1370 import std.datetime; 1371 import std.container; 1372 auto ts = benchmark!(func!(int[]), 1373 func!(RefCountedArray!int), 1374 func!(Array!int), 1375 funcAppender)(100); 1376 1377 writeln(ts); 1378 1379 1380 RefCountedArray!int b; 1381 foreach(e; recurrence!"a[n-1]+a[n-2]"(1, 1).take(1024)) 1382 b ~= e; 1383 }+/ 1384 1385 1386 /** 1387 Cヒープ上に配列を作成します。 1388 この配列は、必ずオブジェクト一つに対してユニークな配列を割り当てます。 1389 つまり、ほとんどの場面で配列の確保とコピーが生じます。 1390 この方式は、C++のSTL std::vectorと同じです。 1391 */ 1392 struct UniqueArray(E) 1393 { 1394 /** 1395 */ 1396 this(size_t n) nothrow @safe @nogc 1397 { 1398 immutable allocLen = allocateSize(n); 1399 1400 _array = newCHeapArray!E(allocLen); 1401 _s = 0; 1402 _e = n; 1403 } 1404 1405 1406 this(this) 1407 { 1408 if(_array.ptr !is null){ 1409 immutable n = this.length; 1410 1411 auto newarr = newCHeapArray!E(allocateSize(n)); 1412 newarr[0 .. n] = _array[_s .. _e]; 1413 _array = newarr; 1414 _s = 0; 1415 _e = n; 1416 } 1417 } 1418 1419 1420 ~this() 1421 { 1422 if(_array.ptr !is null){ 1423 _array.destroyCHeapArray(); 1424 _s = 0; 1425 _e = 0; 1426 } 1427 } 1428 1429 1430 /** 1431 */ 1432 void release() 1433 { 1434 _array.destroyCHeapArray(); 1435 _s = 0; 1436 _e = 0; 1437 } 1438 1439 1440 /** 1441 */ 1442 void clear() 1443 { 1444 static if(is(E == struct) && !__traits(isPOD, E)) 1445 foreach(ref e; _array[_s .. _e]) 1446 e = E.init; 1447 1448 _s = 0; 1449 _e = 0; 1450 } 1451 1452 1453 /** 1454 */ 1455 size_t capacity() const pure nothrow @safe @nogc @property 1456 { 1457 return _array.length - _e; 1458 } 1459 1460 1461 /** 1462 */ 1463 size_t reserve(size_t n) 1464 { 1465 if(n > this.length && _array.length < n + _s){ 1466 if(_array.length < n) 1467 _array.reallocCHeapArray(allocateSize!E(n)); 1468 1469 if(_s != 0){ 1470 foreach(i; 0 .. this.length) 1471 _array[i] = _array[_s + i]; 1472 1473 _e -= _s; 1474 _s = 0; 1475 } 1476 } 1477 } 1478 1479 1480 bool isNull() const pure nothrow @safe @nogc @property 1481 { 1482 return _array is null; 1483 } 1484 1485 1486 void opAssign(typeof(null)) 1487 { 1488 this.release(); 1489 } 1490 1491 1492 /// array, range primitives 1493 ref inout(E) front() inout pure nothrow @safe @property @nogc 1494 { 1495 return _array[_s]; 1496 } 1497 1498 1499 /// ditto 1500 ref inout(E) back() inout pure nothrow @safe @property @nogc 1501 { 1502 return _array[_e-1]; 1503 } 1504 1505 1506 /// ditto 1507 ref inout(E) opIndex(size_t i) inout pure nothrow @safe @nogc 1508 { 1509 return _array[_s + i]; 1510 } 1511 1512 1513 /// ditto 1514 bool empty() const pure nothrow @safe @nogc 1515 { 1516 return _array.ptr is null || _s == _e; 1517 } 1518 1519 1520 /// ditto 1521 void popFront() 1522 { 1523 _array[_s] = E.init; 1524 ++_s; 1525 } 1526 1527 1528 /// ditto 1529 void popBack() 1530 { 1531 _array[_e-1] = E.init; 1532 --_e; 1533 } 1534 1535 1536 /// ditto 1537 typeof(this) save() @property 1538 { 1539 return this; 1540 } 1541 1542 1543 /// ditto 1544 auto dup() const @property 1545 { 1546 return this; 1547 } 1548 1549 1550 /// ditto 1551 size_t length() const pure nothrow @safe @nogc @property 1552 { 1553 return _e - _s; 1554 } 1555 1556 1557 /// ditto 1558 alias opDollar = length; 1559 1560 1561 /// ditto 1562 void length(size_t n) @property 1563 { 1564 if(n < this.length){ 1565 static if(is(E == struct) && !__traits(isPOD, E)) 1566 foreach(ref e; _array[_s + n .. _e]) 1567 e = E.init; 1568 }else 1569 reserve(n); 1570 1571 _e = _s + n; 1572 } 1573 1574 1575 /// ditto 1576 inout(E)[] view() inout pure nothrow @safe @nogc @property 1577 { 1578 return _array[_s .. _e]; 1579 } 1580 1581 1582 /// ditto 1583 void opOpAssign(string op : "~")(E v) 1584 { 1585 this.length = this.length + 1; 1586 this.back = v; 1587 } 1588 1589 1590 /// ditto 1591 void opOpAssign(string op : "~")(E[] arr) 1592 { 1593 immutable oldLen = this.length; 1594 this.length = oldLen + arr.length; 1595 this._array[oldLen .. this.length] = arr[0 .. $]; 1596 } 1597 1598 1599 /// ditto 1600 void opOpAssign(string op : "~")(typeof(this) src) 1601 { 1602 opOpAssign!"~"(src._array[src._s .. src._e]); 1603 } 1604 1605 1606 /// ditto 1607 void opOpAssign(string op : "~", R)(R range) 1608 if(isInputRange!R && is(ElementType!R : E) && !is(R : U[], U) && !isInfinite!R) 1609 { 1610 static if(hasLength!R) 1611 { 1612 immutable oldLen = this.length, 1613 rangeLen = range.length; 1614 this.length = oldLen + rangeLen; 1615 1616 foreach(i; 0 .. rangeLen){ 1617 assert(!range.empty); 1618 _array[_s + oldLen + i] = range.front; 1619 range.popFront(); 1620 } 1621 } 1622 else 1623 { 1624 this = this.dup; 1625 1626 while(!range.empty){ 1627 immutable remN = this._impl._array.length - _e; 1628 1629 size_t cnt; 1630 foreach(i; 0 .. remN){ 1631 if(range.empty) 1632 break; 1633 1634 this._impl._array[_e + i] = range.front; 1635 ++cnt; 1636 range.popFront(); 1637 } 1638 _e += cnt; 1639 1640 if(cnt == remN && !range.empty) 1641 reserve(_e + 1); 1642 } 1643 } 1644 } 1645 1646 1647 /// ditto 1648 int opCmp(R)(auto ref R r) 1649 if(isForwardRange!R && is(typeof(r.front < this.front))) 1650 { 1651 return std.algorithm.cmp(this.view, r.save); 1652 } 1653 1654 1655 /// ditto 1656 bool opEquals(R)(auto ref R r) 1657 if(isForwardRange!R && is(typeof(r.front == this.front))) 1658 { 1659 return std.algorithm.equal(this.view, r.save); 1660 } 1661 1662 1663 private: 1664 E[] _array; 1665 size_t _s, _e; 1666 }