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