1 /// 2 module stdx.collections.array; 3 4 import stdx.collections.common; 5 6 debug(CollectionArray) import std.stdio; 7 8 version(unittest) 9 { 10 import std.experimental.allocator.mallocator; 11 import std.experimental.allocator.building_blocks.stats_collector; 12 import std.experimental.allocator : IAllocator, allocatorObject; 13 14 private alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed); 15 } 16 17 struct Array(T) 18 { 19 import std.experimental.allocator : IAllocator, theAllocator, make, dispose; 20 import std.experimental.allocator.building_blocks.affix_allocator; 21 import std.traits : isImplicitlyConvertible, Unqual, isArray; 22 import std.range.primitives : isInputRange, isInfinite, 23 ElementType, hasLength; 24 import std.conv : emplace; 25 import core.atomic : atomicOp; 26 27 //private: 28 T[] _payload; 29 Unqual!T[] _support; 30 31 static enum double capacityFactor = 3.0 / 2; 32 static enum initCapacity = 3; 33 34 alias MutableAlloc = AffixAllocator!(IAllocator, size_t); 35 Mutable!MutableAlloc _ouroborosAllocator; 36 37 /// Returns the actual allocator from ouroboros 38 @trusted ref auto allocator(this _)() 39 { 40 assert(!_ouroborosAllocator.isNull); 41 return _ouroborosAllocator.get(); 42 } 43 44 /// Constructs the ouroboros allocator from allocator if the ouroboros 45 //allocator wasn't previously set 46 @trusted bool setAllocator(IAllocator allocator) 47 { 48 if (_ouroborosAllocator.isNull) 49 { 50 _ouroborosAllocator = Mutable!(MutableAlloc)(allocator, 51 MutableAlloc(allocator)); 52 return true; 53 } 54 return false; 55 } 56 57 @trusted IAllocator getAllocator(this _)() 58 { 59 return _ouroborosAllocator.isNull ? null : allocator().parent; 60 } 61 62 @trusted void addRef(SupportQual, this Qualified)(SupportQual support) 63 { 64 assert(support !is null); 65 debug(CollectionArray) 66 { 67 writefln("Array.addRef: Array %s has refcount: %s; will be: %s", 68 support, *prefCount(support), *prefCount(support) + 1); 69 } 70 static if (is(Qualified == immutable) || is(Qualified == const)) 71 { 72 atomicOp!"+="(*prefCount(support), 1); 73 } 74 else 75 { 76 ++*prefCount(support); 77 } 78 } 79 80 @trusted void delRef(Unqual!T[] support) 81 { 82 assert(support !is null); 83 size_t *pref = prefCount(support); 84 debug(CollectionArray) writefln("Array.delRef: Array %s has refcount: %s; will be: %s", 85 support, *pref, *pref - 1); 86 if (*pref == 0) 87 { 88 debug(CollectionArray) writefln("Array.delRef: Deleting array %s", support); 89 allocator.dispose(support); 90 } 91 else 92 { 93 --*pref; 94 } 95 } 96 97 @trusted auto prefCount(SupportQual, this Qualified)(SupportQual support) 98 { 99 assert(support !is null); 100 static if (is(Qualified == immutable) || is(Qualified == const)) 101 { 102 return cast(shared size_t*)(&allocator.prefix(support)); 103 } 104 else 105 { 106 return cast(size_t*)(&allocator.prefix(support)); 107 } 108 } 109 110 static string immutableInsert(StuffType)(string stuff) 111 { 112 static if (hasLength!StuffType) 113 { 114 auto stuffLengthStr = "" 115 ~"size_t stuffLength = " ~ stuff ~ ".length;"; 116 } 117 else 118 { 119 auto stuffLengthStr = "" 120 ~"import std.range.primitives : walkLength;" 121 ~"size_t stuffLength = walkLength(" ~ stuff ~ ");"; 122 } 123 124 return "" 125 ~ stuffLengthStr 126 ~"auto tmpAlloc = Mutable!(MutableAlloc)(allocator, MutableAlloc(allocator));" 127 ~"_ouroborosAllocator = (() @trusted => cast(immutable)(tmpAlloc))();" 128 ~"auto tmpSupport = cast(Unqual!T[])(tmpAlloc.get().allocate(stuffLength * T.sizeof));" 129 ~"assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is 130 null));" 131 ~"for (size_t i = 0; i < tmpSupport.length; ++i)" 132 ~"{" 133 ~"emplace(&tmpSupport[i]);" 134 ~"}" 135 ~"size_t i = 0;" 136 ~"foreach (item; " ~ stuff ~ ")" 137 ~"{" 138 ~"tmpSupport[i++] = item;" 139 ~"}" 140 ~"_support = cast(typeof(_support))(tmpSupport);" 141 ~"_payload = cast(T[])(_support[0 .. stuffLength]);"; 142 } 143 144 void destroyUnused() 145 { 146 debug(CollectionArray) 147 { 148 writefln("Array.destoryUnused: begin"); 149 scope(exit) writefln("Array.destoryUnused: end"); 150 } 151 if (_support !is null) 152 { 153 delRef(_support); 154 } 155 } 156 157 public: 158 this(this _)(IAllocator allocator) 159 { 160 debug(CollectionArray) 161 { 162 writefln("Array.ctor: begin"); 163 scope(exit) writefln("Array.ctor: end"); 164 } 165 setAllocator(allocator); 166 } 167 168 this(U, this Qualified)(U[] values...) 169 if (isImplicitlyConvertible!(U, T)) 170 { 171 this(theAllocator, values); 172 } 173 174 this(U, this Qualified)(IAllocator allocator, U[] values...) 175 if (isImplicitlyConvertible!(U, T)) 176 { 177 debug(CollectionArray) 178 { 179 writefln("Array.ctor: begin"); 180 scope(exit) writefln("Array.ctor: end"); 181 } 182 static if (is(Qualified == immutable) || is(Qualified == const)) 183 { 184 mixin(immutableInsert!(typeof(values))("values")); 185 assert(!_ouroborosAllocator.isNull); 186 } 187 else 188 { 189 setAllocator(allocator); 190 insert(0, values); 191 } 192 } 193 194 this(Stuff, this Qualified)(Stuff stuff) 195 if (isInputRange!Stuff && !isInfinite!Stuff 196 && isImplicitlyConvertible!(ElementType!Stuff, T) 197 && !is(Stuff == T[])) 198 { 199 this(theAllocator, stuff); 200 } 201 202 this(Stuff, this Qualified)(IAllocator allocator, Stuff stuff) 203 if (isInputRange!Stuff && !isInfinite!Stuff 204 && isImplicitlyConvertible!(ElementType!Stuff, T) 205 && !is(Stuff == T[])) 206 { 207 debug(CollectionArray) 208 { 209 writefln("Array.ctor: begin"); 210 scope(exit) writefln("Array.ctor: end"); 211 } 212 static if (is(Qualified == immutable) || is(Qualified == const)) 213 { 214 mixin(immutableInsert!(typeof(stuff))("stuff")); 215 assert(!_ouroborosAllocator.isNull); 216 } 217 else 218 { 219 setAllocator(allocator); 220 insert(0, stuff); 221 } 222 } 223 224 this(this) 225 { 226 debug(CollectionArray) 227 { 228 writefln("Array.postblit: begin"); 229 scope(exit) writefln("Array.postblit: end"); 230 } 231 if (_support !is null) 232 { 233 addRef(_support); 234 debug(CollectionArray) writefln("Array.postblit: Array %s has refcount: %s", 235 _support, *prefCount(_support)); 236 } 237 } 238 239 // Immutable ctors 240 private this(SuppQual, PaylQual, OuroQual, this Qualified)(SuppQual support, 241 PaylQual payload, OuroQual ouroborosAllocator) 242 //if (is(typeof(_support) : typeof(support)) 243 //&& (is(Qualified == immutable) || is(Qualified == const))) 244 { 245 _support = support; 246 _payload = payload; 247 _ouroborosAllocator = ouroborosAllocator; 248 if (_support !is null) 249 { 250 addRef(_support); 251 debug(CollectionArray) writefln("Array.ctor immutable: Array %s has " 252 ~ "refcount: %s", _support, *prefCount(_support)); 253 } 254 } 255 256 @trusted ~this() 257 { 258 debug(CollectionArray) 259 { 260 writefln("Array.dtor: Begin for instance %s of type %s", 261 cast(size_t)(&this), typeof(this).stringof); 262 scope(exit) writefln("Array.dtor: End for instance %s of type %s", 263 cast(size_t)(&this), typeof(this).stringof); 264 } 265 destroyUnused(); 266 } 267 268 private @trusted size_t slackFront() const 269 { 270 return _payload.ptr - _support.ptr; 271 } 272 273 private @trusted size_t slackBack() const 274 { 275 return _support.ptr + _support.length - _payload.ptr - _payload.length; 276 } 277 278 size_t length() const 279 { 280 return _payload.length; 281 } 282 283 void forceLength(size_t len) 284 { 285 assert(len <= capacity); 286 _payload = cast(T[])(_support[slackFront .. len]); 287 } 288 289 alias opDollar = length; 290 291 @trusted size_t capacity() const 292 { 293 return length + slackBack; 294 } 295 296 void reserve(size_t n) 297 { 298 debug(CollectionArray) 299 { 300 writefln("Array.reserve: begin"); 301 scope(exit) writefln("Array.reserve: end"); 302 } 303 setAllocator(theAllocator); 304 305 if (n <= capacity) { return; } 306 if (_support && *prefCount(_support) == 0) 307 { 308 void[] buf = _support; 309 if (allocator.expand(buf, (n - capacity) * T.sizeof)) 310 { 311 _support = cast(Unqual!T[])(buf); 312 // TODO: emplace extended buf 313 return; 314 } 315 else 316 { 317 //assert(0, "Array.reserve: Failed to expand array."); 318 } 319 } 320 321 auto tmpSupport = cast(Unqual!T[])(allocator.allocate(n * T.sizeof)); 322 assert(tmpSupport !is null); 323 for (size_t i = 0; i < tmpSupport.length; ++i) 324 { 325 emplace(&tmpSupport[i]); 326 } 327 tmpSupport[0 .. _payload.length] = _payload[]; 328 __dtor(); 329 _support = tmpSupport; 330 _payload = cast(T[])(_support[0 .. _payload.length]); 331 assert(capacity >= n); 332 } 333 334 size_t insert(Stuff)(size_t pos, Stuff stuff) 335 if (!isArray!(typeof(stuff)) && isInputRange!Stuff && !isInfinite!Stuff 336 && isImplicitlyConvertible!(ElementType!Stuff, T)) 337 { 338 debug(CollectionArray) 339 { 340 writefln("Array.insert: begin"); 341 scope(exit) writefln("Array.insert: end"); 342 } 343 setAllocator(theAllocator); 344 345 static if (hasLength!Stuff) 346 { 347 size_t stuffLength = stuff.length; 348 } 349 else 350 { 351 import std.range.primitives : walkLength; 352 size_t stuffLength = walkLength(stuff); 353 } 354 if (stuffLength == 0) return 0; 355 356 auto tmpSupport = cast(Unqual!T[])(allocator.allocate(stuffLength * T.sizeof)); 357 assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is null)); 358 for (size_t i = 0; i < tmpSupport.length; ++i) 359 { 360 emplace(&tmpSupport[i]); 361 } 362 363 size_t i = 0; 364 foreach (item; stuff) 365 { 366 tmpSupport[i++] = item; 367 } 368 size_t result = insert(pos, tmpSupport); 369 allocator.dispose(tmpSupport); 370 return result; 371 } 372 373 size_t insert(Stuff)(size_t pos, Stuff[] stuff...) 374 if (isImplicitlyConvertible!(Stuff, T)) 375 { 376 debug(CollectionArray) 377 { 378 writefln("Array.insert: begin"); 379 scope(exit) writefln("Array.insert: end"); 380 } 381 assert(pos <= _payload.length); 382 setAllocator(theAllocator); 383 384 if (stuff.length == 0) return 0; 385 if (stuff.length > slackBack) 386 { 387 double newCapacity = capacity ? capacity * capacityFactor : stuff.length; 388 while (newCapacity < capacity + stuff.length) 389 { 390 newCapacity = newCapacity * capacityFactor; 391 } 392 reserve(cast(size_t)(newCapacity)); 393 } 394 //_support[pos + stuff.length .. _payload.length + stuff.length] = 395 //_support[pos .. _payload.length]; 396 for (size_t i = _payload.length + stuff.length - 1; i >= pos + 397 stuff.length; --i) 398 { 399 // Avoids underflow if payload is empty 400 _support[i] = _support[i - stuff.length]; 401 } 402 _support[pos .. pos + stuff.length] = stuff[]; 403 _payload = cast(T[])(_support[0 .. _payload.length + stuff.length]); 404 return stuff.length; 405 } 406 407 bool isUnique(this _)() 408 { 409 debug(CollectionArray) 410 { 411 writefln("Array.isUnique: begin"); 412 scope(exit) writefln("Array.isUnique: end"); 413 } 414 415 if (_support !is null) 416 { 417 return *prefCount(_support) == 0; 418 } 419 return true; 420 } 421 422 bool empty(this _)() 423 { 424 return length == 0; 425 } 426 427 ref auto front(this _)() 428 { 429 assert(!empty, "Array.front: Array is empty"); 430 return _payload[0]; 431 } 432 433 void popFront() 434 { 435 debug(CollectionArray) 436 { 437 writefln("Array.popFront: begin"); 438 scope(exit) writefln("Array.popFront: end"); 439 } 440 assert(!empty, "Array.popFront: Array is empty"); 441 _payload = _payload[1 .. $]; 442 } 443 444 Qualified tail(this Qualified)() 445 { 446 debug(CollectionArray) 447 { 448 writefln("Array.tail: begin"); 449 scope(exit) writefln("Array.tail: end"); 450 } 451 assert(!empty, "Array.tail: Array is empty"); 452 453 static if (is(Qualified == immutable) || is(Qualified == const)) 454 { 455 return this[1 .. $]; 456 } 457 else 458 { 459 return .tail(this); 460 } 461 } 462 463 ref auto save(this _)() 464 { 465 debug(CollectionArray) 466 { 467 writefln("Array.save: begin"); 468 scope(exit) writefln("Array.save: end"); 469 } 470 return this; 471 } 472 473 typeof(this) dup() 474 { 475 debug(CollectionArray) 476 { 477 writefln("Array.dup: begin"); 478 scope(exit) writefln("Array.dup: end"); 479 } 480 IAllocator alloc = getAllocator(); 481 if (alloc is null) 482 { 483 alloc = theAllocator; 484 } 485 return typeof(this)(alloc, this); 486 } 487 488 Qualified opSlice(this Qualified)() 489 { 490 debug(CollectionArray) 491 { 492 writefln("Array.opSlice(): begin"); 493 scope(exit) writefln("Array.opSlice(): end"); 494 } 495 return this.save; 496 } 497 498 Qualified opSlice(this Qualified)(size_t start, size_t end) 499 in 500 { 501 assert(start <= end && end <= length, 502 "Array.opSlice(s, e): Invalid bounds: Ensure start <= end <= length"); 503 } 504 body 505 { 506 debug(CollectionArray) 507 { 508 writefln("Array.opSlice(s, e): begin"); 509 scope(exit) writefln("Array.opSlice(s, e): end"); 510 } 511 return typeof(this)(_support, _payload[start .. end], _ouroborosAllocator); 512 } 513 514 ref auto opIndex(this _)(size_t idx) 515 in 516 { 517 assert(idx <= length, "Array.opIndex: Index out of bounds"); 518 } 519 body 520 { 521 return _payload[idx]; 522 } 523 524 ref auto opIndexUnary(string op)(size_t idx) 525 in 526 { 527 assert(idx <= length, "Array.opIndexUnary!" ~ op ~ ": Index out of bounds"); 528 } 529 body 530 { 531 mixin("return " ~ op ~ "_payload[idx];"); 532 } 533 534 ref auto opIndexAssign(U)(U elem, size_t idx) 535 if (isImplicitlyConvertible!(U, T)) 536 in 537 { 538 assert(idx <= length, "Array.opIndexAssign: Index out of bounds"); 539 } 540 body 541 { 542 return _payload[idx] = elem; 543 } 544 545 ref auto opIndexOpAssign(string op, U)(U elem, size_t idx) 546 if (isImplicitlyConvertible!(U, T)) 547 in 548 { 549 assert(idx <= length, "Array.opIndexOpAssign!" ~ op ~ ": Index out of bounds"); 550 } 551 body 552 { 553 mixin("return _payload[idx]" ~ op ~ "= elem;"); 554 } 555 556 auto ref opBinary(string op, U)(auto ref U rhs) 557 if (op == "~" && 558 (is (U == typeof(this)) 559 || is (U : T) 560 || (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T)) 561 )) 562 { 563 debug(CollectionArray) 564 { 565 writefln("Array.opBinary!~: begin"); 566 scope(exit) writefln("Array.opBinary!~: end"); 567 } 568 569 //TODO: should work for immutable, const as well 570 571 typeof(this) newArray = this.dup(); 572 newArray.insert(length, rhs); 573 return newArray; 574 } 575 576 auto ref opAssign()(auto ref typeof(this) rhs) 577 { 578 debug(CollectionArray) 579 { 580 writefln("Array.opAssign: begin"); 581 scope(exit) writefln("Array.opAssign: end"); 582 } 583 584 if (rhs._support !is null && _support is rhs._support) 585 { 586 return this; 587 } 588 589 if (rhs._support !is null) 590 { 591 rhs.addRef(rhs._support); 592 debug(CollectionArray) writefln("Array.opAssign: Array %s has refcount: %s", 593 rhs._payload, *prefCount(rhs._support)); 594 } 595 //__dtor(); 596 destroyUnused(); 597 _support = rhs._support; 598 _payload = rhs._payload; 599 _ouroborosAllocator = rhs._ouroborosAllocator; 600 return this; 601 } 602 603 auto ref opOpAssign(string op, U)(auto ref U rhs) 604 if (op == "~" && 605 (is (U == typeof(this)) 606 || is (U : T) 607 || (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T)) 608 )) 609 { 610 debug(CollectionArray) 611 { 612 writefln("Array.opOpAssign!~: %s begin", typeof(this).stringof); 613 scope(exit) writefln("Array.opOpAssign!~: %s end", typeof(this).stringof); 614 } 615 insert(length, rhs); 616 return this; 617 } 618 } 619 620 version(unittest) private @trusted void testConcatAndAppend(IAllocator allocator) 621 { 622 import std.algorithm.comparison : equal; 623 624 auto a = Array!(int)(allocator, 1, 2, 3); 625 Array!(int) a2 = Array!(int)(allocator); 626 627 auto a3 = a ~ a2; 628 assert(equal(a3, [1, 2, 3])); 629 630 auto a4 = a3; 631 a3 = a3 ~ 4; 632 assert(equal(a3, [1, 2, 3, 4])); 633 a3 = a3 ~ [5]; 634 assert(equal(a3, [1, 2, 3, 4, 5])); 635 assert(equal(a4, [1, 2, 3])); 636 637 a4 = a3; 638 a3 ~= 6; 639 assert(equal(a3, [1, 2, 3, 4, 5, 6])); 640 a3 ~= [7]; 641 642 a3 ~= a3; 643 assert(equal(a3, [1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7])); 644 645 Array!int a5 = Array!(int)(allocator); 646 a5 ~= [1, 2, 3]; 647 assert(equal(a5, [1, 2, 3])); 648 auto a6 = a5; 649 a5 = a5; 650 a5[0] = 10; 651 assert(equal(a5, a6)); 652 } 653 654 @trusted unittest 655 { 656 import std.conv; 657 SCAlloc statsCollectorAlloc; 658 auto _allocator = allocatorObject(statsCollectorAlloc); 659 660 () @safe { 661 testConcatAndAppend(_allocator); 662 auto bytesUsed = _allocator.impl.bytesUsed; 663 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 664 ~ to!string(bytesUsed) ~ " bytes"); 665 }(); 666 } 667 668 version(unittest) private @trusted void testSimple(IAllocator allocator) 669 { 670 import std.algorithm.comparison : equal; 671 import std.algorithm.searching : canFind; 672 673 auto a = Array!int(allocator); 674 assert(a.empty); 675 assert(a.isUnique); 676 677 size_t pos = 0; 678 a.insert(pos, 1, 2, 3); 679 assert(a.front == 1); 680 assert(equal(a, a)); 681 assert(equal(a, [1, 2, 3])); 682 683 a.popFront(); 684 assert(a.front == 2); 685 assert(equal(a, [2, 3])); 686 687 a.insert(pos, [4, 5, 6]); 688 a.insert(pos, 7); 689 a.insert(pos, [8]); 690 assert(equal(a, [8, 7, 4, 5, 6, 2, 3])); 691 692 a.insert(a.length, 0, 1); 693 a.insert(a.length, [-1, -2]); 694 assert(equal(a, [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); 695 696 a.front = 9; 697 assert(equal(a, [9, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); 698 699 auto aTail = a.tail; 700 assert(aTail.front == 7); 701 aTail.front = 8; 702 assert(aTail.front == 8); 703 assert(a.tail.front == 8); 704 assert(!a.isUnique); 705 706 assert(canFind(a, 2)); 707 assert(!canFind(a, -10)); 708 } 709 710 @trusted unittest 711 { 712 import std.conv; 713 SCAlloc statsCollectorAlloc; 714 auto _allocator = allocatorObject(statsCollectorAlloc); 715 716 () @safe { 717 testSimple(_allocator); 718 auto bytesUsed = _allocator.impl.bytesUsed; 719 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 720 ~ to!string(bytesUsed) ~ " bytes"); 721 }(); 722 } 723 724 version(unittest) private @trusted void testSimpleImmutable(IAllocator allocator) 725 { 726 import std.algorithm.comparison : equal; 727 import std.algorithm.searching : canFind; 728 729 auto a = Array!(immutable int)(allocator); 730 assert(a.empty); 731 732 size_t pos = 0; 733 a.insert(pos, 1, 2, 3); 734 assert(a.front == 1); 735 assert(equal(a, a)); 736 assert(equal(a, [1, 2, 3])); 737 738 a.popFront(); 739 assert(a.front == 2); 740 assert(equal(a, [2, 3])); 741 assert(a.tail.front == 3); 742 743 a.insert(pos, [4, 5, 6]); 744 a.insert(pos, 7); 745 a.insert(pos, [8]); 746 assert(equal(a, [8, 7, 4, 5, 6, 2, 3])); 747 748 a.insert(a.length, 0, 1); 749 a.insert(a.length, [-1, -2]); 750 assert(equal(a, [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); 751 752 // Cannot modify immutable values 753 static assert(!__traits(compiles, a.front = 9)); 754 755 assert(canFind(a, 2)); 756 assert(!canFind(a, -10)); 757 } 758 759 @trusted unittest 760 { 761 import std.conv; 762 SCAlloc statsCollectorAlloc; 763 auto _allocator = allocatorObject(statsCollectorAlloc); 764 765 () @safe { 766 testSimpleImmutable(_allocator); 767 auto bytesUsed = _allocator.impl.bytesUsed; 768 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 769 ~ to!string(bytesUsed) ~ " bytes"); 770 }(); 771 } 772 773 version(unittest) private @trusted void testCopyAndRef(IAllocator allocator) 774 { 775 import std.algorithm.comparison : equal; 776 777 auto aFromList = Array!int(allocator, 1, 2, 3); 778 auto aFromRange = Array!int(allocator, aFromList); 779 assert(equal(aFromList, aFromRange)); 780 781 aFromList.popFront(); 782 assert(equal(aFromList, [2, 3])); 783 assert(equal(aFromRange, [1, 2, 3])); 784 785 size_t pos = 0; 786 Array!int aInsFromRange = Array!int(allocator); 787 aInsFromRange.insert(pos, aFromList); 788 aFromList.popFront(); 789 assert(equal(aFromList, [3])); 790 assert(equal(aInsFromRange, [2, 3])); 791 792 Array!int aInsBackFromRange = Array!int(allocator); 793 aInsBackFromRange.insert(pos, aFromList); 794 aFromList.popFront(); 795 assert(aFromList.empty); 796 assert(equal(aInsBackFromRange, [3])); 797 798 auto aFromRef = aInsFromRange; 799 auto aFromDup = aInsFromRange.dup; 800 assert(aInsFromRange.front == 2); 801 aFromRef.front = 5; 802 assert(aInsFromRange.front == 5); 803 assert(aFromDup.front == 2); 804 } 805 806 @trusted unittest 807 { 808 import std.conv; 809 SCAlloc statsCollectorAlloc; 810 auto _allocator = allocatorObject(statsCollectorAlloc); 811 812 () @safe { 813 testCopyAndRef(_allocator); 814 auto bytesUsed = _allocator.impl.bytesUsed; 815 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 816 ~ to!string(bytesUsed) ~ " bytes"); 817 }(); 818 } 819 820 version(unittest) private @trusted void testImmutability(IAllocator allocator) 821 { 822 auto a = immutable Array!(int)(allocator, 1, 2, 3); 823 auto a2 = a; 824 auto a3 = a2.save(); 825 826 assert(a2.front == 1); 827 assert(a2[0] == a2.front); 828 static assert(!__traits(compiles, a2.front = 4)); 829 static assert(!__traits(compiles, a2.popFront())); 830 831 auto a4 = a2.tail; 832 assert(a4.front == 2); 833 static assert(!__traits(compiles, a4 = a4.tail)); 834 } 835 836 version(unittest) private @trusted void testConstness(IAllocator allocator) 837 { 838 auto a = const Array!(int)(allocator, 1, 2, 3); 839 auto a2 = a; 840 auto a3 = a2.save(); 841 842 assert(a2.front == 1); 843 assert(a2[0] == a2.front); 844 static assert(!__traits(compiles, a2.front = 4)); 845 static assert(!__traits(compiles, a2.popFront())); 846 847 auto a4 = a2.tail; 848 assert(a4.front == 2); 849 static assert(!__traits(compiles, a4 = a4.tail)); 850 } 851 852 @trusted unittest 853 { 854 import std.conv; 855 SCAlloc statsCollectorAlloc; 856 auto _allocator = allocatorObject(statsCollectorAlloc); 857 858 () @safe { 859 testImmutability(_allocator); 860 testConstness(_allocator); 861 auto bytesUsed = _allocator.impl.bytesUsed; 862 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 863 ~ to!string(bytesUsed) ~ " bytes"); 864 }(); 865 } 866 867 version(unittest) private @trusted void testWithStruct(IAllocator allocator) 868 { 869 import std.algorithm.comparison : equal; 870 import std.stdio; 871 872 auto array = Array!int(allocator, 1, 2, 3); 873 { 874 auto arrayOfArrays = Array!(Array!int)(allocator, array); 875 assert(equal(arrayOfArrays.front, [1, 2, 3])); 876 arrayOfArrays.front.front = 2; 877 assert(equal(arrayOfArrays.front, [2, 2, 3])); 878 static assert(!__traits(compiles, arrayOfArrays.insert(1))); 879 880 auto immArrayOfArrays = immutable Array!(Array!int)(allocator, array); 881 assert(immArrayOfArrays.front.front == 2); 882 static assert(!__traits(compiles, immArrayOfArrays.front.front = 2)); 883 } 884 assert(equal(array, [2, 2, 3])); 885 } 886 887 @trusted unittest 888 { 889 import std.conv; 890 SCAlloc statsCollectorAlloc; 891 auto _allocator = allocatorObject(statsCollectorAlloc); 892 893 () @safe { 894 testWithStruct(_allocator); 895 auto bytesUsed = _allocator.impl.bytesUsed; 896 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 897 ~ to!string(bytesUsed) ~ " bytes"); 898 }(); 899 } 900 901 version(unittest) private @trusted void testWithClass(IAllocator allocator) 902 { 903 class MyClass 904 { 905 int x; 906 this(int x) { this.x = x; } 907 } 908 909 MyClass c = new MyClass(10); 910 { 911 auto a = Array!MyClass(allocator, c); 912 assert(a.front.x == 10); 913 assert(a.front is c); 914 a.front.x = 20; 915 } 916 assert(c.x == 20); 917 } 918 919 @trusted unittest 920 { 921 import std.conv; 922 SCAlloc statsCollectorAlloc; 923 auto _allocator = allocatorObject(statsCollectorAlloc); 924 925 () @safe { 926 testWithClass(_allocator); 927 auto bytesUsed = _allocator.impl.bytesUsed; 928 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 929 ~ to!string(bytesUsed) ~ " bytes"); 930 }(); 931 } 932 933 version(unittest) private @trusted void testOpOverloads(IAllocator allocator) 934 { 935 auto a = Array!int(allocator, 1, 2, 3, 4); 936 assert(a[0] == 1); // opIndex 937 938 // opIndexUnary 939 ++a[0]; 940 assert(a[0] == 2); 941 --a[0]; 942 assert(a[0] == 1); 943 a[0]++; 944 assert(a[0] == 2); 945 a[0]--; 946 assert(a[0] == 1); 947 948 // opIndexAssign 949 a[0] = 2; 950 assert(a[0] == 2); 951 952 // opIndexOpAssign 953 a[0] /= 2; 954 assert(a[0] == 1); 955 a[0] *= 2; 956 assert(a[0] == 2); 957 a[0] -= 1; 958 assert(a[0] == 1); 959 a[0] += 1; 960 assert(a[0] == 2); 961 } 962 963 @trusted unittest 964 { 965 import std.conv; 966 SCAlloc statsCollectorAlloc; 967 auto _allocator = allocatorObject(statsCollectorAlloc); 968 969 () @safe { 970 testOpOverloads(_allocator); 971 auto bytesUsed = _allocator.impl.bytesUsed; 972 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 973 ~ to!string(bytesUsed) ~ " bytes"); 974 }(); 975 } 976 977 version(unittest) private @trusted void testSlice(IAllocator allocator) 978 { 979 import std.algorithm.comparison : equal; 980 981 auto a = Array!int(allocator, 1, 2, 3, 4); 982 auto b = a[]; 983 assert(equal(a, b)); 984 b[1] = 5; 985 assert(a[1] == 5); 986 987 size_t startPos = 2; 988 auto c = b[startPos .. $]; 989 assert(equal(c, [3, 4])); 990 c[0] = 5; 991 assert(equal(a, b)); 992 assert(equal(a, [1, 5, 5, 4])); 993 assert(a.capacity == b.capacity && b.capacity == c.capacity + startPos); 994 995 c ~= 5; 996 assert(equal(c, [5, 4, 5])); 997 assert(equal(a, b)); 998 assert(equal(a, [1, 5, 5, 4])); 999 } 1000 1001 @trusted unittest 1002 { 1003 import std.conv; 1004 SCAlloc statsCollectorAlloc; 1005 auto _allocator = allocatorObject(statsCollectorAlloc); 1006 1007 () @safe { 1008 testSlice(_allocator); 1009 auto bytesUsed = _allocator.impl.bytesUsed; 1010 assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 1011 ~ to!string(bytesUsed) ~ " bytes"); 1012 }(); 1013 } 1014 1015 //@trusted unittest { 1016 //import stdx.collections.slist; 1017 //import std.stdio; 1018 1019 //{ 1020 //auto a = Array!(SList!int)(SList!int(1)); 1021 //writefln("Array: %s", *a.prefCount(a._support)); 1022 //writefln("SList: %s", *a.front.prefCount(a.front._head)); 1023 //{ 1024 //auto b = a; 1025 //writefln("Array: %s", *a.prefCount(a._support)); 1026 //writefln("SList: %s", *a.front.prefCount(a.front._head)); 1027 //size_t i = 0; 1028 //auto sl = a.front; 1029 ////while(!a.front.empty) 1030 ////{ 1031 ////writefln("[%s] %s", i, a.front.front); 1032 ////a.front.popFront; 1033 ////} 1034 //while(!sl.empty) 1035 //{ 1036 //writefln("[%s] %s", i, sl.front); 1037 //sl.popFront; 1038 //} 1039 //writefln("At end of scope"); 1040 //} 1041 //writefln("After end of scope"); 1042 //writefln("Array: %s", *a.prefCount(a._support)); 1043 //writefln("SList: %s", *a.front.prefCount(a.front._head)); 1044 //} 1045 1046 //import std.conv; 1047 //writefln("HERE"); 1048 //auto bytesUsed = _allocator.bytesUsed; 1049 //assert(bytesUsed == 0, "Array ref count leaks memory; leaked " 1050 //~ to!string(bytesUsed) ~ " bytes"); 1051 /*}*/