/std/array.d
http://github.com/jcd/phobos · D · 3150 lines · 2166 code · 323 blank · 661 comment · 397 complexity · 9c21ab7ef269da24c40f6560b3dd75ee MD5 · raw file
Large files are truncated click here to view the full file
- // Written in the D programming language.
- /**
- Functions and types that manipulate built-in arrays.
- Copyright: Copyright Andrei Alexandrescu 2008- and Jonathan M Davis 2011-.
- License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: $(WEB erdani.org, Andrei Alexandrescu) and Jonathan M Davis
- Source: $(PHOBOSSRC std/_array.d)
- */
- module std.array;
- import core.memory, core.bitop;
- import std.algorithm, std.ascii, std.conv, std.exception, std.functional,
- std.range, std.string, std.traits, std.typecons, std.typetuple,
- std.uni, std.utf;
- import std.c.string : memcpy;
- version(unittest) import core.exception, std.stdio;
- /**
- Returns a newly-allocated dynamic array consisting of a copy of the
- input range, static array, dynamic array, or class or struct with an
- $(D opApply) function $(D r). Note that narrow strings are handled as
- a special case in an overload.
- */
- ForeachType!Range[] array(Range)(Range r)
- if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
- {
- alias ForeachType!Range E;
- static if (hasLength!Range)
- {
- if(r.length == 0) return null;
- static auto trustedAllocateArray(size_t n) @trusted nothrow
- {
- return uninitializedArray!(Unqual!E[])(n);
- }
- auto result = trustedAllocateArray(r.length);
- size_t i;
- static auto trustedGetAddr(T)(ref T t) @trusted nothrow pure
- {
- return &t;
- }
- foreach (e; r)
- {
- emplace(trustedGetAddr(result[i]), e);
- ++i;
- }
- return cast(E[])result;
- }
- else
- {
- auto a = appender!(E[])();
- foreach (e; r)
- {
- a.put(e);
- }
- return a.data;
- }
- }
- ///
- @safe pure nothrow unittest
- {
- auto a = array([1, 2, 3, 4, 5][]);
- assert(a == [ 1, 2, 3, 4, 5 ]);
- }
- @safe pure nothrow unittest
- {
- struct Foo
- {
- int a;
- }
- auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
- assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
- }
- @system unittest
- {
- struct Foo
- {
- int a;
- auto opAssign(Foo foo)
- {
- assert(0);
- }
- auto opEquals(Foo foo)
- {
- return a == foo.a;
- }
- }
- auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
- assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
- }
- /**
- Convert a narrow string to an array type that fully supports random access.
- This is handled as a special case and always returns a $(D dchar[]),
- $(D const(dchar)[]), or $(D immutable(dchar)[]) depending on the constness of
- the input.
- */
- ElementType!String[] array(String)(String str) if (isNarrowString!String)
- {
- return to!(typeof(return))(str);
- }
- unittest
- {
- static struct TestArray { int x; string toString() { return .to!string(x); } }
- static struct OpAssign
- {
- uint num;
- this(uint num) { this.num = num; }
- // Templating opAssign to make sure the bugs with opAssign being
- // templated are fixed.
- void opAssign(T)(T rhs) { this.num = rhs.num; }
- }
- static struct OpApply
- {
- int opApply(int delegate(ref int) dg)
- {
- int res;
- foreach(i; 0..10)
- {
- res = dg(i);
- if(res) break;
- }
- return res;
- }
- }
- auto a = array([1, 2, 3, 4, 5][]);
- //writeln(a);
- assert(a == [ 1, 2, 3, 4, 5 ]);
- auto b = array([TestArray(1), TestArray(2)][]);
- //writeln(b);
- class C
- {
- int x;
- this(int y) { x = y; }
- override string toString() const { return .to!string(x); }
- }
- auto c = array([new C(1), new C(2)][]);
- //writeln(c);
- auto d = array([1.0, 2.2, 3][]);
- assert(is(typeof(d) == double[]));
- //writeln(d);
- auto e = [OpAssign(1), OpAssign(2)];
- auto f = array(e);
- assert(e == f);
- assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
- assert(array("ABC") == "ABC"d);
- assert(array("ABC".dup) == "ABC"d.dup);
- }
- //Bug# 8233
- unittest
- {
- assert(array("hello world"d) == "hello world"d);
- immutable a = [1, 2, 3, 4, 5];
- assert(array(a) == a);
- const b = a;
- assert(array(b) == a);
- //To verify that the opAssign branch doesn't get screwed up by using Unqual.
- //EDIT: array no longer calls opAssign.
- struct S
- {
- ref S opAssign(S)(const ref S rhs)
- {
- assert(0);
- }
- int i;
- }
- foreach(T; TypeTuple!(S, const S, immutable S))
- {
- auto arr = [T(1), T(2), T(3), T(4)];
- assert(array(arr) == arr);
- }
- }
- unittest
- {
- //9824
- static struct S
- {
- @disable void opAssign(S);
- int i;
- }
- auto arr = [S(0), S(1), S(2)];
- arr.array();
- }
- // Bugzilla 10220
- unittest
- {
- import std.algorithm : equal;
- import std.range : repeat;
- static struct S
- {
- int val;
- @disable this();
- this(int v) { val = v; }
- }
- assertCTFEable!(
- {
- auto r = S(1).repeat(2).array();
- assert(equal(r, [S(1), S(1)]));
- });
- }
- unittest
- {
- //Turn down infinity:
- static assert(!is(typeof(
- repeat(1).array()
- )));
- }
- /**
- Returns a newly allocated associative array out of elements of the input range,
- which must be a range of tuples (Key, Value).
- */
- auto assocArray(Range)(Range r)
- if (isInputRange!Range && isTuple!(ElementType!Range) &&
- ElementType!Range.length == 2)
- {
- alias ElementType!Range.Types[0] KeyType;
- alias ElementType!Range.Types[1] ValueType;
- ValueType[KeyType] aa;
- foreach (t; r)
- aa[t[0]] = t[1];
- return aa;
- }
- ///
- /*@safe*/ pure /*nothrow*/ unittest
- {
- auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"]));
- assert(is(typeof(a) == string[int]));
- assert(a == [0:"a", 1:"b", 2:"c"]);
- auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
- assert(is(typeof(b) == string[string]));
- assert(b == ["foo":"bar", "baz":"quux"]);
- }
- /// @@@11053@@@ - Cannot be version(unittest) - recursive instantiation error
- unittest
- {
- static assert(!__traits(compiles, [ tuple("foo", "bar", "baz") ].assocArray()));
- static assert(!__traits(compiles, [ tuple("foo") ].assocArray()));
- static assert( __traits(compiles, [ tuple("foo", "bar") ].assocArray()));
- }
- private template blockAttribute(T)
- {
- static if (hasIndirections!(T) || is(T == void))
- {
- enum blockAttribute = 0;
- }
- else
- {
- enum blockAttribute = GC.BlkAttr.NO_SCAN;
- }
- }
- version(unittest)
- {
- static assert(!(blockAttribute!void & GC.BlkAttr.NO_SCAN));
- }
- // Returns the number of dimensions in an array T.
- private template nDimensions(T)
- {
- static if(isArray!T)
- {
- enum nDimensions = 1 + nDimensions!(typeof(T.init[0]));
- }
- else
- {
- enum nDimensions = 0;
- }
- }
- version(unittest)
- {
- static assert(nDimensions!(uint[]) == 1);
- static assert(nDimensions!(float[][]) == 2);
- }
- /**
- Returns a new array of type $(D T) allocated on the garbage collected heap
- without initializing its elements. This can be a useful optimization if every
- element will be immediately initialized. $(D T) may be a multidimensional
- array. In this case sizes may be specified for any number of dimensions from 1
- to the number in $(D T).
- */
- auto uninitializedArray(T, I...)(I sizes)
- if(allSatisfy!(isIntegral, I))
- {
- return arrayAllocImpl!(false, T, I)(sizes);
- }
- ///
- unittest
- {
- double[] arr = uninitializedArray!(double[])(100);
- assert(arr.length == 100);
- double[][] matrix = uninitializedArray!(double[][])(42, 31);
- assert(matrix.length == 42);
- assert(matrix[0].length == 31);
- }
- /**
- Returns a new array of type $(D T) allocated on the garbage collected heap.
- Initialization is guaranteed only for pointers, references and slices,
- for preservation of memory safety.
- */
- auto minimallyInitializedArray(T, I...)(I sizes) @trusted
- if(allSatisfy!(isIntegral, I))
- {
- return arrayAllocImpl!(true, T, I)(sizes);
- }
- @safe unittest
- {
- double[] arr = minimallyInitializedArray!(double[])(100);
- assert(arr.length == 100);
- double[][] matrix = minimallyInitializedArray!(double[][])(42);
- assert(matrix.length == 42);
- foreach(elem; matrix)
- {
- assert(elem.ptr is null);
- }
- }
- private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes)
- if(allSatisfy!(isIntegral, I))
- {
- static assert(sizes.length >= 1,
- "Cannot allocate an array without the size of at least the first " ~
- " dimension.");
- static assert(sizes.length <= nDimensions!T,
- to!string(sizes.length) ~ " dimensions specified for a " ~
- to!string(nDimensions!T) ~ " dimensional array.");
- alias typeof(T.init[0]) E;
- auto ptr = (__ctfe) ?
- {
- static if(__traits(compiles, new E[1]))
- {
- return (new E[sizes[0]]).ptr;
- }
- else
- {
- E[] arr;
- foreach (i; 0 .. sizes[0])
- arr ~= E.init;
- return arr.ptr;
- }
- }() :
- cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!(E));
- auto ret = ptr[0..sizes[0]];
- static if(sizes.length > 1)
- {
- foreach(ref elem; ret)
- {
- elem = uninitializedArray!(E)(sizes[1..$]);
- }
- }
- else static if(minimallyInitialized && hasIndirections!E)
- {
- ret[] = E.init;
- }
- return ret;
- }
- /**
- Implements the range interface primitive $(D empty) for built-in
- arrays. Due to the fact that nonmember functions can be called with
- the first argument using the dot notation, $(D array.empty) is
- equivalent to $(D empty(array)).
- */
- @property bool empty(T)(in T[] a) @safe pure nothrow
- {
- return !a.length;
- }
- ///
- @safe pure nothrow unittest
- {
- auto a = [ 1, 2, 3 ];
- assert(!a.empty);
- assert(a[3 .. $].empty);
- }
- /**
- Implements the range interface primitive $(D save) for built-in
- arrays. Due to the fact that nonmember functions can be called with
- the first argument using the dot notation, $(D array.save) is
- equivalent to $(D save(array)). The function does not duplicate the
- content of the array, it simply returns its argument.
- */
- @property T[] save(T)(T[] a) @safe pure nothrow
- {
- return a;
- }
- ///
- @safe pure nothrow unittest
- {
- auto a = [ 1, 2, 3 ];
- auto b = a.save;
- assert(b is a);
- }
- /**
- Implements the range interface primitive $(D popFront) for built-in
- arrays. Due to the fact that nonmember functions can be called with
- the first argument using the dot notation, $(D array.popFront) is
- equivalent to $(D popFront(array)). For $(GLOSSARY narrow strings),
- $(D popFront) automaticaly advances to the next $(GLOSSARY code
- point).
- */
- void popFront(T)(ref T[] a) @safe pure nothrow
- if (!isNarrowString!(T[]) && !is(T[] == void[]))
- {
- assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof);
- a = a[1 .. $];
- }
- ///
- @safe pure nothrow unittest
- {
- auto a = [ 1, 2, 3 ];
- a.popFront();
- assert(a == [ 2, 3 ]);
- }
- version(unittest)
- {
- static assert(!is(typeof({ int[4] a; popFront(a); })));
- static assert(!is(typeof({ immutable int[] a; popFront(a); })));
- static assert(!is(typeof({ void[] a; popFront(a); })));
- }
- // Specialization for narrow strings. The necessity of
- void popFront(C)(ref C[] str) @trusted pure nothrow
- if (isNarrowString!(C[]))
- {
- assert(str.length, "Attempting to popFront() past the end of an array of " ~ C.stringof);
- static if(is(Unqual!C == char))
- {
- immutable c = str[0];
- if(c < 0x80)
- {
- //ptr is used to avoid unnnecessary bounds checking.
- str = str.ptr[1 .. str.length];
- }
- else
- {
- import core.bitop;
- auto msbs = 7 - bsr(~c);
- if((msbs < 2) | (msbs > 6))
- {
- //Invalid UTF-8
- msbs = 1;
- }
- str = str[msbs .. $];
- }
- }
- else static if(is(Unqual!C == wchar))
- {
- immutable u = str[0];
- str = str[1 + (u >= 0xD800 && u <= 0xDBFF) .. $];
- }
- else static assert(0, "Bad template constraint.");
- }
- @safe pure unittest
- {
- foreach(S; TypeTuple!(string, wstring, dstring))
- {
- S s = "\xC2\xA9hello";
- s.popFront();
- assert(s == "hello");
- S str = "hello\U00010143\u0100\U00010143";
- foreach(dchar c; ['h', 'e', 'l', 'l', 'o', '\U00010143', '\u0100', '\U00010143'])
- {
- assert(str.front == c);
- str.popFront();
- }
- assert(str.empty);
- static assert(!is(typeof({ immutable S a; popFront(a); })));
- static assert(!is(typeof({ typeof(S.init[0])[4] a; popFront(a); })));
- }
- C[] _eatString(C)(C[] str)
- {
- while(!str.empty)
- str.popFront();
- return str;
- }
- enum checkCTFE = _eatString("ウェブサイト@La_Verité.com");
- static assert(checkCTFE.empty);
- enum checkCTFEW = _eatString("ウェブサイト@La_Verité.com"w);
- static assert(checkCTFEW.empty);
- }
- /**
- Implements the range interface primitive $(D popBack) for built-in
- arrays. Due to the fact that nonmember functions can be called with
- the first argument using the dot notation, $(D array.popBack) is
- equivalent to $(D popBack(array)). For $(GLOSSARY narrow strings), $(D
- popFront) automaticaly eliminates the last $(GLOSSARY code point).
- */
- void popBack(T)(ref T[] a) @safe pure nothrow
- if (!isNarrowString!(T[]) && !is(T[] == void[]))
- {
- assert(a.length);
- a = a[0 .. $ - 1];
- }
- ///
- @safe pure nothrow unittest
- {
- auto a = [ 1, 2, 3 ];
- a.popBack();
- assert(a == [ 1, 2 ]);
- }
- version(unittest)
- {
- static assert(!is(typeof({ immutable int[] a; popBack(a); })));
- static assert(!is(typeof({ int[4] a; popBack(a); })));
- static assert(!is(typeof({ void[] a; popBack(a); })));
- }
- // Specialization for arrays of char
- void popBack(T)(ref T[] a) @safe pure
- if (isNarrowString!(T[]))
- {
- assert(a.length, "Attempting to popBack() past the front of an array of " ~ T.stringof);
- a = a[0 .. $ - std.utf.strideBack(a, $)];
- }
- @safe pure unittest
- {
- foreach(S; TypeTuple!(string, wstring, dstring))
- {
- S s = "hello\xE2\x89\xA0";
- s.popBack();
- assert(s == "hello");
- S s3 = "\xE2\x89\xA0";
- auto c = s3.back;
- assert(c == cast(dchar)'\u2260');
- s3.popBack();
- assert(s3 == "");
- S str = "\U00010143\u0100\U00010143hello";
- foreach(dchar ch; ['o', 'l', 'l', 'e', 'h', '\U00010143', '\u0100', '\U00010143'])
- {
- assert(str.back == ch);
- str.popBack();
- }
- assert(str.empty);
- static assert(!is(typeof({ immutable S a; popBack(a); })));
- static assert(!is(typeof({ typeof(S.init[0])[4] a; popBack(a); })));
- }
- }
- /**
- Implements the range interface primitive $(D front) for built-in
- arrays. Due to the fact that nonmember functions can be called with
- the first argument using the dot notation, $(D array.front) is
- equivalent to $(D front(array)). For $(GLOSSARY narrow strings), $(D
- front) automaticaly returns the first $(GLOSSARY code point) as a $(D
- dchar).
- */
- @property ref T front(T)(T[] a) @safe pure nothrow
- if (!isNarrowString!(T[]) && !is(T[] == void[]))
- {
- assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof);
- return a[0];
- }
- ///
- @safe pure nothrow unittest
- {
- int[] a = [ 1, 2, 3 ];
- assert(a.front == 1);
- }
- @safe pure nothrow unittest
- {
- auto a = [ 1, 2 ];
- a.front = 4;
- assert(a.front == 4);
- assert(a == [ 4, 2 ]);
- immutable b = [ 1, 2 ];
- assert(b.front == 1);
- int[2] c = [ 1, 2 ];
- assert(c.front == 1);
- }
- @property dchar front(T)(T[] a) @safe pure if (isNarrowString!(T[]))
- {
- assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof);
- size_t i = 0;
- return decode(a, i);
- }
- /**
- Implements the range interface primitive $(D back) for built-in
- arrays. Due to the fact that nonmember functions can be called with
- the first argument using the dot notation, $(D array.back) is
- equivalent to $(D back(array)). For $(GLOSSARY narrow strings), $(D
- back) automaticaly returns the last $(GLOSSARY code point) as a $(D
- dchar).
- */
- @property ref T back(T)(T[] a) @safe pure nothrow if (!isNarrowString!(T[]))
- {
- assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof);
- return a[$ - 1];
- }
- ///
- @safe pure nothrow unittest
- {
- int[] a = [ 1, 2, 3 ];
- assert(a.back == 3);
- a.back += 4;
- assert(a.back == 7);
- }
- @safe pure nothrow unittest
- {
- immutable b = [ 1, 2, 3 ];
- assert(b.back == 3);
- int[3] c = [ 1, 2, 3 ];
- assert(c.back == 3);
- }
- // Specialization for strings
- @property dchar back(T)(T[] a) @safe pure if (isNarrowString!(T[]))
- {
- assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof);
- size_t i = a.length - std.utf.strideBack(a, a.length);
- return decode(a, i);
- }
- // overlap
- /*
- NOTE: Undocumented for now, overlap does not yet work with ctfe.
- Returns the overlapping portion, if any, of two arrays. Unlike $(D
- equal), $(D overlap) only compares the pointers in the ranges, not the
- values referred by them. If $(D r1) and $(D r2) have an overlapping
- slice, returns that slice. Otherwise, returns the null slice.
- */
- inout(T)[] overlap(T)(inout(T)[] r1, inout(T)[] r2) @trusted pure nothrow
- {
- alias inout(T) U;
- static U* max(U* a, U* b) nothrow { return a > b ? a : b; }
- static U* min(U* a, U* b) nothrow { return a < b ? a : b; }
- auto b = max(r1.ptr, r2.ptr);
- auto e = min(r1.ptr + r1.length, r2.ptr + r2.length);
- return b < e ? b[0 .. e - b] : null;
- }
- ///
- @safe pure /*nothrow*/ unittest
- {
- int[] a = [ 10, 11, 12, 13, 14 ];
- int[] b = a[1 .. 3];
- assert(overlap(a, b) == [ 11, 12 ]);
- b = b.dup;
- // overlap disappears even though the content is the same
- assert(overlap(a, b).empty);
- }
- /*@safe nothrow*/ unittest
- {
- static void test(L, R)(L l, R r)
- {
- scope(failure) writeln("Types: L %s R %s", L.stringof, R.stringof);
- assert(overlap(l, r) == [ 100, 12 ]);
- assert(overlap(l, l[0 .. 2]) is l[0 .. 2]);
- assert(overlap(l, l[3 .. 5]) is l[3 .. 5]);
- assert(overlap(l[0 .. 2], l) is l[0 .. 2]);
- assert(overlap(l[3 .. 5], l) is l[3 .. 5]);
- }
- int[] a = [ 10, 11, 12, 13, 14 ];
- int[] b = a[1 .. 3];
- a[1] = 100;
- immutable int[] c = a.idup;
- immutable int[] d = c[1 .. 3];
- test(a, b);
- assert(overlap(a, b.dup).empty);
- test(c, d);
- assert(overlap(c, d.idup).empty);
- }
- @safe pure nothrow unittest // bugzilla 9836
- {
- // range primitives for array should work with alias this types
- struct Wrapper
- {
- int[] data;
- alias data this;
- @property Wrapper save() { return this; }
- }
- auto w = Wrapper([1,2,3,4]);
- std.array.popFront(w); // should work
- static assert(isInputRange!Wrapper);
- static assert(isForwardRange!Wrapper);
- static assert(isBidirectionalRange!Wrapper);
- static assert(isRandomAccessRange!Wrapper);
- }
- /+
- Commented out until the insert which has been deprecated has been removed.
- I'd love to just remove it in favor of insertInPlace, but then code would then
- use this version of insert and silently break. So, it's here so that it can
- be used once insert has not only been deprecated but removed, but until then,
- it's commented out.
- /++
- Creates a new array which is a copy of $(D array) with $(D stuff) (which
- must be an input range or a single item) inserted at position $(D pos).
- Examples:
- --------------------
- int[] a = [ 1, 2, 3, 4 ];
- auto b = a.insert(2, [ 1, 2 ]);
- assert(a == [ 1, 2, 3, 4 ]);
- assert(b == [ 1, 2, 1, 2, 3, 4 ]);
- --------------------
- +/
- T[] insert(T, Range)(T[] array, size_t pos, Range stuff)
- if(isInputRange!Range &&
- (is(ElementType!Range : T) ||
- isSomeString!(T[]) && is(ElementType!Range : dchar)))
- {
- static if(hasLength!Range && is(ElementEncodingType!Range : T))
- {
- auto retval = new Unqual!(T)[](array.length + stuff.length);
- retval[0 .. pos] = array[0 .. pos];
- copy(stuff, retval[pos .. pos + stuff.length]);
- retval[pos + stuff.length .. $] = array[pos .. $];
- return cast(T[])retval;
- }
- else
- {
- auto app = appender!(T[])();
- app.put(array[0 .. pos]);
- app.put(stuff);
- app.put(array[pos .. $]);
- return app.data;
- }
- }
- /++ Ditto +/
- T[] insert(T)(T[] array, size_t pos, T stuff)
- {
- auto retval = new T[](array.length + 1);
- retval[0 .. pos] = array[0 .. pos];
- retval[pos] = stuff;
- retval[pos + 1 .. $] = array[pos .. $];
- return retval;
- }
- //Verify Example.
- unittest
- {
- int[] a = [ 1, 2, 3, 4 ];
- auto b = a.insert(2, [ 1, 2 ]);
- assert(a == [ 1, 2, 3, 4 ]);
- assert(b == [ 1, 2, 1, 2, 3, 4 ]);
- }
- unittest
- {
- auto a = [1, 2, 3, 4];
- assert(a.insert(0, [6, 7]) == [6, 7, 1, 2, 3, 4]);
- assert(a.insert(2, [6, 7]) == [1, 2, 6, 7, 3, 4]);
- assert(a.insert(a.length, [6, 7]) == [1, 2, 3, 4, 6, 7]);
- assert(a.insert(0, filter!"true"([6, 7])) == [6, 7, 1, 2, 3, 4]);
- assert(a.insert(2, filter!"true"([6, 7])) == [1, 2, 6, 7, 3, 4]);
- assert(a.insert(a.length, filter!"true"([6, 7])) == [1, 2, 3, 4, 6, 7]);
- assert(a.insert(0, 22) == [22, 1, 2, 3, 4]);
- assert(a.insert(2, 22) == [1, 2, 22, 3, 4]);
- assert(a.insert(a.length, 22) == [1, 2, 3, 4, 22]);
- assert(a == [1, 2, 3, 4]);
- auto testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
- {
- auto l = to!T("hello");
- auto r = to!U(" world");
- enforce(insert(l, 0, r) == " worldhello",
- new AssertError("testStr failure 1", file, line));
- enforce(insert(l, 3, r) == "hel worldlo",
- new AssertError("testStr failure 2", file, line));
- enforce(insert(l, l.length, r) == "hello world",
- new AssertError("testStr failure 3", file, line));
- enforce(insert(l, 0, filter!"true"(r)) == " worldhello",
- new AssertError("testStr failure 4", file, line));
- enforce(insert(l, 3, filter!"true"(r)) == "hel worldlo",
- new AssertError("testStr failure 5", file, line));
- enforce(insert(l, l.length, filter!"true"(r)) == "hello world",
- new AssertError("testStr failure 6", file, line));
- }
- testStr!(string, string)();
- testStr!(string, wstring)();
- testStr!(string, dstring)();
- testStr!(wstring, string)();
- testStr!(wstring, wstring)();
- testStr!(wstring, dstring)();
- testStr!(dstring, string)();
- testStr!(dstring, wstring)();
- testStr!(dstring, dstring)();
- }
- +/
- private void copyBackwards(T)(T[] src, T[] dest)
- {
- import core.stdc.string;
- assert(src.length == dest.length);
- void trustedMemmove(void* d, const void* s, size_t len) @trusted
- {
- memmove(d, s, len);
- }
- if (!__ctfe)
- trustedMemmove(dest.ptr, src.ptr, src.length * T.sizeof);
- else
- {
- immutable len = src.length;
- for (size_t i = len; i-- > 0;)
- {
- dest[i] = src[i];
- }
- }
- }
- /++
- Inserts $(D stuff) (which must be an input range or any number of
- implicitly convertible items) in $(D array) at position $(D pos).
- Example:
- ---
- int[] a = [ 1, 2, 3, 4 ];
- a.insertInPlace(2, [ 1, 2 ]);
- assert(a == [ 1, 2, 1, 2, 3, 4 ]);
- a.insertInPlace(3, 10u, 11);
- assert(a == [ 1, 2, 1, 10, 11, 2, 3, 4]);
- ---
- +/
- void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
- if(!isSomeString!(T[])
- && allSatisfy!(isInputRangeOrConvertible!T, U) && U.length > 0)
- {
- static if(allSatisfy!(isInputRangeWithLengthOrConvertible!T, U))
- {
- import core.stdc.string;
- void assign(E)(ref T dest, ref E src)
- {
- static if (is(typeof(dest.opAssign(src))) ||
- !is(typeof(dest = src)))
- {
- // this should be in-place construction
- emplace(&dest, src);
- }
- else
- {
- dest = src;
- }
- }
- auto trustedAllocateArray(size_t n) @trusted nothrow
- {
- return uninitializedArray!(T[])(n);
- }
- void trustedMemcopy(T[] dest, T[] src) @trusted
- {
- assert(src.length == dest.length);
- if (!__ctfe)
- memcpy(dest.ptr, src.ptr, src.length * T.sizeof);
- else
- {
- dest[] = src[];
- }
- }
- immutable oldLen = array.length;
- size_t to_insert = 0;
- foreach (i, E; U)
- {
- static if (is(E : T)) //a single convertible value, not a range
- to_insert += 1;
- else
- to_insert += stuff[i].length;
- }
- auto tmp = trustedAllocateArray(to_insert);
- auto j = 0;
- foreach (i, E; U)
- {
- static if (is(E : T)) //ditto
- {
- assign(tmp[j++], stuff[i]);
- }
- else
- {
- foreach (v; stuff[i])
- {
- assign(tmp[j++], v);
- }
- }
- }
- array.length += to_insert;
- copyBackwards(array[pos..oldLen], array[pos+to_insert..$]);
- trustedMemcopy(array[pos..pos+to_insert], tmp);
- }
- else
- {
- // stuff has some InputRanges in it that don't have length
- // assume that stuff to be inserted is typically shorter
- // then the array that can be arbitrary big
- // TODO: needs a better implementation as there is no need to build an _array_
- // a singly-linked list of memory blocks (rope, etc.) will do
- auto app = appender!(T[])();
- foreach (i, E; U)
- app.put(stuff[i]);
- insertInPlace(array, pos, app.data);
- }
- }
- /++ Ditto +/
- void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
- if(isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U))
- {
- static if(is(Unqual!T == T)
- && allSatisfy!(isInputRangeWithLengthOrConvertible!dchar, U))
- {
- // mutable, can do in place
- //helper function: re-encode dchar to Ts and store at *ptr
- static T* putDChar(T* ptr, dchar ch)
- {
- static if(is(T == dchar))
- {
- *ptr++ = ch;
- return ptr;
- }
- else
- {
- T[dchar.sizeof/T.sizeof] buf;
- size_t len = encode(buf, ch);
- final switch(len)
- {
- static if(T.sizeof == char.sizeof)
- {
- case 4:
- ptr[3] = buf[3];
- goto case;
- case 3:
- ptr[2] = buf[2];
- goto case;
- }
- case 2:
- ptr[1] = buf[1];
- goto case;
- case 1:
- ptr[0] = buf[0];
- }
- ptr += len;
- return ptr;
- }
- }
- immutable oldLen = array.length;
- size_t to_insert = 0;
- //count up the number of *codeunits* to insert
- foreach (i, E; U)
- to_insert += codeLength!T(stuff[i]);
- array.length += to_insert;
- copyBackwards(array[pos..oldLen], array[pos+to_insert..$]);
- auto ptr = array.ptr + pos;
- foreach (i, E; U)
- {
- static if(is(E : dchar))
- {
- ptr = putDChar(ptr, stuff[i]);
- }
- else
- {
- foreach (dchar ch; stuff[i])
- ptr = putDChar(ptr, ch);
- }
- }
- assert(ptr == array.ptr + pos + to_insert, text(ptr - array.ptr, " vs ", pos + to_insert ));
- }
- else
- {
- // immutable/const, just construct a new array
- auto app = appender!(T[])();
- app.put(array[0..pos]);
- foreach (i, E; U)
- app.put(stuff[i]);
- app.put(array[pos..$]);
- array = app.data;
- }
- }
- //constraint helpers
- private template isInputRangeWithLengthOrConvertible(E)
- {
- template isInputRangeWithLengthOrConvertible(R)
- {
- //hasLength not defined for char[], wchar[] and dchar[]
- enum isInputRangeWithLengthOrConvertible =
- (isInputRange!R && is(typeof(R.init.length))
- && is(ElementType!R : E)) || is(R : E);
- }
- }
- //ditto
- private template isCharOrStringOrDcharRange(T)
- {
- enum isCharOrStringOrDcharRange = isSomeString!T || isSomeChar!T ||
- (isInputRange!T && is(ElementType!T : dchar));
- }
- //ditto
- private template isInputRangeOrConvertible(E)
- {
- template isInputRangeOrConvertible(R)
- {
- enum isInputRangeOrConvertible =
- (isInputRange!R && is(ElementType!R : E)) || is(R : E);
- }
- }
- //Verify Example.
- @safe unittest
- {
- int[] a = [ 1, 2, 3, 4 ];
- a.insertInPlace(2, [ 1, 2 ]);
- assert(a == [ 1, 2, 1, 2, 3, 4 ]);
- a.insertInPlace(3, 10u, 11);
- assert(a == [ 1, 2, 1, 10, 11, 2, 3, 4]);
- }
- unittest
- {
- bool test(T, U, V)(T orig, size_t pos, U toInsert, V result,
- string file = __FILE__, size_t line = __LINE__)
- {
- {
- static if(is(T == typeof(T.init.dup)))
- auto a = orig.dup;
- else
- auto a = orig.idup;
- a.insertInPlace(pos, toInsert);
- if(!std.algorithm.equal(a, result))
- return false;
- }
- static if(isInputRange!U)
- {
- orig.insertInPlace(pos, filter!"true"(toInsert));
- return std.algorithm.equal(orig, result);
- }
- else
- return true;
- }
- assert(test([1, 2, 3, 4], 0, [6, 7], [6, 7, 1, 2, 3, 4]));
- assert(test([1, 2, 3, 4], 2, [8, 9], [1, 2, 8, 9, 3, 4]));
- assert(test([1, 2, 3, 4], 4, [10, 11], [1, 2, 3, 4, 10, 11]));
- assert(test([1, 2, 3, 4], 0, 22, [22, 1, 2, 3, 4]));
- assert(test([1, 2, 3, 4], 2, 23, [1, 2, 23, 3, 4]));
- assert(test([1, 2, 3, 4], 4, 24, [1, 2, 3, 4, 24]));
- auto testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
- {
- auto l = to!T("hello");
- auto r = to!U(" વિશ્વ");
- enforce(test(l, 0, r, " વિશ્વhello"),
- new AssertError("testStr failure 1", file, line));
- enforce(test(l, 3, r, "hel વિશ્વlo"),
- new AssertError("testStr failure 2", file, line));
- enforce(test(l, l.length, r, "hello વિશ્વ"),
- new AssertError("testStr failure 3", file, line));
- }
- foreach (T; TypeTuple!(char, wchar, dchar,
- immutable(char), immutable(wchar), immutable(dchar)))
- {
- foreach (U; TypeTuple!(char, wchar, dchar,
- immutable(char), immutable(wchar), immutable(dchar)))
- {
- testStr!(T[], U[])();
- }
- }
- // variadic version
- bool testVar(T, U...)(T orig, size_t pos, U args)
- {
- static if(is(T == typeof(T.init.dup)))
- auto a = orig.dup;
- else
- auto a = orig.idup;
- auto result = args[$-1];
- a.insertInPlace(pos, args[0..$-1]);
- if (!std.algorithm.equal(a, result))
- return false;
- return true;
- }
- assert(testVar([1, 2, 3, 4], 0, 6, 7u, [6, 7, 1, 2, 3, 4]));
- assert(testVar([1L, 2, 3, 4], 2, 8, 9L, [1, 2, 8, 9, 3, 4]));
- assert(testVar([1L, 2, 3, 4], 4, 10L, 11, [1, 2, 3, 4, 10, 11]));
- assert(testVar([1L, 2, 3, 4], 4, [10, 11], 40L, 42L,
- [1, 2, 3, 4, 10, 11, 40, 42]));
- assert(testVar([1L, 2, 3, 4], 4, 10, 11, [40L, 42],
- [1, 2, 3, 4, 10, 11, 40, 42]));
- assert(testVar("t".idup, 1, 'e', 's', 't', "test"));
- assert(testVar("!!"w.idup, 1, "\u00e9ll\u00f4", 'x', "TTT"w, 'y',
- "!\u00e9ll\u00f4xTTTy!"));
- assert(testVar("flipflop"d.idup, 4, '_',
- "xyz"w, '\U00010143', '_', "abc"d, "__",
- "flip_xyz\U00010143_abc__flop"));
- }
- unittest
- {
- // insertInPlace interop with postblit
- struct Int
- {
- int* payload;
- this(int k)
- {
- payload = new int;
- *payload = k;
- }
- this(this)
- {
- int* np = new int;
- *np = *payload;
- payload = np;
- }
- ~this()
- {
- if (payload)
- *payload = 0; //'destroy' it
- }
- @property int getPayload(){ return *payload; }
- alias getPayload this;
- }
- Int[] arr = [Int(1), Int(4), Int(5)];
- assert(arr[0] == 1);
- insertInPlace(arr, 1, Int(2), Int(3));
- assert(equal(arr, [1, 2, 3, 4, 5])); //check it works with postblit
- }
- @safe unittest
- {
- assertCTFEable!(
- {
- int[] a = [1, 2];
- a.insertInPlace(2, 3);
- a.insertInPlace(0, -1, 0);
- return a == [-1, 0, 1, 2, 3];
- });
- }
- unittest // bugzilla 6874
- {
- // allocate some space
- byte[] a;
- a.length = 1;
- // fill it
- a.length = a.capacity;
- // write beyond
- byte[] b = a[$ .. $];
- b.insertInPlace(0, a);
- // make sure that reallocation has happened
- assert(GC.addrOf(&b[0]) == GC.addrOf(&b[$-1]));
- }
- /++
- Returns whether the $(D front)s of $(D lhs) and $(D rhs) both refer to the
- same place in memory, making one of the arrays a slice of the other which
- starts at index $(D 0).
- +/
- @safe
- pure nothrow bool sameHead(T)(in T[] lhs, in T[] rhs)
- {
- return lhs.ptr == rhs.ptr;
- }
- /++
- Returns whether the $(D back)s of $(D lhs) and $(D rhs) both refer to the
- same place in memory, making one of the arrays a slice of the other which
- end at index $(D $).
- +/
- @trusted
- pure nothrow bool sameTail(T)(in T[] lhs, in T[] rhs)
- {
- return lhs.ptr + lhs.length == rhs.ptr + rhs.length;
- }
- @safe pure nothrow unittest
- {
- foreach(T; TypeTuple!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
- {
- T a = [1, 2, 3, 4, 5];
- T b = a;
- T c = a[1 .. $];
- T d = a[0 .. 1];
- T e = null;
- assert(sameHead(a, a));
- assert(sameHead(a, b));
- assert(!sameHead(a, c));
- assert(sameHead(a, d));
- assert(!sameHead(a, e));
- assert(sameTail(a, a));
- assert(sameTail(a, b));
- assert(sameTail(a, c));
- assert(!sameTail(a, d));
- assert(!sameTail(a, e));
- //verifies R-value compatibilty
- assert(a.sameHead(a[0 .. 0]));
- assert(a.sameTail(a[$ .. $]));
- }
- }
- /********************************************
- Returns an array that consists of $(D s) (which must be an input
- range) repeated $(D n) times. This function allocates, fills, and
- returns a new array. For a lazy version, refer to $(XREF range, repeat).
- */
- ElementEncodingType!S[] replicate(S)(S s, size_t n) if (isDynamicArray!S)
- {
- alias ElementEncodingType!S[] RetType;
- // Optimization for return join(std.range.repeat(s, n));
- if (n == 0)
- return RetType.init;
- if (n == 1)
- return cast(RetType) s;
- auto r = new Unqual!(typeof(s[0]))[n * s.length];
- if (s.length == 1)
- r[] = s[0];
- else
- {
- immutable len = s.length, nlen = n * len;
- for (size_t i = 0; i < nlen; i += len)
- {
- r[i .. i + len] = s[];
- }
- }
- return cast(RetType) r;
- }
- ElementType!S[] replicate(S)(S s, size_t n)
- if (isInputRange!S && !isDynamicArray!S)
- {
- return join(std.range.repeat(s, n));
- }
- unittest
- {
- debug(std_array) printf("array.replicate.unittest\n");
- foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[]))
- {
- S s;
- immutable S t = "abc";
- assert(replicate(to!S("1234"), 0) is null);
- assert(replicate(to!S("1234"), 0) is null);
- assert(replicate(to!S("1234"), 1) == "1234");
- assert(replicate(to!S("1234"), 2) == "12341234");
- assert(replicate(to!S("1"), 4) == "1111");
- assert(replicate(t, 3) == "abcabcabc");
- assert(replicate(cast(S) null, 4) is null);
- }
- }
- /++
- Eagerly split the string $(D s) into an array of words, using whitespace as
- delimiter. Runs of whitespace are merged together (no empty words are produced).
- $(D @safe), $(D pure) and $(D CTFE)-able.
- +/
- S[] split(S)(S s) @safe pure
- if (isSomeString!S)
- {
- size_t istart;
- bool inword = false;
- S[] result;
- foreach (i, dchar c ; s)
- {
- if (std.uni.isWhite(c))
- {
- if (inword)
- {
- result ~= s[istart .. i];
- inword = false;
- }
- }
- else
- {
- if (!inword)
- {
- istart = i;
- inword = true;
- }
- }
- }
- if (inword)
- result ~= s[istart .. $];
- return result;
- }
- unittest
- {
- static auto makeEntry(S)(string l, string[] r)
- {return tuple(l.to!S(), r.to!(S[])());}
- foreach (S; TypeTuple!(string, wstring, dstring,))
- {
- auto entries =
- [
- makeEntry!S("", []),
- makeEntry!S(" ", []),
- makeEntry!S("hello", ["hello"]),
- makeEntry!S(" hello ", ["hello"]),
- makeEntry!S(" h e l l o ", ["h", "e", "l", "l", "o"]),
- makeEntry!S("peter\t\npaul\rjerry", ["peter", "paul", "jerry"]),
- makeEntry!S(" \t\npeter paul\tjerry \n", ["peter", "paul", "jerry"]),
- makeEntry!S("\u2000日\u202F本\u205F語\u3000", ["日", "本", "語"]),
- makeEntry!S(" 哈・郎博尔德} ___一个", ["哈・郎博尔德}", "___一个"])
- ];
- foreach (entry; entries)
- assert(entry[0].split() == entry[1], format("got: %s, expected: %s.", entry[0].split(), entry[1]));
- }
- //Just to test that an immutable is split-able
- immutable string s = " \t\npeter paul\tjerry \n";
- assert(split(s) == ["peter", "paul", "jerry"]);
- }
- unittest //safety, purity, ctfe ...
- {
- void dg() @safe pure {
- assert(split("hello world"c) == ["hello"c, "world"c]);
- assert(split("hello world"w) == ["hello"w, "world"w]);
- assert(split("hello world"d) == ["hello"d, "world"d]);
- }
- dg();
- assertCTFEable!dg;
- }
- /++
- Alias for $(XREF algorithm, splitter).
- +/
- alias splitter = std.algorithm.splitter;
- /++
- Eagerly splits $(D s) into an array, using $(D delim) as the delimiter.
- See also: $(XREF algorithm, splitter) for the lazy version of this operator.
- +/
- auto split(R, E)(R r, E delim)
- if (isForwardRange!R && is(typeof(ElementType!R.init == E.init)))
- {
- auto spl = std.algorithm.splitter(r, delim);
- alias S = typeof(spl.front.init); // "Slice_t"
- auto app = appender!(S[])();
- foreach (e; spl)
- app.put(e);
- return app.data;
- }
- auto split(R1, R2)(R1 r, R2 delim)
- if (isForwardRange!R1 && isForwardRange!R2 && is(typeof(ElementType!R1.init == ElementType!R2.init)))
- {
- auto spl = std.algorithm.splitter(r, delim);
- alias S = typeof(spl.front.init); // "Slice_t"
- auto app = appender!(S[])();
- foreach (e; spl)
- app.put(e);
- return app.data;
- }
- ///ditto
- auto split(alias isTerminator, R)(R r)
- if (isForwardRange!R && is(typeof(unaryFun!isTerminator(r.front))))
- {
- auto spl = std.algorithm.splitter!isTerminator(r);
- alias S = typeof(spl.front.init); // "Slice_t"
- auto app = appender!(S[])();
- foreach (e; spl)
- app.put(e);
- return app.data;
- }
- unittest
- {
- debug(std_array) printf("array.split\n");
- foreach (S; TypeTuple!(string, wstring, dstring,
- immutable(string), immutable(wstring), immutable(dstring),
- char[], wchar[], dchar[],
- const(char)[], const(wchar)[], const(dchar)[],
- const(char[]), immutable(char[])))
- {
- S s = to!S(",peter,paul,jerry,");
- auto words = split(s, ",");
- assert(words.length == 5, text(words.length));
- assert(cmp(words[0], "") == 0);
- assert(cmp(words[1], "peter") == 0);
- assert(cmp(words[2], "paul") == 0);
- assert(cmp(words[3], "jerry") == 0);
- assert(cmp(words[4], "") == 0);
- auto s1 = s[0 .. s.length - 1]; // lop off trailing ','
- words = split(s1, ",");
- assert(words.length == 4);
- assert(cmp(words[3], "jerry") == 0);
- auto s2 = s1[1 .. s1.length]; // lop off leading ','
- words = split(s2, ",");
- assert(words.length == 3);
- assert(cmp(words[0], "peter") == 0);
- auto s3 = to!S(",,peter,,paul,,jerry,,");
- words = split(s3, ",,");
- assert(words.length == 5);
- assert(cmp(words[0], "") == 0);
- assert(cmp(words[1], "peter") == 0);
- assert(cmp(words[2], "paul") == 0);
- assert(cmp(words[3], "jerry") == 0);
- assert(cmp(words[4], "") == 0);
- auto s4 = s3[0 .. s3.length - 2]; // lop off trailing ',,'
- words = split(s4, ",,");
- assert(words.length == 4);
- assert(cmp(words[3], "jerry") == 0);
- auto s5 = s4[2 .. s4.length]; // lop off leading ',,'
- words = split(s5, ",,");
- assert(words.length == 3);
- assert(cmp(words[0], "peter") == 0);
- }
- }
- /++
- Concatenates all of the ranges in $(D ror) together into one array using
- $(D sep) as the separator if present.
- +/
- ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep)
- if(isInputRange!RoR &&
- isInputRange!(ElementType!RoR) &&
- isInputRange!R &&
- is(Unqual!(ElementType!(ElementType!RoR)) == Unqual!(ElementType!R)))
- {
- alias ElementType!RoR RoRElem;
- alias typeof(return) RetType;
- if (ror.empty)
- return RetType.init;
- // Constraint only requires input range for sep.
- // This converts sep to an array (forward range) if it isn't one,
- // and makes sure it has the same string encoding for string types.
- static if (isSomeString!RetType &&
- !is(Unqual!(ElementEncodingType!RetType) == Unqual!(ElementEncodingType!R)))
- auto sepArr = to!RetType(sep);
- else static if (!isArray!R)
- auto sepArr = array(sep);
- else
- alias sep sepArr;
- auto result = appender!RetType();
- static if(isForwardRange!RoR &&
- (isNarrowString!RetType || hasLength!RoRElem))
- {
- // Reserve appender length if it can be computed.
- size_t resultLen = 0;
- immutable sepArrLength = sepArr.length;
- for (auto temp = ror.save; !temp.empty; temp.popFront())
- resultLen += temp.front.length + sepArrLength;
- resultLen -= sepArrLength;
- result.reserve(resultLen);
- version(unittest) scope(exit) assert(result.data.length == resultLen);
- }
- put(result, ror.front);
- ror.popFront();
- for (; !ror.empty; ror.popFront())
- {
- put(result, sepArr);
- put(result, ror.front);
- }
- return result.data;
- }
- /// Ditto
- ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror)
- if(isInputRange!RoR &&
- isInputRange!(ElementType!RoR))
- {
- alias typeof(return) RetType;
- if (ror.empty)
- return RetType.init;
- alias ElementType!RoR R;
- auto result = appender!RetType();
- static if(isForwardRange!RoR && (hasLength!R || isNarrowString!R))
- {
- // Reserve appender length if it can be computed.
- immutable resultLen = reduce!("a + b.length")(cast(size_t) 0, ror.save);
- result.reserve(resultLen);
- version(unittest) scope(exit) assert(result.data.length == resultLen);
- }
- for (; !ror.empty; ror.popFront())
- put(result, ror.front);
- return result.data;
- }
- ///
- @safe pure nothrow unittest
- {
- assert(join(["hello", "silly", "world"], " ") == "hello silly world");
- assert(join(["hello", "silly", "world"]) == "hellosillyworld");
- assert(join([[1, 2, 3], [4, 5]], [72, 73]) == [1, 2, 3, 72, 73, 4, 5]);
- assert(join([[1, 2, 3], [4, 5]]) == [1, 2, 3, 4, 5]);
- }
- unittest
- {
- debug(std_array) printf("array.join.unittest\n");
- foreach(R; TypeTuple!(string, wstring, dstring))
- {
- R word1 = "日本語";
- R word2 = "paul";
- R word3 = "jerry";
- R[] words = [word1, word2, word3];
- auto filteredWord1 = filter!"true"(word1);
- auto filteredLenWord1 = takeExactly(filteredWord1, word1.walkLength());
- auto filteredWord2 = filter!"true"(word2);
- auto filteredLenWord2 = takeExactly(filteredWord2, word2.walkLength());
- auto filteredWord3 = filter!"true"(word3);
- auto filteredLenWord3 = takeExactly(filteredWord3, word3.walkLength());
- auto filteredWordsArr = [filteredWord1, filteredWord2, filteredWord3];
- auto filteredLenWordsArr = [filteredLenWord1, filteredLenWord2, filteredLenWord3];
- auto filteredWords = filter!"true"(filteredWordsArr);
- foreach(S; TypeTuple!(string, wstring, dstring))
- {
- assert(join(filteredWords, to!S(", ")) == "日本語, paul, jerry");
- assert(join(filteredWordsArr, to!S(", ")) == "日本語, paul, jerry");
- assert(join(filteredLenWordsArr, to!S(", ")) == "日本語, paul, jerry");
- assert(join(filter!"true"(words), to!S(", ")) == "日本語, paul, jerry");
- assert(join(words, to!S(", ")) == "日本語, paul, jerry");
- assert(join(filteredWords, to!S("")) == "日本語pauljerry");
- assert(join(filteredWordsArr, to!S("")) == "日本語pauljerry");
- assert(join(filteredLenWordsArr, to!S("")) == "日本語pauljerry");
- assert(join(filter!"true"(words), to!S("")) == "日本語pauljerry");
- assert(join(words, to!S("")) == "日本語pauljerry");
- assert(join(filter!"true"([word1]), to!S(", ")) == "日本語");
- assert(join([filteredWord1], to!S(", ")) == "日本語");
- assert(join([filteredLenWord1], to!S(", ")) == "日本語");
- assert(join(filter!"true"([filteredWord1]), to!S(", ")) == "日本語");
- assert(join([word1], to!S(", ")) == "日本語");
- assert(join(filteredWords, to!S(word1)) == "日本語日本語paul日本語jerry");
- assert(join(filteredWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry");
- assert(join(filteredLenWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry");
- assert(join(filter!"true"(words), to!S(word1)) == "日本語日本語paul日本語jerry");
- assert(join(words, to!S(word1)) == "日本語日本語paul日本語jerry");
- auto filterComma = filter!"true"(to!S(", "));
- assert(join(filteredWords, filterComma) == "日本語, paul, jerry");
- assert(join(filteredWordsArr, filterComma) == "日本語, paul, jerry");
- assert(join(filteredLenWordsArr, filterComma) == "日本語, paul, jerry");
- assert(join(filter!"true"(words), filterComma) == "日本語, paul, jerry");
- assert(join(words, filterComma) == "日本語, paul, jerry");
- }
- assert(join(filteredWords) == "日本語pauljerry");
- assert(join(filteredWordsArr) == "日本語pauljerry");
- assert(join(filteredLenWordsArr) == "日本語pauljerry");
- assert(join(filter!"true"(words)) == "日本語pauljerry");
- assert(join(words) == "日本語pauljerry");
- assert(join(filteredWords, filter!"true"(", ")) == "日本語, paul, jerry");
- assert(join(filteredWordsArr, filter!"true"(", ")) == "日本語, paul, jerry");
- assert(join(filteredLenWordsArr, filter!"true"(", ")) == "日本語, paul, jerry");
- assert(join(filter!"true"(words), filter!"true"(", ")) == "日本語, paul, jerry");
- assert(join(words, filter!"true"(", ")) == "日本語, paul, jerry");
- assert(join(filter!"true"(cast(typeof(filteredWordsArr))[]), ", ").empty);
- assert(join(cast(typeof(filteredWordsArr))[], ", ").empty);
- assert(join(cast(typeof(filteredLenWordsArr))[], ", ").empty);
- assert(join(filter!"true"(cast(R[])[]), ", ").empty);
- assert(join(cast(R[])[], ", ").empty);
- assert(join(filter!"true"(cast(typeof(filteredWordsArr))[])).empty);
- assert(join(cast(typeof(filteredWordsArr))[]).empty);
- assert(join(cast(typeof(filteredLenWordsArr))[]).empty);
- assert(join(filter!"true"(cast(R[])[])).empty);
- assert(join(cast(R[])[]).empty);
- }
- assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
- assert(join([[1, 2], [41, 42]], cast(int[])[]) == [1, 2, 41, 42]);
- assert(join([[1, 2]], [5, 6]) == [1, 2]);
- assert(join(cast(int[][])[], [5, 6]).empty);
- assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]);
- assert(join(cast(int[][])[]).empty);
- alias filter!"true" f;
- assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
- assert(join(f([[1, 2], [41, 42]]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
- assert(join([f([1, 2]), f([41, 42])], [5, 6]) == [1, 2, 5, 6, 41, 42]);
- assert(join(f([f([1, 2]), f([41, 42])]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
- assert(join([[1, 2], [41, 42]], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
- assert(join(f([[1, 2], [41, 42]]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
- assert(join([f([1, 2]), f([41, 42])], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
- assert(join(f([f([1, 2]), f([41, 42])]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
- }
- /++
- Replace occurrences of $(D from) with $(D to) in $(D subject). Returns a new
- array without changing the contents of $(D su…