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