1 module nxt.dynamic_array;
2 
3 // version = debugCtors;
4 
5 import core.internal.traits : Unqual;
6 
7 @safe:
8 
9 /** Array type with deterministic control of memory. The memory allocated for
10  * the array is reclaimed as soon as possible; there is no reliance on the
11  * garbage collector. Array uses malloc, realloc and free for managing its own
12  * memory.
13  *
14  * Use `std.bitmanip.BitArray` for array container storing boolean values.
15  *
16  * TODO optimize by making members templates. 0.579s before, eval-dwim: 0.67s
17  *
18  * TODO Add OutputRange.writer support as
19  * https://github.com/burner/StringBuffer/blob/master/source/stringbuffer.d#L45
20  *
21  * TODO Use `std.traits.areCopyCompatibleArrays`
22  *
23  * See_Also: https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md
24  */
25 struct DynamicArray(T,
26                     alias Allocator = null, // null means means to qcmeman functions. TODO use `PureMallocator` by default
27                     CapacityType = size_t)  // see also https://github.com/izabera/s
28 if (!is(Unqual!T == bool) &&             // use `BitArray` instead
29     (is(CapacityType == ulong) ||        // 3 64-bit words
30      is(CapacityType == uint)))          // 2 64-bit words
31 {
32 @safe:
33 
34     // import core.exception : onOutOfMemoryError;
35     import core.internal.traits : hasElaborateDestructor;
36 
37     import std.range.primitives : isInputRange, ElementType, hasLength, hasSlicing, isInfinite;
38     import std.traits : hasIndirections, hasAliasing,
39         isMutable, TemplateOf, isArray, isAssignable, isType, hasFunctionAttributes, isIterable;
40     import core.lifetime : emplace, move, moveEmplace;
41 
42     import nxt.qcmeman : malloc, calloc, realloc, free, gc_addRange, gc_removeRange;
43     import nxt.container_traits : mustAddGCRange, needsMove;
44 
45     /// Mutable element type.
46     private alias MutableE = Unqual!T;
47 
48     /// Is `true` if `U` can be assign to the element type `T` of `this`.
49     enum isElementAssignable(U) = isAssignable!(MutableE, U);
50 
51 pragma(inline):
52 
53     /// Returns: an array of length `initialLength` with all elements default-initialized to `ElementType.init`.
54     static typeof(this) withLength()(size_t initialLength) // template-lazy
55     {
56         version(D_Coverage) {} else pragma(inline, true);
57         return withCapacityLengthZero(initialLength, initialLength, true);
58     }
59 
60     /// Returns: an array with initial capacity `initialCapacity`.
61     static typeof(this) withCapacity()(size_t initialCapacity) // template-lazy
62     {
63         version(D_Coverage) {} else pragma(inline, true);
64         return withCapacityLengthZero(initialCapacity, 0, false);
65     }
66 
67     static if (__traits(isCopyable, T))
68     {
69         /** Construct using
70          * - initial length `length`,
71          * - and value of all elements `elementValue`.
72          */
73         static typeof(this) withLengthElementValue()(size_t length,
74                                                      T elementValue)
75         {
76             version(D_Coverage) {} else pragma(inline, true);
77             assert(length <= CapacityType.max);
78             return typeof(return)(Store(typeof(this).allocateWithValue(length, move(elementValue)),
79                                         cast(CapacityType)length,
80                                         cast(CapacityType)length));
81         }
82     }
83 
84     /** Construct using
85      * - initial capacity `capacity`,
86      * - initial length `length`,
87      * - and zeroing-flag `zero`.
88      */
89     private static typeof(this) withCapacityLengthZero()(size_t capacity, // template-lazy
90                                                          size_t length,
91                                                          bool zero) @trusted
92     {
93         version(LDC) pragma(inline, true);
94         assert(capacity >= length);
95         assert(capacity <= CapacityType.max);
96         return typeof(return)(Store(typeof(this).allocate(capacity, zero),
97                                     cast(CapacityType)capacity,
98                                     cast(CapacityType)length));
99     }
100 
101     /** Emplace `thatPtr` with elements moved from `elements`. */
102     static ref typeof(this) emplaceWithMovedElements()(typeof(this)* thatPtr, // template-lazy
103                                                        T[] elements) @system
104     {
105         immutable length = elements.length;
106         thatPtr._store.ptr = typeof(this).allocate(length, false);
107         thatPtr._store.capacity = cast(CapacityType)length;
108         thatPtr._store.length = cast(CapacityType)length;
109         foreach (immutable i, ref e; elements[])
110             moveEmplace(e, thatPtr._mptr[i]);
111         return *thatPtr;
112     }
113 
114     /** Emplace `thatPtr` with elements copied from `elements`. */
115     static ref typeof(this) emplaceWithCopiedElements()(typeof(this)* thatPtr, // template-lazy
116                                                         const(T)[] elements) @system
117     if (__traits(isCopyable, T))
118     {
119         immutable length = elements.length;
120         thatPtr._store.ptr = typeof(this).allocate(length, false);
121         thatPtr._store.capacity = cast(CapacityType)length;
122         thatPtr._store.length = cast(CapacityType)length;
123         foreach (immutable i, ref e; elements[])
124             thatPtr._mptr[i] = cast(T)e; // TODO restrict this function using a
125                                          // T-trait where this cast can be @trusted
126         return *thatPtr;
127     }
128 
129     private this(Store store)
130     {
131         version(debugCtors) pragma(msg, __FILE_FULL_PATH__, ":", __LINE__, ": info: ", typeof(store));
132         _store = store;
133     }
134 
135     /// Construct from uncopyable element `value`.
136     this()(T value) @trusted    // template-lazy
137     if (!__traits(isCopyable, T))
138     {
139         version(debugCtors) pragma(msg, __FILE_FULL_PATH__, ":", __LINE__, ": info: ", typeof(value));
140         _store.ptr = typeof(this).allocate(1, false);
141         _store.capacity = 1;
142         _store.length = 1;
143         moveEmplace(value, _mptr[0]); // TODO remove `moveEmplace` when compiler does it for us
144     }
145 
146     /// Construct from copyable element `value`.
147     this(U)(U value) @trusted
148     if (__traits(isCopyable, U) &&
149         isElementAssignable!U)
150     {
151         version(debugCtors) pragma(msg, __FILE_FULL_PATH__, ":", __LINE__, ": info: ", typeof(value));
152         _store.ptr = typeof(this).allocate(1, false);
153         _store.capacity = 1;
154         _store.length = 1;
155         emplace(&_mptr[0], value);
156     }
157 
158     static if (__traits(isCopyable, T) &&
159                !is(T == union)) // forbid copying of unions such as `HybridBin` in hashmap.d
160     {
161         static typeof(this) withElements()(const T[] elements) @trusted // template-lazy
162         {
163             version(debugCtors) pragma(msg, __FILE_FULL_PATH__, ":", __LINE__, ": info: ", typeof(elements));
164             immutable length = elements.length;
165             auto ptr = typeof(this).allocate(length, false);
166 
167             foreach (immutable i, const element; elements[])
168                 // TODO: be more strict
169                 // static if (hasIndirections!T)
170                 // {
171                 //     ptr[i] = element;
172                 // }
173                 // else
174                 // {
175                 //     ptr[i] = *cast(MutableE*)&element;
176                 // }
177                 ptr[i] = *cast(MutableE*)&element;
178 
179             // ptr[0 .. length] = elements[];
180             return typeof(return)(Store(ptr,
181                                         cast(CapacityType)length,
182                                         cast(CapacityType)length));
183         }
184 
185         /// Returns: shallow duplicate of `this`.
186         @property DynamicArray!(Unqual!T, Allocator, CapacityType) dup()() const @trusted // template-lazy
187         {
188             version(D_Coverage) {} else pragma(inline, true);
189             return typeof(this).withElements(this[]);
190         }
191     }
192 
193     /// Construct from the element(s) of the dynamic array `values`.
194     this(U)(U[] values) @trusted
195     if (isElementAssignable!(U))
196     {
197         version(debugCtors) pragma(msg, __FILE_FULL_PATH__, ":", __LINE__, ": info: ", typeof(values));
198         // TODO use import emplace_all instead
199 
200         _store.ptr = allocate(values.length, false);
201         _store.capacity = values.length;
202 
203         foreach (index; 0 .. values.length)
204             static if (needsMove!(T))
205                 move(values[index], _mptr[index]);
206             else
207                 _mptr[index] = values[index];
208 
209         setLengthChecked(values.length);
210     }
211 
212     /// Construct from the `n` number of element(s) in the static array `values`.
213     this(uint n, U)(U[n] values) @trusted
214     if (isElementAssignable!(U))
215     {
216         version(debugCtors) pragma(msg, __FILE_FULL_PATH__, ":", __LINE__, ": info: ", typeof(values));
217         // TODO use import emplace_all instead
218 
219         _store.ptr = allocate(values.length, false);
220         _store.capacity = values.length;
221 
222         static foreach (index; 0 .. values.length)
223             static if (needsMove!(T))
224                 move(values[index], _mptr[index]);
225             else
226                 _mptr[index] = values[index];
227 
228         setLengthChecked(values.length);
229     }
230 
231     /// ditto
232     this(R)(scope R values) @trusted
233     if (// isRefIterable!R &&
234         isElementAssignable!(ElementType!R) &&
235         !isArray!R)
236     {
237         version(debugCtors) pragma(msg, __FILE_FULL_PATH__, ":", __LINE__, ": info: ", typeof(values));
238         static if (hasLength!R)
239         {
240             reserve(values.length);
241             size_t index = 0;
242             foreach (ref value; values)
243                 _mptr[index++] = value;
244             setLengthChecked(values.length);
245         }
246         else
247             foreach (ref value; values)
248                 insertBack1(value);
249     }
250 
251     /** Is `true` iff the iterable container `C` can be insert to `this`.
252      */
253     private enum isInsertableContainer(C) = (is(C == struct) && // exclude class ranges for aliasing control
254                                              isRefIterable!C && // elements may be non-copyable
255                                              !isInfinite!C &&
256                                              isElementAssignable!(ElementType!C));
257 
258     /// Construct from the elements `values`.
259     static typeof(this) withElementsOfRange_untested(R)(R values) @trusted
260     if (isInsertableContainer!R)
261     {
262         typeof(this) result;
263 
264         static if (hasLength!R)
265             result.reserve(values.length);
266 
267         static if (hasLength!R &&
268                    hasSlicing!R &&
269                    !needsMove!(ElementType!R))
270         {
271             import std.algorithm.mutation : copy;
272             copy(values[0 .. values.length],
273                  result._mptr[0 .. values.length]); // TODO better to use foreach instead?
274             result.setLengthChecked(values.length);
275         }
276         else
277         {
278             static if (hasLength!R)
279             {
280                 size_t i = 0;
281                 foreach (ref value; move(values)) // TODO remove `move` when compiler does it for us
282                     static if (needsMove!(typeof(value)))
283                         moveEmplace(value, result._mptr[i++]);
284                     else
285                         result._mptr[i++] = value;
286                 result.setLengthChecked(values.length);
287             }
288             else
289             {
290                 // import std.algorithm.mutation : moveEmplaceAll;
291                 /* TODO optimize with `moveEmplaceAll` that does a raw copy and
292                  * zeroing of values */
293                 foreach (ref value; move(values)) // TODO remove `move` when compiler does it for us
294                     static if (needsMove!(ElementType!R))
295                         result.insertBackMove(value); // steal element
296                     else
297                         result.insertBack1(value);
298             }
299         }
300         return result;
301     }
302 
303     /// No default copying.
304     @disable this(this);
305 
306     // TODO: this gives error in insertBack. why?
307     // void opAssign()(typeof(this) rhs) @trusted pure nothrow @nogc // template-lazy
308     // {
309     //     move(rhs, this);
310     // }
311 
312     /** Destruct.
313      *
314      * TODO what effect does have here?
315      * See_Also: https://github.com/atilaneves/automem/blob/master/source/automem/vector.d#L92
316      */
317     ~this() @nogc /*TODO scope*/
318     {
319         release();
320     }
321 
322     /// Empty.
323     void clear() @nogc
324     {
325         release();
326         resetInternalData();
327     }
328 
329     /// Release internal store.
330     private void release() @nogc
331     {
332         static if (hasElaborateDestructor!T)
333             destroyElements();
334         freeStore();
335     }
336 
337     /// Destroy elements.
338     static if (hasElaborateDestructor!T)
339     {
340         private void destroyElements() @trusted
341         {
342             foreach (immutable index; 0 .. _store.length)
343                 .destroy(_mptr[index]);
344         }
345     }
346 
347     /// Free internal store.
348     private void freeStore() @trusted
349     {
350         static if (mustAddGCRange!T)
351             gc_removeRange(_mptr);
352         free(_mptr);
353     }
354 
355     /// Reset internal data.
356     private void resetInternalData() @nogc
357     {
358         version(D_Coverage) {} else pragma(inline, true);
359         _store.ptr = null;
360         _store.capacity = 0;
361         _store.length = 0;
362     }
363 
364     /** Allocate heap region with `initialCapacity` number of elements of type `T`.
365      *
366      * If `zero` is `true` they will be zero-initialized.
367      */
368     private static MutableE* allocate(size_t initialCapacity, bool zero) @trusted
369     {
370         immutable size_t numBytes = initialCapacity * T.sizeof;
371 
372         typeof(return) ptr = null;
373         static if (!is(typeof(Allocator) == typeof(null)))
374         {
375             import std.experimental.allocator : makeArray;
376             if (zero)
377                 ptr = Allocator.makeArray!T(initialCapacity, 0).ptr; // TODO set length
378             else
379                 ptr = cast(typeof(return))Allocator.allocate(numBytes).ptr; // TODo set length
380         }
381         else
382         {
383             if (zero)
384                 ptr = cast(typeof(return))calloc(initialCapacity, T.sizeof);
385             else
386                 ptr = cast(typeof(return))malloc(numBytes);
387             assert(ptr, "Allocation failed");
388         }
389 
390         if (ptr is null &&
391             initialCapacity >= 1 )
392             // TODO onOutOfMemoryError();
393             return null;
394 
395         static if (mustAddGCRange!T)
396             gc_addRange(ptr, numBytes);
397 
398         return ptr;
399     }
400 
401     static if (__traits(isCopyable, T))
402     {
403         /** Allocate heap region with `initialCapacity` number of elements of type `T` all set to `elementValue`.
404          */
405         private static MutableE* allocateWithValue(size_t initialCapacity,
406                                                    T elementValue) @trusted
407         {
408             immutable size_t numBytes = initialCapacity * T.sizeof;
409 
410             typeof(return) ptr = null;
411             static if (!is(typeof(Allocator) == typeof(null)))
412             {
413                 import std.experimental.allocator : makeArray;
414                 ptr = Allocator.makeArray!T(initialCapacity, elementValue).ptr; // TODO set length
415                 if (ptr is null &&
416                     initialCapacity >= 1)
417                     // TODO onOutOfMemoryError();
418                     return null;
419             }
420             else
421             {
422                 ptr = cast(typeof(return))malloc(numBytes);
423                 if (ptr is null &&
424                     initialCapacity >= 1)
425                     // TODO onOutOfMemoryError();
426                     return null;
427                 foreach (immutable index; 0 .. initialCapacity)
428                     emplace(&ptr[index], elementValue);
429             }
430 
431             static if (mustAddGCRange!T)
432                 gc_addRange(ptr, numBytes);
433 
434             return ptr;
435         }
436     }
437 
438     /** Comparison for equality. */
439     bool opEquals()(const scope auto ref typeof(this) rhs) const scope // template-lazy
440     {
441         version(D_Coverage) {} else pragma(inline, true);
442         return opSlice() == rhs.opSlice();
443     }
444     /// ditto
445     bool opEquals(U)(const scope U[] rhs) const scope
446     if (is(typeof(T[].init == U[].init)))
447     {
448         version(D_Coverage) {} else pragma(inline, true);
449         return opSlice() == rhs;
450     }
451 
452     /// Calculate D associative array (AA) key hash.
453     hash_t toHash()() const scope @trusted // template-lazy
454     {
455         import core.internal.hash : hashOf;
456         static if (__traits(isCopyable, T))
457             return hashOf(this.length) ^ hashOf(opSlice());
458         else
459         {
460             typeof(return) hash = hashOf(this.length);
461             foreach (immutable index; 0 .. this.length)
462                 hash ^= this.ptr[index].hashOf;
463             return hash;
464         }
465     }
466 
467     static if (__traits(isCopyable, T))
468     {
469         /** Construct a string representation of `this` at `sink`.
470          */
471         void toString()(scope void delegate(scope const(char)[]) sink) const scope // template-lazy
472         {
473             sink("[");
474             foreach (immutable index, ref value; opSlice())
475             {
476                 import std.conv : to;
477                 sink(to!string(value));
478                 if (index + 1 < length) { sink(", "); } // separator
479             }
480             sink("]");
481         }
482     }
483 
484     /// Check if empty.
485     @property bool empty()() const scope // template-lazy
486     {
487         version(D_Coverage) {} else pragma(inline, true);
488         return _store.length == 0;
489     }
490 
491     /// Get length.
492     @property size_t length() const scope // can't be template-lazy
493     {
494         version(D_Coverage) {} else pragma(inline, true);
495         return _store.length;
496     }
497     alias opDollar = length;    /// ditto
498 
499     /** Set length to `newLength`.
500      *
501      * If `newLength` < `length` elements are truncate.
502      * If `newLength` > `length` default-initialized elements are appended.
503      */
504     @property void length(size_t newLength) @trusted scope // can't template-lazy
505     {
506         if (newLength < length) // if truncatation
507         {
508             static if (hasElaborateDestructor!T)
509                 foreach (immutable index; newLength .. _store.length)
510                     .destroy(_mptr[index]);
511             else static if (mustAddGCRange!T)
512                 foreach (immutable index; newLength .. _store.length)
513                     _mptr[index] = T.init; // avoid GC mark-phase dereference
514         }
515         else
516         {
517             reserve(newLength);
518             static if (hasElaborateDestructor!T)
519                 // TODO remove when compiler does it for us
520                 foreach (immutable index; _store.length .. newLength)
521                 {
522                     // TODO remove when compiler does it for us:
523                     static if (__traits(isCopyable, T))
524                         emplace(&_mptr[index], T.init);
525                     else
526                     {
527                         auto _ = T.init;
528                         moveEmplace(_, _mptr[index]);
529                     }
530                 }
531             else
532                 _mptr[_store.length .. newLength] = T.init;
533         }
534 
535         setLengthChecked(newLength);
536     }
537 
538     /// Set capacity, checking for overflow when `CapacityType` is not `size_t`.
539     private void setLengthChecked(size_t newLength) scope
540     {
541         static if (!is(CapacityType == size_t))
542             assert(newLength <= CapacityType.max,
543                    "New length doesn't fit in capacity type.");
544         _store.length = cast(CapacityType)newLength;
545     }
546 
547     /// Get capacity.
548     @property size_t capacity() const scope // can't be template-lazy
549     {
550         version(D_Coverage) {} else pragma(inline, true);
551         return _store.capacity;
552     }
553 
554     /** Ensures sufficient capacity to accommodate for minimumCapacity number
555      * of elements. If `minimumCapacity` < `capacity`, this method does
556      * nothing.
557      */
558     void reserve(size_t minimumCapacity) @trusted scope pure nothrow @nogc
559     {
560         static if (!is(CapacityType == size_t))
561             assert(minimumCapacity <= CapacityType.max,
562                    "Minimum capacity doesn't fit in capacity type.");
563 
564         if (minimumCapacity <= capacity)
565             return;
566 
567         // growth factor
568         // Motivation: https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md#memory-handling
569         reallocateAndSetCapacity(3*minimumCapacity/2); // use 1.5 like Facebook's `fbvector` does
570         // import std.math : nextPow2;
571         // reallocateAndSetCapacity(minimumCapacity.nextPow2);
572     }
573 
574     /// Reallocate storage.
575     private void reallocateAndSetCapacity()(size_t newCapacity) @trusted // template-lazy
576     {
577         static if (!is(CapacityType == size_t))
578             assert(newCapacity <= CapacityType.max,
579                    "New capacity doesn't fit in capacity type.");
580 
581         static if (mustAddGCRange!T)
582             gc_removeRange(_store.ptr);
583 
584         _store.capacity = cast(CapacityType)newCapacity;
585         _store.ptr = cast(T*)realloc(_mptr, T.sizeof * _store.capacity);
586 
587         if (_store.ptr is null &&
588             newCapacity >= 1)
589             // TODO onOutOfMemoryError();
590             return;
591 
592         static if (mustAddGCRange!T)
593             gc_addRange(_store.ptr, _store.capacity * T.sizeof);
594     }
595 
596     /// Index support.
597     ref inout(T) opIndex()(size_t i) inout return // template-lazy
598     {
599         version(D_Coverage) {} else pragma(inline, true);
600         return opSlice()[i];
601     }
602 
603     /// Slice support.
604     inout(T)[] opSlice()(size_t i, size_t j) inout return // template-lazy
605     {
606         version(D_Coverage) {} else pragma(inline, true);
607         return opSlice()[i .. j];
608     }
609     /// ditto
610     inout(T)[] opSlice()() inout return @trusted // template-lazy
611     {
612         version(D_Coverage) {} else pragma(inline, true);
613         return _store.ptr[0 .. _store.length];
614     }
615 
616     /// Index assignment support.
617     ref T opIndexAssign(U)(scope U value, size_t i) @trusted return
618     {
619         static if (needsMove!T)
620         {
621             move(*(cast(MutableE*)(&value)), _mptr[i]); // TODO is this correct?
622             return opSlice()[i];
623         }
624         else static if (hasIndirections!T && // TODO `hasAliasing` instead?
625                         !isMutable!T)
626             static assert("Cannot modify constant elements with indirections");
627         else
628             return opSlice()[i] = value;
629     }
630 
631     /// Slice assignment support.
632     T[] opSliceAssign(U)(scope U value) return
633     {
634         version(D_Coverage) {} else pragma(inline, true);
635         return opSlice()[] = value;
636     }
637 
638     /// ditto
639     T[] opSliceAssign(U)(scope U value, size_t i, size_t j) return
640     {
641         version(D_Coverage) {} else pragma(inline, true);
642         return opSlice()[i .. j] = value;
643     }
644 
645     /// Get reference to front element.
646     ref inout(T) front()() inout return @property // template-lazy
647     {
648         version(D_Coverage) {} else pragma(inline, true);
649         return opSlice()[0];      // range-checked by default
650     }
651 
652     /// Get reference to back element.
653     ref inout(T) back()() inout return @property // template-lazy
654     {
655         version(D_Coverage) {} else pragma(inline, true);
656         return opSlice()[_store.length - 1]; // range-checked by default
657 
658     }
659 
660     /** Move `value` into the end of the array.
661      */
662     void insertBackMove()(ref T value) @trusted // template-lazy
663     {
664         version(LDC) pragma(inline, true);
665         reserve(_store.length + 1);
666         moveEmplace(value, _mptr[_store.length]);
667         _store.length += 1;
668     }
669 
670     /** Insert unmoveable `value` into the end of the array.
671      */
672     void insertBack()(T value) @trusted // template-lazy
673     if (!__traits(isCopyable, T))
674     {
675         version(LDC) pragma(inline, true);
676         insertBackMove(value);
677     }
678 
679     /** Insert the elements `values` into the end of the array.
680      */
681     void insertBack(U)(U[] values...) @trusted
682     if (isElementAssignable!U &&
683         __traits(isCopyable, U))       // prevent accidental move of l-value `values`
684     {
685         if (values.length == 1) // TODO branch should be detected at compile-time
686             // twice as fast as array assignment below
687             return insertBack1(values[0]);
688 
689         static if (is(T == immutable(T)))
690         {
691             /* An array of immutable values cannot overlap with the `this`
692                mutable array container data, which entails no need to check for
693                overlap.
694             */
695             reserve(_store.length + values.length);
696             _mptr[_store.length .. _store.length + values.length] = values;
697         }
698         else
699         {
700             import nxt.overlapping : overlaps;
701             if (_store.ptr == values.ptr) // called for instances as: `this ~= this`
702             {
703                 reserve(2*_store.length); // invalidates `values.ptr`
704                 foreach (immutable i; 0 .. _store.length)
705                 {
706                     _mptr[_store.length + i] = _store.ptr[i];
707                 }
708             }
709             else if (overlaps(this[], values[]))
710                 assert(0, `TODO Handle overlapping arrays`);
711             else
712             {
713                 reserve(_store.length + values.length);
714                 _mptr[_store.length .. _store.length + values.length] = values;
715             }
716         }
717         _store.length += values.length;
718     }
719 
720     /** Insert `value` into the end of the array.
721      *
722      * TODO rename to `insertBack` and make this steal scalar calls over
723      * insertBack(U)(U[] values...) overload below
724      */
725     void insertBack1()(T value) @trusted // template-lazy
726     {
727         reserve(_store.length + 1);
728         static if (needsMove!T)
729             insertBackMove(*cast(MutableE*)(&value));
730         else
731             _mptr[_store.length] = value;
732         _store.length += 1;
733     }
734 
735     /** Insert the elements `elements` into the end of the array.
736      */
737     void insertBack(R)(scope R elements) @trusted
738     if (isInsertableContainer!R)
739     {
740         import std.range.primitives : hasLength;
741         static if (isInputRange!R &&
742                    hasLength!R)
743         {
744             reserve(_store.length + elements.length);
745             import std.algorithm.mutation : copy;
746             copy(elements, _mptr[_store.length .. _store.length + elements.length]);
747             _store.length += elements.length;
748         }
749         else
750         {
751             foreach (ref element; move(elements)) // TODO remove `move` when compiler does it for us
752                 static if (__traits(isCopyable, ElementType!R))
753                     insertBack(element);
754                 else
755                     insertBackMove(element);
756         }
757     }
758 
759     /// ditto
760     alias put = insertBack;
761 
762     /** Remove last value fromm the end of the array.
763      */
764     void popBack()() @trusted   // template-lazy
765     {
766         version(D_Coverage) {} else pragma(inline, true);
767         assert(!empty);
768         _store.length -= 1;
769         static if (hasElaborateDestructor!T)
770             .destroy(_mptr[_store.length]);
771         else static if (mustAddGCRange!T)
772             _mptr[_store.length] = T.init; // avoid GC mark-phase dereference
773     }
774 
775     /** Rmove `n` last values from the end of the array.
776      *
777      * See_Also: http://mir-algorithm.libmir.org/mir_appender.html#.ScopedBuffer.popBackN
778      */
779     void popBackN()(size_t n) @trusted   // template-lazy
780     {
781         assert(length >= n);
782         _store.length -= n;
783         static if (hasElaborateDestructor!T)
784             foreach (immutable index; 0 .. n)
785                 .destroy(_mptr[_store.length + index]);
786         else static if (mustAddGCRange!T)
787             foreach (immutable index; 0 .. n)
788                 _mptr[_store.length + index] = T.init; // avoid GC mark-phase dereference
789     }
790 
791     /** Pop back element and return it.
792      *
793      * This is well-missed feature of C++'s `std::vector` because of problems
794      * with exception handling. For more details see
795      * https://stackoverflow.com/questions/12600330/pop-back-return-value.
796      */
797     T backPop()() @trusted      // template-lazy
798     {
799         version(D_Coverage) {} else pragma(inline, true);
800         assert(!empty);
801         _store.length -= 1;
802         static if (needsMove!T)
803             return move(_mptr[_store.length]); // move is indeed need here
804         else
805             return _mptr[_store.length]; // no move needed
806     }
807 
808     /** Pop element at `index`. */
809     void popAt()(size_t index) @trusted // template-lazy
810         @("complexity", "O(length)")
811     {
812         assert(index < this.length);
813         static if (hasElaborateDestructor!T)
814             .destroy(_mptr[index]);
815         else static if (mustAddGCRange!T)
816             _mptr[index] = T.init; // avoid GC mark-phase dereference
817         shiftToFrontAt(index);
818         _store.length -= 1;
819     }
820 
821     /** Move element at `index` to return. */
822     T moveAt()(size_t index) @trusted // template-lazy
823         @("complexity", "O(length)")
824     {
825         assert(index < this.length);
826         auto value = move(_mptr[index]);
827         shiftToFrontAt(index);
828         _store.length -= 1;
829         return move(value); // TODO remove `move` when compiler does it for us
830     }
831 
832     /** Move element at front. */
833     T frontPop()()              // template-lazy
834         @("complexity", "O(length)")
835     {
836         version(D_Coverage) {} else pragma(inline, true);
837         return moveAt(0);
838     }
839 
840     private void shiftToFrontAt()(size_t index) @trusted // template-lazy
841     {
842         // TODO use this instead:
843         // immutable si = index + 1;   // source index
844         // immutable ti = index;       // target index
845         // immutable restLength = this.length - (index + 1);
846         // import std.algorithm.mutation : moveEmplaceAll;
847         // moveEmplaceAll(_mptr[si .. si + restLength],
848         //                _mptr[ti .. ti + restLength]);
849         foreach (immutable i; 0 .. this.length - (index + 1)) // each element index that needs to be moved
850         {
851             immutable si = index + i + 1; // source index
852             immutable ti = index + i; // target index
853             moveEmplace(_mptr[si], // TODO remove `move` when compiler does it for us
854                         _mptr[ti]);
855         }
856     }
857 
858     /** Forwards to $(D insertBack(values)).
859      */
860     void opOpAssign(string op)(T value)
861     if (op == "~")
862     {
863         version(D_Coverage) {} else pragma(inline, true);
864         insertBackMove(value);
865     }
866 
867     /// ditto
868     void opOpAssign(string op, U)(U[] values...) @trusted
869     if (op == "~" &&
870         isElementAssignable!U &&
871         __traits(isCopyable, U))       // prevent accidental move of l-value `values`
872     {
873         version(D_Coverage) {} else pragma(inline, true);
874         insertBack(values);
875     }
876 
877     /// ditto
878     void opOpAssign(string op, R)(R values)
879     if (op == "~" &&
880         isInputRange!R &&
881         !isInfinite!R &&
882         !isArray!R &&
883         isElementAssignable!(ElementType!R))
884     {
885         version(D_Coverage) {} else pragma(inline, true);
886         insertBack(values);
887     }
888 
889     void opOpAssign(string op)(auto ref typeof(this) values)
890     if (op == "~")
891     {
892         version(D_Coverage) {} else pragma(inline, true);
893         insertBack(values[]);
894     }
895 
896     // typeof(this) opBinary(string op, R)(R values)
897     //     if (op == "~")
898     // {
899     //     // TODO: optimize
900     //     typeof(this) result;
901     //     result ~= this[];
902     //     assert(result.length == length);
903     //     result ~= values[];
904     //     return result;
905     // }
906 
907     /// Unsafe access to pointer.
908     inout(T)* ptr()() inout return @system // template-lazy
909     {
910         version(D_Coverage) {} else pragma(inline, true);
911         return _store.ptr;
912     }
913 
914     /// Mutable pointer.
915     private MutableE* _mptr() const return @trusted
916     {
917         version(D_Coverage) {} else pragma(inline, true);
918         return cast(typeof(return))_store.ptr;
919     }
920 
921 private:
922     /** For more convenient construction. */
923     struct Store
924     {
925         static if (!is(typeof(Allocator) == typeof(null)) &&
926                    !hasFunctionAttributes!(Allocator.allocate, "@nogc"))
927         {
928             T* ptr;             // GC-allocated store pointer
929         }
930         else
931         {
932             import nxt.gc_traits : NoGc;
933             @NoGc T* ptr;       // non-GC-allocated store pointer
934         }
935 
936         CapacityType capacity; // store capacity
937         CapacityType length;   // store length
938     }
939 
940     Store _store;
941 }
942 
943 import std.traits : isInstanceOf;
944 import std.functional : unaryFun;
945 
946 /** Remove all elements matching `predicate`.
947  *
948  * Returns: number of elements that were removed.
949  *
950  * TODO implement version that doesn't use a temporary array `tmp`, which is
951  * probably faster for small arrays.
952  */
953 size_t remove(alias predicate, C)(ref C c) @trusted
954     @("complexity", "O(length)")
955 if (isInstanceOf!(DynamicArray, C) &&
956     is(typeof(unaryFun!predicate(C.init[0]))))
957 {
958     C tmp;
959     size_t count = 0;
960     foreach (immutable i; 0 .. c.length)
961     {
962         if (unaryFun!predicate(c[i]))
963         {
964             count += 1;
965             import core.internal.traits : hasElaborateDestructor;
966             import nxt.container_traits : mustAddGCRange;
967             alias T = typeof(c[i]);
968             static if (hasElaborateDestructor!(T))
969                 .destroy(c[i]);
970             else static if (mustAddGCRange!(T))
971                 c[i] = T.init;    // avoid GC mark-phase dereference
972         }
973         else
974             tmp.insertBackMove(c[i]); // TODO remove unnecessary clearing of `_mptr[i]`
975     }
976 
977     c.freeStore();
978 
979     import core.lifetime : moveEmplace;
980     moveEmplace(tmp, c);
981 
982     return count;
983 }
984 
985 /// construct and append from slices
986 @safe pure nothrow @nogc unittest
987 {
988     alias T = int;
989     alias A = DynamicArray!(T, null, uint);
990     static if (size_t.sizeof == 8) // only 64-bit
991         static assert(A.sizeof == 2 * size_t.sizeof); // only two words
992 
993     auto a = A([10, 11, 12].s);
994 
995     a ~= a[];
996     assert(a[] == [10, 11, 12,
997                    10, 11, 12].s);
998 
999     a ~= false;
1000     assert(a[] == [10, 11, 12,
1001                    10, 11, 12, 0].s);
1002 }
1003 
1004 ///
1005 @safe pure nothrow @nogc unittest
1006 {
1007     alias T = int;
1008     alias A = DynamicArray!(T);
1009 
1010     A a;
1011 
1012     a.length = 1;
1013     assert(a.length == 1);
1014     assert(a.capacity >= 1);
1015 
1016     a[0] = 10;
1017 
1018     a.insertBack(11, 12);
1019 
1020     a ~= T.init;
1021     a.insertBack([3].s);
1022     assert(a[] == [10, 11, 12, 0, 3].s);
1023 
1024     import std.algorithm.iteration : filter;
1025 
1026     a.insertBack([42].s[].filter!(_ => _ is 42));
1027     assert(a[] == [10, 11, 12, 0, 3, 42].s);
1028 
1029     a.insertBack([42].s[].filter!(_ => _ !is 42));
1030     assert(a[] == [10, 11, 12, 0, 3, 42].s);
1031 
1032     a ~= a[];
1033     assert(a[] == [10, 11, 12, 0, 3, 42,
1034                    10, 11, 12, 0, 3, 42].s);
1035 }
1036 
1037 ///
1038 @safe pure nothrow @nogc unittest
1039 {
1040     alias T = int;
1041     alias A = DynamicArray!(T);
1042 
1043     A a;                        // default construction allowed
1044     assert(a.empty);
1045     assert(a.length == 0);
1046     assert(a.capacity == 0);
1047     assert(a[] == []);
1048 
1049     auto b = DynamicArray!int.withLength(3);
1050     assert(!b.empty);
1051     assert(b.length == 3);
1052     assert(b.capacity == 3);
1053     b[0] = 1;
1054     b[1] = 2;
1055     b[2] = 3;
1056     assert(b[] == [1, 2, 3].s);
1057 
1058     b[] = [4, 5, 6].s;
1059     assert(b[] == [4, 5, 6].s);
1060 
1061     const c = DynamicArray!int.withCapacity(3);
1062     assert(c.empty);
1063     assert(c.capacity == 3);
1064     assert(c[] == []);
1065 
1066     // TODO this should fail with -dip1000
1067     auto f() @safe
1068     {
1069         A a;
1070         return a[];
1071     }
1072     auto d = f();
1073 
1074     const e = DynamicArray!int([1, 2, 3, 4].s);
1075     assert(e.length == 4);
1076     assert(e[] == [1, 2, 3, 4].s);
1077 }
1078 
1079 ///
1080 @trusted pure nothrow @nogc unittest
1081 {
1082     alias T = int;
1083     alias A = DynamicArray!(T);
1084 
1085     auto a = A([1, 2, 3].s);
1086     A b = a.dup;                // copy construction enabled
1087 
1088     assert(a[] == b[]);          // same content
1089     assert(&a[0] !is &b[0]); // but not the same
1090 
1091     assert(b[] == [1, 2, 3].s);
1092     assert(b.length == 3);
1093 
1094     b ~= 4;
1095     assert(a != b);
1096     a.clear();
1097     assert(a != b);
1098     b.clear();
1099     assert(a == b);
1100 
1101     auto c = A([1, 2, 3].s);
1102 }
1103 
1104 /// DIP-1000 return ref escape analysis
1105 @safe pure nothrow @nogc unittest
1106 {
1107     alias T = int;
1108     alias A = DynamicArray!T;
1109 
1110     T[] leakSlice() @safe pure nothrow @nogc
1111     {
1112         A a;
1113         return a[];             // TODO shouldn't compile with -dip1000
1114     }
1115 
1116     T* leakPointer() @safe pure nothrow @nogc
1117     {
1118         A a;
1119         return a._store.ptr;    // TODO shouldn't compile with -dip1000
1120     }
1121 
1122     auto lp = leakPointer();    // TODO shouldn't compile with -dip1000
1123     auto ls = leakSlice();      // TODO shouldn't compile with -dip1000
1124 }
1125 
1126 version(unittest)
1127 {
1128     private static struct SomeUncopyable
1129     {
1130         @disable this(this);
1131         int x;
1132     }
1133 }
1134 
1135 /// construct and insert from non-copyable element type passed by value
1136 @safe pure nothrow /*@nogc*/ unittest
1137 {
1138     alias A = DynamicArray!(SomeUncopyable);
1139 
1140     A a = A(SomeUncopyable(17));
1141     assert(a[] == [SomeUncopyable(17)]);
1142 
1143     a.insertBack(SomeUncopyable(18));
1144     assert(a[] == [SomeUncopyable(17),
1145                    SomeUncopyable(18)]);
1146 
1147     a ~= SomeUncopyable(19);
1148     assert(a[] == [SomeUncopyable(17),
1149                    SomeUncopyable(18),
1150                    SomeUncopyable(19)]);
1151 }
1152 
1153 /// construct from slice of uncopyable type
1154 @safe pure nothrow @nogc unittest
1155 {
1156     alias A = DynamicArray!(SomeUncopyable);
1157     // TODO can we safely support this?: A a = [SomeUncopyable(17)];
1158 }
1159 
1160 // construct from array with uncopyable elements
1161 @safe pure nothrow @nogc unittest
1162 {
1163     alias A = DynamicArray!(SomeUncopyable);
1164 
1165     A a;
1166     assert(a.empty);
1167 
1168     a.insertBack(A.init);
1169     assert(a.empty);
1170 }
1171 
1172 // construct from ranges of uncopyable elements
1173 @safe pure nothrow @nogc unittest
1174 {
1175     alias T = SomeUncopyable;
1176     alias A = DynamicArray!T;
1177 
1178     A a;
1179     assert(a.empty);
1180 
1181     import std.algorithm.iteration : map, filter;
1182 
1183     const b = A.withElementsOfRange_untested([10, 20, 30].s[].map!(_ => T(_^^2))); // hasLength
1184     assert(b.length == 3);
1185     assert(b == [T(100), T(400), T(900)].s);
1186 
1187     const c = A.withElementsOfRange_untested([10, 20, 30].s[].filter!(_ => _ == 30).map!(_ => T(_^^2))); // !hasLength
1188     assert(c.length == 1);
1189     assert(c[0].x == 900);
1190 }
1191 
1192 // construct from ranges of copyable elements
1193 @safe pure nothrow @nogc unittest
1194 {
1195     alias T = int;
1196     alias A = DynamicArray!T;
1197 
1198     A a;
1199     assert(a.empty);
1200 
1201     import std.algorithm.iteration : map, filter;
1202 
1203     const b = A.withElementsOfRange_untested([10, 20, 30].s[].map!(_ => T(_^^2))); // hasLength
1204     assert(b.length == 3);
1205     assert(b == [T(100), T(400), T(900)].s);
1206 
1207     const c = A.withElementsOfRange_untested([10, 20, 30].s[].filter!(_ => _ == 30).map!(_ => T(_^^2))); // !hasLength
1208     assert(c == [T(900)].s);
1209 }
1210 
1211 /// construct with string as element type that needs GC-range
1212 @safe pure nothrow @nogc unittest
1213 {
1214     alias T = string;
1215     alias A = DynamicArray!(T);
1216 
1217     A a;
1218     a ~= `alpha`;
1219     a ~= `beta`;
1220     a ~= [`gamma`, `delta`].s;
1221     assert(a[] == [`alpha`, `beta`, `gamma`, `delta`].s);
1222 
1223     const b = [`epsilon`].s;
1224 
1225     a.insertBack(b);
1226     assert(a[] == [`alpha`, `beta`, `gamma`, `delta`, `epsilon`].s);
1227 
1228     a ~= b;
1229     assert(a[] == [`alpha`, `beta`, `gamma`, `delta`, `epsilon`, `epsilon`].s);
1230 }
1231 
1232 /// convert to string
1233 unittest
1234 {
1235     alias T = int;
1236     alias A = DynamicArray!(T);
1237 
1238     DynamicArray!char sink;
1239     // TODO make this work: A([1, 2, 3]).toString(sink.put);
1240 }
1241 
1242 /// iteration over mutable elements
1243 @safe pure nothrow @nogc unittest
1244 {
1245     alias T = int;
1246     alias A = DynamicArray!(T);
1247 
1248     auto a = A([1, 2, 3].s);
1249 
1250     foreach (immutable i, const e; a)
1251     {
1252         assert(i + 1 == e);
1253     }
1254 }
1255 
1256 /// iteration over `const`ant elements
1257 @safe pure nothrow @nogc unittest
1258 {
1259     alias T = const(int);
1260     alias A = DynamicArray!(T);
1261 
1262     auto a = A([1, 2, 3].s);
1263 
1264     foreach (immutable i, const e; a)
1265     {
1266         assert(i + 1 == e);
1267     }
1268 }
1269 
1270 /// iteration over immutable elements
1271 @safe pure nothrow @nogc unittest
1272 {
1273     alias T = immutable(int);
1274     alias A = DynamicArray!(T);
1275 
1276     auto a = A([1, 2, 3].s);
1277 
1278     foreach (immutable i, const e; a)
1279     {
1280         assert(i + 1 == e);
1281     }
1282 }
1283 
1284 /// removal
1285 @safe pure nothrow @nogc unittest
1286 {
1287     alias T = int;
1288     alias A = DynamicArray!(T);
1289 
1290     auto a = A([1, 2, 3].s);
1291     assert(a == [1, 2, 3].s);
1292 
1293     assert(a.frontPop() == 1);
1294     assert(a == [2, 3].s);
1295 
1296     a.popAt(1);
1297     assert(a == [2].s);
1298 
1299     a.popAt(0);
1300     assert(a == []);
1301 
1302     a.insertBack(11);
1303     assert(a == [11].s);
1304 
1305     assert(a.backPop == 11);
1306 
1307     a.insertBack(17);
1308     assert(a == [17].s);
1309     a.popBack();
1310     assert(a.empty);
1311 
1312     a.insertBack([11, 12, 13, 14, 15].s[]);
1313     a.popAt(2);
1314     assert(a == [11, 12, 14, 15].s);
1315     a.popAt(0);
1316     assert(a == [12, 14, 15].s);
1317     a.popAt(2);
1318 
1319     assert(a == [12, 14].s);
1320 
1321     a ~= a;
1322 }
1323 
1324 /// removal
1325 @safe pure nothrow unittest
1326 {
1327     import nxt.container_traits : mustAddGCRange;
1328 
1329     size_t mallocCount = 0;
1330     size_t freeCount = 0;
1331 
1332     struct S
1333     {
1334         @safe pure nothrow @nogc:
1335 
1336         alias E = int;
1337 
1338         import nxt.qcmeman : malloc, free;
1339 
1340         this(E x) @trusted
1341         {
1342             _ptr = cast(E*)malloc(E.sizeof);
1343             mallocCount += 1;
1344             *_ptr = x;
1345         }
1346 
1347         @disable this(this);
1348 
1349         ~this() @trusted @nogc
1350         {
1351             free(_ptr);
1352             freeCount += 1;
1353         }
1354 
1355         import nxt.gc_traits : NoGc;
1356         @NoGc E* _ptr;
1357     }
1358 
1359     /* D compilers cannot currently move stuff efficiently when using
1360      * std.algorithm.mutation.move. A final dtor call to the cleared sourced is
1361      * always done. */
1362     size_t extraDtor = 1;
1363 
1364     alias A = DynamicArray!(S);
1365     static assert(!mustAddGCRange!A);
1366     alias AA = DynamicArray!(A);
1367     static assert(!mustAddGCRange!AA);
1368 
1369     assert(mallocCount == 0);
1370 
1371     {
1372         A a;
1373         a.insertBack(S(11));
1374         assert(mallocCount == 1);
1375         assert(freeCount == extraDtor + 0);
1376     }
1377 
1378     assert(freeCount == extraDtor + 1);
1379 
1380     // assert(a.front !is S(11));
1381     // assert(a.back !is S(11));
1382     // a.insertBack(S(12));
1383 }
1384 
1385 /// test `OutputRange` behaviour with std.format
1386 version(none)                   // TODO replace with other exercise of std.format
1387 @safe pure /*TODO nothrow @nogc*/ unittest
1388 {
1389     import std.format : formattedWrite;
1390     const x = "42";
1391     alias A = DynamicArray!(char);
1392     A a;
1393     a.formattedWrite!("x : %s")(x);
1394     assert(a == "x : 42");
1395 }
1396 
1397 /// test emplaceWithMovedElements
1398 @trusted pure nothrow @nogc unittest
1399 {
1400     const x = "42";
1401     alias A = DynamicArray!(char);
1402 
1403     auto ae = ['a', 'b'].s;
1404 
1405     A a = void;
1406     A.emplaceWithMovedElements(&a, ae[]);
1407 
1408     assert(a.length == ae.length);
1409     assert(a.capacity == ae.length);
1410     assert(a[] == ae);
1411 }
1412 
1413 /// test emplaceWithCopiedElements
1414 @trusted pure nothrow @nogc unittest
1415 {
1416     const x = "42";
1417     alias A = DynamicArray!(char);
1418 
1419     auto ae = ['a', 'b'].s;
1420 
1421     A a = void;
1422     A.emplaceWithCopiedElements(&a, ae[]);
1423 
1424     assert(a.length == ae.length);
1425     assert(a.capacity == ae.length);
1426     assert(a[] == ae);
1427 }
1428 
1429 @safe pure nothrow @nogc unittest
1430 {
1431     alias T = int;
1432     alias A = DynamicArray!(T, null, uint);
1433     const a = A(17);
1434     assert(a[] == [17].s);
1435 }
1436 
1437 /// check duplication via `dup`
1438 @trusted pure nothrow @nogc unittest
1439 {
1440     alias T = int;
1441     alias A = DynamicArray!(T);
1442 
1443     static assert(!__traits(compiles, { A b = a; })); // copying disabled
1444 
1445     auto a = A([10, 11, 12].s);
1446     auto b = a.dup;
1447     assert(a == b);
1448     assert(&a[0] !is &b[0]);
1449 }
1450 
1451 /// element type is a class
1452 @safe pure nothrow unittest
1453 {
1454     class T
1455     {
1456         this (int x)
1457         {
1458             this.x = x;
1459         }
1460         ~this() @nogc { x = 42; }
1461         int x;
1462     }
1463     alias A = DynamicArray!(T);
1464     auto a = A([new T(10),
1465                 new T(11),
1466                 new T(12)].s);
1467     assert(a.length == 3);
1468     a.remove!(_ => _.x == 12);
1469     assert(a.length == 2);
1470 }
1471 
1472 /// check filtered removal via `remove`
1473 @safe pure nothrow @nogc unittest
1474 {
1475     struct T
1476     {
1477         int value;
1478     }
1479 
1480     alias A = DynamicArray!(T);
1481 
1482     static assert(!__traits(compiles, { A b = a; })); // copying disabled
1483 
1484     auto a = A([T(10), T(11), T(12)].s);
1485 
1486     assert(a.remove!"a.value == 13" == 0);
1487     assert(a[] == [T(10), T(11), T(12)].s);
1488 
1489     assert(a.remove!"a.value >= 12" == 1);
1490     assert(a[] == [T(10), T(11)].s);
1491 
1492     assert(a.remove!(_ => _.value == 10) == 1);
1493     assert(a[] == [T(11)].s);
1494 
1495     assert(a.remove!(_ => _.value == 11) == 1);
1496     assert(a.empty);
1497 }
1498 
1499 /// construct from map range
1500 @safe pure nothrow unittest
1501 {
1502     import std.algorithm.iteration : map;
1503     alias T = int;
1504     alias A = DynamicArray!(T);
1505 
1506     A a = A.withElementsOfRange_untested([10, 20, 30].s[].map!(_ => _^^2));
1507     assert(a[] == [100, 400, 900].s);
1508     a.popBackN(2);
1509     assert(a.length == 1);
1510     a.popBackN(1);
1511     assert(a.empty);
1512 
1513     A b = A([10, 20, 30].s[].map!(_ => _^^2));
1514     assert(b[] == [100, 400, 900].s);
1515     b.popBackN(2);
1516     assert(b.length == 1);
1517     b.popBackN(1);
1518     assert(b.empty);
1519 
1520     A c = A([10, 20, 30].s[]);
1521     assert(c[] == [10, 20, 30].s);
1522 }
1523 
1524 /// construct from map range
1525 @trusted pure nothrow unittest
1526 {
1527     alias T = int;
1528     alias A = DynamicArray!(T);
1529 
1530     import std.typecons : RefCounted;
1531     RefCounted!A x;
1532 
1533     auto z = [1, 2, 3].s;
1534     x ~= z[];
1535 
1536     auto y = x;
1537     assert(y[] == z);
1538 
1539     auto _ = x.toHash;
1540 }
1541 
1542 /// construct from static array
1543 @trusted pure nothrow @nogc unittest
1544 {
1545     alias T = uint;
1546     alias A = DynamicArray!(T);
1547 
1548     ushort[3] a = [1, 2, 3];
1549 
1550     auto x = A(a);
1551     assert(x == a);
1552     assert(x == a[]);
1553 }
1554 
1555 /// construct from static array slice
1556 @trusted pure nothrow @nogc unittest
1557 {
1558     alias T = uint;
1559     alias A = DynamicArray!(T);
1560 
1561     ushort[3] a = [1, 2, 3];
1562     ushort[] b = a[];
1563 
1564     auto y = A(b);          // cannot construct directly from `a[]` because its type is `ushort[3]`
1565     assert(y == a);
1566     assert(y == a[]);
1567 }
1568 
1569 /// GCAllocator
1570 @trusted pure nothrow unittest
1571 {
1572     import std.experimental.allocator.gc_allocator : GCAllocator;
1573     alias T = int;
1574     alias A = DynamicArray!(T, GCAllocator.instance);
1575     A a;
1576 }
1577 
1578 /// construct with slices as element types
1579 @trusted pure nothrow unittest
1580 {
1581     alias A = DynamicArray!(string);
1582     alias B = DynamicArray!(char[]);
1583 }
1584 
1585 /** Variant of `DynamicArray` with copy construction (postblit) enabled.
1586  *
1587  * See_Also: suppressing.d
1588  * See_Also: http://forum.dlang.org/post/eitlbtfbavdphbvplnrk@forum.dlang.org
1589  */
1590 struct BasicCopyableArray
1591 {
1592     /** TODO implement using instructions at:
1593      * http://forum.dlang.org/post/eitlbtfbavdphbvplnrk@forum.dlang.org
1594      */
1595 }
1596 
1597 /// TODO Move to Phobos.
1598 private enum bool isRefIterable(T) = is(typeof({ foreach (ref elem; T.init) {} }));
1599 
1600 version(unittest)
1601 {
1602     import nxt.array_help : s;
1603 }