d: Merge upstream dmd 568496d5b, druntime 178c44ff, phobos 574bf883b.
[official-gcc.git] / libphobos / src / std / array.d
blobffdda8e1d4c1a4029cba4019d56412a466d4443e
1 // Written in the D programming language.
2 /**
3 Functions and types that manipulate built-in arrays and associative arrays.
5 This module provides all kinds of functions to create, manipulate or convert arrays:
7 $(SCRIPT inhibitQuickIndex = 1;)
8 $(DIVC quickindex,
9 $(BOOKTABLE ,
10 $(TR $(TH Function Name) $(TH Description)
12 $(TR $(TD $(LREF array))
13 $(TD Returns a copy of the input in a newly allocated dynamic array.
15 $(TR $(TD $(LREF appender))
16 $(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given array.
18 $(TR $(TD $(LREF assocArray))
19 $(TD Returns a newly allocated associative array from a range of key/value tuples.
21 $(TR $(TD $(LREF byPair))
22 $(TD Construct a range iterating over an associative array by key/value tuples.
24 $(TR $(TD $(LREF insertInPlace))
25 $(TD Inserts into an existing array at a given position.
27 $(TR $(TD $(LREF join))
28 $(TD Concatenates a range of ranges into one array.
30 $(TR $(TD $(LREF minimallyInitializedArray))
31 $(TD Returns a new array of type `T`.
33 $(TR $(TD $(LREF replace))
34 $(TD Returns a new array with all occurrences of a certain subrange replaced.
36 $(TR $(TD $(LREF replaceFirst))
37 $(TD Returns a new array with the first occurrence of a certain subrange replaced.
39 $(TR $(TD $(LREF replaceInPlace))
40 $(TD Replaces all occurrences of a certain subrange and puts the result into a given array.
42 $(TR $(TD $(LREF replaceInto))
43 $(TD Replaces all occurrences of a certain subrange and puts the result into an output range.
45 $(TR $(TD $(LREF replaceLast))
46 $(TD Returns a new array with the last occurrence of a certain subrange replaced.
48 $(TR $(TD $(LREF replaceSlice))
49 $(TD Returns a new array with a given slice replaced.
51 $(TR $(TD $(LREF replicate))
52 $(TD Creates a new array out of several copies of an input array or range.
54 $(TR $(TD $(LREF sameHead))
55 $(TD Checks if the initial segments of two arrays refer to the same
56 place in memory.
58 $(TR $(TD $(LREF sameTail))
59 $(TD Checks if the final segments of two arrays refer to the same place
60 in memory.
62 $(TR $(TD $(LREF split))
63 $(TD Eagerly split a range or string into an array.
65 $(TR $(TD $(LREF staticArray))
66 $(TD Creates a new static array from given data.
68 $(TR $(TD $(LREF uninitializedArray))
69 $(TD Returns a new array of type `T` without initializing its elements.
73 Copyright: Copyright Andrei Alexandrescu 2008- and Jonathan M Davis 2011-.
75 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
77 Authors: $(HTTP erdani.org, Andrei Alexandrescu) and
78 $(HTTP jmdavisprog.com, Jonathan M Davis)
80 Source: $(PHOBOSSRC std/array.d)
82 module std.array;
84 import std.functional;
85 import std.meta;
86 import std.traits;
88 import std.range.primitives;
89 public import std.range.primitives : save, empty, popFront, popBack, front, back;
91 /**
92 * Allocates an array and initializes it with copies of the elements
93 * of range `r`.
95 * Narrow strings are handled as follows:
96 * - If autodecoding is turned on (default), then they are handled as a separate overload.
97 * - If autodecoding is turned off, then this is equivalent to duplicating the array.
99 * Params:
100 * r = range (or aggregate with `opApply` function) whose elements are copied into the allocated array
101 * Returns:
102 * allocated and initialized array
104 ForeachType!Range[] array(Range)(Range r)
105 if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range)
107 if (__ctfe)
109 // Compile-time version to avoid memcpy calls.
110 // Also used to infer attributes of array().
111 typeof(return) result;
112 foreach (e; r)
113 result ~= e;
114 return result;
117 alias E = ForeachType!Range;
118 static if (hasLength!Range)
120 auto length = r.length;
121 if (length == 0)
122 return null;
124 import core.internal.lifetime : emplaceRef;
126 auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))();
128 // Every element of the uninitialized array must be initialized
129 size_t i;
130 foreach (e; r)
132 emplaceRef!E(result[i], e);
133 ++i;
135 return (() @trusted => cast(E[]) result)();
137 else
139 auto a = appender!(E[])();
140 foreach (e; r)
142 a.put(e);
144 return a.data;
148 /// ditto
149 ForeachType!(PointerTarget!Range)[] array(Range)(Range r)
150 if (isPointer!Range && isIterable!(PointerTarget!Range) && !isAutodecodableString!Range && !isInfinite!Range)
152 return array(*r);
156 @safe pure nothrow unittest
158 auto a = array([1, 2, 3, 4, 5][]);
159 assert(a == [ 1, 2, 3, 4, 5 ]);
162 @safe pure nothrow unittest
164 import std.algorithm.comparison : equal;
165 struct Foo
167 int a;
169 auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
170 assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
173 @safe pure nothrow unittest
175 struct MyRange
177 enum front = 123;
178 enum empty = true;
179 void popFront() {}
182 auto arr = (new MyRange).array;
183 assert(arr.empty);
186 @safe pure nothrow unittest
188 immutable int[] a = [1, 2, 3, 4];
189 auto b = (&a).array;
190 assert(b == a);
193 @safe pure nothrow unittest
195 import std.algorithm.comparison : equal;
196 struct Foo
198 int a;
199 noreturn opAssign(Foo)
201 assert(0);
203 auto opEquals(Foo foo)
205 return a == foo.a;
208 auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
209 assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
212 // https://issues.dlang.org/show_bug.cgi?id=12315
213 @safe pure nothrow unittest
215 static struct Bug12315 { immutable int i; }
216 enum bug12315 = [Bug12315(123456789)].array();
217 static assert(bug12315[0].i == 123456789);
220 @safe pure nothrow unittest
222 import std.range;
223 static struct S{int* p;}
224 auto a = array(immutable(S).init.repeat(5));
225 assert(a.length == 5);
228 // https://issues.dlang.org/show_bug.cgi?id=18995
229 @system unittest
231 import core.memory : __delete;
232 int nAlive = 0;
233 struct S
235 bool alive;
236 this(int) { alive = true; ++nAlive; }
237 this(this) { nAlive += alive; }
238 ~this() { nAlive -= alive; alive = false; }
241 import std.algorithm.iteration : map;
242 import std.range : iota;
244 auto arr = iota(3).map!(a => S(a)).array;
245 assert(nAlive == 3);
247 // No good way to ensure the GC frees this, just call the lifetime function
248 // directly.
249 __delete(arr);
251 assert(nAlive == 0);
254 @safe pure nothrow @nogc unittest
256 //Turn down infinity:
257 static assert(!is(typeof(
258 repeat(1).array()
259 )));
262 // https://issues.dlang.org/show_bug.cgi?id=20937
263 @safe pure nothrow unittest
265 struct S {int* x;}
266 struct R
268 immutable(S) front;
269 bool empty;
270 @safe pure nothrow void popFront(){empty = true;}
272 R().array;
276 Convert a narrow autodecoding string to an array type that fully supports
277 random access. This is handled as a special case and always returns an array
278 of `dchar`
280 NOTE: This function is never used when autodecoding is turned off.
282 Params:
283 str = `isNarrowString` to be converted to an array of `dchar`
284 Returns:
285 a `dchar[]`, `const(dchar)[]`, or `immutable(dchar)[]` depending on the constness of
286 the input.
288 CopyTypeQualifiers!(ElementType!String,dchar)[] array(String)(scope String str)
289 if (isAutodecodableString!String)
291 import std.utf : toUTF32;
292 auto temp = str.toUTF32;
293 /* Unsafe cast. Allowed because toUTF32 makes a new array
294 and copies all the elements.
296 return () @trusted { return cast(CopyTypeQualifiers!(ElementType!String, dchar)[]) temp; } ();
300 @safe pure nothrow unittest
302 import std.range.primitives : isRandomAccessRange;
303 import std.traits : isAutodecodableString;
305 // note that if autodecoding is turned off, `array` will not transcode these.
306 static if (isAutodecodableString!string)
307 assert("Hello D".array == "Hello D"d);
308 else
309 assert("Hello D".array == "Hello D");
311 static if (isAutodecodableString!wstring)
312 assert("Hello D"w.array == "Hello D"d);
313 else
314 assert("Hello D"w.array == "Hello D"w);
316 static assert(isRandomAccessRange!dstring == true);
319 @safe unittest
321 import std.conv : to;
323 static struct TestArray { int x; string toString() @safe { return to!string(x); } }
325 static struct OpAssign
327 uint num;
328 this(uint num) { this.num = num; }
330 // Templating opAssign to make sure the bugs with opAssign being
331 // templated are fixed.
332 void opAssign(T)(T rhs) { this.num = rhs.num; }
335 static struct OpApply
337 int opApply(scope int delegate(ref int) @safe dg)
339 int res;
340 foreach (i; 0 .. 10)
342 res = dg(i);
343 if (res) break;
346 return res;
350 auto a = array([1, 2, 3, 4, 5][]);
351 assert(a == [ 1, 2, 3, 4, 5 ]);
353 auto b = array([TestArray(1), TestArray(2)][]);
354 assert(b == [TestArray(1), TestArray(2)]);
356 class C
358 int x;
359 this(int y) { x = y; }
360 override string toString() const @safe { return to!string(x); }
362 auto c = array([new C(1), new C(2)][]);
363 assert(c[0].x == 1);
364 assert(c[1].x == 2);
366 auto d = array([1.0, 2.2, 3][]);
367 assert(is(typeof(d) == double[]));
368 assert(d == [1.0, 2.2, 3]);
370 auto e = [OpAssign(1), OpAssign(2)];
371 auto f = array(e);
372 assert(e == f);
374 assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
375 static if (isAutodecodableString!string)
377 assert(array("ABC") == "ABC"d);
378 assert(array("ABC".dup) == "ABC"d.dup);
382 // https://issues.dlang.org/show_bug.cgi?id=8233
383 @safe pure nothrow unittest
385 assert(array("hello world"d) == "hello world"d);
386 immutable a = [1, 2, 3, 4, 5];
387 assert(array(a) == a);
388 const b = a;
389 assert(array(b) == a);
391 //To verify that the opAssign branch doesn't get screwed up by using Unqual.
392 //EDIT: array no longer calls opAssign.
393 struct S
395 ref S opAssign(S)(const ref S rhs)
397 assert(0);
400 int i;
403 static foreach (T; AliasSeq!(S, const S, immutable S))
405 auto arr = [T(1), T(2), T(3), T(4)];
406 assert(array(arr) == arr);
410 // https://issues.dlang.org/show_bug.cgi?id=9824
411 @safe pure nothrow unittest
413 static struct S
415 @disable void opAssign(S);
416 int i;
418 auto arr = [S(0), S(1), S(2)];
419 arr.array();
422 // https://issues.dlang.org/show_bug.cgi?id=10220
423 @safe pure nothrow unittest
425 import std.algorithm.comparison : equal;
426 import std.exception;
427 import std.range : repeat;
429 static struct S
431 int val;
433 @disable this();
434 this(int v) { val = v; }
436 assertCTFEable!(
438 auto r = S(1).repeat(2).array();
439 assert(equal(r, [S(1), S(1)]));
444 Returns a newly allocated associative array from a range of key/value tuples
445 or from a range of keys and a range of values.
447 Params:
448 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
449 of tuples of keys and values.
450 keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys
451 values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values
453 Returns:
455 A newly allocated associative array out of elements of the input
456 range, which must be a range of tuples (Key, Value) or
457 a range of keys and a range of values. If given two ranges of unequal
458 lengths after the elements of the shorter are exhausted the remaining
459 elements of the longer will not be considered.
460 Returns a null associative array reference when given an empty range.
461 Duplicates: Associative arrays have unique keys. If r contains duplicate keys,
462 then the result will contain the value of the last pair for that key in r.
464 See_Also: $(REF Tuple, std,typecons), $(REF zip, std,range)
467 auto assocArray(Range)(Range r)
468 if (isInputRange!Range)
470 import std.typecons : isTuple;
472 alias E = ElementType!Range;
473 static assert(isTuple!E, "assocArray: argument must be a range of tuples,"
474 ~" but was a range of "~E.stringof);
475 static assert(E.length == 2, "assocArray: tuple dimension must be 2");
476 alias KeyType = E.Types[0];
477 alias ValueType = E.Types[1];
478 static assert(isMutable!ValueType, "assocArray: value type must be mutable");
480 ValueType[KeyType] aa;
481 foreach (t; r)
482 aa[t[0]] = t[1];
483 return aa;
486 /// ditto
487 auto assocArray(Keys, Values)(Keys keys, Values values)
488 if (isInputRange!Values && isInputRange!Keys)
490 static if (isDynamicArray!Keys && isDynamicArray!Values
491 && !isNarrowString!Keys && !isNarrowString!Values)
493 void* aa;
495 // aaLiteral is nothrow when the destructors don't throw
496 static if (is(typeof(() nothrow
498 import std.range : ElementType;
499 import std.traits : hasElaborateDestructor;
500 alias KeyElement = ElementType!Keys;
501 static if (hasElaborateDestructor!KeyElement)
502 KeyElement.init.__xdtor();
504 alias ValueElement = ElementType!Values;
505 static if (hasElaborateDestructor!ValueElement)
506 ValueElement.init.__xdtor();
507 })))
509 scope(failure) assert(false, "aaLiteral must not throw");
511 if (values.length > keys.length)
512 values = values[0 .. keys.length];
513 else if (keys.length > values.length)
514 keys = keys[0 .. values.length];
515 aa = aaLiteral(keys, values);
517 alias Key = typeof(keys[0]);
518 alias Value = typeof(values[0]);
519 return (() @trusted => cast(Value[Key]) aa)();
521 else
523 // zip is not always able to infer nothrow
524 alias Key = ElementType!Keys;
525 alias Value = ElementType!Values;
526 static assert(isMutable!Value, "assocArray: value type must be mutable");
527 Value[Key] aa;
528 foreach (key; keys)
530 if (values.empty) break;
532 // aa[key] is incorrectly not @safe if the destructor throws
533 // https://issues.dlang.org/show_bug.cgi?id=18592
534 static if (is(typeof(() @safe
536 import std.range : ElementType;
537 import std.traits : hasElaborateDestructor;
538 alias KeyElement = ElementType!Keys;
539 static if (hasElaborateDestructor!KeyElement)
540 KeyElement.init.__xdtor();
542 alias ValueElement = ElementType!Values;
543 static if (hasElaborateDestructor!ValueElement)
544 ValueElement.init.__xdtor();
545 })))
547 () @trusted {
548 aa[key] = values.front;
549 }();
551 else
553 aa[key] = values.front;
555 values.popFront();
557 return aa;
562 @safe pure /*nothrow*/ unittest
564 import std.range : repeat, zip;
565 import std.typecons : tuple;
566 import std.range.primitives : autodecodeStrings;
567 auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap
568 static assert(is(typeof(a) == string[int]));
569 assert(a == [0:"a", 1:"b", 2:"c"]);
571 auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
572 static assert(is(typeof(b) == string[string]));
573 assert(b == ["foo":"bar", "baz":"quux"]);
575 static if (autodecodeStrings)
576 alias achar = dchar;
577 else
578 alias achar = immutable(char);
579 auto c = assocArray("ABCD", true.repeat);
580 static assert(is(typeof(c) == bool[achar]));
581 bool[achar] expected = ['D':true, 'A':true, 'B':true, 'C':true];
582 assert(c == expected);
585 // Cannot be version (StdUnittest) - recursive instantiation error
586 // https://issues.dlang.org/show_bug.cgi?id=11053
587 @safe pure nothrow unittest
589 import std.typecons;
590 static assert(!__traits(compiles, [ 1, 2, 3 ].assocArray()));
591 static assert(!__traits(compiles, [ tuple("foo", "bar", "baz") ].assocArray()));
592 static assert(!__traits(compiles, [ tuple("foo") ].assocArray()));
593 assert([ tuple("foo", "bar") ].assocArray() == ["foo": "bar"]);
596 // https://issues.dlang.org/show_bug.cgi?id=13909
597 @safe pure nothrow unittest
599 import std.typecons;
600 auto a = [tuple!(const string, string)("foo", "bar")];
601 auto b = [tuple!(string, const string)("foo", "bar")];
602 assert(a == b);
603 assert(assocArray(a) == [cast(const(string)) "foo": "bar"]);
604 static assert(!__traits(compiles, assocArray(b)));
607 // https://issues.dlang.org/show_bug.cgi?id=5502
608 @safe pure nothrow unittest
610 auto a = assocArray([0, 1, 2], ["a", "b", "c"]);
611 static assert(is(typeof(a) == string[int]));
612 assert(a == [0:"a", 1:"b", 2:"c"]);
614 auto b = assocArray([0, 1, 2], [3L, 4, 5]);
615 static assert(is(typeof(b) == long[int]));
616 assert(b == [0: 3L, 1: 4, 2: 5]);
619 // https://issues.dlang.org/show_bug.cgi?id=5502
620 @safe pure unittest
622 import std.algorithm.iteration : filter, map;
623 import std.range : enumerate;
624 import std.range.primitives : autodecodeStrings;
626 auto r = "abcde".enumerate.filter!(a => a.index == 2);
627 auto a = assocArray(r.map!(a => a.value), r.map!(a => a.index));
628 static if (autodecodeStrings)
629 alias achar = dchar;
630 else
631 alias achar = immutable(char);
632 static assert(is(typeof(a) == size_t[achar]));
633 assert(a == [achar('c'): size_t(2)]);
636 @safe nothrow pure unittest
638 import std.range : iota;
639 auto b = assocArray(3.iota, 3.iota(6));
640 static assert(is(typeof(b) == int[int]));
641 assert(b == [0: 3, 1: 4, 2: 5]);
643 b = assocArray([0, 1, 2], [3, 4, 5]);
644 assert(b == [0: 3, 1: 4, 2: 5]);
647 @safe unittest
649 struct ThrowingElement
651 int i;
652 static bool b;
653 ~this(){
654 if (b)
655 throw new Exception("");
658 static assert(!__traits(compiles, () nothrow { assocArray([ThrowingElement()], [0]);}));
659 assert(assocArray([ThrowingElement()], [0]) == [ThrowingElement(): 0]);
661 static assert(!__traits(compiles, () nothrow { assocArray([0], [ThrowingElement()]);}));
662 assert(assocArray([0], [ThrowingElement()]) == [0: ThrowingElement()]);
664 import std.range : iota;
665 static assert(!__traits(compiles, () nothrow { assocArray(1.iota, [ThrowingElement()]);}));
666 assert(assocArray(1.iota, [ThrowingElement()]) == [0: ThrowingElement()]);
669 @system unittest
671 import std.range : iota;
672 struct UnsafeElement
674 int i;
675 static bool b;
676 ~this(){
677 int[] arr;
678 void* p = arr.ptr + 1; // unsafe
681 static assert(!__traits(compiles, () @safe { assocArray(1.iota, [UnsafeElement()]);}));
682 assert(assocArray(1.iota, [UnsafeElement()]) == [0: UnsafeElement()]);
686 Construct a range iterating over an associative array by key/value tuples.
688 Params:
689 aa = The associative array to iterate over.
691 Returns: A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
692 of Tuple's of key and value pairs from the given associative array. The members
693 of each pair can be accessed by name (`.key` and `.value`). or by integer
694 index (0 and 1 respectively).
696 auto byPair(AA)(AA aa)
697 if (isAssociativeArray!AA)
699 import std.algorithm.iteration : map;
700 import std.typecons : tuple;
702 return aa.byKeyValue
703 .map!(pair => tuple!("key", "value")(pair.key, pair.value));
707 @safe pure nothrow unittest
709 import std.algorithm.sorting : sort;
710 import std.typecons : tuple, Tuple;
712 auto aa = ["a": 1, "b": 2, "c": 3];
713 Tuple!(string, int)[] pairs;
715 // Iteration over key/value pairs.
716 foreach (pair; aa.byPair)
718 if (pair.key == "b")
719 pairs ~= tuple("B", pair.value);
720 else
721 pairs ~= pair;
724 // Iteration order is implementation-dependent, so we should sort it to get
725 // a fixed order.
726 pairs.sort();
727 assert(pairs == [
728 tuple("B", 2),
729 tuple("a", 1),
730 tuple("c", 3)
734 @safe pure nothrow unittest
736 import std.typecons : tuple, Tuple;
737 import std.meta : AliasSeq;
739 auto aa = ["a":2];
740 auto pairs = aa.byPair();
742 alias PT = typeof(pairs.front);
743 static assert(is(PT : Tuple!(string,int)));
744 static assert(PT.fieldNames == AliasSeq!("key", "value"));
745 static assert(isForwardRange!(typeof(pairs)));
747 assert(!pairs.empty);
748 assert(pairs.front == tuple("a", 2));
750 auto savedPairs = pairs.save;
752 pairs.popFront();
753 assert(pairs.empty);
754 assert(!savedPairs.empty);
755 assert(savedPairs.front == tuple("a", 2));
758 // https://issues.dlang.org/show_bug.cgi?id=17711
759 @safe pure nothrow unittest
761 const(int[string]) aa = [ "abc": 123 ];
763 // Ensure that byKeyValue is usable with a const AA.
764 auto kv = aa.byKeyValue;
765 assert(!kv.empty);
766 assert(kv.front.key == "abc" && kv.front.value == 123);
767 kv.popFront();
768 assert(kv.empty);
770 // Ensure byPair is instantiable with const AA.
771 auto r = aa.byPair;
772 static assert(isInputRange!(typeof(r)));
773 assert(!r.empty && r.front[0] == "abc" && r.front[1] == 123);
774 r.popFront();
775 assert(r.empty);
778 private template blockAttribute(T)
780 import core.memory;
781 static if (hasIndirections!(T) || is(T == void))
783 enum blockAttribute = 0;
785 else
787 enum blockAttribute = GC.BlkAttr.NO_SCAN;
791 @safe unittest
793 import core.memory : UGC = GC;
794 static assert(!(blockAttribute!void & UGC.BlkAttr.NO_SCAN));
797 // Returns the number of dimensions in an array T.
798 private template nDimensions(T)
800 static if (isArray!T)
802 enum nDimensions = 1 + nDimensions!(typeof(T.init[0]));
804 else
806 enum nDimensions = 0;
810 @safe unittest
812 static assert(nDimensions!(uint[]) == 1);
813 static assert(nDimensions!(float[][]) == 2);
817 Returns a new array of type `T` allocated on the garbage collected heap
818 without initializing its elements. This can be a useful optimization if every
819 element will be immediately initialized. `T` may be a multidimensional
820 array. In this case sizes may be specified for any number of dimensions from 0
821 to the number in `T`.
823 uninitializedArray is `nothrow` and weakly `pure`.
825 uninitializedArray is `@system` if the uninitialized element type has pointers.
827 Params:
828 T = The type of the resulting array elements
829 sizes = The length dimension(s) of the resulting array
830 Returns:
831 An array of `T` with `I.length` dimensions.
833 auto uninitializedArray(T, I...)(I sizes) nothrow @system
834 if (isDynamicArray!T && allSatisfy!(isIntegral, I) && hasIndirections!(ElementEncodingType!T))
836 enum isSize_t(E) = is (E : size_t);
837 alias toSize_t(E) = size_t;
839 static assert(allSatisfy!(isSize_t, I),
840 "Argument types in "~I.stringof~" are not all convertible to size_t: "
841 ~Filter!(templateNot!(isSize_t), I).stringof);
843 //Eagerlly transform non-size_t into size_t to avoid template bloat
844 alias ST = staticMap!(toSize_t, I);
846 return arrayAllocImpl!(false, T, ST)(sizes);
849 /// ditto
850 auto uninitializedArray(T, I...)(I sizes) nothrow @trusted
851 if (isDynamicArray!T && allSatisfy!(isIntegral, I) && !hasIndirections!(ElementEncodingType!T))
853 enum isSize_t(E) = is (E : size_t);
854 alias toSize_t(E) = size_t;
856 static assert(allSatisfy!(isSize_t, I),
857 "Argument types in "~I.stringof~" are not all convertible to size_t: "
858 ~Filter!(templateNot!(isSize_t), I).stringof);
860 //Eagerlly transform non-size_t into size_t to avoid template bloat
861 alias ST = staticMap!(toSize_t, I);
863 return arrayAllocImpl!(false, T, ST)(sizes);
866 @system nothrow pure unittest
868 double[] arr = uninitializedArray!(double[])(100);
869 assert(arr.length == 100);
871 double[][] matrix = uninitializedArray!(double[][])(42, 31);
872 assert(matrix.length == 42);
873 assert(matrix[0].length == 31);
875 char*[] ptrs = uninitializedArray!(char*[])(100);
876 assert(ptrs.length == 100);
880 Returns a new array of type `T` allocated on the garbage collected heap.
882 Partial initialization is done for types with indirections, for preservation
883 of memory safety. Note that elements will only be initialized to 0, but not
884 necessarily the element type's `.init`.
886 minimallyInitializedArray is `nothrow` and weakly `pure`.
888 Params:
889 T = The type of the array elements
890 sizes = The length dimension(s) of the resulting array
891 Returns:
892 An array of `T` with `I.length` dimensions.
894 auto minimallyInitializedArray(T, I...)(I sizes) nothrow @trusted
895 if (isDynamicArray!T && allSatisfy!(isIntegral, I))
897 enum isSize_t(E) = is (E : size_t);
898 alias toSize_t(E) = size_t;
900 static assert(allSatisfy!(isSize_t, I),
901 "Argument types in "~I.stringof~" are not all convertible to size_t: "
902 ~Filter!(templateNot!(isSize_t), I).stringof);
903 //Eagerlly transform non-size_t into size_t to avoid template bloat
904 alias ST = staticMap!(toSize_t, I);
906 return arrayAllocImpl!(true, T, ST)(sizes);
910 @safe pure nothrow unittest
912 import std.algorithm.comparison : equal;
913 import std.range : repeat;
915 auto arr = minimallyInitializedArray!(int[])(42);
916 assert(arr.length == 42);
918 // Elements aren't necessarily initialized to 0, so don't do this:
919 // assert(arr.equal(0.repeat(42)));
920 // If that is needed, initialize the array normally instead:
921 auto arr2 = new int[42];
922 assert(arr2.equal(0.repeat(42)));
925 @safe pure nothrow unittest
927 cast(void) minimallyInitializedArray!(int[][][][][])();
928 double[] arr = minimallyInitializedArray!(double[])(100);
929 assert(arr.length == 100);
931 double[][] matrix = minimallyInitializedArray!(double[][])(42);
932 assert(matrix.length == 42);
933 foreach (elem; matrix)
935 assert(elem.ptr is null);
939 // from rt/lifetime.d
940 private extern(C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow;
942 private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
944 static assert(I.length <= nDimensions!T,
945 I.length.stringof~"dimensions specified for a "~nDimensions!T.stringof~" dimensional array.");
947 alias E = ElementEncodingType!T;
949 E[] ret;
951 static if (I.length != 0)
953 static assert(is(I[0] == size_t), "I[0] must be of type size_t not "
954 ~ I[0].stringof);
955 alias size = sizes[0];
958 static if (I.length == 1)
960 if (__ctfe)
962 static if (__traits(compiles, new E[](size)))
963 ret = new E[](size);
964 else static if (__traits(compiles, ret ~= E.init))
968 //Issue: if E has an impure postblit, then all of arrayAllocImpl
969 //Will be impure, even during non CTFE.
970 foreach (i; 0 .. size)
971 ret ~= E.init;
973 catch (Exception e)
974 assert(0, e.msg);
976 else
977 assert(0, "No postblit nor default init on " ~ E.stringof ~
978 ": At least one is required for CTFE.");
980 else
982 import core.stdc.string : memset;
985 NOTES:
986 _d_newarrayU is part of druntime, and creates an uninitialized
987 block, just like GC.malloc. However, it also sets the appropriate
988 bits, and sets up the block as an appendable array of type E[],
989 which will inform the GC how to destroy the items in the block
990 when it gets collected.
992 _d_newarrayU returns a void[], but with the length set according
993 to E.sizeof.
995 *(cast(void[]*)&ret) = _d_newarrayU(typeid(E[]), size);
996 static if (minimallyInitialized && hasIndirections!E)
997 // _d_newarrayU would have asserted if the multiplication below
998 // had overflowed, so we don't have to check it again.
999 memset(ret.ptr, 0, E.sizeof * ret.length);
1002 else static if (I.length > 1)
1004 ret = arrayAllocImpl!(false, E[])(size);
1005 foreach (ref elem; ret)
1006 elem = arrayAllocImpl!(minimallyInitialized, E)(sizes[1..$]);
1009 return ret;
1012 @safe nothrow pure unittest
1014 auto s1 = uninitializedArray!(int[])();
1015 auto s2 = minimallyInitializedArray!(int[])();
1016 assert(s1.length == 0);
1017 assert(s2.length == 0);
1020 // https://issues.dlang.org/show_bug.cgi?id=9803
1021 @safe nothrow pure unittest
1023 auto a = minimallyInitializedArray!(int*[])(1);
1024 assert(a[0] == null);
1025 auto b = minimallyInitializedArray!(int[][])(1);
1026 assert(b[0].empty);
1027 auto c = minimallyInitializedArray!(int*[][])(1, 1);
1028 assert(c[0][0] == null);
1031 // https://issues.dlang.org/show_bug.cgi?id=10637
1032 @safe pure nothrow unittest
1034 static struct S
1036 static struct I{int i; alias i this;}
1037 int* p;
1038 this() @disable;
1039 this(int i)
1041 p = &(new I(i)).i;
1043 this(this)
1045 p = &(new I(*p)).i;
1047 ~this()
1049 // note, this assert is invalid -- a struct should always be able
1050 // to run its dtor on the .init value, I'm leaving it here
1051 // commented out because the original test case had it. I'm not
1052 // sure what it's trying to prove.
1054 // What happens now that minimallyInitializedArray adds the
1055 // destructor run to the GC, is that this assert would fire in the
1056 // GC, which triggers an invalid memory operation.
1057 //assert(p != null);
1060 auto a = minimallyInitializedArray!(S[])(1);
1061 assert(a[0].p == null);
1062 enum b = minimallyInitializedArray!(S[])(1);
1063 assert(b[0].p == null);
1066 @safe pure nothrow unittest
1068 static struct S1
1070 this() @disable;
1071 this(this) @disable;
1073 auto a1 = minimallyInitializedArray!(S1[][])(2, 2);
1074 assert(a1);
1075 static struct S2
1077 this() @disable;
1078 //this(this) @disable;
1080 auto a2 = minimallyInitializedArray!(S2[][])(2, 2);
1081 assert(a2);
1082 enum b2 = minimallyInitializedArray!(S2[][])(2, 2);
1083 assert(b2);
1084 static struct S3
1086 //this() @disable;
1087 this(this) @disable;
1089 auto a3 = minimallyInitializedArray!(S3[][])(2, 2);
1090 assert(a3);
1091 enum b3 = minimallyInitializedArray!(S3[][])(2, 2);
1092 assert(b3);
1096 Returns the overlapping portion, if any, of two arrays. Unlike `equal`,
1097 `overlap` only compares the pointers and lengths in the
1098 ranges, not the values referred by them. If `r1` and `r2` have an
1099 overlapping slice, returns that slice. Otherwise, returns the null
1100 slice.
1102 Params:
1103 a = The first array to compare
1104 b = The second array to compare
1105 Returns:
1106 The overlapping portion of the two arrays.
1108 CommonType!(T[], U[]) overlap(T, U)(T[] a, U[] b) @trusted
1109 if (is(typeof(a.ptr < b.ptr) == bool))
1111 import std.algorithm.comparison : min;
1113 auto end = min(a.ptr + a.length, b.ptr + b.length);
1114 // CTFE requires pairing pointer comparisons, which forces a
1115 // slightly inefficient implementation.
1116 if (a.ptr <= b.ptr && b.ptr < a.ptr + a.length)
1118 return b.ptr[0 .. end - b.ptr];
1121 if (b.ptr <= a.ptr && a.ptr < b.ptr + b.length)
1123 return a.ptr[0 .. end - a.ptr];
1126 return null;
1130 @safe pure nothrow unittest
1132 int[] a = [ 10, 11, 12, 13, 14 ];
1133 int[] b = a[1 .. 3];
1134 assert(overlap(a, b) == [ 11, 12 ]);
1135 b = b.dup;
1136 // overlap disappears even though the content is the same
1137 assert(overlap(a, b).empty);
1139 static test()() @nogc
1141 auto a = "It's three o'clock"d;
1142 auto b = a[5 .. 10];
1143 return b.overlap(a);
1146 //works at compile-time
1147 static assert(test == "three"d);
1150 @safe pure nothrow unittest
1152 static void test(L, R)(L l, R r)
1154 assert(overlap(l, r) == [ 100, 12 ]);
1156 assert(overlap(l, l[0 .. 2]) is l[0 .. 2]);
1157 assert(overlap(l, l[3 .. 5]) is l[3 .. 5]);
1158 assert(overlap(l[0 .. 2], l) is l[0 .. 2]);
1159 assert(overlap(l[3 .. 5], l) is l[3 .. 5]);
1162 int[] a = [ 10, 11, 12, 13, 14 ];
1163 int[] b = a[1 .. 3];
1164 a[1] = 100;
1166 immutable int[] c = a.idup;
1167 immutable int[] d = c[1 .. 3];
1169 test(a, b);
1170 assert(overlap(a, b.dup).empty);
1171 test(c, d);
1172 assert(overlap(c, d.dup.idup).empty);
1175 // https://issues.dlang.org/show_bug.cgi?id=9836
1176 @safe pure nothrow unittest
1178 // range primitives for array should work with alias this types
1179 struct Wrapper
1181 int[] data;
1182 alias data this;
1184 @property Wrapper save() { return this; }
1186 auto w = Wrapper([1,2,3,4]);
1187 std.array.popFront(w); // should work
1189 static assert(isInputRange!Wrapper);
1190 static assert(isForwardRange!Wrapper);
1191 static assert(isBidirectionalRange!Wrapper);
1192 static assert(isRandomAccessRange!Wrapper);
1195 private void copyBackwards(T)(T[] src, T[] dest)
1197 import core.stdc.string : memmove;
1198 import std.format : format;
1200 assert(src.length == dest.length, format!
1201 "src.length %s must equal dest.length %s"(src.length, dest.length));
1203 if (!__ctfe || hasElaborateCopyConstructor!T)
1205 /* insertInPlace relies on dest being uninitialized, so no postblits allowed,
1206 * as this is a MOVE that overwrites the destination, not a COPY.
1207 * BUG: insertInPlace will not work with ctfe and postblits
1209 memmove(dest.ptr, src.ptr, src.length * T.sizeof);
1211 else
1213 immutable len = src.length;
1214 for (size_t i = len; i-- > 0;)
1216 dest[i] = src[i];
1222 Inserts `stuff` (which must be an input range or any number of
1223 implicitly convertible items) in `array` at position `pos`.
1225 Params:
1226 array = The array that `stuff` will be inserted into.
1227 pos = The position in `array` to insert the `stuff`.
1228 stuff = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives),
1229 or any number of implicitly convertible items to insert into `array`.
1231 void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
1232 if (!isSomeString!(T[])
1233 && allSatisfy!(isInputRangeOrConvertible!T, U) && U.length > 0)
1235 static if (allSatisfy!(isInputRangeWithLengthOrConvertible!T, U))
1237 import core.internal.lifetime : emplaceRef;
1239 immutable oldLen = array.length;
1241 size_t to_insert = 0;
1242 foreach (i, E; U)
1244 static if (is(E : T)) //a single convertible value, not a range
1245 to_insert += 1;
1246 else
1247 to_insert += stuff[i].length;
1249 if (to_insert)
1251 array.length += to_insert;
1253 // Takes arguments array, pos, stuff
1254 // Spread apart array[] at pos by moving elements
1255 (() @trusted { copyBackwards(array[pos .. oldLen], array[pos+to_insert..$]); })();
1257 // Initialize array[pos .. pos+to_insert] with stuff[]
1258 auto j = 0;
1259 foreach (i, E; U)
1261 static if (is(E : T))
1263 emplaceRef!T(array[pos + j++], stuff[i]);
1265 else
1267 foreach (v; stuff[i])
1269 emplaceRef!T(array[pos + j++], v);
1275 else
1277 // stuff has some InputRanges in it that don't have length
1278 // assume that stuff to be inserted is typically shorter
1279 // then the array that can be arbitrary big
1280 // TODO: needs a better implementation as there is no need to build an _array_
1281 // a singly-linked list of memory blocks (rope, etc.) will do
1282 auto app = appender!(T[])();
1283 foreach (i, E; U)
1284 app.put(stuff[i]);
1285 insertInPlace(array, pos, app.data);
1289 /// Ditto
1290 void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
1291 if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U))
1293 static if (is(Unqual!T == T)
1294 && allSatisfy!(isInputRangeWithLengthOrConvertible!dchar, U))
1296 import std.utf : codeLength, byDchar;
1297 // mutable, can do in place
1298 //helper function: re-encode dchar to Ts and store at *ptr
1299 static T* putDChar(T* ptr, dchar ch)
1301 static if (is(T == dchar))
1303 *ptr++ = ch;
1304 return ptr;
1306 else
1308 import std.utf : encode;
1309 T[dchar.sizeof/T.sizeof] buf;
1310 immutable len = encode(buf, ch);
1311 final switch (len)
1313 static if (T.sizeof == char.sizeof)
1315 case 4:
1316 ptr[3] = buf[3];
1317 goto case;
1318 case 3:
1319 ptr[2] = buf[2];
1320 goto case;
1322 case 2:
1323 ptr[1] = buf[1];
1324 goto case;
1325 case 1:
1326 ptr[0] = buf[0];
1328 ptr += len;
1329 return ptr;
1332 size_t to_insert = 0;
1333 //count up the number of *codeunits* to insert
1334 foreach (i, E; U)
1335 to_insert += codeLength!T(stuff[i]);
1336 array.length += to_insert;
1338 @trusted static void moveToRight(T[] arr, size_t gap)
1340 static assert(!hasElaborateCopyConstructor!T,
1341 "T must not have an elaborate copy constructor");
1342 import core.stdc.string : memmove;
1343 if (__ctfe)
1345 for (size_t i = arr.length - gap; i; --i)
1346 arr[gap + i - 1] = arr[i - 1];
1348 else
1349 memmove(arr.ptr + gap, arr.ptr, (arr.length - gap) * T.sizeof);
1351 moveToRight(array[pos .. $], to_insert);
1352 auto ptr = array.ptr + pos;
1353 foreach (i, E; U)
1355 static if (is(E : dchar))
1357 ptr = putDChar(ptr, stuff[i]);
1359 else
1361 foreach (ch; stuff[i].byDchar)
1362 ptr = putDChar(ptr, ch);
1365 assert(ptr == array.ptr + pos + to_insert, "(ptr == array.ptr + pos + to_insert) is false");
1367 else
1369 // immutable/const, just construct a new array
1370 auto app = appender!(T[])();
1371 app.put(array[0 .. pos]);
1372 foreach (i, E; U)
1373 app.put(stuff[i]);
1374 app.put(array[pos..$]);
1375 array = app.data;
1380 @safe pure unittest
1382 int[] a = [ 1, 2, 3, 4 ];
1383 a.insertInPlace(2, [ 1, 2 ]);
1384 assert(a == [ 1, 2, 1, 2, 3, 4 ]);
1385 a.insertInPlace(3, 10u, 11);
1386 assert(a == [ 1, 2, 1, 10, 11, 2, 3, 4]);
1388 union U
1390 float a = 3.0;
1391 int b;
1394 U u1 = { b : 3 };
1395 U u2 = { b : 4 };
1396 U u3 = { b : 5 };
1397 U[] unionArr = [u2, u3];
1398 unionArr.insertInPlace(2, [u1]);
1399 assert(unionArr == [u2, u3, u1]);
1400 unionArr.insertInPlace(0, [u3, u2]);
1401 assert(unionArr == [u3, u2, u2, u3, u1]);
1403 static class C
1405 int a;
1406 float b;
1408 this(int a, float b) { this.a = a; this.b = b; }
1411 C c1 = new C(42, 1.0);
1412 C c2 = new C(0, 0.0);
1413 C c3 = new C(int.max, float.init);
1415 C[] classArr = [c1, c2, c3];
1416 insertInPlace(classArr, 3, [c2, c3]);
1417 C[5] classArr1 = classArr;
1418 assert(classArr1 == [c1, c2, c3, c2, c3]);
1419 insertInPlace(classArr, 0, c3, c1);
1420 C[7] classArr2 = classArr;
1421 assert(classArr2 == [c3, c1, c1, c2, c3, c2, c3]);
1424 //constraint helpers
1425 private template isInputRangeWithLengthOrConvertible(E)
1427 template isInputRangeWithLengthOrConvertible(R)
1429 //hasLength not defined for char[], wchar[] and dchar[]
1430 enum isInputRangeWithLengthOrConvertible =
1431 (isInputRange!R && is(typeof(R.init.length))
1432 && is(ElementType!R : E)) || is(R : E);
1436 //ditto
1437 private template isCharOrStringOrDcharRange(T)
1439 enum isCharOrStringOrDcharRange = isSomeString!T || isSomeChar!T ||
1440 (isInputRange!T && is(ElementType!T : dchar));
1443 //ditto
1444 private template isInputRangeOrConvertible(E)
1446 template isInputRangeOrConvertible(R)
1448 enum isInputRangeOrConvertible =
1449 (isInputRange!R && is(ElementType!R : E)) || is(R : E);
1453 @system unittest
1455 // @system due to insertInPlace
1456 import core.exception;
1457 import std.algorithm.comparison : equal;
1458 import std.algorithm.iteration : filter;
1459 import std.conv : to;
1460 import std.exception;
1463 bool test(T, U, V)(T orig, size_t pos, U toInsert, V result)
1466 static if (is(T == typeof(T.init.dup)))
1467 auto a = orig.dup;
1468 else
1469 auto a = orig.idup;
1471 a.insertInPlace(pos, toInsert);
1472 if (!equal(a, result))
1473 return false;
1476 static if (isInputRange!U)
1478 orig.insertInPlace(pos, filter!"true"(toInsert));
1479 return equal(orig, result);
1481 else
1482 return true;
1486 assert(test([1, 2, 3, 4], 0, [6, 7], [6, 7, 1, 2, 3, 4]));
1487 assert(test([1, 2, 3, 4], 2, [8, 9], [1, 2, 8, 9, 3, 4]));
1488 assert(test([1, 2, 3, 4], 4, [10, 11], [1, 2, 3, 4, 10, 11]));
1490 assert(test([1, 2, 3, 4], 0, 22, [22, 1, 2, 3, 4]));
1491 assert(test([1, 2, 3, 4], 2, 23, [1, 2, 23, 3, 4]));
1492 assert(test([1, 2, 3, 4], 4, 24, [1, 2, 3, 4, 24]));
1494 void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
1497 auto l = to!T("hello");
1498 auto r = to!U(" વિશ્વ");
1500 enforce(test(l, 0, r, " વિશ્વhello"),
1501 new AssertError("testStr failure 1", file, line));
1502 enforce(test(l, 3, r, "hel વિશ્વlo"),
1503 new AssertError("testStr failure 2", file, line));
1504 enforce(test(l, l.length, r, "hello વિશ્વ"),
1505 new AssertError("testStr failure 3", file, line));
1508 static foreach (T; AliasSeq!(char, wchar, dchar,
1509 immutable(char), immutable(wchar), immutable(dchar)))
1511 static foreach (U; AliasSeq!(char, wchar, dchar,
1512 immutable(char), immutable(wchar), immutable(dchar)))
1514 testStr!(T[], U[])();
1519 // variadic version
1520 bool testVar(T, U...)(T orig, size_t pos, U args)
1522 static if (is(T == typeof(T.init.dup)))
1523 auto a = orig.dup;
1524 else
1525 auto a = orig.idup;
1526 auto result = args[$-1];
1528 a.insertInPlace(pos, args[0..$-1]);
1529 if (!equal(a, result))
1530 return false;
1531 return true;
1533 assert(testVar([1, 2, 3, 4], 0, 6, 7u, [6, 7, 1, 2, 3, 4]));
1534 assert(testVar([1L, 2, 3, 4], 2, 8, 9L, [1, 2, 8, 9, 3, 4]));
1535 assert(testVar([1L, 2, 3, 4], 4, 10L, 11, [1, 2, 3, 4, 10, 11]));
1536 assert(testVar([1L, 2, 3, 4], 4, [10, 11], 40L, 42L,
1537 [1, 2, 3, 4, 10, 11, 40, 42]));
1538 assert(testVar([1L, 2, 3, 4], 4, 10, 11, [40L, 42],
1539 [1, 2, 3, 4, 10, 11, 40, 42]));
1540 assert(testVar("t".idup, 1, 'e', 's', 't', "test"));
1541 assert(testVar("!!"w.idup, 1, "\u00e9ll\u00f4", 'x', "TTT"w, 'y',
1542 "!\u00e9ll\u00f4xTTTy!"));
1543 assert(testVar("flipflop"d.idup, 4, '_',
1544 "xyz"w, '\U00010143', '_', "abc"d, "__",
1545 "flip_xyz\U00010143_abc__flop"));
1548 @system unittest
1550 import std.algorithm.comparison : equal;
1551 // insertInPlace interop with postblit
1552 static struct Int
1554 int* payload;
1555 this(int k)
1557 payload = new int;
1558 *payload = k;
1560 this(this)
1562 int* np = new int;
1563 *np = *payload;
1564 payload = np;
1566 ~this()
1568 if (payload)
1569 *payload = 0; //'destroy' it
1571 @property int getPayload(){ return *payload; }
1572 alias getPayload this;
1575 Int[] arr = [Int(1), Int(4), Int(5)];
1576 assert(arr[0] == 1);
1577 insertInPlace(arr, 1, Int(2), Int(3));
1578 assert(equal(arr, [1, 2, 3, 4, 5])); //check it works with postblit
1581 @safe unittest
1583 import std.exception;
1584 assertCTFEable!(
1586 int[] a = [1, 2];
1587 a.insertInPlace(2, 3);
1588 a.insertInPlace(0, -1, 0);
1589 return a == [-1, 0, 1, 2, 3];
1593 // https://issues.dlang.org/show_bug.cgi?id=6874
1594 @system unittest
1596 import core.memory;
1597 // allocate some space
1598 byte[] a;
1599 a.length = 1;
1601 // fill it
1602 a.length = a.capacity;
1604 // write beyond
1605 byte[] b = a[$ .. $];
1606 b.insertInPlace(0, a);
1608 // make sure that reallocation has happened
1609 assert(GC.addrOf(&b[0]) == GC.addrOf(&b[$-1]));
1614 Returns whether the `front`s of `lhs` and `rhs` both refer to the
1615 same place in memory, making one of the arrays a slice of the other which
1616 starts at index `0`.
1618 Params:
1619 lhs = the first array to compare
1620 rhs = the second array to compare
1621 Returns:
1622 `true` if $(D lhs.ptr == rhs.ptr), `false` otherwise.
1624 @safe
1625 pure nothrow @nogc bool sameHead(T)(in T[] lhs, in T[] rhs)
1627 return lhs.ptr == rhs.ptr;
1631 @safe pure nothrow unittest
1633 auto a = [1, 2, 3, 4, 5];
1634 auto b = a[0 .. 2];
1636 assert(a.sameHead(b));
1641 Returns whether the `back`s of `lhs` and `rhs` both refer to the
1642 same place in memory, making one of the arrays a slice of the other which
1643 end at index `$`.
1645 Params:
1646 lhs = the first array to compare
1647 rhs = the second array to compare
1648 Returns:
1649 `true` if both arrays are the same length and $(D lhs.ptr == rhs.ptr),
1650 `false` otherwise.
1652 @trusted
1653 pure nothrow @nogc bool sameTail(T)(in T[] lhs, in T[] rhs)
1655 return lhs.ptr + lhs.length == rhs.ptr + rhs.length;
1659 @safe pure nothrow unittest
1661 auto a = [1, 2, 3, 4, 5];
1662 auto b = a[3..$];
1664 assert(a.sameTail(b));
1667 @safe pure nothrow unittest
1669 static foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
1671 T a = [1, 2, 3, 4, 5];
1672 T b = a;
1673 T c = a[1 .. $];
1674 T d = a[0 .. 1];
1675 T e = null;
1677 assert(sameHead(a, a));
1678 assert(sameHead(a, b));
1679 assert(!sameHead(a, c));
1680 assert(sameHead(a, d));
1681 assert(!sameHead(a, e));
1683 assert(sameTail(a, a));
1684 assert(sameTail(a, b));
1685 assert(sameTail(a, c));
1686 assert(!sameTail(a, d));
1687 assert(!sameTail(a, e));
1689 //verifies R-value compatibilty
1690 assert(a.sameHead(a[0 .. 0]));
1691 assert(a.sameTail(a[$ .. $]));
1696 Params:
1697 s = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
1698 or a dynamic array
1699 n = number of times to repeat `s`
1701 Returns:
1702 An array that consists of `s` repeated `n` times. This function allocates, fills, and
1703 returns a new array.
1705 See_Also:
1706 For a lazy version, refer to $(REF repeat, std,range).
1708 ElementEncodingType!S[] replicate(S)(S s, size_t n)
1709 if (isDynamicArray!S)
1711 alias RetType = ElementEncodingType!S[];
1713 // Optimization for return join(std.range.repeat(s, n));
1714 if (n == 0)
1715 return RetType.init;
1716 if (n == 1)
1717 return cast(RetType) s;
1718 auto r = new Unqual!(typeof(s[0]))[n * s.length];
1719 if (s.length == 1)
1720 r[] = s[0];
1721 else
1723 immutable len = s.length, nlen = n * len;
1724 for (size_t i = 0; i < nlen; i += len)
1726 r[i .. i + len] = s[];
1729 return r;
1732 /// ditto
1733 ElementType!S[] replicate(S)(S s, size_t n)
1734 if (isInputRange!S && !isDynamicArray!S)
1736 import std.range : repeat;
1737 return join(std.range.repeat(s, n));
1742 @safe unittest
1744 auto a = "abc";
1745 auto s = replicate(a, 3);
1747 assert(s == "abcabcabc");
1749 auto b = [1, 2, 3];
1750 auto c = replicate(b, 3);
1752 assert(c == [1, 2, 3, 1, 2, 3, 1, 2, 3]);
1754 auto d = replicate(b, 0);
1756 assert(d == []);
1759 @safe unittest
1761 import std.conv : to;
1763 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
1765 immutable S t = "abc";
1767 assert(replicate(to!S("1234"), 0) is null);
1768 assert(replicate(to!S("1234"), 0) is null);
1769 assert(replicate(to!S("1234"), 1) == "1234");
1770 assert(replicate(to!S("1234"), 2) == "12341234");
1771 assert(replicate(to!S("1"), 4) == "1111");
1772 assert(replicate(t, 3) == "abcabcabc");
1773 assert(replicate(cast(S) null, 4) is null);
1778 Eagerly splits `range` into an array, using `sep` as the delimiter.
1780 When no delimiter is provided, strings are split into an array of words,
1781 using whitespace as delimiter.
1782 Runs of whitespace are merged together (no empty words are produced).
1784 The `range` must be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives).
1785 The separator can be a value of the same type as the elements in `range`
1786 or it can be another forward `range`.
1788 Params:
1789 s = the string to split by word if no separator is given
1790 range = the range to split
1791 sep = a value of the same type as the elements of `range` or another
1792 isTerminator = a predicate that splits the range when it returns `true`.
1794 Returns:
1795 An array containing the divided parts of `range` (or the words of `s`).
1797 See_Also:
1798 $(REF splitter, std,algorithm,iteration) for a lazy version without allocating memory.
1800 $(REF splitter, std,regex) for a version that splits using a regular
1801 expression defined separator.
1803 S[] split(S)(S s) @safe pure
1804 if (isSomeString!S)
1806 size_t istart;
1807 bool inword = false;
1808 auto result = appender!(S[]);
1810 foreach (i, dchar c ; s)
1812 import std.uni : isWhite;
1813 if (isWhite(c))
1815 if (inword)
1817 put(result, s[istart .. i]);
1818 inword = false;
1821 else
1823 if (!inword)
1825 istart = i;
1826 inword = true;
1830 if (inword)
1831 put(result, s[istart .. $]);
1832 return result.data;
1836 @safe unittest
1838 import std.uni : isWhite;
1839 assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]);
1840 assert("Learning D is fun".split!isWhite == ["Learning", "D", "is", "fun"]);
1841 assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]);
1845 @safe unittest
1847 string str = "Hello World!";
1848 assert(str.split == ["Hello", "World!"]);
1850 string str2 = "Hello\t\tWorld\t!";
1851 assert(str2.split == ["Hello", "World", "!"]);
1854 @safe unittest
1856 import std.conv : to;
1857 import std.format : format;
1858 import std.typecons;
1860 static auto makeEntry(S)(string l, string[] r)
1861 {return tuple(l.to!S(), r.to!(S[])());}
1863 static foreach (S; AliasSeq!(string, wstring, dstring,))
1865 auto entries =
1867 makeEntry!S("", []),
1868 makeEntry!S(" ", []),
1869 makeEntry!S("hello", ["hello"]),
1870 makeEntry!S(" hello ", ["hello"]),
1871 makeEntry!S(" h e l l o ", ["h", "e", "l", "l", "o"]),
1872 makeEntry!S("peter\t\npaul\rjerry", ["peter", "paul", "jerry"]),
1873 makeEntry!S(" \t\npeter paul\tjerry \n", ["peter", "paul", "jerry"]),
1874 makeEntry!S("\u2000\u202F\u205F\u3000", ["日", "本", "語"]),
1875 makeEntry!S("  哈・郎博尔德}    ___一个", ["哈・郎博尔德}", "___一个"])
1877 foreach (entry; entries)
1878 assert(entry[0].split() == entry[1], format("got: %s, expected: %s.", entry[0].split(), entry[1]));
1881 //Just to test that an immutable is split-able
1882 immutable string s = " \t\npeter paul\tjerry \n";
1883 assert(split(s) == ["peter", "paul", "jerry"]);
1886 @safe unittest //purity, ctfe ...
1888 import std.exception;
1889 void dg() @safe pure {
1890 assert(split("hello world"c) == ["hello"c, "world"c]);
1891 assert(split("hello world"w) == ["hello"w, "world"w]);
1892 assert(split("hello world"d) == ["hello"d, "world"d]);
1894 dg();
1895 assertCTFEable!dg;
1899 @safe unittest
1901 assert(split("hello world") == ["hello","world"]);
1902 assert(split("192.168.0.1", ".") == ["192", "168", "0", "1"]);
1904 auto a = split([1, 2, 3, 4, 5, 1, 2, 3, 4, 5], [2, 3]);
1905 assert(a == [[1], [4, 5, 1], [4, 5]]);
1908 ///ditto
1909 auto split(Range, Separator)(Range range, Separator sep)
1910 if (isForwardRange!Range && (
1911 is(typeof(ElementType!Range.init == Separator.init)) ||
1912 is(typeof(ElementType!Range.init == ElementType!Separator.init)) && isForwardRange!Separator
1915 import std.algorithm.iteration : splitter;
1916 return range.splitter(sep).array;
1918 ///ditto
1919 auto split(alias isTerminator, Range)(Range range)
1920 if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(range.front))))
1922 import std.algorithm.iteration : splitter;
1923 return range.splitter!isTerminator.array;
1926 @safe unittest
1928 import std.algorithm.comparison : cmp;
1929 import std.conv;
1931 static foreach (S; AliasSeq!(string, wstring, dstring,
1932 immutable(string), immutable(wstring), immutable(dstring),
1933 char[], wchar[], dchar[],
1934 const(char)[], const(wchar)[], const(dchar)[],
1935 const(char[]), immutable(char[])))
1937 S s = to!S(",peter,paul,jerry,");
1939 auto words = split(s, ",");
1940 assert(words.length == 5, text(words.length));
1941 assert(cmp(words[0], "") == 0);
1942 assert(cmp(words[1], "peter") == 0);
1943 assert(cmp(words[2], "paul") == 0);
1944 assert(cmp(words[3], "jerry") == 0);
1945 assert(cmp(words[4], "") == 0);
1947 auto s1 = s[0 .. s.length - 1]; // lop off trailing ','
1948 words = split(s1, ",");
1949 assert(words.length == 4);
1950 assert(cmp(words[3], "jerry") == 0);
1952 auto s2 = s1[1 .. s1.length]; // lop off leading ','
1953 words = split(s2, ",");
1954 assert(words.length == 3);
1955 assert(cmp(words[0], "peter") == 0);
1957 auto s3 = to!S(",,peter,,paul,,jerry,,");
1959 words = split(s3, ",,");
1960 assert(words.length == 5);
1961 assert(cmp(words[0], "") == 0);
1962 assert(cmp(words[1], "peter") == 0);
1963 assert(cmp(words[2], "paul") == 0);
1964 assert(cmp(words[3], "jerry") == 0);
1965 assert(cmp(words[4], "") == 0);
1967 auto s4 = s3[0 .. s3.length - 2]; // lop off trailing ',,'
1968 words = split(s4, ",,");
1969 assert(words.length == 4);
1970 assert(cmp(words[3], "jerry") == 0);
1972 auto s5 = s4[2 .. s4.length]; // lop off leading ',,'
1973 words = split(s5, ",,");
1974 assert(words.length == 3);
1975 assert(cmp(words[0], "peter") == 0);
1980 Conservative heuristic to determine if a range can be iterated cheaply.
1981 Used by `join` in decision to do an extra iteration of the range to
1982 compute the resultant length. If iteration is not cheap then precomputing
1983 length could be more expensive than using `Appender`.
1985 For now, we only assume arrays are cheap to iterate.
1987 private enum bool hasCheapIteration(R) = isArray!R;
1990 Eagerly concatenates all of the ranges in `ror` together (with the GC)
1991 into one array using `sep` as the separator if present.
1993 Params:
1994 ror = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
1995 of input ranges
1996 sep = An input range, or a single element, to join the ranges on
1998 Returns:
1999 An array of elements
2001 See_Also:
2002 For a lazy version, see $(REF joiner, std,algorithm,iteration)
2004 ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep)
2005 if (isInputRange!RoR &&
2006 isInputRange!(Unqual!(ElementType!RoR)) &&
2007 isInputRange!R &&
2008 (is(immutable ElementType!(ElementType!RoR) == immutable ElementType!R) ||
2009 (isSomeChar!(ElementType!(ElementType!RoR)) && isSomeChar!(ElementType!R))
2012 alias RetType = typeof(return);
2013 alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
2014 alias RoRElem = ElementType!RoR;
2016 if (ror.empty)
2017 return RetType.init;
2019 // Constraint only requires input range for sep.
2020 // This converts sep to an array (forward range) if it isn't one,
2021 // and makes sure it has the same string encoding for string types.
2022 static if (isSomeString!RetType &&
2023 !is(immutable ElementEncodingType!RetType == immutable ElementEncodingType!R))
2025 import std.conv : to;
2026 auto sepArr = to!RetType(sep);
2028 else static if (!isArray!R)
2029 auto sepArr = array(sep);
2030 else
2031 alias sepArr = sep;
2033 static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
2035 import core.internal.lifetime : emplaceRef;
2036 size_t length; // length of result array
2037 size_t rorLength; // length of range ror
2038 foreach (r; ror.save)
2040 length += r.length;
2041 ++rorLength;
2043 if (!rorLength)
2044 return null;
2045 length += (rorLength - 1) * sepArr.length;
2047 auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))();
2048 size_t len;
2049 foreach (e; ror.front)
2050 emplaceRef(result[len++], e);
2051 ror.popFront();
2052 foreach (r; ror)
2054 foreach (e; sepArr)
2055 emplaceRef(result[len++], e);
2056 foreach (e; r)
2057 emplaceRef(result[len++], e);
2059 assert(len == result.length);
2060 return (() @trusted => cast(RetType) result)();
2062 else
2064 auto result = appender!RetType();
2065 put(result, ror.front);
2066 ror.popFront();
2067 for (; !ror.empty; ror.popFront())
2069 put(result, sepArr);
2070 put(result, ror.front);
2072 return result.data;
2076 // https://issues.dlang.org/show_bug.cgi?id=14230
2077 @safe unittest
2079 string[] ary = ["","aa","bb","cc"]; // leaded by _empty_ element
2080 assert(ary.join(" @") == " @aa @bb @cc"); // OK in 2.067b1 and olders
2083 // https://issues.dlang.org/show_bug.cgi?id=21337
2084 @system unittest
2086 import std.algorithm.iteration : map;
2088 static class Once
2090 bool empty;
2092 void popFront()
2094 empty = true;
2097 int front()
2099 return 0;
2103 assert([1, 2].map!"[a]".join(new Once) == [1, 0, 2]);
2106 /// Ditto
2107 ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, scope E sep)
2108 if (isInputRange!RoR &&
2109 isInputRange!(Unqual!(ElementType!RoR)) &&
2110 ((is(E : ElementType!(ElementType!RoR))) ||
2111 (!autodecodeStrings && isSomeChar!(ElementType!(ElementType!RoR)) &&
2112 isSomeChar!E)))
2114 alias RetType = typeof(return);
2115 alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
2116 alias RoRElem = ElementType!RoR;
2118 if (ror.empty)
2119 return RetType.init;
2121 static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
2123 static if (isSomeChar!E && isSomeChar!RetTypeElement && E.sizeof > RetTypeElement.sizeof)
2125 import std.utf : encode;
2126 RetTypeElement[4 / RetTypeElement.sizeof] encodeSpace;
2127 immutable size_t sepArrLength = encode(encodeSpace, sep);
2128 return join(ror, encodeSpace[0 .. sepArrLength]);
2130 else
2132 import core.internal.lifetime : emplaceRef;
2133 import std.format : format;
2134 size_t length;
2135 size_t rorLength;
2136 foreach (r; ror.save)
2138 length += r.length;
2139 ++rorLength;
2141 if (!rorLength)
2142 return null;
2143 length += rorLength - 1;
2144 auto result = uninitializedArray!(RetTypeElement[])(length);
2147 size_t len;
2148 foreach (e; ror.front)
2149 emplaceRef(result[len++], e);
2150 ror.popFront();
2151 foreach (r; ror)
2153 emplaceRef(result[len++], sep);
2154 foreach (e; r)
2155 emplaceRef(result[len++], e);
2157 assert(len == result.length, format!
2158 "len %s must equal result.lenght %s"(len, result.length));
2159 return (() @trusted => cast(RetType) result)();
2162 else
2164 auto result = appender!RetType();
2165 put(result, ror.front);
2166 ror.popFront();
2167 for (; !ror.empty; ror.popFront())
2169 put(result, sep);
2170 put(result, ror.front);
2172 return result.data;
2176 // https://issues.dlang.org/show_bug.cgi?id=10895
2177 @safe unittest
2179 class A
2181 string name;
2182 alias name this;
2183 this(string name) { this.name = name; }
2185 auto a = [new A(`foo`)];
2186 assert(a[0].length == 3);
2187 auto temp = join(a, " ");
2188 assert(a[0].length == 3);
2189 assert(temp.length == 3);
2192 // https://issues.dlang.org/show_bug.cgi?id=14230
2193 @safe unittest
2195 string[] ary = ["","aa","bb","cc"];
2196 assert(ary.join('@') == "@aa@bb@cc");
2199 /// Ditto
2200 ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror)
2201 if (isInputRange!RoR &&
2202 isInputRange!(Unqual!(ElementType!RoR)))
2204 alias RetType = typeof(return);
2205 alias ConstRetTypeElement = ElementEncodingType!RetType;
2206 static if (isAssignable!(Unqual!ConstRetTypeElement, ConstRetTypeElement))
2208 alias RetTypeElement = Unqual!ConstRetTypeElement;
2210 else
2212 alias RetTypeElement = ConstRetTypeElement;
2214 alias RoRElem = ElementType!RoR;
2216 if (ror.empty)
2217 return RetType.init;
2219 static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
2221 import core.internal.lifetime : emplaceRef;
2222 size_t length;
2223 foreach (r; ror.save)
2224 length += r.length;
2226 auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))();
2227 size_t len;
2228 foreach (r; ror)
2229 foreach (e; r)
2230 emplaceRef!RetTypeElement(result[len++], e);
2231 assert(len == result.length,
2232 "emplaced an unexpected number of elements");
2233 return (() @trusted => cast(RetType) result)();
2235 else
2237 auto result = appender!RetType();
2238 for (; !ror.empty; ror.popFront())
2239 put(result, ror.front);
2240 return result.data;
2245 @safe pure nothrow unittest
2247 assert(join(["hello", "silly", "world"], " ") == "hello silly world");
2248 assert(join(["hello", "silly", "world"]) == "hellosillyworld");
2250 assert(join([[1, 2, 3], [4, 5]], [72, 73]) == [1, 2, 3, 72, 73, 4, 5]);
2251 assert(join([[1, 2, 3], [4, 5]]) == [1, 2, 3, 4, 5]);
2253 const string[] arr = ["apple", "banana"];
2254 assert(arr.join(",") == "apple,banana");
2255 assert(arr.join() == "applebanana");
2258 @safe pure unittest
2260 import std.conv : to;
2261 import std.range.primitives : autodecodeStrings;
2263 static foreach (T; AliasSeq!(string,wstring,dstring))
2265 auto arr2 = "Здравствуй Мир Unicode".to!(T);
2266 auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]);
2267 assert(join(arr) == "ЗдравствуйМирUnicode");
2268 static foreach (S; AliasSeq!(char,wchar,dchar))
2270 auto jarr = arr.join(to!S(' '));
2271 static assert(is(typeof(jarr) == T));
2272 assert(jarr == arr2);
2274 static foreach (S; AliasSeq!(string,wstring,dstring))
2276 auto jarr = arr.join(to!S(" "));
2277 static assert(is(typeof(jarr) == T));
2278 assert(jarr == arr2);
2282 static foreach (T; AliasSeq!(string,wstring,dstring))
2284 auto arr2 = "Здравствуй\u047CМир\u047CUnicode".to!(T);
2285 auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]);
2286 static foreach (S; AliasSeq!(wchar,dchar))
2288 auto jarr = arr.join(to!S('\u047C'));
2289 static assert(is(typeof(jarr) == T));
2290 assert(jarr == arr2);
2294 const string[] arr = ["apple", "banana"];
2295 assert(arr.join(',') == "apple,banana");
2298 @safe unittest
2300 class A { }
2302 const A[][] array;
2303 auto result = array.join; // can't remove constness, so don't try
2305 static assert(is(typeof(result) == const(A)[]));
2308 @safe unittest
2310 import std.algorithm;
2311 import std.conv : to;
2312 import std.range;
2314 static foreach (R; AliasSeq!(string, wstring, dstring))
2316 R word1 = "日本語";
2317 R word2 = "paul";
2318 R word3 = "jerry";
2319 R[] words = [word1, word2, word3];
2321 auto filteredWord1 = filter!"true"(word1);
2322 auto filteredLenWord1 = takeExactly(filteredWord1, word1.walkLength());
2323 auto filteredWord2 = filter!"true"(word2);
2324 auto filteredLenWord2 = takeExactly(filteredWord2, word2.walkLength());
2325 auto filteredWord3 = filter!"true"(word3);
2326 auto filteredLenWord3 = takeExactly(filteredWord3, word3.walkLength());
2327 auto filteredWordsArr = [filteredWord1, filteredWord2, filteredWord3];
2328 auto filteredLenWordsArr = [filteredLenWord1, filteredLenWord2, filteredLenWord3];
2329 auto filteredWords = filter!"true"(filteredWordsArr);
2331 static foreach (S; AliasSeq!(string, wstring, dstring))
2333 assert(join(filteredWords, to!S(", ")) == "日本語, paul, jerry");
2334 assert(join(filteredWords, to!(ElementType!S)(',')) == "日本語,paul,jerry");
2335 assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry");
2336 assert(join(filteredWordsArr, to!S(", ")) == "日本語, paul, jerry");
2337 assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry");
2338 assert(join(filteredLenWordsArr, to!S(", ")) == "日本語, paul, jerry");
2339 assert(join(filter!"true"(words), to!S(", ")) == "日本語, paul, jerry");
2340 assert(join(words, to!S(", ")) == "日本語, paul, jerry");
2342 assert(join(filteredWords, to!S("")) == "日本語pauljerry");
2343 assert(join(filteredWordsArr, to!S("")) == "日本語pauljerry");
2344 assert(join(filteredLenWordsArr, to!S("")) == "日本語pauljerry");
2345 assert(join(filter!"true"(words), to!S("")) == "日本語pauljerry");
2346 assert(join(words, to!S("")) == "日本語pauljerry");
2348 assert(join(filter!"true"([word1]), to!S(", ")) == "日本語");
2349 assert(join([filteredWord1], to!S(", ")) == "日本語");
2350 assert(join([filteredLenWord1], to!S(", ")) == "日本語");
2351 assert(join(filter!"true"([filteredWord1]), to!S(", ")) == "日本語");
2352 assert(join([word1], to!S(", ")) == "日本語");
2354 assert(join(filteredWords, to!S(word1)) == "日本語日本語paul日本語jerry");
2355 assert(join(filteredWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry");
2356 assert(join(filteredLenWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry");
2357 assert(join(filter!"true"(words), to!S(word1)) == "日本語日本語paul日本語jerry");
2358 assert(join(words, to!S(word1)) == "日本語日本語paul日本語jerry");
2360 auto filterComma = filter!"true"(to!S(", "));
2361 assert(join(filteredWords, filterComma) == "日本語, paul, jerry");
2362 assert(join(filteredWordsArr, filterComma) == "日本語, paul, jerry");
2363 assert(join(filteredLenWordsArr, filterComma) == "日本語, paul, jerry");
2364 assert(join(filter!"true"(words), filterComma) == "日本語, paul, jerry");
2365 assert(join(words, filterComma) == "日本語, paul, jerry");
2368 assert(join(filteredWords) == "日本語pauljerry");
2369 assert(join(filteredWordsArr) == "日本語pauljerry");
2370 assert(join(filteredLenWordsArr) == "日本語pauljerry");
2371 assert(join(filter!"true"(words)) == "日本語pauljerry");
2372 assert(join(words) == "日本語pauljerry");
2374 assert(join(filteredWords, filter!"true"(", ")) == "日本語, paul, jerry");
2375 assert(join(filteredWordsArr, filter!"true"(", ")) == "日本語, paul, jerry");
2376 assert(join(filteredLenWordsArr, filter!"true"(", ")) == "日本語, paul, jerry");
2377 assert(join(filter!"true"(words), filter!"true"(", ")) == "日本語, paul, jerry");
2378 assert(join(words, filter!"true"(", ")) == "日本語, paul, jerry");
2380 assert(join(filter!"true"(cast(typeof(filteredWordsArr))[]), ", ").empty);
2381 assert(join(cast(typeof(filteredWordsArr))[], ", ").empty);
2382 assert(join(cast(typeof(filteredLenWordsArr))[], ", ").empty);
2383 assert(join(filter!"true"(cast(R[])[]), ", ").empty);
2384 assert(join(cast(R[])[], ", ").empty);
2386 assert(join(filter!"true"(cast(typeof(filteredWordsArr))[])).empty);
2387 assert(join(cast(typeof(filteredWordsArr))[]).empty);
2388 assert(join(cast(typeof(filteredLenWordsArr))[]).empty);
2390 assert(join(filter!"true"(cast(R[])[])).empty);
2391 assert(join(cast(R[])[]).empty);
2394 assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2395 assert(join([[1, 2], [41, 42]], cast(int[])[]) == [1, 2, 41, 42]);
2396 assert(join([[1, 2]], [5, 6]) == [1, 2]);
2397 assert(join(cast(int[][])[], [5, 6]).empty);
2399 assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]);
2400 assert(join(cast(int[][])[]).empty);
2402 alias f = filter!"true";
2403 assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2404 assert(join(f([[1, 2], [41, 42]]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
2405 assert(join([f([1, 2]), f([41, 42])], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2406 assert(join(f([f([1, 2]), f([41, 42])]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
2407 assert(join([[1, 2], [41, 42]], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2408 assert(join(f([[1, 2], [41, 42]]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2409 assert(join([f([1, 2]), f([41, 42])], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2410 assert(join(f([f([1, 2]), f([41, 42])]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2413 // https://issues.dlang.org/show_bug.cgi?id=10683
2414 @safe unittest
2416 import std.range : join;
2417 import std.typecons : tuple;
2418 assert([[tuple(1)]].join == [tuple(1)]);
2419 assert([[tuple("x")]].join == [tuple("x")]);
2422 // https://issues.dlang.org/show_bug.cgi?id=13877
2423 @safe unittest
2425 // Test that the range is iterated only once.
2426 import std.algorithm.iteration : map;
2427 int c = 0;
2428 auto j1 = [1, 2, 3].map!(_ => [c++]).join;
2429 assert(c == 3);
2430 assert(j1 == [0, 1, 2]);
2432 c = 0;
2433 auto j2 = [1, 2, 3].map!(_ => [c++]).join(9);
2434 assert(c == 3);
2435 assert(j2 == [0, 9, 1, 9, 2]);
2437 c = 0;
2438 auto j3 = [1, 2, 3].map!(_ => [c++]).join([9]);
2439 assert(c == 3);
2440 assert(j3 == [0, 9, 1, 9, 2]);
2445 Replace occurrences of `from` with `to` in `subject` in a new array.
2447 Params:
2448 subject = the array to scan
2449 from = the item to replace
2450 to = the item to replace all instances of `from` with
2452 Returns:
2453 A new array without changing the contents of `subject`, or the original
2454 array if no match is found.
2456 See_Also:
2457 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2459 E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to)
2460 if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
2461 is(Unqual!E : Unqual!R1))
2463 import std.algorithm.searching : find;
2464 import std.range : dropOne;
2466 static if (isInputRange!R1)
2468 if (from.empty) return subject;
2469 alias rSave = a => a.save;
2471 else
2473 alias rSave = a => a;
2476 auto balance = find(subject, rSave(from));
2477 if (balance.empty)
2478 return subject;
2480 auto app = appender!(E[])();
2481 app.put(subject[0 .. subject.length - balance.length]);
2482 app.put(rSave(to));
2483 // replacing an element in an array is different to a range replacement
2484 static if (is(Unqual!E : Unqual!R1))
2485 replaceInto(app, balance.dropOne, from, to);
2486 else
2487 replaceInto(app, balance[from.length .. $], from, to);
2489 return app.data;
2493 @safe unittest
2495 assert("Hello Wörld".replace("o Wö", "o Wo") == "Hello World");
2496 assert("Hello Wörld".replace("l", "h") == "Hehho Wörhd");
2499 @safe unittest
2501 assert([1, 2, 3, 4, 2].replace([2], [5]) == [1, 5, 3, 4, 5]);
2502 assert([3, 3, 3].replace([3], [0]) == [0, 0, 0]);
2503 assert([3, 3, 4, 3].replace([3, 3], [1, 1, 1]) == [1, 1, 1, 4, 3]);
2506 // https://issues.dlang.org/show_bug.cgi?id=18215
2507 @safe unittest
2509 auto arr = ["aaa.dd", "b"];
2510 arr = arr.replace("aaa.dd", ".");
2511 assert(arr == [".", "b"]);
2513 arr = ["_", "_", "aaa.dd", "b", "c", "aaa.dd", "e"];
2514 arr = arr.replace("aaa.dd", ".");
2515 assert(arr == ["_", "_", ".", "b", "c", ".", "e"]);
2518 // https://issues.dlang.org/show_bug.cgi?id=18215
2519 @safe unittest
2521 assert([[0], [1, 2], [0], [3]].replace([0], [4]) == [[4], [1, 2], [4], [3]]);
2522 assert([[0], [1, 2], [0], [3], [1, 2]]
2523 .replace([1, 2], [0]) == [[0], [0], [0], [3], [0]]);
2524 assert([[0], [1, 2], [0], [3], [1, 2], [0], [1, 2]]
2525 .replace([[0], [1, 2]], [[4]]) == [[4], [0], [3], [1, 2], [4]]);
2528 // https://issues.dlang.org/show_bug.cgi?id=10930
2529 @safe unittest
2531 assert([0, 1, 2].replace(1, 4) == [0, 4, 2]);
2532 assert("äbö".replace('ä', 'a') == "abö");
2535 // empty array
2536 @safe unittest
2538 int[] arr;
2539 assert(replace(arr, 1, 2) == []);
2543 Replace occurrences of `from` with `to` in `subject` and output the result into
2544 `sink`.
2546 Params:
2547 sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
2548 subject = the array to scan
2549 from = the item to replace
2550 to = the item to replace all instances of `from` with
2552 See_Also:
2553 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2555 void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to)
2556 if (isOutputRange!(Sink, E) &&
2557 ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
2558 is(Unqual!E : Unqual!R1)))
2560 import std.algorithm.searching : find;
2561 import std.range : dropOne;
2563 static if (isInputRange!R1)
2565 if (from.empty)
2567 sink.put(subject);
2568 return;
2570 alias rSave = a => a.save;
2572 else
2574 alias rSave = a => a;
2576 for (;;)
2578 auto balance = find(subject, rSave(from));
2579 if (balance.empty)
2581 sink.put(subject);
2582 break;
2584 sink.put(subject[0 .. subject.length - balance.length]);
2585 sink.put(rSave(to));
2586 // replacing an element in an array is different to a range replacement
2587 static if (is(Unqual!E : Unqual!R1))
2588 subject = balance.dropOne;
2589 else
2590 subject = balance[from.length .. $];
2595 @safe unittest
2597 auto arr = [1, 2, 3, 4, 5];
2598 auto from = [2, 3];
2599 auto to = [4, 6];
2600 auto sink = appender!(int[])();
2602 replaceInto(sink, arr, from, to);
2604 assert(sink.data == [1, 4, 6, 4, 5]);
2607 // empty array
2608 @safe unittest
2610 auto sink = appender!(int[])();
2611 int[] arr;
2612 replaceInto(sink, arr, 1, 2);
2613 assert(sink.data == []);
2616 @safe unittest
2618 import std.algorithm.comparison : cmp;
2619 import std.conv : to;
2621 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
2623 static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
2625 auto s = to!S("This is a foo foo list");
2626 auto from = to!T("foo");
2627 auto into = to!S("silly");
2628 S r;
2629 int i;
2631 r = replace(s, from, into);
2632 i = cmp(r, "This is a silly silly list");
2633 assert(i == 0);
2635 r = replace(s, to!S(""), into);
2636 i = cmp(r, "This is a foo foo list");
2637 assert(i == 0);
2639 assert(replace(r, to!S("won't find this"), to!S("whatever")) is r);
2643 immutable s = "This is a foo foo list";
2644 assert(replace(s, "foo", "silly") == "This is a silly silly list");
2647 @safe unittest
2649 import std.algorithm.searching : skipOver;
2650 import std.conv : to;
2652 struct CheckOutput(C)
2654 C[] desired;
2655 this(C[] arr){ desired = arr; }
2656 void put(C[] part){ assert(skipOver(desired, part)); }
2658 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
2660 alias Char = ElementEncodingType!S;
2661 S s = to!S("yet another dummy text, yet another ...");
2662 S from = to!S("yet another");
2663 S into = to!S("some");
2664 replaceInto(CheckOutput!(Char)(to!S("some dummy text, some ..."))
2665 , s, from, into);
2669 // https://issues.dlang.org/show_bug.cgi?id=10930
2670 @safe unittest
2672 auto sink = appender!(int[])();
2673 replaceInto(sink, [0, 1, 2], 1, 5);
2674 assert(sink.data == [0, 5, 2]);
2676 auto sink2 = appender!(dchar[])();
2677 replaceInto(sink2, "äbö", 'ä', 'a');
2678 assert(sink2.data == "abö");
2682 Replaces elements from `array` with indices ranging from `from`
2683 (inclusive) to `to` (exclusive) with the range `stuff`.
2685 Params:
2686 subject = the array to scan
2687 from = the starting index
2688 to = the ending index
2689 stuff = the items to replace in-between `from` and `to`
2691 Returns:
2692 A new array without changing the contents of `subject`.
2694 See_Also:
2695 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2697 T[] replace(T, Range)(T[] subject, size_t from, size_t to, Range stuff)
2698 if (isInputRange!Range &&
2699 (is(ElementType!Range : T) ||
2700 isSomeString!(T[]) && is(ElementType!Range : dchar)))
2702 static if (hasLength!Range && is(ElementEncodingType!Range : T))
2704 import std.algorithm.mutation : copy;
2705 assert(from <= to, "from must be before or equal to to");
2706 immutable sliceLen = to - from;
2707 auto retval = new Unqual!(T)[](subject.length - sliceLen + stuff.length);
2708 retval[0 .. from] = subject[0 .. from];
2710 if (!stuff.empty)
2711 copy(stuff, retval[from .. from + stuff.length]);
2713 retval[from + stuff.length .. $] = subject[to .. $];
2714 static if (is(T == const) || is(T == immutable))
2716 return () @trusted { return cast(T[]) retval; } ();
2718 else
2720 return cast(T[]) retval;
2723 else
2725 auto app = appender!(T[])();
2726 app.put(subject[0 .. from]);
2727 app.put(stuff);
2728 app.put(subject[to .. $]);
2729 return app.data;
2734 @safe unittest
2736 auto a = [ 1, 2, 3, 4 ];
2737 auto b = a.replace(1, 3, [ 9, 9, 9 ]);
2738 assert(a == [ 1, 2, 3, 4 ]);
2739 assert(b == [ 1, 9, 9, 9, 4 ]);
2742 @system unittest
2744 import core.exception;
2745 import std.algorithm.iteration : filter;
2746 import std.conv : to;
2747 import std.exception;
2750 auto a = [ 1, 2, 3, 4 ];
2751 assert(replace(a, 0, 0, [5, 6, 7]) == [5, 6, 7, 1, 2, 3, 4]);
2752 assert(replace(a, 0, 2, cast(int[])[]) == [3, 4]);
2753 assert(replace(a, 0, 4, [5, 6, 7]) == [5, 6, 7]);
2754 assert(replace(a, 0, 2, [5, 6, 7]) == [5, 6, 7, 3, 4]);
2755 assert(replace(a, 2, 4, [5, 6, 7]) == [1, 2, 5, 6, 7]);
2757 assert(replace(a, 0, 0, filter!"true"([5, 6, 7])) == [5, 6, 7, 1, 2, 3, 4]);
2758 assert(replace(a, 0, 2, filter!"true"(cast(int[])[])) == [3, 4]);
2759 assert(replace(a, 0, 4, filter!"true"([5, 6, 7])) == [5, 6, 7]);
2760 assert(replace(a, 0, 2, filter!"true"([5, 6, 7])) == [5, 6, 7, 3, 4]);
2761 assert(replace(a, 2, 4, filter!"true"([5, 6, 7])) == [1, 2, 5, 6, 7]);
2762 assert(a == [ 1, 2, 3, 4 ]);
2764 void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
2767 auto l = to!T("hello");
2768 auto r = to!U(" world");
2770 enforce(replace(l, 0, 0, r) == " worldhello",
2771 new AssertError("testStr failure 1", file, line));
2772 enforce(replace(l, 0, 3, r) == " worldlo",
2773 new AssertError("testStr failure 2", file, line));
2774 enforce(replace(l, 3, l.length, r) == "hel world",
2775 new AssertError("testStr failure 3", file, line));
2776 enforce(replace(l, 0, l.length, r) == " world",
2777 new AssertError("testStr failure 4", file, line));
2778 enforce(replace(l, l.length, l.length, r) == "hello world",
2779 new AssertError("testStr failure 5", file, line));
2782 testStr!(string, string)();
2783 testStr!(string, wstring)();
2784 testStr!(string, dstring)();
2785 testStr!(wstring, string)();
2786 testStr!(wstring, wstring)();
2787 testStr!(wstring, dstring)();
2788 testStr!(dstring, string)();
2789 testStr!(dstring, wstring)();
2790 testStr!(dstring, dstring)();
2792 enum s = "0123456789";
2793 enum w = "⁰¹²³⁴⁵⁶⁷⁸⁹"w;
2794 enum d = "⁰¹²³⁴⁵⁶⁷⁸⁹"d;
2796 assert(replace(s, 0, 0, "***") == "***0123456789");
2797 assert(replace(s, 10, 10, "***") == "0123456789***");
2798 assert(replace(s, 3, 8, "1012") == "012101289");
2799 assert(replace(s, 0, 5, "43210") == "4321056789");
2800 assert(replace(s, 5, 10, "43210") == "0123443210");
2802 assert(replace(w, 0, 0, "***"w) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"w);
2803 assert(replace(w, 10, 10, "***"w) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"w);
2804 assert(replace(w, 3, 8, "¹⁰¹²"w) == "⁰¹²¹⁰¹²⁸⁹"w);
2805 assert(replace(w, 0, 5, "⁴³²¹⁰"w) == "⁴³²¹⁰⁵⁶⁷⁸⁹"w);
2806 assert(replace(w, 5, 10, "⁴³²¹⁰"w) == "⁰¹²³⁴⁴³²¹⁰"w);
2808 assert(replace(d, 0, 0, "***"d) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"d);
2809 assert(replace(d, 10, 10, "***"d) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"d);
2810 assert(replace(d, 3, 8, "¹⁰¹²"d) == "⁰¹²¹⁰¹²⁸⁹"d);
2811 assert(replace(d, 0, 5, "⁴³²¹⁰"d) == "⁴³²¹⁰⁵⁶⁷⁸⁹"d);
2812 assert(replace(d, 5, 10, "⁴³²¹⁰"d) == "⁰¹²³⁴⁴³²¹⁰"d);
2815 // https://issues.dlang.org/show_bug.cgi?id=18166
2816 @safe pure unittest
2818 auto str = replace("aaaaa"d, 1, 4, "***"d);
2819 assert(str == "a***a");
2823 Replaces elements from `array` with indices ranging from `from`
2824 (inclusive) to `to` (exclusive) with the range `stuff`. Expands or
2825 shrinks the array as needed.
2827 Params:
2828 array = the array to scan
2829 from = the starting index
2830 to = the ending index
2831 stuff = the items to replace in-between `from` and `to`
2833 void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
2834 if (is(typeof(replace(array, from, to, stuff))))
2836 static if (isDynamicArray!Range &&
2837 is(Unqual!(ElementEncodingType!Range) == T) &&
2838 !isNarrowString!(T[]))
2840 // optimized for homogeneous arrays that can be overwritten.
2841 import std.algorithm.mutation : remove;
2842 import std.typecons : tuple;
2844 if (overlap(array, stuff).length)
2846 // use slower/conservative method
2847 array = array[0 .. from] ~ stuff ~ array[to .. $];
2849 else if (stuff.length <= to - from)
2851 // replacement reduces length
2852 immutable stuffEnd = from + stuff.length;
2853 array[from .. stuffEnd] = stuff[];
2854 if (stuffEnd < to)
2855 array = remove(array, tuple(stuffEnd, to));
2857 else
2859 // replacement increases length
2860 // @@@TODO@@@: optimize this
2861 immutable replaceLen = to - from;
2862 array[from .. to] = stuff[0 .. replaceLen];
2863 insertInPlace(array, to, stuff[replaceLen .. $]);
2866 else
2868 // default implementation, just do what replace does.
2869 array = replace(array, from, to, stuff);
2874 @safe unittest
2876 int[] a = [1, 4, 5];
2877 replaceInPlace(a, 1u, 2u, [2, 3, 4]);
2878 assert(a == [1, 2, 3, 4, 5]);
2879 replaceInPlace(a, 1u, 2u, cast(int[])[]);
2880 assert(a == [1, 3, 4, 5]);
2881 replaceInPlace(a, 1u, 3u, a[2 .. 4]);
2882 assert(a == [1, 4, 5, 5]);
2885 // https://issues.dlang.org/show_bug.cgi?id=12889
2886 @safe unittest
2888 int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]];
2889 int[1][] stuff = [[0], [1]];
2890 replaceInPlace(arr, 4, 6, stuff);
2891 assert(arr == [[0], [1], [2], [3], [0], [1], [6]]);
2894 @system unittest
2896 // https://issues.dlang.org/show_bug.cgi?id=14925
2897 char[] a = "mon texte 1".dup;
2898 char[] b = "abc".dup;
2899 replaceInPlace(a, 4, 9, b);
2900 assert(a == "mon abc 1");
2902 // ensure we can replace in place with different encodings
2903 string unicoded = "\U00010437";
2904 string unicodedLong = "\U00010437aaaaa";
2905 string base = "abcXXXxyz";
2906 string result = "abc\U00010437xyz";
2907 string resultLong = "abc\U00010437aaaaaxyz";
2908 size_t repstart = 3;
2909 size_t repend = 3 + 3;
2911 void testStringReplaceInPlace(T, U)()
2913 import std.algorithm.comparison : equal;
2914 import std.conv;
2915 auto a = unicoded.to!(U[]);
2916 auto b = unicodedLong.to!(U[]);
2918 auto test = base.to!(T[]);
2920 test.replaceInPlace(repstart, repend, a);
2921 assert(equal(test, result), "Failed for types " ~ T.stringof ~ " and " ~ U.stringof);
2923 test = base.to!(T[]);
2925 test.replaceInPlace(repstart, repend, b);
2926 assert(equal(test, resultLong), "Failed for types " ~ T.stringof ~ " and " ~ U.stringof);
2929 import std.meta : AliasSeq;
2930 alias allChars = AliasSeq!(char, immutable(char), const(char),
2931 wchar, immutable(wchar), const(wchar),
2932 dchar, immutable(dchar), const(dchar));
2933 foreach (T; allChars)
2934 foreach (U; allChars)
2935 testStringReplaceInPlace!(T, U)();
2937 void testInout(inout(int)[] a)
2939 // will be transferred to the 'replace' function
2940 replaceInPlace(a, 1, 2, [1,2,3]);
2944 @safe unittest
2946 // the constraint for the first overload used to match this, which wouldn't compile.
2947 import std.algorithm.comparison : equal;
2948 long[] a = [1L, 2, 3];
2949 int[] b = [4, 5, 6];
2950 a.replaceInPlace(1, 2, b);
2951 assert(equal(a, [1L, 4, 5, 6, 3]));
2954 @system unittest
2956 import core.exception;
2957 import std.algorithm.comparison : equal;
2958 import std.algorithm.iteration : filter;
2959 import std.conv : to;
2960 import std.exception;
2963 bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result)
2966 static if (is(T == typeof(T.init.dup)))
2967 auto a = orig.dup;
2968 else
2969 auto a = orig.idup;
2971 a.replaceInPlace(from, to, toReplace);
2972 if (!equal(a, result))
2973 return false;
2976 static if (isInputRange!U)
2978 orig.replaceInPlace(from, to, filter!"true"(toReplace));
2979 return equal(orig, result);
2981 else
2982 return true;
2985 assert(test([1, 2, 3, 4], 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4]));
2986 assert(test([1, 2, 3, 4], 0, 2, cast(int[])[], [3, 4]));
2987 assert(test([1, 2, 3, 4], 0, 4, [5, 6, 7], [5, 6, 7]));
2988 assert(test([1, 2, 3, 4], 0, 2, [5, 6, 7], [5, 6, 7, 3, 4]));
2989 assert(test([1, 2, 3, 4], 2, 4, [5, 6, 7], [1, 2, 5, 6, 7]));
2991 assert(test([1, 2, 3, 4], 0, 0, filter!"true"([5, 6, 7]), [5, 6, 7, 1, 2, 3, 4]));
2992 assert(test([1, 2, 3, 4], 0, 2, filter!"true"(cast(int[])[]), [3, 4]));
2993 assert(test([1, 2, 3, 4], 0, 4, filter!"true"([5, 6, 7]), [5, 6, 7]));
2994 assert(test([1, 2, 3, 4], 0, 2, filter!"true"([5, 6, 7]), [5, 6, 7, 3, 4]));
2995 assert(test([1, 2, 3, 4], 2, 4, filter!"true"([5, 6, 7]), [1, 2, 5, 6, 7]));
2997 void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
3000 auto l = to!T("hello");
3001 auto r = to!U(" world");
3003 enforce(test(l, 0, 0, r, " worldhello"),
3004 new AssertError("testStr failure 1", file, line));
3005 enforce(test(l, 0, 3, r, " worldlo"),
3006 new AssertError("testStr failure 2", file, line));
3007 enforce(test(l, 3, l.length, r, "hel world"),
3008 new AssertError("testStr failure 3", file, line));
3009 enforce(test(l, 0, l.length, r, " world"),
3010 new AssertError("testStr failure 4", file, line));
3011 enforce(test(l, l.length, l.length, r, "hello world"),
3012 new AssertError("testStr failure 5", file, line));
3015 testStr!(string, string)();
3016 testStr!(string, wstring)();
3017 testStr!(string, dstring)();
3018 testStr!(wstring, string)();
3019 testStr!(wstring, wstring)();
3020 testStr!(wstring, dstring)();
3021 testStr!(dstring, string)();
3022 testStr!(dstring, wstring)();
3023 testStr!(dstring, dstring)();
3027 Replaces the first occurrence of `from` with `to` in `subject`.
3029 Params:
3030 subject = the array to scan
3031 from = the item to replace
3032 to = the item to replace `from` with
3034 Returns:
3035 A new array without changing the contents of `subject`, or the original
3036 array if no match is found.
3038 E[] replaceFirst(E, R1, R2)(E[] subject, R1 from, R2 to)
3039 if (isDynamicArray!(E[]) &&
3040 isForwardRange!R1 && is(typeof(appender!(E[])().put(from[0 .. 1]))) &&
3041 isForwardRange!R2 && is(typeof(appender!(E[])().put(to[0 .. 1]))))
3043 if (from.empty) return subject;
3044 static if (isSomeString!(E[]))
3046 import std.string : indexOf;
3047 immutable idx = subject.indexOf(from);
3049 else
3051 import std.algorithm.searching : countUntil;
3052 immutable idx = subject.countUntil(from);
3054 if (idx == -1)
3055 return subject;
3057 auto app = appender!(E[])();
3058 app.put(subject[0 .. idx]);
3059 app.put(to);
3061 static if (isSomeString!(E[]) && isSomeString!R1)
3063 import std.utf : codeLength;
3064 immutable fromLength = codeLength!(Unqual!E, R1)(from);
3066 else
3067 immutable fromLength = from.length;
3069 app.put(subject[idx + fromLength .. $]);
3071 return app.data;
3075 @safe unittest
3077 auto a = [1, 2, 2, 3, 4, 5];
3078 auto b = a.replaceFirst([2], [1337]);
3079 assert(b == [1, 1337, 2, 3, 4, 5]);
3081 auto s = "This is a foo foo list";
3082 auto r = s.replaceFirst("foo", "silly");
3083 assert(r == "This is a silly foo list");
3086 @safe unittest
3088 import std.algorithm.comparison : cmp;
3089 import std.conv : to;
3091 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
3092 const(char[]), immutable(char[])))
3094 static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
3095 const(char[]), immutable(char[])))
3097 auto s = to!S("This is a foo foo list");
3098 auto s2 = to!S("Thüs is a ßöö foo list");
3099 auto from = to!T("foo");
3100 auto from2 = to!T("ßöö");
3101 auto into = to!T("silly");
3102 auto into2 = to!T("sälly");
3104 S r1 = replaceFirst(s, from, into);
3105 assert(cmp(r1, "This is a silly foo list") == 0);
3107 S r11 = replaceFirst(s2, from2, into2);
3108 assert(cmp(r11, "Thüs is a sälly foo list") == 0,
3109 to!string(r11) ~ " : " ~ S.stringof ~ " " ~ T.stringof);
3111 S r2 = replaceFirst(r1, from, into);
3112 assert(cmp(r2, "This is a silly silly list") == 0);
3114 S r3 = replaceFirst(s, to!T(""), into);
3115 assert(cmp(r3, "This is a foo foo list") == 0);
3117 assert(replaceFirst(r3, to!T("won't find"), to!T("whatever")) is r3);
3122 // https://issues.dlang.org/show_bug.cgi?id=8187
3123 @safe unittest
3125 auto res = ["a", "a"];
3126 assert(replace(res, "a", "b") == ["b", "b"]);
3127 assert(replaceFirst(res, "a", "b") == ["b", "a"]);
3131 Replaces the last occurrence of `from` with `to` in `subject`.
3133 Params:
3134 subject = the array to scan
3135 from = the item to replace
3136 to = the item to replace `from` with
3138 Returns:
3139 A new array without changing the contents of `subject`, or the original
3140 array if no match is found.
3142 E[] replaceLast(E, R1, R2)(E[] subject, R1 from , R2 to)
3143 if (isDynamicArray!(E[]) &&
3144 isForwardRange!R1 && is(typeof(appender!(E[])().put(from[0 .. 1]))) &&
3145 isForwardRange!R2 && is(typeof(appender!(E[])().put(to[0 .. 1]))))
3147 import std.range : retro;
3148 if (from.empty) return subject;
3149 static if (isSomeString!(E[]))
3151 import std.string : lastIndexOf;
3152 auto idx = subject.lastIndexOf(from);
3154 else
3156 import std.algorithm.searching : countUntil;
3157 auto idx = retro(subject).countUntil(retro(from));
3160 if (idx == -1)
3161 return subject;
3163 static if (isSomeString!(E[]) && isSomeString!R1)
3165 import std.utf : codeLength;
3166 auto fromLength = codeLength!(Unqual!E, R1)(from);
3168 else
3169 auto fromLength = from.length;
3171 auto app = appender!(E[])();
3172 static if (isSomeString!(E[]))
3173 app.put(subject[0 .. idx]);
3174 else
3175 app.put(subject[0 .. $ - idx - fromLength]);
3177 app.put(to);
3179 static if (isSomeString!(E[]))
3180 app.put(subject[idx+fromLength .. $]);
3181 else
3182 app.put(subject[$ - idx .. $]);
3184 return app.data;
3188 @safe unittest
3190 auto a = [1, 2, 2, 3, 4, 5];
3191 auto b = a.replaceLast([2], [1337]);
3192 assert(b == [1, 2, 1337, 3, 4, 5]);
3194 auto s = "This is a foo foo list";
3195 auto r = s.replaceLast("foo", "silly");
3196 assert(r == "This is a foo silly list", r);
3199 @safe unittest
3201 import std.algorithm.comparison : cmp;
3202 import std.conv : to;
3204 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
3205 const(char[]), immutable(char[])))
3207 static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
3208 const(char[]), immutable(char[])))
3210 auto s = to!S("This is a foo foo list");
3211 auto s2 = to!S("Thüs is a ßöö ßöö list");
3212 auto from = to!T("foo");
3213 auto from2 = to!T("ßöö");
3214 auto into = to!T("silly");
3215 auto into2 = to!T("sälly");
3217 S r1 = replaceLast(s, from, into);
3218 assert(cmp(r1, "This is a foo silly list") == 0, to!string(r1));
3220 S r11 = replaceLast(s2, from2, into2);
3221 assert(cmp(r11, "Thüs is a ßöö sälly list") == 0,
3222 to!string(r11) ~ " : " ~ S.stringof ~ " " ~ T.stringof);
3224 S r2 = replaceLast(r1, from, into);
3225 assert(cmp(r2, "This is a silly silly list") == 0);
3227 S r3 = replaceLast(s, to!T(""), into);
3228 assert(cmp(r3, "This is a foo foo list") == 0);
3230 assert(replaceLast(r3, to!T("won't find"), to!T("whatever")) is r3);
3236 Creates a new array such that the items in `slice` are replaced with the
3237 items in `replacement`. `slice` and `replacement` do not need to be the
3238 same length. The result will grow or shrink based on the items given.
3240 Params:
3241 s = the base of the new array
3242 slice = the slice of `s` to be replaced
3243 replacement = the items to replace `slice` with
3245 Returns:
3246 A new array that is `s` with `slice` replaced by
3247 `replacement[]`.
3249 See_Also:
3250 $(REF substitute, std,algorithm,iteration) for a lazy replace.
3252 inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement)
3255 // Verify that slice[] really is a slice of s[]
3256 assert(overlap(s, slice) is slice, "slice[] is not a subslice of s[]");
3260 auto result = new T[s.length - slice.length + replacement.length];
3261 immutable so = &slice[0] - &s[0];
3262 result[0 .. so] = s[0 .. so];
3263 result[so .. so + replacement.length] = replacement[];
3264 result[so + replacement.length .. result.length] =
3265 s[so + slice.length .. s.length];
3267 return () @trusted inout {
3268 return cast(inout(T)[]) result;
3269 }();
3273 @safe unittest
3275 auto a = [1, 2, 3, 4, 5];
3276 auto b = replaceSlice(a, a[1 .. 4], [0, 0, 0]);
3278 assert(b == [1, 0, 0, 0, 5]);
3281 @safe unittest
3283 import std.algorithm.comparison : cmp;
3285 string s = "hello";
3286 string slice = s[2 .. 4];
3288 auto r = replaceSlice(s, slice, "bar");
3289 int i;
3290 i = cmp(r, "hebaro");
3291 assert(i == 0);
3295 Implements an output range that appends data to an array. This is
3296 recommended over $(D array ~= data) when appending many elements because it is more
3297 efficient. `Appender` maintains its own array metadata locally, so it can avoid
3298 global locking for each append where $(LREF capacity) is non-zero.
3300 Params:
3301 A = the array type to simulate.
3303 See_Also: $(LREF appender)
3305 struct Appender(A)
3306 if (isDynamicArray!A)
3308 import core.memory : GC;
3310 private alias T = ElementEncodingType!A;
3312 private struct Data
3314 size_t capacity;
3315 Unqual!T[] arr;
3316 bool tryExtendBlock = false;
3319 private Data* _data;
3322 * Constructs an `Appender` with a given array. Note that this does not copy the
3323 * data. If the array has a larger capacity as determined by `arr.capacity`,
3324 * it will be used by the appender. After initializing an appender on an array,
3325 * appending to the original array will reallocate.
3327 this(A arr) @trusted
3329 // initialize to a given array.
3330 _data = new Data;
3331 _data.arr = cast(Unqual!T[]) arr; //trusted
3333 if (__ctfe)
3334 return;
3336 // We want to use up as much of the block the array is in as possible.
3337 // if we consume all the block that we can, then array appending is
3338 // safe WRT built-in append, and we can use the entire block.
3339 // We only do this for mutable types that can be extended.
3340 static if (isMutable!T && is(typeof(arr.length = size_t.max)))
3342 immutable cap = arr.capacity; //trusted
3343 // Replace with "GC.setAttr( Not Appendable )" once pure (and fixed)
3344 if (cap > arr.length)
3345 arr.length = cap;
3347 _data.capacity = arr.length;
3351 * Reserve at least newCapacity elements for appending. Note that more elements
3352 * may be reserved than requested. If `newCapacity <= capacity`, then nothing is
3353 * done.
3355 * Params:
3356 * newCapacity = the capacity the `Appender` should have
3358 void reserve(size_t newCapacity)
3360 if (_data)
3362 if (newCapacity > _data.capacity)
3363 ensureAddable(newCapacity - _data.arr.length);
3365 else
3367 ensureAddable(newCapacity);
3372 * Returns: the capacity of the array (the maximum number of elements the
3373 * managed array can accommodate before triggering a reallocation). If any
3374 * appending will reallocate, `0` will be returned.
3376 @property size_t capacity() const
3378 return _data ? _data.capacity : 0;
3382 * Use opSlice() from now on.
3383 * Returns: The managed array.
3385 @property inout(T)[] data() inout @trusted
3387 return this[];
3391 * Returns: The managed array.
3393 @property inout(T)[] opSlice() inout @trusted
3395 /* @trusted operation:
3396 * casting Unqual!T[] to inout(T)[]
3398 return cast(typeof(return))(_data ? _data.arr : null);
3401 // ensure we can add nelems elements, resizing as necessary
3402 private void ensureAddable(size_t nelems)
3404 if (!_data)
3405 _data = new Data;
3406 immutable len = _data.arr.length;
3407 immutable reqlen = len + nelems;
3409 if (_data.capacity >= reqlen)
3410 return;
3412 // need to increase capacity
3413 if (__ctfe)
3415 static if (__traits(compiles, new Unqual!T[1]))
3417 _data.arr.length = reqlen;
3419 else
3421 // avoid restriction of @disable this()
3422 _data.arr = _data.arr[0 .. _data.capacity];
3423 foreach (i; _data.capacity .. reqlen)
3424 _data.arr ~= Unqual!T.init;
3426 _data.arr = _data.arr[0 .. len];
3427 _data.capacity = reqlen;
3429 else
3431 // Time to reallocate.
3432 // We need to almost duplicate what's in druntime, except we
3433 // have better access to the capacity field.
3434 auto newlen = appenderNewCapacity!(T.sizeof)(_data.capacity, reqlen);
3435 // first, try extending the current block
3436 if (_data.tryExtendBlock)
3438 immutable u = (() @trusted => GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof))();
3439 if (u)
3441 // extend worked, update the capacity
3442 _data.capacity = u / T.sizeof;
3443 return;
3448 // didn't work, must reallocate
3449 import core.checkedint : mulu;
3450 bool overflow;
3451 const nbytes = mulu(newlen, T.sizeof, overflow);
3452 if (overflow) assert(false, "the reallocation would exceed the "
3453 ~ "available pointer range");
3455 auto bi = (() @trusted => GC.qalloc(nbytes, blockAttribute!T))();
3456 _data.capacity = bi.size / T.sizeof;
3457 import core.stdc.string : memcpy;
3458 if (len)
3459 () @trusted { memcpy(bi.base, _data.arr.ptr, len * T.sizeof); }();
3460 _data.arr = (() @trusted => (cast(Unqual!T*) bi.base)[0 .. len])();
3461 _data.tryExtendBlock = true;
3462 // leave the old data, for safety reasons
3466 private template canPutItem(U)
3468 enum bool canPutItem =
3469 isImplicitlyConvertible!(Unqual!U, Unqual!T) ||
3470 isSomeChar!T && isSomeChar!U;
3472 private template canPutConstRange(Range)
3474 enum bool canPutConstRange =
3475 isInputRange!(Unqual!Range) &&
3476 !isInputRange!Range &&
3477 is(typeof(Appender.init.put(Range.init.front)));
3479 private template canPutRange(Range)
3481 enum bool canPutRange =
3482 isInputRange!Range &&
3483 is(typeof(Appender.init.put(Range.init.front)));
3487 * Appends `item` to the managed array. Performs encoding for
3488 * `char` types if `A` is a differently typed `char` array.
3490 * Params:
3491 * item = the single item to append
3493 void put(U)(U item) if (canPutItem!U)
3495 static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof)
3497 /* may throwable operation:
3498 * - std.utf.encode
3500 // must do some transcoding around here
3501 import std.utf : encode;
3502 Unqual!T[T.sizeof == 1 ? 4 : 2] encoded;
3503 auto len = encode(encoded, item);
3504 put(encoded[0 .. len]);
3506 else
3508 import core.lifetime : emplace;
3510 ensureAddable(1);
3511 immutable len = _data.arr.length;
3513 auto bigData = (() @trusted => _data.arr.ptr[0 .. len + 1])();
3514 auto itemUnqual = (() @trusted => & cast() item)();
3515 emplace(&bigData[len], *itemUnqual);
3516 //We do this at the end, in case of exceptions
3517 _data.arr = bigData;
3521 // Const fixing hack.
3522 void put(Range)(Range items) if (canPutConstRange!Range)
3524 alias p = put!(Unqual!Range);
3525 p(items);
3529 * Appends an entire range to the managed array. Performs encoding for
3530 * `char` elements if `A` is a differently typed `char` array.
3532 * Params:
3533 * items = the range of items to append
3535 void put(Range)(Range items) if (canPutRange!Range)
3537 // note, we disable this branch for appending one type of char to
3538 // another because we can't trust the length portion.
3539 static if (!(isSomeChar!T && isSomeChar!(ElementType!Range) &&
3540 !is(immutable Range == immutable T[])) &&
3541 is(typeof(items.length) == size_t))
3543 // optimization -- if this type is something other than a string,
3544 // and we are adding exactly one element, call the version for one
3545 // element.
3546 static if (!isSomeChar!T)
3548 if (items.length == 1)
3550 put(items.front);
3551 return;
3555 // make sure we have enough space, then add the items
3556 auto bigDataFun(size_t extra)
3558 ensureAddable(extra);
3559 return (() @trusted => _data.arr.ptr[0 .. _data.arr.length + extra])();
3561 auto bigData = bigDataFun(items.length);
3563 immutable len = _data.arr.length;
3564 immutable newlen = bigData.length;
3566 alias UT = Unqual!T;
3568 static if (is(typeof(_data.arr[] = items[])) &&
3569 !hasElaborateAssign!UT && isAssignable!(UT, ElementEncodingType!Range))
3571 bigData[len .. newlen] = items[];
3573 else
3575 import core.internal.lifetime : emplaceRef;
3576 foreach (ref it ; bigData[len .. newlen])
3578 emplaceRef!T(it, items.front);
3579 items.popFront();
3583 //We do this at the end, in case of exceptions
3584 _data.arr = bigData;
3586 else static if (isSomeChar!T && isSomeChar!(ElementType!Range) &&
3587 !is(immutable T == immutable ElementType!Range))
3589 // need to decode and encode
3590 import std.utf : decodeFront;
3591 while (!items.empty)
3593 auto c = items.decodeFront;
3594 put(c);
3597 else
3599 //pragma(msg, Range.stringof);
3600 // Generic input range
3601 for (; !items.empty; items.popFront())
3603 put(items.front);
3609 * Appends to the managed array.
3611 * See_Also: $(LREF Appender.put)
3613 alias opOpAssign(string op : "~") = put;
3615 // only allow overwriting data on non-immutable and non-const data
3616 static if (isMutable!T)
3619 * Clears the managed array. This allows the elements of the array to be reused
3620 * for appending.
3622 * Note: clear is disabled for immutable or const element types, due to the
3623 * possibility that `Appender` might overwrite immutable data.
3625 void clear() @trusted pure nothrow
3627 if (_data)
3629 _data.arr = _data.arr.ptr[0 .. 0];
3634 * Shrinks the managed array to the given length.
3636 * Throws: `Exception` if newlength is greater than the current array length.
3637 * Note: shrinkTo is disabled for immutable or const element types.
3639 void shrinkTo(size_t newlength) @trusted pure
3641 import std.exception : enforce;
3642 if (_data)
3644 enforce(newlength <= _data.arr.length, "Attempting to shrink Appender with newlength > length");
3645 _data.arr = _data.arr.ptr[0 .. newlength];
3647 else
3648 enforce(newlength == 0, "Attempting to shrink empty Appender with non-zero newlength");
3653 * Gives a string in the form of `Appender!(A)(data)`.
3655 * Params:
3656 * w = A `char` accepting
3657 * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives).
3658 * fmt = A $(REF FormatSpec, std, format) which controls how the array
3659 * is formatted.
3660 * Returns:
3661 * A `string` if `writer` is not set; `void` otherwise.
3663 string toString()() const
3665 import std.format.spec : singleSpec;
3667 auto app = appender!string();
3668 auto spec = singleSpec("%s");
3669 immutable len = _data ? _data.arr.length : 0;
3670 // different reserve lengths because each element in a
3671 // non-string-like array uses two extra characters for `, `.
3672 static if (isSomeString!A)
3674 app.reserve(len + 25);
3676 else
3678 // Multiplying by three is a very conservative estimate of
3679 // length, as it assumes each element is only one char
3680 app.reserve((len * 3) + 25);
3682 toString(app, spec);
3683 return app.data;
3686 import std.format.spec : FormatSpec;
3688 /// ditto
3689 template toString(Writer)
3690 if (isOutputRange!(Writer, char))
3692 void toString(ref Writer w, scope const ref FormatSpec!char fmt) const
3694 import std.format.write : formatValue;
3695 import std.range.primitives : put;
3696 put(w, Unqual!(typeof(this)).stringof);
3697 put(w, '(');
3698 formatValue(w, data, fmt);
3699 put(w, ')');
3705 @safe pure nothrow unittest
3707 auto app = appender!string();
3708 string b = "abcdefg";
3709 foreach (char c; b)
3710 app.put(c);
3711 assert(app[] == "abcdefg");
3713 int[] a = [ 1, 2 ];
3714 auto app2 = appender(a);
3715 app2.put(3);
3716 app2.put([ 4, 5, 6 ]);
3717 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
3720 @safe pure unittest
3722 import std.format : format;
3723 import std.format.spec : singleSpec;
3725 auto app = appender!(int[])();
3726 app.put(1);
3727 app.put(2);
3728 app.put(3);
3729 assert("%s".format(app) == "Appender!(int[])(%s)".format([1,2,3]));
3731 auto app2 = appender!string();
3732 auto spec = singleSpec("%s");
3733 app.toString(app2, spec);
3734 assert(app2[] == "Appender!(int[])([1, 2, 3])");
3736 auto app3 = appender!string();
3737 spec = singleSpec("%(%04d, %)");
3738 app.toString(app3, spec);
3739 assert(app3[] == "Appender!(int[])(0001, 0002, 0003)");
3742 // https://issues.dlang.org/show_bug.cgi?id=17251
3743 @safe pure nothrow unittest
3745 static struct R
3747 int front() const { return 0; }
3748 bool empty() const { return true; }
3749 void popFront() {}
3752 auto app = appender!(R[]);
3753 const(R)[1] r;
3754 app.put(r[0]);
3755 app.put(r[]);
3758 // https://issues.dlang.org/show_bug.cgi?id=13300
3759 @safe pure nothrow unittest
3761 static test(bool isPurePostblit)()
3763 static if (!isPurePostblit)
3764 static int i;
3766 struct Simple
3768 @disable this(); // Without this, it works.
3769 static if (!isPurePostblit)
3770 this(this) { i++; }
3771 else
3772 pure this(this) { }
3774 private:
3775 this(int tmp) { }
3778 struct Range
3780 @property Simple front() { return Simple(0); }
3781 void popFront() { count++; }
3782 @property empty() { return count < 3; }
3783 size_t count;
3786 Range r;
3787 auto a = r.array();
3790 static assert(__traits(compiles, () pure { test!true(); }));
3791 static assert(!__traits(compiles, () pure { test!false(); }));
3794 // https://issues.dlang.org/show_bug.cgi?id=19572
3795 @safe pure nothrow unittest
3797 static struct Struct
3799 int value;
3801 int fun() const { return 23; }
3803 alias fun this;
3806 Appender!(Struct[]) appender;
3808 appender.put(const(Struct)(42));
3810 auto result = appender[][0];
3812 assert(result.value != 23);
3815 @safe pure unittest
3817 import std.conv : to;
3818 import std.utf : byCodeUnit;
3819 auto str = "ウェブサイト";
3820 auto wstr = appender!wstring();
3821 put(wstr, str.byCodeUnit);
3822 assert(wstr.data == str.to!wstring);
3825 // https://issues.dlang.org/show_bug.cgi?id=21256
3826 @safe pure unittest
3828 Appender!string app1;
3829 app1.toString();
3831 Appender!(int[]) app2;
3832 app2.toString();
3835 //Calculates an efficient growth scheme based on the old capacity
3836 //of data, and the minimum requested capacity.
3837 //arg curLen: The current length
3838 //arg reqLen: The length as requested by the user
3839 //ret sugLen: A suggested growth.
3840 private size_t appenderNewCapacity(size_t TSizeOf)(size_t curLen, size_t reqLen)
3842 import core.bitop : bsr;
3843 import std.algorithm.comparison : max;
3844 if (curLen == 0)
3845 return max(reqLen,8);
3846 ulong mult = 100 + (1000UL) / (bsr(curLen * TSizeOf) + 1);
3847 // limit to doubling the length, we don't want to grow too much
3848 if (mult > 200)
3849 mult = 200;
3850 auto sugLen = cast(size_t)((curLen * mult + 99) / 100);
3851 return max(reqLen, sugLen);
3855 * A version of $(LREF Appender) that can update an array in-place.
3856 * It forwards all calls to an underlying appender implementation.
3857 * Any calls made to the appender also update the pointer to the
3858 * original array passed in.
3860 * Tip: Use the `arrayPtr` overload of $(LREF appender) for construction with type-inference.
3862 * Params:
3863 * A = The array type to simulate
3865 struct RefAppender(A)
3866 if (isDynamicArray!A)
3868 private alias T = ElementEncodingType!A;
3870 private
3872 Appender!A impl;
3873 A* arr;
3877 * Constructs a `RefAppender` with a given array reference. This does not copy the
3878 * data. If the array has a larger capacity as determined by `arr.capacity`, it
3879 * will be used by the appender.
3881 * Note: Do not use built-in appending (i.e. `~=`) on the original array
3882 * until you are done with the appender, because subsequent calls to the appender
3883 * will reallocate the array data without those appends.
3885 * Params:
3886 * arr = Pointer to an array. Must not be _null.
3888 this(A* arr)
3890 impl = Appender!A(*arr);
3891 this.arr = arr;
3894 /** Wraps remaining `Appender` methods such as $(LREF put).
3895 * Params:
3896 * fn = Method name to call.
3897 * args = Arguments to pass to the method.
3899 void opDispatch(string fn, Args...)(Args args)
3900 if (__traits(compiles, (Appender!A a) => mixin("a." ~ fn ~ "(args)")))
3902 // we do it this way because we can't cache a void return
3903 scope(exit) *this.arr = impl[];
3904 mixin("return impl." ~ fn ~ "(args);");
3908 * Appends `rhs` to the managed array.
3909 * Params:
3910 * rhs = Element or range.
3912 void opOpAssign(string op : "~", U)(U rhs)
3913 if (__traits(compiles, (Appender!A a){ a.put(rhs); }))
3915 scope(exit) *this.arr = impl[];
3916 impl.put(rhs);
3920 * Returns the capacity of the array (the maximum number of elements the
3921 * managed array can accommodate before triggering a reallocation). If any
3922 * appending will reallocate, `capacity` returns `0`.
3924 @property size_t capacity() const
3926 return impl.capacity;
3929 /* Use opSlice() instead.
3930 * Returns: the managed array.
3932 @property inout(T)[] data() inout
3934 return impl[];
3938 * Returns: the managed array.
3940 @property inout(ElementEncodingType!A)[] opSlice() inout
3942 return impl[];
3947 @safe pure nothrow
3948 unittest
3950 int[] a = [1, 2];
3951 auto app2 = appender(&a);
3952 assert(app2[] == [1, 2]);
3953 assert(a == [1, 2]);
3954 app2 ~= 3;
3955 app2 ~= [4, 5, 6];
3956 assert(app2[] == [1, 2, 3, 4, 5, 6]);
3957 assert(a == [1, 2, 3, 4, 5, 6]);
3959 app2.reserve(5);
3960 assert(app2.capacity >= 5);
3964 Convenience function that returns an $(LREF Appender) instance,
3965 optionally initialized with `array`.
3967 Appender!A appender(A)()
3968 if (isDynamicArray!A)
3970 return Appender!A(null);
3972 /// ditto
3973 Appender!(E[]) appender(A : E[], E)(auto ref A array)
3975 static assert(!isStaticArray!A || __traits(isRef, array),
3976 "Cannot create Appender from an rvalue static array");
3978 return Appender!(E[])(array);
3981 @safe pure nothrow unittest
3983 auto app = appender!(char[])();
3984 string b = "abcdefg";
3985 foreach (char c; b) app.put(c);
3986 assert(app[] == "abcdefg");
3989 @safe pure nothrow unittest
3991 auto app = appender!(char[])();
3992 string b = "abcdefg";
3993 foreach (char c; b) app ~= c;
3994 assert(app[] == "abcdefg");
3997 @safe pure nothrow unittest
3999 int[] a = [ 1, 2 ];
4000 auto app2 = appender(a);
4001 assert(app2[] == [ 1, 2 ]);
4002 app2.put(3);
4003 app2.put([ 4, 5, 6 ][]);
4004 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
4005 app2.put([ 7 ]);
4006 assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
4009 @safe pure nothrow unittest
4011 auto app4 = appender([]);
4012 try // shrinkTo may throw
4014 app4.shrinkTo(0);
4016 catch (Exception) assert(0);
4019 // https://issues.dlang.org/show_bug.cgi?id=5663
4020 // https://issues.dlang.org/show_bug.cgi?id=9725
4021 @safe pure nothrow unittest
4023 import std.exception : assertNotThrown;
4025 static foreach (S; AliasSeq!(char[], const(char)[], string))
4028 Appender!S app5663i;
4029 assertNotThrown(app5663i.put("\xE3"));
4030 assert(app5663i[] == "\xE3");
4032 Appender!S app5663c;
4033 assertNotThrown(app5663c.put(cast(const(char)[])"\xE3"));
4034 assert(app5663c[] == "\xE3");
4036 Appender!S app5663m;
4037 assertNotThrown(app5663m.put("\xE3".dup));
4038 assert(app5663m[] == "\xE3");
4040 // ditto for ~=
4042 Appender!S app5663i;
4043 assertNotThrown(app5663i ~= "\xE3");
4044 assert(app5663i[] == "\xE3");
4046 Appender!S app5663c;
4047 assertNotThrown(app5663c ~= cast(const(char)[])"\xE3");
4048 assert(app5663c[] == "\xE3");
4050 Appender!S app5663m;
4051 assertNotThrown(app5663m ~= "\xE3".dup);
4052 assert(app5663m[] == "\xE3");
4057 // https://issues.dlang.org/show_bug.cgi?id=10122
4058 @safe pure nothrow unittest
4060 import std.exception : assertCTFEable;
4062 static struct S10122
4064 int val;
4066 @disable this();
4067 this(int v) @safe pure nothrow { val = v; }
4069 assertCTFEable!(
4071 auto w = appender!(S10122[])();
4072 w.put(S10122(1));
4073 assert(w[].length == 1 && w[][0].val == 1);
4077 @safe pure nothrow unittest
4079 import std.exception : assertThrown;
4081 int[] a = [ 1, 2 ];
4082 auto app2 = appender(a);
4083 assert(app2[] == [ 1, 2 ]);
4084 app2 ~= 3;
4085 app2 ~= [ 4, 5, 6 ][];
4086 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
4087 app2 ~= [ 7 ];
4088 assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
4090 app2.reserve(5);
4091 assert(app2.capacity >= 5);
4093 try // shrinkTo may throw
4095 app2.shrinkTo(3);
4097 catch (Exception) assert(0);
4098 assert(app2[] == [ 1, 2, 3 ]);
4099 assertThrown(app2.shrinkTo(5));
4101 const app3 = app2;
4102 assert(app3.capacity >= 3);
4103 assert(app3[] == [1, 2, 3]);
4107 @safe pure nothrow
4108 unittest
4110 auto w = appender!string;
4111 // pre-allocate space for at least 10 elements (this avoids costly reallocations)
4112 w.reserve(10);
4113 assert(w.capacity >= 10);
4115 w.put('a'); // single elements
4116 w.put("bc"); // multiple elements
4118 // use the append syntax
4119 w ~= 'd';
4120 w ~= "ef";
4122 assert(w[] == "abcdef");
4125 @safe pure nothrow unittest
4127 auto w = appender!string();
4128 w.reserve(4);
4129 cast(void) w.capacity;
4130 cast(void) w[];
4133 wchar wc = 'a';
4134 dchar dc = 'a';
4135 w.put(wc); // decoding may throw
4136 w.put(dc); // decoding may throw
4138 catch (Exception) assert(0);
4141 @safe pure nothrow unittest
4143 auto w = appender!(int[])();
4144 w.reserve(4);
4145 cast(void) w.capacity;
4146 cast(void) w[];
4147 w.put(10);
4148 w.put([10]);
4149 w.clear();
4152 w.shrinkTo(0);
4154 catch (Exception) assert(0);
4156 struct N
4158 int payload;
4159 alias payload this;
4161 w.put(N(1));
4162 w.put([N(2)]);
4164 struct S(T)
4166 @property bool empty() { return true; }
4167 @property T front() { return T.init; }
4168 void popFront() {}
4170 S!int r;
4171 w.put(r);
4174 // https://issues.dlang.org/show_bug.cgi?id=10690
4175 @safe pure nothrow unittest
4177 import std.algorithm.iteration : filter;
4178 import std.typecons : tuple;
4179 [tuple(1)].filter!(t => true).array; // No error
4180 [tuple("A")].filter!(t => true).array; // error
4183 @safe pure nothrow unittest
4185 import std.range;
4186 //Coverage for put(Range)
4187 struct S1
4190 struct S2
4192 void opAssign(S2){}
4194 auto a1 = Appender!(S1[])();
4195 auto a2 = Appender!(S2[])();
4196 auto au1 = Appender!(const(S1)[])();
4197 a1.put(S1().repeat().take(10));
4198 a2.put(S2().repeat().take(10));
4199 auto sc1 = const(S1)();
4200 au1.put(sc1.repeat().take(10));
4203 @system pure unittest
4205 import std.range;
4206 struct S2
4208 void opAssign(S2){}
4210 auto au2 = Appender!(const(S2)[])();
4211 auto sc2 = const(S2)();
4212 au2.put(sc2.repeat().take(10));
4215 @system pure nothrow unittest
4217 struct S
4219 int* p;
4222 auto a0 = Appender!(S[])();
4223 auto a1 = Appender!(const(S)[])();
4224 auto a2 = Appender!(immutable(S)[])();
4225 auto s0 = S(null);
4226 auto s1 = const(S)(null);
4227 auto s2 = immutable(S)(null);
4228 a1.put(s0);
4229 a1.put(s1);
4230 a1.put(s2);
4231 a1.put([s0]);
4232 a1.put([s1]);
4233 a1.put([s2]);
4234 a0.put(s0);
4235 static assert(!is(typeof(a0.put(a1))));
4236 static assert(!is(typeof(a0.put(a2))));
4237 a0.put([s0]);
4238 static assert(!is(typeof(a0.put([a1]))));
4239 static assert(!is(typeof(a0.put([a2]))));
4240 static assert(!is(typeof(a2.put(a0))));
4241 static assert(!is(typeof(a2.put(a1))));
4242 a2.put(s2);
4243 static assert(!is(typeof(a2.put([a0]))));
4244 static assert(!is(typeof(a2.put([a1]))));
4245 a2.put([s2]);
4248 // https://issues.dlang.org/show_bug.cgi?id=9528
4249 @safe pure nothrow unittest
4251 const(E)[] fastCopy(E)(E[] src) {
4252 auto app = appender!(const(E)[])();
4253 foreach (i, e; src)
4254 app.put(e);
4255 return app[];
4258 class C {}
4259 struct S { const(C) c; }
4260 S[] s = [ S(new C) ];
4262 auto t = fastCopy(s); // Does not compile
4263 assert(t.length == 1);
4266 // https://issues.dlang.org/show_bug.cgi?id=10753
4267 @safe pure unittest
4269 import std.algorithm.iteration : map;
4270 struct Foo {
4271 immutable dchar d;
4273 struct Bar {
4274 immutable int x;
4276 "12".map!Foo.array;
4277 [1, 2].map!Bar.array;
4280 @safe pure nothrow unittest
4282 import std.algorithm.comparison : equal;
4284 //New appender signature tests
4285 alias mutARR = int[];
4286 alias conARR = const(int)[];
4287 alias immARR = immutable(int)[];
4289 mutARR mut;
4290 conARR con;
4291 immARR imm;
4293 auto app1 = Appender!mutARR(mut); //Always worked. Should work. Should not create a warning.
4294 app1.put(7);
4295 assert(equal(app1[], [7]));
4296 static assert(!is(typeof(Appender!mutARR(con)))); //Never worked. Should not work.
4297 static assert(!is(typeof(Appender!mutARR(imm)))); //Never worked. Should not work.
4299 auto app2 = Appender!conARR(mut); //Always worked. Should work. Should not create a warning.
4300 app2.put(7);
4301 assert(equal(app2[], [7]));
4302 auto app3 = Appender!conARR(con); //Didn't work. Now works. Should not create a warning.
4303 app3.put(7);
4304 assert(equal(app3[], [7]));
4305 auto app4 = Appender!conARR(imm); //Didn't work. Now works. Should not create a warning.
4306 app4.put(7);
4307 assert(equal(app4[], [7]));
4309 //{auto app = Appender!immARR(mut);} //Worked. Will cease to work. Creates warning.
4310 //static assert(!is(typeof(Appender!immARR(mut)))); //Worked. Will cease to work. Uncomment me after full deprecation.
4311 static assert(!is(typeof(Appender!immARR(con)))); //Never worked. Should not work.
4312 auto app5 = Appender!immARR(imm); //Didn't work. Now works. Should not create a warning.
4313 app5.put(7);
4314 assert(equal(app5[], [7]));
4316 //Deprecated. Please uncomment and make sure this doesn't work:
4317 //char[] cc;
4318 //static assert(!is(typeof(Appender!string(cc))));
4320 //This should always work:
4321 auto app6 = appender!string(null);
4322 assert(app6[] == null);
4323 auto app7 = appender!(const(char)[])(null);
4324 assert(app7[] == null);
4325 auto app8 = appender!(char[])(null);
4326 assert(app8[] == null);
4329 @safe pure nothrow unittest //Test large allocations (for GC.extend)
4331 import std.algorithm.comparison : equal;
4332 import std.range;
4333 Appender!(char[]) app;
4334 app.reserve(1); //cover reserve on non-initialized
4335 foreach (_; 0 .. 100_000)
4336 app.put('a');
4337 assert(equal(app[], 'a'.repeat(100_000)));
4340 @safe pure nothrow unittest
4342 auto reference = new ubyte[](2048 + 1); //a number big enough to have a full page (EG: the GC extends)
4343 auto arr = reference.dup;
4344 auto app = appender(arr[0 .. 0]);
4345 app.reserve(1); //This should not trigger a call to extend
4346 app.put(ubyte(1)); //Don't clobber arr
4347 assert(reference[] == arr[]);
4350 @safe pure nothrow unittest // clear method is supported only for mutable element types
4352 Appender!string app;
4353 app.put("foo");
4354 static assert(!__traits(compiles, app.clear()));
4355 assert(app[] == "foo");
4358 @safe pure nothrow unittest
4360 static struct D//dynamic
4362 int[] i;
4363 alias i this;
4365 static struct S//static
4367 int[5] i;
4368 alias i this;
4370 static assert(!is(Appender!(char[5])));
4371 static assert(!is(Appender!D));
4372 static assert(!is(Appender!S));
4374 enum int[5] a = [];
4375 int[5] b;
4376 D d;
4377 S s;
4378 int[5] foo(){return a;}
4380 static assert(!is(typeof(appender(a))));
4381 static assert( is(typeof(appender(b))));
4382 static assert( is(typeof(appender(d))));
4383 static assert( is(typeof(appender(s))));
4384 static assert(!is(typeof(appender(foo()))));
4387 @system unittest
4389 // https://issues.dlang.org/show_bug.cgi?id=13077
4390 static class A {}
4392 // reduced case
4393 auto w = appender!(shared(A)[])();
4394 w.put(new shared A());
4396 // original case
4397 import std.range;
4398 InputRange!(shared A) foo()
4400 return [new shared A].inputRangeObject;
4402 auto res = foo.array;
4403 assert(res.length == 1);
4407 Convenience function that returns a $(LREF RefAppender) instance initialized
4408 with `arrayPtr`. Don't use null for the array pointer, use the other
4409 version of `appender` instead.
4411 RefAppender!(E[]) appender(P : E[]*, E)(P arrayPtr)
4413 return RefAppender!(E[])(arrayPtr);
4417 @safe pure nothrow
4418 unittest
4420 int[] a = [1, 2];
4421 auto app2 = appender(&a);
4422 assert(app2[] == [1, 2]);
4423 assert(a == [1, 2]);
4424 app2 ~= 3;
4425 app2 ~= [4, 5, 6];
4426 assert(app2[] == [1, 2, 3, 4, 5, 6]);
4427 assert(a == [1, 2, 3, 4, 5, 6]);
4429 app2.reserve(5);
4430 assert(app2.capacity >= 5);
4433 @safe pure nothrow unittest
4435 auto arr = new char[0];
4436 auto app = appender(&arr);
4437 string b = "abcdefg";
4438 foreach (char c; b) app.put(c);
4439 assert(app[] == "abcdefg");
4440 assert(arr == "abcdefg");
4443 @safe pure nothrow unittest
4445 auto arr = new char[0];
4446 auto app = appender(&arr);
4447 string b = "abcdefg";
4448 foreach (char c; b) app ~= c;
4449 assert(app[] == "abcdefg");
4450 assert(arr == "abcdefg");
4453 @safe pure nothrow unittest
4455 int[] a = [ 1, 2 ];
4456 auto app2 = appender(&a);
4457 assert(app2[] == [ 1, 2 ]);
4458 assert(a == [ 1, 2 ]);
4459 app2.put(3);
4460 app2.put([ 4, 5, 6 ][]);
4461 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
4462 assert(a == [ 1, 2, 3, 4, 5, 6 ]);
4465 @safe pure nothrow unittest
4467 import std.exception : assertThrown;
4469 int[] a = [ 1, 2 ];
4470 auto app2 = appender(&a);
4471 assert(app2[] == [ 1, 2 ]);
4472 assert(a == [ 1, 2 ]);
4473 app2 ~= 3;
4474 app2 ~= [ 4, 5, 6 ][];
4475 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
4476 assert(a == [ 1, 2, 3, 4, 5, 6 ]);
4478 app2.reserve(5);
4479 assert(app2.capacity >= 5);
4481 try // shrinkTo may throw
4483 app2.shrinkTo(3);
4485 catch (Exception) assert(0);
4486 assert(app2[] == [ 1, 2, 3 ]);
4487 assertThrown(app2.shrinkTo(5));
4489 const app3 = app2;
4490 assert(app3.capacity >= 3);
4491 assert(app3[] == [1, 2, 3]);
4494 // https://issues.dlang.org/show_bug.cgi?id=14605
4495 @safe pure nothrow unittest
4497 static assert(isOutputRange!(Appender!(int[]), int));
4498 static assert(isOutputRange!(RefAppender!(int[]), int));
4501 @safe pure nothrow unittest
4503 Appender!(int[]) app;
4504 short[] range = [1, 2, 3];
4505 app.put(range);
4506 assert(app[] == [1, 2, 3]);
4509 @safe pure nothrow unittest
4511 string s = "hello".idup;
4512 char[] a = "hello".dup;
4513 auto appS = appender(s);
4514 auto appA = appender(a);
4515 put(appS, 'w');
4516 put(appA, 'w');
4517 s ~= 'a'; //Clobbers here?
4518 a ~= 'a'; //Clobbers here?
4519 assert(appS[] == "hellow");
4520 assert(appA[] == "hellow");
4524 Constructs a static array from `a`.
4525 The type of elements can be specified implicitly so that $(D [1, 2].staticArray) results in `int[2]`,
4526 or explicitly, e.g. $(D [1, 2].staticArray!float) returns `float[2]`.
4527 When `a` is a range whose length is not known at compile time, the number of elements must be
4528 given as template argument (e.g. `myrange.staticArray!2`).
4529 Size and type can be combined, if the source range elements are implicitly
4530 convertible to the requested element type (eg: `2.iota.staticArray!(long[2])`).
4531 When the range `a` is known at compile time, it can also be specified as a
4532 template argument to avoid having to specify the number of elements
4533 (e.g.: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`).
4535 Note: `staticArray` returns by value, so expressions involving large arrays may be inefficient.
4537 Params:
4538 a = The input elements. If there are less elements than the specified length of the static array,
4539 the rest of it is default-initialized. If there are more than specified, the first elements
4540 up to the specified length are used.
4541 rangeLength = outputs the number of elements used from `a` to it. Optional.
4543 Returns: A static array constructed from `a`.
4545 pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
4547 return a;
4550 /// static array from array literal
4551 nothrow pure @safe @nogc unittest
4553 auto a = [0, 1].staticArray;
4554 static assert(is(typeof(a) == int[2]));
4555 assert(a == [0, 1]);
4558 pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a)
4559 if (!is(T == U) && is(T : U))
4561 return a[].staticArray!(U[n]);
4564 /// static array from array with implicit casting of elements
4565 nothrow pure @safe @nogc unittest
4567 auto b = [0, 1].staticArray!long;
4568 static assert(is(typeof(b) == long[2]));
4569 assert(b == [0, 1]);
4572 nothrow pure @safe @nogc unittest
4574 int val = 3;
4575 static immutable gold = [1, 2, 3];
4576 [1, 2, val].staticArray.checkStaticArray!int([1, 2, 3]);
4578 @nogc void checkNogc()
4580 [1, 2, val].staticArray.checkStaticArray!int(gold);
4583 checkNogc();
4585 [1, 2, val].staticArray!double.checkStaticArray!double(gold);
4586 [1, 2, 3].staticArray!int.checkStaticArray!int(gold);
4588 [1, 2, 3].staticArray!(const(int)).checkStaticArray!(const(int))(gold);
4589 [1, 2, 3].staticArray!(const(double)).checkStaticArray!(const(double))(gold);
4591 const(int)[3] a2 = [1, 2, 3].staticArray;
4594 [cast(byte) 1, cast(byte) 129].staticArray.checkStaticArray!byte([1, -127]);
4597 /// ditto
4598 auto staticArray(size_t n, T)(scope T a)
4599 if (isInputRange!T)
4601 alias U = ElementType!T;
4602 return staticArray!(U[n], U, n)(a);
4605 /// ditto
4606 auto staticArray(size_t n, T)(scope T a, out size_t rangeLength)
4607 if (isInputRange!T)
4609 alias U = ElementType!T;
4610 return staticArray!(U[n], U, n)(a, rangeLength);
4613 /// ditto
4614 auto staticArray(Un : U[n], U, size_t n, T)(scope T a)
4615 if (isInputRange!T && is(ElementType!T : U))
4617 size_t extraStackSpace;
4618 return staticArray!(Un, U, n)(a, extraStackSpace);
4621 /// ditto
4622 auto staticArray(Un : U[n], U, size_t n, T)(scope T a, out size_t rangeLength)
4623 if (isInputRange!T && is(ElementType!T : U))
4625 import std.algorithm.mutation : uninitializedFill;
4626 import std.range : take;
4627 import core.internal.lifetime : emplaceRef;
4629 if (__ctfe)
4631 size_t i;
4632 // Compile-time version to avoid unchecked memory access.
4633 Unqual!U[n] ret;
4634 for (auto iter = a.take(n); !iter.empty; iter.popFront())
4636 ret[i] = iter.front;
4637 i++;
4640 rangeLength = i;
4641 return (() @trusted => cast(U[n]) ret)();
4644 auto ret = (() @trusted
4646 Unqual!U[n] theArray = void;
4647 return theArray;
4648 }());
4650 size_t i;
4651 if (true)
4653 // ret was void-initialized so let's initialize the unfilled part manually.
4654 // also prevents destructors to be called on uninitialized memory if
4655 // an exception is thrown
4656 scope (exit) ret[i .. $].uninitializedFill(U.init);
4658 for (auto iter = a.take(n); !iter.empty; iter.popFront())
4660 emplaceRef!U(ret[i++], iter.front);
4664 rangeLength = i;
4665 return (() @trusted => cast(U[n]) ret)();
4668 /// static array from range + size
4669 nothrow pure @safe @nogc unittest
4671 import std.range : iota;
4673 auto input = 3.iota;
4674 auto a = input.staticArray!2;
4675 static assert(is(typeof(a) == int[2]));
4676 assert(a == [0, 1]);
4677 auto b = input.staticArray!(long[4]);
4678 static assert(is(typeof(b) == long[4]));
4679 assert(b == [0, 1, 2, 0]);
4682 // Tests that code compiles when there is an elaborate destructor and exceptions
4683 // are thrown. Unfortunately can't test that memory is initialized
4684 // before having a destructor called on it.
4685 @safe nothrow unittest
4687 // exists only to allow doing something in the destructor. Not tested
4688 // at the end because value appears to depend on implementation of the.
4689 // function.
4690 static int preventersDestroyed = 0;
4692 static struct CopyPreventer
4694 bool on = false;
4695 this(this)
4697 if (on) throw new Exception("Thou shalt not copy past me!");
4700 ~this()
4702 preventersDestroyed++;
4705 auto normalArray =
4707 CopyPreventer(false),
4708 CopyPreventer(false),
4709 CopyPreventer(true),
4710 CopyPreventer(false),
4711 CopyPreventer(true),
4716 auto staticArray = normalArray.staticArray!5;
4717 assert(false);
4719 catch (Exception e){}
4723 nothrow pure @safe @nogc unittest
4725 auto a = [1, 2].staticArray;
4726 assert(is(typeof(a) == int[2]) && a == [1, 2]);
4728 import std.range : iota;
4730 2.iota.staticArray!2.checkStaticArray!int([0, 1]);
4731 2.iota.staticArray!(double[2]).checkStaticArray!double([0, 1]);
4732 2.iota.staticArray!(long[2]).checkStaticArray!long([0, 1]);
4735 nothrow pure @safe @nogc unittest
4737 import std.range : iota;
4738 size_t copiedAmount;
4739 2.iota.staticArray!1(copiedAmount);
4740 assert(copiedAmount == 1);
4741 2.iota.staticArray!3(copiedAmount);
4742 assert(copiedAmount == 2);
4745 /// ditto
4746 auto staticArray(alias a)()
4747 if (isInputRange!(typeof(a)))
4749 return .staticArray!(size_t(a.length))(a);
4752 /// ditto
4753 auto staticArray(U, alias a)()
4754 if (isInputRange!(typeof(a)))
4756 return .staticArray!(U[size_t(a.length)])(a);
4759 /// static array from CT range
4760 nothrow pure @safe @nogc unittest
4762 import std.range : iota;
4764 enum a = staticArray!(2.iota);
4765 static assert(is(typeof(a) == int[2]));
4766 assert(a == [0, 1]);
4768 enum b = staticArray!(long, 2.iota);
4769 static assert(is(typeof(b) == long[2]));
4770 assert(b == [0, 1]);
4773 nothrow pure @safe @nogc unittest
4775 import std.range : iota;
4777 enum a = staticArray!(2.iota);
4778 staticArray!(2.iota).checkStaticArray!int([0, 1]);
4779 staticArray!(double, 2.iota).checkStaticArray!double([0, 1]);
4780 staticArray!(long, 2.iota).checkStaticArray!long([0, 1]);
4783 version (StdUnittest) private void checkStaticArray(T, T1, T2)(T1 a, T2 b) nothrow @safe pure @nogc
4785 static assert(is(T1 == T[T1.length]));
4786 assert(a == b, "a must be equal to b");