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