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