1 /** 2 * This module provides an `Array` type with deterministic memory usage not 3 * reliant on the GC, as an alternative to the built-in arrays. 4 * 5 * This module is a submodule of $(MREF std, container). 6 * 7 * Source: $(PHOBOSSRC std/container/array.d) 8 * 9 * Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. 10 * 11 * License: Distributed under the Boost Software License, Version 1.0. 12 * (See accompanying file LICENSE_1_0.txt or copy at $(HTTP 13 * boost.org/LICENSE_1_0.txt)). 14 * 15 * Authors: $(HTTP erdani.com, Andrei Alexandrescu) 16 * 17 * $(SCRIPT inhibitQuickIndex = 1;) 18 */ 19 module std.container.array; 20 21 import core.exception : RangeError; 22 import std.range.primitives; 23 import std.traits; 24 25 public import std.container.util; 26 27 pure @system unittest 28 { 29 // We test multiple array lengths in order to ensure that the "a1.capacity == a0.length" test is meaningful 30 // for the version in which the constructor uses insertBack 31 // (because for some lengths, the test passes even without reserve). 32 for (size_t n = 0; n < 100; ++n) 33 { 34 float[] a0; 35 { 36 import std.range : iota; 37 import std.array; 38 import std.algorithm.iteration : map; 39 a0 = iota (0, n).map!(i => i * 1.1f).array; 40 } 41 42 auto a1 = Array!float(a0); 43 44 // We check that a1 has the same length and contents as a0: 45 { 46 assert(a1.length == a0.length); 47 48 // I wish that I could write "assert(a1[] == a0[]);", 49 // but the compiler complains: "Error: incompatible types for `(a1.opSlice()) == (a0[])`: `RangeT!(Array!float)` and `float[]`". 50 import std.algorithm.comparison : equal; 51 assert(equal(a1[], a0[])); 52 } 53 54 // We check that a1's constructor has called reserve (to maintain good performance): 55 assert(a1.capacity == a0.length); 56 } 57 } 58 59 pure @system unittest 60 { 61 // To avoid bad performance, we check that an Array constructed from an empty range 62 // does not initialize its RefCountedStore object, even after a call to "reserve(0)". 63 64 { 65 Array!float a1; 66 assert(! a1._data.refCountedStore.isInitialized); 67 a1.reserve(0); 68 assert(! a1._data.refCountedStore.isInitialized); 69 } 70 71 { 72 float[] a0 = []; 73 Array!float a1 = a0; 74 // [2021-09-26] TODO: Investigate RefCounted. 75 //assert(! a1._data.refCountedStore.isInitialized); 76 a1.reserve(0); 77 // [2021-09-26] TODO: Investigate RefCounted. 78 //assert(! a1._data.refCountedStore.isInitialized); 79 } 80 } 81 82 /// 83 pure @system unittest 84 { 85 auto arr = Array!int(0, 2, 3); 86 assert(arr[0] == 0); 87 assert(arr.front == 0); 88 assert(arr.back == 3); 89 90 // reserve space 91 arr.reserve(1000); 92 assert(arr.length == 3); 93 assert(arr.capacity >= 1000); 94 95 // insertion 96 arr.insertBefore(arr[1..$], 1); 97 assert(arr.front == 0); 98 assert(arr.length == 4); 99 100 arr.insertBack(4); 101 assert(arr.back == 4); 102 assert(arr.length == 5); 103 104 // set elements 105 arr[1] *= 42; 106 assert(arr[1] == 42); 107 } 108 109 /// 110 pure @system unittest 111 { 112 import std.algorithm.comparison : equal; 113 auto arr = Array!int(1, 2, 3); 114 115 // concat 116 auto b = Array!int(11, 12, 13); 117 arr ~= b; 118 assert(arr.length == 6); 119 120 // slicing 121 assert(arr[1 .. 3].equal([2, 3])); 122 123 // remove 124 arr.linearRemove(arr[1 .. 3]); 125 assert(arr[0 .. 2].equal([1, 11])); 126 } 127 128 /// `Array!bool` packs together values efficiently by allocating one bit per element 129 pure @system unittest 130 { 131 auto arr = Array!bool([true, true, false, true, false]); 132 assert(arr.length == 5); 133 } 134 135 private struct RangeT(A) 136 { 137 /* Workaround for https://issues.dlang.org/show_bug.cgi?id=13629 138 See also: http://forum.dlang.org/post/vbmwhzvawhnkoxrhbnyb@forum.dlang.org 139 */ 140 private A[1] _outer_; 141 private @property ref inout(A) _outer() inout { return _outer_[0]; } 142 143 private size_t _a, _b; 144 145 /* E is different from T when A is more restrictively qualified than T: 146 immutable(Array!int) => T == int, E = immutable(int) */ 147 alias E = typeof(_outer_[0]._data._payload[0]); 148 149 private this(ref A data, size_t a, size_t b) 150 { 151 _outer_ = data; 152 _a = a; 153 _b = b; 154 } 155 156 @property RangeT save() 157 { 158 return this; 159 } 160 161 @property bool empty() @safe pure nothrow const 162 { 163 return _a >= _b; 164 } 165 166 @property size_t length() @safe pure nothrow const 167 { 168 return _b - _a; 169 } 170 alias opDollar = length; 171 172 @property ref inout(E) front() inout 173 { 174 assert(!empty, "Attempting to access the front of an empty Array"); 175 return _outer[_a]; 176 } 177 @property ref inout(E) back() inout 178 { 179 assert(!empty, "Attempting to access the back of an empty Array"); 180 return _outer[_b - 1]; 181 } 182 183 void popFront() @safe @nogc pure nothrow 184 { 185 assert(!empty, "Attempting to popFront an empty Array"); 186 ++_a; 187 } 188 189 void popBack() @safe @nogc pure nothrow 190 { 191 assert(!empty, "Attempting to popBack an empty Array"); 192 --_b; 193 } 194 195 static if (isMutable!A) 196 { 197 import std.algorithm.mutation : move; 198 199 E moveFront() 200 { 201 assert(!empty, "Attempting to moveFront an empty Array"); 202 assert(_a < _outer.length, 203 "Attempting to moveFront using an out of bounds low index on an Array"); 204 return move(_outer._data._payload[_a]); 205 } 206 207 E moveBack() 208 { 209 assert(!empty, "Attempting to moveBack an empty Array"); 210 assert(_b - 1 < _outer.length, 211 "Attempting to moveBack using an out of bounds high index on an Array"); 212 return move(_outer._data._payload[_b - 1]); 213 } 214 215 E moveAt(size_t i) 216 { 217 assert(_a + i < _b, 218 "Attempting to moveAt using an out of bounds index on an Array"); 219 assert(_a + i < _outer.length, 220 "Cannot move past the end of the array"); 221 return move(_outer._data._payload[_a + i]); 222 } 223 } 224 225 ref inout(E) opIndex(size_t i) inout 226 { 227 assert(_a + i < _b, 228 "Attempting to fetch using an out of bounds index on an Array"); 229 return _outer[_a + i]; 230 } 231 232 RangeT opSlice() 233 { 234 return typeof(return)(_outer, _a, _b); 235 } 236 237 RangeT opSlice(size_t i, size_t j) 238 { 239 assert(i <= j && _a + j <= _b, 240 "Attempting to slice using an out of bounds indices on an Array"); 241 return typeof(return)(_outer, _a + i, _a + j); 242 } 243 244 RangeT!(const(A)) opSlice() const 245 { 246 return typeof(return)(_outer, _a, _b); 247 } 248 249 RangeT!(const(A)) opSlice(size_t i, size_t j) const 250 { 251 assert(i <= j && _a + j <= _b, 252 "Attempting to slice using an out of bounds indices on an Array"); 253 return typeof(return)(_outer, _a + i, _a + j); 254 } 255 256 static if (isMutable!A) 257 { 258 void opSliceAssign(E value) 259 { 260 assert(_b <= _outer.length, 261 "Attempting to assign using an out of bounds indices on an Array"); 262 _outer[_a .. _b] = value; 263 } 264 265 void opSliceAssign(E value, size_t i, size_t j) 266 { 267 assert(_a + j <= _b, 268 "Attempting to slice assign using an out of bounds indices on an Array"); 269 _outer[_a + i .. _a + j] = value; 270 } 271 272 void opSliceUnary(string op)() 273 if (op == "++" || op == "--") 274 { 275 assert(_b <= _outer.length, 276 "Attempting to slice using an out of bounds indices on an Array"); 277 mixin(op~"_outer[_a .. _b];"); 278 } 279 280 void opSliceUnary(string op)(size_t i, size_t j) 281 if (op == "++" || op == "--") 282 { 283 assert(_a + j <= _b, 284 "Attempting to slice using an out of bounds indices on an Array"); 285 mixin(op~"_outer[_a + i .. _a + j];"); 286 } 287 288 void opSliceOpAssign(string op)(E value) 289 { 290 assert(_b <= _outer.length, 291 "Attempting to slice using an out of bounds indices on an Array"); 292 mixin("_outer[_a .. _b] "~op~"= value;"); 293 } 294 295 void opSliceOpAssign(string op)(E value, size_t i, size_t j) 296 { 297 assert(_a + j <= _b, 298 "Attempting to slice using an out of bounds indices on an Array"); 299 mixin("_outer[_a + i .. _a + j] "~op~"= value;"); 300 } 301 } 302 } 303 304 @system unittest 305 { 306 enum : bool { display = false } 307 static if (display) 308 { 309 import std.stdio; 310 enum { nDigitsForPointer = 2 * size_t.sizeof, nDigitsForNObjects = 4 } 311 } 312 313 static struct S 314 { 315 static size_t s_nConstructed; 316 static size_t s_nDestroyed; 317 static void throwIfTooMany() 318 { 319 if (s_nConstructed >= 7) throw new Exception ("Ka-boom !"); 320 } 321 322 uint _i; 323 324 ~this() 325 { 326 static if (display) writefln("@%*Xh: Destroying.", nDigitsForPointer, &this); 327 ++s_nDestroyed; 328 } 329 330 this(uint i) 331 { 332 static if (display) writefln("@%*Xh: Constructing.", nDigitsForPointer, &this); 333 _i = i; 334 ++s_nConstructed; 335 throwIfTooMany(); 336 } 337 338 this(this) 339 { 340 static if (display) writefln("@%*Xh: Copying.", nDigitsForPointer, &this); 341 ++s_nConstructed; 342 throwIfTooMany(); 343 } 344 } 345 346 try 347 { 348 auto a = Array!S (S(0), S(1), S(2), S(3)); 349 static if (display) writefln("@%*Xh: This is where the array elements are.", nDigitsForPointer, &a [0]); 350 } 351 catch (Exception e) 352 { 353 static if (display) writefln("Exception caught !"); 354 } 355 356 static if (display) 357 { 358 writefln("s_nConstructed %*Xh.", nDigitsForNObjects, S.s_nConstructed); 359 writefln("s_nDestroyed %*Xh.", nDigitsForNObjects, S.s_nDestroyed); 360 writefln("s_nConstructed should be equal to s_nDestroyed."); 361 writefln(""); 362 } 363 364 assert(S.s_nDestroyed == S.s_nConstructed); 365 } 366 367 368 /** 369 * _Array type with deterministic control of memory. The memory allocated 370 * for the array is reclaimed as soon as possible; there is no reliance 371 * on the garbage collector. `Array` uses `malloc`, `realloc` and `free` 372 * for managing its own memory. 373 * 374 * This means that pointers to elements of an `Array` will become 375 * dangling as soon as the element is removed from the `Array`. On the other hand 376 * the memory allocated by an `Array` will be scanned by the GC and 377 * GC managed objects referenced from an `Array` will be kept alive. 378 * 379 * Note: 380 * 381 * When using `Array` with range-based functions like those in `std.algorithm`, 382 * `Array` must be sliced to get a range (for example, use `array[].map!` 383 * instead of `array.map!`). The container itself is not a range. 384 */ 385 struct Array(T) 386 if (!is(immutable T == immutable bool)) 387 { 388 import core.memory : free = pureFree; 389 import std.internal.memory : enforceMalloc, enforceRealloc; 390 import core.stdc.string : memcpy, memmove, memset; 391 392 import core.memory : GC; 393 394 import std.exception : enforce; 395 import std.typecons : RefCounted, RefCountedAutoInitialize; 396 397 // This structure is not copyable. 398 private struct Payload 399 { 400 size_t _capacity; 401 T[] _payload; 402 403 this(T[] p) { _capacity = p.length; _payload = p; } 404 405 // Destructor releases array memory 406 ~this() 407 { 408 // Warning: destroy would destroy also class instances. 409 // The hasElaborateDestructor protects us here. 410 static if (hasElaborateDestructor!T) 411 foreach (ref e; _payload) 412 .destroy(e); 413 414 static if (hasIndirections!T) 415 GC.removeRange(_payload.ptr); 416 417 free(_payload.ptr); 418 } 419 420 this(this) @disable; 421 422 void opAssign(Payload rhs) @disable; 423 424 @property size_t length() const 425 { 426 return _payload.length; 427 } 428 429 @property void length(size_t newLength) 430 { 431 if (length >= newLength) 432 { 433 // shorten 434 static if (hasElaborateDestructor!T) 435 foreach (ref e; _payload.ptr[newLength .. _payload.length]) 436 .destroy(e); 437 438 _payload = _payload.ptr[0 .. newLength]; 439 return; 440 } 441 442 static if (__traits(compiles, { static T _; })) 443 { 444 import std.algorithm.mutation : initializeAll; 445 446 immutable startEmplace = length; 447 reserve(newLength); 448 initializeAll(_payload.ptr[startEmplace .. newLength]); 449 _payload = _payload.ptr[0 .. newLength]; 450 } 451 else 452 { 453 assert(0, "Cannot add elements to array because `" ~ 454 fullyQualifiedName!T ~ ".this()` is annotated with " ~ 455 "`@disable`."); 456 } 457 } 458 459 @property size_t capacity() const 460 { 461 return _capacity; 462 } 463 464 void reserve(size_t elements) 465 { 466 if (elements <= capacity) return; 467 static if (T.sizeof == 1) 468 { 469 const sz = elements; 470 } 471 else 472 { 473 import core.checkedint : mulu; 474 bool overflow; 475 const sz = mulu(elements, T.sizeof, overflow); 476 if (overflow) 477 assert(false, "Overflow"); 478 } 479 static if (hasIndirections!T) 480 { 481 /* Because of the transactional nature of this 482 * relative to the garbage collector, ensure no 483 * threading bugs by using malloc/copy/free rather 484 * than realloc. 485 */ 486 immutable oldLength = length; 487 488 auto newPayloadPtr = cast(T*) enforceMalloc(sz); 489 auto newPayload = newPayloadPtr[0 .. oldLength]; 490 491 // copy old data over to new array 492 memcpy(newPayload.ptr, _payload.ptr, T.sizeof * oldLength); 493 // Zero out unused capacity to prevent gc from seeing false pointers 494 memset(newPayload.ptr + oldLength, 495 0, 496 (elements - oldLength) * T.sizeof); 497 GC.addRange(newPayload.ptr, sz); 498 GC.removeRange(_payload.ptr); 499 free(_payload.ptr); 500 _payload = newPayload; 501 } 502 else 503 { 504 // These can't have pointers, so no need to zero unused region 505 auto newPayloadPtr = cast(T*) enforceRealloc(_payload.ptr, sz); 506 auto newPayload = newPayloadPtr[0 .. length]; 507 _payload = newPayload; 508 } 509 _capacity = elements; 510 } 511 512 // Insert one item 513 size_t insertBack(Elem)(Elem elem) 514 if (isImplicitlyConvertible!(Elem, T)) 515 { 516 import core.lifetime : emplace; 517 assert(_capacity >= length); 518 if (_capacity == length) 519 { 520 import core.checkedint : addu; 521 522 bool overflow; 523 immutable size_t newCapacity = addu(capacity, capacity / 2 + 1, overflow); 524 if (overflow) 525 assert(false, "Overflow"); 526 527 reserve(newCapacity); 528 } 529 assert(capacity > length && _payload.ptr, 530 "Failed to reserve memory"); 531 emplace(_payload.ptr + _payload.length, elem); 532 _payload = _payload.ptr[0 .. _payload.length + 1]; 533 return 1; 534 } 535 536 // Insert a range of items 537 size_t insertBack(Range)(Range r) 538 if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T)) 539 { 540 immutable size_t oldLength = length; 541 542 static if (hasLength!Range) 543 { 544 immutable size_t rLength = r.length; 545 reserve(oldLength + rLength); 546 } 547 548 size_t result; 549 foreach (item; r) 550 { 551 insertBack(item); 552 ++result; 553 } 554 555 static if (hasLength!Range) 556 assert(result == rLength, "insertBack: range might have changed length"); 557 558 assert(length == oldLength + result, 559 "Failed to insertBack range"); 560 561 return result; 562 } 563 } 564 private alias Data = RefCounted!(Payload, RefCountedAutoInitialize.no); 565 private Data _data; 566 567 /** 568 * Constructor taking a number of items. 569 */ 570 this(U)(U[] values...) 571 if (isImplicitlyConvertible!(U, T)) 572 { 573 // [2021-07-17] Checking to see whether *always* calling ensureInitialized works-around-and/or-is-related-to https://issues.dlang.org/show_bug.cgihttps://issues.dlang.org/show_bug.cgi... 574 //if (values.length) 575 { 576 _data.refCountedStore.ensureInitialized(); 577 _data.reserve(values.length); 578 foreach (ref value; values) 579 { 580 // We do not simply write "_data.insertBack(value);" 581 // because that might perform, on each iteration, a now-redundant check of length vs capacity. 582 // Thanks to @dkorpel (https://github.com/dlang/phobos/pull/8162#discussion_r667479090). 583 584 import core.lifetime : emplace; 585 emplace(_data._payload.ptr + _data._payload.length, value); 586 587 // We increment the length after each iteration (as opposed to adjusting it just once, after the loop) 588 // in order to improve error-safety (in case one of the calls to emplace throws). 589 _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1]; 590 } 591 } 592 593 assert(length == values.length); // We check that all values have been inserted. 594 assert(capacity == values.length); // We check that reserve has been called before the loop. 595 } 596 597 /** 598 * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 599 */ 600 this(Range)(Range r) 601 if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[])) 602 { 603 insertBack(r); 604 } 605 606 /** 607 * Comparison for equality. 608 */ 609 bool opEquals(const Array rhs) const 610 { 611 return opEquals(rhs); 612 } 613 614 /// ditto 615 bool opEquals(ref const Array rhs) const 616 { 617 if (empty) return rhs.empty; 618 if (rhs.empty) return false; 619 return _data._payload == rhs._data._payload; 620 } 621 622 /** 623 * Defines the array's primary range, which is a random-access range. 624 * 625 * `ConstRange` is a variant with `const` elements. 626 * `ImmutableRange` is a variant with `immutable` elements. 627 */ 628 alias Range = RangeT!Array; 629 630 /// ditto 631 alias ConstRange = RangeT!(const Array); 632 633 /// ditto 634 alias ImmutableRange = RangeT!(immutable Array); 635 636 /** 637 * Duplicates the array. The elements themselves are not transitively 638 * duplicated. 639 * 640 * Complexity: $(BIGOH length). 641 */ 642 @property Array dup() 643 { 644 if (!_data.refCountedStore.isInitialized) return this; 645 return Array(_data._payload); 646 } 647 648 /** 649 * Returns: `true` if and only if the array has no elements. 650 * 651 * Complexity: $(BIGOH 1) 652 */ 653 @property bool empty() const 654 { 655 return !_data.refCountedStore.isInitialized || _data._payload.empty; 656 } 657 658 /** 659 * Returns: The number of elements in the array. 660 * 661 * Complexity: $(BIGOH 1). 662 */ 663 @property size_t length() const 664 { 665 return _data.refCountedStore.isInitialized ? _data._payload.length : 0; 666 } 667 668 /// ditto 669 size_t opDollar() const 670 { 671 return length; 672 } 673 674 /** 675 * Returns: The maximum number of elements the array can store without 676 * reallocating memory and invalidating iterators upon insertion. 677 * 678 * Complexity: $(BIGOH 1) 679 */ 680 @property size_t capacity() 681 { 682 return _data.refCountedStore.isInitialized ? _data._capacity : 0; 683 } 684 685 /** 686 * Returns: the internal representation of the array. 687 * 688 * Complexity: $(BIGOH 1). 689 */ 690 691 inout(T)[] data() inout @system 692 { 693 return _data.refCountedStore.isInitialized ? _data._payload : []; 694 } 695 696 /** 697 * Ensures sufficient capacity to accommodate `e` _elements. 698 * If `e < capacity`, this method does nothing. 699 * 700 * Postcondition: `capacity >= e` 701 * 702 * Note: If the capacity is increased, one should assume that all 703 * iterators to the elements are invalidated. 704 * 705 * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). 706 */ 707 void reserve(size_t elements) 708 { 709 if (elements > capacity) 710 { 711 _data.refCountedStore.ensureInitialized(); 712 _data.reserve(elements); 713 assert(capacity == elements); // Might need changing to ">=" if implementation of Payload.reserve is changed. 714 } 715 } 716 717 /** 718 * Returns: A range that iterates over elements of the array in 719 * forward order. 720 * 721 * Complexity: $(BIGOH 1) 722 */ 723 Range opSlice() 724 { 725 return typeof(return)(this, 0, length); 726 } 727 728 ConstRange opSlice() const 729 { 730 return typeof(return)(this, 0, length); 731 } 732 733 ImmutableRange opSlice() immutable 734 { 735 return typeof(return)(this, 0, length); 736 } 737 738 /** 739 * Returns: A range that iterates over elements of the array from 740 * index `i` up to (excluding) index `j`. 741 * 742 * Precondition: `i <= j && j <= length` 743 * 744 * Complexity: $(BIGOH 1) 745 */ 746 Range opSlice(size_t i, size_t j) 747 { 748 assert(i <= j && j <= length, "Invalid slice bounds"); 749 return typeof(return)(this, i, j); 750 } 751 752 ConstRange opSlice(size_t i, size_t j) const 753 { 754 assert(i <= j && j <= length, "Invalid slice bounds"); 755 return typeof(return)(this, i, j); 756 } 757 758 ImmutableRange opSlice(size_t i, size_t j) immutable 759 { 760 assert(i <= j && j <= length, "Invalid slice bounds"); 761 return typeof(return)(this, i, j); 762 } 763 764 /** 765 * Returns: The first element of the array. 766 * 767 * Precondition: `empty == false` 768 * 769 * Complexity: $(BIGOH 1) 770 */ 771 @property ref inout(T) front() inout 772 { 773 assert(_data.refCountedStore.isInitialized, 774 "Cannot get front of empty range"); 775 return _data._payload[0]; 776 } 777 778 /** 779 * Returns: The last element of the array. 780 * 781 * Precondition: `empty == false` 782 * 783 * Complexity: $(BIGOH 1) 784 */ 785 @property ref inout(T) back() inout 786 { 787 assert(_data.refCountedStore.isInitialized, 788 "Cannot get back of empty range"); 789 return _data._payload[$ - 1]; 790 } 791 792 /** 793 * Returns: The element or a reference to the element at the specified index. 794 * 795 * Precondition: `i < length` 796 * 797 * Complexity: $(BIGOH 1) 798 */ 799 ref inout(T) opIndex(size_t i) inout 800 { 801 assert(_data.refCountedStore.isInitialized, 802 "Cannot index empty range"); 803 return _data._payload[i]; 804 } 805 806 /** 807 * Slicing operators executing the specified operation on the entire slice. 808 * 809 * Precondition: `i < j && j < length` 810 * 811 * Complexity: $(BIGOH slice.length) 812 */ 813 void opSliceAssign(T value) 814 { 815 if (!_data.refCountedStore.isInitialized) return; 816 _data._payload[] = value; 817 } 818 819 /// ditto 820 void opSliceAssign(T value, size_t i, size_t j) 821 { 822 auto slice = _data.refCountedStore.isInitialized ? 823 _data._payload : 824 T[].init; 825 slice[i .. j] = value; 826 } 827 828 /// ditto 829 void opSliceUnary(string op)() 830 if (op == "++" || op == "--") 831 { 832 if (!_data.refCountedStore.isInitialized) return; 833 mixin(op~"_data._payload[];"); 834 } 835 836 /// ditto 837 void opSliceUnary(string op)(size_t i, size_t j) 838 if (op == "++" || op == "--") 839 { 840 auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; 841 mixin(op~"slice[i .. j];"); 842 } 843 844 /// ditto 845 void opSliceOpAssign(string op)(T value) 846 { 847 if (!_data.refCountedStore.isInitialized) return; 848 mixin("_data._payload[] "~op~"= value;"); 849 } 850 851 /// ditto 852 void opSliceOpAssign(string op)(T value, size_t i, size_t j) 853 { 854 auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; 855 mixin("slice[i .. j] "~op~"= value;"); 856 } 857 858 private enum hasSliceWithLength(T) = is(typeof({ T t = T.init; t[].length; })); 859 860 /** 861 * Returns: A new array which is a concatenation of `this` and its argument. 862 * 863 * Complexity: 864 * $(BIGOH length + m), where `m` is the number of elements in `stuff`. 865 */ 866 Array opBinary(string op, Stuff)(Stuff stuff) 867 if (op == "~") 868 { 869 Array result; 870 871 static if (hasLength!Stuff || isNarrowString!Stuff) 872 result.reserve(length + stuff.length); 873 else static if (hasSliceWithLength!Stuff) 874 result.reserve(length + stuff[].length); 875 else static if (isImplicitlyConvertible!(Stuff, T)) 876 result.reserve(length + 1); 877 878 result.insertBack(this[]); 879 result ~= stuff; 880 return result; 881 } 882 883 /** 884 * Forwards to `insertBack`. 885 */ 886 void opOpAssign(string op, Stuff)(auto ref Stuff stuff) 887 if (op == "~") 888 { 889 static if (is(typeof(stuff[])) && isImplicitlyConvertible!(typeof(stuff[0]), T)) 890 { 891 insertBack(stuff[]); 892 } 893 else 894 { 895 insertBack(stuff); 896 } 897 } 898 899 /** 900 * Removes all the elements from the array and releases allocated memory. 901 * 902 * Postcondition: `empty == true && capacity == 0` 903 * 904 * Complexity: $(BIGOH length) 905 */ 906 void clear() 907 { 908 _data = Data.init; 909 } 910 911 /** 912 * Sets the number of elements in the array to `newLength`. If `newLength` 913 * is greater than `length`, the new elements are added to the end of the 914 * array and initialized with `T.init`. If `T` is a `struct` whose default 915 * constructor is annotated with `@disable`, `newLength` must be lower than 916 * or equal to `length`. 917 * 918 * Complexity: 919 * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. 920 * If `capacity < newLength` the worst case is $(BIGOH newLength). 921 * 922 * Precondition: `__traits(compiles, { static T _; }) || newLength <= length` 923 * 924 * Postcondition: `length == newLength` 925 */ 926 @property void length(size_t newLength) 927 { 928 _data.refCountedStore.ensureInitialized(); 929 _data.length = newLength; 930 } 931 932 /** 933 * Removes the last element from the array and returns it. 934 * Both stable and non-stable versions behave the same and guarantee 935 * that ranges iterating over the array are never invalidated. 936 * 937 * Precondition: `empty == false` 938 * 939 * Returns: The element removed. 940 * 941 * Complexity: $(BIGOH 1). 942 * 943 * Throws: `Exception` if the array is empty. 944 */ 945 T removeAny() 946 { 947 auto result = back; 948 removeBack(); 949 return result; 950 } 951 952 /// ditto 953 alias stableRemoveAny = removeAny; 954 955 /** 956 * Inserts the specified elements at the back of the array. `stuff` can be 957 * a value convertible to `T` or a range of objects convertible to `T`. 958 * 959 * Returns: The number of elements inserted. 960 * 961 * Complexity: 962 * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), 963 * where `m` is the number of elements in `stuff`. 964 */ 965 size_t insertBack(Stuff)(Stuff stuff) 966 if (isImplicitlyConvertible!(Stuff, T) || 967 isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 968 { 969 _data.refCountedStore.ensureInitialized(); 970 return _data.insertBack(stuff); 971 } 972 973 /// ditto 974 alias insert = insertBack; 975 976 /** 977 * Removes the value from the back of the array. Both stable and non-stable 978 * versions behave the same and guarantee that ranges iterating over the 979 * array are never invalidated. 980 * 981 * Precondition: `empty == false` 982 * 983 * Complexity: $(BIGOH 1). 984 * 985 * Throws: `Exception` if the array is empty. 986 */ 987 void removeBack() 988 { 989 enforce(!empty); 990 static if (hasElaborateDestructor!T) 991 .destroy(_data._payload[$ - 1]); 992 993 _data._payload = _data._payload[0 .. $ - 1]; 994 } 995 996 /// ditto 997 alias stableRemoveBack = removeBack; 998 999 /** 1000 * Removes `howMany` values from the back of the array. 1001 * Unlike the unparameterized versions above, these functions 1002 * do not throw if they could not remove `howMany` elements. Instead, 1003 * if `howMany > n`, all elements are removed. The returned value is 1004 * the effective number of elements removed. Both stable and non-stable 1005 * versions behave the same and guarantee that ranges iterating over 1006 * the array are never invalidated. 1007 * 1008 * Returns: The number of elements removed. 1009 * 1010 * Complexity: $(BIGOH howMany). 1011 */ 1012 size_t removeBack(size_t howMany) 1013 { 1014 if (howMany > length) howMany = length; 1015 static if (hasElaborateDestructor!T) 1016 foreach (ref e; _data._payload[$ - howMany .. $]) 1017 .destroy(e); 1018 1019 _data._payload = _data._payload[0 .. $ - howMany]; 1020 return howMany; 1021 } 1022 1023 /// ditto 1024 alias stableRemoveBack = removeBack; 1025 1026 /** 1027 * Inserts `stuff` before, after, or instead range `r`, which must 1028 * be a valid range previously extracted from this array. `stuff` 1029 * can be a value convertible to `T` or a range of objects convertible 1030 * to `T`. Both stable and non-stable version behave the same and 1031 * guarantee that ranges iterating over the array are never invalidated. 1032 * 1033 * Returns: The number of values inserted. 1034 * 1035 * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. 1036 * 1037 * Throws: `Exception` if `r` is not a range extracted from this array. 1038 */ 1039 size_t insertBefore(Stuff)(Range r, Stuff stuff) 1040 if (isImplicitlyConvertible!(Stuff, T)) 1041 { 1042 import core.lifetime : emplace; 1043 enforce(r._outer._data is _data && r._a <= length); 1044 reserve(length + 1); 1045 assert(_data.refCountedStore.isInitialized, 1046 "Failed to allocate capacity to insertBefore"); 1047 // Move elements over by one slot 1048 memmove(_data._payload.ptr + r._a + 1, 1049 _data._payload.ptr + r._a, 1050 T.sizeof * (length - r._a)); 1051 emplace(_data._payload.ptr + r._a, stuff); 1052 _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1]; 1053 return 1; 1054 } 1055 1056 /// ditto 1057 size_t insertBefore(Stuff)(Range r, Stuff stuff) 1058 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 1059 { 1060 import core.lifetime : emplace; 1061 enforce(r._outer._data is _data && r._a <= length); 1062 static if (isForwardRange!Stuff) 1063 { 1064 // Can find the length in advance 1065 auto extra = walkLength(stuff); 1066 if (!extra) return 0; 1067 reserve(length + extra); 1068 assert(_data.refCountedStore.isInitialized, 1069 "Failed to allocate capacity to insertBefore"); 1070 // Move elements over by extra slots 1071 memmove(_data._payload.ptr + r._a + extra, 1072 _data._payload.ptr + r._a, 1073 T.sizeof * (length - r._a)); 1074 foreach (p; _data._payload.ptr + r._a .. 1075 _data._payload.ptr + r._a + extra) 1076 { 1077 emplace(p, stuff.front); 1078 stuff.popFront(); 1079 } 1080 _data._payload = 1081 _data._payload.ptr[0 .. _data._payload.length + extra]; 1082 return extra; 1083 } 1084 else 1085 { 1086 import std.algorithm.mutation : bringToFront; 1087 enforce(_data); 1088 immutable offset = r._a; 1089 enforce(offset <= length); 1090 auto result = insertBack(stuff); 1091 bringToFront(this[offset .. length - result], 1092 this[length - result .. length]); 1093 return result; 1094 } 1095 } 1096 1097 /// ditto 1098 alias stableInsertBefore = insertBefore; 1099 1100 /// ditto 1101 size_t insertAfter(Stuff)(Range r, Stuff stuff) 1102 if (isImplicitlyConvertible!(Stuff, T) || 1103 isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 1104 { 1105 import std.algorithm.mutation : bringToFront; 1106 enforce(r._outer._data is _data); 1107 // TODO: optimize 1108 immutable offset = r._b; 1109 enforce(offset <= length); 1110 auto result = insertBack(stuff); 1111 bringToFront(this[offset .. length - result], 1112 this[length - result .. length]); 1113 return result; 1114 } 1115 1116 /// ditto 1117 size_t replace(Stuff)(Range r, Stuff stuff) 1118 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 1119 { 1120 enforce(r._outer._data is _data); 1121 size_t result; 1122 for (; !stuff.empty; stuff.popFront()) 1123 { 1124 if (r.empty) 1125 { 1126 // insert the rest 1127 return result + insertBefore(r, stuff); 1128 } 1129 r.front = stuff.front; 1130 r.popFront(); 1131 ++result; 1132 } 1133 // Remove remaining stuff in r 1134 linearRemove(r); 1135 return result; 1136 } 1137 1138 /// ditto 1139 size_t replace(Stuff)(Range r, Stuff stuff) 1140 if (isImplicitlyConvertible!(Stuff, T)) 1141 { 1142 enforce(r._outer._data is _data); 1143 if (r.empty) 1144 { 1145 insertBefore(r, stuff); 1146 } 1147 else 1148 { 1149 r.front = stuff; 1150 r.popFront(); 1151 linearRemove(r); 1152 } 1153 return 1; 1154 } 1155 1156 /** 1157 * Removes all elements belonging to `r`, which must be a range 1158 * obtained originally from this array. 1159 * 1160 * Returns: A range spanning the remaining elements in the array that 1161 * initially were right after `r`. 1162 * 1163 * Complexity: $(BIGOH length) 1164 * 1165 * Throws: `Exception` if `r` is not a valid range extracted from this array. 1166 */ 1167 Range linearRemove(Range r) 1168 { 1169 import std.algorithm.mutation : copy; 1170 1171 enforce(r._outer._data is _data); 1172 enforce(_data.refCountedStore.isInitialized); 1173 enforce(r._a <= r._b && r._b <= length); 1174 immutable offset1 = r._a; 1175 immutable offset2 = r._b; 1176 immutable tailLength = length - offset2; 1177 // Use copy here, not a[] = b[] because the ranges may overlap 1178 copy(this[offset2 .. length], this[offset1 .. offset1 + tailLength]); 1179 length = offset1 + tailLength; 1180 return this[length - tailLength .. length]; 1181 } 1182 } 1183 1184 @system unittest 1185 { 1186 Array!int a; 1187 assert(a.empty); 1188 } 1189 1190 @system unittest 1191 { 1192 Array!int a; 1193 a.length = 10; 1194 assert(a.length == 10); 1195 assert(a.capacity >= a.length); 1196 } 1197 1198 @system unittest 1199 { 1200 struct Dumb { int x = 5; } 1201 Array!Dumb a; 1202 a.length = 10; 1203 assert(a.length == 10); 1204 assert(a.capacity >= a.length); 1205 immutable cap = a.capacity; 1206 foreach (ref e; a) 1207 e.x = 10; 1208 a.length = 5; 1209 assert(a.length == 5); 1210 // do not realloc if length decreases 1211 assert(a.capacity == cap); 1212 foreach (ref e; a) 1213 assert(e.x == 10); 1214 1215 a.length = 8; 1216 assert(a.length == 8); 1217 // do not realloc if capacity sufficient 1218 assert(a.capacity == cap); 1219 assert(Dumb.init.x == 5); 1220 foreach (i; 0 .. 5) 1221 assert(a[i].x == 10); 1222 foreach (i; 5 .. a.length) 1223 assert(a[i].x == Dumb.init.x); 1224 1225 // realloc required, check if values properly copied 1226 a[] = Dumb(1); 1227 a.length = 20; 1228 assert(a.capacity >= 20); 1229 foreach (i; 0 .. 8) 1230 assert(a[i].x == 1); 1231 foreach (i; 8 .. a.length) 1232 assert(a[i].x == Dumb.init.x); 1233 1234 // check if overlapping elements properly initialized 1235 a.length = 1; 1236 a.length = 20; 1237 assert(a[0].x == 1); 1238 foreach (e; a[1 .. $]) 1239 assert(e.x == Dumb.init.x); 1240 } 1241 1242 @system unittest 1243 { 1244 Array!int a = Array!int(1, 2, 3); 1245 //a._data._refCountedDebug = true; 1246 auto b = a.dup; 1247 assert(b == Array!int(1, 2, 3)); 1248 b.front = 42; 1249 assert(b == Array!int(42, 2, 3)); 1250 assert(a == Array!int(1, 2, 3)); 1251 } 1252 1253 @system unittest 1254 { 1255 auto a = Array!int(1, 2, 3); 1256 assert(a.length == 3); 1257 } 1258 1259 @system unittest 1260 { 1261 const Array!int a = [1, 2]; 1262 1263 assert(a[0] == 1); 1264 assert(a.front == 1); 1265 assert(a.back == 2); 1266 1267 static assert(!__traits(compiles, { a[0] = 1; })); 1268 static assert(!__traits(compiles, { a.front = 1; })); 1269 static assert(!__traits(compiles, { a.back = 1; })); 1270 1271 auto r = a[]; 1272 size_t i; 1273 foreach (e; r) 1274 { 1275 assert(e == i + 1); 1276 i++; 1277 } 1278 } 1279 1280 @safe unittest 1281 { 1282 // https://issues.dlang.org/show_bug.cgi?id=13621 1283 import std.container : Array, BinaryHeap; 1284 alias Heap = BinaryHeap!(Array!int); 1285 } 1286 1287 @system unittest 1288 { 1289 // https://issues.dlang.org/show_bug.cgi?id=18800 1290 static struct S { void* p; } 1291 Array!S a; 1292 a.length = 10; 1293 } 1294 1295 @system unittest 1296 { 1297 Array!int a; 1298 a.reserve(1000); 1299 assert(a.length == 0); 1300 assert(a.empty); 1301 assert(a.capacity >= 1000); 1302 auto p = a._data._payload.ptr; 1303 foreach (i; 0 .. 1000) 1304 { 1305 a.insertBack(i); 1306 } 1307 assert(p == a._data._payload.ptr); 1308 } 1309 1310 @system unittest 1311 { 1312 auto a = Array!int(1, 2, 3); 1313 a[1] *= 42; 1314 assert(a[1] == 84); 1315 } 1316 1317 @system unittest 1318 { 1319 auto a = Array!int(1, 2, 3); 1320 auto b = Array!int(11, 12, 13); 1321 auto c = a ~ b; 1322 assert(c == Array!int(1, 2, 3, 11, 12, 13)); 1323 assert(a ~ b[] == Array!int(1, 2, 3, 11, 12, 13)); 1324 assert(a ~ [4,5] == Array!int(1,2,3,4,5)); 1325 } 1326 1327 @system unittest 1328 { 1329 auto a = Array!int(1, 2, 3); 1330 auto b = Array!int(11, 12, 13); 1331 a ~= b; 1332 assert(a == Array!int(1, 2, 3, 11, 12, 13)); 1333 } 1334 1335 @system unittest 1336 { 1337 auto a = Array!int(1, 2, 3, 4); 1338 assert(a.removeAny() == 4); 1339 assert(a == Array!int(1, 2, 3)); 1340 } 1341 1342 @system unittest 1343 { 1344 auto a = Array!int(1, 2, 3, 4, 5); 1345 auto r = a[2 .. a.length]; 1346 assert(a.insertBefore(r, 42) == 1); 1347 assert(a == Array!int(1, 2, 42, 3, 4, 5)); 1348 r = a[2 .. 2]; 1349 assert(a.insertBefore(r, [8, 9]) == 2); 1350 assert(a == Array!int(1, 2, 8, 9, 42, 3, 4, 5)); 1351 } 1352 1353 @system unittest 1354 { 1355 auto a = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8); 1356 a.linearRemove(a[4 .. 6]); 1357 assert(a == Array!int(0, 1, 2, 3, 6, 7, 8)); 1358 } 1359 1360 // Give the Range object some testing. 1361 @system unittest 1362 { 1363 import std.algorithm.comparison : equal; 1364 import std.range : retro; 1365 auto a = Array!int(0, 1, 2, 3, 4, 5, 6)[]; 1366 auto b = Array!int(6, 5, 4, 3, 2, 1, 0)[]; 1367 alias A = typeof(a); 1368 1369 static assert(isRandomAccessRange!A); 1370 static assert(hasSlicing!A); 1371 static assert(hasAssignableElements!A); 1372 static assert(hasMobileElements!A); 1373 1374 assert(equal(retro(b), a)); 1375 assert(a.length == 7); 1376 assert(equal(a[1 .. 4], [1, 2, 3])); 1377 } 1378 1379 // https://issues.dlang.org/show_bug.cgi?id=5920 1380 @system unittest 1381 { 1382 struct structBug5920 1383 { 1384 int order; 1385 uint* pDestructionMask; 1386 ~this() 1387 { 1388 if (pDestructionMask) 1389 *pDestructionMask += 1 << order; 1390 } 1391 } 1392 1393 alias S = structBug5920; 1394 uint dMask; 1395 1396 auto arr = Array!S(cast(S[])[]); 1397 foreach (i; 0 .. 8) 1398 arr.insertBack(S(i, &dMask)); 1399 // don't check dMask now as S may be copied multiple times (it's ok?) 1400 { 1401 assert(arr.length == 8); 1402 dMask = 0; 1403 arr.length = 6; 1404 assert(arr.length == 6); // make sure shrinking calls the d'tor 1405 assert(dMask == 0b1100_0000); 1406 arr.removeBack(); 1407 assert(arr.length == 5); // make sure removeBack() calls the d'tor 1408 assert(dMask == 0b1110_0000); 1409 arr.removeBack(3); 1410 assert(arr.length == 2); // ditto 1411 assert(dMask == 0b1111_1100); 1412 arr.clear(); 1413 assert(arr.length == 0); // make sure clear() calls the d'tor 1414 assert(dMask == 0b1111_1111); 1415 } 1416 assert(dMask == 0b1111_1111); // make sure the d'tor is called once only. 1417 } 1418 1419 // Test for https://issues.dlang.org/show_bug.cgi?id=5792 1420 // (mainly just to check if this piece of code is compilable) 1421 @system unittest 1422 { 1423 auto a = Array!(int[])([[1,2],[3,4]]); 1424 a.reserve(4); 1425 assert(a.capacity >= 4); 1426 assert(a.length == 2); 1427 assert(a[0] == [1,2]); 1428 assert(a[1] == [3,4]); 1429 a.reserve(16); 1430 assert(a.capacity >= 16); 1431 assert(a.length == 2); 1432 assert(a[0] == [1,2]); 1433 assert(a[1] == [3,4]); 1434 } 1435 1436 // test replace!Stuff with range Stuff 1437 @system unittest 1438 { 1439 import std.algorithm.comparison : equal; 1440 auto a = Array!int([1, 42, 5]); 1441 a.replace(a[1 .. 2], [2, 3, 4]); 1442 assert(equal(a[], [1, 2, 3, 4, 5])); 1443 } 1444 1445 // test insertBefore and replace with empty Arrays 1446 @system unittest 1447 { 1448 import std.algorithm.comparison : equal; 1449 auto a = Array!int(); 1450 a.insertBefore(a[], 1); 1451 assert(equal(a[], [1])); 1452 } 1453 @system unittest 1454 { 1455 import std.algorithm.comparison : equal; 1456 auto a = Array!int(); 1457 a.insertBefore(a[], [1, 2]); 1458 assert(equal(a[], [1, 2])); 1459 } 1460 @system unittest 1461 { 1462 import std.algorithm.comparison : equal; 1463 auto a = Array!int(); 1464 a.replace(a[], [1, 2]); 1465 assert(equal(a[], [1, 2])); 1466 } 1467 @system unittest 1468 { 1469 import std.algorithm.comparison : equal; 1470 auto a = Array!int(); 1471 a.replace(a[], 1); 1472 assert(equal(a[], [1])); 1473 } 1474 // make sure that Array instances refuse ranges that don't belong to them 1475 @system unittest 1476 { 1477 import std.exception : assertThrown; 1478 1479 Array!int a = [1, 2, 3]; 1480 auto r = a.dup[]; 1481 assertThrown(a.insertBefore(r, 42)); 1482 assertThrown(a.insertBefore(r, [42])); 1483 assertThrown(a.insertAfter(r, 42)); 1484 assertThrown(a.replace(r, 42)); 1485 assertThrown(a.replace(r, [42])); 1486 assertThrown(a.linearRemove(r)); 1487 } 1488 @system unittest 1489 { 1490 auto a = Array!int([1, 1]); 1491 a[1] = 0; //Check Array.opIndexAssign 1492 assert(a[1] == 0); 1493 a[1] += 1; //Check Array.opIndexOpAssign 1494 assert(a[1] == 1); 1495 1496 //Check Array.opIndexUnary 1497 ++a[0]; 1498 //a[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed 1499 assert(a[0] == 2); 1500 assert(+a[0] == +2); 1501 assert(-a[0] == -2); 1502 assert(~a[0] == ~2); 1503 1504 auto r = a[]; 1505 r[1] = 0; //Check Array.Range.opIndexAssign 1506 assert(r[1] == 0); 1507 r[1] += 1; //Check Array.Range.opIndexOpAssign 1508 assert(r[1] == 1); 1509 1510 //Check Array.Range.opIndexUnary 1511 ++r[0]; 1512 //r[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed 1513 assert(r[0] == 3); 1514 assert(+r[0] == +3); 1515 assert(-r[0] == -3); 1516 assert(~r[0] == ~3); 1517 } 1518 1519 @system unittest 1520 { 1521 import std.algorithm.comparison : equal; 1522 1523 //Test "array-wide" operations 1524 auto a = Array!int([0, 1, 2]); //Array 1525 a[] += 5; 1526 assert(a[].equal([5, 6, 7])); 1527 ++a[]; 1528 assert(a[].equal([6, 7, 8])); 1529 a[1 .. 3] *= 5; 1530 assert(a[].equal([6, 35, 40])); 1531 a[0 .. 2] = 0; 1532 assert(a[].equal([0, 0, 40])); 1533 1534 //Test empty array 1535 auto a2 = Array!int.init; 1536 ++a2[]; 1537 ++a2[0 .. 0]; 1538 a2[] = 0; 1539 a2[0 .. 0] = 0; 1540 a2[] += 0; 1541 a2[0 .. 0] += 0; 1542 1543 //Test "range-wide" operations 1544 auto r = Array!int([0, 1, 2])[]; //Array.Range 1545 r[] += 5; 1546 assert(r.equal([5, 6, 7])); 1547 ++r[]; 1548 assert(r.equal([6, 7, 8])); 1549 r[1 .. 3] *= 5; 1550 assert(r.equal([6, 35, 40])); 1551 r[0 .. 2] = 0; 1552 assert(r.equal([0, 0, 40])); 1553 1554 //Test empty Range 1555 auto r2 = Array!int.init[]; 1556 ++r2[]; 1557 ++r2[0 .. 0]; 1558 r2[] = 0; 1559 r2[0 .. 0] = 0; 1560 r2[] += 0; 1561 r2[0 .. 0] += 0; 1562 } 1563 1564 // Test issue 11194 1565 @system unittest 1566 { 1567 static struct S { 1568 int i = 1337; 1569 void* p; 1570 this(this) { assert(i == 1337); } 1571 ~this() { assert(i == 1337); } 1572 } 1573 Array!S arr; 1574 S s; 1575 arr ~= s; 1576 arr ~= s; 1577 } 1578 1579 // https://issues.dlang.org/show_bug.cgi?id=11459 1580 @safe unittest 1581 { 1582 static struct S 1583 { 1584 bool b; 1585 alias b this; 1586 } 1587 alias A = Array!S; 1588 alias B = Array!(shared bool); 1589 } 1590 1591 // https://issues.dlang.org/show_bug.cgi?id=11884 1592 @system unittest 1593 { 1594 import std.algorithm.iteration : filter; 1595 auto a = Array!int([1, 2, 2].filter!"true"()); 1596 } 1597 1598 // https://issues.dlang.org/show_bug.cgi?id=8282 1599 @safe unittest 1600 { 1601 auto arr = new Array!int; 1602 } 1603 1604 // https://issues.dlang.org/show_bug.cgi?id=6998 1605 @system unittest 1606 { 1607 static int i = 0; 1608 class C 1609 { 1610 int dummy = 1; 1611 this(){++i;} 1612 ~this(){--i;} 1613 } 1614 1615 assert(i == 0); 1616 auto c = new C(); 1617 assert(i == 1); 1618 1619 //scope 1620 { 1621 auto arr = Array!C(c); 1622 assert(i == 1); 1623 } 1624 //Array should not have destroyed the class instance 1625 assert(i == 1); 1626 1627 //Just to make sure the GC doesn't collect before the above test. 1628 assert(c.dummy == 1); 1629 } 1630 1631 //https://issues.dlang.org/show_bug.cgi?id=6998 (2) 1632 @system unittest 1633 { 1634 static class C {int i;} 1635 auto c = new C; 1636 c.i = 42; 1637 Array!C a; 1638 a ~= c; 1639 a.clear; 1640 assert(c.i == 42); //fails 1641 } 1642 1643 @safe unittest 1644 { 1645 static assert(is(Array!int.Range)); 1646 static assert(is(Array!int.ConstRange)); 1647 } 1648 1649 @system unittest // const/immutable Array and Ranges 1650 { 1651 static void test(A, R, E, S)() 1652 { 1653 A a; 1654 R r = a[]; 1655 assert(r.empty); 1656 assert(r.length == 0); 1657 static assert(is(typeof(r.front) == E)); 1658 static assert(is(typeof(r.back) == E)); 1659 static assert(is(typeof(r[0]) == E)); 1660 static assert(is(typeof(r[]) == S)); 1661 static assert(is(typeof(r[0 .. 0]) == S)); 1662 } 1663 1664 alias A = Array!int; 1665 1666 test!(A, A.Range, int, A.Range); 1667 test!(A, const A.Range, const int, A.ConstRange); 1668 1669 test!(const A, A.ConstRange, const int, A.ConstRange); 1670 test!(const A, const A.ConstRange, const int, A.ConstRange); 1671 1672 test!(immutable A, A.ImmutableRange, immutable int, A.ImmutableRange); 1673 test!(immutable A, const A.ImmutableRange, immutable int, A.ImmutableRange); 1674 test!(immutable A, immutable A.ImmutableRange, immutable int, 1675 A.ImmutableRange); 1676 } 1677 1678 // ensure @nogc 1679 @nogc @system unittest 1680 { 1681 Array!int ai; 1682 ai ~= 1; 1683 assert(ai.front == 1); 1684 1685 ai.reserve(10); 1686 assert(ai.capacity == 10); 1687 1688 static immutable arr = [1, 2, 3]; 1689 ai.insertBack(arr); 1690 } 1691 1692 /* 1693 * typeof may give wrong result in case of classes defining `opCall` operator 1694 * https://issues.dlang.org/show_bug.cgi?id=20589 1695 * 1696 * destructor std.container.array.Array!(MyClass).Array.~this is @system 1697 * so the unittest is @system too 1698 */ 1699 @system unittest 1700 { 1701 class MyClass 1702 { 1703 T opCall(T)(T p) 1704 { 1705 return p; 1706 } 1707 } 1708 1709 Array!MyClass arr; 1710 } 1711 1712 @system unittest 1713 { 1714 import std.algorithm.comparison : equal; 1715 auto a = Array!int([1,2,3,4,5]); 1716 assert(a.length == 5); 1717 1718 assert(a.insertAfter(a[0 .. 5], [7, 8]) == 2); 1719 assert(equal(a[], [1,2,3,4,5,7,8])); 1720 1721 assert(a.insertAfter(a[0 .. 5], 6) == 1); 1722 assert(equal(a[], [1,2,3,4,5,6,7,8])); 1723 } 1724 1725 // https://issues.dlang.org/show_bug.cgi?id=22105 1726 @system unittest 1727 { 1728 import core.exception : AssertError; 1729 import std.exception : assertThrown, assertNotThrown; 1730 1731 struct NoDefaultCtor 1732 { 1733 int i; 1734 @disable this(); 1735 this(int j) { i = j; } 1736 } 1737 1738 auto array = Array!NoDefaultCtor([NoDefaultCtor(1), NoDefaultCtor(2)]); 1739 assertNotThrown!AssertError(array.length = 1); 1740 assertThrown!AssertError(array.length = 5); 1741 } 1742 1743 //////////////////////////////////////////////////////////////////////////////// 1744 // Array!bool 1745 //////////////////////////////////////////////////////////////////////////////// 1746 1747 /** 1748 * _Array specialized for `bool`. Packs together values efficiently by 1749 * allocating one bit per element. 1750 */ 1751 struct Array(T) 1752 if (is(immutable T == immutable bool)) 1753 { 1754 import std.exception : enforce; 1755 import std.typecons : RefCounted, RefCountedAutoInitialize; 1756 1757 static immutable uint bitsPerWord = size_t.sizeof * 8; 1758 private static struct Data 1759 { 1760 Array!size_t.Payload _backend; 1761 size_t _length; 1762 } 1763 private RefCounted!(Data, RefCountedAutoInitialize.no) _store; 1764 1765 private @property ref size_t[] data() 1766 { 1767 assert(_store.refCountedStore.isInitialized, 1768 "Cannot get data of uninitialized Array"); 1769 return _store._backend._payload; 1770 } 1771 1772 /** 1773 * Defines the array's primary range. 1774 */ 1775 struct Range 1776 { 1777 private Array _outer; 1778 private size_t _a, _b; 1779 /// Range primitives 1780 @property Range save() 1781 { 1782 version (bug4437) 1783 { 1784 return this; 1785 } 1786 else 1787 { 1788 auto copy = this; 1789 return copy; 1790 } 1791 } 1792 /// Ditto 1793 @property bool empty() 1794 { 1795 return _a >= _b || _outer.length < _b; 1796 } 1797 /// Ditto 1798 @property T front() 1799 { 1800 enforce(!empty, "Attempting to access the front of an empty Array"); 1801 return _outer[_a]; 1802 } 1803 /// Ditto 1804 @property void front(bool value) 1805 { 1806 enforce(!empty, "Attempting to set the front of an empty Array"); 1807 _outer[_a] = value; 1808 } 1809 /// Ditto 1810 T moveFront() 1811 { 1812 enforce(!empty, "Attempting to move the front of an empty Array"); 1813 return _outer.moveAt(_a); 1814 } 1815 /// Ditto 1816 void popFront() 1817 { 1818 enforce(!empty, "Attempting to popFront an empty Array"); 1819 ++_a; 1820 } 1821 /// Ditto 1822 @property T back() 1823 { 1824 enforce(!empty, "Attempting to access the back of an empty Array"); 1825 return _outer[_b - 1]; 1826 } 1827 /// Ditto 1828 @property void back(bool value) 1829 { 1830 enforce(!empty, "Attempting to set the back of an empty Array"); 1831 _outer[_b - 1] = value; 1832 } 1833 /// Ditto 1834 T moveBack() 1835 { 1836 enforce(!empty, "Attempting to move the back of an empty Array"); 1837 return _outer.moveAt(_b - 1); 1838 } 1839 /// Ditto 1840 void popBack() 1841 { 1842 enforce(!empty, "Attempting to popBack an empty Array"); 1843 --_b; 1844 } 1845 /// Ditto 1846 T opIndex(size_t i) 1847 { 1848 return _outer[_a + i]; 1849 } 1850 /// Ditto 1851 void opIndexAssign(T value, size_t i) 1852 { 1853 _outer[_a + i] = value; 1854 } 1855 /// Ditto 1856 T moveAt(size_t i) 1857 { 1858 return _outer.moveAt(_a + i); 1859 } 1860 /// Ditto 1861 @property size_t length() const 1862 { 1863 assert(_a <= _b, "Invalid bounds"); 1864 return _b - _a; 1865 } 1866 alias opDollar = length; 1867 /// ditto 1868 Range opSlice(size_t low, size_t high) 1869 { 1870 // Note: indexes start at 0, which is equivalent to _a 1871 assert( 1872 low <= high && high <= (_b - _a), 1873 "Using out of bounds indexes on an Array" 1874 ); 1875 return Range(_outer, _a + low, _a + high); 1876 } 1877 } 1878 1879 /** 1880 * Constructor taking a number of items. 1881 */ 1882 this(U)(U[] values...) 1883 if (isImplicitlyConvertible!(U, T)) 1884 { 1885 reserve(values.length); 1886 foreach (i, v; values) 1887 { 1888 auto rem = i % bitsPerWord; 1889 if (rem) 1890 { 1891 // Fits within the current array 1892 if (v) 1893 { 1894 data[$ - 1] |= (cast(size_t) 1 << rem); 1895 } 1896 else 1897 { 1898 data[$ - 1] &= ~(cast(size_t) 1 << rem); 1899 } 1900 } 1901 else 1902 { 1903 // Need to add more data 1904 _store._backend.insertBack(v); 1905 } 1906 } 1907 _store._length = values.length; 1908 } 1909 1910 /** 1911 * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 1912 */ 1913 this(Range)(Range r) 1914 if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[])) 1915 { 1916 insertBack(r); 1917 } 1918 1919 /** 1920 * Property returning `true` if and only if the array has 1921 * no elements. 1922 * 1923 * Complexity: $(BIGOH 1) 1924 */ 1925 @property bool empty() 1926 { 1927 return !length; 1928 } 1929 1930 /** 1931 * Returns: A duplicate of the array. 1932 * 1933 * Complexity: $(BIGOH length). 1934 */ 1935 @property Array dup() 1936 { 1937 Array result; 1938 result.insertBack(this[]); 1939 return result; 1940 } 1941 1942 /** 1943 * Returns the number of elements in the array. 1944 * 1945 * Complexity: $(BIGOH 1). 1946 */ 1947 @property size_t length() const 1948 { 1949 return _store.refCountedStore.isInitialized ? _store._length : 0; 1950 } 1951 alias opDollar = length; 1952 1953 /** 1954 * Returns: The maximum number of elements the array can store without 1955 * reallocating memory and invalidating iterators upon insertion. 1956 * 1957 * Complexity: $(BIGOH 1). 1958 */ 1959 @property size_t capacity() 1960 { 1961 return _store.refCountedStore.isInitialized 1962 ? cast(size_t) bitsPerWord * _store._backend.capacity 1963 : 0; 1964 } 1965 1966 /** 1967 * Ensures sufficient capacity to accommodate `e` _elements. 1968 * If `e < capacity`, this method does nothing. 1969 * 1970 * Postcondition: `capacity >= e` 1971 * 1972 * Note: If the capacity is increased, one should assume that all 1973 * iterators to the elements are invalidated. 1974 * 1975 * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). 1976 */ 1977 void reserve(size_t e) 1978 { 1979 import std.conv : to; 1980 _store.refCountedStore.ensureInitialized(); 1981 _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord)); 1982 } 1983 1984 /** 1985 * Returns: A range that iterates over all elements of the array in forward order. 1986 * 1987 * Complexity: $(BIGOH 1) 1988 */ 1989 Range opSlice() 1990 { 1991 return Range(this, 0, length); 1992 } 1993 1994 1995 /** 1996 * Returns: A range that iterates the array between two specified positions. 1997 * 1998 * Complexity: $(BIGOH 1) 1999 */ 2000 Range opSlice(size_t a, size_t b) 2001 { 2002 enforce(a <= b && b <= length); 2003 return Range(this, a, b); 2004 } 2005 2006 /** 2007 * Returns: The first element of the array. 2008 * 2009 * Precondition: `empty == false` 2010 * 2011 * Complexity: $(BIGOH 1) 2012 * 2013 * Throws: `Exception` if the array is empty. 2014 */ 2015 @property bool front() 2016 { 2017 enforce(!empty); 2018 return data.ptr[0] & 1; 2019 } 2020 2021 /// Ditto 2022 @property void front(bool value) 2023 { 2024 enforce(!empty); 2025 if (value) data.ptr[0] |= 1; 2026 else data.ptr[0] &= ~cast(size_t) 1; 2027 } 2028 2029 /** 2030 * Returns: The last element of the array. 2031 * 2032 * Precondition: `empty == false` 2033 * 2034 * Complexity: $(BIGOH 1) 2035 * 2036 * Throws: `Exception` if the array is empty. 2037 */ 2038 @property bool back() 2039 { 2040 enforce(!empty); 2041 return cast(bool)(data.back & (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord))); 2042 } 2043 2044 /// Ditto 2045 @property void back(bool value) 2046 { 2047 enforce(!empty); 2048 if (value) 2049 { 2050 data.back |= (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)); 2051 } 2052 else 2053 { 2054 data.back &= 2055 ~(cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)); 2056 } 2057 } 2058 2059 /** 2060 * Indexing operators yielding or modifyng the value at the specified index. 2061 * 2062 * Precondition: `i < length` 2063 * 2064 * Complexity: $(BIGOH 1) 2065 */ 2066 bool opIndex(size_t i) 2067 { 2068 auto div = cast(size_t) (i / bitsPerWord); 2069 auto rem = i % bitsPerWord; 2070 enforce(div < data.length); 2071 return cast(bool)(data.ptr[div] & (cast(size_t) 1 << rem)); 2072 } 2073 2074 /// ditto 2075 void opIndexAssign(bool value, size_t i) 2076 { 2077 auto div = cast(size_t) (i / bitsPerWord); 2078 auto rem = i % bitsPerWord; 2079 enforce(div < data.length); 2080 if (value) data.ptr[div] |= (cast(size_t) 1 << rem); 2081 else data.ptr[div] &= ~(cast(size_t) 1 << rem); 2082 } 2083 2084 /// ditto 2085 void opIndexOpAssign(string op)(bool value, size_t i) 2086 { 2087 auto div = cast(size_t) (i / bitsPerWord); 2088 auto rem = i % bitsPerWord; 2089 enforce(div < data.length); 2090 auto oldValue = cast(bool) (data.ptr[div] & (cast(size_t) 1 << rem)); 2091 // Do the deed 2092 auto newValue = mixin("oldValue "~op~" value"); 2093 // Write back the value 2094 if (newValue != oldValue) 2095 { 2096 if (newValue) data.ptr[div] |= (cast(size_t) 1 << rem); 2097 else data.ptr[div] &= ~(cast(size_t) 1 << rem); 2098 } 2099 } 2100 2101 /// Ditto 2102 T moveAt(size_t i) 2103 { 2104 return this[i]; 2105 } 2106 2107 /** 2108 * Returns: A new array which is a concatenation of `this` and its argument. 2109 * 2110 * Complexity: 2111 * $(BIGOH length + m), where `m` is the number of elements in `stuff`. 2112 */ 2113 Array!bool opBinary(string op, Stuff)(Stuff rhs) 2114 if (op == "~") 2115 { 2116 Array!bool result; 2117 2118 static if (hasLength!Stuff) 2119 result.reserve(length + rhs.length); 2120 else static if (is(typeof(rhs[])) && hasLength!(typeof(rhs[]))) 2121 result.reserve(length + rhs[].length); 2122 else static if (isImplicitlyConvertible!(Stuff, bool)) 2123 result.reserve(length + 1); 2124 2125 result.insertBack(this[]); 2126 result ~= rhs; 2127 return result; 2128 } 2129 2130 /** 2131 * Forwards to `insertBack`. 2132 */ 2133 Array!bool opOpAssign(string op, Stuff)(Stuff stuff) 2134 if (op == "~") 2135 { 2136 static if (is(typeof(stuff[]))) insertBack(stuff[]); 2137 else insertBack(stuff); 2138 return this; 2139 } 2140 2141 /** 2142 * Removes all the elements from the array and releases allocated memory. 2143 * 2144 * Postcondition: `empty == true && capacity == 0` 2145 * 2146 * Complexity: $(BIGOH length) 2147 */ 2148 void clear() 2149 { 2150 this = Array(); 2151 } 2152 2153 /** 2154 * Sets the number of elements in the array to `newLength`. If `newLength` 2155 * is greater than `length`, the new elements are added to the end of the 2156 * array and initialized with `false`. 2157 * 2158 * Complexity: 2159 * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. 2160 * If `capacity < newLength` the worst case is $(BIGOH newLength). 2161 * 2162 * Postcondition: `length == newLength` 2163 */ 2164 @property void length(size_t newLength) 2165 { 2166 import std.conv : to; 2167 _store.refCountedStore.ensureInitialized(); 2168 auto newDataLength = 2169 to!size_t((newLength + bitsPerWord - 1) / bitsPerWord); 2170 _store._backend.length = newDataLength; 2171 _store._length = newLength; 2172 } 2173 2174 /** 2175 * Removes the last element from the array and returns it. 2176 * Both stable and non-stable versions behave the same and guarantee 2177 * that ranges iterating over the array are never invalidated. 2178 * 2179 * Precondition: `empty == false` 2180 * 2181 * Returns: The element removed. 2182 * 2183 * Complexity: $(BIGOH 1). 2184 * 2185 * Throws: `Exception` if the array is empty. 2186 */ 2187 T removeAny() 2188 { 2189 auto result = back; 2190 removeBack(); 2191 return result; 2192 } 2193 2194 /// ditto 2195 alias stableRemoveAny = removeAny; 2196 2197 /** 2198 * Inserts the specified elements at the back of the array. `stuff` can be 2199 * a value convertible to `bool` or a range of objects convertible to `bool`. 2200 * 2201 * Returns: The number of elements inserted. 2202 * 2203 * Complexity: 2204 * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), 2205 * where `m` is the number of elements in `stuff`. 2206 */ 2207 size_t insertBack(Stuff)(Stuff stuff) 2208 if (is(Stuff : bool)) 2209 { 2210 _store.refCountedStore.ensureInitialized(); 2211 auto rem = _store._length % bitsPerWord; 2212 if (rem) 2213 { 2214 // Fits within the current array 2215 if (stuff) 2216 { 2217 data[$ - 1] |= (cast(size_t) 1 << rem); 2218 } 2219 else 2220 { 2221 data[$ - 1] &= ~(cast(size_t) 1 << rem); 2222 } 2223 } 2224 else 2225 { 2226 // Need to add more data 2227 _store._backend.insertBack(stuff); 2228 } 2229 ++_store._length; 2230 return 1; 2231 } 2232 2233 /// ditto 2234 size_t insertBack(Stuff)(Stuff stuff) 2235 if (isInputRange!Stuff && is(ElementType!Stuff : bool)) 2236 { 2237 size_t result; 2238 static if (hasLength!Stuff) 2239 result = stuff.length; 2240 2241 for (; !stuff.empty; stuff.popFront()) 2242 { 2243 insertBack(stuff.front); 2244 static if (!hasLength!Stuff) ++result; 2245 } 2246 2247 return result; 2248 } 2249 2250 /// ditto 2251 alias stableInsertBack = insertBack; 2252 2253 /// ditto 2254 alias insert = insertBack; 2255 2256 /// ditto 2257 alias stableInsert = insertBack; 2258 2259 /// ditto 2260 alias linearInsert = insertBack; 2261 2262 /// ditto 2263 alias stableLinearInsert = insertBack; 2264 2265 /** 2266 * Removes the value from the back of the array. Both stable and non-stable 2267 * versions behave the same and guarantee that ranges iterating over the 2268 * array are never invalidated. 2269 * 2270 * Precondition: `empty == false` 2271 * 2272 * Complexity: $(BIGOH 1). 2273 * 2274 * Throws: `Exception` if the array is empty. 2275 */ 2276 void removeBack() 2277 { 2278 enforce(_store._length); 2279 if (_store._length % bitsPerWord) 2280 { 2281 // Cool, just decrease the length 2282 --_store._length; 2283 } 2284 else 2285 { 2286 // Reduce the allocated space 2287 --_store._length; 2288 _store._backend.length = _store._backend.length - 1; 2289 } 2290 } 2291 2292 /// ditto 2293 alias stableRemoveBack = removeBack; 2294 2295 /** 2296 * Removes `howMany` values from the back of the array. Unlike the 2297 * unparameterized versions above, these functions do not throw if 2298 * they could not remove `howMany` elements. Instead, if `howMany > n`, 2299 * all elements are removed. The returned value is the effective number 2300 * of elements removed. Both stable and non-stable versions behave the same 2301 * and guarantee that ranges iterating over the array are never invalidated. 2302 * 2303 * Returns: The number of elements removed. 2304 * 2305 * Complexity: $(BIGOH howMany). 2306 */ 2307 size_t removeBack(size_t howMany) 2308 { 2309 if (howMany >= length) 2310 { 2311 howMany = length; 2312 clear(); 2313 } 2314 else 2315 { 2316 length = length - howMany; 2317 } 2318 return howMany; 2319 } 2320 2321 /// ditto 2322 alias stableRemoveBack = removeBack; 2323 2324 /** 2325 * Inserts `stuff` before, after, or instead range `r`, which must 2326 * be a valid range previously extracted from this array. `stuff` 2327 * can be a value convertible to `bool` or a range of objects convertible 2328 * to `bool`. Both stable and non-stable version behave the same and 2329 * guarantee that ranges iterating over the array are never invalidated. 2330 * 2331 * Returns: The number of values inserted. 2332 * 2333 * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. 2334 */ 2335 size_t insertBefore(Stuff)(Range r, Stuff stuff) 2336 { 2337 import std.algorithm.mutation : bringToFront; 2338 // TODO: make this faster, it moves one bit at a time 2339 immutable inserted = stableInsertBack(stuff); 2340 immutable tailLength = length - inserted; 2341 bringToFront( 2342 this[r._a .. tailLength], 2343 this[tailLength .. length]); 2344 return inserted; 2345 } 2346 2347 /// ditto 2348 alias stableInsertBefore = insertBefore; 2349 2350 /// ditto 2351 size_t insertAfter(Stuff)(Range r, Stuff stuff) 2352 if (isImplicitlyConvertible!(Stuff, T) || 2353 isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 2354 { 2355 import std.algorithm.mutation : bringToFront; 2356 // TODO: make this faster, it moves one bit at a time 2357 immutable inserted = stableInsertBack(stuff); 2358 immutable tailLength = length - inserted; 2359 bringToFront( 2360 this[r._b .. tailLength], 2361 this[tailLength .. length]); 2362 return inserted; 2363 } 2364 2365 /// ditto 2366 alias stableInsertAfter = insertAfter; 2367 2368 /// ditto 2369 size_t replace(Stuff)(Range r, Stuff stuff) 2370 if (is(Stuff : bool)) 2371 { 2372 if (!r.empty) 2373 { 2374 // There is room 2375 r.front = stuff; 2376 r.popFront(); 2377 linearRemove(r); 2378 } 2379 else 2380 { 2381 // No room, must insert 2382 insertBefore(r, stuff); 2383 } 2384 return 1; 2385 } 2386 2387 /// ditto 2388 alias stableReplace = replace; 2389 2390 /** 2391 * Removes all elements belonging to `r`, which must be a range 2392 * obtained originally from this array. 2393 * 2394 * Returns: A range spanning the remaining elements in the array that 2395 * initially were right after `r`. 2396 * 2397 * Complexity: $(BIGOH length) 2398 */ 2399 Range linearRemove(Range r) 2400 { 2401 import std.algorithm.mutation : copy; 2402 copy(this[r._b .. length], this[r._a .. length]); 2403 length = length - r.length; 2404 return this[r._a .. length]; 2405 } 2406 } 2407 2408 @system unittest 2409 { 2410 import std.algorithm.comparison : equal; 2411 2412 auto a = Array!bool([true, true, false, false, true, false]); 2413 assert(equal(a[], [true, true, false, false, true, false])); 2414 } 2415 2416 // using Ranges 2417 @system unittest 2418 { 2419 import std.algorithm.comparison : equal; 2420 import std.range : retro; 2421 bool[] arr = [true, true, false, false, true, false]; 2422 2423 auto a = Array!bool(retro(arr)); 2424 assert(equal(a[], retro(arr))); 2425 } 2426 2427 @system unittest 2428 { 2429 Array!bool a; 2430 assert(a.empty); 2431 } 2432 2433 @system unittest 2434 { 2435 auto arr = Array!bool([false, false, false, false]); 2436 assert(arr.front == false); 2437 assert(arr.back == false); 2438 assert(arr[1] == false); 2439 auto slice = arr[]; 2440 slice = arr[0 .. $]; 2441 slice = slice[1 .. $]; 2442 slice.front = true; 2443 slice.back = true; 2444 slice[1] = true; 2445 slice = slice[0 .. $]; // https://issues.dlang.org/show_bug.cgi?id=19171 2446 assert(slice.front == true); 2447 assert(slice.back == true); 2448 assert(slice[1] == true); 2449 assert(slice.moveFront == true); 2450 assert(slice.moveBack == true); 2451 assert(slice.moveAt(1) == true); 2452 } 2453 2454 // uncomparable values are valid values for an array 2455 // https://issues.dlang.org/show_bug.cgi?id=16331 2456 @system unittest 2457 { 2458 double[] values = [double.nan, double.nan]; 2459 auto arr = Array!double(values); 2460 } 2461 2462 @nogc @system unittest 2463 { 2464 auto a = Array!int(0, 1, 2); 2465 int[3] b = [3, 4, 5]; 2466 short[3] ci = [0, 1, 0]; 2467 auto c = Array!short(ci); 2468 assert(Array!int(0, 1, 2, 0, 1, 2) == a ~ a); 2469 assert(Array!int(0, 1, 2, 3, 4, 5) == a ~ b); 2470 assert(Array!int(0, 1, 2, 3) == a ~ 3); 2471 assert(Array!int(0, 1, 2, 0, 1, 0) == a ~ c); 2472 } 2473 2474 @nogc @system unittest 2475 { 2476 auto a = Array!char('a', 'b'); 2477 assert(Array!char("abc") == a ~ 'c'); 2478 import std.utf : byCodeUnit; 2479 assert(Array!char("abcd") == a ~ "cd".byCodeUnit); 2480 } 2481 2482 @nogc @system unittest 2483 { 2484 auto a = Array!dchar("ąćę"d); 2485 assert(Array!dchar("ąćęϢϖ"d) == a ~ "Ϣϖ"d); 2486 wchar x = 'Ϣ'; 2487 assert(Array!dchar("ąćęϢz"d) == a ~ x ~ 'z'); 2488 } 2489 2490 @system unittest 2491 { 2492 Array!bool a; 2493 assert(a.empty); 2494 a.insertBack(false); 2495 assert(!a.empty); 2496 } 2497 2498 @system unittest 2499 { 2500 Array!bool a; 2501 assert(a.empty); 2502 auto b = a.dup; 2503 assert(b.empty); 2504 a.insertBack(true); 2505 assert(b.empty); 2506 } 2507 2508 @system unittest 2509 { 2510 import std.conv : to; 2511 Array!bool a; 2512 assert(a.length == 0); 2513 a.insert(true); 2514 assert(a.length == 1, to!string(a.length)); 2515 } 2516 2517 @system unittest 2518 { 2519 import std.conv : to; 2520 Array!bool a; 2521 assert(a.capacity == 0); 2522 foreach (i; 0 .. 100) 2523 { 2524 a.insert(true); 2525 assert(a.capacity >= a.length, to!string(a.capacity)); 2526 } 2527 } 2528 2529 @system unittest 2530 { 2531 Array!bool a; 2532 assert(a.capacity == 0); 2533 a.reserve(15657); 2534 assert(a.capacity >= 15657); 2535 a.reserve(100); 2536 assert(a.capacity >= 15657); 2537 } 2538 2539 @system unittest 2540 { 2541 auto a = Array!bool([true, false, true, true]); 2542 assert(a[0 .. 2].length == 2); 2543 } 2544 2545 @system unittest 2546 { 2547 auto a = Array!bool([true, false, true, true]); 2548 assert(a[].length == 4); 2549 } 2550 2551 @system unittest 2552 { 2553 auto a = Array!bool([true, false, true, true]); 2554 assert(a.front); 2555 a.front = false; 2556 assert(!a.front); 2557 } 2558 2559 @system unittest 2560 { 2561 auto a = Array!bool([true, false, true, true]); 2562 assert(a[].length == 4); 2563 } 2564 2565 @system unittest 2566 { 2567 auto a = Array!bool([true, false, true, true]); 2568 assert(a.back); 2569 a.back = false; 2570 assert(!a.back); 2571 } 2572 2573 @system unittest 2574 { 2575 auto a = Array!bool([true, false, true, true]); 2576 assert(a[0] && !a[1]); 2577 a[0] &= a[1]; 2578 assert(!a[0]); 2579 } 2580 2581 @system unittest 2582 { 2583 import std.algorithm.comparison : equal; 2584 auto a = Array!bool([true, false, true, true]); 2585 auto b = Array!bool([true, true, false, true]); 2586 assert(equal((a ~ b)[], 2587 [true, false, true, true, true, true, false, true])); 2588 assert((a ~ [true, false])[].equal([true, false, true, true, true, false])); 2589 Array!bool c; 2590 c.insertBack(true); 2591 assert((c ~ false)[].equal([true, false])); 2592 } 2593 @system unittest 2594 { 2595 import std.algorithm.comparison : equal; 2596 auto a = Array!bool([true, false, true, true]); 2597 auto b = Array!bool([false, true, false, true, true]); 2598 a ~= b; 2599 assert(equal( 2600 a[], 2601 [true, false, true, true, false, true, false, true, true])); 2602 } 2603 2604 @system unittest 2605 { 2606 auto a = Array!bool([true, false, true, true]); 2607 a.clear(); 2608 assert(a.capacity == 0); 2609 } 2610 2611 @system unittest 2612 { 2613 Array!bool a; 2614 a.length = 1057; 2615 assert(a.length == 1057); 2616 assert(a.capacity >= a.length); 2617 foreach (e; a) 2618 { 2619 assert(!e); 2620 } 2621 immutable cap = a.capacity; 2622 a.length = 100; 2623 assert(a.length == 100); 2624 // do not realloc if length decreases 2625 assert(a.capacity == cap); 2626 } 2627 2628 @system unittest 2629 { 2630 Array!bool a; 2631 a.length = 1057; 2632 assert(!a.removeAny()); 2633 assert(a.length == 1056); 2634 foreach (e; a) 2635 { 2636 assert(!e); 2637 } 2638 } 2639 2640 @system unittest 2641 { 2642 Array!bool a; 2643 for (int i = 0; i < 100; ++i) 2644 a.insertBack(true); 2645 foreach (e; a) 2646 assert(e); 2647 } 2648 2649 @system unittest 2650 { 2651 Array!bool a; 2652 a.length = 1057; 2653 assert(a.removeBack(1000) == 1000); 2654 assert(a.length == 57); 2655 foreach (e; a) 2656 { 2657 assert(!e); 2658 } 2659 } 2660 2661 @system unittest 2662 { 2663 import std.conv : to; 2664 Array!bool a; 2665 version (bugxxxx) 2666 { 2667 a._store.refCountedDebug = true; 2668 } 2669 a.insertBefore(a[], true); 2670 assert(a.length == 1, to!string(a.length)); 2671 a.insertBefore(a[], false); 2672 assert(a.length == 2, to!string(a.length)); 2673 a.insertBefore(a[1 .. $], true); 2674 import std.algorithm.comparison : equal; 2675 assert(a[].equal([false, true, true])); 2676 } 2677 2678 // https://issues.dlang.org/show_bug.cgi?id=21555 2679 @system unittest 2680 { 2681 import std.algorithm.comparison : equal; 2682 Array!bool arr; 2683 size_t len = arr.insertBack([false, true]); 2684 assert(len == 2); 2685 } 2686 2687 // https://issues.dlang.org/show_bug.cgi?id=21556 2688 @system unittest 2689 { 2690 import std.algorithm.comparison : equal; 2691 Array!bool a; 2692 a.insertBack([true, true, false, false, true]); 2693 assert(a.length == 5); 2694 2695 assert(a.insertAfter(a[0 .. 5], [false, false]) == 2); 2696 assert(equal(a[], [true, true, false, false, true, false, false])); 2697 2698 assert(a.insertAfter(a[0 .. 5], true) == 1); 2699 assert(equal(a[], [true, true, false, false, true, true, false, false])); 2700 } 2701 2702 @system unittest 2703 { 2704 import std.conv : to; 2705 Array!bool a; 2706 a.length = 10; 2707 a.insertAfter(a[0 .. 5], true); 2708 assert(a.length == 11, to!string(a.length)); 2709 assert(a[5]); 2710 } 2711 @system unittest 2712 { 2713 alias V3 = int[3]; 2714 V3 v = [1, 2, 3]; 2715 Array!V3 arr; 2716 arr ~= v; 2717 assert(arr[0] == [1, 2, 3]); 2718 } 2719 @system unittest 2720 { 2721 alias V3 = int[3]; 2722 V3[2] v = [[1, 2, 3], [4, 5, 6]]; 2723 Array!V3 arr; 2724 arr ~= v; 2725 assert(arr[0] == [1, 2, 3]); 2726 assert(arr[1] == [4, 5, 6]); 2727 } 2728 2729 // Change of length reallocates without calling GC. 2730 // https://issues.dlang.org/show_bug.cgi?id=13642 2731 @system unittest 2732 { 2733 import core.memory; 2734 class ABC { void func() { int x = 5; } } 2735 2736 Array!ABC arr; 2737 // Length only allocates if capacity is too low. 2738 arr.reserve(4); 2739 assert(arr.capacity == 4); 2740 2741 void func() @nogc 2742 { 2743 arr.length = 5; 2744 } 2745 func(); 2746 2747 foreach (ref b; arr) b = new ABC; 2748 GC.collect(); 2749 arr[1].func(); 2750 } 2751 2752 @system unittest 2753 { 2754 2755 Array!int arr = [1, 2, 4, 5]; 2756 int[] data = arr.data(); 2757 2758 const Array!int arr2 = [8, 9]; 2759 assert(arr2.data() == [8, 9]); 2760 2761 data[0] = 0; 2762 assert(arr[0] == 0); 2763 2764 arr.length = 0; 2765 assert(arr.data == []); 2766 2767 Array!int empty; 2768 assert(empty.data == []); 2769 } 2770