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