number of elements that were removed.
TODO: implement version that doesn't use a temporary array tmp, which is probably faster for small arrays.
construct and append from slices
alias T = int; alias A = DynamicArray!(T, TestAllocator); auto a = A([10,11,12].s); a ~= a[]; assert(a[] == [10,11,12, 10,11,12].s); a ~= false; assert(a[] == [10,11,12, 10,11,12, 0].s);
construct and append using self
alias T = int; alias A = DynamicArray!(T, TestAllocator); auto a = A([10,11,12].s); a ~= a; assert(a[] == [10,11,12, 10,11,12].s);
alias T = int; alias A = DynamicArray!(T, TestAllocator); A a; a.length = 1; assert(a.length == 1); assert(a.capacity == 1); a[0] = 10; a.insertBack(11, 12); a ~= T.init; a.insertBack([3].s); assert(a[] == [10,11,12, 0, 3].s); import std.algorithm.iteration : filter; a.insertBack([42].s[].filter!(_ => _ is 42)); assert(a[] == [10,11,12, 0, 3, 42].s); a.insertBack([42].s[].filter!(_ => _ !is 42)); assert(a[] == [10,11,12, 0, 3, 42].s); a ~= a[]; assert(a[] == [10,11,12, 0, 3, 42, 10,11,12, 0, 3, 42].s);
alias T = int; alias A = DynamicArray!(T); A a; // default construction allowed assert(a.length == 0); assert(a.length == 0); assert(a.capacity == 0); assert(a[] == []); alias B = DynamicArray!(int, TestAllocator); B b; b.length = 3; assert(b.length != 0); assert(b.length == 3); assert(b.capacity == 3); b[0] = 1; b[1] = 2; b[2] = 3; assert(b[] == [1, 2, 3].s); b[] = [4, 5, 6].s; assert(b[] == [4, 5, 6].s); auto c = DynamicArray!(int, TestAllocator)(); c.reserve(3); assert(c.length == 0); assert(c.capacity >= 3); assert(c[] == []); version (none) // TODO: enable static if (hasPreviewDIP1000) static assert(!__traits(compiles, { T[] f() @safe { A a; return a[]; } })); const e = DynamicArray!(int, TestAllocator)([1, 2, 3, 4].s); assert(e.length == 4); assert(e[] == [1, 2, 3, 4].s);
alias T = int; alias A = DynamicArray!(T, TestAllocator); auto a = A([1, 2, 3].s); A b = a.dupShallow; // copy construction enabled assert(a[] == b[]); // same content assert(&a[0] !is &b[0]); // but not the same assert(b[] == [1, 2, 3].s); assert(b.length == 3); b ~= 4; assert(a != b); a.clear(); assert(a != b); b.clear(); assert(a == b); const c = A([1, 2, 3].s); assert(c.length == 3);
DIP-1000 return ref escape analysis
alias T = int; alias A = DynamicArray!T; version (none) // TODO: enable static if (hasPreviewDIP1000) static assert(!__traits(compiles, { T[] leakSlice() pure nothrow @safe @nogc { A a; return a[]; } })); T* leakPointer() pure nothrow @safe @nogc { A a; return a._store.ptr; /+ TODO: shouldn't compile with -dip1000 +/ } const _lp = leakPointer(); /+ TODO: shouldn't compile with -dip1000 +/
construct and insert from non-copyable element type passed by value
alias E = Uncopyable; alias A = DynamicArray!(E); A a = A(E(17)); assert(a[] == [E(17)]); a.insertBack(E(18)); assert(a[] == [E(17), E(18)]); a ~= E(19); assert(a[] == [E(17), E(18), E(19)]);
construct from slice of uncopyable type
alias _A = DynamicArray!(Uncopyable); /+ TODO: can we safely support this?: A a = [Uncopyable(17)]; +/
construct with string as element type that needs GC-range
alias T = string; alias A = DynamicArray!(T, TestAllocator); A a; a ~= `alpha`; a ~= `beta`; a ~= [`gamma`, `delta`].s; assert(a[] == [`alpha`, `beta`, `gamma`, `delta`].s); const b = [`epsilon`].s; a.insertBack(b); assert(a[] == [`alpha`, `beta`, `gamma`, `delta`, `epsilon`].s); a ~= b; assert(a[] == [`alpha`, `beta`, `gamma`, `delta`, `epsilon`, `epsilon`].s);
convert to string
alias T = int; alias A = DynamicArray!(T); DynamicArray!char sink; A([1, 2, 3]).toString(sink.put);
iteration over mutable elements
alias T = int; alias A = DynamicArray!(T, TestAllocator); auto a = A([1, 2, 3].s); foreach (const i, const e; a) assert(i + 1 == e);
iteration over constant elements
alias T = const(int); alias A = DynamicArray!(T, TestAllocator); auto a = A([1, 2, 3].s); foreach (const i, const e; a) assert(i + 1 == e);
iteration over immutable elements
alias T = immutable(int); alias A = DynamicArray!(T, TestAllocator); auto a = A([1, 2, 3].s); foreach (const i, const e; a) assert(i + 1 == e);
removal
alias T = int; alias A = DynamicArray!(T, TestAllocator); auto a = A([1, 2, 3].s); assert(a == [1, 2, 3].s); assert(a.takeFront() == 1); assert(a == [2, 3].s); a.popAt(1); assert(a == [2].s); a.popAt(0); assert(a == []); a.insertBack(11); assert(a == [11].s); assert(a.takeBack == 11); a.insertBack(17); assert(a == [17].s); a.popBack(); assert(a.length == 0); a.insertBack([11, 12, 13, 14, 15].s[]); a.popAt(2); assert(a == [11, 12, 14, 15].s); a.popAt(0); assert(a == [12, 14, 15].s); a.popAt(2); assert(a == [12, 14].s); a ~= a;
removal
import nxt.container.traits : mustAddGCRange; size_t mallocCount = 0; size_t freeCount = 0; struct S { pure nothrow @safe @nogc: alias E = int; import nxt.qcmeman : malloc, free; this(E x) @trusted { _ptr = cast(E*)malloc(E.sizeof); mallocCount += 1; *_ptr = x; } this(this) @disable; ~this() nothrow @trusted @nogc { free(_ptr); freeCount += 1; } import nxt.gc_traits : NoGc; @NoGc E* _ptr; } /* D compilers cannot currently move stuff efficiently when using std.algorithm.mutation.move. A final dtor call to the cleared sourced is always done. */ const size_t extraDtor = 1; alias A = DynamicArray!(S, TestAllocator); static assert(!mustAddGCRange!A); alias AA = DynamicArray!(A, TestAllocator); static assert(!mustAddGCRange!AA); assert(mallocCount == 0); { A a; a.insertBack(S(11)); assert(mallocCount == 1); assert(freeCount == extraDtor + 0); } assert(freeCount == extraDtor + 1); // assert(a.front !is S(11)); // assert(a.back !is S(11)); // a.insertBack(S(12));
test OutputRange behaviour with std.format
import std.format : formattedWrite; const x = "42"; alias A = DynamicArray!(char); A a; a.formattedWrite!("x : %s")(x); assert(a == "x : 42");
check duplication via dupShallow
alias T = int; alias A = DynamicArray!(T, TestAllocator); static assert(!__traits(compiles, { A b = a; })); // copying disabled auto a = A([10,11,12].s); auto b = a.dupShallow; assert(a == b); assert(&a[0] !is &b[0]);
element type is a class
class T { this (int x) { this.x = x; } ~this() nothrow @nogc { x = 42; } int x; } alias A = DynamicArray!(T, TestAllocator); auto a = A([new T(10), new T(11), new T(12)].s); assert(a.length == 3); a.remove!(_ => _.x == 12); assert(a.length == 2);
check filtered removal via remove
struct T { int value; } alias A = DynamicArray!(T, TestAllocator); static assert(!__traits(compiles, { A b = a; })); // copying disabled auto a = A([T(10), T(11), T(12)].s); assert(a.remove!"a.value == 13" == 0); assert(a[] == [T(10), T(11), T(12)].s); assert(a.remove!"a.value >= 12" == 1); assert(a[] == [T(10), T(11)].s); assert(a.remove!(_ => _.value == 10) == 1); assert(a[] == [T(11)].s); assert(a.remove!(_ => _.value == 11) == 1); assert(a.length == 0);
construct from map range
import std.algorithm.iteration : map; alias T = int; alias A = DynamicArray!(T, TestAllocator); A a = A.withElementsOfRange_untested([10, 20, 30].s[].map!(_ => _^^2)); assert(a[] == [100, 400, 900].s); a.popBackN(2); assert(a.length == 1); a.popBackN(1); assert(a.length == 0); A b = A([10, 20, 30].s[].map!(_ => _^^2)); assert(b[] == [100, 400, 900].s); b.popBackN(2); assert(b.length == 1); b.popBackN(1); assert(b.length == 0); A c = A([10, 20, 30].s[]); assert(c[] == [10, 20, 30].s);
construct from map range
alias T = int; alias A = DynamicArray!(T, TestAllocator); import std.typecons : RefCounted; RefCounted!A x; auto z = [1, 2, 3].s; x ~= z[]; auto y = x; assert(y[] == z); const _ = x.toHash;
construct from static array
alias T = uint; alias A = DynamicArray!(T, TestAllocator); ushort[3] a = [1, 2, 3]; const x = A(a); assert(x == a); assert(x == a[]);
construct from static array slice
alias T = uint; alias A = DynamicArray!(T, TestAllocator); ushort[3] a = [1, 2, 3]; ushort[] b = a[]; const y = A(b); // cannot construct directly from `a[]` because its type is `ushort[3]` assert(y == a); assert(y == a[]);
GCAllocator
import std.experimental.allocator.gc_allocator : GCAllocator; alias T = int; alias A = DynamicArray!(T, GCAllocator); const A a; assert(a.length == 0);
construct with slices as element types
alias A = DynamicArray!(string); const A a; assert(a.length == 0); alias B = DynamicArray!(char[]); const B b; assert(b.length == 0);
Remove all elements matching predicate.