1 /** Array container(s) with optional sortedness via template-parameter
2  * `Ordering` and optional use of GC via `useGCAllocation`.
3  *
4  * TODO UniqueArray!(const(E)) where E has indirections
5  *
6  * TODO Support for constructing from r-value range (container) of non-copyable
7  * elements.
8  *
9  * TODO Add some way to implement lazy sorting either for the whole array (via
10  * bool flag) or completeSort at a certain offset (extra member).
11  *
12  * TODO Replace ` = void` with construction or emplace
13  *
14  * TODO Break out common logic into private `DynamicArray` and reuse with `alias
15  * this` to express StandardArray, SortedArray, SortedSetArray
16  *
17  * TODO Use std.array.insertInPlace in insert()?
18  * TODO Use std.array.replaceInPlace?
19  *
20  * TODO Use `std.algorithm.mutation.move` and `std.range.primitives.moveAt`
21  * when moving internal sub-slices
22  *
23  * TODO Add `c.insertAfter(r, x)` where `c` is a collection, `r` is a range
24  * previously extracted from `c`, and `x` is a value convertible to
25  * collection's element type. See_Also:
26  * https://forum.dlang.org/post/n3qq6e$2bis$1@digitalmars.com
27  *
28  * TODO replace qcmeman with std.experimental.allocator parameter defaulting to
29  * `Mallocator`
30  *
31  * TODO use `integer_sorting.radixSort` when element type `isSigned` or `isUnsigned` and
32  * above or below a certain threshold calculated by my benchmarks
33  *
34  * TODO Remove explicit moves when DMD std.algorithm.mutation.move calls these
35  * members for us (if they exist)
36  */
37 module nxt.array_ex;
38 
39 /// Array element ordering.
40 enum Ordering
41 {
42     unsorted, // unsorted array
43     sortedValues, // sorted array with possibly duplicate values
44     sortedUniqueSet, // sorted array with unique values
45 }
46 
47 /// Is `true` iff `ordering` is sorted.
48 enum isOrdered(Ordering ordering) = ordering != Ordering.unsorted;
49 
50 version(unittest)
51 {
52     import std.algorithm.iteration : map, filter;
53     import std.algorithm.comparison : equal;
54     import std.conv : to;
55     import std.meta : AliasSeq;
56     import core.internal.traits : Unqual;
57     import nxt.dbgio : dbg;
58     import nxt.array_help : s;
59 }
60 
61 import nxt.container_traits : ContainerElementType, needsMove;
62 
63 /// Is `true` iff `C` is an instance of an `Array` container.
64 template isArrayContainer(C)
65 {
66     import std.traits : isInstanceOf;
67     enum isArrayContainer = isInstanceOf!(Array, C);
68 }
69 
70 /// Semantics of copy construction and copy assignment.
71 enum Assignment
72 {
73     disabled,           /// for reference counting use `std.typecons.RefCounted`. for safe slicing use `borrown`
74     move,               /// only move construction allowed
75     copy                /// always copy (often not the desirable)
76 }
77 
78 /** Array of value types `E` with optional sortedness/ordering.
79  *
80  * Always `@safe pure nothrow @nogc` when possible.
81  *
82  * `Assignment` either
83  * - is disabled
84  * - does Rust-style move, or
85  * - does C++-style copying
86  *
87  * Params:
88  * GCAllocation = `true` iff `GC.malloc` is used for store allocation,
89  * otherwise C's `{m,ce,re}alloc()` is used.
90  */
91 private struct Array(E,
92                      // TODO merge these flags into one to reduce template bloat
93                      Assignment assignment = Assignment.disabled,
94                      Ordering ordering = Ordering.unsorted,
95                      bool useGCAllocation = false,
96                      CapacityType = size_t, // see also https://github.com/izabera/s
97                      alias less = "a < b") // TODO move out of this definition and support only for the case when `ordering` is not `Ordering.unsorted`
98 if (is(CapacityType == ulong) ||           // 3 64-bit words
99     is(CapacityType == uint))              // 2 64-bit words
100 {
101     import core.internal.traits : hasElaborateDestructor;
102     import core.lifetime : emplace, move, moveEmplace;
103     import std.algorithm.mutation : moveEmplaceAll;
104     import std.range.primitives : isInputRange, isInfinite, ElementType;
105     import std.traits : isIterable, isAssignable, Unqual, isArray, isScalarType, hasIndirections, TemplateOf;
106     import std.functional : binaryFun;
107     import std.meta : allSatisfy;
108 
109     import nxt.qcmeman : malloc, calloc, realloc, free, gc_addRange, gc_removeRange;
110 
111     private template shouldAddGCRange(T)
112     {
113         import std.traits : hasIndirections, isInstanceOf;
114         enum shouldAddGCRange = hasIndirections!T && !isInstanceOf!(Array, T); // TODO unify to container_traits.shouldAddGCRange
115     }
116 
117     /// Mutable element type.
118     private alias MutableE = Unqual!E;
119 
120     /// Template for type of `this`.
121     private alias ThisTemplate = TemplateOf!(typeof(this));
122 
123     /// Same type as this but with mutable element type.
124     private alias MutableThis = ThisTemplate!(MutableE, assignment, ordering, useGCAllocation, CapacityType, less);
125 
126     static if (useGCAllocation || // either we asked for allocation
127                shouldAddGCRange!E) // or we need GC ranges
128     {
129         import core.memory : GC;
130     }
131 
132     /// Is `true` iff `Array` can be interpreted as a narrow D `string` or `wstring`.
133     private enum isNarrowString = (is(MutableE == char) ||
134                                    is(MutableE == wchar));
135 
136     static if (isOrdered!ordering)
137     {
138         static assert(!isNarrowString, "A narrow string cannot be an ordered array because it's not random access'");
139     }
140 
141     alias comp = binaryFun!less; //< comparison
142 
143     /// Create a empty array.
144     // this(typeof(null)) nothrow
145     // {
146     //     version(showCtors) dbg("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
147     //     // nothing needed, rely on default initialization of data members
148     // }
149 
150     /// Returns: an array of length `initialLength` with all elements default-initialized to `ElementType.init`.
151     static typeof(this) withLength(size_t initialLength)
152         @trusted
153     {
154         version(showCtors) dbg("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
155 
156         debug { typeof(return) that; }
157         else { typeof(return) that = void; }
158 
159         // TODO functionize:
160         if (initialLength > smallCapacity)
161         {
162             emplace!Large(&that._large, initialLength, initialLength, true); // no elements so we need to zero
163         }
164         else
165         {
166             that._small.length = cast(ubyte)initialLength;
167         }
168 
169         version(showCtors) dbg("EXITING: ", __PRETTY_FUNCTION__);
170         return that;
171     }
172 
173     /// Returns: an array with initial capacity `initialCapacity`.
174     static typeof(this) withCapacity(size_t initialCapacity)
175         @trusted
176     {
177         version(showCtors) dbg("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
178 
179         debug { typeof(return) that; }
180         else { typeof(return) that = void; }
181 
182         if (initialCapacity > smallCapacity)
183         {
184             emplace!Large(&that._large, initialCapacity, 0, false);
185         }
186         else
187         {
188             that._small.length = 0;
189         }
190 
191         version(showCtors) dbg("EXITING: ", __PRETTY_FUNCTION__);
192         return that;
193     }
194 
195     /// Returns: an array of one element `element`.
196     static typeof(this) withElement(E element)
197         @trusted
198     {
199         version(showCtors) dbg("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
200 
201         debug { typeof(return) that; }
202         else { typeof(return) that = void; }
203 
204         // TODO functionize:
205         enum initialLength = 1;
206         if (initialLength > smallCapacity)
207         {
208             emplace!Large(&that._large, initialLength, initialLength, false);
209         }
210         else
211         {
212             emplace!Small(&that._small, initialLength, false);
213         }
214 
215         // move element
216         static if (__traits(isCopyable, E))
217         {
218             that._mptr[0] = element;
219         }
220         else static if (!shouldAddGCRange!E)
221         {
222             moveEmplace(*cast(MutableE*)&element, // TODO can we prevent this cast?
223                         that._mptr[0]); // safe to cast away constness when no indirections
224         }
225         else
226         {
227             moveEmplace(element, that._mptr[0]); // TODO remove `move` when compiler does it for us
228         }
229 
230         return that;
231     }
232 
233     /// Returns: an array of `Us.length` number of elements set to `elements`.
234     static typeof(this) withElements(Us...)(Us elements)
235         @trusted
236     {
237         version(showCtors) dbg("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
238 
239         debug { typeof(return) that; }
240         else { typeof(return) that = void; }
241 
242         // TODO functionize:
243         enum initialLength = Us.length;
244         if (initialLength > smallCapacity)
245         {
246             emplace!Large(&that._large, initialLength, initialLength, false);
247         }
248         else
249         {
250             emplace!Small(&that._small, initialLength, false);
251         }
252 
253         // move elements
254         foreach (immutable i, ref element; elements)
255         {
256             static if (!shouldAddGCRange!E)
257             {
258                 moveEmplace(*cast(MutableE*)&element,
259                             that._mptr[i]); // safe to cast away constness when no indirections
260             }
261             else
262             {
263                 moveEmplace(element, that._mptr[i]); // TODO remove `move` when compiler does it for us
264             }
265         }
266 
267         static if (isOrdered!ordering)
268         {
269             that.sortElements!comp();
270         }
271 
272         return that;
273     }
274 
275     static if (assignment == Assignment.copy)
276     {
277         /// Copy construction.
278         this(this) @trusted
279         {
280             version(showCtors) dbg("Copy ctor: ", typeof(this).stringof);
281             if (isLarge)        // only large case needs special treatment
282             {
283                 auto rhs_storePtr = _large.ptr; // save store pointer
284                 _large.setCapacity(this.length); // pack by default
285                 // _large.length already copied
286                 _large.ptr = allocate(this.length, false);
287                 foreach (immutable i; 0 .. this.length)
288                 {
289                     _large.ptr[i] = rhs_storePtr[i];
290                 }
291             }
292         }
293 
294         /// Copy assignment.
295         void opAssign(typeof(this) rhs) @trusted
296         {
297             version(showCtors) dbg("Copy assign: ", typeof(this).stringof);
298             // self-assignment may happen when assigning derefenced pointer
299             if (isLarge)        // large = ...
300             {
301                 if (rhs.isLarge) // large = large
302                 {
303                     // TODO functionize to Large.opAssign(Large rhs):
304                     if (_large.ptr != rhs._large.ptr) // if not self assignment
305                     {
306                         _large.length = rhs._large.length;
307                         reserve(rhs._large.length);
308                         foreach (immutable i; 0 .. rhs._large.length)
309                         {
310                             _large.ptr[i] = rhs._large.ptr[i];
311                         }
312                     }
313                 }
314                 else            // large = small
315                 {
316                     {            // make it small
317                         clear(); // clear large storage
318                         _large.isLarge = false; // TODO needed?
319                     }
320                     _small = rhs._small; // small
321                 }
322             }
323             else                // small = ...
324             {
325                 if (rhs.isLarge) // small = large
326                 {
327                     {            // make it large
328                         clear(); // clear small storage
329                         _large.isLarge = true; // TODO needed?
330                     }
331                     // TODO functionize to Large.opAssign(Large rhs):
332                     if (_large.ptr != rhs._large.ptr) // if not self assignment
333                     {
334                         _large.length = rhs._large.length;
335                         reserve(rhs._large.length);
336                         foreach (immutable i; 0 .. rhs._large.length)
337                         {
338                             _large.ptr[i] = rhs._large.ptr[i];
339                         }
340                     }
341                 }
342                 else            // small = small
343                 {
344                     _small = rhs._small;
345                 }
346             }
347         }
348     }
349     else static if (assignment == Assignment.disabled)
350     {
351         @disable this(this);
352     }
353     else static if (assignment == Assignment.move)
354     {
355         /// Copy ctor moves.
356         this(typeof(this) rhs) @trusted
357         {
358             version(showCtors) dbg("Copying: ", typeof(this).stringof);
359             assert(!isBorrowed);
360             moveEmplace(rhs, this); // TODO remove `move` when compiler does it for us
361         }
362 
363         /// Assignment moves.
364         void opAssign(typeof(this) rhs) @trusted
365         {
366             assert(!isBorrowed);
367             import std.algorith.mutation : move;
368             move(rhs, this);  // TODO remove `move` when compiler does it for us
369         }
370     }
371 
372     static if (__traits(isCopyable, E))
373     {
374         /// Returns: shallow duplicate of `this`.
375         @property MutableThis dup() const @trusted // `MutableThis` mimics behaviour of `dup` for builtin D arrays
376         {
377             debug { typeof(return) that; }
378             else { typeof(return) that = void; }
379 
380             if (isLarge)
381             {
382                 emplace!(that.Large)(&that._large, _large.length, _large.length, false);
383                 foreach (immutable i; 0 .. _large.length)
384                 {
385                     // TODO is there a more standardized way of solving this other than this hacky cast?
386                     that._large.ptr[i] = (cast(E*)_large.ptr)[i];
387                 }
388             }
389             else
390             {
391                 emplace!(that.Small)(&that._small, _small.length, false);
392                 // TODO is there a more standardized way of solving this other than this hacky cast?
393                 that._small.elms[0 .. _small.length] = (cast(E[_small.elms.length])_small.elms)[0 .. _small.length]; // copy elements
394             }
395             return that;
396         }
397     }
398 
399     bool opEquals(in ref typeof(this) rhs) const
400         @trusted
401     {
402         static if (__traits(isCopyable, E))
403         {
404             return this[] == rhs[]; // TODO fix DMD to make this work for non-copyable aswell
405         }
406         else
407         {
408             if (this.length != rhs.length) { return false; }
409             foreach (immutable i; 0 .. this.length)
410             {
411                 if (this.ptr[i] != rhs.ptr[i]) { return false; }
412             }
413             return true;
414         }
415     }
416     bool opEquals(in typeof(this) rhs) const
417         @trusted
418     {
419         static if (__traits(isCopyable, E))
420         {
421             return this[] == rhs[]; // TODO fix DMD to make this work for non-copyable aswell
422         }
423         else
424         {
425             if (this.length != rhs.length) { return false; }
426             foreach (immutable i; 0 .. this.length)
427             {
428                 if (this.ptr[i] != rhs.ptr[i]) { return false; }
429             }
430             return true;
431         }
432     }
433 
434     /// Compare with range `R` with comparable element type.
435     pragma(inline, true)
436     bool opEquals(R)(R rhs) const
437         if (isInputRange!R && !isInfinite!R)
438     {
439         return opSlice.equal(rhs);
440     }
441 
442     /// Calculate D associative array (AA) key hash.
443     hash_t toHash() const     // cannot currently be template-lazy
444         nothrow @trusted
445     {
446         pragma(msg, "WARNING: using toHash() when we should use toDigest instead");
447         import core.internal.hash : hashOf;
448         static if (__traits(isCopyable, E))
449         {
450             return this.length ^ hashOf(slice());
451         }
452         else
453         {
454             typeof(return) hash = this.length;
455             foreach (immutable i; 0 .. this.length)
456             {
457                 hash ^= this.ptr[i].hashOf;
458             }
459             return hash;
460         }
461     }
462 
463     /** Construct from InputRange `values`.
464         If `values` are sorted `assumeSortedParameter` is `true`.
465 
466         TODO Have `assumeSortedParameter` only when `isOrdered!ordering` is true
467      */
468     this(R)(R values, bool assumeSortedParameter = false)
469         @trusted
470         @("complexity", "O(n*log(n))")
471         if (isIterable!R)
472     {
473         version(showCtors) dbg("ENTERING: smallCapacity:", smallCapacity, " @",  __PRETTY_FUNCTION__);
474 
475         // append new data
476         import std.range.primitives : hasLength;
477         static if (hasLength!R)
478         {
479             // TODO choose large or small depending on values.length
480             _small.isLarge = false;
481             _small.length = 0;
482 
483             reserve(values.length); // fast reserve
484             setOnlyLength(values.length);
485 
486             // static if (__traits(isRef))
487             // {
488             //     // TODO dup elements
489             // }
490             // else
491             // {
492             //     // TODO move elements
493             // }
494 
495             size_t i = 0;
496             foreach (ref value; move(values)) // TODO remove `move` when compiler does it for us
497             {
498                 // TODO functionize:
499                 static if (needsMove!(typeof(value)))
500                 {
501                     moveEmplace(value, _mptr[i++]);
502                 }
503                 else
504                 {
505                     _mptr[i++] = value;
506                 }
507             }
508         }
509         else
510         {
511             // always start small
512             _small.isLarge = false;
513             _small.length = 0;
514 
515             size_t i = 0;
516             foreach (ref value; move(values)) // TODO remove `move` when compiler does it for us
517             {
518                 reserve(i + 1); // slower reserve
519                 // TODO functionize:
520                 static if (needsMove!(typeof(value)))
521                 {
522                     moveEmplace(value, _mptr[i++]);
523                 }
524                 else
525                 {
526                     _mptr[i++] = value;
527                 }
528                 setOnlyLength(i); // must be set here because correct length is needed in reserve call above in this same scope
529             }
530         }
531 
532         if (!assumeSortedParameter)
533         {
534             static if (isOrdered!ordering)
535             {
536                 sortElements!comp();
537             }
538             static if (ordering == Ordering.sortedUniqueSet)
539             {
540                 import std.algorithm.iteration : uniq;
541                 size_t j = 0;
542                 foreach (ref e; uniq(slice))
543                 {
544                     auto ePtr = &e;
545                     const separate = ePtr != &_mptr[j];
546                     if (separate)
547                     {
548                         move(*cast(MutableE*)ePtr, _mptr[j]);
549                     }
550                     ++j;
551                 }
552                 shrinkTo(j);
553             }
554         }
555 
556         version(showCtors) dbg("EXITING: ", __PRETTY_FUNCTION__);
557     }
558 
559     /// Sort all elements in-place regardless of `ordering`.
560     private void sortElements(alias comp_)()
561         @trusted
562     {
563         import std.algorithm.sorting : sort;
564         sort!comp_(_mptr[0 .. length]);
565     }
566 
567     /// Reserve room for `newCapacity`.
568     void reserve(size_t newCapacity)
569         pure @trusted
570     {
571         assert(!isBorrowed);
572         if (newCapacity <= capacity) { return; }
573         if (isLarge)
574         {
575             static if (shouldAddGCRange!E)
576             {
577                 gc_removeRange(_mptr);
578             }
579             import std.math : nextPow2;
580             reallocateLargeStoreAndSetCapacity(newCapacity.nextPow2);
581             static if (shouldAddGCRange!E)
582             {
583                 gc_addRange(_mptr, _large.capacity * E.sizeof);
584             }
585         }
586         else
587         {
588             if (newCapacity > smallCapacity) // convert to large
589             {
590                 auto tempLarge = Large(newCapacity, length, false);
591 
592                 // move small to temporary large
593                 foreach (immutable i; 0 .. length)
594                 {
595                     moveEmplace(_small._mptr[i],
596                                 tempLarge._mptr[i]);
597                 }
598 
599                 static if (hasElaborateDestructor!E)
600                 {
601                     destroyElements();
602                 }
603 
604                 // TODO functionize:
605                 {               // make this large
606                     moveEmplace(tempLarge, _large);
607                 }
608 
609                 assert(isLarge);
610             }
611             else
612             {
613                 // staying small
614             }
615         }
616     }
617 
618     /// Pack/Compress storage.
619     void compress()
620         pure @trusted
621     {
622         assert(!isBorrowed);
623         if (isLarge)
624         {
625             if (this.length)
626             {
627                 if (this.length <= smallCapacity)
628                 {
629                     Small tempSmall = Small(length, false);
630 
631                     // move elements to temporary small. TODO make moveEmplaceAll work on char[],char[] and use
632                     foreach (immutable i; 0 .. length)
633                     {
634                         moveEmplace(_large._mptr[i],
635                                     tempSmall._mptr[i]);
636                     }
637 
638                     // free existing large data
639                     static if (shouldAddGCRange!E)
640                     {
641                         gc_removeRange(_mptr);
642                     }
643                     static if (useGCAllocation)
644                     {
645                         GC.free(_mptr);
646                     }
647                     else
648                     {
649                         free(_mptr);
650                     }
651 
652                     moveEmplace(tempSmall, _small);
653                 }
654                 else
655                 {
656                     if (_large.capacity != this.length)
657                     {
658                         static if (shouldAddGCRange!E)
659                         {
660                             gc_removeRange(_mptr);
661                         }
662                         reallocateLargeStoreAndSetCapacity(this.length);
663                         static if (shouldAddGCRange!E)
664                         {
665                             gc_addRange(_mptr, _large.capacity * E.sizeof);
666                         }
667                     }
668                 }
669             }
670             else                // if empty
671             {
672                 // free data
673                 static if (shouldAddGCRange!E)
674                 {
675                     gc_removeRange(_mptr);
676                 }
677                 static if (useGCAllocation)
678                 {
679                     GC.free(_mptr);
680                 }
681                 else
682                 {
683                     free(_mptr);
684                 }
685                 _large.capacity = 0;
686                 _large.ptr = null;
687             }
688         }
689     }
690     /// ditto
691     alias pack = compress;
692 
693     /// Reallocate storage. TODO move to Large.reallocateAndSetCapacity
694     private void reallocateLargeStoreAndSetCapacity(size_t newCapacity) pure @trusted
695     {
696         pragma(inline, true);
697         _large.setCapacity(newCapacity);
698         static if (useGCAllocation)
699         {
700             _large.ptr = cast(E*)GC.realloc(_mptr, E.sizeof * _large.capacity);
701         }
702         else                    // @nogc
703         {
704             _large.ptr = cast(E*)realloc(_mptr, E.sizeof * _large.capacity);
705             assert(_large.ptr, "Reallocation failed");
706         }
707     }
708 
709     /// Destruct.
710     ~this() @trusted @nogc
711     {
712         pragma(inline, true);
713         assert(!isBorrowed);
714         if (isLarge)
715         {
716             debug assert(_large.ptr != _ptrMagic, "Double free."); // trigger fault for double frees
717         }
718         release();
719         if (isLarge)
720         {
721             debug _large.ptr = _ptrMagic; // tag as freed
722         }
723     }
724 
725     /// Empty.
726     void clear() @nogc
727     {
728         pragma(inline, true);
729         assert(!isBorrowed);
730         release();
731         resetInternalData();
732     }
733     /// ditto
734     pragma(inline, true)
735     void opAssign(typeof(null))
736     {
737         clear();
738     }
739 
740     /// Destroy elements.
741     static if (hasElaborateDestructor!E)
742     {
743         private void destroyElements() @trusted
744         {
745             foreach (immutable i; 0 .. this.length)
746             {
747                 .destroy(_mptr[i]);
748             }
749         }
750     }
751 
752     /// Release internal store.
753     private void release() @trusted @nogc
754     {
755         static if (hasElaborateDestructor!E)
756         {
757             destroyElements();
758         }
759         if (isLarge)
760         {
761             static if (shouldAddGCRange!E)
762             {
763                 gc_removeRange(_large.ptr);
764             }
765             static if (useGCAllocation)
766             {
767                 GC.free(_large.ptr);
768             }
769             else                // @nogc
770             {
771                 static if (!shouldAddGCRange!E)
772                 {
773                     free(cast(MutableE*)_large.ptr); // safe to case away constness
774                 }
775                 else
776                 {
777                     free(_large.ptr);
778                 }
779             }
780         }
781     }
782 
783     /// Reset internal data.
784     pragma(inline, true)
785     private void resetInternalData() @trusted pure @nogc
786     {
787         if (isLarge)
788         {
789             _large.ptr = null;
790             _large.length = 0;
791             _large.capacity = 0;
792         }
793         else
794         {
795             _small.length = 0; // fast discardal
796         }
797     }
798 
799     /// Is `true` if `U` can be assign to the element type `E` of `this`.
800     enum isElementAssignable(U) = isAssignable!(E, U);
801 
802     /** Removal doesn't need to care about ordering. */
803     ContainerElementType!(typeof(this), E) removeAt(size_t index)
804         @trusted
805         @("complexity", "O(length)")
806     {
807         assert(!isBorrowed);
808         assert(index < this.length);
809         auto value = move(_mptr[index]);
810 
811         // TODO use this instead:
812         // immutable si = index + 1;   // source index
813         // immutable ti = index;       // target index
814         // immutable restLength = this.length - (index + 1);
815         // moveEmplaceAll(_mptr[si .. si + restLength],
816         //                _mptr[ti .. ti + restLength]);
817 
818         foreach (immutable i; 0 .. this.length - (index + 1)) // each element index that needs to be moved
819         {
820             immutable si = index + i + 1; // source index
821             immutable ti = index + i; // target index
822             moveEmplace(_mptr[si], // TODO remove `move` when compiler does it for us
823                         _mptr[ti]);
824         }
825 
826         decOnlyLength();
827         return move(value); // TODO remove `move` when compiler does it for us
828     }
829 
830     /** Removal doesn't need to care about ordering. */
831     pragma(inline, true)
832     ContainerElementType!(typeof(this), E) popFront()
833         @trusted
834         @("complexity", "O(length)")
835     {
836         return removeAt(0);
837     }
838 
839     /** Removal doesn't need to care about ordering. */
840     pragma(inline)
841     void popBack() @("complexity", "O(1)")
842     {
843         assert(!isBorrowed);
844         assert(!empty);
845         decOnlyLength();
846     }
847 
848     /** Pop back element and return it. */
849     pragma(inline)
850     E backPop()
851         @trusted
852     {
853         assert(!isBorrowed);
854         assert(!empty);
855         decOnlyLength();
856         // TODO functionize:
857         static if (needsMove!E)
858         {
859             return move(_mptr[this.length]); // move is indeed need here
860         }
861         else
862         {
863             return _mptr[this.length]; // no move needed
864         }
865     }
866 
867     /** Pop last `count` back elements. */
868     pragma(inline, true)
869     void popBackN(size_t count) @("complexity", "O(1)")
870     {
871         assert(!isBorrowed);
872         shrinkTo(this.length - count);
873     }
874 
875     static if (!isOrdered!ordering) // for unsorted arrays
876     {
877         /// Push back (append) `values`.
878         pragma(inline) void insertBack(Us...)(Us values)
879             @trusted
880             @("complexity", "O(1)")
881             if (values.length >= 1 &&
882                 allSatisfy!(isElementAssignable, Us))
883         {
884             assert(!isBorrowed);
885             immutable newLength = this.length + values.length;
886             reserve(newLength);
887             foreach (immutable i, ref value; values) // `ref` so we can `move`
888             {
889                 // TODO functionize:
890                 static if (needsMove!(typeof(value)))
891                 {
892                     moveEmplace(*cast(MutableE*)&value, _mptr[this.length + i]);
893                 }
894                 else
895                 {
896                     _mptr[this.length + i] = value;
897                 }
898             }
899             setOnlyLength(this.length + values.length);
900         }
901 
902         /// ditto
903         void insertBack(R)(R values) @("complexity", "O(values.length)") @trusted
904             if (isInputRange!R && !isInfinite!R &&
905                 !(isArray!R) &&
906                 !(isArrayContainer!R) &&
907                 isElementAssignable!(ElementType!R))
908         {
909             assert(!isBorrowed);
910             import std.range.primitives : hasLength;
911             static if (hasLength!R)
912             {
913                 const nextLength = this.length + values.length;
914                 reserve(nextLength);
915                 size_t i = 0;
916                 foreach (ref value; values) // `ref` so we can `move`
917                 {
918                     // TODO functionize:
919                     static if (needsMove!(typeof(value)))
920                     {
921                         moveEmplace(*cast(Mutable!E*)&value, _mptr[this.length + i]);
922                     }
923                     else
924                     {
925                         _mptr[this.length + i] = value;
926                     }
927                     ++i;
928                 }
929                 setOnlyLength(nextLength);
930             }
931             else
932             {
933                 foreach (ref value; values) // `ref` so we can `move`
934                 {
935                     insertBack(value);
936                 }
937             }
938         }
939 
940         /// ditto.
941         void insertBack(A)(A values) @trusted @("complexity", "O(values.length)")
942             if (isArray!A &&
943                 (is(MutableE == Unqual!(typeof(A.init[0]))) || // for narrow strings
944                  isElementAssignable!(ElementType!A)))
945         {
946             assert(!isBorrowed);
947             static if (is(A == immutable(E)[]))
948             {
949                 // immutable array cannot overlap with mutable array container
950                 // data; no need to check for overlap with `overlaps()`
951                 reserve(this.length + values.length);
952                 _mptr[this.length .. this.length + values.length] = values[];
953                 setOnlyLength(this.length + values.length);
954             }
955             else
956             {
957                 import nxt.overlapping : overlaps;
958                 if (this.ptr == values[].ptr) // called for instances as: `this ~= this`
959                 {
960                     reserve(2*this.length);
961                     foreach (immutable i; 0 .. this.length)
962                     {
963                         _mptr[this.length + i] = ptr[i]; // needs copying
964                     }
965                     setOnlyLength(2 * this.length);
966                 }
967                 else if (overlaps(this[], values[]))
968                 {
969                     assert(0, `TODO Handle overlapping arrays`);
970                 }
971                 else
972                 {
973                     reserve(this.length + values.length);
974                     static if (is(MutableE == Unqual!(ElementType!A))) // TODO also when `E[]` is `A[]`
975                     {
976                         _mptr[this.length .. this.length + values.length] = values[];
977                     }
978                     else
979                     {
980                         foreach (immutable i, ref value; values)
981                         {
982                             _mptr[this.length + i] = value;
983                         }
984                     }
985                     setOnlyLength(this.length + values.length);
986                 }
987             }
988         }
989 
990         /// ditto.
991         void insertBack(A)(in ref A values) @trusted @("complexity", "O(values.length)") // TODO `in` parameter qualifier doesn't work here. Compiler bug?
992             if (isArrayContainer!A &&
993                 (is(MutableE == Unqual!(typeof(A.init[0]))) || // for narrow strings
994                  isElementAssignable!(ElementType!A)))
995         {
996             insertBack(values[]);
997         }
998         alias put = insertBack;   // OutputRange support
999 
1000 
1001         // NOTE these separate overloads of opOpAssign are needed because one
1002         // `const ref`-parameter-overload doesn't work because of compiler bug
1003         // with: `this(this) @disable`
1004         pragma(inline)
1005         void opOpAssign(string op, Us...)(Us values)
1006             if (op == "~" &&
1007                 values.length >= 1 &&
1008                 allSatisfy!(isElementAssignable, Us))
1009         {
1010             assert(!isBorrowed);
1011             insertBack(move(values)); // TODO remove `move` when compiler does it for us
1012             // static if (values.length == 1)
1013             // {
1014             //     import std.traits : hasIndirections;
1015             //     static if (hasIndirections!(Us[0]))
1016             //     {
1017             //         insertBack(move(values)); // TODO remove `move` when compiler does it for us
1018             //     }
1019             //     else
1020             //     {
1021             //         insertBack(move(cast(Unqual!(Us[0]))values[0])); // TODO remove `move` when compiler does it for us
1022             //     }
1023             // }
1024             // else
1025             // {
1026             //     insertBack(move(values)); // TODO remove `move` when compiler does it for us
1027             // }
1028         }
1029 
1030         pragma(inline)
1031         void opOpAssign(string op, R)(R values)
1032             if (op == "~" &&
1033                 isInputRange!R &&
1034                 !isInfinite!R &&
1035                 allSatisfy!(isElementAssignable, ElementType!R))
1036         {
1037             assert(!isBorrowed);
1038             // TODO use move(values)
1039             insertBack(values); // TODO remove `move` when compiler does it for us
1040         }
1041 
1042         pragma(inline, true)
1043         void opOpAssign(string op, A)(ref A values)
1044             if (op == "~" &&
1045                 isArrayContainer!A &&
1046                 isElementAssignable!(ElementType!A))
1047         {
1048             assert(!isBorrowed);
1049             insertBack(values);
1050         }
1051     }
1052 
1053     import searching_ex : containsStoreIndex; // TODO this is redundant but elides rdmd dependency error for array_ex.d
1054 
1055     static if (isOrdered!ordering)
1056     {
1057         import std.range : SearchPolicy, assumeSorted;
1058 
1059         /// Returns: `true` iff this contains `value`.
1060         bool contains(U)(U value) const @nogc @("complexity", "O(log(length))")
1061         {
1062             return this[].contains(value); // reuse `SortedRange.contains`
1063         }
1064 
1065         /** Wrapper for `std.range.SortedRange.lowerBound` when this `ordering` is sorted. */
1066         auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, U)(U e) inout @("complexity", "O(log(length))")
1067         {
1068             return this[].lowerBound!sp(e); // reuse `SortedRange.lowerBound`
1069         }
1070 
1071         /** Wrapper for `std.range.SortedRange.upperBound` when this `ordering` is sorted. */
1072         auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, U)(U e) inout @("complexity", "O(log(length))")
1073         {
1074             return this[].upperBound!sp(e); // reuse `SortedRange.upperBound`
1075         }
1076 
1077         static if (ordering == Ordering.sortedUniqueSet)
1078         {
1079             /** Inserts several `values` into `this` ordered set.
1080 
1081                 Returns: `bool`-array with same length as `values`, where i:th
1082                 `bool` value is set if `value[i]` wasn't previously in `this`.
1083             */
1084             bool[Us.length] insertMany(SearchPolicy sp = SearchPolicy.binarySearch, Us...)(Us values) @("complexity", "O(length)")
1085                 if (values.length >= 1 &&
1086                     allSatisfy!(isElementAssignable, Us))
1087             in
1088             {
1089                 assert(!isBorrowed);
1090 
1091                 // assert no duplicates in `values`
1092                 import std.range.primitives : empty;
1093                 import std.algorithm.searching : findAdjacent;
1094                 import std.algorithm.sorting : sort;
1095 
1096                 // TODO functionize or use other interface in pushing `values`
1097                 import std.traits : CommonType;
1098                 CommonType!Us[Us.length] valuesArray;
1099                 foreach (immutable i, const ref value; values)
1100                 {
1101                     valuesArray[i] = value;
1102                 }
1103                 assert(sort(valuesArray[]).findAdjacent.empty,
1104                        "Parameter `values` must not contain duplicate elements");
1105             }
1106             do
1107             {
1108                 static if (values.length == 1) // faster because `contains()` followed by `completeSort()` searches array twice
1109                 {
1110                     import nxt.searching_ex : containsStoreIndex;
1111                     size_t index;
1112                     if (slice.assumeSorted!comp.containsStoreIndex!sp(values, index)) // faster than `completeSort` for single value
1113                     {
1114                         return [false];
1115                     }
1116                     else
1117                     {
1118                         insertAtIndexHelper(index, values);
1119                         return [true];
1120                     }
1121                 }
1122                 else
1123                 {
1124                     import std.algorithm.sorting : completeSort;
1125 
1126                     debug { typeof(return) hits; }
1127                     else  { typeof(return) hits = void; }
1128 
1129                     size_t expandedLength = 0;
1130                     immutable initialLength = this.length;
1131                     foreach (immutable i, ref value; values)
1132                     {
1133                         // TODO reuse completeSort with uniqueness handling?
1134                         static if (values.length == 1)
1135                         {
1136                             // TODO reuse single parameter overload linearUniqueInsert() and return
1137                         }
1138                         else
1139                         {
1140                             // TODO reuse completeSort with uniqueness handling?
1141                         }
1142                         hits[i] = !this[0 .. initialLength].contains(value);
1143                         if (hits[i])
1144                         {
1145                             insertBackHelper(value); // NOTE: append but don't yet sort
1146                             ++expandedLength;
1147                         }
1148                     }
1149 
1150                     if (expandedLength != 0)
1151                     {
1152                         immutable ix = this.length - expandedLength;
1153                         // TODO use `_mptr` here instead and functionize to @trusted helper function
1154                         completeSort!comp(slice[0 .. ix].assumeSorted!comp,
1155                                           slice[ix .. this.length]);
1156                     }
1157                     return hits;
1158                 }
1159             }
1160         }
1161         else static if (ordering == Ordering.sortedValues)
1162         {
1163             /** Inserts `values`. */
1164             void insertMany(SearchPolicy sp = SearchPolicy.binarySearch, Us...)(Us values) @("complexity", "O(log(length))")
1165                 if (values.length >= 1 &&
1166                     allSatisfy!(isElementAssignable, Us))
1167             {
1168                 assert(!isBorrowed);
1169 
1170                 // TODO add optimization for values.length == 2
1171                 static if (values.length == 1)
1172                 {
1173                     import nxt.searching_ex : containsStoreIndex;
1174                     size_t index;
1175                     if (!slice.assumeSorted!comp.containsStoreIndex!sp(values, index)) // faster than `completeSort` for single value
1176                     {
1177                         insertAtIndexHelper(index, values);
1178                     }
1179                 }
1180                 else
1181                 {
1182                     insertBackHelper(values); // simpler because duplicates are allowed
1183                     immutable ix = this.length - values.length;
1184                     import std.algorithm.sorting : completeSort;
1185                     completeSort!comp(_mptr[0 .. ix].assumeSorted!comp,
1186                                       _mptr[ix .. this.length]);
1187                 }
1188             }
1189         }
1190     }
1191     else
1192     {
1193         /** Insert element(s) `values` at array offset `index`. */
1194         pragma(inline, true)
1195         void insertAtIndex(Us...)(size_t index, Us values) @("complexity", "O(length)")
1196             if (values.length >= 1 &&
1197                 allSatisfy!(isElementAssignable, Us))
1198         {
1199             assert(!isBorrowed);
1200             insertAtIndexHelper(index, values);
1201         }
1202 
1203         /** Insert element(s) `values` at the beginning. */
1204         pragma(inline, true)
1205         void pushFront(Us...)(Us values) @("complexity", "O(length)")
1206             if (values.length >= 1 &&
1207                 allSatisfy!(isElementAssignable, Us))
1208         {
1209             insertAtIndex(0, values);
1210         }
1211 
1212         alias prepend = pushFront;
1213 
1214         import nxt.traits_ex : isComparable;
1215         static if (__traits(isCopyable, E) &&
1216                    !is(E == char) &&
1217                    !is(E == wchar) &&
1218                    isComparable!E)
1219         {
1220             /// Returns: a sorted array copy.
1221             Array!(E, assignment, ordering.sortedValues, useGCAllocation, CapacityType, less_) toSortedArray(alias less_ = "a < b")() const
1222             {
1223                 return typeof(return)(slice);
1224             }
1225             /// Returns: a sorted set array copy.
1226             Array!(E, assignment, ordering.sortedUniqueSet, useGCAllocation, CapacityType, less_) toSortedSetArray(alias less_ = "a < b")() const
1227             {
1228                 return typeof(return)(slice);
1229             }
1230         }
1231     }
1232 
1233     /** Helper function used externally for unsorted and internally for sorted. */
1234     private void insertAtIndexHelper(Us...)(size_t index, Us values) @trusted @("complexity", "O(length)")
1235     {
1236         reserve(this.length + values.length);
1237 
1238         // TODO factor this to robustCopy. It uses copy when no overlaps (my algorithm_em), iteration otherwise
1239         enum usePhobosCopy = false;
1240         static if (usePhobosCopy)
1241         {
1242             // TODO why does this fail?
1243             import std.algorithm.mutation : copy;
1244             copy(ptr[index ..
1245                      this.length],        // source
1246                  _mptr[index + values.length ..
1247                        this.length + values.length]); // target
1248         }
1249         else
1250         {
1251             // move second part in reverse
1252             // TODO functionize move
1253             foreach (immutable i; 0 .. this.length - index) // each element index that needs to be moved
1254             {
1255                 immutable si = this.length - 1 - i; // source index
1256                 immutable ti = si + values.length; // target index
1257                 _mptr[ti] = ptr[si]; // TODO move construct?
1258             }
1259         }
1260 
1261         // set new values
1262         foreach (immutable i, ref value; values)
1263         {
1264             ptr[index + i] = value; // TODO use range algorithm instead?
1265         }
1266 
1267         setOnlyLength(this.length + values.length);
1268     }
1269 
1270     private void insertBackHelper(Us...)(Us values)
1271         @trusted
1272         @("complexity", "O(1)")
1273     {
1274         const newLength = this.length + values.length;
1275         reserve(newLength);
1276         foreach (immutable i, ref value; values)
1277         {
1278             // TODO functionize:
1279             static if (hasIndirections!(typeof(value)))
1280             {
1281                 moveEmplace(*cast(MutableE*)&value,
1282                             _mptr[this.length + i]);
1283             }
1284             else
1285             {
1286                 _mptr[this.length + i] = value;
1287             }
1288         }
1289         setOnlyLength(newLength);
1290     }
1291 
1292     @property @("complexity", "O(1)")
1293     pragma(inline):
1294 
1295     /// ditto
1296     static if (isOrdered!ordering)
1297     {
1298         const pure: // indexing and slicing must be `const` when ordered
1299 
1300         /// Slice operator must be const when ordered.
1301         auto opSlice() return scope @trusted  // TODO remove @trusted?
1302         {
1303             static if (is(E == class))
1304             {
1305                 // TODO remove this workaround when else branch works for classes
1306                 return (cast(E[])slice).assumeSorted!comp;
1307             }
1308             else
1309             {
1310                 return (cast(const(E)[])slice).assumeSorted!comp;
1311             }
1312         }
1313         /// ditto
1314         auto opSlice(this This)(size_t i, size_t j) return scope @trusted // TODO remove @trusted?
1315         {
1316             import std.range : assumeSorted;
1317             return (cast(const(E)[])slice[i .. j]).assumeSorted!comp;
1318         }
1319         private alias This = typeof(this);
1320 
1321         /// Index operator must be const to preserve ordering.
1322         ref const(E) opIndex(size_t i) return scope @nogc @trusted
1323         {
1324             assert(i < this.length);
1325             return ptr[i];
1326         }
1327 
1328         /// Get front element (as constant reference to preserve ordering).
1329         ref const(E) front() return scope @nogc @trusted
1330         {
1331             assert(!empty);
1332             return ptr[0];
1333         }
1334 
1335         /// Get back element (as constant reference to preserve ordering).
1336         ref const(E) back() return scope @nogc @trusted
1337         {
1338             assert(!empty);
1339             return ptr[this.length - 1];
1340         }
1341     }
1342     else
1343     {
1344         /// Set length to `newLength`.
1345         @property void length(size_t newLength)
1346         {
1347             if (newLength < length)
1348             {
1349                 shrinkTo(newLength);
1350             }
1351             else
1352             {
1353                 reserve(newLength);
1354                 setOnlyLength(newLength);
1355             }
1356         }
1357 
1358         @nogc:
1359 
1360         /// Index assign operator.
1361         ref E opIndexAssign(V)(V value, size_t i)
1362             @trusted return scope
1363         {
1364             assert(!isBorrowed);
1365             assert(i < this.length);
1366             static if (isScalarType!E)
1367                 ptr[i] = value;
1368             else
1369                 move(*(cast(MutableE*)(&value)), _mptr[i]); // TODO is this correct?
1370             return ptr[i];
1371         }
1372 
1373         /// Slice assign operator.
1374         static if (__traits(isCopyable, E))
1375         {
1376             void opSliceAssign(V)(V value, size_t i, size_t j)
1377                 @trusted return scope
1378             {
1379                 assert(!isBorrowed);
1380                 assert(i <= j);
1381                 assert(j <= this.length);
1382                 foreach (immutable i; 0 .. this.length)
1383                 {
1384                     ptr[i] = value;
1385                 }
1386             }
1387         }
1388 
1389         pure inout: // indexing and slicing has mutable access only when unordered
1390 
1391         /// Slice operator.
1392         pragma(inline)
1393         inout(E)[] opSlice() return
1394         {
1395             return this.opSlice(0, this.length);
1396         }
1397         /// ditto
1398         inout(E)[] opSlice(size_t i, size_t j)
1399             @trusted return scope
1400         {
1401             assert(i <= j);
1402             assert(j <= this.length);
1403             return ptr[i .. j];
1404         }
1405 
1406         @trusted:
1407 
1408         /// Index operator.
1409         ref inout(E) opIndex(size_t i) return scope
1410         {
1411             assert(i < this.length);
1412             return ptr[i];
1413         }
1414 
1415         /// Get front element reference.
1416         ref inout(E) front() return scope
1417         {
1418             assert(!empty);
1419             return ptr[0];
1420         }
1421 
1422         /// Get back element reference.
1423         ref inout(E) back() return scope
1424         {
1425             assert(!empty);
1426             return ptr[this.length - 1];
1427         }
1428     }
1429 
1430     alias data = opSlice;   // `std.array.Appender` compatibility
1431 
1432     // static if (__traits(isCopyable, E))
1433     // {
1434     //     string toString() const @property @trusted pure
1435     //     {
1436     //         import std.array : Appender;
1437     //         import std.conv : to;
1438     //         Appender!string s = "[";
1439     //         foreach (immutable i; 0 .. this.length)
1440     //         {
1441     //             if (i) { s.put(','); }
1442     //             s.put(ptr[i].to!string);
1443     //         }
1444     //         s.put("]");
1445     //         return s.data;
1446     //     }
1447     // }
1448 
1449     pure:
1450 
1451     /** Allocate heap regionwith `newCapacity` number of elements of type `E`.
1452         If `zero` is `true` they will be zero-initialized.
1453     */
1454     private static MutableE* allocate(size_t newCapacity, bool zero = false)
1455     {
1456         typeof(return) ptr = null;
1457         static if (useGCAllocation)
1458         {
1459             if (zero) { ptr = cast(typeof(return))GC.calloc(newCapacity, E.sizeof); }
1460             else      { ptr = cast(typeof(return))GC.malloc(newCapacity * E.sizeof); }
1461         }
1462         else                    // @nogc
1463         {
1464             if (zero) { ptr = cast(typeof(return))calloc(newCapacity, E.sizeof); }
1465             else      { ptr = cast(typeof(return))malloc(newCapacity * E.sizeof); }
1466             assert(ptr, "Allocation failed");
1467         }
1468         static if (shouldAddGCRange!E)
1469         {
1470             gc_addRange(ptr, newCapacity * E.sizeof);
1471         }
1472         return ptr;
1473     }
1474 
1475     @nogc:
1476 
1477     /// Check if empty.
1478     bool empty() const { return this.length == 0; }
1479 
1480     /// Get length.
1481     size_t length()
1482         const @trusted
1483     {
1484         if (isLarge)
1485         {
1486             return _large.length;
1487         }
1488         else
1489         {
1490             return _small.length;
1491         }
1492     }
1493     alias opDollar = length;    /// ditto
1494 
1495     /// Decrease only length.
1496     private void decOnlyLength()
1497         @trusted
1498     {
1499         if (isLarge)
1500         {
1501             assert(_large.length);
1502             _large.length = _large.length - 1;
1503         }
1504         else
1505         {
1506             assert(_small.length);
1507             _small.length = cast(SmallLengthType)(_small.length - 1);
1508         }
1509     }
1510 
1511     /// Set only length.
1512     private void setOnlyLength(size_t newLength)
1513         @trusted
1514     {
1515         if (isLarge)
1516         {
1517             _large.length = newLength; // TODO compress?
1518         }
1519         else
1520         {
1521             assert(newLength <= SmallLengthType.max);
1522             _small.length = cast(SmallLengthType)newLength;
1523         }
1524     }
1525 
1526     /// Get reserved capacity of store.
1527     size_t capacity()
1528         const
1529         @trusted
1530     {
1531         if (isLarge)
1532         {
1533             return _large.capacity;
1534         }
1535         else
1536         {
1537             return smallCapacity;
1538         }
1539     }
1540 
1541     /** Shrink length to `newLength`.
1542      *
1543      * If `newLength` >= `length` operation has no effect.
1544      */
1545     pragma(inline)
1546     void shrinkTo(size_t newLength)
1547     {
1548         assert(!isBorrowed);
1549         if (newLength < length)
1550         {
1551             static if (hasElaborateDestructor!E)
1552             {
1553                 dbg(length, " => ", newLength, " ", E.stringof);
1554                 foreach (immutable i; newLength .. length)
1555                 {
1556                     .destroy(_mptr[i]);
1557                 }
1558             }
1559             setOnlyLength(newLength);
1560         }
1561     }
1562 
1563     /// Get internal pointer.
1564     inout(E*) ptr()
1565         inout
1566         @trusted return scope // array access is @trusted
1567     {
1568         // TODO Use cast(ET[])?: alias ET = ContainerElementType!(typeof(this), E);
1569         if (isLarge)
1570         {
1571             return _large.ptr;
1572         }
1573         else
1574         {
1575             return _small.elms.ptr;
1576         }
1577     }
1578 
1579     /// Get internal pointer to mutable content. Doesn't need to be qualified with `scope`.
1580     private MutableE* _mptr()
1581         const return scope
1582     {
1583         if (isLarge)
1584         {
1585             return _large._mptr;
1586         }
1587         else
1588         {
1589             return _small._mptr;
1590         }
1591     }
1592 
1593     /// Get internal slice.
1594     private auto slice() inout
1595         @trusted return scope
1596     {
1597         return ptr[0 .. this.length];
1598     }
1599 
1600     /** Magic pointer value used to detect double calls to `free`.
1601 
1602         Cannot conflict with return value from `malloc` because the least
1603         significant bit is set (when the value ends with a one).
1604     */
1605     debug private enum _ptrMagic = cast(E*)0x0C6F3C6c0f3a8471;
1606 
1607     /// Returns: `true` if `this` currently uses large array storage.
1608     bool isLarge()
1609         const @trusted // trusted access to anonymous union
1610     {
1611         assert(_large.isLarge == _small.isLarge); // must always be same
1612         return _large.isLarge;
1613     }
1614 
1615     /// Returns: `true` if `this` currently uses small (packed) array storage.
1616     bool isSmall() const { return !isLarge; }
1617 
1618     private
1619     {
1620         private alias SmallLengthType = ubyte;
1621 
1622         private enum largeSmallLengthDifference = Large.sizeof - SmallLengthType.sizeof;
1623         private enum smallCapacity = largeSmallLengthDifference / E.sizeof;
1624         private enum smallPadSize = largeSmallLengthDifference - smallCapacity*E.sizeof;
1625     }
1626 
1627     /** Tag `this` borrowed.
1628         Used by wrapper logic in owned.d and borrowed.d
1629     */
1630     void tagAsBorrowed()
1631         @trusted
1632     {
1633         if (isLarge) { _large.isBorrowed = true; }
1634         else         { _small.isBorrowed = true; }
1635     }
1636 
1637     /** Tag `this` as not borrowed.
1638         Used by wrapper logic in owned.d and borrowed.d
1639     */
1640     void untagAsNotBorrowed()
1641         @trusted
1642     {
1643         if (isLarge) { _large.isBorrowed = false; }
1644         else         { _small.isBorrowed = false; }
1645     }
1646 
1647     /// Returns: `true` if this is borrowed
1648     bool isBorrowed()
1649         @trusted
1650     {
1651         if (isLarge) { return _large.isBorrowed; }
1652         else         { return _small.isBorrowed; }
1653     }
1654 
1655 private:                        // data
1656     enum useAlignedTaggedPointer = false; // TODO make this work when true
1657     static struct Large
1658     {
1659         static if (useAlignedTaggedPointer)
1660         {
1661             private enum lengthMax = CapacityType.max;
1662             version(LittleEndian) // see: http://forum.dlang.org/posting/zifyahfohbwavwkwbgmw
1663             {
1664                 import std.bitmanip : taggedPointer;
1665                 mixin(taggedPointer!(uint*, "_uintptr", // GC-allocated store pointer. See_Also: http://forum.dlang.org/post/iubialncuhahhxsfvbbg@forum.dlang.org
1666                                      bool, "isLarge", 1, // bit 0
1667                                      bool, "isBorrowed", 1, // bit 1
1668                           ));
1669 
1670                 pragma(inline, true):
1671                 @property void ptr(E* c)
1672                 {
1673                     assert((cast(ulong)c & 0b11) == 0);
1674                     _uintptr = cast(uint*)c;
1675                 }
1676 
1677                 @property inout(E)* ptr() inout
1678                 {
1679                     return cast(E*)_uintptr;
1680                 }
1681 
1682                 CapacityType capacity;  // store capacity
1683                 CapacityType length;  // store length
1684             }
1685             else
1686             {
1687                 static assert(0, "BigEndian support and test");
1688             }
1689         }
1690         else
1691         {
1692             private enum lengthBits = 8*CapacityType.sizeof - 2;
1693             private enum lengthMax = 2^^lengthBits - 1;
1694 
1695             static if (useGCAllocation)
1696             {
1697                 E* ptr; // GC-allocated store pointer. See_Also: http://forum.dlang.org/post/iubialncuhahhxsfvbbg@forum.dlang.org
1698             }
1699             else
1700             {
1701                 @nogc E* ptr;   // non-GC-allocated store pointer
1702             }
1703 
1704             CapacityType capacity;  // store capacity
1705 
1706             import std.bitmanip : bitfields; // TODO replace with own logic cause this mixin costs compilation speed
1707             mixin(bitfields!(CapacityType, "length", lengthBits,
1708                              bool, "isBorrowed", 1,
1709                              bool, "isLarge", 1,
1710                       ));
1711         }
1712 
1713         pragma(inline)
1714         this(size_t initialCapacity, size_t initialLength, bool zero)
1715         {
1716             assert(initialCapacity <= lengthMax);
1717             assert(initialLength <= lengthMax);
1718 
1719             setCapacity(initialCapacity);
1720             this.capacity = cast(CapacityType)initialCapacity;
1721             this.ptr = allocate(initialCapacity, zero);
1722             this.length = initialLength;
1723             this.isLarge = true;
1724             this.isBorrowed = false;
1725         }
1726 
1727         pragma(inline, true):
1728 
1729         void setCapacity(size_t newCapacity)
1730         {
1731             assert(newCapacity <= capacity.max);
1732             capacity = cast(CapacityType)newCapacity;
1733         }
1734 
1735         MutableE* _mptr() const
1736             @trusted
1737         {
1738             return cast(typeof(return))ptr;
1739         }
1740     }
1741 
1742     /// Small string storage.
1743     static struct Small
1744     {
1745         enum capacity = smallCapacity;
1746         private enum lengthBits = 8*SmallLengthType.sizeof - 2;
1747         private enum lengthMax = 2^^lengthBits - 1;
1748 
1749         import std.bitmanip : bitfields; // TODO replace with own logic cause this mixin costs compilation speed
1750         static if (useAlignedTaggedPointer)
1751         {
1752             mixin(bitfields!(bool, "isLarge", 1, // defaults to false
1753                              bool, "isBorrowed", 1, // default to false
1754                              SmallLengthType, "length", lengthBits,
1755                       ));
1756             static if (smallPadSize) { ubyte[smallPadSize] _ignoredPadding; }
1757             E[capacity] elms;
1758         }
1759         else
1760         {
1761             E[capacity] elms;
1762             static if (smallPadSize) { ubyte[smallPadSize] _ignoredPadding; }
1763             mixin(bitfields!(SmallLengthType, "length", lengthBits,
1764                              bool, "isBorrowed", 1, // default to false
1765                              bool, "isLarge", 1, // defaults to false
1766                              ));
1767         }
1768 
1769         pragma(inline)
1770         this(size_t initialLength, bool zero)
1771         {
1772             assert(initialLength <= lengthMax);
1773 
1774             this.length = cast(SmallLengthType)initialLength;
1775             this.isLarge = false;
1776             this.isBorrowed = false;
1777             if (zero)
1778             {
1779                 elms[] = E.init;
1780             }
1781         }
1782 
1783         pragma(inline, true)
1784         MutableE* _mptr() const
1785             @trusted
1786         {
1787             return cast(typeof(return))(elms.ptr);
1788         }
1789     }
1790 
1791     static assert(Large.sizeof ==
1792                   Small.sizeof);
1793 
1794     static if (E.sizeof == 1 &&
1795                size_t.sizeof == 8 && // 64-bit
1796                Small.capacity == 23)
1797     {
1798         static assert(Large.sizeof == 24);
1799         static assert(Small.sizeof == 24);
1800     }
1801 
1802     union
1803     {
1804         Large _large;            // indirected storage
1805         Small _small;            // non-indirected storage
1806     }
1807 }
1808 
1809 import std.traits : hasMember, isDynamicArray;
1810 
1811 /** Return an instance of `R` with capacity `capacity`. */
1812 R withCapacityMake(R)(size_t capacity)
1813 if (hasMember!(R, "withCapacity"))
1814 {
1815     return R.withCapacity(capacity);
1816 }
1817 /// ditto
1818 R withCapacityMake(R)(size_t capacity)
1819 if (isDynamicArray!R)
1820 {
1821     R r;
1822     // See http://forum.dlang.org/post/nupffaitocqjlamffuqi@forum.dlang.org
1823     r.reserve(capacity);
1824     return r;
1825 }
1826 
1827 @safe pure nothrow unittest
1828 {
1829     immutable capacity = 10;
1830     auto x = capacity.withCapacityMake!(int[]);
1831     assert(x.capacity >= capacity);
1832 }
1833 
1834 /** Return an instance of `R` of length `length`. */
1835 R withLengthMake(R)(size_t length)
1836 if (hasMember!(R, "withLength"))
1837 {
1838     return R.withLength(length);
1839 }
1840 /// ditto
1841 R withLengthMake(R)(size_t length)
1842 if (isDynamicArray!R)
1843 {
1844     R r;
1845     r.length = length;
1846     return r;
1847 }
1848 
1849 /** Return an instance of `R` containing a single element `e`. */
1850 R withElementMake(R)(typeof(R.init[0]) e)
1851 if (hasMember!(R, "withElement"))
1852 {
1853     return R.withElement(e);
1854 }
1855 /// ditto
1856 R withElementMake(R)(typeof(R.init[0]) e)
1857 if (isDynamicArray!R)
1858 {
1859     return [e];
1860 }
1861 
1862 alias UniqueArray(E, bool useGCAllocation = false) = Array!(E, Assignment.disabled, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1863 alias CopyingArray(E, bool useGCAllocation = false) = Array!(E, Assignment.copy, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1864 
1865 alias SortedCopyingArray(E, bool useGCAllocation = false, alias less = "a < b") = Array!(E, Assignment.copy, Ordering.sortedValues, useGCAllocation, size_t, less);
1866 alias SortedSetCopyingArray(E, bool useGCAllocation = false, alias less = "a < b") = Array!(E, Assignment.copy, Ordering.sortedUniqueSet, useGCAllocation, size_t, less);
1867 
1868 alias SortedUniqueArray(E, bool useGCAllocation = false, alias less = "a < b") = Array!(E, Assignment.disabled, Ordering.sortedValues, useGCAllocation, size_t, less);
1869 alias SortedSetUniqueArray(E, bool useGCAllocation = false, alias less = "a < b") = Array!(E, Assignment.disabled, Ordering.sortedUniqueSet, useGCAllocation, size_t, less);
1870 
1871 // string aliases
1872 alias UniqueString(bool useGCAllocation = false) = Array!(char,  Assignment.disabled, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1873 alias CopyingString(bool useGCAllocation = false) = Array!(char,  Assignment.copy, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1874 alias UniqueWString(bool useGCAllocation = false) = Array!(wchar, Assignment.disabled, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1875 alias CopyingWString(bool useGCAllocation = false) = Array!(wchar, Assignment.copy, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1876 alias UniqueDString(bool useGCAllocation = false) = Array!(dchar, Assignment.disabled, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1877 alias CopyingDString(bool useGCAllocation = false) = Array!(dchar, Assignment.copy, Ordering.unsorted, useGCAllocation, size_t, "a < b");
1878 
1879 @safe pure unittest
1880 {
1881     auto c = UniqueString!false();
1882     auto w = UniqueWString!false();
1883     auto d = UniqueDString!false();
1884 }
1885 
1886 @safe pure unittest
1887 {
1888     auto c = CopyingString!false();
1889     auto w = CopyingWString!false();
1890     auto d = CopyingDString!false();
1891 }
1892 
1893 @safe pure unittest
1894 {
1895     import std.conv : to;
1896     foreach (assignment; AliasSeq!(Assignment.disabled, Assignment.copy))
1897     {
1898         foreach (Ch; AliasSeq!(char, wchar, dchar))
1899         {
1900             alias Str = Array!(Ch, assignment);
1901             Str str_as = Str.withElement('a');
1902             Str str_as2 = 'a'.withElementMake!Str;
1903             Str str_as3 = 'a'.withElementMake!(Ch[]);
1904             assert(str_as == str_as2);
1905             assert(str_as2 == str_as3);
1906             str_as ~= Ch('_');
1907             assert(str_as[].equal("a_"));
1908         }
1909     }
1910 }
1911 
1912 static void tester(Ordering ordering, bool supportGC, alias less)()
1913 {
1914     import std.functional : binaryFun;
1915     import std.range : iota, chain, repeat, only, ElementType;
1916     import std.algorithm : filter, map;
1917     import std.algorithm.sorting : isSorted, sort;
1918     import std.exception : assertThrown, assertNotThrown;
1919     import std.traits : isInstanceOf;
1920     import core.internal.traits : Unqual;
1921 
1922     enum assignment = Assignment.copy;
1923     alias comp = binaryFun!less; //< comparison
1924 
1925     alias E = int;
1926 
1927     {
1928         alias A = SortedUniqueArray!E;
1929         auto x = A.withElements(0, 3, 2, 1);
1930         assert(x[].equal([0, 1, 2, 3].s[]));
1931     }
1932 
1933     foreach (Ch; AliasSeq!(char, wchar, dchar))
1934     {
1935         static if (!isOrdered!ordering || // either not ordered
1936                    is(Ch == dchar))       // or not a not a narrow string
1937         {
1938             alias Str = Array!(Ch, assignment, ordering, supportGC, size_t, less);
1939             auto y = Str.withElements('a', 'b', 'c');
1940             static assert(is(Unqual!(ElementType!Str) == Ch));
1941             y = Str.init;
1942 
1943             const(Ch)[] xs;
1944             {
1945                 // immutable
1946                 immutable x = Str.withElements('a', 'b', 'c');
1947                 static if (!isOrdered!ordering)
1948                 {
1949                     xs = x[];       // TODO should fail with DIP-1000 scope
1950                 }
1951             }
1952         }
1953     }
1954 
1955     foreach (Ch; AliasSeq!(char))
1956     {
1957         static if (!isOrdered!ordering)
1958         {
1959             alias Str = Array!(Ch, assignment, ordering, supportGC, size_t, less);
1960             auto str = Str.withElements('a', 'b', 'c');
1961 
1962             static if (isOrdered!ordering)
1963             {
1964                 static assert(is(Unqual!(ElementType!Str) == Ch));
1965             }
1966             else
1967             {
1968                 static assert(is(ElementType!Str == Ch));
1969                 assert(str[] == `abc`); // TODO this fails for wchar and dchar
1970             }
1971         }
1972     }
1973 
1974     {
1975         alias A = Array!(int, assignment, ordering, supportGC, size_t, less);
1976         foreach (immutable n; [0, 1, 2, 3, 4, 5])
1977         {
1978             assert(A.withLength(n).isSmall);
1979         }
1980         assert(!(A.withLength(6).isSmall));
1981         assert((A.withLength(6).isLarge));
1982     }
1983 
1984     // test move construction
1985     {
1986         immutable maxLength = 1024;
1987         foreach (immutable n; 0 .. maxLength)
1988         {
1989             auto x = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(n);
1990 
1991             // test resize
1992             static if (!isOrdered!ordering)
1993             {
1994                 assert(x.length == n);
1995                 x.length = n + 1;
1996                 assert(x.length == n + 1);
1997                 x.length = n;
1998             }
1999 
2000             const ptr = x.ptr;
2001             immutable capacity = x.capacity;
2002             assert(x.length == n);
2003 
2004             import std.algorithm.mutation : move;
2005             auto y = Array!(E, assignment, ordering, supportGC, size_t, less)();
2006             move(x, y);
2007 
2008             assert(x.length == 0);
2009             assert(x.capacity == x.smallCapacity);
2010 
2011             assert(y.length == n);
2012             assert(y.capacity == capacity);
2013         }
2014     }
2015 
2016     foreach (immutable n; chain(0.only, iota(0, 10).map!(x => 2^^x)))
2017     {
2018         import std.array : array;
2019         import std.range : radial;
2020 
2021         immutable zi = cast(int)0; // zero index
2022         immutable ni = cast(int)n; // number index
2023 
2024         auto fw = iota(zi, ni); // 0, 1, 2, ..., n-1
2025 
2026         auto bw = fw.array.radial;
2027 
2028         Array!(E, assignment, ordering, supportGC, size_t, less) ss0 = bw; // reversed
2029         static assert(is(Unqual!(ElementType!(typeof(ss0))) == E));
2030         static assert(isInstanceOf!(Array, typeof(ss0)));
2031         assert(ss0.length == n);
2032 
2033         static if (isOrdered!ordering)
2034         {
2035             if (!ss0.empty) { assert(ss0[0] == ss0[0]); } // trigger use of opindex
2036             assert(ss0[].equal(fw.array
2037                                  .sort!comp));
2038             assert(ss0[].isSorted!comp);
2039         }
2040 
2041         Array!(E, assignment, ordering, supportGC, size_t, less) ss1 = fw; // ordinary
2042         assert(ss1.length == n);
2043 
2044         static if (isOrdered!ordering)
2045         {
2046             assert(ss1[].equal(fw.array
2047                                  .sort!comp));
2048             assert(ss1[].isSorted!comp);
2049         }
2050 
2051         Array!(E, assignment, ordering, supportGC, size_t, less) ss2 = fw.filter!(x => x & 1);
2052         assert(ss2.length == n/2);
2053 
2054         static if (isOrdered!ordering)
2055         {
2056             assert(ss2[].equal(fw.filter!(x => x & 1)
2057                                  .array
2058                                  .sort!comp));
2059             assert(ss2[].isSorted!comp);
2060         }
2061 
2062         auto ssA = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(0);
2063         static if (isOrdered!ordering)
2064         {
2065             static if (less == "a < b")
2066             {
2067                 alias A = Array!(E, assignment, ordering, supportGC, size_t, less);
2068                 const A x = [1, 2, 3, 4, 5, 6];
2069                 assert(x.front == 1);
2070                 assert(x.back == 6);
2071                 assert(x.lowerBound(3).equal([1, 2].s[]));
2072                 assert(x.upperBound(3).equal([4, 5, 6].s[]));
2073                 assert(x[].equal(x[])); // already sorted
2074             }
2075 
2076             foreach (i; bw)
2077             {
2078                 static if (ordering == Ordering.sortedUniqueSet)
2079                 {
2080                     assert(ssA.insertMany(i)[].equal([true].s[]));
2081                     assert(ssA.insertMany(i)[].equal([false].s[]));
2082                 }
2083                 else
2084                 {
2085                     ssA.insertMany(i);
2086                 }
2087             }
2088             assert(ssA[].equal(sort!comp(fw.array)));
2089 
2090             auto ssB = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(0);
2091             static if (ordering == Ordering.sortedUniqueSet)
2092             {
2093                 assert(ssB.insertMany(1, 7, 4, 9)[].equal(true.repeat(4)));
2094                 assert(ssB.insertMany(3, 6, 8, 5, 1, 9)[].equal([true, true, true, true, false, false].s[]));
2095                 assert(ssB.insertMany(3, 0, 2, 10, 11, 5)[].equal([false, true, true, true, true, false].s[]));
2096                 assert(ssB.insertMany(0, 2, 10, 11)[].equal(false.repeat(4))); // false becuse already inserted
2097                 assert(ssB.capacity == 16);
2098             }
2099             else
2100             {
2101                 ssB.insertMany(1, 7, 4, 9);
2102                 ssB.insertMany(3, 6, 8, 5);
2103                 ssB.insertMany(0, 2, 10, 11);
2104                 assert(ssB.capacity == 16);
2105             }
2106 
2107             auto ssI = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].sort!comp; // values
2108             immutable ssO = [12, 13]; // values not range
2109 
2110             assert(ssB[].equal(ssI));
2111 
2112             foreach (s; ssI) { assert(ssB.contains(s)); }
2113             foreach (s; ssO) { assert(!ssB.contains(s)); }
2114 
2115             ssB.compress();
2116             assert(ssB.capacity == 12);
2117         }
2118         else
2119         {
2120             {
2121                 alias A = Array!(E, assignment, ordering, supportGC);
2122                 A x = [1, 2, 3];
2123                 x ~= x;
2124                 assert(x[].equal([1, 2, 3,
2125                                   1, 2, 3].s[]));
2126                 x ~= x[];
2127                 assert(x[].equal([1, 2, 3, 1, 2, 3,
2128                                   1, 2, 3, 1, 2, 3].s[]));
2129             }
2130 
2131             ssA ~= 3;
2132             ssA ~= 2;
2133             ssA ~= 1;
2134             assert(ssA[].equal([3, 2, 1].s[]));
2135 
2136             ssA.compress();
2137 
2138             // popBack
2139             ssA[0] = 1;
2140             ssA[1] = 2;
2141             assert(ssA[].equal([1, 2, 1].s[]));
2142             assert(!ssA.empty);
2143             assert(ssA.front == 1);
2144             assert(ssA.back == 1);
2145 
2146             assertNotThrown(ssA.popBack());
2147             assert(ssA[].equal([1, 2].s[]));
2148             assert(!ssA.empty);
2149             assert(ssA.front == 1);
2150             assert(ssA.back == 2);
2151 
2152             assertNotThrown(ssA.popBack());
2153             assert(ssA[].equal([1].s[]));
2154             assert(!ssA.empty);
2155             assert(ssA.front == 1);
2156             assert(ssA.back == 1);
2157 
2158             assertNotThrown(ssA.popBack());
2159             assert(ssA.length == 0);
2160             assert(ssA.empty);
2161             assert(ssA.capacity != 0);
2162 
2163             ssA.compress();
2164             assert(ssA.length == 0);
2165             assert(ssA.empty);
2166 
2167             // insertAt
2168             ssA ~= 1;
2169             ssA ~= 2;
2170             ssA ~= 3;
2171             ssA ~= 4;
2172             ssA ~= 5;
2173             ssA ~= 6;
2174             ssA ~= 7;
2175             ssA ~= 8;
2176             assert(ssA[].equal([1, 2, 3, 4, 5, 6, 7, 8].s[]));
2177             ssA.insertAtIndex(3, 100, 101);
2178             assert(ssA[].equal([1, 2, 3, 100, 101, 4, 5, 6, 7, 8].s[]));
2179             assertNotThrown(ssA.popFront());
2180             assert(ssA[].equal([2, 3, 100, 101, 4, 5, 6, 7, 8].s[]));
2181             assertNotThrown(ssA.popFront());
2182             assert(ssA[].equal([3, 100, 101, 4, 5, 6, 7, 8].s[]));
2183             assertNotThrown(ssA.popFront());
2184             assert(ssA[].equal([100, 101, 4, 5, 6, 7, 8].s[]));
2185             assertNotThrown(ssA.popFront());
2186             assertNotThrown(ssA.popFront());
2187             assertNotThrown(ssA.popFront());
2188             assertNotThrown(ssA.popFront());
2189             assertNotThrown(ssA.popFront());
2190             assertNotThrown(ssA.popFront());
2191             assertNotThrown(ssA.popFront());
2192             assert(ssA.empty);
2193             ssA.compress();
2194 
2195             // removeAt
2196             ssA ~= 1;
2197             ssA ~= 2;
2198             ssA ~= 3;
2199             ssA ~= 4;
2200             ssA ~= 5;
2201             assertNotThrown(ssA.removeAt(2));
2202             assert(ssA[].equal([1, 2, 4, 5].s[]));
2203 
2204             // insertBack and assignment from slice
2205             auto ssB = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(0);
2206             ssB.insertBack([1, 2, 3, 4, 5].s[]);
2207             ssB.insertBack([6, 7]);
2208             assert(ssB[].equal([1, 2, 3, 4, 5, 6, 7].s[]));
2209             assert(ssB.backPop() == 7);
2210             assert(ssB.backPop() == 6);
2211             assert(ssB.backPop() == 5);
2212             assert(ssB.backPop() == 4);
2213             assert(ssB.backPop() == 3);
2214             assert(ssB.backPop() == 2);
2215             assert(ssB.backPop() == 1);
2216             assert(ssB.empty);
2217 
2218             // insertBack(Array)
2219             {
2220                 immutable s = [1, 2, 3];
2221                 Array!(E, assignment, ordering, supportGC, size_t, less) s1 = s;
2222                 Array!(E, assignment, ordering, supportGC, size_t, less) s2 = s1[];
2223                 assert(s1[].equal(s));
2224                 s1 ~= s1;
2225                 assert(s1[].equal(chain(s, s)));
2226                 s1 ~= s2;
2227                 assert(s1[].equal(chain(s, s, s)));
2228             }
2229 
2230             // immutable ss_ = Array!(E, assignment, ordering, supportGC, size_t, less)(null);
2231             // assert(ss_.empty);
2232 
2233             auto ssC = Array!(E, assignment, ordering, supportGC, size_t, less).withLength(0);
2234             immutable int[5] i5_ = [1, 2, 3, 4, 5];
2235             immutable(int)[] i5 = i5_[];
2236             ssC.insertBack(i5);
2237             assert(i5 == [1, 2, 3, 4, 5].s[]);
2238             assert(ssC[].equal(i5));
2239 
2240             auto ssCc = ssC;    // copy it
2241             assert(ssCc[].equal(i5));
2242 
2243             ssC.shrinkTo(4);
2244             assert(ssC[].equal([1, 2, 3, 4].s[]));
2245 
2246             ssC.shrinkTo(3);
2247             assert(ssC[].equal([1, 2, 3].s[]));
2248 
2249             ssC.shrinkTo(2);
2250             assert(ssC[].equal([1, 2].s[]));
2251 
2252             ssC.shrinkTo(1);
2253             assert(ssC[].equal([1].s[]));
2254 
2255             ssC.shrinkTo(0);
2256             assert(ssC[].length == 0);
2257             assert(ssC.empty);
2258 
2259             ssC.insertBack(i5);
2260             ssC.popBackN(3);
2261             assert(ssC[].equal([1, 2].s[]));
2262 
2263             auto ssD = ssC;
2264             ssC.clear();
2265             assert(ssC.empty);
2266 
2267             assert(!ssD.empty);
2268             ssD = null;
2269             assert(ssD.empty);
2270             assert(ssD == typeof(ssD).init);
2271 
2272             assert(ssCc[].equal(i5));
2273 
2274             ssCc = ssCc;   // self assignment
2275         }
2276     }
2277 }
2278 
2279 /// disabled copying
2280 @safe pure nothrow @nogc unittest
2281 {
2282     alias E = ubyte;
2283     alias A = Array!(E, Assignment.disabled, Ordering.unsorted, false, size_t, "a < b");
2284     A a;
2285     immutable n = ubyte.max;
2286     size_t i = 0;
2287     foreach (ubyte e; 0 .. n)
2288     {
2289         a ~= e;
2290         // assert(a.back == e.to!E);
2291         assert(a.length == i + 1);
2292         ++i;
2293     }
2294     const b = a.dup;
2295     static assert(is(typeof(a) == Unqual!(typeof(b))));
2296     assert(b.length == a.length);
2297     assert(a !is b);
2298     assert(a == b);
2299 }
2300 
2301 /// disabled copying
2302 @safe pure nothrow unittest
2303 {
2304     alias E = string;
2305 
2306     alias A = Array!(E, Assignment.disabled, Ordering.unsorted, false, size_t, "a < b");
2307     A a;
2308     immutable n = 100_000;
2309     size_t i = 0;
2310     foreach (const ref e; 0 .. n)
2311     {
2312         a ~= e.to!E;
2313         // assert(a.back == e.to!E);
2314         assert(a.length == i + 1);
2315         ++i;
2316     }
2317     const b = a.dup;
2318     static assert(is(typeof(a) == Unqual!(typeof(b))));
2319     assert(b.length == a.length);
2320     assert(a !is b);
2321     assert(a == b);
2322 }
2323 
2324 /// disabled copying
2325 @safe pure nothrow unittest
2326 {
2327     alias E = string;
2328     alias A = Array!(E, Assignment.disabled, Ordering.unsorted, false, size_t, "a < b");
2329     A a;
2330     immutable n = 100_000;
2331     size_t i = 0;
2332     foreach (const ref e; 0 .. n)
2333     {
2334         a ~= e.to!E;
2335         assert(a.length == i + 1);
2336         ++i;
2337     }
2338     const b = a.dup;
2339     static assert(is(typeof(a) == Unqual!(typeof(b))));
2340     assert(a[] == b[]);
2341 }
2342 
2343 /// disabled copying
2344 @safe pure nothrow @nogc unittest
2345 {
2346     import std.traits : isAssignable;
2347 
2348     alias E = string;
2349 
2350     alias A = Array!E;
2351     static assert(!__traits(isCopyable, A));
2352 
2353     alias CA = CopyingArray!E;
2354     static assert(__traits(isCopyable, CA));
2355 
2356     // import std.traits : isRvalueAssignable, isLvalueAssignable;
2357     // static assert(isRvalueAssignable!(A));
2358     // static assert(isLvalueAssignable!(A));
2359 
2360     static assert(isAssignable!(A));
2361 
2362     // import std.range.primitives : hasSlicing;
2363     // TODO make this evaluate to `true`
2364     // static assert(hasSlicing!A);
2365 
2366     alias AA = Array!A;
2367 
2368     AA aa;
2369     A a;
2370     a ~= "string";
2371     aa ~= A.init;
2372 
2373     assert(aa == aa);
2374     assert(AA.withLength(3) == AA.withLength(3));
2375     assert(AA.withCapacity(3) == AA.withCapacity(3));
2376     assert(AA.withLength(3).length == 3);
2377     assert(aa != AA.init);
2378 }
2379 
2380 ///
2381 @safe pure nothrow @nogc unittest
2382 {
2383     alias E = int;
2384     alias A = Array!E;
2385     A a;
2386     import std.range : iota;
2387     import std.container.util : make;
2388     foreach (n; 0 .. 100)
2389     {
2390         const e = iota(0, n).make!Array;
2391         assert(e[].equal(iota(0, n)));
2392     }
2393 }
2394 
2395 version(unittest)
2396 {
2397     import std.traits : EnumMembers;
2398 }
2399 
2400 /// use GC
2401 pure nothrow unittest
2402 {
2403     foreach (ordering; EnumMembers!Ordering)
2404     {
2405         tester!(ordering, true, "a < b"); // use GC
2406         tester!(ordering, true, "a > b"); // use GC
2407     }
2408 }
2409 
2410 /// don't use GC
2411 pure nothrow /+TODO @nogc+/ unittest
2412 {
2413     foreach (ordering; EnumMembers!Ordering)
2414     {
2415         tester!(ordering, false, "a < b"); // don't use GC
2416         tester!(ordering, false, "a > b"); // don't use GC
2417     }
2418 }
2419 
2420 @safe pure nothrow unittest
2421 {
2422     alias E = int;
2423     alias A = Array!E;
2424     A[string] map;
2425     map["a"] = A.init;
2426     map["B"] = A.withLength(42);
2427 
2428     auto aPtr = "a" in map;
2429     assert(aPtr);
2430     assert(A.init == *aPtr);
2431     assert(*aPtr == A.init);
2432 
2433     assert("z" !in map);
2434     auto zPtr = "z" in map;
2435     assert(!zPtr);
2436 }
2437 
2438 /// test withElement and withElements
2439 @safe pure nothrow @nogc unittest
2440 {
2441     import std.algorithm.mutation : move;
2442     import std.range.primitives : ElementType;
2443 
2444     alias A = Array!int;
2445     alias AA = Array!A;
2446     alias AAA = Array!AA;
2447 
2448     foreach (A_; AliasSeq!(A, AA, AAA))
2449     {
2450         alias E = ElementType!A_;
2451         A_ x = A_.withElement(E.init);
2452         A_ y = A_.withElements(E.init, E.init);
2453         assert(x.length == 1);
2454         assert(y.length == 2);
2455         immutable n = 100;
2456         foreach (_; 0 .. n)
2457         {
2458             auto e = E.init;
2459             x ~= move(e);
2460             y ~= E.init;
2461         }
2462         foreach (_; 0 .. n)
2463         {
2464             assert(x.backPop() == E.init);
2465             assert(y.backPop() == E.init);
2466         }
2467         assert(x.length == 1);
2468         assert(y.length == 2);
2469 
2470         import std.algorithm : swap;
2471         swap(x, y);
2472         assert(x.length == 2);
2473         assert(y.length == 1);
2474 
2475         swap(x[0], y[0]);
2476     }
2477 
2478 }
2479 
2480 /// assert same behaviour of `dup` as for builtin arrays
2481 @safe pure nothrow unittest
2482 {
2483     struct Vec { int x, y; }
2484     class Db { int* _ptr; }
2485     struct Node { int x; class Db; }
2486     // struct Node1 { const(int) x; class Db; }
2487     foreach (E; AliasSeq!(int, const(int), Vec, Node// , Node1
2488                  ))
2489     {
2490         alias DA = E[];         // builtin D array/slice
2491         immutable DA da = [E.init]; // construct from array
2492         auto daCopy = da.dup;   // duplicate
2493         daCopy[] = E.init;   // opSliceAssign
2494 
2495         alias CA = Array!E;         // container array
2496         immutable ca = CA.withElement(E.init);
2497 
2498         auto caCopy = ca.dup;
2499 
2500         import std.traits : hasIndirections;
2501         static if (!hasIndirections!E)
2502         {
2503             const(E)[2] x = [E.init, E.init];
2504             // TODO caCopy ~= E.init;
2505             caCopy ~= x[];
2506             assert(caCopy.length == 3);
2507             assert(caCopy[1 .. $] == x[]);
2508         }
2509 
2510         // should have same element type
2511         static assert(is(typeof(caCopy[0]) ==
2512                          typeof(daCopy[0])));
2513 
2514     }
2515 }
2516 
2517 /// array as AA key type
2518 @safe pure nothrow unittest
2519 {
2520     struct E { int x, y; }
2521     foreach (A; AliasSeq!(Array!E,
2522                           CopyingArray!E))
2523     {
2524         int[A] x;
2525         immutable n = 100;
2526         foreach (immutable i; 0 .. n)
2527         {
2528             assert(x.length == i);
2529             assert(A.withElement(E(i, 2*i)) !in x);
2530             x[A.withElement(E(i, 2*i))] = 42;
2531             assert(x.length == i + 1);
2532             auto a = A.withElement(E(i, 2*i));
2533             import std.traits : isCopyable;
2534             static if (__traits(isCopyable, A))
2535             {
2536                 // TODO why do these fail when `A` is uncopyable?
2537                 assert(a in x);
2538                 assert(A.withElement(E(i, 2*i)) in x);
2539                 assert(x[A.withElement(E(i, 2*i))] == 42);
2540             }
2541         }
2542     }
2543 }
2544 
2545 /// init and append to empty array as AA value type
2546 @safe pure nothrow unittest
2547 {
2548     alias Key = string;
2549     alias A = Array!int;
2550 
2551     A[Key] x;
2552 
2553     assert("a" !in x);
2554 
2555     x["a"] = A.init;            // if this init is removed..
2556     x["a"] ~= 42;               // ..then this fails
2557 
2558     assert(x["a"] == A.withElement(42));
2559 }
2560 
2561 /// compress
2562 @safe pure nothrow @nogc unittest
2563 {
2564     alias A = Array!string;
2565     A a;
2566 
2567     a.compress();
2568 
2569     a ~= "a";
2570     a ~= "b";
2571     a ~= "c";
2572 
2573     assert(a.length == 3);
2574     assert(a.capacity == 4);
2575 
2576     a.compress();
2577 
2578     assert(a.capacity == a.length);
2579 }
2580 
2581 ///
2582 @safe pure nothrow @nogc unittest
2583 {
2584     alias Key = UniqueArray!char;
2585     alias Value = UniqueArray!int;
2586     struct E
2587     {
2588         Key key;
2589         Value value;
2590         E dup() @safe pure nothrow @nogc
2591         {
2592             return E(key.dup, value.dup);
2593         }
2594     }
2595     E e;
2596     e.key = Key.withElement('a');
2597     e.value = Value.withElement(42);
2598 
2599     auto f = e.dup;
2600     assert(e == f);
2601 
2602     e.key = Key.withElement('b');
2603     assert(e != f);
2604 
2605     e.key = Key.withElement('a');
2606     assert(e == f);
2607 
2608     e.value = Value.withElement(43);
2609     assert(e != f);
2610 
2611     e.value = Value.withElement(42);
2612     assert(e == f);
2613 
2614 }
2615 
2616 /// append to empty to array as AA value type
2617 @safe pure nothrow @nogc unittest
2618 {
2619     import std.exception: assertThrown;
2620     import core.exception : RangeError;
2621 
2622     alias Key = string;
2623     alias A = Array!int;
2624 
2625     A[Key] x;
2626     // assertThrown!RangeError({ x["a"] ~= 42; }); // TODO make this work
2627 }
2628 
2629 /// map array of uncopyable
2630 @safe pure nothrow unittest
2631 {
2632     import std.range.primitives : isInputRange;
2633     import std.array : array;
2634 
2635     alias A = UniqueArray!int;
2636     auto y = A.init[].map!(_ => _^^2).array;
2637 
2638     A z = y.dup;                // check that dup returns same type
2639     z = A.init;
2640     const w = [0, 1].s;
2641     z.insertBack(w[]);
2642     assert(z[].equal(w[]));
2643 }
2644 
2645 ///
2646 version(none)
2647 @safe pure nothrow @nogc unittest
2648 {
2649     alias A = UniqueArray!int;
2650     A x;
2651     const y = [0, 1, 2, 3].s;
2652 
2653     x.insertBack(y[]);
2654     assert(x[].equal(y[]));
2655 
2656     x.clear();
2657     x.insertBack(y[].map!(_ => _^^2)); // rhs has length (`hasLength`)
2658     assert(x[].equal(y[].map!(_ => _^^2)));
2659 
2660     x.clear();
2661     x.insertBack(y[].filter!(_ => _ & 1)); // rhs has no length (`!hasLength`)
2662     assert(x[].equal(y[].filter!(_ => _ & 1)));
2663 }
2664 
2665 /// collection
2666 /*@safe*/ pure nothrow @nogc unittest // TODO make @safe when collect has been made safe
2667 {
2668     import std.range : iota, isOutputRange;
2669     import nxt.algorithm_ex : collect;
2670 
2671     alias E = int;
2672     alias A = Array!E;
2673 
2674     immutable n = 100;
2675     static assert(isOutputRange!(A, E));
2676 
2677     assert((0.iota(n).collect!A)[].equal(0.iota(n)));
2678 }
2679 
2680 /// map array of uncopyable
2681 @safe pure nothrow @nogc unittest
2682 {
2683     foreach (AT; AliasSeq!(SortedUniqueArray,
2684                            SortedSetUniqueArray))
2685     {
2686         alias A = AT!int;
2687         A a;
2688         A b = a.dup;
2689     }
2690 }
2691 
2692 /// init and append to empty array as AA value type
2693 @safe pure nothrow @nogc unittest
2694 {
2695     alias A = Array!int;
2696 
2697     const x = A.withElements(0, 1, 3, 0, 2, 1, 3);
2698 
2699     assert(x.toSortedArray == [0, 0, 1, 1, 2, 3, 3].s[]);
2700     assert(x.toSortedSetArray == [0, 1, 2, 3].s[]);
2701 
2702     assert(x.toSortedArray!"a > b" == [3, 3, 2, 1, 1, 0, 0].s[]);
2703     assert(x.toSortedSetArray!"a > b" == [3, 2, 1, 0].s[]);
2704 }
2705 
2706 /** Return `data` appended with arguments `args`.
2707 
2708     If `data` is an r-value it's modified and returned, otherwise a copy is made
2709     and returned.
2710  */
2711 C append(C, Args...)(auto ref C data,
2712                      auto ref Args args)
2713 if (args.length >= 1)    // TODO trait: when `C` is a container supporting `insertBack`
2714 {
2715     static if (__traits(isRef, data)) // `data` is an r-value
2716     {
2717         C mutableData = data.dup;
2718     }
2719     else                        // `data` is an l-value
2720     {
2721         alias mutableData = data;
2722     }
2723     // TODO use `mutableData.insertBack(args);` instead
2724     foreach (ref arg; args)
2725     {
2726         mutableData.insertBack(arg);
2727     }
2728     import std.algorithm.mutation : move;
2729     return move(mutableData);
2730 }
2731 
2732 /// append
2733 @safe pure nothrow @nogc unittest
2734 {
2735     alias Str = UniqueString!false;
2736 
2737     assert(Str(`a`).append('b', 'c')[] == `abc`);
2738     assert(Str(`a`).append(`b`, `c`)[] == `abc`);
2739 
2740     const Str x = Str(`a`).append('b', 'c'); // is moved
2741     assert(x[] == `abc`);
2742 
2743     Str y = `x`;
2744     Str z = y.append('y', 'z', `w`); // needs dup
2745     assert(y.ptr != z.ptr);
2746     assert(z[] == `xyzw`);
2747 }
2748 
2749 version(unittest)
2750 {
2751     private static struct SS
2752     {
2753         @disable this(this);
2754         int x;
2755     }
2756 }
2757 
2758 /// uncopyable elements
2759 @safe pure nothrow @nogc unittest
2760 {
2761     alias A = UniqueArray!SS;
2762     A x;
2763     x ~= SS.init;
2764     // TODO x.insertBack(A.init);
2765 }
2766 
2767 // TODO implement?
2768 // T opBinary(string op, R, Args...)(R lhs,
2769 //                                   auto ref Args args)
2770 // {
2771 //     return append(lhs, rhs);
2772 // }
2773 // @safe pure nothrow @nogc unittest
2774 // {
2775 //     alias S = UniqueString!false;
2776 //     // TODO
2777 //     // const S x = S(`a`) ~ 'b';
2778 //     // assert(x[] == `abc`);
2779 // }
2780 
2781 /// See_Also: http://forum.dlang.org/post/omfm56$28nu$1@digitalmars.com
2782 version(none)
2783 @safe pure nothrow unittest
2784 {
2785     import std.range.primitives : ElementType;
2786     import std.array : Appender;
2787 
2788     struct S
2789     {
2790         string src;
2791         S[] subs;
2792     }
2793 
2794     struct T
2795     {
2796         string src;
2797         Appender!(T[]) subs;
2798     }
2799 
2800     static assert(is(ElementType!(S[]) == S));
2801     static assert(is(ElementType!(T[]) == void)); // TODO forward-reference bug: should be `T`
2802 
2803     S s;
2804     s.subs ~= S.init;
2805 
2806     T t;
2807     // t.subs ~= T.init;
2808     // t.subs.put(T.init);
2809 
2810     // struct U
2811     // {
2812     //     string src;
2813     //     UniqueArray!U subs;
2814     // }
2815     // U u;
2816 }
2817 
2818 /// class element
2819 @safe pure nothrow unittest
2820 {
2821     class Zing
2822     {
2823         void* raw;
2824     }
2825     class Edge : Zing
2826     {
2827         Zing[] actors;
2828     }
2829 
2830     foreach (AT; AliasSeq!(UniqueArray,
2831                            CopyingArray,
2832                            SortedCopyingArray,
2833                            SortedSetCopyingArray,
2834                            SortedUniqueArray,
2835                            SortedSetUniqueArray))
2836     {
2837         alias A = AT!int;
2838         A a;
2839     }
2840 }