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