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