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