1 ///
2 module stdx.collections.array;
3 
4 import stdx.collections.common;
5 
6 debug(CollectionArray) import std.stdio;
7 
8 version(unittest)
9 {
10     import std.experimental.allocator.mallocator;
11     import std.experimental.allocator.building_blocks.stats_collector;
12     import std.experimental.allocator : RCIAllocator, RCISharedAllocator,
13            allocatorObject, sharedAllocatorObject;
14     import std.stdio;
15 
16     private alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
17 }
18 
19 ///
20 struct Array(T)
21 {
22     import std.experimental.allocator : RCIAllocator, RCISharedAllocator,
23            make, dispose, stateSize;
24     import std.experimental.allocator.building_blocks.affix_allocator;
25     import std.traits : isImplicitlyConvertible, Unqual, isArray;
26     import std.range.primitives : isInputRange, isInfinite, ElementType, hasLength;
27     import std.conv : emplace;
28     import core.atomic : atomicOp;
29     import std.algorithm.mutation : move;
30 
31     package T[] _payload;
32     package Unqual!T[] _support;
33 
34 private:
35 
36     static enum double capacityFactor = 3.0 / 2;
37     static enum initCapacity = 3;
38 
39     mixin(allocatorHandler);
40 
41     @nogc nothrow pure @trusted
42     void addRef(SupportQual, this Q)(SupportQual support)
43     {
44         assert(support !is null);
45         cast(void) _allocator.opPrefix!("+=")(support, 1);
46     }
47 
48     void delRef(Unqual!T[] support)
49     {
50         // Will be optimized away, but the type system infers T's safety
51         if (0) { T t = T.init; }
52 
53         assert(support !is null);
54         if (_allocator.opPrefix!("-=")(support, 1) == 0)
55         {
56             () @trusted { dispose(_allocator, support); }();
57         }
58     }
59 
60     static string immutableInsert(StuffType)(string stuff)
61     {
62         static if (hasLength!StuffType)
63         {
64             auto stuffLengthStr = q{
65                 size_t stuffLength = } ~ stuff ~ ".length;";
66         }
67         else
68         {
69             auto stuffLengthStr = q{
70                 import std.range.primitives : walkLength;
71                 size_t stuffLength = walkLength(} ~ stuff ~ ");";
72         }
73 
74         return stuffLengthStr ~ q{
75         _allocator = immutable AllocatorHandler(allocator);
76         auto tmpSupport = (() @trusted => cast(Unqual!T[])(_allocator.allocate(stuffLength * T.sizeof)))();
77         assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is null));
78         size_t i = 0;
79         foreach (item; } ~ stuff ~ q{)
80         {
81           (() @trusted => emplace(&tmpSupport[i++], item))();
82         }
83         _support = (() @trusted => cast(typeof(_support))(tmpSupport))();
84         _payload = (() @trusted => cast(typeof(_payload))(_support[0 .. stuffLength]))();
85         if (_support) addRef(_support);
86         };
87     }
88 
89     void destroyUnused()
90     {
91         debug(CollectionArray)
92         {
93             writefln("Array.destroyUnused: begin");
94             scope(exit) writefln("Array.destroyUnused: end");
95         }
96         if (_support !is null)
97         {
98             delRef(_support);
99         }
100     }
101 
102 public:
103     /**
104      * Constructs a qualified array that will use the provided
105      * allocator object. For `immutable` objects, a `RCISharedAllocator` must
106      * be supplied.
107      *
108      * Params:
109      *      allocator = a $(REF RCIAllocator, std,experimental,allocator) or
110      *                  $(REF RCISharedAllocator, std,experimental,allocator)
111      *                  allocator object
112      *
113      * Complexity: $(BIGOH 1)
114      */
115     this(A, this Q)(A allocator)
116     if (!is(Q == shared)
117         && (is(A == RCISharedAllocator) || !is(Q == immutable))
118         && (is(A == RCIAllocator) || is(A == RCISharedAllocator)))
119     {
120         debug(CollectionArray)
121         {
122             writefln("Array.ctor: begin");
123             scope(exit) writefln("Array.ctor: end");
124         }
125         static if (is(Q == immutable) || is(Q == const))
126         {
127             T[] empty;
128             this(allocator, empty);
129         }
130         else
131         {
132             setAllocator(allocator);
133         }
134     }
135 
136     ///
137     static if (is(T == int))
138     @safe unittest
139     {
140         import std.experimental.allocator : theAllocator, processAllocator;
141 
142         auto a = Array!int(theAllocator);
143         auto ca = const Array!int(processAllocator);
144         auto ia = immutable Array!int(processAllocator);
145     }
146 
147     /**
148      * Constructs a qualified array out of a number of items.
149      * Because no allocator was provided, the array will use the
150      * $(REF, GCAllocator, std,experimental,allocator,gc_allocator).
151      *
152      * Params:
153      *      values = a variable number of items, either in the form of a
154      *               list or as a built-in array
155      *
156      * Complexity: $(BIGOH m), where `m` is the number of items.
157      */
158     this(U, this Q)(U[] values...)
159     if (isImplicitlyConvertible!(U, T))
160     {
161         this(defaultAllocator!(typeof(this)), values);
162     }
163 
164     ///
165     static if (is(T == int))
166     @safe unittest
167     {
168         import std.algorithm.comparison : equal;
169 
170         // Create a list from a list of ints
171         {
172             auto a = Array!int(1, 2, 3);
173             assert(equal(a, [1, 2, 3]));
174         }
175         // Create a list from an array of ints
176         {
177             auto a = Array!int([1, 2, 3]);
178             assert(equal(a, [1, 2, 3]));
179         }
180         // Create a list from a list from an input range
181         {
182             auto a = Array!int(1, 2, 3);
183             auto a2 = Array!int(a);
184             assert(equal(a2, [1, 2, 3]));
185         }
186     }
187 
188     /**
189      * Constructs a qualified array out of a number of items
190      * that will use the provided allocator object.
191      * For `immutable` objects, a `RCISharedAllocator` must be supplied.
192      *
193      * Params:
194      *      allocator = a $(REF RCIAllocator, std,experimental,allocator) or
195      *                  $(REF RCISharedAllocator, std,experimental,allocator)
196      *                  allocator object
197      *      values = a variable number of items, either in the form of a
198      *               list or as a built-in array
199      *
200      * Complexity: $(BIGOH m), where `m` is the number of items.
201      */
202     this(A, U, this Q)(A allocator, U[] values...)
203     if (!is(Q == shared)
204         && (is(A == RCISharedAllocator) || !is(Q == immutable))
205         && (is(A == RCIAllocator) || is(A == RCISharedAllocator))
206         && isImplicitlyConvertible!(U, T))
207     {
208         debug(CollectionArray)
209         {
210             writefln("Array.ctor: begin");
211             scope(exit) writefln("Array.ctor: end");
212         }
213         static if (is(Q == immutable) || is(Q == const))
214         {
215             mixin(immutableInsert!(typeof(values))("values"));
216         }
217         else
218         {
219             setAllocator(allocator);
220             insert(0, values);
221         }
222     }
223 
224     /**
225      * Constructs a qualified array out of an
226      * $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
227      * Because no allocator was provided, the array will use the
228      * $(REF, GCAllocator, std,experimental,allocator,gc_allocator).
229      * If `Stuff` defines `length`, `Array` will use it to reserve the
230      * necessary amount of memory.
231      *
232      * Params:
233      *      stuff = an input range of elements that are implitictly convertible
234      *              to `T`
235      *
236      * Complexity: $(BIGOH m), where `m` is the number of elements in the range.
237      */
238     this(Stuff, this Q)(Stuff stuff)
239     if (isInputRange!Stuff && !isInfinite!Stuff
240         && isImplicitlyConvertible!(ElementType!Stuff, T)
241         && !is(Stuff == T[]))
242     {
243         this(defaultAllocator!(typeof(this)), stuff);
244     }
245 
246     /**
247      * Constructs a qualified array out of an
248      * $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
249      * that will use the provided allocator object.
250      * For `immutable` objects, a `RCISharedAllocator` must be supplied.
251      * If `Stuff` defines `length`, `Array` will use it to reserve the
252      * necessary amount of memory.
253      *
254      * Params:
255      *      allocator = a $(REF RCIAllocator, std,experimental,allocator) or
256      *                  $(REF RCISharedAllocator, std,experimental,allocator)
257      *                  allocator object
258      *      stuff = an input range of elements that are implitictly convertible
259      *              to `T`
260      *
261      * Complexity: $(BIGOH m), where `m` is the number of elements in the range.
262      */
263     this(A, Stuff, this Q)(A allocator, Stuff stuff)
264     if (!is(Q == shared)
265         && (is(A == RCISharedAllocator) || !is(Q == immutable))
266         && (is(A == RCIAllocator) || is(A == RCISharedAllocator))
267         && isInputRange!Stuff
268         && isImplicitlyConvertible!(ElementType!Stuff, T)
269         && !is(Stuff == T[]))
270     {
271         debug(CollectionArray)
272         {
273             writefln("Array.ctor: begin");
274             scope(exit) writefln("Array.ctor: end");
275         }
276         static if (is(Q == immutable) || is(Q == const))
277         {
278             mixin(immutableInsert!(typeof(stuff))("stuff"));
279         }
280         else
281         {
282             setAllocator(allocator);
283             insert(0, stuff);
284         }
285     }
286 
287     this(this)
288     {
289         debug(CollectionArray)
290         {
291             writefln("Array.postblit: begin");
292             scope(exit) writefln("Array.postblit: end");
293         }
294         _allocator.bootstrap();
295         if (_support !is null)
296         {
297             addRef(_support);
298             debug(CollectionArray) writefln("Array.postblit: Array %s has refcount: %s",
299                     _support, *prefCount(_support));
300         }
301     }
302 
303     // Immutable ctors
304     // Very important to pass the allocator by ref! (Related to postblit bug)
305     private this(SuppQual, PaylQual, AllocQual, this Qualified)(SuppQual support,
306             PaylQual payload, ref AllocQual _newAllocator)
307         if (is(typeof(_support) : typeof(support)))
308     {
309         _support = support;
310         _payload = payload;
311         // Needs a bootstrap
312         // bootstrap is the equivalent of incRef
313         _newAllocator.bootstrap();
314         _allocator = _newAllocator;
315         if (_support !is null)
316         {
317             addRef(_support);
318             debug(CollectionArray) writefln("Array.ctor immutable: Array %s has "
319                     ~ "refcount: %s", _support, *prefCount(_support));
320         }
321     }
322 
323     ~this()
324     {
325         debug(CollectionArray)
326         {
327             writefln("Array.dtor: Begin for instance %s of type %s",
328                 cast(size_t)(&this), typeof(this).stringof);
329             scope(exit) writefln("Array.dtor: End for instance %s of type %s",
330                     cast(size_t)(&this), typeof(this).stringof);
331         }
332         destroyUnused();
333     }
334 
335     static if (is(T == int))
336     nothrow pure @safe unittest
337     {
338         auto a = Array!int(1, 2, 3);
339 
340         // Infer safety
341         static assert(!__traits(compiles, () @safe { Array!Unsafe(Unsafe(1)); }));
342         static assert(!__traits(compiles, () @safe { auto a = const Array!Unsafe(Unsafe(1)); }));
343         static assert(!__traits(compiles, () @safe { auto a = immutable Array!Unsafe(Unsafe(1)); }));
344 
345         static assert(!__traits(compiles, () @safe { Array!UnsafeDtor(UnsafeDtor(1)); }));
346         static assert(!__traits(compiles, () @safe { auto s = const Array!UnsafeDtor(UnsafeDtor(1)); }));
347         static assert(!__traits(compiles, () @safe { auto s = immutable Array!UnsafeDtor(UnsafeDtor(1)); }));
348 
349         // Infer purity
350         static assert(!__traits(compiles, () pure { Array!Impure(Impure(1)); }));
351         static assert(!__traits(compiles, () pure { auto a = const Array!Impure(Impure(1)); }));
352         static assert(!__traits(compiles, () pure { auto a = immutable Array!Impure(Impure(1)); }));
353 
354         static assert(!__traits(compiles, () pure { Array!ImpureDtor(ImpureDtor(1)); }));
355         static assert(!__traits(compiles, () pure { auto s = const Array!ImpureDtor(ImpureDtor(1)); }));
356         static assert(!__traits(compiles, () pure { auto s = immutable Array!ImpureDtor(ImpureDtor(1)); }));
357 
358         // Infer throwability
359         static assert(!__traits(compiles, () nothrow { Array!Throws(Throws(1)); }));
360         static assert(!__traits(compiles, () nothrow { auto a = const Array!Throws(Throws(1)); }));
361         static assert(!__traits(compiles, () nothrow { auto a = immutable Array!Throws(Throws(1)); }));
362 
363         static assert(!__traits(compiles, () nothrow { Array!ThrowsDtor(ThrowsDtor(1)); }));
364         static assert(!__traits(compiles, () nothrow { auto s = const Array!ThrowsDtor(ThrowsDtor(1)); }));
365         static assert(!__traits(compiles, () nothrow { auto s = immutable Array!ThrowsDtor(ThrowsDtor(1)); }));
366     }
367 
368     private @nogc nothrow pure @trusted
369     size_t slackFront() const
370     {
371         return _payload.ptr - _support.ptr;
372     }
373 
374     private @nogc nothrow pure @trusted
375     size_t slackBack() const
376     {
377         return _support.ptr + _support.length - _payload.ptr - _payload.length;
378     }
379 
380     /**
381      * Return the number of elements in the array..
382      *
383      * Returns:
384      *      the length of the array.
385      *
386      * Complexity: $(BIGOH 1).
387      */
388     @nogc nothrow pure @safe
389     size_t length() const
390     {
391         return _payload.length;
392     }
393 
394     /// ditto
395     alias opDollar = length;
396 
397     ///
398     static if (is(T == int))
399     @safe unittest
400     {
401         auto a = Array!int(1, 2, 3);
402         assert(a.length == 3);
403         assert(a[$ - 1] == 3);
404     }
405 
406     /**
407      * Set the length of the array to `len`. `len` must be less than or equal
408      * to the `capacity` of the array.
409      *
410      * Params:
411      *      len = a positive integer
412      *
413      * Complexity: $(BIGOH 1).
414      */
415     @nogc nothrow pure @trusted
416     void forceLength(size_t len)
417     {
418         assert(len <= capacity);
419         _payload = cast(T[])(_support[slackFront .. len]);
420     }
421 
422     ///
423     static if (is(T == int))
424     @safe unittest
425     {
426         auto a = Array!int(1, 2, 3);
427         a.forceLength(2);
428         assert(a.length == 2);
429     }
430 
431     /**
432      * Get the available capacity of the `array`; this is equal to `length` of
433      * the array plus the available pre-allocated, free, space.
434      *
435      * Returns:
436      *      a positive integer denoting the capacity.
437      *
438      * Complexity: $(BIGOH 1).
439      */
440     @nogc nothrow pure @safe
441     size_t capacity() const
442     {
443         return length + slackBack;
444     }
445 
446     ///
447     static if (is(T == int))
448     @safe unittest
449     {
450         auto a = Array!int(1, 2, 3);
451         a.reserve(10);
452         assert(a.capacity == 10);
453     }
454 
455     /**
456      * Reserve enough memory from the allocator to store `n` elements.
457      * If the current `capacity` exceeds `n` nothing will happen.
458      * If `n` exceeds the current `capacity`, an attempt to `expand` the
459      * current array is made. If `expand` is successful, all the expanded
460      * elements are default initialized to `T.init`. If the `expand` fails
461      * a new buffer will be allocated, the old elements of the array will be
462      * copied and the new elements will be default initialized to `T.init`.
463      *
464      * Params:
465      *      n = a positive integer
466      *
467      * Complexity: $(BIGOH max(length, n)).
468      */
469     void reserve(size_t n)
470     {
471         debug(CollectionArray)
472         {
473             writefln("Array.reserve: begin");
474             scope(exit) writefln("Array.reserve: end");
475         }
476 
477         // Will be optimized away, but the type sistem infers T's safety
478         if (0) { T t = T.init; }
479 
480         if (n <= capacity) { return; }
481 
482         // TODO: why would we want to overwrite the user-defined allocator?
483         auto a = threadAllocatorObject();
484         setAllocator(a);
485 
486         if (_support && _allocator.opCmpPrefix!"=="(_support, 0))
487         {
488             void[] buf = _support;
489             if (_allocator.expand(buf, (n - capacity) * T.sizeof))
490             {
491                 const oldLength = _support.length;
492                 _support = (() @trusted => cast(Unqual!T[])(buf))();
493                 // Emplace extended buf
494                 // TODO: maybe? emplace only if T has indirections
495                 foreach (i; oldLength .. _support.length)
496                 {
497                     emplace(&_support[i]);
498                 }
499                 return;
500             }
501             else
502             {
503                 assert(0, "Array.reserve: Failed to expand array.");
504             }
505         }
506 
507         auto tmpSupport = (() @trusted => cast(Unqual!T[])(_allocator.allocate(n * T.sizeof)))();
508         assert(tmpSupport !is null);
509         for (size_t i = 0; i < tmpSupport.length; ++i)
510         {
511             if (i < _payload.length)
512             {
513                 emplace(&tmpSupport[i], _payload[i]);
514             }
515             else
516             {
517                 emplace(&tmpSupport[i]);
518             }
519         }
520         //tmpSupport[0 .. _payload.length] = _payload[];
521         destroyUnused();
522         _support = tmpSupport;
523         addRef(_support);
524         _payload = (() @trusted => cast(T[])(_support[0 .. _payload.length]))();
525         assert(capacity >= n);
526     }
527 
528     ///
529     static if (is(T == int))
530     @safe unittest
531     {
532         import std.algorithm.comparison : equal;
533 
534         auto stuff = [1, 2, 3];
535         Array!int a;
536         a.reserve(stuff.length);
537         a ~= stuff;
538         assert(equal(a, stuff));
539     }
540 
541     /**
542      * Inserts the elements of an
543      * $(REF_ALTTEXT input range, isInputRange, std,range,primitives), or a
544      * variable number of items, at the given `pos`.
545      *
546      * If no allocator was provided when the array was created, the
547      * $(REF, GCAllocator, std,experimental,allocator,gc_allocator) will be used.
548      * If `Stuff` defines `length`, `Array` will use it to reserve the
549      * necessary amount of memory.
550      *
551      * Params:
552      *      pos = a positive integer
553      *      stuff = an input range of elements that are implitictly convertible
554      *              to `T`; a variable number of items either in the form of a
555      *              list or as a built-in array
556      *
557      * Returns:
558      *      the number of elements inserted
559      *
560      * Complexity: $(BIGOH max(length, pos + m)), where `m` is the number of
561      *             elements in the range.
562      */
563     size_t insert(Stuff)(size_t pos, Stuff stuff)
564     if (!isArray!(typeof(stuff)) && isInputRange!Stuff && !isInfinite!Stuff
565         && isImplicitlyConvertible!(ElementType!Stuff, T))
566     {
567         debug(CollectionArray)
568         {
569             writefln("Array.insert: begin");
570             scope(exit) writefln("Array.insert: end");
571         }
572 
573         // Will be optimized away, but the type system infers T's safety
574         if (0) { T t = T.init; }
575 
576         auto a = threadAllocatorObject();
577         setAllocator(a);
578 
579         static if (hasLength!Stuff)
580         {
581             size_t stuffLength = stuff.length;
582         }
583         else
584         {
585             import std.range.primitives : walkLength;
586             size_t stuffLength = walkLength(stuff);
587         }
588         if (stuffLength == 0) return 0;
589 
590         auto tmpSupport = (() @trusted => cast(Unqual!T[])(_allocator.allocate(stuffLength * T.sizeof)))();
591         assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is null));
592         for (size_t i = 0; i < tmpSupport.length; ++i)
593         {
594                 emplace(&tmpSupport[i]);
595         }
596 
597         size_t i = 0;
598         foreach (item; stuff)
599         {
600             tmpSupport[i++] = item;
601         }
602         size_t result = insert(pos, tmpSupport);
603         () @trusted { dispose(_allocator, tmpSupport); }();
604         return result;
605     }
606 
607     /// ditto
608     size_t insert(Stuff)(size_t pos, Stuff[] stuff...)
609     if (isImplicitlyConvertible!(Stuff, T))
610     {
611         debug(CollectionArray)
612         {
613             writefln("Array.insert: begin");
614             scope(exit) writefln("Array.insert: end");
615         }
616 
617         // Will be optimized away, but the type system infers T's safety
618         if (0) { T t = T.init; }
619 
620         assert(pos <= _payload.length);
621         auto a = threadAllocatorObject();
622         setAllocator(a);
623 
624         if (stuff.length == 0) return 0;
625         if (stuff.length > slackBack)
626         {
627             double newCapacity = capacity ? capacity * capacityFactor : stuff.length;
628             while (newCapacity < capacity + stuff.length)
629             {
630                 newCapacity = newCapacity * capacityFactor;
631             }
632             reserve((() @trusted => cast(size_t)(newCapacity))());
633         }
634         //_support[pos + stuff.length .. _payload.length + stuff.length] =
635             //_support[pos .. _payload.length];
636         for (size_t i = _payload.length + stuff.length - 1; i >= pos +
637                 stuff.length; --i)
638         {
639             // Avoids underflow if payload is empty
640             _support[i] = _support[i - stuff.length];
641         }
642         _support[pos .. pos + stuff.length] = stuff[];
643         _payload = (() @trusted => cast(T[])(_support[0 .. _payload.length + stuff.length]))();
644         return stuff.length;
645     }
646 
647     ///
648     static if (is(T == int))
649     @safe unittest
650     {
651         import std.algorithm.comparison : equal;
652 
653         Array!int a;
654         assert(a.empty);
655 
656         size_t pos = 0;
657         pos += a.insert(pos, 1);
658         pos += a.insert(pos, [2, 3]);
659         assert(equal(a, [1, 2, 3]));
660     }
661 
662     /**
663      * Check whether there are no more references to this array instance.
664      *
665      * Returns:
666      *      `true` if this is the only reference to this array instance;
667      *      `false` otherwise.
668      *
669      * Complexity: $(BIGOH 1).
670      */
671     @nogc nothrow pure @safe
672     bool isUnique(this _)()
673     {
674         debug(CollectionArray)
675         {
676             writefln("Array.isUnique: begin");
677             scope(exit) writefln("Array.isUnique: end");
678         }
679 
680         if (_support !is null)
681         {
682             return cast(bool) _allocator.opCmpPrefix!"=="(_support, 1);
683         }
684         return true;
685     }
686 
687     ///
688     static if (is(T == int))
689     @safe unittest
690     {
691         auto a = Array!int(24, 42);
692         assert(a.isUnique);
693         {
694             auto a2 = a;
695             assert(!a.isUnique);
696             a2.front = 0;
697             assert(a.front == 0);
698         } // a2 goes out of scope
699         assert(a.isUnique);
700     }
701 
702     /**
703      * Check if the array is empty.
704      *
705      * Returns:
706      *      `true` if there are no elements in the array; `false` otherwise.
707      *
708      * Complexity: $(BIGOH 1).
709      */
710     @nogc nothrow pure @safe
711     bool empty() const
712     {
713         return length == 0;
714     }
715 
716     ///
717     static if (is(T == int))
718     @safe unittest
719     {
720         Array!int a;
721         assert(a.empty);
722         size_t pos = 0;
723         a.insert(pos, 1);
724         assert(!a.empty);
725     }
726 
727     /**
728      * Provide access to the first element in the array. The user must check
729      * that the array isn't `empty`, prior to calling this function.
730      *
731      * Returns:
732      *      a reference to the first element.
733      *
734      * Complexity: $(BIGOH 1).
735      */
736     ref auto front(this _)()
737     {
738         assert(!empty, "Array.front: Array is empty");
739         return _payload[0];
740     }
741 
742     ///
743     static if (is(T == int))
744     @safe unittest
745     {
746         auto a = Array!int(1, 2, 3);
747         assert(a.front == 1);
748         a.front = 0;
749         assert(a.front == 0);
750     }
751 
752     /**
753      * Advance to the next element in the array. The user must check
754      * that the array isn't `empty`, prior to calling this function.
755      *
756      * Complexity: $(BIGOH 1).
757      */
758     @nogc nothrow pure @safe
759     void popFront()
760     {
761         debug(CollectionArray)
762         {
763             writefln("Array.popFront: begin");
764             scope(exit) writefln("Array.popFront: end");
765         }
766         assert(!empty, "Array.popFront: Array is empty");
767         _payload = _payload[1 .. $];
768     }
769 
770     ///
771     static if (is(T == int))
772     @safe unittest
773     {
774         auto stuff = [1, 2, 3];
775         auto a = Array!int(stuff);
776         size_t i = 0;
777         while (!a.empty)
778         {
779             assert(a.front == stuff[i++]);
780             a.popFront;
781         }
782         assert(a.empty);
783     }
784 
785     /**
786      * Advance to the next element in the array. The user must check
787      * that the array isn't `empty`, prior to calling this function.
788      *
789      * This must be used in order to iterate through a `const` or `immutable`
790      * array For a mutable array this is equivalent to calling `popFront`.
791      *
792      * Returns:
793      *      an array that starts with the next element in the original array.
794      *
795      * Complexity: $(BIGOH 1).
796      */
797     Qualified tail(this Qualified)()
798     {
799         debug(CollectionArray)
800         {
801             writefln("Array.tail: begin");
802             scope(exit) writefln("Array.tail: end");
803         }
804         assert(!empty, "Array.tail: Array is empty");
805 
806         static if (is(Qualified == immutable) || is(Qualified == const))
807         {
808             return this[1 .. $];
809         }
810         else
811         {
812             return .tail(this);
813         }
814     }
815 
816     ///
817     static if (is(T == int))
818     @safe unittest
819     {
820         auto ia = immutable Array!int([1, 2, 3]);
821         assert(ia.tail.front == 2);
822     }
823 
824     /**
825      * Eagerly iterate over each element in the array and call `fun` over each
826      * element. This should be used to iterate through `const` and `immutable`
827      * arrays.
828      *
829      * Normally, the entire array is iterated. If partial iteration (early stopping)
830      * is desired, `fun` needs to return a value of type
831      * $(REF Flag, std,typecons)`!"each"` (`Yes.each` to continue iteration, or
832      * `No.each` to stop).
833      *
834      * Params:
835      *      fun = unary function to apply on each element of the array.
836      *
837      * Returns:
838      *      `Yes.each` if it has iterated through all the elements in the array,
839      *      or `No.each` otherwise.
840      *
841      * Complexity: $(BIGOH n).
842      */
843     template each(alias fun)
844     {
845         import std.typecons : Flag, Yes, No;
846         import std.functional : unaryFun;
847         import stdx.collections.slist : SList;
848 
849         Flag!"each" each(this Q)()
850         if (is (typeof(unaryFun!fun(T.init))))
851         {
852             alias fn = unaryFun!fun;
853 
854             auto sl = SList!(const Array!T)(this);
855             while (!sl.empty && !sl.front.empty)
856             {
857                 static if (!is(typeof(fn(T.init)) == Flag!"each"))
858                 {
859                     cast(void) fn(sl.front.front);
860                 }
861                 else
862                 {
863                     if (fn(sl.front.front) == No.each)
864                         return No.each;
865                 }
866                 sl ~= sl.front.tail;
867                 sl.popFront;
868             }
869             return Yes.each;
870         }
871     }
872 
873     ///
874     static if (is(T == int))
875     @safe unittest
876     {
877         import std.typecons : Flag, Yes, No;
878 
879         auto ia = immutable Array!int([1, 2, 3]);
880 
881         static bool foo(int x) { return x > 0; }
882         static Flag!"each" bar(int x) { return x > 1 ? Yes.each : No.each; }
883 
884         assert(ia.each!foo == Yes.each);
885         assert(ia.each!bar == No.each);
886     }
887 
888     //int opApply(int delegate(const ref T) dg) const
889     //{
890         //if (_payload.length && dg(_payload[0])) return 1;
891         //if (!this.empty) this.tail.opApply(dg);
892         //return 0;
893     //}
894 
895     /**
896      * Perform a shallow copy of the array.
897      *
898      * Returns:
899      *      a new reference to the current array.
900      *
901      * Complexity: $(BIGOH 1).
902      */
903     ref auto save(this _)()
904     {
905         debug(CollectionArray)
906         {
907             writefln("Array.save: begin");
908             scope(exit) writefln("Array.save: end");
909         }
910         return this;
911     }
912 
913     ///
914     static if (is(T == int))
915     @safe unittest
916     {
917         auto stuff = [1, 2, 3];
918         auto a = Array!int(stuff);
919         size_t i = 0;
920 
921         auto tmp = a.save;
922         while (!tmp.empty)
923         {
924             assert(tmp.front == stuff[i++]);
925             tmp.popFront;
926         }
927         assert(tmp.empty);
928         assert(!a.empty);
929     }
930 
931     // TODO: needs to know if _allocator is shared or not
932     // We also need to create a tmp array for all the elements
933     Array!T idup(this Q)();
934 
935     /**
936      * Perform a copy of the array. This will create a new array that will copy
937      * the elements of the current array. This will `NOT` call `dup` on the
938      * elements of the array, regardless if `T` defines it or not.
939      *
940      * Returns:
941      *      a new mutable array.
942      *
943      * Complexity: $(BIGOH n).
944      */
945     Array!T dup(this Q)()
946     {
947         debug(CollectionArray)
948         {
949             writefln("Array.dup: begin");
950             scope(exit) writefln("Array.dup: end");
951         }
952         Array!T result;
953         result._allocator = _allocator;
954 
955         static if (is(Q == immutable) || is(Q == const))
956         {
957             result.reserve(length);
958             foreach(i; 0 .. length)
959             {
960                 result ~= this[i];
961             }
962         }
963         else
964         {
965             result.insert(0, this);
966         }
967         return result;
968     }
969 
970     ///
971     static if (is(T == int))
972     @safe unittest
973     {
974         import std.algorithm.comparison : equal;
975 
976         auto stuff = [1, 2, 3];
977         auto a = immutable Array!int(stuff);
978         auto aDup = a.dup;
979         assert(equal(aDup, stuff));
980         aDup.front = 0;
981         assert(aDup.front == 0);
982         assert(a.front == 1);
983     }
984 
985     /**
986      * Return a slice to the current array. This is equivalent to calling
987      * `save`.
988      *
989      * Returns:
990      *      an array that references the current array.
991      *
992      * Complexity: $(BIGOH 1)
993      */
994     Qualified opSlice(this Qualified)()
995     {
996         debug(CollectionArray)
997         {
998             writefln("Array.opSlice(): begin");
999             scope(exit) writefln("Array.opSlice(): end");
1000         }
1001         return this.save;
1002     }
1003 
1004     /**
1005      * Return a slice to the current array that is bounded by `start` and `end`.
1006      * `start` must be less than or equal to `end` and `end` must be less than
1007      * or equal to `length`.
1008      *
1009      * Returns:
1010      *      an array that references the current array.
1011      *
1012      * Params:
1013      *      start = a positive integer
1014      *      end = a positive integer
1015      *
1016      * Complexity: $(BIGOH 1)
1017      */
1018     Qualified opSlice(this Qualified)(size_t start, size_t end)
1019     in
1020     {
1021         assert(start <= end && end <= length,
1022                "Array.opSlice(s, e): Invalid bounds: Ensure start <= end <= length");
1023     }
1024     body
1025     {
1026         debug(CollectionArray)
1027         {
1028             scope(failure) assert(0, "Array.opSlice");
1029             writefln("Array.opSlice(%d, %d): begin", start, end);
1030             scope(exit) writefln("Array.opSlice(%d, %d): end", start, end);
1031         }
1032         return typeof(this)(_support, _payload[start .. end], _allocator);
1033     }
1034 
1035     ///
1036     static if (is(T == int))
1037     @safe unittest
1038     {
1039         import std.algorithm.comparison : equal;
1040 
1041         auto stuff = [1, 2, 3];
1042         auto a = Array!int(stuff);
1043         assert(equal(a[], stuff));
1044         assert(equal(a[1 .. $], stuff[1 .. $]));
1045     }
1046 
1047     /**
1048      * Provide access to the element at `idx` in the array.
1049      * `idx` must be less than `length`.
1050      *
1051      * Returns:
1052      *      a reference to the element found at `idx`.
1053      *
1054      * Params:
1055      *      idx = a positive integer
1056      *
1057      * Complexity: $(BIGOH 1).
1058      */
1059     ref auto opIndex(this _)(size_t idx)
1060     in
1061     {
1062         assert(idx < length, "Array.opIndex: Index out of bounds");
1063     }
1064     body
1065     {
1066         return _payload[idx];
1067     }
1068 
1069     ///
1070     static if (is(T == int))
1071     @safe unittest
1072     {
1073         auto a = Array!int([1, 2, 3]);
1074         assert(a[2] == 3);
1075     }
1076 
1077     /**
1078      * Apply an unary operation to the element at `idx` in the array.
1079      * `idx` must be less than `length`.
1080      *
1081      * Returns:
1082      *      a reference to the element found at `idx`.
1083      *
1084      * Params:
1085      *      idx = a positive integer
1086      *
1087      * Complexity: $(BIGOH 1).
1088      */
1089     ref auto opIndexUnary(string op)(size_t idx)
1090     in
1091     {
1092         assert(idx < length, "Array.opIndexUnary!" ~ op ~ ": Index out of bounds");
1093     }
1094     body
1095     {
1096         mixin("return " ~ op ~ "_payload[idx];");
1097     }
1098 
1099     ///
1100     static if (is(T == int))
1101     @safe unittest
1102     {
1103         auto a = Array!int([1, 2, 3]);
1104         int x = --a[2];
1105         assert(a[2] == 2);
1106         assert(x == 2);
1107     }
1108 
1109     /**
1110      * Assign `elem` to the element at `idx` in the array.
1111      * `idx` must be less than `length`.
1112      *
1113      * Returns:
1114      *      a reference to the element found at `idx`.
1115      *
1116      * Params:
1117      *      elem = an element that is implicitly convertible to `T`
1118      *      idx = a positive integer
1119      *
1120      * Complexity: $(BIGOH 1).
1121      */
1122     ref auto opIndexAssign(U)(U elem, size_t idx)
1123     if (isImplicitlyConvertible!(U, T))
1124     in
1125     {
1126         assert(idx < length, "Array.opIndexAssign: Index out of bounds");
1127     }
1128     body
1129     {
1130         return _payload[idx] = elem;
1131     }
1132 
1133     ///
1134     static if (is(T == int))
1135     @safe unittest
1136     {
1137         auto a = Array!int([1, 2, 3]);
1138         a[2] = 2;
1139         assert(a[2] == 2);
1140         (a[2] = 3)++;
1141         assert(a[2] == 4);
1142     }
1143 
1144     /**
1145      Assign `elem` to all element in the array.
1146 
1147      Returns:
1148           a reference to itself
1149 
1150      Params:
1151           elem = an element that is implicitly convertible to `T`
1152 
1153      Complexity: $(BIGOH n).
1154      */
1155     ref auto opIndexAssign(U)(U elem)
1156     if (isImplicitlyConvertible!(U, T))
1157     body
1158     {
1159         _payload[] = elem;
1160         return this;
1161     }
1162 
1163     ///
1164     static if (is(T == int))
1165     @safe unittest
1166     {
1167         import std.algorithm.comparison : equal;
1168         auto a = Array!int([1, 2, 3]);
1169         a[] = 0;
1170         assert(a.equal([0, 0, 0]));
1171     }
1172 
1173     /**
1174     Assign `elem` to the element at `idx` in the array.
1175     `idx` must be less than `length`.
1176 
1177     Returns:
1178          a reference to the element found at `idx`.
1179 
1180     Params:
1181          elem = an element that is implicitly convertible to `T`
1182          indices = a positive integer
1183 
1184     Complexity: $(BIGOH n).
1185     */
1186     auto opSliceAssign(U)(U elem, size_t start, size_t end)
1187     if (isImplicitlyConvertible!(U, T))
1188     in
1189     {
1190         assert(start <= end, "Array.opSliceAssign: Index out of bounds");
1191         assert(end < length, "Array.opSliceAssign: Index out of bounds");
1192     }
1193     body
1194     {
1195         return _payload[start .. end] = elem;
1196     }
1197 
1198     ///
1199     static if (is(T == int))
1200     @safe unittest
1201     {
1202         import std.algorithm.comparison : equal;
1203         auto a = Array!int([1, 2, 3, 4, 5, 6]);
1204         a[1 .. 3] = 0;
1205         assert(a.equal([1, 0, 0, 4, 5, 6]));
1206     }
1207 
1208     /**
1209      * Assign to the element at `idx` in the array the result of
1210      * $(D a[idx] op elem).
1211      * `idx` must be less than `length`.
1212      *
1213      * Returns:
1214      *      a reference to the element found at `idx`.
1215      *
1216      * Params:
1217      *      elem = an element that is implicitly convertible to `T`
1218      *      idx = a positive integer
1219      *
1220      * Complexity: $(BIGOH 1).
1221      */
1222     ref auto opIndexOpAssign(string op, U)(U elem, size_t idx)
1223     if (isImplicitlyConvertible!(U, T))
1224     in
1225     {
1226         assert(idx < length, "Array.opIndexOpAssign!" ~ op ~ ": Index out of bounds");
1227     }
1228     body
1229     {
1230         mixin("return _payload[idx]" ~ op ~ "= elem;");
1231     }
1232 
1233     ///
1234     static if (is(T == int))
1235     @safe unittest
1236     {
1237         auto a = Array!int([1, 2, 3]);
1238         a[2] += 2;
1239         assert(a[2] == 5);
1240         (a[2] += 3)++;
1241         assert(a[2] == 9);
1242     }
1243 
1244     /**
1245      * Create a new array that results from the concatenation of this array
1246      * with `rhs`.
1247      *
1248      * Params:
1249      *      rhs = can be an element that is implicitly convertible to `T`, an
1250      *            input range of such elements, or another `Array`
1251      *
1252      * Returns:
1253      *      the newly created array
1254      *
1255      * Complexity: $(BIGOH n + m), where `m` is the number of elements in `rhs`.
1256      */
1257     auto ref opBinary(string op, U)(auto ref U rhs)
1258         if (op == "~" &&
1259             (is (U : const typeof(this))
1260              || is (U : T)
1261              || (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T))
1262             ))
1263     {
1264         debug(CollectionArray)
1265         {
1266             writefln("Array.opBinary!~: begin");
1267             scope(exit) writefln("Array.opBinary!~: end");
1268         }
1269 
1270         auto newArray = this.dup();
1271         static if (is(U : const typeof(this)))
1272         {
1273             foreach(i; 0 .. rhs.length)
1274             {
1275                 newArray ~= rhs[i];
1276             }
1277         }
1278         else
1279         {
1280             newArray.insert(length, rhs);
1281             // Or
1282             // newArray ~= rhs;
1283         }
1284         return newArray;
1285     }
1286 
1287     ///
1288     static if (is(T == int))
1289     @safe unittest
1290     {
1291         import std.algorithm.comparison : equal;
1292 
1293         auto a = Array!int(1);
1294         auto a2 = a ~ 2;
1295 
1296         assert(equal(a2, [1, 2]));
1297         a.front = 0;
1298         assert(equal(a2, [1, 2]));
1299     }
1300 
1301     /**
1302      * Assign `rhs` to this array. The current array will now become another
1303      * reference to `rhs`, unless `rhs` is `null`, in which case the current
1304      * array will become empty. If `rhs` refers to the current array nothing will
1305      * happen.
1306      *
1307      * If there are no more references to the previous array, the previous
1308      * array will be destroyed; this leads to a $(BIGOH n) complexity.
1309      *
1310      * Params:
1311      *      rhs = a reference to an array
1312      *
1313      * Returns:
1314      *      a reference to this array
1315      *
1316      * Complexity: $(BIGOH n).
1317      */
1318     auto ref opAssign()(auto ref typeof(this) rhs)
1319     {
1320         debug(CollectionArray)
1321         {
1322             scope(failure) assert(0, "Array.opAssign");
1323             writefln("Array.opAssign: begin: %s", rhs);
1324             scope(exit) writefln("Array.opAssign: end");
1325         }
1326 
1327         if (rhs._support !is null && _support is rhs._support)
1328         {
1329             if (rhs._payload is _payload)
1330                 return this;
1331         }
1332 
1333         if (rhs._support !is null)
1334         {
1335             rhs.addRef(rhs._support);
1336             debug(CollectionArray) writefln("Array.opAssign: Array %s has refcount: %s",
1337                     rhs._payload, *prefCount(rhs._support));
1338         }
1339         destroyUnused();
1340         _support = rhs._support;
1341         _payload = rhs._payload;
1342         _allocator = rhs._allocator;
1343         return this;
1344     }
1345 
1346     ///
1347     static if (is(T == int))
1348     @safe unittest
1349     {
1350         import std.algorithm.comparison : equal;
1351 
1352         auto a = Array!int(1);
1353         auto a2 = Array!int(1, 2);
1354 
1355         a = a2; // this will free the old a
1356         assert(equal(a, [1, 2]));
1357         a.front = 0;
1358         assert(equal(a2, [0, 2]));
1359     }
1360 
1361     static if (is(T == int))
1362     @safe unittest
1363     {
1364         import std.algorithm.comparison : equal;
1365         auto arr = Array!int(1, 2, 3, 4, 5, 6);
1366         auto arr1 = arr[1 .. $];
1367         auto arr2 = arr[3 .. $];
1368         arr1 = arr2;
1369         assert(arr1.equal([4, 5, 6]));
1370         assert(arr2.equal([4, 5, 6]));
1371     }
1372 
1373     /**
1374      * Append the elements of `rhs` at the end of the array.
1375      *
1376      * If no allocator was provided when the list was created, the
1377      * $(REF, GCAllocator, std,experimental,allocator,gc_allocator) will be used.
1378      *
1379      * Params:
1380      *      rhs = can be an element that is implicitly convertible to `T`, an
1381      *            input range of such elements, or another `Array`
1382      *
1383      * Returns:
1384      *      a reference to this array
1385      *
1386      * Complexity: $(BIGOH n + m), where `m` is the number of elements in `rhs`.
1387      */
1388     auto ref opOpAssign(string op, U)(auto ref U rhs)
1389         if (op == "~" &&
1390             (is (U == typeof(this))
1391              || is (U : T)
1392              || (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T))
1393             ))
1394     {
1395         debug(CollectionArray)
1396         {
1397             writefln("Array.opOpAssign!~: %s begin", typeof(this).stringof);
1398             scope(exit) writefln("Array.opOpAssign!~: %s end", typeof(this).stringof);
1399         }
1400         insert(length, rhs);
1401         return this;
1402     }
1403 
1404     ///
1405     static if (is(T == int))
1406     @safe unittest
1407     {
1408         import std.algorithm.comparison : equal;
1409 
1410         Array!int a;
1411         auto a2 = Array!int(4, 5);
1412         assert(a.empty);
1413 
1414         a ~= 1;
1415         a ~= [2, 3];
1416         assert(equal(a, [1, 2, 3]));
1417 
1418         // append an input range
1419         a ~= a2;
1420         assert(equal(a, [1, 2, 3, 4, 5]));
1421         a2.front = 0;
1422         assert(equal(a, [1, 2, 3, 4, 5]));
1423     }
1424 
1425     ///
1426     bool opEquals()(auto ref typeof(this) rhs) const
1427     {
1428         import std.algorithm.comparison : equal;
1429         return _support.equal(rhs);
1430     }
1431 
1432     ///
1433     static if (is(T == int))
1434     @safe unittest
1435     {
1436         auto arr1 = Array!int(1, 2);
1437         auto arr2 = Array!int(1, 2);
1438         auto arr3 = Array!int(2, 3);
1439         assert(arr1 == arr2);
1440         assert(arr2 == arr1);
1441         assert(arr1 != arr3);
1442         assert(arr3 != arr1);
1443         assert(arr2 != arr3);
1444         assert(arr3 != arr2);
1445     }
1446 
1447     ///
1448     int opCmp(U)(auto ref U rhs)
1449     if (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T))
1450     {
1451         import std.algorithm.comparison : min, equal;
1452         import std.math : sgn;
1453         import std.range.primitives : empty, front, popFront;
1454         auto r1 = this;
1455         auto r2 = rhs;
1456         for (;!r1.empty && !r2.empty; r1.popFront, r2.popFront)
1457         {
1458             if (r1.front < r2.front)
1459                 return -1;
1460             else if (r1.front > r2.front)
1461                 return 1;
1462         }
1463         // arrays are equal until here, but it could be that one of them is shorter
1464         if (r1.empty && r2.empty)
1465             return 0;
1466         return r1.empty ? -1 : 1;
1467     }
1468 
1469     ///
1470     static if (is(T == int))
1471     @safe unittest
1472     {
1473         auto arr1 = Array!int(1, 2);
1474         auto arr2 = Array!int(1, 2);
1475         auto arr3 = Array!int(2, 3);
1476         auto arr4 = Array!int(0, 3);
1477         assert(arr1 <= arr2);
1478         assert(arr2 >= arr1);
1479         assert(arr1 < arr3);
1480         assert(arr3 > arr1);
1481         assert(arr4 < arr1);
1482         assert(arr4 < arr3);
1483         assert(arr3 > arr4);
1484     }
1485 
1486     static if (is(T == int))
1487     @safe unittest
1488     {
1489         auto arr1 = Array!int(1, 2);
1490         auto arr2 = [1, 2];
1491         auto arr3 = Array!int(2, 3);
1492         auto arr4 = [0, 3];
1493         assert(arr1 <= arr2);
1494         assert(arr2 >= arr1);
1495         assert(arr1 < arr3);
1496         assert(arr3 > arr1);
1497         assert(arr4 < arr1);
1498         assert(arr4 < arr3);
1499         assert(arr3 > arr4);
1500     }
1501 
1502     ///
1503     auto toHash()
1504     {
1505         // will be safe with 2.082
1506         return () @trusted { return _support.hashOf; }();
1507     }
1508 
1509     ///
1510     @safe unittest
1511     {
1512         auto arr1 = Array!int(1, 2);
1513         assert(arr1.toHash == Array!int(1, 2).toHash);
1514         arr1 ~= 3;
1515         assert(arr1.toHash == Array!int(1, 2, 3).toHash);
1516         assert(Array!int().toHash == Array!int().toHash);
1517     }
1518 }
1519 
1520 version(unittest) private nothrow pure @safe
1521 void testConcatAndAppend(RCIAllocator allocator)
1522 {
1523     import std.algorithm.comparison : equal;
1524 
1525     auto a = Array!(int)(allocator, 1, 2, 3);
1526     Array!(int) a2 = Array!(int)(allocator);
1527 
1528     auto a3 = a ~ a2;
1529     assert(equal(a3, [1, 2, 3]));
1530 
1531     auto a4 = a3;
1532     a3 = a3 ~ 4;
1533     assert(equal(a3, [1, 2, 3, 4]));
1534     a3 = a3 ~ [5];
1535     assert(equal(a3, [1, 2, 3, 4, 5]));
1536     assert(equal(a4, [1, 2, 3]));
1537 
1538     a4 = a3;
1539     a3 ~= 6;
1540     assert(equal(a3, [1, 2, 3, 4, 5, 6]));
1541     a3 ~= [7];
1542 
1543     a3 ~= a3;
1544     assert(equal(a3, [1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]));
1545 
1546     Array!int a5 = Array!(int)(allocator);
1547     a5 ~= [1, 2, 3];
1548     assert(equal(a5, [1, 2, 3]));
1549     auto a6 = a5;
1550     a5 = a5;
1551     a5[0] = 10;
1552     assert(equal(a5, a6));
1553 
1554     // Test concat with mixed qualifiers
1555     auto a7 = immutable Array!(int)(a5);
1556     assert(a7.front == 10);
1557     a5.front = 1;
1558     assert(a7.front == 10);
1559     auto a8 = a5 ~ a7;
1560     assert(equal(a8, [1, 2, 3, 10, 2, 3]));
1561 
1562     auto a9 = const Array!(int)(a5);
1563     auto a10 = a5 ~ a9;
1564     assert(equal(a10, [1, 2, 3, 1, 2, 3]));
1565 }
1566 
1567 @safe unittest
1568 {
1569     import std.conv;
1570     SCAlloc statsCollectorAlloc;
1571     {
1572         auto _allocator = (() @trusted => allocatorObject(&statsCollectorAlloc))();
1573         () nothrow pure @safe {
1574             testConcatAndAppend(_allocator);
1575         }();
1576     }
1577     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1578     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1579             ~ to!string(bytesUsed) ~ " bytes");
1580 }
1581 
1582 version(unittest) private nothrow pure @safe
1583 void testSimple(RCIAllocator allocator)
1584 {
1585     import std.algorithm.comparison : equal;
1586     import std.algorithm.searching : canFind;
1587 
1588     auto a = Array!int(allocator);
1589     assert(a.empty);
1590     assert(a.isUnique);
1591 
1592     size_t pos = 0;
1593     a.insert(pos, 1, 2, 3);
1594     assert(a.front == 1);
1595     assert(equal(a, a));
1596     assert(equal(a, [1, 2, 3]));
1597 
1598     a.popFront();
1599     assert(a.front == 2);
1600     assert(equal(a, [2, 3]));
1601 
1602     a.insert(pos, [4, 5, 6]);
1603     a.insert(pos, 7);
1604     a.insert(pos, [8]);
1605     assert(equal(a, [8, 7, 4, 5, 6, 2, 3]));
1606 
1607     a.insert(a.length, 0, 1);
1608     a.insert(a.length, [-1, -2]);
1609     assert(equal(a, [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]));
1610 
1611     a.front = 9;
1612     assert(equal(a, [9, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]));
1613 
1614     auto aTail = a.tail;
1615     assert(aTail.front == 7);
1616     aTail.front = 8;
1617     assert(aTail.front == 8);
1618     assert(a.tail.front == 8);
1619     assert(!a.isUnique);
1620 
1621     assert(canFind(a, 2));
1622     assert(!canFind(a, -10));
1623 }
1624 
1625 @safe unittest
1626 {
1627     import std.conv;
1628     SCAlloc statsCollectorAlloc;
1629     {
1630         auto _allocator = (() @trusted => allocatorObject(&statsCollectorAlloc))();
1631         () nothrow pure @safe {
1632             testSimple(_allocator);
1633         }();
1634     }
1635     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1636     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1637             ~ to!string(bytesUsed) ~ " bytes");
1638 }
1639 
1640 version(unittest) private nothrow pure @safe
1641 void testSimpleImmutable(RCIAllocator allocator)
1642 {
1643     import std.algorithm.comparison : equal;
1644     import std.algorithm.searching : canFind;
1645 
1646     auto a = Array!(immutable int)(allocator);
1647     assert(a.empty);
1648 
1649     size_t pos = 0;
1650     a.insert(pos, 1, 2, 3);
1651     assert(a.front == 1);
1652     assert(equal(a, a));
1653     assert(equal(a, [1, 2, 3]));
1654 
1655     a.popFront();
1656     assert(a.front == 2);
1657     assert(equal(a, [2, 3]));
1658     assert(a.tail.front == 3);
1659 
1660     a.insert(pos, [4, 5, 6]);
1661     a.insert(pos, 7);
1662     a.insert(pos, [8]);
1663     assert(equal(a, [8, 7, 4, 5, 6, 2, 3]));
1664 
1665     a.insert(a.length, 0, 1);
1666     a.insert(a.length, [-1, -2]);
1667     assert(equal(a, [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]));
1668 
1669     // Cannot modify immutable values
1670     static assert(!__traits(compiles, a.front = 9));
1671 
1672     assert(canFind(a, 2));
1673     assert(!canFind(a, -10));
1674 }
1675 
1676 @safe unittest
1677 {
1678     import std.conv;
1679     SCAlloc statsCollectorAlloc;
1680     {
1681         auto _allocator = (() @trusted => allocatorObject(&statsCollectorAlloc))();
1682         () nothrow pure @safe {
1683             testSimpleImmutable(_allocator);
1684         }();
1685     }
1686     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1687     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1688             ~ to!string(bytesUsed) ~ " bytes");
1689 }
1690 
1691 version(unittest) private nothrow pure @safe
1692 void testCopyAndRef(RCIAllocator allocator)
1693 {
1694     import std.algorithm.comparison : equal;
1695 
1696     auto aFromList = Array!int(allocator, 1, 2, 3);
1697     auto aFromRange = Array!int(allocator, aFromList);
1698     assert(equal(aFromList, aFromRange));
1699 
1700     aFromList.popFront();
1701     assert(equal(aFromList, [2, 3]));
1702     assert(equal(aFromRange, [1, 2, 3]));
1703 
1704     size_t pos = 0;
1705     Array!int aInsFromRange = Array!int(allocator);
1706     aInsFromRange.insert(pos, aFromList);
1707     aFromList.popFront();
1708     assert(equal(aFromList, [3]));
1709     assert(equal(aInsFromRange, [2, 3]));
1710 
1711     Array!int aInsBackFromRange = Array!int(allocator);
1712     aInsBackFromRange.insert(pos, aFromList);
1713     aFromList.popFront();
1714     assert(aFromList.empty);
1715     assert(equal(aInsBackFromRange, [3]));
1716 
1717     auto aFromRef = aInsFromRange;
1718     auto aFromDup = aInsFromRange.dup;
1719     assert(aInsFromRange.front == 2);
1720     aFromRef.front = 5;
1721     assert(aInsFromRange.front == 5);
1722     assert(aFromDup.front == 2);
1723 }
1724 
1725 @safe unittest
1726 {
1727     import std.conv;
1728     SCAlloc statsCollectorAlloc;
1729     {
1730         auto _allocator = (() @trusted => allocatorObject(&statsCollectorAlloc))();
1731         () nothrow pure @safe {
1732             testCopyAndRef(_allocator);
1733         }();
1734     }
1735     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1736     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1737             ~ to!string(bytesUsed) ~ " bytes");
1738 }
1739 
1740 version(unittest) private nothrow pure @safe
1741 void testImmutability(RCISharedAllocator allocator)
1742 {
1743     import std.algorithm.comparison : equal;
1744 
1745     auto a = immutable Array!(int)(allocator, 1, 2, 3);
1746     auto a2 = a;
1747     auto a3 = a2.save();
1748 
1749     assert(a2.front == 1);
1750     assert(a2[0] == a2.front);
1751     static assert(!__traits(compiles, a2.front = 4));
1752     static assert(!__traits(compiles, a2.popFront()));
1753 
1754     auto a4 = a2.tail;
1755     assert(a4.front == 2);
1756     static assert(!__traits(compiles, a4 = a4.tail));
1757 
1758     // Create a mutable copy from an immutable array
1759     auto a5 = a.dup();
1760     assert(equal(a5, [1, 2, 3]));
1761     assert(a5.front == 1);
1762     a5.front = 2;
1763     assert(a5.front == 2);
1764     assert(a.front == 1);
1765     assert(equal(a5, [2, 2, 3]));
1766 }
1767 
1768 version(unittest) private nothrow pure @safe
1769 void testConstness(RCISharedAllocator allocator)
1770 {
1771     auto a = const Array!(int)(allocator, 1, 2, 3);
1772     auto a2 = a;
1773     auto a3 = a2.save();
1774 
1775     assert(a2.front == 1);
1776     assert(a2[0] == a2.front);
1777     static assert(!__traits(compiles, a2.front = 4));
1778     static assert(!__traits(compiles, a2.popFront()));
1779 
1780     auto a4 = a2.tail;
1781     assert(a4.front == 2);
1782     static assert(!__traits(compiles, a4 = a4.tail));
1783 }
1784 
1785 @safe unittest
1786 {
1787     import std.conv;
1788     import std.experimental.allocator : processAllocator;
1789     SCAlloc statsCollectorAlloc;
1790     // TODO: StatsCollector needs to be made shareable
1791     //auto _allocator = sharedAllocatorObject(&statsCollectorAlloc);
1792 
1793     () nothrow pure @safe {
1794         testImmutability(processAllocatorObject());
1795         testConstness(processAllocatorObject());
1796     }();
1797     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1798     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1799             ~ to!string(bytesUsed) ~ " bytes");
1800 }
1801 
1802 version(unittest) private nothrow pure @safe
1803 void testWithStruct(RCIAllocator allocator, RCISharedAllocator sharedAlloc)
1804 {
1805     import std.algorithm.comparison : equal;
1806 
1807     auto array = Array!int(allocator, 1, 2, 3);
1808     {
1809         auto arrayOfArrays = Array!(Array!int)(allocator, array);
1810         assert(equal(arrayOfArrays.front, [1, 2, 3]));
1811         arrayOfArrays.front.front = 2;
1812         assert(equal(arrayOfArrays.front, [2, 2, 3]));
1813         static assert(!__traits(compiles, arrayOfArrays.insert(1)));
1814 
1815         auto immArrayOfArrays = immutable Array!(Array!int)(sharedAlloc, array);
1816         assert(immArrayOfArrays.front.front == 2);
1817         static assert(!__traits(compiles, immArrayOfArrays.front.front = 2));
1818     }
1819     assert(equal(array, [2, 2, 3]));
1820 }
1821 
1822 @safe unittest
1823 {
1824     import std.conv;
1825     import std.experimental.allocator : processAllocator;
1826     SCAlloc statsCollectorAlloc;
1827     {
1828         auto _allocator = (() @trusted => allocatorObject(&statsCollectorAlloc))();
1829         () nothrow pure @safe {
1830             testWithStruct(_allocator, processAllocatorObject());
1831         }();
1832     }
1833     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1834     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1835             ~ to!string(bytesUsed) ~ " bytes");
1836 }
1837 
1838 version(unittest) private nothrow pure @safe
1839 void testWithClass(RCIAllocator allocator)
1840 {
1841     class MyClass
1842     {
1843         int x;
1844         this(int x) { this.x = x; }
1845     }
1846 
1847     MyClass c = new MyClass(10);
1848     {
1849         auto a = Array!MyClass(allocator, c);
1850         assert(a.front.x == 10);
1851         assert(a.front is c);
1852         a.front.x = 20;
1853     }
1854     assert(c.x == 20);
1855 }
1856 
1857 @safe unittest
1858 {
1859     import std.conv;
1860     SCAlloc statsCollectorAlloc;
1861     {
1862         auto _allocator = (() @trusted => allocatorObject(&statsCollectorAlloc))();
1863         () nothrow pure @safe {
1864             testWithClass(_allocator);
1865         }();
1866     }
1867     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1868     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1869             ~ to!string(bytesUsed) ~ " bytes");
1870 }
1871 
1872 version(unittest) private nothrow pure @safe
1873 void testOpOverloads(RCIAllocator allocator)
1874 {
1875     auto a = Array!int(allocator, 1, 2, 3, 4);
1876     assert(a[0] == 1); // opIndex
1877 
1878     // opIndexUnary
1879     ++a[0];
1880     assert(a[0] == 2);
1881     --a[0];
1882     assert(a[0] == 1);
1883     a[0]++;
1884     assert(a[0] == 2);
1885     a[0]--;
1886     assert(a[0] == 1);
1887 
1888     // opIndexAssign
1889     a[0] = 2;
1890     assert(a[0] == 2);
1891 
1892     // opIndexOpAssign
1893     a[0] /= 2;
1894     assert(a[0] == 1);
1895     a[0] *= 2;
1896     assert(a[0] == 2);
1897     a[0] -= 1;
1898     assert(a[0] == 1);
1899     a[0] += 1;
1900     assert(a[0] == 2);
1901 }
1902 
1903 @safe unittest
1904 {
1905     import std.conv;
1906     SCAlloc statsCollectorAlloc;
1907     {
1908         auto _allocator = (() @trusted => allocatorObject(&statsCollectorAlloc))();
1909         () nothrow pure @safe {
1910             testOpOverloads(_allocator);
1911         }();
1912     }
1913     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1914     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1915             ~ to!string(bytesUsed) ~ " bytes");
1916 }
1917 
1918 version(unittest) private nothrow pure @safe
1919 void testSlice(RCIAllocator allocator)
1920 {
1921     import std.algorithm.comparison : equal;
1922 
1923     auto a = Array!int(allocator, 1, 2, 3, 4);
1924     auto b = a[];
1925     assert(equal(a, b));
1926     b[1] = 5;
1927     assert(a[1] == 5);
1928 
1929     size_t startPos = 2;
1930     auto c = b[startPos .. $];
1931     assert(equal(c, [3, 4]));
1932     c[0] = 5;
1933     assert(equal(a, b));
1934     assert(equal(a, [1, 5, 5, 4]));
1935     assert(a.capacity == b.capacity && b.capacity == c.capacity + startPos);
1936 
1937     c ~= 5;
1938     assert(equal(c, [5, 4, 5]));
1939     assert(equal(a, b));
1940     assert(equal(a, [1, 5, 5, 4]));
1941 }
1942 
1943 @safe unittest
1944 {
1945     import std.conv;
1946     SCAlloc statsCollectorAlloc;
1947     {
1948         auto _allocator = (() @trusted => allocatorObject(&statsCollectorAlloc))();
1949         () nothrow pure @safe {
1950             testSlice(_allocator);
1951         }();
1952     }
1953     auto bytesUsed = statsCollectorAlloc.bytesUsed;
1954     assert(bytesUsed == 0, "Array ref count leaks memory; leaked "
1955             ~ to!string(bytesUsed) ~ " bytes");
1956 }
1957 
1958 /*@nogc*/ nothrow pure @safe
1959 unittest
1960 {
1961     Array!int a;
1962     auto b = Array!int(1, 2, 3);
1963 }