1 module nxt.container.dynamic_array;
2 
3 import std.experimental.allocator.gc_allocator : GCAllocator;
4 import std.experimental.allocator.common : isAllocator;
5 
6 /** Dynamic array container.
7 
8 	TODO: Move members dealing with `Allocator`, such as DynamicArrray.reserve,
9     and others to private members of `ArrayStore` and then rerun container
10     benchmark for `DynamicArray`. Also include a benchmark of calls to
11     `ArrayStore.reserve()`.
12 
13 	TODO: Generalize to bucket array either via specialized allocator to by
14 	extra Storage class given as template type parameter. Integrate
15 	`nxt.bucket_array` for details.
16 
17 	TODO: Add OutputRange.writer support as
18 	https://github.com/burner/StringBuffer/blob/master/source/stringbuffer.d#L45
19 
20 	TODO: Use `std.traits.areCopyCompatibleArrays`
21 
22 	TODO: Check if using the std::vector-compatible store is faster: struct
23 	Store { T* begin; T* endData; T* endCapacity; }
24 
25     See: http://forum.dlang.org/thread/wswbtzakdvpgaebuhbom@forum.dlang.org See
26 	also https://github.com/izabera/s */
27 @safe struct DynamicArray(T, Allocator = GCAllocator, Capacity = size_t)
28 if (!is(immutable T == immutable bool) && // use `BitArray` instead for now
29 	(is(Capacity == ulong) || // two 64-bit words
30 	 is(Capacity == uint)) && // two 64-bit words
31 	isAllocator!Allocator) {
32 
33 	/** Growth factor P/Q.
34 		https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md#memory-handling
35 		Use 1.5 like Facebook's `fbvector` does.
36 	*/
37 	enum _growthP = 3;		  // numerator
38 	/// ditto
39 	enum _growthQ = 2;		  // denominator
40 
41 	// import core.exception : onOutOfMemoryError;
42 	import core.stdc.string : memset;
43 	import core.internal.traits : Unqual, hasElaborateDestructor;
44 	import std.experimental.allocator : makeArray;
45 	import std.range.primitives : isInputRange, ElementType, hasLength, hasSlicing, isInfinite;
46 	import std.traits : hasIndirections, hasAliasing,
47 		isMutable, TemplateOf, isArray, isType, isIterable, isPointer;
48 	import core.lifetime : emplace, move, moveEmplace;
49 
50 	import nxt.qcmeman : gc_addRange, gc_removeRange;
51 	import nxt.container.traits : mustAddGCRange, isRvalueAssignable;
52 
53 	/// Mutable element type.
54 	private alias MT = Unqual!T;
55 
56 	/// Is `true` if `U` can be assigned to the elements of `this`.
57 	enum isElementAssignable(U) = isRvalueAssignable!(MT, U);
58 
59 	private this(Store store) {
60 		version (D_Coverage) {} else pragma(inline, true);
61 		_store = store;
62 	}
63 
64 	/// Construct from element `value`.
65 	this(U)(U value) @trusted if (isElementAssignable!U) {
66 		_store = Store(typeof(this).allocate(1, false), 1);
67 		static if (__traits(isPOD, T))
68 			_mptr[0] = value;
69 		else
70 			moveEmplace(value, _mptr[0]); /+ TODO: remove when compiler does this +/
71 	}
72 
73 	/// Construct from the element(s) of the dynamic array `values`.
74 	this(U)(U[] values) @trusted if (isElementAssignable!(U)) {
75 		/+ TODO: use import emplace_all instead +/
76 		_store = Store(allocate(values.length, false), values.length);
77 		foreach (index; 0 .. values.length)
78 			static if (__traits(isPOD, T))
79 				_mptr[index] = values[index];
80 			else
81 				move(values[index], _mptr[index]);
82 	}
83 
84 	/// Construct from the `n` number of element(s) in the static array `values`.
85 	this(uint n, U)(U[n] values) @trusted
86 	if (values.length <= Capacity.max && isElementAssignable!(U)) {
87 		/+ TODO: use import emplace_all instead +/
88 		_store = Store(allocate(values.length, false), values.length, values.length);
89 		static foreach (index; 0 .. values.length)
90 			static if (__traits(isPOD, T))
91 				_mptr[index] = values[index];
92 			else
93 				move(values[index], _mptr[index]);
94 	}
95 	/// ditto
96 	this(R)(scope R values) @trusted
97 	if (// isRefIterable!R &&
98 		isElementAssignable!(ElementType!R) &&
99 		!isArray!R) {
100 		static if (hasLength!R) {
101 			reserve(values.length);
102 			size_t index = 0;
103 			foreach (ref value; values)
104 				_mptr[index++] = value;
105 			_store._length = values.length;
106 		} else
107 			foreach (ref value; values)
108 				insertBack1(value);
109 	}
110 
111 	/** Is `true` iff the iterable container `C` can be insert to `this`.
112 	 */
113 	private enum isInsertableContainer(C) = (is(C == struct) && // exclude class ranges for aliasing control
114 											 isRefIterable!C && // elements may be non-copyable
115 											 !isInfinite!C &&
116 											 isElementAssignable!(ElementType!C));
117 
118 	/// Construct from the elements `values`.
119 	static typeof(this) withElementsOfRange_untested(R)(R values) @trusted
120 	if (isInsertableContainer!R) {
121 		typeof(this) result;
122 		static if (hasLength!R)
123 			result.reserve(values.length);
124 		static if (__traits(isPOD, ElementType!R) &&
125 				   hasLength!R &&
126 				   hasSlicing!R) {
127 			import std.algorithm.mutation : copy;
128 			copy(values[0 .. values.length],
129 				 result._mptr[0 .. values.length]); /+ TODO: better to use foreach instead? +/
130 			result._store._length = values.length;
131 		} else {
132 			static if (hasLength!R) {
133 				size_t i = 0;
134 				foreach (ref value; move(values)) /+ TODO: remove `move` when compiler does it for us +/
135 					static if (__traits(isPOD, typeof(value)))
136 						result._mptr[i++] = value;
137 					else
138 						moveEmplace(value, result._mptr[i++]);
139 				result._store._length = values.length;
140 			} else {
141 				// import std.algorithm.mutation : moveEmplaceAll;
142 				/* TODO: optimize with `moveEmplaceAll` that does a raw copy and
143 				 * zeroing of values */
144 				foreach (ref value; move(values)) /+ TODO: remove `move` when compiler does it for us +/
145 					static if (__traits(isPOD, ElementType!R))
146 						result.insertBack(value);
147 					else
148 						result.insertBackMove(value); // steal element
149 			}
150 		}
151 		return result;
152 	}
153 
154 	/// No default copying.
155 	this(this) @disable;
156 
157 	/+ TODO: this gives error in insertBack. why? +/
158 	// void opAssign()(typeof(this) rhs) @trusted pure nothrow @nogc /*tlm*/
159 	// {
160 	//	 move(rhs, this);
161 	// }
162 
163 	/** Destruct.
164 	 *
165 	 * TODO: what effect does have here?
166 	 * See_Also: https://github.com/atilaneves/automem/blob/master/source/automem/vector.d#L92
167 	 */
168 	~this() nothrow @nogc /*TODO:scope*/ {
169 		releaseElementsStore();
170 	}
171 
172 	/// Clear.
173 	void clear() @nogc {
174 		releaseElementsStore();
175 		resetInternalData();
176 	}
177 
178 	/// Release elements and internal store.
179 	private void releaseElementsStore() @nogc @trusted {
180 		foreach (const index; 0 .. _store.length)
181 			static if (hasElaborateDestructor!T)
182 				.destroy(_mptr[index]);
183 			else static if (is(T == class) || isPointer!T || hasIndirections!T)
184 				_mptr[index] = T.init; // nullify any pointers
185 		freeStore();
186 	}
187 
188 	/// Free internal store.
189 	private void freeStore() @trusted {
190 		static if (mustAddGCRange!T)
191 			gc_removeRange(_mptr);
192 		allocator.deallocate(cast(void[])_store[]);
193 	}
194 
195 	/// Reset internal data.
196 	private void resetInternalData() @nogc {
197 		version (D_Coverage) {} else pragma(inline, true);
198 		_store = Store.init;
199 	}
200 
201 	/** Allocate heap region with `initialCapacity` number of elements of type `T`.
202 	 *
203 	 * If `initFlag` is `true` elements will be initialized
204 	 */
205 	private static MT* allocate(in Capacity initialCapacity, in bool initFlag) @trusted {
206 		const size_t numBytes = initialCapacity * T.sizeof;
207 		typeof(return) ptr = null;
208 		if (initFlag) {
209 			static if (__traits(isZeroInit, T) &&
210 					   __traits(hasMember, Allocator, "allocateZeroed") &&
211 					   is(typeof(allocator.allocateZeroed(numBytes)) == void[]))
212 				ptr = cast(typeof(return))(allocator.allocateZeroed(numBytes).ptr);
213 			else {
214 				ptr = cast(typeof(return))(allocator.allocate(numBytes).ptr);
215 				static if (__traits(isZeroInit, T))
216 					memset(ptr, 0, numBytes);
217 				else
218 					foreach (i; 0 .. initialCapacity)
219 						ptr[i] = MT.init;
220 			}
221 		} else
222 			ptr = cast(typeof(return))(allocator.allocate(numBytes).ptr);
223 		if (ptr is null &&
224 			initialCapacity >= 1 )
225 			/+ TODO: onOutOfMemoryError(); +/
226 			return null;
227 		static if (mustAddGCRange!T)
228 			gc_addRange(ptr, numBytes);
229 		return ptr;
230 	}
231 
232 	static if (__traits(isCopyable, T)) {
233 		/** Allocate heap region with `initialCapacity` number of elements of type `T` all set to `elementValue`.
234 		 */
235 		private static MT* allocateWithValue(in Capacity initialCapacity, T elementValue) @trusted {
236 			const size_t numBytes = initialCapacity * T.sizeof;
237 			typeof(return) ptr = null;
238 			ptr = allocator.makeArray!MT(initialCapacity, elementValue).ptr; /+ TODO: set length +/
239 			if (ptr is null &&
240 				initialCapacity >= 1)
241 				/+ TODO: onOutOfMemoryError(); +/
242 				return null;
243 			static if (mustAddGCRange!T)
244 				gc_addRange(ptr, numBytes);
245 			return ptr;
246 		}
247 	}
248 
249 	/** Comparison for equality. */
250 	bool opEquals()(const auto ref typeof(this) rhs) const scope /*tlm*/ {
251 		version (D_Coverage) {} else version (LDC) pragma(inline, true);
252 		return opSlice() == rhs.opSlice();
253 	}
254 
255 	/// ditto
256 	pragma(inline, true)
257 	bool opEquals(U)(const scope U[] rhs) const scope
258 	if (is(typeof(T[].init == U[].init)))
259 		=> opSlice() == rhs;
260 
261 	/// Calculate D associative array (AA) key hash.
262 	pragma(inline, true)
263 	hash_t toHash()() const scope @trusted /*tlm*/
264 		=> .hashOf(length) + .hashOf(opSlice());
265 
266 	static if (__traits(isCopyable, T)) {
267 		/** Construct a string representation of `this` at `sink`. */
268 		void toString(Sink)(ref scope Sink sink) const scope /*tlm*/ {
269 			import std.conv : to;
270 			sink("[");
271 			foreach (const index, ref value; opSlice()) {
272 				sink(to!string(value));
273 				if (index + 1 < length) { sink(", "); } // separator
274 			}
275 			sink("]");
276 		}
277 	}
278 
279 	/// Get length.
280 	pragma(inline, true)
281 	@property Capacity length() const scope => _store.length;
282 	/// ditto
283 	alias opDollar = length;
284 
285 	/** Set length to `newLength`.
286 	 *
287 	 * If `newLength` < `length` elements are truncate.
288 	 * If `newLength` > `length` default-initialized elements are appended.
289 	 */
290 	@property void length(in Capacity newLength) @trusted scope {
291 		if (newLength < length) { // if truncation
292 			static if (hasElaborateDestructor!T)
293 				foreach (const index; newLength .. _store.length)
294 					.destroy(_mptr[index]);
295 			else static if (mustAddGCRange!T)
296 				foreach (const index; newLength .. _store.length)
297 					_mptr[index] = T.init; // avoid GC mark-phase dereference
298 		} else {
299 			reserveFitLength(newLength);
300 			static if (hasElaborateDestructor!T) {
301 				/+ TODO: remove when compiler does it for us +/
302 				foreach (const index; _store.length .. newLength) {
303 					/+ TODO: remove when compiler does it for us: +/
304 					static if (__traits(isCopyable, T))
305 						emplace(&_mptr[index], T.init);
306 					else {
307 						auto _ = T.init;
308 						moveEmplace(_, _mptr[index]);
309 					}
310 				}
311 			} else
312 				_mptr[_store.length .. newLength] = T.init;
313 		}
314 		_store._length = newLength;
315 	}
316 
317 	/// Get capacity.
318 	pragma(inline, true)
319 	@property Capacity capacity() const scope pure nothrow @nogc => _store.capacity;
320 
321 	/** Ensures sufficient capacity to accommodate for minimumCapacity number of
322 		elements. If `minimumCapacity` < `capacity`, this method does nothing.
323 	 */
324 	Capacity reserve(in Capacity minimumCapacity) @trusted scope pure nothrow {
325 		version (D_Coverage) {} else version (LDC) pragma(inline, true);
326 		if (minimumCapacity <= capacity)
327 			return capacity;
328 		return reallocateAndSetCapacity(_growthP * minimumCapacity / _growthQ);
329 		// import std.math.algebraic : nextPow2;
330 		// reallocateAndSetCapacity(minimumCapacity.nextPow2);
331 	}
332 	/** Ensures exactly sufficient capacity to accommodate for minimumCapacity number of
333 		elements. If `minimumCapacity` < `capacity`, this method does nothing.
334 	 */
335 	private Capacity reserveFitLength(in Capacity minimumCapacity) @trusted scope pure nothrow {
336 		version (D_Coverage) {} else version (LDC) pragma(inline, true);
337 		if (minimumCapacity <= capacity)
338 			return capacity;
339 		return reallocateAndSetCapacity(minimumCapacity);
340 		// import std.math.algebraic : nextPow2;
341 		// reallocateAndSetCapacity(minimumCapacity.nextPow2);
342 	}
343 
344 	/// Reallocate storage.
345 	private Capacity reallocateAndSetCapacity()(in Capacity newCapacity) @trusted /*tlm*/ {
346 		static if (mustAddGCRange!T)
347 			gc_removeRange(_store.ptr);
348 
349 		/+ TODO: functionize: +/
350 		auto slice = cast(void[])(_store[]);
351 		const ok = allocator.reallocate(slice, T.sizeof * newCapacity);
352 
353 		assert(ok);
354 
355 		_store = Store(cast(T[])slice, _store.length); /+ TODO: only mutate _store.slice +/
356 
357 		if (_store.ptr is null &&
358 			newCapacity >= 1)
359 			/+ TODO: onOutOfMemoryError(); +/
360 			return _store.capacity;
361 
362 		static if (mustAddGCRange!T)
363 			gc_addRange(_store.ptr, _store.capacity * T.sizeof);
364 
365 		return _store.capacity;
366 	}
367 
368 	/// Slice support.
369 	pragma(inline, true)
370 	inout(T)[] opSlice()(in size_t i, in size_t j) inout return scope @trusted /*tlm*/ => _store.ptr[i .. j];
371 	/// ditto
372 	pragma(inline, true)
373 	inout(T)[] opSlice()() inout return scope @trusted /*tlm*/ => _store.ptr[0 .. _store.length];
374 
375 	/// Slice assignment support.
376 	pragma(inline, true)
377 	inout(T)[] opSliceAssign(U)(scope U value) inout return scope @trusted => cast(T[])(opSlice()[]) = value;
378 	/// ditto
379 	pragma(inline, true)
380 	inout(T)[] opSliceAssign(U)(scope U value, in size_t i, in size_t j) inout return scope => cast(T[])(opSlice()[i .. j]) = value;
381 
382 	/// Index support.
383 	pragma(inline, true)
384 	ref inout(T) opIndex()(in size_t i) inout return scope /*tlm*/ => opSlice()[i];
385 
386 	/// Index assignment support.
387 	ref T opIndexAssign(U)(scope U value, in size_t i) @trusted return scope {
388 		version (D_Coverage) {} else version (LDC) pragma(inline, true);
389 		static if (!__traits(isPOD, T)) {
390 			move(*(cast(MT*)(&value)), _mptr[i]); /+ TODO: is this correct? +/
391 			return opSlice()[i];
392 		} else static if ((is(T == class) || isPointer!T || hasIndirections!T) && !isMutable!T)
393 			static assert(0, "Cannot modify constant elements with indirections");
394 		else
395 			return opSlice()[i] = value;
396 	}
397 
398 	/// Get reference to front element.
399 	pragma(inline, true)
400 	@property ref inout(T) front() inout return scope => opSlice()[0];	  // range-checked by default
401 
402 	/// Get reference to back element.
403 	pragma(inline, true)
404 	@property ref inout(T) back() inout return scope => opSlice()[_store.length - 1]; // range-checked by default
405 
406 	/** Insert `value` into the end of the array.
407 	 */
408 	void insertBack(scope T value) scope @trusted {
409 		version (D_Coverage) {} else version (LDC) pragma(inline, true);
410 		static if (__traits(isPOD, T)) {
411 			reserve(_store.length + 1);
412 			_mptr[_store.length] = value;
413 			_store._length += 1;
414 		} else
415 			insertBackMove(*cast(MT*)(&value));
416 	}
417 
418 	/** Move `value` into the end of the array.
419 	 */
420 	void insertBackMove(scope ref T value) scope @trusted {
421 		version (D_Coverage) {} else version (LDC) pragma(inline, true);
422 		reserve(_store.length + 1);
423 		static if (__traits(isPOD, T))
424 			_mptr[_store.length] = value;
425 		else
426 			moveEmplace(value, _mptr[_store.length]);
427 		_store._length += 1;
428 	}
429 
430 	alias put = insertBack;
431 
432 	/** Insert the elements `values` into the end of the array.
433 	 */
434 	void insertBack(U)(U[] values...) scope @trusted
435 	if (isElementAssignable!U &&
436 		__traits(isCopyable, U)) { // prevent accidental move of l-value `values`
437 		if (values.length == 1) /+ TODO: branch should be detected at compile-time +/
438 			// twice as fast as array assignment below
439 			return insertBack(values[0]);
440 		static if (is(T == immutable(T))) {
441 			/* An array of immutable values cannot overlap with the `this`
442 			   mutable array container data, which entails no need to check for
443 			   overlap.
444 			*/
445 			reserve(_store.length + values.length);
446 			_mptr[_store.length .. _store.length + values.length] = values;
447 		} else {
448 			import nxt.overlapping : overlaps;
449 			if (_store.ptr == values.ptr) { // called for instances as: `this ~= this`
450 				reserve(2*_store.length); // invalidates `values.ptr`
451 				foreach (const i; 0 .. _store.length)
452 					_mptr[_store.length + i] = _store.ptr[i];
453 			} else if (overlaps(this[], values[]))
454 				assert(0, `TODO: Handle overlapping arrays`);
455 			else {
456 				reserve(_store.length + values.length);
457 				_mptr[_store.length .. _store.length + values.length] = values;
458 			}
459 		}
460 		_store._length += values.length;
461 	}
462 
463 	/** Insert the elements `elements` into the end of the array.
464 	 */
465 	void insertBack(R)(scope R elements) @trusted
466 	if (isInsertableContainer!R) {
467 		import std.range.primitives : hasLength;
468 		static if (isInputRange!R &&
469 				   hasLength!R) {
470 			reserve(_store.length + elements.length);
471 			import std.algorithm.mutation : copy;
472 			copy(elements, _mptr[_store.length .. _store.length + elements.length]);
473 			_store.length += elements.length;
474 		} else {
475 			foreach (ref element; move(elements)) /+ TODO: remove `move` when compiler does it for us +/
476 				static if (__traits(isCopyable, ElementType!R))
477 					insertBack(element);
478 				else
479 					insertBackMove(element);
480 		}
481 	}
482 	/// ditto
483 	alias put = insertBack;
484 
485 	/** Remove last value from the end of the array.
486 	 */
487 	void popBack()() @trusted in(length != 0) /*tlm*/ {
488 		version (D_Coverage) {} else pragma(inline, true);
489 		_store._length -= 1;
490 		static if (hasElaborateDestructor!T)
491 			.destroy(_mptr[_store.length]);
492 		else static if (mustAddGCRange!T)
493 			_mptr[_store.length] = T.init; // avoid GC mark-phase dereference
494 	}
495 
496 	/** Rmove `n` last values from the end of the array.
497 
498 		See_Also: http://mir-algorithm.libmir.org/mir_appender.html#.ScopedBuffer.popBackN
499 	 */
500 	void popBackN()(in Capacity n) @trusted in(n <= length) /*tlm*/ {
501 		_store._length -= n;
502 		static if (hasElaborateDestructor!T)
503 			foreach (const index; 0 .. n)
504 				.destroy(_mptr[_store.length + index]);
505 		else static if (mustAddGCRange!T)
506 			foreach (const index; 0 .. n)
507 				_mptr[_store.length + index] = T.init; // avoid GC mark-phase dereference
508 	}
509 
510 	/** Pop back element and return it.
511 
512 		This is well-missed feature of C++'s `std::vector` because of problems
513 		with exception handling. For more details see
514 		https://stackoverflow.com/questions/12600330/pop-back-return-value.
515 	 */
516 	T takeBack()() @trusted in(length != 0) /*tlm*/ {
517 		version (D_Coverage) {} else pragma(inline, true);
518 		_store._length -= 1;
519 		static if (!__traits(isPOD, T))
520 			return move(_mptr[_store.length]);
521 		else static if (is(T == class) || isPointer!T || hasIndirections!T) { // fast, medium, slow path
522 			T e = void;
523 			moveEmplace(_mptr[_store.length], e); // reset any pointers at `back`
524 			return e;
525 		} else
526 			return _mptr[_store.length];
527 	}
528 
529 	/** Pop element at `index`. */
530 	void popAt()(in Capacity index) @trusted @("complexity", "O(length)") /*tlm*/
531 	in(index < this.length) {
532 		static if (hasElaborateDestructor!T)
533 			.destroy(_mptr[index]);
534 		else static if (mustAddGCRange!T)
535 			_mptr[index] = T.init; // avoid GC mark-phase dereference
536 		shiftToFrontAt(index);
537 		_store._length -= 1;
538 	}
539 
540 	/** Move element at `index` to return. */
541 	static if (isMutable!T)
542 		T moveAt()(in Capacity index) @trusted @("complexity", "O(length)") /*tlm*/
543 		in(index < this.length) {
544 			auto value = move(_mptr[index]);
545 			shiftToFrontAt(index);
546 			_store._length -= 1;
547 			return move(value); /+ TODO: remove `move` when compiler does it for us +/
548 		}
549 
550 	/** Move element at front. */
551 	static if (isMutable!T)
552 		pragma(inline, true)
553 		T takeFront()() @("complexity", "O(length)") /*tlm*/
554 			=> moveAt(0);
555 
556 	private void shiftToFrontAt()(in Capacity index) @trusted /*tlm*/ {
557 		/+ TODO: use this instead: +/
558 		// const si = index + 1;   // source index
559 		// const ti = index;	   // target index
560 		// const restLength = this.length - (index + 1);
561 		// import std.algorithm.mutation : moveEmplaceAll;
562 		// moveEmplaceAll(_mptr[si .. si + restLength],
563 		//				_mptr[ti .. ti + restLength]);
564 		foreach (const i; 0 .. this.length - (index + 1)) { // each element index that needs to be moved
565 			const si = index + i + 1; // source index
566 			const ti = index + i; // target index
567 			moveEmplace(_mptr[si], /+ TODO: remove when compiler does this +/
568 						_mptr[ti]);
569 		}
570 	}
571 
572 	/** Forwards to $(D insertBack(values)). */
573 	void opOpAssign(string op)(T value) if (op == "~") {
574 		version (D_Coverage) {} else pragma(inline, true);
575 		insertBackMove(value);
576 	}
577 	/// ditto
578 	void opOpAssign(string op, U)(U[] values...) @trusted if (op == "~" &&
579 		isElementAssignable!U &&
580 		__traits(isCopyable, U))	   // prevent accidental move of l-value `values`
581 	{
582 		version (D_Coverage) {} else pragma(inline, true);
583 		insertBack(values);
584 	}
585 	/// ditto
586 	void opOpAssign(string op, R)(R values) if (op == "~" &&
587 		isInputRange!R &&
588 		!isInfinite!R &&
589 		!isArray!R &&
590 		isElementAssignable!(ElementType!R)) {
591 		version (D_Coverage) {} else pragma(inline, true);
592 		insertBack(values);
593 	}
594 
595 	void opOpAssign(string op)(auto ref typeof(this) values) if (op == "~") {
596 		version (D_Coverage) {} else pragma(inline, true);
597 		insertBack(values[]);
598 	}
599 
600 	/// Unsafe access to store pointer.
601 	pragma(inline, true)
602 	@property inout(T)* ptr() inout return @system => _store.ptr;
603 
604 	/// Convenience access of pointer to mutable store.
605 	pragma(inline, true)
606 	private @property MT* _mptr() const return @trusted => cast(typeof(return))_store.ptr;
607 
608 private:
609 	import nxt.allocator_traits : AllocatorState;
610 	mixin AllocatorState!Allocator; // put first as emsi-containers do
611 
612 	import nxt.container.array_store : ArrayStore;
613 	alias Store = ArrayStore!(T, Allocator, Capacity);
614 	Store _store;
615 }
616 
617 import std.functional : unaryFun;
618 
619 /** Remove all elements matching `predicate`.
620 
621 	Returns: number of elements that were removed.
622 
623 	TODO: implement version that doesn't use a temporary array `tmp`, which is
624 	probably faster for small arrays.
625  */
626 size_t remove(alias predicate, C)(ref C c) @trusted @("complexity", "O(length)")
627 if (is(C == DynamicArray!(_), _...) &&
628 	is(typeof(unaryFun!predicate(C.init[0])))) {
629 	C tmp;
630 	size_t count = 0;
631 	foreach (const i; 0 .. c.length) {
632 		if (unaryFun!predicate(c[i])) {
633 			count += 1;
634 			import core.internal.traits : hasElaborateDestructor;
635 			import nxt.container.traits : mustAddGCRange;
636 			alias T = typeof(c[i]);
637 			static if (hasElaborateDestructor!(T))
638 				.destroy(c[i]);
639 			else static if (mustAddGCRange!(T))
640 				c[i] = T.init;	// avoid GC mark-phase dereference
641 		} else
642 			tmp.insertBackMove(c[i]); /+ TODO: remove unnecessary clearing of `_mptr[i]` +/
643 	}
644 	c.freeStore();
645 	import core.lifetime : moveEmplace;
646 	moveEmplace(tmp, c);
647 	return count;
648 }
649 
650 pure nothrow @safe @nogc unittest {
651 	alias T = uint;
652 
653 	DynamicArray!(T, TestAllocator) s;
654 	assert(s.length == 0);
655 
656 	s.insertBack(13U);
657 	assert(s.length == 1);
658 	assert(s.back == 13);
659 
660 	s.insertBack(14U);
661 	assert(s.length == 2);
662 	assert(s.back == 14);
663 
664 	s.popBack();
665 	assert(s.length == 1);
666 	s.popBack();
667 	assert(s.length == 0);
668 }
669 
670 pure nothrow @safe @nogc unittest {
671 	alias T = int;
672 	alias A = DynamicArray!(T, TestAllocator, uint);
673 	static if (size_t.sizeof == 8) // for 64-bit
674 		static assert(A.sizeof == 2 * size_t.sizeof); // only two words
675 }
676 
677 /// construct and append from slices
678 pure nothrow @safe @nogc unittest {
679 	alias T = int;
680 	alias A = DynamicArray!(T, TestAllocator);
681 	auto a = A([10,11,12].s);
682 	a ~= a[];
683 	assert(a[] == [10,11,12, 10,11,12].s);
684 	a ~= false;
685 	assert(a[] == [10,11,12, 10,11,12, 0].s);
686 }
687 
688 /// construct and append using self
689 pure nothrow @safe @nogc unittest {
690 	alias T = int;
691 	alias A = DynamicArray!(T, TestAllocator);
692 	auto a = A([10,11,12].s);
693 	a ~= a;
694 	assert(a[] == [10,11,12, 10,11,12].s);
695 }
696 
697 ///
698 pure nothrow @safe @nogc unittest {
699 	alias T = int;
700 	alias A = DynamicArray!(T, TestAllocator);
701 	A a;
702 	a.length = 1;
703 	assert(a.length == 1);
704 	assert(a.capacity == 1);
705 
706 	a[0] = 10;
707 	a.insertBack(11, 12);
708 	a ~= T.init;
709 	a.insertBack([3].s);
710 	assert(a[] == [10,11,12, 0, 3].s);
711 
712 	import std.algorithm.iteration : filter;
713 
714 	a.insertBack([42].s[].filter!(_ => _ is 42));
715 	assert(a[] == [10,11,12, 0, 3, 42].s);
716 
717 	a.insertBack([42].s[].filter!(_ => _ !is 42));
718 	assert(a[] == [10,11,12, 0, 3, 42].s);
719 
720 	a ~= a[];
721 	assert(a[] == [10,11,12, 0, 3, 42,
722 				   10,11,12, 0, 3, 42].s);
723 }
724 
725 ///
726 pure nothrow @safe @nogc unittest {
727 	alias T = int;
728 	alias A = DynamicArray!(T);
729 
730 	A a;						// default construction allowed
731 	assert(a.length == 0);
732 	assert(a.length == 0);
733 	assert(a.capacity == 0);
734 	assert(a[] == []);
735 
736 	alias B = DynamicArray!(int, TestAllocator);
737 	B b;
738 	b.length = 3;
739 	assert(b.length != 0);
740 	assert(b.length == 3);
741 	assert(b.capacity == 3);
742 	b[0] = 1;
743 	b[1] = 2;
744 	b[2] = 3;
745 	assert(b[] == [1, 2, 3].s);
746 
747 	b[] = [4, 5, 6].s;
748 	assert(b[] == [4, 5, 6].s);
749 
750 	auto c = DynamicArray!(int, TestAllocator)();
751 	c.reserve(3);
752 	assert(c.length == 0);
753 	assert(c.capacity >= 3);
754 	assert(c[] == []);
755 
756 	version (none) // TODO: enable
757 	static if (hasPreviewDIP1000)
758 		static assert(!__traits(compiles, { T[] f() @safe { A a; return a[]; } }));
759 
760 	const e = DynamicArray!(int, TestAllocator)([1, 2, 3, 4].s);
761 	assert(e.length == 4);
762 	assert(e[] == [1, 2, 3, 4].s);
763 }
764 
765 ///
766 @trusted pure nothrow @nogc unittest {
767 	alias T = int;
768 	alias A = DynamicArray!(T, TestAllocator);
769 
770 	auto a = A([1, 2, 3].s);
771 	A b = a.dupShallow;				// copy construction enabled
772 
773 	assert(a[] == b[]);		  // same content
774 	assert(&a[0] !is &b[0]); // but not the same
775 
776 	assert(b[] == [1, 2, 3].s);
777 	assert(b.length == 3);
778 
779 	b ~= 4;
780 	assert(a != b);
781 	a.clear();
782 	assert(a != b);
783 	b.clear();
784 	assert(a == b);
785 
786 	const c = A([1, 2, 3].s);
787 	assert(c.length == 3);
788 }
789 
790 /// DIP-1000 return ref escape analysis
791 pure nothrow @safe @nogc unittest {
792 	alias T = int;
793 	alias A = DynamicArray!T;
794 	version (none) // TODO: enable
795 	static if (hasPreviewDIP1000)
796 		static assert(!__traits(compiles, { T[] leakSlice() pure nothrow @safe @nogc { A a; return a[]; } }));
797 	T* leakPointer() pure nothrow @safe @nogc {
798 		A a;
799 		return a._store.ptr;	/+ TODO: shouldn't compile with -dip1000 +/
800 	}
801 	const _lp = leakPointer();	/+ TODO: shouldn't compile with -dip1000 +/
802 }
803 
804 /// construct and insert from non-copyable element type passed by value
805 @safe pure nothrow /*@nogc*/ unittest {
806 	alias E = Uncopyable;
807 	alias A = DynamicArray!(E);
808 
809 	A a = A(E(17));
810 	assert(a[] == [E(17)]);
811 
812 	a.insertBack(E(18));
813 	assert(a[] == [E(17),
814 				   E(18)]);
815 
816 	a ~= E(19);
817 	assert(a[] == [E(17),
818 				   E(18),
819 				   E(19)]);
820 }
821 
822 /// construct from slice of uncopyable type
823 pure nothrow @safe @nogc unittest {
824 	alias _A = DynamicArray!(Uncopyable);
825 	/+ TODO: can we safely support this?: A a = [Uncopyable(17)]; +/
826 }
827 
828 // construct from array with uncopyable elements
829 pure nothrow @safe @nogc unittest {
830 	alias A = DynamicArray!(Uncopyable);
831 	const A a;
832 	assert(a.length == 0);
833 	/+ TODO: a.insertBack(A.init); +/
834 	assert(a.length == 0);
835 	const _ = a.toHash;
836 }
837 
838 // construct from ranges of uncopyable elements
839 pure nothrow @safe @nogc unittest {
840 	alias T = Uncopyable;
841 	alias A = DynamicArray!T;
842 	const A a;
843 	assert(a.length == 0);
844 	// import std.algorithm.iteration : map, filter;
845 	// const b = A.withElementsOfRange_untested([10, 20, 30].s[].map!(_ => T(_^^2))); // hasLength
846 	// assert(b.length == 3);
847 	// assert(b == [T(100), T(400), T(900)].s);
848 	// const c = A.withElementsOfRange_untested([10, 20, 30].s[].filter!(_ => _ == 30).map!(_ => T(_^^2))); // !hasLength
849 	// assert(c.length == 1);
850 	// assert(c[0].x == 900);
851 }
852 
853 // construct from ranges of copyable elements
854 pure nothrow @safe @nogc unittest {
855 	alias T = int;
856 	alias A = DynamicArray!(T, TestAllocator);
857 
858 	const A a;
859 	assert(a.length == 0);
860 
861 	import std.algorithm.iteration : map, filter;
862 
863 	const b = A.withElementsOfRange_untested([10, 20, 30].s[].map!(_ => T(_^^2))); // hasLength
864 	assert(b.length == 3);
865 	assert(b == [T(100), T(400), T(900)].s);
866 
867 	() @trusted {			   /+ TODO: remove @trusted +/
868 		const c = A.withElementsOfRange_untested([10, 20, 30].s[].filter!(_ => _ == 30).map!(_ => T(_^^2))); // !hasLength
869 		assert(c == [T(900)].s);
870 	} ();
871 }
872 
873 /// construct with string as element type that needs GC-range
874 pure nothrow @safe @nogc unittest {
875 	alias T = string;
876 	alias A = DynamicArray!(T, TestAllocator);
877 
878 	A a;
879 	a ~= `alpha`;
880 	a ~= `beta`;
881 	a ~= [`gamma`, `delta`].s;
882 	assert(a[] == [`alpha`, `beta`, `gamma`, `delta`].s);
883 
884 	const b = [`epsilon`].s;
885 
886 	a.insertBack(b);
887 	assert(a[] == [`alpha`, `beta`, `gamma`, `delta`, `epsilon`].s);
888 
889 	a ~= b;
890 	assert(a[] == [`alpha`, `beta`, `gamma`, `delta`, `epsilon`, `epsilon`].s);
891 }
892 
893 /// convert to string
894 version (none)				   /+ TODO: make this work +/
895 unittest {
896 	alias T = int;
897 	alias A = DynamicArray!(T);
898 	DynamicArray!char sink;
899 	A([1, 2, 3]).toString(sink.put);
900 }
901 
902 /// iteration over mutable elements
903 pure nothrow @safe @nogc unittest {
904 	alias T = int;
905 	alias A = DynamicArray!(T, TestAllocator);
906 	auto a = A([1, 2, 3].s);
907 	foreach (const i, const e; a)
908 		assert(i + 1 == e);
909 }
910 
911 /// iteration over `const`ant elements
912 pure nothrow @safe @nogc unittest {
913 	alias T = const(int);
914 	alias A = DynamicArray!(T, TestAllocator);
915 	auto a = A([1, 2, 3].s);
916 	foreach (const i, const e; a)
917 		assert(i + 1 == e);
918 }
919 
920 /// iteration over immutable elements
921 pure nothrow @safe @nogc unittest {
922 	alias T = immutable(int);
923 	alias A = DynamicArray!(T, TestAllocator);
924 	auto a = A([1, 2, 3].s);
925 	foreach (const i, const e; a)
926 		assert(i + 1 == e);
927 }
928 
929 /// removal
930 pure nothrow @safe @nogc unittest {
931 	alias T = int;
932 	alias A = DynamicArray!(T, TestAllocator);
933 
934 	auto a = A([1, 2, 3].s);
935 	assert(a == [1, 2, 3].s);
936 
937 	assert(a.takeFront() == 1);
938 	assert(a == [2, 3].s);
939 
940 	a.popAt(1);
941 	assert(a == [2].s);
942 
943 	a.popAt(0);
944 	assert(a == []);
945 
946 	a.insertBack(11);
947 	assert(a == [11].s);
948 
949 	assert(a.takeBack == 11);
950 
951 	a.insertBack(17);
952 	assert(a == [17].s);
953 	a.popBack();
954 	assert(a.length == 0);
955 
956 	a.insertBack([11, 12, 13, 14, 15].s[]);
957 	a.popAt(2);
958 	assert(a == [11, 12, 14, 15].s);
959 	a.popAt(0);
960 	assert(a == [12, 14, 15].s);
961 	a.popAt(2);
962 
963 	assert(a == [12, 14].s);
964 
965 	a ~= a;
966 }
967 
968 /// removal
969 pure nothrow @safe unittest {
970 	import nxt.container.traits : mustAddGCRange;
971 
972 	size_t mallocCount = 0;
973 	size_t freeCount = 0;
974 
975 	struct S {
976 		pure nothrow @safe @nogc:
977 		alias E = int;
978 		import nxt.qcmeman : malloc, free;
979 		this(E x) @trusted {
980 			_ptr = cast(E*)malloc(E.sizeof);
981 			mallocCount += 1;
982 			*_ptr = x;
983 		}
984 		this(this) @disable;
985 		~this() nothrow @trusted @nogc {
986 			free(_ptr);
987 			freeCount += 1;
988 		}
989 		import nxt.gc_traits : NoGc;
990 		@NoGc E* _ptr;
991 	}
992 
993 	/* D compilers cannot currently move stuff efficiently when using
994 	   std.algorithm.mutation.move. A final dtor call to the cleared sourced is
995 	   always done.
996 	*/
997 	const size_t extraDtor = 1;
998 
999 	alias A = DynamicArray!(S, TestAllocator);
1000 	static assert(!mustAddGCRange!A);
1001 	alias AA = DynamicArray!(A, TestAllocator);
1002 	static assert(!mustAddGCRange!AA);
1003 
1004 	assert(mallocCount == 0);
1005 
1006 	{
1007 		A a;
1008 		a.insertBack(S(11));
1009 		assert(mallocCount == 1);
1010 		assert(freeCount == extraDtor + 0);
1011 	}
1012 
1013 	assert(freeCount == extraDtor + 1);
1014 
1015 	// assert(a.front !is S(11));
1016 	// assert(a.back !is S(11));
1017 	// a.insertBack(S(12));
1018 }
1019 
1020 /// test `OutputRange` behaviour with std.format
1021 version (none)				   /+ TODO: replace with other exercise of std.format +/
1022 @safe pure /*TODO: nothrow @nogc*/ unittest {
1023 	import std.format : formattedWrite;
1024 	const x = "42";
1025 	alias A = DynamicArray!(char);
1026 	A a;
1027 	a.formattedWrite!("x : %s")(x);
1028 	assert(a == "x : 42");
1029 }
1030 
1031 pure nothrow @safe @nogc unittest {
1032 	alias T = int;
1033 	alias A = DynamicArray!(T, TestAllocator, uint);
1034 	const a = A(17);
1035 	assert(a[] == [17].s);
1036 }
1037 
1038 /// check duplication via `dupShallow`
1039 @trusted pure nothrow @nogc unittest {
1040 	alias T = int;
1041 	alias A = DynamicArray!(T, TestAllocator);
1042 	static assert(!__traits(compiles, { A b = a; })); // copying disabled
1043 	auto a = A([10,11,12].s);
1044 	auto b = a.dupShallow;
1045 	assert(a == b);
1046 	assert(&a[0] !is &b[0]);
1047 }
1048 
1049 /// element type is a class
1050 pure nothrow @safe unittest {
1051 	class T {
1052 		this (int x) { this.x = x; }
1053 		~this() nothrow @nogc { x = 42; }
1054 		int x;
1055 	}
1056 	alias A = DynamicArray!(T, TestAllocator);
1057 	auto a = A([new T(10), new T(11), new T(12)].s);
1058 	assert(a.length == 3);
1059 	a.remove!(_ => _.x == 12);
1060 	assert(a.length == 2);
1061 }
1062 
1063 /// check filtered removal via `remove`
1064 pure nothrow @safe @nogc unittest {
1065 	struct T { int value; }
1066 	alias A = DynamicArray!(T, TestAllocator);
1067 	static assert(!__traits(compiles, { A b = a; })); // copying disabled
1068 
1069 	auto a = A([T(10), T(11), T(12)].s);
1070 
1071 	assert(a.remove!"a.value == 13" == 0);
1072 	assert(a[] == [T(10), T(11), T(12)].s);
1073 
1074 	assert(a.remove!"a.value >= 12" == 1);
1075 	assert(a[] == [T(10), T(11)].s);
1076 
1077 	assert(a.remove!(_ => _.value == 10) == 1);
1078 	assert(a[] == [T(11)].s);
1079 
1080 	assert(a.remove!(_ => _.value == 11) == 1);
1081 	assert(a.length == 0);
1082 }
1083 
1084 /// construct from map range
1085 pure nothrow @safe unittest {
1086 	import std.algorithm.iteration : map;
1087 	alias T = int;
1088 	alias A = DynamicArray!(T, TestAllocator);
1089 
1090 	A a = A.withElementsOfRange_untested([10, 20, 30].s[].map!(_ => _^^2));
1091 	assert(a[] == [100, 400, 900].s);
1092 	a.popBackN(2);
1093 	assert(a.length == 1);
1094 	a.popBackN(1);
1095 	assert(a.length == 0);
1096 
1097 	A b = A([10, 20, 30].s[].map!(_ => _^^2));
1098 	assert(b[] == [100, 400, 900].s);
1099 	b.popBackN(2);
1100 	assert(b.length == 1);
1101 	b.popBackN(1);
1102 	assert(b.length == 0);
1103 
1104 	A c = A([10, 20, 30].s[]);
1105 	assert(c[] == [10, 20, 30].s);
1106 }
1107 
1108 /// construct from map range
1109 @trusted pure nothrow unittest {
1110 	alias T = int;
1111 	alias A = DynamicArray!(T, TestAllocator);
1112 
1113 	import std.typecons : RefCounted;
1114 	RefCounted!A x;
1115 
1116 	auto z = [1, 2, 3].s;
1117 	x ~= z[];
1118 
1119 	auto y = x;
1120 	assert(y[] == z);
1121 
1122 	const _ = x.toHash;
1123 }
1124 
1125 /// construct from static array
1126 @trusted pure nothrow @nogc unittest {
1127 	alias T = uint;
1128 	alias A = DynamicArray!(T, TestAllocator);
1129 
1130 	ushort[3] a = [1, 2, 3];
1131 
1132 	const x = A(a);
1133 	assert(x == a);
1134 	assert(x == a[]);
1135 }
1136 
1137 /// construct from static array slice
1138 @trusted pure nothrow @nogc unittest {
1139 	alias T = uint;
1140 	alias A = DynamicArray!(T, TestAllocator);
1141 	ushort[3] a = [1, 2, 3];
1142 	ushort[] b = a[];
1143 	const y = A(b); // cannot construct directly from `a[]` because its type is `ushort[3]`
1144 	assert(y == a);
1145 	assert(y == a[]);
1146 }
1147 
1148 /// GCAllocator
1149 @trusted pure nothrow unittest {
1150 	import std.experimental.allocator.gc_allocator : GCAllocator;
1151 	alias T = int;
1152 	alias A = DynamicArray!(T, GCAllocator);
1153 	const A a;
1154 	assert(a.length == 0);
1155 }
1156 
1157 /// construct with slices as element types
1158 @trusted pure nothrow unittest {
1159 	alias A = DynamicArray!(string);
1160 	const A a;
1161 	assert(a.length == 0);
1162 	alias B = DynamicArray!(char[]);
1163 	const B b;
1164 	assert(b.length == 0);
1165 }
1166 
1167 /** Variant of `DynamicArray` with copy construction (postblit) enabled.
1168  *
1169  * See_Also: suppressing.d
1170  * See_Also: http://forum.dlang.org/post/eitlbtfbavdphbvplnrk@forum.dlang.org
1171  */
1172 struct BasicCopyableArray {
1173 	/** TODO: implement using instructions at:
1174 	 * http://forum.dlang.org/post/eitlbtfbavdphbvplnrk@forum.dlang.org
1175 	 */
1176 }
1177 
1178 //+ TODO: Move to Phobos. +/
1179 private enum bool isRefIterable(T) = is(typeof({ foreach (ref elem; T.init) {} }));
1180 
1181 version (unittest) {
1182 	import std.experimental.allocator.mallocator : TestAllocator = Mallocator;
1183 	import nxt.array_help : s;
1184 	import nxt.dip_traits : hasPreviewDIP1000;
1185 	import nxt.construction : dupShallow;
1186 	import nxt.debugio;
1187 	private static struct Uncopyable { this(this) @disable; int _x; }
1188 }