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(cast(void*) _payload.ptr); 416 417 free(cast(void*) _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(cast(void*) newPayload.ptr, cast(void*) _payload.ptr, T.sizeof * oldLength); 493 // Zero out unused capacity to prevent gc from seeing false pointers 494 memset( cast(void*) (newPayload.ptr + oldLength), 495 0, 496 (elements - oldLength) * T.sizeof); 497 GC.addRange(cast(void*) newPayload.ptr, sz); 498 GC.removeRange(cast(void*) _payload.ptr); 499 free(cast(void*) _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 // fix https://issues.dlang.org/show_bug.cgi?23140 615 private alias Unshared(T) = T; 616 private alias Unshared(T: shared U, U) = U; 617 618 /// ditto 619 bool opEquals(ref const Array rhs) const 620 { 621 if (empty) return rhs.empty; 622 if (rhs.empty) return false; 623 624 return cast(Unshared!(T)[]) _data._payload == cast(Unshared!(T)[]) rhs._data._payload; 625 } 626 627 /** 628 * Defines the array's primary range, which is a random-access range. 629 * 630 * `ConstRange` is a variant with `const` elements. 631 * `ImmutableRange` is a variant with `immutable` elements. 632 */ 633 alias Range = RangeT!Array; 634 635 /// ditto 636 alias ConstRange = RangeT!(const Array); 637 638 /// ditto 639 alias ImmutableRange = RangeT!(immutable Array); 640 641 /** 642 * Duplicates the array. The elements themselves are not transitively 643 * duplicated. 644 * 645 * Complexity: $(BIGOH length). 646 */ 647 @property Array dup() 648 { 649 if (!_data.refCountedStore.isInitialized) return this; 650 return Array(_data._payload); 651 } 652 653 /** 654 * Returns: `true` if and only if the array has no elements. 655 * 656 * Complexity: $(BIGOH 1) 657 */ 658 @property bool empty() const 659 { 660 return !_data.refCountedStore.isInitialized || _data._payload.empty; 661 } 662 663 /** 664 * Returns: The number of elements in the array. 665 * 666 * Complexity: $(BIGOH 1). 667 */ 668 @property size_t length() const 669 { 670 return _data.refCountedStore.isInitialized ? _data._payload.length : 0; 671 } 672 673 /// ditto 674 size_t opDollar() const 675 { 676 return length; 677 } 678 679 /** 680 * Returns: The maximum number of elements the array can store without 681 * reallocating memory and invalidating iterators upon insertion. 682 * 683 * Complexity: $(BIGOH 1) 684 */ 685 @property size_t capacity() 686 { 687 return _data.refCountedStore.isInitialized ? _data._capacity : 0; 688 } 689 690 /** 691 * Returns: the internal representation of the array. 692 * 693 * Complexity: $(BIGOH 1). 694 */ 695 696 inout(T)[] data() inout @system 697 { 698 return _data.refCountedStore.isInitialized ? _data._payload : []; 699 } 700 701 /** 702 * Ensures sufficient capacity to accommodate `e` _elements. 703 * If `e < capacity`, this method does nothing. 704 * 705 * Postcondition: `capacity >= e` 706 * 707 * Note: If the capacity is increased, one should assume that all 708 * iterators to the elements are invalidated. 709 * 710 * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). 711 */ 712 void reserve(size_t elements) 713 { 714 if (elements > capacity) 715 { 716 _data.refCountedStore.ensureInitialized(); 717 _data.reserve(elements); 718 assert(capacity == elements); // Might need changing to ">=" if implementation of Payload.reserve is changed. 719 } 720 } 721 722 /** 723 * Returns: A range that iterates over elements of the array in 724 * forward order. 725 * 726 * Complexity: $(BIGOH 1) 727 */ 728 Range opSlice() 729 { 730 return typeof(return)(this, 0, length); 731 } 732 733 ConstRange opSlice() const 734 { 735 return typeof(return)(this, 0, length); 736 } 737 738 ImmutableRange opSlice() immutable 739 { 740 return typeof(return)(this, 0, length); 741 } 742 743 /** 744 * Returns: A range that iterates over elements of the array from 745 * index `i` up to (excluding) index `j`. 746 * 747 * Precondition: `i <= j && j <= length` 748 * 749 * Complexity: $(BIGOH 1) 750 */ 751 Range opSlice(size_t i, size_t j) 752 { 753 assert(i <= j && j <= length, "Invalid slice bounds"); 754 return typeof(return)(this, i, j); 755 } 756 757 ConstRange opSlice(size_t i, size_t j) const 758 { 759 assert(i <= j && j <= length, "Invalid slice bounds"); 760 return typeof(return)(this, i, j); 761 } 762 763 ImmutableRange opSlice(size_t i, size_t j) immutable 764 { 765 assert(i <= j && j <= length, "Invalid slice bounds"); 766 return typeof(return)(this, i, j); 767 } 768 769 /** 770 * Returns: The first element of the array. 771 * 772 * Precondition: `empty == false` 773 * 774 * Complexity: $(BIGOH 1) 775 */ 776 @property ref inout(T) front() inout 777 { 778 assert(_data.refCountedStore.isInitialized, 779 "Cannot get front of empty range"); 780 return _data._payload[0]; 781 } 782 783 /** 784 * Returns: The last element of the array. 785 * 786 * Precondition: `empty == false` 787 * 788 * Complexity: $(BIGOH 1) 789 */ 790 @property ref inout(T) back() inout 791 { 792 assert(_data.refCountedStore.isInitialized, 793 "Cannot get back of empty range"); 794 return _data._payload[$ - 1]; 795 } 796 797 /** 798 * Returns: The element or a reference to the element at the specified index. 799 * 800 * Precondition: `i < length` 801 * 802 * Complexity: $(BIGOH 1) 803 */ 804 ref inout(T) opIndex(size_t i) inout 805 { 806 assert(_data.refCountedStore.isInitialized, 807 "Cannot index empty range"); 808 return _data._payload[i]; 809 } 810 811 /** 812 * Slicing operators executing the specified operation on the entire slice. 813 * 814 * Precondition: `i < j && j < length` 815 * 816 * Complexity: $(BIGOH slice.length) 817 */ 818 void opSliceAssign(T value) 819 { 820 if (!_data.refCountedStore.isInitialized) return; 821 _data._payload[] = value; 822 } 823 824 /// ditto 825 void opSliceAssign(T value, size_t i, size_t j) 826 { 827 auto slice = _data.refCountedStore.isInitialized ? 828 _data._payload : 829 T[].init; 830 slice[i .. j] = value; 831 } 832 833 /// ditto 834 void opSliceUnary(string op)() 835 if (op == "++" || op == "--") 836 { 837 if (!_data.refCountedStore.isInitialized) return; 838 mixin(op~"_data._payload[];"); 839 } 840 841 /// ditto 842 void opSliceUnary(string op)(size_t i, size_t j) 843 if (op == "++" || op == "--") 844 { 845 auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; 846 mixin(op~"slice[i .. j];"); 847 } 848 849 /// ditto 850 void opSliceOpAssign(string op)(T value) 851 { 852 if (!_data.refCountedStore.isInitialized) return; 853 mixin("_data._payload[] "~op~"= value;"); 854 } 855 856 /// ditto 857 void opSliceOpAssign(string op)(T value, size_t i, size_t j) 858 { 859 auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; 860 mixin("slice[i .. j] "~op~"= value;"); 861 } 862 863 private enum hasSliceWithLength(T) = is(typeof({ T t = T.init; t[].length; })); 864 865 /** 866 * Returns: A new array which is a concatenation of `this` and its argument. 867 * 868 * Complexity: 869 * $(BIGOH length + m), where `m` is the number of elements in `stuff`. 870 */ 871 Array opBinary(string op, Stuff)(Stuff stuff) 872 if (op == "~") 873 { 874 Array result; 875 876 static if (hasLength!Stuff || isNarrowString!Stuff) 877 result.reserve(length + stuff.length); 878 else static if (hasSliceWithLength!Stuff) 879 result.reserve(length + stuff[].length); 880 else static if (isImplicitlyConvertible!(Stuff, T)) 881 result.reserve(length + 1); 882 883 result.insertBack(this[]); 884 result ~= stuff; 885 return result; 886 } 887 888 /** 889 * Forwards to `insertBack`. 890 */ 891 void opOpAssign(string op, Stuff)(auto ref Stuff stuff) 892 if (op == "~") 893 { 894 static if (is(typeof(stuff[])) && isImplicitlyConvertible!(typeof(stuff[0]), T)) 895 { 896 insertBack(stuff[]); 897 } 898 else 899 { 900 insertBack(stuff); 901 } 902 } 903 904 /** 905 * Removes all the elements from the array and releases allocated memory. 906 * 907 * Postcondition: `empty == true && capacity == 0` 908 * 909 * Complexity: $(BIGOH length) 910 */ 911 void clear() 912 { 913 _data = Data.init; 914 } 915 916 /** 917 * Sets the number of elements in the array to `newLength`. If `newLength` 918 * is greater than `length`, the new elements are added to the end of the 919 * array and initialized with `T.init`. If `T` is a `struct` whose default 920 * constructor is annotated with `@disable`, `newLength` must be lower than 921 * or equal to `length`. 922 * 923 * Complexity: 924 * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. 925 * If `capacity < newLength` the worst case is $(BIGOH newLength). 926 * 927 * Precondition: `__traits(compiles, { static T _; }) || newLength <= length` 928 * 929 * Postcondition: `length == newLength` 930 */ 931 @property void length(size_t newLength) 932 { 933 _data.refCountedStore.ensureInitialized(); 934 _data.length = newLength; 935 } 936 937 /** 938 * Removes the last element from the array and returns it. 939 * Both stable and non-stable versions behave the same and guarantee 940 * that ranges iterating over the array are never invalidated. 941 * 942 * Precondition: `empty == false` 943 * 944 * Returns: The element removed. 945 * 946 * Complexity: $(BIGOH 1). 947 * 948 * Throws: `Exception` if the array is empty. 949 */ 950 T removeAny() 951 { 952 auto result = back; 953 removeBack(); 954 return result; 955 } 956 957 /// ditto 958 alias stableRemoveAny = removeAny; 959 960 /** 961 * Inserts the specified elements at the back of the array. `stuff` can be 962 * a value convertible to `T` or a range of objects convertible to `T`. 963 * 964 * Returns: The number of elements inserted. 965 * 966 * Complexity: 967 * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), 968 * where `m` is the number of elements in `stuff`. 969 */ 970 size_t insertBack(Stuff)(Stuff stuff) 971 if (isImplicitlyConvertible!(Stuff, T) || 972 isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 973 { 974 _data.refCountedStore.ensureInitialized(); 975 return _data.insertBack(stuff); 976 } 977 978 /// ditto 979 alias insert = insertBack; 980 981 /** 982 * Removes the value from the back of the array. Both stable and non-stable 983 * versions behave the same and guarantee that ranges iterating over the 984 * array are never invalidated. 985 * 986 * Precondition: `empty == false` 987 * 988 * Complexity: $(BIGOH 1). 989 * 990 * Throws: `Exception` if the array is empty. 991 */ 992 void removeBack() 993 { 994 enforce(!empty); 995 static if (hasElaborateDestructor!T) 996 .destroy(_data._payload[$ - 1]); 997 998 _data._payload = _data._payload[0 .. $ - 1]; 999 } 1000 1001 /// ditto 1002 alias stableRemoveBack = removeBack; 1003 1004 /** 1005 * Removes `howMany` values from the back of the array. 1006 * Unlike the unparameterized versions above, these functions 1007 * do not throw if they could not remove `howMany` elements. Instead, 1008 * if `howMany > n`, all elements are removed. The returned value is 1009 * the effective number of elements removed. Both stable and non-stable 1010 * versions behave the same and guarantee that ranges iterating over 1011 * the array are never invalidated. 1012 * 1013 * Returns: The number of elements removed. 1014 * 1015 * Complexity: $(BIGOH howMany). 1016 */ 1017 size_t removeBack(size_t howMany) 1018 { 1019 if (howMany > length) howMany = length; 1020 static if (hasElaborateDestructor!T) 1021 foreach (ref e; _data._payload[$ - howMany .. $]) 1022 .destroy(e); 1023 1024 _data._payload = _data._payload[0 .. $ - howMany]; 1025 return howMany; 1026 } 1027 1028 /// ditto 1029 alias stableRemoveBack = removeBack; 1030 1031 /** 1032 * Inserts `stuff` before, after, or instead range `r`, which must 1033 * be a valid range previously extracted from this array. `stuff` 1034 * can be a value convertible to `T` or a range of objects convertible 1035 * to `T`. Both stable and non-stable version behave the same and 1036 * guarantee that ranges iterating over the array are never invalidated. 1037 * 1038 * Returns: The number of values inserted. 1039 * 1040 * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. 1041 * 1042 * Throws: `Exception` if `r` is not a range extracted from this array. 1043 */ 1044 size_t insertBefore(Stuff)(Range r, Stuff stuff) 1045 if (isImplicitlyConvertible!(Stuff, T)) 1046 { 1047 import core.lifetime : emplace; 1048 enforce(r._outer._data is _data && r._a <= length); 1049 reserve(length + 1); 1050 assert(_data.refCountedStore.isInitialized, 1051 "Failed to allocate capacity to insertBefore"); 1052 // Move elements over by one slot 1053 memmove(_data._payload.ptr + r._a + 1, 1054 _data._payload.ptr + r._a, 1055 T.sizeof * (length - r._a)); 1056 emplace(_data._payload.ptr + r._a, stuff); 1057 _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1]; 1058 return 1; 1059 } 1060 1061 /// ditto 1062 size_t insertBefore(Stuff)(Range r, Stuff stuff) 1063 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 1064 { 1065 import core.lifetime : emplace; 1066 enforce(r._outer._data is _data && r._a <= length); 1067 static if (isForwardRange!Stuff) 1068 { 1069 // Can find the length in advance 1070 auto extra = walkLength(stuff); 1071 if (!extra) return 0; 1072 reserve(length + extra); 1073 assert(_data.refCountedStore.isInitialized, 1074 "Failed to allocate capacity to insertBefore"); 1075 // Move elements over by extra slots 1076 memmove(_data._payload.ptr + r._a + extra, 1077 _data._payload.ptr + r._a, 1078 T.sizeof * (length - r._a)); 1079 foreach (p; _data._payload.ptr + r._a .. 1080 _data._payload.ptr + r._a + extra) 1081 { 1082 emplace(p, stuff.front); 1083 stuff.popFront(); 1084 } 1085 _data._payload = 1086 _data._payload.ptr[0 .. _data._payload.length + extra]; 1087 return extra; 1088 } 1089 else 1090 { 1091 import std.algorithm.mutation : bringToFront; 1092 enforce(_data); 1093 immutable offset = r._a; 1094 enforce(offset <= length); 1095 auto result = insertBack(stuff); 1096 bringToFront(this[offset .. length - result], 1097 this[length - result .. length]); 1098 return result; 1099 } 1100 } 1101 1102 /// ditto 1103 alias stableInsertBefore = insertBefore; 1104 1105 /// ditto 1106 size_t insertAfter(Stuff)(Range r, Stuff stuff) 1107 if (isImplicitlyConvertible!(Stuff, T) || 1108 isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 1109 { 1110 import std.algorithm.mutation : bringToFront; 1111 enforce(r._outer._data is _data); 1112 // TODO: optimize 1113 immutable offset = r._b; 1114 enforce(offset <= length); 1115 auto result = insertBack(stuff); 1116 bringToFront(this[offset .. length - result], 1117 this[length - result .. length]); 1118 return result; 1119 } 1120 1121 /// ditto 1122 size_t replace(Stuff)(Range r, Stuff stuff) 1123 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 1124 { 1125 enforce(r._outer._data is _data); 1126 size_t result; 1127 for (; !stuff.empty; stuff.popFront()) 1128 { 1129 if (r.empty) 1130 { 1131 // insert the rest 1132 return result + insertBefore(r, stuff); 1133 } 1134 r.front = stuff.front; 1135 r.popFront(); 1136 ++result; 1137 } 1138 // Remove remaining stuff in r 1139 linearRemove(r); 1140 return result; 1141 } 1142 1143 /// ditto 1144 size_t replace(Stuff)(Range r, Stuff stuff) 1145 if (isImplicitlyConvertible!(Stuff, T)) 1146 { 1147 enforce(r._outer._data is _data); 1148 if (r.empty) 1149 { 1150 insertBefore(r, stuff); 1151 } 1152 else 1153 { 1154 r.front = stuff; 1155 r.popFront(); 1156 linearRemove(r); 1157 } 1158 return 1; 1159 } 1160 1161 /** 1162 * Removes all elements belonging to `r`, which must be a range 1163 * obtained originally from this array. 1164 * 1165 * Returns: A range spanning the remaining elements in the array that 1166 * initially were right after `r`. 1167 * 1168 * Complexity: $(BIGOH length) 1169 * 1170 * Throws: `Exception` if `r` is not a valid range extracted from this array. 1171 */ 1172 Range linearRemove(Range r) 1173 { 1174 import std.algorithm.mutation : copy; 1175 1176 enforce(r._outer._data is _data); 1177 enforce(_data.refCountedStore.isInitialized); 1178 enforce(r._a <= r._b && r._b <= length); 1179 immutable offset1 = r._a; 1180 immutable offset2 = r._b; 1181 immutable tailLength = length - offset2; 1182 // Use copy here, not a[] = b[] because the ranges may overlap 1183 copy(this[offset2 .. length], this[offset1 .. offset1 + tailLength]); 1184 length = offset1 + tailLength; 1185 return this[length - tailLength .. length]; 1186 } 1187 } 1188 1189 @system unittest 1190 { 1191 Array!int a; 1192 assert(a.empty); 1193 } 1194 1195 @system unittest 1196 { 1197 Array!int a; 1198 a.length = 10; 1199 assert(a.length == 10); 1200 assert(a.capacity >= a.length); 1201 } 1202 1203 @system unittest 1204 { 1205 struct Dumb { int x = 5; } 1206 Array!Dumb a; 1207 a.length = 10; 1208 assert(a.length == 10); 1209 assert(a.capacity >= a.length); 1210 immutable cap = a.capacity; 1211 foreach (ref e; a) 1212 e.x = 10; 1213 a.length = 5; 1214 assert(a.length == 5); 1215 // do not realloc if length decreases 1216 assert(a.capacity == cap); 1217 foreach (ref e; a) 1218 assert(e.x == 10); 1219 1220 a.length = 8; 1221 assert(a.length == 8); 1222 // do not realloc if capacity sufficient 1223 assert(a.capacity == cap); 1224 assert(Dumb.init.x == 5); 1225 foreach (i; 0 .. 5) 1226 assert(a[i].x == 10); 1227 foreach (i; 5 .. a.length) 1228 assert(a[i].x == Dumb.init.x); 1229 1230 // realloc required, check if values properly copied 1231 a[] = Dumb(1); 1232 a.length = 20; 1233 assert(a.capacity >= 20); 1234 foreach (i; 0 .. 8) 1235 assert(a[i].x == 1); 1236 foreach (i; 8 .. a.length) 1237 assert(a[i].x == Dumb.init.x); 1238 1239 // check if overlapping elements properly initialized 1240 a.length = 1; 1241 a.length = 20; 1242 assert(a[0].x == 1); 1243 foreach (e; a[1 .. $]) 1244 assert(e.x == Dumb.init.x); 1245 } 1246 1247 @system unittest 1248 { 1249 Array!int a = Array!int(1, 2, 3); 1250 //a._data._refCountedDebug = true; 1251 auto b = a.dup; 1252 assert(b == Array!int(1, 2, 3)); 1253 b.front = 42; 1254 assert(b == Array!int(42, 2, 3)); 1255 assert(a == Array!int(1, 2, 3)); 1256 } 1257 1258 @system unittest 1259 { 1260 auto a = Array!int(1, 2, 3); 1261 assert(a.length == 3); 1262 } 1263 1264 @system unittest 1265 { 1266 const Array!int a = [1, 2]; 1267 1268 assert(a[0] == 1); 1269 assert(a.front == 1); 1270 assert(a.back == 2); 1271 1272 static assert(!__traits(compiles, { a[0] = 1; })); 1273 static assert(!__traits(compiles, { a.front = 1; })); 1274 static assert(!__traits(compiles, { a.back = 1; })); 1275 1276 auto r = a[]; 1277 size_t i; 1278 foreach (e; r) 1279 { 1280 assert(e == i + 1); 1281 i++; 1282 } 1283 } 1284 1285 @safe unittest 1286 { 1287 // https://issues.dlang.org/show_bug.cgi?id=13621 1288 import std.container : Array, BinaryHeap; 1289 alias Heap = BinaryHeap!(Array!int); 1290 } 1291 1292 @system unittest 1293 { 1294 // https://issues.dlang.org/show_bug.cgi?id=18800 1295 static struct S { void* p; } 1296 Array!S a; 1297 a.length = 10; 1298 } 1299 1300 @system unittest 1301 { 1302 Array!int a; 1303 a.reserve(1000); 1304 assert(a.length == 0); 1305 assert(a.empty); 1306 assert(a.capacity >= 1000); 1307 auto p = a._data._payload.ptr; 1308 foreach (i; 0 .. 1000) 1309 { 1310 a.insertBack(i); 1311 } 1312 assert(p == a._data._payload.ptr); 1313 } 1314 1315 @system unittest 1316 { 1317 auto a = Array!int(1, 2, 3); 1318 a[1] *= 42; 1319 assert(a[1] == 84); 1320 } 1321 1322 @system unittest 1323 { 1324 auto a = Array!int(1, 2, 3); 1325 auto b = Array!int(11, 12, 13); 1326 auto c = a ~ b; 1327 assert(c == Array!int(1, 2, 3, 11, 12, 13)); 1328 assert(a ~ b[] == Array!int(1, 2, 3, 11, 12, 13)); 1329 assert(a ~ [4,5] == Array!int(1,2,3,4,5)); 1330 } 1331 1332 @system unittest 1333 { 1334 auto a = Array!int(1, 2, 3); 1335 auto b = Array!int(11, 12, 13); 1336 a ~= b; 1337 assert(a == Array!int(1, 2, 3, 11, 12, 13)); 1338 } 1339 1340 @system unittest 1341 { 1342 auto a = Array!int(1, 2, 3, 4); 1343 assert(a.removeAny() == 4); 1344 assert(a == Array!int(1, 2, 3)); 1345 } 1346 1347 @system unittest 1348 { 1349 auto a = Array!int(1, 2, 3, 4, 5); 1350 auto r = a[2 .. a.length]; 1351 assert(a.insertBefore(r, 42) == 1); 1352 assert(a == Array!int(1, 2, 42, 3, 4, 5)); 1353 r = a[2 .. 2]; 1354 assert(a.insertBefore(r, [8, 9]) == 2); 1355 assert(a == Array!int(1, 2, 8, 9, 42, 3, 4, 5)); 1356 } 1357 1358 @system unittest 1359 { 1360 auto a = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8); 1361 a.linearRemove(a[4 .. 6]); 1362 assert(a == Array!int(0, 1, 2, 3, 6, 7, 8)); 1363 } 1364 1365 // Give the Range object some testing. 1366 @system unittest 1367 { 1368 import std.algorithm.comparison : equal; 1369 import std.range : retro; 1370 auto a = Array!int(0, 1, 2, 3, 4, 5, 6)[]; 1371 auto b = Array!int(6, 5, 4, 3, 2, 1, 0)[]; 1372 alias A = typeof(a); 1373 1374 static assert(isRandomAccessRange!A); 1375 static assert(hasSlicing!A); 1376 static assert(hasAssignableElements!A); 1377 static assert(hasMobileElements!A); 1378 1379 assert(equal(retro(b), a)); 1380 assert(a.length == 7); 1381 assert(equal(a[1 .. 4], [1, 2, 3])); 1382 } 1383 1384 // https://issues.dlang.org/show_bug.cgi?id=5920 1385 @system unittest 1386 { 1387 struct structBug5920 1388 { 1389 int order; 1390 uint* pDestructionMask; 1391 ~this() 1392 { 1393 if (pDestructionMask) 1394 *pDestructionMask += 1 << order; 1395 } 1396 } 1397 1398 alias S = structBug5920; 1399 uint dMask; 1400 1401 auto arr = Array!S(cast(S[])[]); 1402 foreach (i; 0 .. 8) 1403 arr.insertBack(S(i, &dMask)); 1404 // don't check dMask now as S may be copied multiple times (it's ok?) 1405 { 1406 assert(arr.length == 8); 1407 dMask = 0; 1408 arr.length = 6; 1409 assert(arr.length == 6); // make sure shrinking calls the d'tor 1410 assert(dMask == 0b1100_0000); 1411 arr.removeBack(); 1412 assert(arr.length == 5); // make sure removeBack() calls the d'tor 1413 assert(dMask == 0b1110_0000); 1414 arr.removeBack(3); 1415 assert(arr.length == 2); // ditto 1416 assert(dMask == 0b1111_1100); 1417 arr.clear(); 1418 assert(arr.length == 0); // make sure clear() calls the d'tor 1419 assert(dMask == 0b1111_1111); 1420 } 1421 assert(dMask == 0b1111_1111); // make sure the d'tor is called once only. 1422 } 1423 1424 // Test for https://issues.dlang.org/show_bug.cgi?id=5792 1425 // (mainly just to check if this piece of code is compilable) 1426 @system unittest 1427 { 1428 auto a = Array!(int[])([[1,2],[3,4]]); 1429 a.reserve(4); 1430 assert(a.capacity >= 4); 1431 assert(a.length == 2); 1432 assert(a[0] == [1,2]); 1433 assert(a[1] == [3,4]); 1434 a.reserve(16); 1435 assert(a.capacity >= 16); 1436 assert(a.length == 2); 1437 assert(a[0] == [1,2]); 1438 assert(a[1] == [3,4]); 1439 } 1440 1441 // test replace!Stuff with range Stuff 1442 @system unittest 1443 { 1444 import std.algorithm.comparison : equal; 1445 auto a = Array!int([1, 42, 5]); 1446 a.replace(a[1 .. 2], [2, 3, 4]); 1447 assert(equal(a[], [1, 2, 3, 4, 5])); 1448 } 1449 1450 // test insertBefore and replace with empty Arrays 1451 @system unittest 1452 { 1453 import std.algorithm.comparison : equal; 1454 auto a = Array!int(); 1455 a.insertBefore(a[], 1); 1456 assert(equal(a[], [1])); 1457 } 1458 @system unittest 1459 { 1460 import std.algorithm.comparison : equal; 1461 auto a = Array!int(); 1462 a.insertBefore(a[], [1, 2]); 1463 assert(equal(a[], [1, 2])); 1464 } 1465 @system unittest 1466 { 1467 import std.algorithm.comparison : equal; 1468 auto a = Array!int(); 1469 a.replace(a[], [1, 2]); 1470 assert(equal(a[], [1, 2])); 1471 } 1472 @system unittest 1473 { 1474 import std.algorithm.comparison : equal; 1475 auto a = Array!int(); 1476 a.replace(a[], 1); 1477 assert(equal(a[], [1])); 1478 } 1479 // make sure that Array instances refuse ranges that don't belong to them 1480 @system unittest 1481 { 1482 import std.exception : assertThrown; 1483 1484 Array!int a = [1, 2, 3]; 1485 auto r = a.dup[]; 1486 assertThrown(a.insertBefore(r, 42)); 1487 assertThrown(a.insertBefore(r, [42])); 1488 assertThrown(a.insertAfter(r, 42)); 1489 assertThrown(a.replace(r, 42)); 1490 assertThrown(a.replace(r, [42])); 1491 assertThrown(a.linearRemove(r)); 1492 } 1493 @system unittest 1494 { 1495 auto a = Array!int([1, 1]); 1496 a[1] = 0; //Check Array.opIndexAssign 1497 assert(a[1] == 0); 1498 a[1] += 1; //Check Array.opIndexOpAssign 1499 assert(a[1] == 1); 1500 1501 //Check Array.opIndexUnary 1502 ++a[0]; 1503 //a[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed 1504 assert(a[0] == 2); 1505 assert(+a[0] == +2); 1506 assert(-a[0] == -2); 1507 assert(~a[0] == ~2); 1508 1509 auto r = a[]; 1510 r[1] = 0; //Check Array.Range.opIndexAssign 1511 assert(r[1] == 0); 1512 r[1] += 1; //Check Array.Range.opIndexOpAssign 1513 assert(r[1] == 1); 1514 1515 //Check Array.Range.opIndexUnary 1516 ++r[0]; 1517 //r[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed 1518 assert(r[0] == 3); 1519 assert(+r[0] == +3); 1520 assert(-r[0] == -3); 1521 assert(~r[0] == ~3); 1522 } 1523 1524 @system unittest 1525 { 1526 import std.algorithm.comparison : equal; 1527 1528 //Test "array-wide" operations 1529 auto a = Array!int([0, 1, 2]); //Array 1530 a[] += 5; 1531 assert(a[].equal([5, 6, 7])); 1532 ++a[]; 1533 assert(a[].equal([6, 7, 8])); 1534 a[1 .. 3] *= 5; 1535 assert(a[].equal([6, 35, 40])); 1536 a[0 .. 2] = 0; 1537 assert(a[].equal([0, 0, 40])); 1538 1539 //Test empty array 1540 auto a2 = Array!int.init; 1541 ++a2[]; 1542 ++a2[0 .. 0]; 1543 a2[] = 0; 1544 a2[0 .. 0] = 0; 1545 a2[] += 0; 1546 a2[0 .. 0] += 0; 1547 1548 //Test "range-wide" operations 1549 auto r = Array!int([0, 1, 2])[]; //Array.Range 1550 r[] += 5; 1551 assert(r.equal([5, 6, 7])); 1552 ++r[]; 1553 assert(r.equal([6, 7, 8])); 1554 r[1 .. 3] *= 5; 1555 assert(r.equal([6, 35, 40])); 1556 r[0 .. 2] = 0; 1557 assert(r.equal([0, 0, 40])); 1558 1559 //Test empty Range 1560 auto r2 = Array!int.init[]; 1561 ++r2[]; 1562 ++r2[0 .. 0]; 1563 r2[] = 0; 1564 r2[0 .. 0] = 0; 1565 r2[] += 0; 1566 r2[0 .. 0] += 0; 1567 } 1568 1569 // Test issue 11194 1570 @system unittest 1571 { 1572 static struct S { 1573 int i = 1337; 1574 void* p; 1575 this(this) { assert(i == 1337); } 1576 ~this() { assert(i == 1337); } 1577 } 1578 Array!S arr; 1579 S s; 1580 arr ~= s; 1581 arr ~= s; 1582 } 1583 1584 // https://issues.dlang.org/show_bug.cgi?id=11459 1585 @safe unittest 1586 { 1587 static struct S 1588 { 1589 bool b; 1590 alias b this; 1591 } 1592 alias A = Array!S; 1593 alias B = Array!(shared bool); 1594 } 1595 1596 // https://issues.dlang.org/show_bug.cgi?id=11884 1597 @system unittest 1598 { 1599 import std.algorithm.iteration : filter; 1600 auto a = Array!int([1, 2, 2].filter!"true"()); 1601 } 1602 1603 // https://issues.dlang.org/show_bug.cgi?id=8282 1604 @safe unittest 1605 { 1606 auto arr = new Array!int; 1607 } 1608 1609 // https://issues.dlang.org/show_bug.cgi?id=6998 1610 @system unittest 1611 { 1612 static int i = 0; 1613 class C 1614 { 1615 int dummy = 1; 1616 this(){++i;} 1617 ~this(){--i;} 1618 } 1619 1620 assert(i == 0); 1621 auto c = new C(); 1622 assert(i == 1); 1623 1624 //scope 1625 { 1626 auto arr = Array!C(c); 1627 assert(i == 1); 1628 } 1629 //Array should not have destroyed the class instance 1630 assert(i == 1); 1631 1632 //Just to make sure the GC doesn't collect before the above test. 1633 assert(c.dummy == 1); 1634 } 1635 1636 //https://issues.dlang.org/show_bug.cgi?id=6998 (2) 1637 @system unittest 1638 { 1639 static class C {int i;} 1640 auto c = new C; 1641 c.i = 42; 1642 Array!C a; 1643 a ~= c; 1644 a.clear; 1645 assert(c.i == 42); //fails 1646 } 1647 1648 @safe unittest 1649 { 1650 static assert(is(Array!int.Range)); 1651 static assert(is(Array!int.ConstRange)); 1652 } 1653 1654 @system unittest // const/immutable Array and Ranges 1655 { 1656 static void test(A, R, E, S)() 1657 { 1658 A a; 1659 R r = a[]; 1660 assert(r.empty); 1661 assert(r.length == 0); 1662 static assert(is(typeof(r.front) == E)); 1663 static assert(is(typeof(r.back) == E)); 1664 static assert(is(typeof(r[0]) == E)); 1665 static assert(is(typeof(r[]) == S)); 1666 static assert(is(typeof(r[0 .. 0]) == S)); 1667 } 1668 1669 alias A = Array!int; 1670 1671 test!(A, A.Range, int, A.Range); 1672 test!(A, const A.Range, const int, A.ConstRange); 1673 1674 test!(const A, A.ConstRange, const int, A.ConstRange); 1675 test!(const A, const A.ConstRange, const int, A.ConstRange); 1676 1677 test!(immutable A, A.ImmutableRange, immutable int, A.ImmutableRange); 1678 test!(immutable A, const A.ImmutableRange, immutable int, A.ImmutableRange); 1679 test!(immutable A, immutable A.ImmutableRange, immutable int, 1680 A.ImmutableRange); 1681 } 1682 1683 // ensure @nogc 1684 @nogc @system unittest 1685 { 1686 Array!int ai; 1687 ai ~= 1; 1688 assert(ai.front == 1); 1689 1690 ai.reserve(10); 1691 assert(ai.capacity == 10); 1692 1693 static immutable arr = [1, 2, 3]; 1694 ai.insertBack(arr); 1695 } 1696 1697 /* 1698 * typeof may give wrong result in case of classes defining `opCall` operator 1699 * https://issues.dlang.org/show_bug.cgi?id=20589 1700 * 1701 * destructor std.container.array.Array!(MyClass).Array.~this is @system 1702 * so the unittest is @system too 1703 */ 1704 @system unittest 1705 { 1706 class MyClass 1707 { 1708 T opCall(T)(T p) 1709 { 1710 return p; 1711 } 1712 } 1713 1714 Array!MyClass arr; 1715 } 1716 1717 @system unittest 1718 { 1719 import std.algorithm.comparison : equal; 1720 auto a = Array!int([1,2,3,4,5]); 1721 assert(a.length == 5); 1722 1723 assert(a.insertAfter(a[0 .. 5], [7, 8]) == 2); 1724 assert(equal(a[], [1,2,3,4,5,7,8])); 1725 1726 assert(a.insertAfter(a[0 .. 5], 6) == 1); 1727 assert(equal(a[], [1,2,3,4,5,6,7,8])); 1728 } 1729 1730 // https://issues.dlang.org/show_bug.cgi?id=22105 1731 @system unittest 1732 { 1733 import core.exception : AssertError; 1734 import std.exception : assertThrown, assertNotThrown; 1735 1736 struct NoDefaultCtor 1737 { 1738 int i; 1739 @disable this(); 1740 this(int j) { i = j; } 1741 } 1742 1743 auto array = Array!NoDefaultCtor([NoDefaultCtor(1), NoDefaultCtor(2)]); 1744 assertNotThrown!AssertError(array.length = 1); 1745 assertThrown!AssertError(array.length = 5); 1746 } 1747 1748 // https://issues.dlang.org/show_bug.cgi?id=23140 1749 @system unittest 1750 { 1751 shared class C 1752 { 1753 } 1754 1755 Array!C ac; 1756 ac = Array!C([new C]); 1757 } 1758 //////////////////////////////////////////////////////////////////////////////// 1759 // Array!bool 1760 //////////////////////////////////////////////////////////////////////////////// 1761 1762 /** 1763 * _Array specialized for `bool`. Packs together values efficiently by 1764 * allocating one bit per element. 1765 */ 1766 struct Array(T) 1767 if (is(immutable T == immutable bool)) 1768 { 1769 import std.exception : enforce; 1770 import std.typecons : RefCounted, RefCountedAutoInitialize; 1771 1772 static immutable uint bitsPerWord = size_t.sizeof * 8; 1773 private static struct Data 1774 { 1775 Array!size_t.Payload _backend; 1776 size_t _length; 1777 } 1778 private RefCounted!(Data, RefCountedAutoInitialize.no) _store; 1779 1780 private @property ref size_t[] data() 1781 { 1782 assert(_store.refCountedStore.isInitialized, 1783 "Cannot get data of uninitialized Array"); 1784 return _store._backend._payload; 1785 } 1786 1787 /** 1788 * Defines the array's primary range. 1789 */ 1790 struct Range 1791 { 1792 private Array _outer; 1793 private size_t _a, _b; 1794 /// Range primitives 1795 @property Range save() 1796 { 1797 version (bug4437) 1798 { 1799 return this; 1800 } 1801 else 1802 { 1803 auto copy = this; 1804 return copy; 1805 } 1806 } 1807 /// Ditto 1808 @property bool empty() 1809 { 1810 return _a >= _b || _outer.length < _b; 1811 } 1812 /// Ditto 1813 @property T front() 1814 { 1815 enforce(!empty, "Attempting to access the front of an empty Array"); 1816 return _outer[_a]; 1817 } 1818 /// Ditto 1819 @property void front(bool value) 1820 { 1821 enforce(!empty, "Attempting to set the front of an empty Array"); 1822 _outer[_a] = value; 1823 } 1824 /// Ditto 1825 T moveFront() 1826 { 1827 enforce(!empty, "Attempting to move the front of an empty Array"); 1828 return _outer.moveAt(_a); 1829 } 1830 /// Ditto 1831 void popFront() 1832 { 1833 enforce(!empty, "Attempting to popFront an empty Array"); 1834 ++_a; 1835 } 1836 /// Ditto 1837 @property T back() 1838 { 1839 enforce(!empty, "Attempting to access the back of an empty Array"); 1840 return _outer[_b - 1]; 1841 } 1842 /// Ditto 1843 @property void back(bool value) 1844 { 1845 enforce(!empty, "Attempting to set the back of an empty Array"); 1846 _outer[_b - 1] = value; 1847 } 1848 /// Ditto 1849 T moveBack() 1850 { 1851 enforce(!empty, "Attempting to move the back of an empty Array"); 1852 return _outer.moveAt(_b - 1); 1853 } 1854 /// Ditto 1855 void popBack() 1856 { 1857 enforce(!empty, "Attempting to popBack an empty Array"); 1858 --_b; 1859 } 1860 /// Ditto 1861 T opIndex(size_t i) 1862 { 1863 return _outer[_a + i]; 1864 } 1865 /// Ditto 1866 void opIndexAssign(T value, size_t i) 1867 { 1868 _outer[_a + i] = value; 1869 } 1870 /// Ditto 1871 T moveAt(size_t i) 1872 { 1873 return _outer.moveAt(_a + i); 1874 } 1875 /// Ditto 1876 @property size_t length() const 1877 { 1878 assert(_a <= _b, "Invalid bounds"); 1879 return _b - _a; 1880 } 1881 alias opDollar = length; 1882 /// ditto 1883 Range opSlice(size_t low, size_t high) 1884 { 1885 // Note: indexes start at 0, which is equivalent to _a 1886 assert( 1887 low <= high && high <= (_b - _a), 1888 "Using out of bounds indexes on an Array" 1889 ); 1890 return Range(_outer, _a + low, _a + high); 1891 } 1892 } 1893 1894 /** 1895 * Constructor taking a number of items. 1896 */ 1897 this(U)(U[] values...) 1898 if (isImplicitlyConvertible!(U, T)) 1899 { 1900 reserve(values.length); 1901 foreach (i, v; values) 1902 { 1903 auto rem = i % bitsPerWord; 1904 if (rem) 1905 { 1906 // Fits within the current array 1907 if (v) 1908 { 1909 data[$ - 1] |= (cast(size_t) 1 << rem); 1910 } 1911 else 1912 { 1913 data[$ - 1] &= ~(cast(size_t) 1 << rem); 1914 } 1915 } 1916 else 1917 { 1918 // Need to add more data 1919 _store._backend.insertBack(v); 1920 } 1921 } 1922 _store._length = values.length; 1923 } 1924 1925 /** 1926 * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 1927 */ 1928 this(Range)(Range r) 1929 if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[])) 1930 { 1931 insertBack(r); 1932 } 1933 1934 /** 1935 * Property returning `true` if and only if the array has 1936 * no elements. 1937 * 1938 * Complexity: $(BIGOH 1) 1939 */ 1940 @property bool empty() 1941 { 1942 return !length; 1943 } 1944 1945 /** 1946 * Returns: A duplicate of the array. 1947 * 1948 * Complexity: $(BIGOH length). 1949 */ 1950 @property Array dup() 1951 { 1952 Array result; 1953 result.insertBack(this[]); 1954 return result; 1955 } 1956 1957 /** 1958 * Returns the number of elements in the array. 1959 * 1960 * Complexity: $(BIGOH 1). 1961 */ 1962 @property size_t length() const 1963 { 1964 return _store.refCountedStore.isInitialized ? _store._length : 0; 1965 } 1966 alias opDollar = length; 1967 1968 /** 1969 * Returns: The maximum number of elements the array can store without 1970 * reallocating memory and invalidating iterators upon insertion. 1971 * 1972 * Complexity: $(BIGOH 1). 1973 */ 1974 @property size_t capacity() 1975 { 1976 return _store.refCountedStore.isInitialized 1977 ? cast(size_t) bitsPerWord * _store._backend.capacity 1978 : 0; 1979 } 1980 1981 /** 1982 * Ensures sufficient capacity to accommodate `e` _elements. 1983 * If `e < capacity`, this method does nothing. 1984 * 1985 * Postcondition: `capacity >= e` 1986 * 1987 * Note: If the capacity is increased, one should assume that all 1988 * iterators to the elements are invalidated. 1989 * 1990 * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). 1991 */ 1992 void reserve(size_t e) 1993 { 1994 import std.conv : to; 1995 _store.refCountedStore.ensureInitialized(); 1996 _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord)); 1997 } 1998 1999 /** 2000 * Returns: A range that iterates over all elements of the array in forward order. 2001 * 2002 * Complexity: $(BIGOH 1) 2003 */ 2004 Range opSlice() 2005 { 2006 return Range(this, 0, length); 2007 } 2008 2009 2010 /** 2011 * Returns: A range that iterates the array between two specified positions. 2012 * 2013 * Complexity: $(BIGOH 1) 2014 */ 2015 Range opSlice(size_t a, size_t b) 2016 { 2017 enforce(a <= b && b <= length); 2018 return Range(this, a, b); 2019 } 2020 2021 /** 2022 * Returns: The first element of the array. 2023 * 2024 * Precondition: `empty == false` 2025 * 2026 * Complexity: $(BIGOH 1) 2027 * 2028 * Throws: `Exception` if the array is empty. 2029 */ 2030 @property bool front() 2031 { 2032 enforce(!empty); 2033 return data.ptr[0] & 1; 2034 } 2035 2036 /// Ditto 2037 @property void front(bool value) 2038 { 2039 enforce(!empty); 2040 if (value) data.ptr[0] |= 1; 2041 else data.ptr[0] &= ~cast(size_t) 1; 2042 } 2043 2044 /** 2045 * Returns: The last element of the array. 2046 * 2047 * Precondition: `empty == false` 2048 * 2049 * Complexity: $(BIGOH 1) 2050 * 2051 * Throws: `Exception` if the array is empty. 2052 */ 2053 @property bool back() 2054 { 2055 enforce(!empty); 2056 return cast(bool)(data.back & (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord))); 2057 } 2058 2059 /// Ditto 2060 @property void back(bool value) 2061 { 2062 enforce(!empty); 2063 if (value) 2064 { 2065 data.back |= (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)); 2066 } 2067 else 2068 { 2069 data.back &= 2070 ~(cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)); 2071 } 2072 } 2073 2074 /** 2075 * Indexing operators yielding or modifyng the value at the specified index. 2076 * 2077 * Precondition: `i < length` 2078 * 2079 * Complexity: $(BIGOH 1) 2080 */ 2081 bool opIndex(size_t i) 2082 { 2083 auto div = cast(size_t) (i / bitsPerWord); 2084 auto rem = i % bitsPerWord; 2085 enforce(div < data.length); 2086 return cast(bool)(data.ptr[div] & (cast(size_t) 1 << rem)); 2087 } 2088 2089 /// ditto 2090 void opIndexAssign(bool value, size_t i) 2091 { 2092 auto div = cast(size_t) (i / bitsPerWord); 2093 auto rem = i % bitsPerWord; 2094 enforce(div < data.length); 2095 if (value) data.ptr[div] |= (cast(size_t) 1 << rem); 2096 else data.ptr[div] &= ~(cast(size_t) 1 << rem); 2097 } 2098 2099 /// ditto 2100 void opIndexOpAssign(string op)(bool value, size_t i) 2101 { 2102 auto div = cast(size_t) (i / bitsPerWord); 2103 auto rem = i % bitsPerWord; 2104 enforce(div < data.length); 2105 auto oldValue = cast(bool) (data.ptr[div] & (cast(size_t) 1 << rem)); 2106 // Do the deed 2107 auto newValue = mixin("oldValue "~op~" value"); 2108 // Write back the value 2109 if (newValue != oldValue) 2110 { 2111 if (newValue) data.ptr[div] |= (cast(size_t) 1 << rem); 2112 else data.ptr[div] &= ~(cast(size_t) 1 << rem); 2113 } 2114 } 2115 2116 /// Ditto 2117 T moveAt(size_t i) 2118 { 2119 return this[i]; 2120 } 2121 2122 /** 2123 * Returns: A new array which is a concatenation of `this` and its argument. 2124 * 2125 * Complexity: 2126 * $(BIGOH length + m), where `m` is the number of elements in `stuff`. 2127 */ 2128 Array!bool opBinary(string op, Stuff)(Stuff rhs) 2129 if (op == "~") 2130 { 2131 Array!bool result; 2132 2133 static if (hasLength!Stuff) 2134 result.reserve(length + rhs.length); 2135 else static if (is(typeof(rhs[])) && hasLength!(typeof(rhs[]))) 2136 result.reserve(length + rhs[].length); 2137 else static if (isImplicitlyConvertible!(Stuff, bool)) 2138 result.reserve(length + 1); 2139 2140 result.insertBack(this[]); 2141 result ~= rhs; 2142 return result; 2143 } 2144 2145 /** 2146 * Forwards to `insertBack`. 2147 */ 2148 Array!bool opOpAssign(string op, Stuff)(Stuff stuff) 2149 if (op == "~") 2150 { 2151 static if (is(typeof(stuff[]))) insertBack(stuff[]); 2152 else insertBack(stuff); 2153 return this; 2154 } 2155 2156 /** 2157 * Removes all the elements from the array and releases allocated memory. 2158 * 2159 * Postcondition: `empty == true && capacity == 0` 2160 * 2161 * Complexity: $(BIGOH length) 2162 */ 2163 void clear() 2164 { 2165 this = Array(); 2166 } 2167 2168 /** 2169 * Sets the number of elements in the array to `newLength`. If `newLength` 2170 * is greater than `length`, the new elements are added to the end of the 2171 * array and initialized with `false`. 2172 * 2173 * Complexity: 2174 * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. 2175 * If `capacity < newLength` the worst case is $(BIGOH newLength). 2176 * 2177 * Postcondition: `length == newLength` 2178 */ 2179 @property void length(size_t newLength) 2180 { 2181 import std.conv : to; 2182 _store.refCountedStore.ensureInitialized(); 2183 auto newDataLength = 2184 to!size_t((newLength + bitsPerWord - 1) / bitsPerWord); 2185 _store._backend.length = newDataLength; 2186 _store._length = newLength; 2187 } 2188 2189 /** 2190 * Removes the last element from the array and returns it. 2191 * Both stable and non-stable versions behave the same and guarantee 2192 * that ranges iterating over the array are never invalidated. 2193 * 2194 * Precondition: `empty == false` 2195 * 2196 * Returns: The element removed. 2197 * 2198 * Complexity: $(BIGOH 1). 2199 * 2200 * Throws: `Exception` if the array is empty. 2201 */ 2202 T removeAny() 2203 { 2204 auto result = back; 2205 removeBack(); 2206 return result; 2207 } 2208 2209 /// ditto 2210 alias stableRemoveAny = removeAny; 2211 2212 /** 2213 * Inserts the specified elements at the back of the array. `stuff` can be 2214 * a value convertible to `bool` or a range of objects convertible to `bool`. 2215 * 2216 * Returns: The number of elements inserted. 2217 * 2218 * Complexity: 2219 * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), 2220 * where `m` is the number of elements in `stuff`. 2221 */ 2222 size_t insertBack(Stuff)(Stuff stuff) 2223 if (is(Stuff : bool)) 2224 { 2225 _store.refCountedStore.ensureInitialized(); 2226 auto rem = _store._length % bitsPerWord; 2227 if (rem) 2228 { 2229 // Fits within the current array 2230 if (stuff) 2231 { 2232 data[$ - 1] |= (cast(size_t) 1 << rem); 2233 } 2234 else 2235 { 2236 data[$ - 1] &= ~(cast(size_t) 1 << rem); 2237 } 2238 } 2239 else 2240 { 2241 // Need to add more data 2242 _store._backend.insertBack(stuff); 2243 } 2244 ++_store._length; 2245 return 1; 2246 } 2247 2248 /// ditto 2249 size_t insertBack(Stuff)(Stuff stuff) 2250 if (isInputRange!Stuff && is(ElementType!Stuff : bool)) 2251 { 2252 size_t result; 2253 static if (hasLength!Stuff) 2254 result = stuff.length; 2255 2256 for (; !stuff.empty; stuff.popFront()) 2257 { 2258 insertBack(stuff.front); 2259 static if (!hasLength!Stuff) ++result; 2260 } 2261 2262 return result; 2263 } 2264 2265 /// ditto 2266 alias stableInsertBack = insertBack; 2267 2268 /// ditto 2269 alias insert = insertBack; 2270 2271 /// ditto 2272 alias stableInsert = insertBack; 2273 2274 /// ditto 2275 alias linearInsert = insertBack; 2276 2277 /// ditto 2278 alias stableLinearInsert = insertBack; 2279 2280 /** 2281 * Removes the value from the back of the array. Both stable and non-stable 2282 * versions behave the same and guarantee that ranges iterating over the 2283 * array are never invalidated. 2284 * 2285 * Precondition: `empty == false` 2286 * 2287 * Complexity: $(BIGOH 1). 2288 * 2289 * Throws: `Exception` if the array is empty. 2290 */ 2291 void removeBack() 2292 { 2293 enforce(_store._length); 2294 if (_store._length % bitsPerWord) 2295 { 2296 // Cool, just decrease the length 2297 --_store._length; 2298 } 2299 else 2300 { 2301 // Reduce the allocated space 2302 --_store._length; 2303 _store._backend.length = _store._backend.length - 1; 2304 } 2305 } 2306 2307 /// ditto 2308 alias stableRemoveBack = removeBack; 2309 2310 /** 2311 * Removes `howMany` values from the back of the array. Unlike the 2312 * unparameterized versions above, these functions do not throw if 2313 * they could not remove `howMany` elements. Instead, if `howMany > n`, 2314 * all elements are removed. The returned value is the effective number 2315 * of elements removed. Both stable and non-stable versions behave the same 2316 * and guarantee that ranges iterating over the array are never invalidated. 2317 * 2318 * Returns: The number of elements removed. 2319 * 2320 * Complexity: $(BIGOH howMany). 2321 */ 2322 size_t removeBack(size_t howMany) 2323 { 2324 if (howMany >= length) 2325 { 2326 howMany = length; 2327 clear(); 2328 } 2329 else 2330 { 2331 length = length - howMany; 2332 } 2333 return howMany; 2334 } 2335 2336 /// ditto 2337 alias stableRemoveBack = removeBack; 2338 2339 /** 2340 * Inserts `stuff` before, after, or instead range `r`, which must 2341 * be a valid range previously extracted from this array. `stuff` 2342 * can be a value convertible to `bool` or a range of objects convertible 2343 * to `bool`. Both stable and non-stable version behave the same and 2344 * guarantee that ranges iterating over the array are never invalidated. 2345 * 2346 * Returns: The number of values inserted. 2347 * 2348 * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. 2349 */ 2350 size_t insertBefore(Stuff)(Range r, Stuff stuff) 2351 { 2352 import std.algorithm.mutation : bringToFront; 2353 // TODO: make this faster, it moves one bit at a time 2354 immutable inserted = stableInsertBack(stuff); 2355 immutable tailLength = length - inserted; 2356 bringToFront( 2357 this[r._a .. tailLength], 2358 this[tailLength .. length]); 2359 return inserted; 2360 } 2361 2362 /// ditto 2363 alias stableInsertBefore = insertBefore; 2364 2365 /// ditto 2366 size_t insertAfter(Stuff)(Range r, Stuff stuff) 2367 if (isImplicitlyConvertible!(Stuff, T) || 2368 isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 2369 { 2370 import std.algorithm.mutation : bringToFront; 2371 // TODO: make this faster, it moves one bit at a time 2372 immutable inserted = stableInsertBack(stuff); 2373 immutable tailLength = length - inserted; 2374 bringToFront( 2375 this[r._b .. tailLength], 2376 this[tailLength .. length]); 2377 return inserted; 2378 } 2379 2380 /// ditto 2381 alias stableInsertAfter = insertAfter; 2382 2383 /// ditto 2384 size_t replace(Stuff)(Range r, Stuff stuff) 2385 if (is(Stuff : bool)) 2386 { 2387 if (!r.empty) 2388 { 2389 // There is room 2390 r.front = stuff; 2391 r.popFront(); 2392 linearRemove(r); 2393 } 2394 else 2395 { 2396 // No room, must insert 2397 insertBefore(r, stuff); 2398 } 2399 return 1; 2400 } 2401 2402 /// ditto 2403 alias stableReplace = replace; 2404 2405 /** 2406 * Removes all elements belonging to `r`, which must be a range 2407 * obtained originally from this array. 2408 * 2409 * Returns: A range spanning the remaining elements in the array that 2410 * initially were right after `r`. 2411 * 2412 * Complexity: $(BIGOH length) 2413 */ 2414 Range linearRemove(Range r) 2415 { 2416 import std.algorithm.mutation : copy; 2417 copy(this[r._b .. length], this[r._a .. length]); 2418 length = length - r.length; 2419 return this[r._a .. length]; 2420 } 2421 } 2422 2423 @system unittest 2424 { 2425 import std.algorithm.comparison : equal; 2426 2427 auto a = Array!bool([true, true, false, false, true, false]); 2428 assert(equal(a[], [true, true, false, false, true, false])); 2429 } 2430 2431 // using Ranges 2432 @system unittest 2433 { 2434 import std.algorithm.comparison : equal; 2435 import std.range : retro; 2436 bool[] arr = [true, true, false, false, true, false]; 2437 2438 auto a = Array!bool(retro(arr)); 2439 assert(equal(a[], retro(arr))); 2440 } 2441 2442 @system unittest 2443 { 2444 Array!bool a; 2445 assert(a.empty); 2446 } 2447 2448 @system unittest 2449 { 2450 auto arr = Array!bool([false, false, false, false]); 2451 assert(arr.front == false); 2452 assert(arr.back == false); 2453 assert(arr[1] == false); 2454 auto slice = arr[]; 2455 slice = arr[0 .. $]; 2456 slice = slice[1 .. $]; 2457 slice.front = true; 2458 slice.back = true; 2459 slice[1] = true; 2460 slice = slice[0 .. $]; // https://issues.dlang.org/show_bug.cgi?id=19171 2461 assert(slice.front == true); 2462 assert(slice.back == true); 2463 assert(slice[1] == true); 2464 assert(slice.moveFront == true); 2465 assert(slice.moveBack == true); 2466 assert(slice.moveAt(1) == true); 2467 } 2468 2469 // uncomparable values are valid values for an array 2470 // https://issues.dlang.org/show_bug.cgi?id=16331 2471 @system unittest 2472 { 2473 double[] values = [double.nan, double.nan]; 2474 auto arr = Array!double(values); 2475 } 2476 2477 @nogc @system unittest 2478 { 2479 auto a = Array!int(0, 1, 2); 2480 int[3] b = [3, 4, 5]; 2481 short[3] ci = [0, 1, 0]; 2482 auto c = Array!short(ci); 2483 assert(Array!int(0, 1, 2, 0, 1, 2) == a ~ a); 2484 assert(Array!int(0, 1, 2, 3, 4, 5) == a ~ b); 2485 assert(Array!int(0, 1, 2, 3) == a ~ 3); 2486 assert(Array!int(0, 1, 2, 0, 1, 0) == a ~ c); 2487 } 2488 2489 @nogc @system unittest 2490 { 2491 auto a = Array!char('a', 'b'); 2492 assert(Array!char("abc") == a ~ 'c'); 2493 import std.utf : byCodeUnit; 2494 assert(Array!char("abcd") == a ~ "cd".byCodeUnit); 2495 } 2496 2497 @nogc @system unittest 2498 { 2499 auto a = Array!dchar("ąćę"d); 2500 assert(Array!dchar("ąćęϢϖ"d) == a ~ "Ϣϖ"d); 2501 wchar x = 'Ϣ'; 2502 assert(Array!dchar("ąćęϢz"d) == a ~ x ~ 'z'); 2503 } 2504 2505 @system unittest 2506 { 2507 Array!bool a; 2508 assert(a.empty); 2509 a.insertBack(false); 2510 assert(!a.empty); 2511 } 2512 2513 @system unittest 2514 { 2515 Array!bool a; 2516 assert(a.empty); 2517 auto b = a.dup; 2518 assert(b.empty); 2519 a.insertBack(true); 2520 assert(b.empty); 2521 } 2522 2523 @system unittest 2524 { 2525 import std.conv : to; 2526 Array!bool a; 2527 assert(a.length == 0); 2528 a.insert(true); 2529 assert(a.length == 1, to!string(a.length)); 2530 } 2531 2532 @system unittest 2533 { 2534 import std.conv : to; 2535 Array!bool a; 2536 assert(a.capacity == 0); 2537 foreach (i; 0 .. 100) 2538 { 2539 a.insert(true); 2540 assert(a.capacity >= a.length, to!string(a.capacity)); 2541 } 2542 } 2543 2544 @system unittest 2545 { 2546 Array!bool a; 2547 assert(a.capacity == 0); 2548 a.reserve(15657); 2549 assert(a.capacity >= 15657); 2550 a.reserve(100); 2551 assert(a.capacity >= 15657); 2552 } 2553 2554 @system unittest 2555 { 2556 auto a = Array!bool([true, false, true, true]); 2557 assert(a[0 .. 2].length == 2); 2558 } 2559 2560 @system unittest 2561 { 2562 auto a = Array!bool([true, false, true, true]); 2563 assert(a[].length == 4); 2564 } 2565 2566 @system unittest 2567 { 2568 auto a = Array!bool([true, false, true, true]); 2569 assert(a.front); 2570 a.front = false; 2571 assert(!a.front); 2572 } 2573 2574 @system unittest 2575 { 2576 auto a = Array!bool([true, false, true, true]); 2577 assert(a[].length == 4); 2578 } 2579 2580 @system unittest 2581 { 2582 auto a = Array!bool([true, false, true, true]); 2583 assert(a.back); 2584 a.back = false; 2585 assert(!a.back); 2586 } 2587 2588 @system unittest 2589 { 2590 auto a = Array!bool([true, false, true, true]); 2591 assert(a[0] && !a[1]); 2592 a[0] &= a[1]; 2593 assert(!a[0]); 2594 } 2595 2596 @system unittest 2597 { 2598 import std.algorithm.comparison : equal; 2599 auto a = Array!bool([true, false, true, true]); 2600 auto b = Array!bool([true, true, false, true]); 2601 assert(equal((a ~ b)[], 2602 [true, false, true, true, true, true, false, true])); 2603 assert((a ~ [true, false])[].equal([true, false, true, true, true, false])); 2604 Array!bool c; 2605 c.insertBack(true); 2606 assert((c ~ false)[].equal([true, false])); 2607 } 2608 @system unittest 2609 { 2610 import std.algorithm.comparison : equal; 2611 auto a = Array!bool([true, false, true, true]); 2612 auto b = Array!bool([false, true, false, true, true]); 2613 a ~= b; 2614 assert(equal( 2615 a[], 2616 [true, false, true, true, false, true, false, true, true])); 2617 } 2618 2619 @system unittest 2620 { 2621 auto a = Array!bool([true, false, true, true]); 2622 a.clear(); 2623 assert(a.capacity == 0); 2624 } 2625 2626 @system unittest 2627 { 2628 Array!bool a; 2629 a.length = 1057; 2630 assert(a.length == 1057); 2631 assert(a.capacity >= a.length); 2632 foreach (e; a) 2633 { 2634 assert(!e); 2635 } 2636 immutable cap = a.capacity; 2637 a.length = 100; 2638 assert(a.length == 100); 2639 // do not realloc if length decreases 2640 assert(a.capacity == cap); 2641 } 2642 2643 @system unittest 2644 { 2645 Array!bool a; 2646 a.length = 1057; 2647 assert(!a.removeAny()); 2648 assert(a.length == 1056); 2649 foreach (e; a) 2650 { 2651 assert(!e); 2652 } 2653 } 2654 2655 @system unittest 2656 { 2657 Array!bool a; 2658 for (int i = 0; i < 100; ++i) 2659 a.insertBack(true); 2660 foreach (e; a) 2661 assert(e); 2662 } 2663 2664 @system unittest 2665 { 2666 Array!bool a; 2667 a.length = 1057; 2668 assert(a.removeBack(1000) == 1000); 2669 assert(a.length == 57); 2670 foreach (e; a) 2671 { 2672 assert(!e); 2673 } 2674 } 2675 2676 @system unittest 2677 { 2678 import std.conv : to; 2679 Array!bool a; 2680 version (bugxxxx) 2681 { 2682 a._store.refCountedDebug = true; 2683 } 2684 a.insertBefore(a[], true); 2685 assert(a.length == 1, to!string(a.length)); 2686 a.insertBefore(a[], false); 2687 assert(a.length == 2, to!string(a.length)); 2688 a.insertBefore(a[1 .. $], true); 2689 import std.algorithm.comparison : equal; 2690 assert(a[].equal([false, true, true])); 2691 } 2692 2693 // https://issues.dlang.org/show_bug.cgi?id=21555 2694 @system unittest 2695 { 2696 import std.algorithm.comparison : equal; 2697 Array!bool arr; 2698 size_t len = arr.insertBack([false, true]); 2699 assert(len == 2); 2700 } 2701 2702 // https://issues.dlang.org/show_bug.cgi?id=21556 2703 @system unittest 2704 { 2705 import std.algorithm.comparison : equal; 2706 Array!bool a; 2707 a.insertBack([true, true, false, false, true]); 2708 assert(a.length == 5); 2709 2710 assert(a.insertAfter(a[0 .. 5], [false, false]) == 2); 2711 assert(equal(a[], [true, true, false, false, true, false, false])); 2712 2713 assert(a.insertAfter(a[0 .. 5], true) == 1); 2714 assert(equal(a[], [true, true, false, false, true, true, false, false])); 2715 } 2716 2717 @system unittest 2718 { 2719 import std.conv : to; 2720 Array!bool a; 2721 a.length = 10; 2722 a.insertAfter(a[0 .. 5], true); 2723 assert(a.length == 11, to!string(a.length)); 2724 assert(a[5]); 2725 } 2726 @system unittest 2727 { 2728 alias V3 = int[3]; 2729 V3 v = [1, 2, 3]; 2730 Array!V3 arr; 2731 arr ~= v; 2732 assert(arr[0] == [1, 2, 3]); 2733 } 2734 @system unittest 2735 { 2736 alias V3 = int[3]; 2737 V3[2] v = [[1, 2, 3], [4, 5, 6]]; 2738 Array!V3 arr; 2739 arr ~= v; 2740 assert(arr[0] == [1, 2, 3]); 2741 assert(arr[1] == [4, 5, 6]); 2742 } 2743 2744 // Change of length reallocates without calling GC. 2745 // https://issues.dlang.org/show_bug.cgi?id=13642 2746 @system unittest 2747 { 2748 import core.memory; 2749 class ABC { void func() { int x = 5; } } 2750 2751 Array!ABC arr; 2752 // Length only allocates if capacity is too low. 2753 arr.reserve(4); 2754 assert(arr.capacity == 4); 2755 2756 void func() @nogc 2757 { 2758 arr.length = 5; 2759 } 2760 func(); 2761 2762 foreach (ref b; arr) b = new ABC; 2763 GC.collect(); 2764 arr[1].func(); 2765 } 2766 2767 @system unittest 2768 { 2769 2770 Array!int arr = [1, 2, 4, 5]; 2771 int[] data = arr.data(); 2772 2773 const Array!int arr2 = [8, 9]; 2774 assert(arr2.data() == [8, 9]); 2775 2776 data[0] = 0; 2777 assert(arr[0] == 0); 2778 2779 arr.length = 0; 2780 assert(arr.data == []); 2781 2782 Array!int empty; 2783 assert(empty.data == []); 2784 } 2785