1 // Written in the D programming language.
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;)
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
58 $(TR $(TD $(LREF sameTail))
59 $(TD Checks if the final segments of two arrays refer to the same place
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)
84 import std
.functional
;
88 import std
.range
.primitives
;
89 public import std
.range
.primitives
: save
, empty
, popFront
, popBack
, front
, back
;
92 * Allocates an array and initializes it with copies of the elements
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.
100 * r = range (or aggregate with `opApply` function) whose elements are copied into the allocated array
102 * allocated and initialized array
104 ForeachType
!Range
[] array(Range
)(Range r
)
105 if (isIterable
!Range
&& !isAutodecodableString
!Range
&& !isInfinite
!Range
)
109 // Compile-time version to avoid memcpy calls.
110 // Also used to infer attributes of array().
111 typeof(return) result
;
117 alias E
= ForeachType
!Range
;
118 static if (hasLength
!Range
)
120 const length
= r
.length
;
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 cnt
; //Number of elements that have been initialized
134 emplaceRef
!E(result
[cnt
], e
);
137 } catch (Exception e
)
139 //https://issues.dlang.org/show_bug.cgi?id=22185
140 //Make any uninitialized elements safely destructible.
141 foreach (ref elem
; result
[cnt
..$])
143 import core
.internal
.lifetime
: emplaceInitializer
;
144 emplaceInitializer(elem
);
149 https://issues.dlang.org/show_bug.cgi?id=22673
151 We preallocated an array, we should ensure that enough range elements
152 were gathered such that every slot in the array is filled. If not, the GC
153 will collect the allocated array, leading to the `length - cnt` left over elements
154 being collected too - despite their contents having no guarantee of destructibility.
156 assert(length
== cnt
,
157 "Range .length property was not equal to the length yielded by the range before becoming empty");
158 return (() @trusted => cast(E
[]) result
)();
162 auto a
= appender
!(E
[])();
172 ForeachType
!(typeof((*Range
).init
))[] array(Range
)(Range r
)
173 if (is(Range
== U
*, U
) && isIterable
!U
&& !isAutodecodableString
!Range
&& !isInfinite
!Range
)
179 @safe pure nothrow unittest
181 auto a
= array([1, 2, 3, 4, 5][]);
182 assert(a
== [ 1, 2, 3, 4, 5 ]);
185 @safe pure nothrow unittest
187 import std
.algorithm
.comparison
: equal
;
192 auto a
= array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
193 assert(equal(a
, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
196 @safe pure nothrow unittest
205 auto arr
= (new MyRange
).array
;
209 @safe pure nothrow unittest
211 immutable int[] a
= [1, 2, 3, 4];
216 @safe pure nothrow unittest
218 import std
.algorithm
.comparison
: equal
;
222 noreturn
opAssign(Foo
)
226 auto opEquals(Foo foo
)
231 auto a
= array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
232 assert(equal(a
, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
235 // https://issues.dlang.org/show_bug.cgi?id=12315
236 @safe pure nothrow unittest
238 static struct Bug12315
{ immutable int i
; }
239 enum bug12315
= [Bug12315(123456789)].array();
240 static assert(bug12315
[0].i
== 123456789);
243 @safe pure nothrow unittest
246 static struct S
{int* p
;}
247 auto a
= array(immutable(S
).init
.repeat(5));
248 assert(a
.length
== 5);
251 // https://issues.dlang.org/show_bug.cgi?id=18995
254 import core
.memory
: __delete
;
259 this(int) { alive
= true; ++nAlive
; }
260 this(this) { nAlive
+= alive
; }
261 ~this() { nAlive
-= alive
; alive
= false; }
264 import std
.algorithm
.iteration
: map
;
265 import std
.range
: iota
;
267 auto arr
= iota(3).map
!(a
=> S(a
)).array
;
270 // No good way to ensure the GC frees this, just call the lifetime function
277 @safe pure nothrow @nogc unittest
279 //Turn down infinity:
280 static assert(!is(typeof(
285 // https://issues.dlang.org/show_bug.cgi?id=20937
286 @safe pure nothrow unittest
293 @safe pure nothrow void popFront(){empty
= true;}
298 // Test that `array(scope InputRange r)` returns a non-scope array
299 // https://issues.dlang.org/show_bug.cgi?id=23300
300 @safe pure nothrow unittest
304 import std
.algorithm
.iteration
: map
;
305 int[3] arr
= [1, 2, 3];
306 scope r
= arr
[].map
!(x
=> x
+ 3);
312 Convert a narrow autodecoding string to an array type that fully supports
313 random access. This is handled as a special case and always returns an array
316 NOTE: This function is never used when autodecoding is turned off.
319 str = `isNarrowString` to be converted to an array of `dchar`
321 a `dchar[]`, `const(dchar)[]`, or `immutable(dchar)[]` depending on the constness of
324 CopyTypeQualifiers
!(ElementType
!String
,dchar)[] array(String
)(scope String
str)
325 if (isAutodecodableString
!String
)
327 import std
.utf
: toUTF32
;
328 auto temp
= str.toUTF32
;
329 /* Unsafe cast. Allowed because toUTF32 makes a new array
330 and copies all the elements.
332 return () @trusted { return cast(CopyTypeQualifiers
!(ElementType
!String
, dchar)[]) temp
; } ();
336 @safe pure nothrow unittest
338 import std
.range
.primitives
: isRandomAccessRange
;
339 import std
.traits
: isAutodecodableString
;
341 // note that if autodecoding is turned off, `array` will not transcode these.
342 static if (isAutodecodableString
!string
)
343 assert("Hello D".array
== "Hello D"d
);
345 assert("Hello D".array
== "Hello D");
347 static if (isAutodecodableString
!wstring
)
348 assert("Hello D"w
.array
== "Hello D"d
);
350 assert("Hello D"w
.array
== "Hello D"w
);
352 static assert(isRandomAccessRange
!dstring
== true);
357 import std
.conv
: to
;
359 static struct TestArray
{ int x
; string
toString() @safe { return to
!string(x
); } }
361 static struct OpAssign
364 this(uint num
) { this.num
= num
; }
366 // Templating opAssign to make sure the bugs with opAssign being
367 // templated are fixed.
368 void opAssign(T
)(T rhs
) { this.num
= rhs
.num
; }
371 static struct OpApply
373 int opApply(scope int delegate(ref int) @safe dg
)
386 auto a
= array([1, 2, 3, 4, 5][]);
387 assert(a
== [ 1, 2, 3, 4, 5 ]);
389 auto b
= array([TestArray(1), TestArray(2)][]);
390 assert(b
== [TestArray(1), TestArray(2)]);
395 this(int y
) { x
= y
; }
396 override string
toString() const @safe { return to
!string(x
); }
398 auto c
= array([new C(1), new C(2)][]);
402 auto d
= array([1.0, 2.2, 3][]);
403 assert(is(typeof(d
) == double[]));
404 assert(d
== [1.0, 2.2, 3]);
406 auto e
= [OpAssign(1), OpAssign(2)];
410 assert(array(OpApply
.init
) == [0,1,2,3,4,5,6,7,8,9]);
411 static if (isAutodecodableString
!string
)
413 assert(array("ABC") == "ABC"d
);
414 assert(array("ABC".dup
) == "ABC"d
.dup
);
418 // https://issues.dlang.org/show_bug.cgi?id=8233
419 @safe pure nothrow unittest
421 assert(array("hello world"d
) == "hello world"d
);
422 immutable a
= [1, 2, 3, 4, 5];
423 assert(array(a
) == a
);
425 assert(array(b
) == a
);
427 //To verify that the opAssign branch doesn't get screwed up by using Unqual.
428 //EDIT: array no longer calls opAssign.
431 ref S
opAssign(S
)(const ref S rhs
)
439 static foreach (T
; AliasSeq
!(S
, const S
, immutable S
))
441 auto arr
= [T(1), T(2), T(3), T(4)];
442 assert(array(arr
) == arr
);
446 // https://issues.dlang.org/show_bug.cgi?id=9824
447 @safe pure nothrow unittest
451 @disable void opAssign(S
);
454 auto arr
= [S(0), S(1), S(2)];
458 // https://issues.dlang.org/show_bug.cgi?id=10220
459 @safe pure nothrow unittest
461 import std
.algorithm
.comparison
: equal
;
462 import std
.exception
;
463 import std
.range
: repeat
;
470 this(int v
) { val
= v
; }
474 auto r
= S(1).repeat(2).array();
475 assert(equal(r
, [S(1), S(1)]));
478 //https://issues.dlang.org/show_bug.cgi?id=22673
483 enum size_t length
= 100;
484 enum theRealLength
= 50;
488 return idx
<= theRealLength
;
499 static assert(hasLength
!LyingRange
);
501 import std
.exception
: assertThrown
;
502 assertThrown
!Error(array(rng
));
504 //https://issues.dlang.org/show_bug.cgi?id=22185
508 static struct ThrowingCopy
511 this(ref return scope ThrowingCopy rhs
)
515 throw new Exception("This throws");
520 Any time this destructor runs, it should be running on "valid"
521 data. This is is mimicked by having a .init other than 0 (the value the memory
522 practically will be from the GC).
526 //This will only trigger during GC finalization so avoid writefln for now.
527 printf("Destructor failure in ThrowingCopy(%d) @ %p", x
, &this);
528 assert(x
== 420, "unittest destructor failed");
532 static struct LyingThrowingRange
534 enum size_t length
= 100;
535 enum size_t evilRealLength
= 50;
539 return ThrowingCopy(12);
543 return idx
== evilRealLength
;
550 static assert(hasLength
!LyingThrowingRange
);
551 import std
.exception
: assertThrown
;
553 assertThrown(array(LyingThrowingRange()));
555 import core
.memory
: GC
;
557 Force a collection early. Doesn't always actually finalize the bad objects
558 but trying to collect soon after the allocation is thrown away means any potential failures
565 Returns a newly allocated associative array from a range of key/value tuples
566 or from a range of keys and a range of values.
569 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
570 of tuples of keys and values.
571 keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys
572 values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values
576 A newly allocated associative array out of elements of the input
577 range, which must be a range of tuples (Key, Value) or
578 a range of keys and a range of values. If given two ranges of unequal
579 lengths after the elements of the shorter are exhausted the remaining
580 elements of the longer will not be considered.
581 Returns a null associative array reference when given an empty range.
582 Duplicates: Associative arrays have unique keys. If r contains duplicate keys,
583 then the result will contain the value of the last pair for that key in r.
585 See_Also: $(REF Tuple, std,typecons), $(REF zip, std,range)
588 auto assocArray(Range
)(Range r
)
589 if (isInputRange
!Range
)
591 import std
.typecons
: isTuple
;
593 alias E
= ElementType
!Range
;
594 static assert(isTuple
!E
, "assocArray: argument must be a range of tuples,"
595 ~" but was a range of "~E
.stringof
);
596 static assert(E
.length
== 2, "assocArray: tuple dimension must be 2");
597 alias KeyType
= E
.Types
[0];
598 alias ValueType
= E
.Types
[1];
599 static assert(isMutable
!ValueType
, "assocArray: value type must be mutable");
601 ValueType
[KeyType
] aa
;
608 auto assocArray(Keys
, Values
)(Keys keys
, Values values
)
609 if (isInputRange
!Values
&& isInputRange
!Keys
)
611 static if (isDynamicArray
!Keys
&& isDynamicArray
!Values
612 && !isNarrowString
!Keys
&& !isNarrowString
!Values
)
616 // aaLiteral is nothrow when the destructors don't throw
617 static if (is(typeof(() nothrow
619 import std
.range
: ElementType
;
620 import std
.traits
: hasElaborateDestructor
;
621 alias KeyElement
= ElementType
!Keys
;
622 static if (hasElaborateDestructor
!KeyElement
)
623 KeyElement
.init
.__xdtor();
625 alias ValueElement
= ElementType
!Values
;
626 static if (hasElaborateDestructor
!ValueElement
)
627 ValueElement
.init
.__xdtor();
630 scope(failure
) assert(false, "aaLiteral must not throw");
632 if (values
.length
> keys
.length
)
633 values
= values
[0 .. keys
.length
];
634 else if (keys
.length
> values
.length
)
635 keys
= keys
[0 .. values
.length
];
636 aa
= aaLiteral(keys
, values
);
638 alias Key
= typeof(keys
[0]);
639 alias Value
= typeof(values
[0]);
640 return (() @trusted => cast(Value
[Key
]) aa
)();
644 // zip is not always able to infer nothrow
645 alias Key
= ElementType
!Keys
;
646 alias Value
= ElementType
!Values
;
647 static assert(isMutable
!Value
, "assocArray: value type must be mutable");
651 if (values
.empty
) break;
653 // aa[key] is incorrectly not @safe if the destructor throws
654 // https://issues.dlang.org/show_bug.cgi?id=18592
655 static if (is(typeof(() @safe
657 import std
.range
: ElementType
;
658 import std
.traits
: hasElaborateDestructor
;
659 alias KeyElement
= ElementType
!Keys
;
660 static if (hasElaborateDestructor
!KeyElement
)
661 KeyElement
.init
.__xdtor();
663 alias ValueElement
= ElementType
!Values
;
664 static if (hasElaborateDestructor
!ValueElement
)
665 ValueElement
.init
.__xdtor();
667 aa
[key
] = values
.front
;
671 aa
[key
] = values
.front
;
676 aa
[key
] = values
.front
;
685 @safe pure /*nothrow*/ unittest
687 import std
.range
: repeat
, zip
;
688 import std
.typecons
: tuple
;
689 import std
.range
.primitives
: autodecodeStrings
;
690 auto a
= assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap
691 static assert(is(typeof(a
) == string
[int]));
692 assert(a
== [0:"a", 1:"b", 2:"c"]);
694 auto b
= assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
695 static assert(is(typeof(b
) == string
[string
]));
696 assert(b
== ["foo":"bar", "baz":"quux"]);
698 static if (autodecodeStrings
)
701 alias achar
= immutable(char);
702 auto c
= assocArray("ABCD", true.repeat
);
703 static assert(is(typeof(c
) == bool[achar
]));
704 bool[achar
] expected
= ['D':true, 'A':true, 'B':true, 'C':true];
705 assert(c
== expected
);
708 // Cannot be version (StdUnittest) - recursive instantiation error
709 // https://issues.dlang.org/show_bug.cgi?id=11053
710 @safe pure nothrow unittest
713 static assert(!__traits(compiles
, [ 1, 2, 3 ].assocArray()));
714 static assert(!__traits(compiles
, [ tuple("foo", "bar", "baz") ].assocArray()));
715 static assert(!__traits(compiles
, [ tuple("foo") ].assocArray()));
716 assert([ tuple("foo", "bar") ].assocArray() == ["foo": "bar"]);
719 // https://issues.dlang.org/show_bug.cgi?id=13909
720 @safe pure nothrow unittest
723 auto a
= [tuple
!(const string
, string
)("foo", "bar")];
724 auto b
= [tuple
!(string
, const string
)("foo", "bar")];
726 assert(assocArray(a
) == [cast(const(string
)) "foo": "bar"]);
727 static assert(!__traits(compiles
, assocArray(b
)));
730 // https://issues.dlang.org/show_bug.cgi?id=5502
731 @safe pure nothrow unittest
733 auto a
= assocArray([0, 1, 2], ["a", "b", "c"]);
734 static assert(is(typeof(a
) == string
[int]));
735 assert(a
== [0:"a", 1:"b", 2:"c"]);
737 auto b
= assocArray([0, 1, 2], [3L, 4, 5]);
738 static assert(is(typeof(b
) == long[int]));
739 assert(b
== [0: 3L, 1: 4, 2: 5]);
742 // https://issues.dlang.org/show_bug.cgi?id=5502
745 import std
.algorithm
.iteration
: filter
, map
;
746 import std
.range
: enumerate
;
747 import std
.range
.primitives
: autodecodeStrings
;
749 auto r
= "abcde".enumerate
.filter
!(a
=> a
.index
== 2);
750 auto a
= assocArray(r
.map
!(a
=> a
.value
), r
.map
!(a
=> a
.index
));
751 static if (autodecodeStrings
)
754 alias achar
= immutable(char);
755 static assert(is(typeof(a
) == size_t
[achar
]));
756 assert(a
== [achar('c'): size_t(2)]);
759 @safe nothrow pure unittest
761 import std
.range
: iota
;
762 auto b
= assocArray(3.iota
, 3.iota(6));
763 static assert(is(typeof(b
) == int[int]));
764 assert(b
== [0: 3, 1: 4, 2: 5]);
766 b
= assocArray([0, 1, 2], [3, 4, 5]);
767 assert(b
== [0: 3, 1: 4, 2: 5]);
772 struct ThrowingElement
778 throw new Exception("");
781 static assert(!__traits(compiles
, () nothrow { assocArray([ThrowingElement()], [0]);}));
782 assert(assocArray([ThrowingElement()], [0]) == [ThrowingElement(): 0]);
784 static assert(!__traits(compiles
, () nothrow { assocArray([0], [ThrowingElement()]);}));
785 assert(assocArray([0], [ThrowingElement()]) == [0: ThrowingElement()]);
787 import std
.range
: iota
;
788 static assert(!__traits(compiles
, () nothrow { assocArray(1.iota
, [ThrowingElement()]);}));
789 assert(assocArray(1.iota
, [ThrowingElement()]) == [0: ThrowingElement()]);
794 import std
.range
: iota
;
801 void* p
= arr
.ptr
+ 1; // unsafe
804 static assert(!__traits(compiles
, () @safe { assocArray(1.iota
, [UnsafeElement()]);}));
805 assert(assocArray(1.iota
, [UnsafeElement()]) == [0: UnsafeElement()]);
812 string
front() const @system;
815 bool empty() const { return false; }
819 static assert(!__traits(compiles
, assocArray(keys
, values
)));
823 Construct a range iterating over an associative array by key/value tuples.
826 aa = The associative array to iterate over.
828 Returns: A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
829 of Tuple's of key and value pairs from the given associative array. The members
830 of each pair can be accessed by name (`.key` and `.value`). or by integer
831 index (0 and 1 respectively).
833 auto byPair(AA
)(AA aa
)
834 if (isAssociativeArray
!AA
)
836 import std
.algorithm
.iteration
: map
;
837 import std
.typecons
: tuple
;
840 .map
!(pair
=> tuple
!("key", "value")(pair
.key
, pair
.value
));
844 @safe pure nothrow unittest
846 import std
.algorithm
.sorting
: sort
;
847 import std
.typecons
: tuple
, Tuple
;
849 auto aa
= ["a": 1, "b": 2, "c": 3];
850 Tuple
!(string
, int)[] pairs
;
852 // Iteration over key/value pairs.
853 foreach (pair
; aa
.byPair
)
856 pairs
~= tuple("B", pair
.value
);
861 // Iteration order is implementation-dependent, so we should sort it to get
871 @safe pure nothrow unittest
873 import std
.typecons
: tuple
, Tuple
;
874 import std
.meta
: AliasSeq
;
877 auto pairs
= aa
.byPair();
879 alias PT
= typeof(pairs
.front
);
880 static assert(is(PT
: Tuple
!(string
,int)));
881 static assert(PT
.fieldNames
== AliasSeq
!("key", "value"));
882 static assert(isForwardRange
!(typeof(pairs
)));
884 assert(!pairs
.empty
);
885 assert(pairs
.front
== tuple("a", 2));
887 auto savedPairs
= pairs
.save
;
891 assert(!savedPairs
.empty
);
892 assert(savedPairs
.front
== tuple("a", 2));
895 // https://issues.dlang.org/show_bug.cgi?id=17711
896 @safe pure nothrow unittest
898 const(int[string
]) aa
= [ "abc": 123 ];
900 // Ensure that byKeyValue is usable with a const AA.
901 auto kv
= aa
.byKeyValue
;
903 assert(kv
.front
.key
== "abc" && kv
.front
.value
== 123);
907 // Ensure byPair is instantiable with const AA.
909 static assert(isInputRange
!(typeof(r
)));
910 assert(!r
.empty
&& r
.front
[0] == "abc" && r
.front
[1] == 123);
915 private template blockAttribute(T
)
918 static if (hasIndirections
!(T
) ||
is(T
== void))
920 enum blockAttribute
= 0;
924 enum blockAttribute
= GC
.BlkAttr
.NO_SCAN
;
930 import core
.memory
: UGC
= GC
;
931 static assert(!(blockAttribute
!void & UGC
.BlkAttr
.NO_SCAN
));
934 // Returns the number of dimensions in an array T.
935 private template nDimensions(T
)
937 static if (isArray
!T
)
939 enum nDimensions
= 1 + nDimensions
!(typeof(T
.init
[0]));
943 enum nDimensions
= 0;
949 static assert(nDimensions
!(uint[]) == 1);
950 static assert(nDimensions
!(float[][]) == 2);
954 Returns a new array of type `T` allocated on the garbage collected heap
955 without initializing its elements. This can be a useful optimization if every
956 element will be immediately initialized. `T` may be a multidimensional
957 array. In this case sizes may be specified for any number of dimensions from 0
958 to the number in `T`.
960 uninitializedArray is `nothrow` and weakly `pure`.
962 uninitializedArray is `@system` if the uninitialized element type has pointers.
965 T = The type of the resulting array elements
966 sizes = The length dimension(s) of the resulting array
968 An array of `T` with `I.length` dimensions.
970 auto uninitializedArray(T
, I
...)(I sizes
) nothrow @system
971 if (isDynamicArray
!T
&& allSatisfy
!(isIntegral
, I
) && hasIndirections
!(ElementEncodingType
!T
))
973 enum isSize_t(E
) = is (E
: size_t
);
974 alias toSize_t(E
) = size_t
;
976 static assert(allSatisfy
!(isSize_t
, I
),
977 "Argument types in "~I
.stringof
~" are not all convertible to size_t: "
978 ~Filter
!(templateNot
!(isSize_t
), I
).stringof
);
980 //Eagerlly transform non-size_t into size_t to avoid template bloat
981 alias ST
= staticMap
!(toSize_t
, I
);
983 return arrayAllocImpl
!(false, T
, ST
)(sizes
);
987 auto uninitializedArray(T
, I
...)(I sizes
) nothrow @trusted
988 if (isDynamicArray
!T
&& allSatisfy
!(isIntegral
, I
) && !hasIndirections
!(ElementEncodingType
!T
))
990 enum isSize_t(E
) = is (E
: size_t
);
991 alias toSize_t(E
) = size_t
;
993 static assert(allSatisfy
!(isSize_t
, I
),
994 "Argument types in "~I
.stringof
~" are not all convertible to size_t: "
995 ~Filter
!(templateNot
!(isSize_t
), I
).stringof
);
997 //Eagerlly transform non-size_t into size_t to avoid template bloat
998 alias ST
= staticMap
!(toSize_t
, I
);
1000 return arrayAllocImpl
!(false, T
, ST
)(sizes
);
1003 @system nothrow pure unittest
1005 double[] arr
= uninitializedArray
!(double[])(100);
1006 assert(arr
.length
== 100);
1008 double[][] matrix
= uninitializedArray
!(double[][])(42, 31);
1009 assert(matrix
.length
== 42);
1010 assert(matrix
[0].length
== 31);
1012 char*[] ptrs
= uninitializedArray
!(char*[])(100);
1013 assert(ptrs
.length
== 100);
1017 Returns a new array of type `T` allocated on the garbage collected heap.
1019 Partial initialization is done for types with indirections, for preservation
1020 of memory safety. Note that elements will only be initialized to 0, but not
1021 necessarily the element type's `.init`.
1023 minimallyInitializedArray is `nothrow` and weakly `pure`.
1026 T = The type of the array elements
1027 sizes = The length dimension(s) of the resulting array
1029 An array of `T` with `I.length` dimensions.
1031 auto minimallyInitializedArray(T
, I
...)(I sizes
) nothrow @trusted
1032 if (isDynamicArray
!T
&& allSatisfy
!(isIntegral
, I
))
1034 enum isSize_t(E
) = is (E
: size_t
);
1035 alias toSize_t(E
) = size_t
;
1037 static assert(allSatisfy
!(isSize_t
, I
),
1038 "Argument types in "~I
.stringof
~" are not all convertible to size_t: "
1039 ~Filter
!(templateNot
!(isSize_t
), I
).stringof
);
1040 //Eagerlly transform non-size_t into size_t to avoid template bloat
1041 alias ST
= staticMap
!(toSize_t
, I
);
1043 return arrayAllocImpl
!(true, T
, ST
)(sizes
);
1047 @safe pure nothrow unittest
1049 import std
.algorithm
.comparison
: equal
;
1050 import std
.range
: repeat
;
1052 auto arr
= minimallyInitializedArray
!(int[])(42);
1053 assert(arr
.length
== 42);
1055 // Elements aren't necessarily initialized to 0, so don't do this:
1056 // assert(arr.equal(0.repeat(42)));
1057 // If that is needed, initialize the array normally instead:
1058 auto arr2
= new int[42];
1059 assert(arr2
.equal(0.repeat(42)));
1062 @safe pure nothrow unittest
1064 cast(void) minimallyInitializedArray
!(int[][][][][])();
1065 double[] arr
= minimallyInitializedArray
!(double[])(100);
1066 assert(arr
.length
== 100);
1068 double[][] matrix
= minimallyInitializedArray
!(double[][])(42);
1069 assert(matrix
.length
== 42);
1070 foreach (elem
; matrix
)
1072 assert(elem
.ptr
is null);
1076 // from rt/lifetime.d
1077 private extern(C
) void[] _d_newarrayU(const TypeInfo ti
, size_t length
) pure nothrow;
1079 // from rt/tracegc.d
1080 version (D_ProfileGC
)
1081 private extern (C
) void[] _d_newarrayUTrace(string file
, size_t line
,
1082 string funcname
, const scope TypeInfo ti
, size_t length
) pure nothrow;
1084 private auto arrayAllocImpl(bool minimallyInitialized
, T
, I
...)(I sizes
) nothrow
1086 static assert(I
.length
<= nDimensions
!T
,
1087 I
.length
.stringof
~"dimensions specified for a "~nDimensions
!T
.stringof
~" dimensional array.");
1089 alias E
= ElementEncodingType
!T
;
1093 static if (I
.length
!= 0)
1095 static assert(is(I
[0] == size_t
), "I[0] must be of type size_t not "
1097 alias size
= sizes
[0];
1100 static if (I
.length
== 1)
1104 static if (__traits(compiles
, new E
[](size
)))
1105 ret = new E
[](size
);
1106 else static if (__traits(compiles
, ret ~= E
.init
))
1110 //Issue: if E has an impure postblit, then all of arrayAllocImpl
1111 //Will be impure, even during non CTFE.
1112 foreach (i
; 0 .. size
)
1119 assert(0, "No postblit nor default init on " ~ E
.stringof
~
1120 ": At least one is required for CTFE.");
1124 import core
.stdc
.string
: memset
;
1128 _d_newarrayU is part of druntime, and creates an uninitialized
1129 block, just like GC.malloc. However, it also sets the appropriate
1130 bits, and sets up the block as an appendable array of type E[],
1131 which will inform the GC how to destroy the items in the block
1132 when it gets collected.
1134 _d_newarrayU returns a void[], but with the length set according
1137 version (D_ProfileGC
)
1139 // FIXME: file, line, function should be propagated from the
1140 // caller, not here.
1141 *(cast(void[]*)&ret) = _d_newarrayUTrace(__FILE__
, __LINE__
,
1142 __FUNCTION__
, typeid(E
[]), size
);
1145 *(cast(void[]*)&ret) = _d_newarrayU(typeid(E
[]), size
);
1146 static if (minimallyInitialized
&& hasIndirections
!E
)
1147 // _d_newarrayU would have asserted if the multiplication below
1148 // had overflowed, so we don't have to check it again.
1149 memset(ret.ptr
, 0, E
.sizeof
* ret.length
);
1152 else static if (I
.length
> 1)
1154 ret = arrayAllocImpl
!(false, E
[])(size
);
1155 foreach (ref elem
; ret)
1156 elem
= arrayAllocImpl
!(minimallyInitialized
, E
)(sizes
[1..$]);
1162 @safe nothrow pure unittest
1164 auto s1
= uninitializedArray
!(int[])();
1165 auto s2
= minimallyInitializedArray
!(int[])();
1166 assert(s1
.length
== 0);
1167 assert(s2
.length
== 0);
1170 // https://issues.dlang.org/show_bug.cgi?id=9803
1171 @safe nothrow pure unittest
1173 auto a
= minimallyInitializedArray
!(int*[])(1);
1174 assert(a
[0] == null);
1175 auto b
= minimallyInitializedArray
!(int[][])(1);
1177 auto c
= minimallyInitializedArray
!(int*[][])(1, 1);
1178 assert(c
[0][0] == null);
1181 // https://issues.dlang.org/show_bug.cgi?id=10637
1182 @safe pure nothrow unittest
1186 static struct I
{int i
; alias i
this;}
1199 // note, this assert is invalid -- a struct should always be able
1200 // to run its dtor on the .init value, I'm leaving it here
1201 // commented out because the original test case had it. I'm not
1202 // sure what it's trying to prove.
1204 // What happens now that minimallyInitializedArray adds the
1205 // destructor run to the GC, is that this assert would fire in the
1206 // GC, which triggers an invalid memory operation.
1207 //assert(p != null);
1210 auto a
= minimallyInitializedArray
!(S
[])(1);
1211 assert(a
[0].p
== null);
1212 enum b
= minimallyInitializedArray
!(S
[])(1);
1213 assert(b
[0].p
== null);
1216 @safe pure nothrow unittest
1221 this(this) @disable;
1223 auto a1
= minimallyInitializedArray
!(S1
[][])(2, 2);
1228 //this(this) @disable;
1230 auto a2
= minimallyInitializedArray
!(S2
[][])(2, 2);
1232 enum b2
= minimallyInitializedArray
!(S2
[][])(2, 2);
1233 assert(b2
!is null);
1237 this(this) @disable;
1239 auto a3
= minimallyInitializedArray
!(S3
[][])(2, 2);
1241 enum b3
= minimallyInitializedArray
!(S3
[][])(2, 2);
1242 assert(b3
!is null);
1246 Returns the overlapping portion, if any, of two arrays. Unlike `equal`,
1247 `overlap` only compares the pointers and lengths in the
1248 ranges, not the values referred by them. If `r1` and `r2` have an
1249 overlapping slice, returns that slice. Otherwise, returns the null
1253 a = The first array to compare
1254 b = The second array to compare
1256 The overlapping portion of the two arrays.
1258 CommonType
!(T
[], U
[]) overlap(T
, U
)(T
[] a
, U
[] b
) @trusted
1259 if (is(typeof(a
.ptr
< b
.ptr
) == bool))
1261 import std
.algorithm
.comparison
: min
;
1263 auto end
= min(a
.ptr
+ a
.length
, b
.ptr
+ b
.length
);
1264 // CTFE requires pairing pointer comparisons, which forces a
1265 // slightly inefficient implementation.
1266 if (a
.ptr
<= b
.ptr
&& b
.ptr
< a
.ptr
+ a
.length
)
1268 return b
.ptr
[0 .. end
- b
.ptr
];
1271 if (b
.ptr
<= a
.ptr
&& a
.ptr
< b
.ptr
+ b
.length
)
1273 return a
.ptr
[0 .. end
- a
.ptr
];
1280 @safe pure nothrow unittest
1282 int[] a
= [ 10, 11, 12, 13, 14 ];
1283 int[] b
= a
[1 .. 3];
1284 assert(overlap(a
, b
) == [ 11, 12 ]);
1286 // overlap disappears even though the content is the same
1287 assert(overlap(a
, b
).empty
);
1289 static test()() @nogc
1291 auto a
= "It's three o'clock"d
;
1292 auto b
= a
[5 .. 10];
1293 return b
.overlap(a
);
1296 //works at compile-time
1297 static assert(test == "three"d
);
1301 @safe pure nothrow unittest
1303 import std
.meta
: AliasSeq
;
1305 // can be used as an alternative implementation of overlap that returns
1306 // `true` or `false` instead of a slice of the overlap
1307 bool isSliceOf(T
)(const scope T
[] part
, const scope T
[] whole
)
1309 return part
.overlap(whole
) is part
;
1312 auto x
= [1, 2, 3, 4, 5];
1314 assert(isSliceOf(x
[3..$], x
));
1315 assert(isSliceOf(x
[], x
));
1316 assert(!isSliceOf(x
, x
[3..$]));
1317 assert(!isSliceOf([7, 8], x
));
1318 assert(isSliceOf(null, x
));
1320 // null is a slice of itself
1321 assert(isSliceOf(null, null));
1323 foreach (T
; AliasSeq
!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
1325 T a
= [1, 2, 3, 4, 5];
1331 assert(isSliceOf(a
, a
));
1332 assert(isSliceOf(b
, a
));
1333 assert(isSliceOf(a
, b
));
1335 assert(isSliceOf(c
, a
));
1336 assert(isSliceOf(c
, b
));
1337 assert(!isSliceOf(a
, c
));
1338 assert(!isSliceOf(b
, c
));
1340 assert(isSliceOf(d
, a
));
1341 assert(isSliceOf(d
, b
));
1342 assert(!isSliceOf(a
, d
));
1343 assert(!isSliceOf(b
, d
));
1345 assert(isSliceOf(e
, a
));
1346 assert(isSliceOf(e
, b
));
1347 assert(isSliceOf(e
, c
));
1348 assert(isSliceOf(e
, d
));
1350 //verifies R-value compatibilty
1351 assert(!isSliceOf(a
[$ .. $], a
));
1352 assert(isSliceOf(a
[0 .. 0], a
));
1353 assert(isSliceOf(a
, a
[0.. $]));
1354 assert(isSliceOf(a
[0 .. $], a
));
1358 @safe pure nothrow unittest
1360 static void test(L
, R
)(L l
, R r
)
1362 assert(overlap(l
, r
) == [ 100, 12 ]);
1364 assert(overlap(l
, l
[0 .. 2]) is l
[0 .. 2]);
1365 assert(overlap(l
, l
[3 .. 5]) is l
[3 .. 5]);
1366 assert(overlap(l
[0 .. 2], l
) is l
[0 .. 2]);
1367 assert(overlap(l
[3 .. 5], l
) is l
[3 .. 5]);
1370 int[] a
= [ 10, 11, 12, 13, 14 ];
1371 int[] b
= a
[1 .. 3];
1374 immutable int[] c
= a
.idup
;
1375 immutable int[] d
= c
[1 .. 3];
1378 assert(overlap(a
, b
.dup
).empty
);
1380 assert(overlap(c
, d
.dup
.idup
).empty
);
1383 // https://issues.dlang.org/show_bug.cgi?id=9836
1384 @safe pure nothrow unittest
1386 // range primitives for array should work with alias this types
1392 @property Wrapper
save() { return this; }
1394 auto w
= Wrapper([1,2,3,4]);
1395 std
.array
.popFront(w
); // should work
1397 static assert(isInputRange
!Wrapper
);
1398 static assert(isForwardRange
!Wrapper
);
1399 static assert(isBidirectionalRange
!Wrapper
);
1400 static assert(isRandomAccessRange
!Wrapper
);
1403 private void copyBackwards(T
)(T
[] src
, T
[] dest
)
1405 import core
.stdc
.string
: memmove
;
1406 import std
.format
: format
;
1408 assert(src
.length
== dest
.length
, format
!
1409 "src.length %s must equal dest.length %s"(src
.length
, dest
.length
));
1411 if (!__ctfe || hasElaborateCopyConstructor
!T
)
1413 /* insertInPlace relies on dest being uninitialized, so no postblits allowed,
1414 * as this is a MOVE that overwrites the destination, not a COPY.
1415 * BUG: insertInPlace will not work with ctfe and postblits
1417 memmove(dest
.ptr
, src
.ptr
, src
.length
* T
.sizeof
);
1421 immutable len
= src
.length
;
1422 for (size_t i
= len
; i
-- > 0;)
1430 Inserts `stuff` (which must be an input range or any number of
1431 implicitly convertible items) in `array` at position `pos`.
1434 array = The array that `stuff` will be inserted into.
1435 pos = The position in `array` to insert the `stuff`.
1436 stuff = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives),
1437 or any number of implicitly convertible items to insert into `array`.
1439 void insertInPlace(T
, U
...)(ref T
[] array
, size_t pos
, U stuff
)
1440 if (!isSomeString
!(T
[])
1441 && allSatisfy
!(isInputRangeOrConvertible
!T
, U
) && U
.length
> 0)
1443 static if (allSatisfy
!(isInputRangeWithLengthOrConvertible
!T
, U
))
1445 import core
.internal
.lifetime
: emplaceRef
;
1447 immutable oldLen
= array
.length
;
1449 size_t to_insert
= 0;
1452 static if (is(E
: T
)) //a single convertible value, not a range
1455 to_insert
+= stuff
[i
].length
;
1459 array
.length
+= to_insert
;
1461 // Takes arguments array, pos, stuff
1462 // Spread apart array[] at pos by moving elements
1463 (() @trusted { copyBackwards(array
[pos
.. oldLen
], array
[pos
+to_insert
..$]); })();
1465 // Initialize array[pos .. pos+to_insert] with stuff[]
1469 static if (is(E
: T
))
1471 emplaceRef
!T(array
[pos
+ j
++], stuff
[i
]);
1475 foreach (v
; stuff
[i
])
1477 emplaceRef
!T(array
[pos
+ j
++], v
);
1485 // stuff has some InputRanges in it that don't have length
1486 // assume that stuff to be inserted is typically shorter
1487 // then the array that can be arbitrary big
1488 // TODO: needs a better implementation as there is no need to build an _array_
1489 // a singly-linked list of memory blocks (rope, etc.) will do
1490 auto app
= appender
!(T
[])();
1493 insertInPlace(array
, pos
, app
.data
);
1498 void insertInPlace(T
, U
...)(ref T
[] array
, size_t pos
, U stuff
)
1499 if (isSomeString
!(T
[]) && allSatisfy
!(isCharOrStringOrDcharRange
, U
))
1501 static if (is(Unqual
!T
== T
)
1502 && allSatisfy
!(isInputRangeWithLengthOrConvertible
!dchar, U
))
1504 import std
.utf
: codeLength
, byDchar
;
1505 // mutable, can do in place
1506 //helper function: re-encode dchar to Ts and store at *ptr
1507 static T
* putDChar(T
* ptr
, dchar ch
)
1509 static if (is(T
== dchar))
1516 import std
.utf
: encode
;
1517 T
[dchar.sizeof
/T
.sizeof
] buf
;
1518 immutable len
= encode(buf
, ch
);
1521 static if (T
.sizeof
== char.sizeof
)
1540 size_t to_insert
= 0;
1541 //count up the number of *codeunits* to insert
1543 to_insert
+= codeLength
!T(stuff
[i
]);
1544 array
.length
+= to_insert
;
1546 @trusted static void moveToRight(T
[] arr
, size_t gap
)
1548 static assert(!hasElaborateCopyConstructor
!T
,
1549 "T must not have an elaborate copy constructor");
1550 import core
.stdc
.string
: memmove
;
1553 for (size_t i
= arr
.length
- gap
; i
; --i
)
1554 arr
[gap
+ i
- 1] = arr
[i
- 1];
1557 memmove(arr
.ptr
+ gap
, arr
.ptr
, (arr
.length
- gap
) * T
.sizeof
);
1559 moveToRight(array
[pos
.. $], to_insert
);
1560 auto ptr
= array
.ptr
+ pos
;
1563 static if (is(E
: dchar))
1565 ptr
= putDChar(ptr
, stuff
[i
]);
1569 foreach (ch
; stuff
[i
].byDchar
)
1570 ptr
= putDChar(ptr
, ch
);
1573 assert(ptr
== array
.ptr
+ pos
+ to_insert
, "(ptr == array.ptr + pos + to_insert) is false");
1577 // immutable/const, just construct a new array
1578 auto app
= appender
!(T
[])();
1579 app
.put(array
[0 .. pos
]);
1582 app
.put(array
[pos
..$]);
1590 int[] a
= [ 1, 2, 3, 4 ];
1591 a
.insertInPlace(2, [ 1, 2 ]);
1592 assert(a
== [ 1, 2, 1, 2, 3, 4 ]);
1593 a
.insertInPlace(3, 10u, 11);
1594 assert(a
== [ 1, 2, 1, 10, 11, 2, 3, 4]);
1605 U
[] unionArr
= [u2
, u3
];
1606 unionArr
.insertInPlace(2, [u1
]);
1607 assert(unionArr
== [u2
, u3
, u1
]);
1608 unionArr
.insertInPlace(0, [u3
, u2
]);
1609 assert(unionArr
== [u3
, u2
, u2
, u3
, u1
]);
1616 this(int a
, float b
) { this.a
= a
; this.b
= b
; }
1619 C c1
= new C(42, 1.0);
1620 C c2
= new C(0, 0.0);
1621 C c3
= new C(int.max
, float.init
);
1623 C
[] classArr
= [c1
, c2
, c3
];
1624 insertInPlace(classArr
, 3, [c2
, c3
]);
1625 C
[5] classArr1
= classArr
;
1626 assert(classArr1
== [c1
, c2
, c3
, c2
, c3
]);
1627 insertInPlace(classArr
, 0, c3
, c1
);
1628 C
[7] classArr2
= classArr
;
1629 assert(classArr2
== [c3
, c1
, c1
, c2
, c3
, c2
, c3
]);
1632 //constraint helpers
1633 private template isInputRangeWithLengthOrConvertible(E
)
1635 template isInputRangeWithLengthOrConvertible(R
)
1637 //hasLength not defined for char[], wchar[] and dchar[]
1638 enum isInputRangeWithLengthOrConvertible
=
1639 (isInputRange
!R
&& is(typeof(R
.init
.length
))
1640 && is(ElementType
!R
: E
)) ||
is(R
: E
);
1645 private template isCharOrStringOrDcharRange(T
)
1647 enum isCharOrStringOrDcharRange
= isSomeString
!T || isSomeChar
!T ||
1648 (isInputRange
!T
&& is(ElementType
!T
: dchar));
1652 private template isInputRangeOrConvertible(E
)
1654 template isInputRangeOrConvertible(R
)
1656 enum isInputRangeOrConvertible
=
1657 (isInputRange
!R
&& is(ElementType
!R
: E
)) ||
is(R
: E
);
1663 // @system due to insertInPlace
1664 import core
.exception
;
1665 import std
.algorithm
.comparison
: equal
;
1666 import std
.algorithm
.iteration
: filter
;
1667 import std
.conv
: to
;
1668 import std
.exception
;
1671 bool test(T
, U
, V
)(T orig
, size_t pos
, U toInsert
, V result
)
1674 static if (is(T
== typeof(T
.init
.dup
)))
1679 a
.insertInPlace(pos
, toInsert
);
1680 if (!equal(a
, result
))
1684 static if (isInputRange
!U
)
1686 orig
.insertInPlace(pos
, filter
!"true"(toInsert
));
1687 return equal(orig
, result
);
1694 assert(test([1, 2, 3, 4], 0, [6, 7], [6, 7, 1, 2, 3, 4]));
1695 assert(test([1, 2, 3, 4], 2, [8, 9], [1, 2, 8, 9, 3, 4]));
1696 assert(test([1, 2, 3, 4], 4, [10, 11], [1, 2, 3, 4, 10, 11]));
1698 assert(test([1, 2, 3, 4], 0, 22, [22, 1, 2, 3, 4]));
1699 assert(test([1, 2, 3, 4], 2, 23, [1, 2, 23, 3, 4]));
1700 assert(test([1, 2, 3, 4], 4, 24, [1, 2, 3, 4, 24]));
1702 void testStr(T
, U
)(string file
= __FILE__
, size_t line
= __LINE__
)
1705 auto l
= to
!T("hello");
1706 auto r
= to
!U(" વિશ્વ");
1708 enforce(test(l
, 0, r
, " વિશ્વhello"),
1709 new AssertError("testStr failure 1", file
, line
));
1710 enforce(test(l
, 3, r
, "hel વિશ્વlo"),
1711 new AssertError("testStr failure 2", file
, line
));
1712 enforce(test(l
, l
.length
, r
, "hello વિશ્વ"),
1713 new AssertError("testStr failure 3", file
, line
));
1716 static foreach (T
; AliasSeq
!(char, wchar, dchar,
1717 immutable(char), immutable(wchar), immutable(dchar)))
1719 static foreach (U
; AliasSeq
!(char, wchar, dchar,
1720 immutable(char), immutable(wchar), immutable(dchar)))
1722 testStr
!(T
[], U
[])();
1728 bool testVar(T
, U
...)(T orig
, size_t pos
, U args
)
1730 static if (is(T
== typeof(T
.init
.dup
)))
1734 auto result
= args
[$-1];
1736 a
.insertInPlace(pos
, args
[0..$-1]);
1737 if (!equal(a
, result
))
1741 assert(testVar([1, 2, 3, 4], 0, 6, 7u, [6, 7, 1, 2, 3, 4]));
1742 assert(testVar([1L, 2, 3, 4], 2, 8, 9L, [1, 2, 8, 9, 3, 4]));
1743 assert(testVar([1L, 2, 3, 4], 4, 10L, 11, [1, 2, 3, 4, 10, 11]));
1744 assert(testVar([1L, 2, 3, 4], 4, [10, 11], 40L, 42L,
1745 [1, 2, 3, 4, 10, 11, 40, 42]));
1746 assert(testVar([1L, 2, 3, 4], 4, 10, 11, [40L, 42],
1747 [1, 2, 3, 4, 10, 11, 40, 42]));
1748 assert(testVar("t".idup
, 1, 'e', 's', 't', "test"));
1749 assert(testVar("!!"w
.idup
, 1, "\u00e9ll\u00f4", 'x', "TTT"w
, 'y',
1750 "!\u00e9ll\u00f4xTTTy!"));
1751 assert(testVar("flipflop"d
.idup
, 4, '_',
1752 "xyz"w
, '\U00010143', '_', "abc"d
, "__",
1753 "flip_xyz\U00010143_abc__flop"));
1758 import std
.algorithm
.comparison
: equal
;
1759 // insertInPlace interop with postblit
1777 *payload
= 0; //'destroy' it
1779 @property int getPayload(){ return *payload
; }
1780 alias getPayload
this;
1783 Int
[] arr
= [Int(1), Int(4), Int(5)];
1784 assert(arr
[0] == 1);
1785 insertInPlace(arr
, 1, Int(2), Int(3));
1786 assert(equal(arr
, [1, 2, 3, 4, 5])); //check it works with postblit
1791 import std
.exception
;
1795 a
.insertInPlace(2, 3);
1796 a
.insertInPlace(0, -1, 0);
1797 return a
== [-1, 0, 1, 2, 3];
1801 // https://issues.dlang.org/show_bug.cgi?id=6874
1805 // allocate some space
1810 a
.length
= a
.capacity
;
1813 byte[] b
= a
[$ .. $];
1814 b
.insertInPlace(0, a
);
1816 // make sure that reallocation has happened
1817 assert(GC
.addrOf(&b
[0]) == GC
.addrOf(&b
[$-1]));
1822 Returns whether the `front`s of `lhs` and `rhs` both refer to the
1823 same place in memory, making one of the arrays a slice of the other which
1824 starts at index `0`.
1827 lhs = the first array to compare
1828 rhs = the second array to compare
1830 `true` if $(D lhs.ptr == rhs.ptr), `false` otherwise.
1833 pure nothrow @nogc bool sameHead(T
)(in T
[] lhs
, in T
[] rhs
)
1835 return lhs
.ptr
== rhs
.ptr
;
1839 @safe pure nothrow unittest
1841 auto a
= [1, 2, 3, 4, 5];
1844 assert(a
.sameHead(b
));
1849 Returns whether the `back`s of `lhs` and `rhs` both refer to the
1850 same place in memory, making one of the arrays a slice of the other which
1854 lhs = the first array to compare
1855 rhs = the second array to compare
1857 `true` if both arrays are the same length and $(D lhs.ptr == rhs.ptr),
1861 pure nothrow @nogc bool sameTail(T
)(in T
[] lhs
, in T
[] rhs
)
1863 return lhs
.ptr
+ lhs
.length
== rhs
.ptr
+ rhs
.length
;
1867 @safe pure nothrow unittest
1869 auto a
= [1, 2, 3, 4, 5];
1872 assert(a
.sameTail(b
));
1875 @safe pure nothrow unittest
1877 static foreach (T
; AliasSeq
!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
1879 T a
= [1, 2, 3, 4, 5];
1885 assert(sameHead(a
, a
));
1886 assert(sameHead(a
, b
));
1887 assert(!sameHead(a
, c
));
1888 assert(sameHead(a
, d
));
1889 assert(!sameHead(a
, e
));
1891 assert(sameTail(a
, a
));
1892 assert(sameTail(a
, b
));
1893 assert(sameTail(a
, c
));
1894 assert(!sameTail(a
, d
));
1895 assert(!sameTail(a
, e
));
1897 //verifies R-value compatibilty
1898 assert(a
.sameHead(a
[0 .. 0]));
1899 assert(a
.sameTail(a
[$ .. $]));
1905 s = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
1907 n = number of times to repeat `s`
1910 An array that consists of `s` repeated `n` times. This function allocates, fills, and
1911 returns a new array.
1914 For a lazy version, refer to $(REF repeat, std,range).
1916 ElementEncodingType
!S
[] replicate(S
)(S s
, size_t n
)
1917 if (isDynamicArray
!S
)
1919 alias RetType
= ElementEncodingType
!S
[];
1921 // Optimization for return join(std.range.repeat(s, n));
1923 return RetType
.init
;
1925 return cast(RetType
) s
;
1926 auto r
= new Unqual
!(typeof(s
[0]))[n
* s
.length
];
1931 immutable len
= s
.length
, nlen
= n
* len
;
1932 for (size_t i
= 0; i
< nlen
; i
+= len
)
1934 r
[i
.. i
+ len
] = s
[];
1941 ElementType
!S
[] replicate(S
)(S s
, size_t n
)
1942 if (isInputRange
!S
&& !isDynamicArray
!S
)
1944 import std
.range
: repeat
;
1945 return join(std
.range
.repeat(s
, n
));
1953 auto s
= replicate(a
, 3);
1955 assert(s
== "abcabcabc");
1958 auto c
= replicate(b
, 3);
1960 assert(c
== [1, 2, 3, 1, 2, 3, 1, 2, 3]);
1962 auto d
= replicate(b
, 0);
1969 import std
.conv
: to
;
1971 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
, char[], wchar[], dchar[]))
1973 immutable S t
= "abc";
1975 assert(replicate(to
!S("1234"), 0) is null);
1976 assert(replicate(to
!S("1234"), 0) is null);
1977 assert(replicate(to
!S("1234"), 1) == "1234");
1978 assert(replicate(to
!S("1234"), 2) == "12341234");
1979 assert(replicate(to
!S("1"), 4) == "1111");
1980 assert(replicate(t
, 3) == "abcabcabc");
1981 assert(replicate(cast(S
) null, 4) is null);
1986 Eagerly splits `range` into an array, using `sep` as the delimiter.
1988 When no delimiter is provided, strings are split into an array of words,
1989 using whitespace as delimiter.
1990 Runs of whitespace are merged together (no empty words are produced).
1992 The `range` must be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives).
1993 The separator can be a value of the same type as the elements in `range`
1994 or it can be another forward `range`.
1997 s = the string to split by word if no separator is given
1998 range = the range to split
1999 sep = a value of the same type as the elements of `range` or another
2000 isTerminator = a predicate that splits the range when it returns `true`.
2003 An array containing the divided parts of `range` (or the words of `s`).
2006 $(REF splitter, std,algorithm,iteration) for a lazy version without allocating memory.
2008 $(REF splitter, std,regex) for a version that splits using a regular
2009 expression defined separator.
2011 S
[] split(S
)(S s
) @safe pure
2015 bool inword
= false;
2016 auto result
= appender
!(S
[]);
2018 foreach (i
, dchar c
; s
)
2020 import std
.uni
: isWhite
;
2025 put(result
, s
[istart
.. i
]);
2039 put(result
, s
[istart
.. $]);
2046 import std
.uni
: isWhite
;
2047 assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]);
2048 assert("Learning D is fun".split
!isWhite
== ["Learning", "D", "is", "fun"]);
2049 assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]);
2055 string
str = "Hello World!";
2056 assert(str.split
== ["Hello", "World!"]);
2058 string str2
= "Hello\t\tWorld\t!";
2059 assert(str2
.split
== ["Hello", "World", "!"]);
2064 import std
.conv
: to
;
2065 import std
.format
: format
;
2066 import std
.typecons
;
2068 static auto makeEntry(S
)(string l
, string
[] r
)
2069 {return tuple(l
.to
!S(), r
.to
!(S
[])());}
2071 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
,))
2075 makeEntry
!S("", []),
2076 makeEntry
!S(" ", []),
2077 makeEntry
!S("hello", ["hello"]),
2078 makeEntry
!S(" hello ", ["hello"]),
2079 makeEntry
!S(" h e l l o ", ["h", "e", "l", "l", "o"]),
2080 makeEntry
!S("peter\t\npaul\rjerry", ["peter", "paul", "jerry"]),
2081 makeEntry
!S(" \t\npeter paul\tjerry \n", ["peter", "paul", "jerry"]),
2082 makeEntry
!S("\u2000日\u202F本\u205F語\u3000", ["日", "本", "語"]),
2083 makeEntry
!S(" 哈・郎博尔德} ___一个", ["哈・郎博尔德}", "___一个"])
2085 foreach (entry
; entries
)
2086 assert(entry
[0].split() == entry
[1], format("got: %s, expected: %s.", entry
[0].split(), entry
[1]));
2089 //Just to test that an immutable is split-able
2090 immutable string s
= " \t\npeter paul\tjerry \n";
2091 assert(split(s
) == ["peter", "paul", "jerry"]);
2094 @safe unittest //purity, ctfe ...
2096 import std
.exception
;
2097 void dg() @safe pure {
2098 assert(split("hello world"c
) == ["hello"c
, "world"c
]);
2099 assert(split("hello world"w
) == ["hello"w
, "world"w
]);
2100 assert(split("hello world"d
) == ["hello"d
, "world"d
]);
2109 assert(split("hello world") == ["hello","world"]);
2110 assert(split("192.168.0.1", ".") == ["192", "168", "0", "1"]);
2112 auto a
= split([1, 2, 3, 4, 5, 1, 2, 3, 4, 5], [2, 3]);
2113 assert(a
== [[1], [4, 5, 1], [4, 5]]);
2117 auto split(Range
, Separator
)(Range range
, Separator sep
)
2118 if (isForwardRange
!Range
&& (
2119 is(typeof(ElementType
!Range
.init
== Separator
.init
)) ||
2120 is(typeof(ElementType
!Range
.init
== ElementType
!Separator
.init
)) && isForwardRange
!Separator
2123 import std
.algorithm
.iteration
: splitter
;
2124 return range
.splitter(sep
).array
;
2127 auto split(alias isTerminator
, Range
)(Range range
)
2128 if (isForwardRange
!Range
&& is(typeof(unaryFun
!isTerminator(range
.front
))))
2130 import std
.algorithm
.iteration
: splitter
;
2131 return range
.splitter
!isTerminator
.array
;
2136 import std
.algorithm
.comparison
: cmp;
2139 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
,
2140 immutable(string
), immutable(wstring
), immutable(dstring
),
2141 char[], wchar[], dchar[],
2142 const(char)[], const(wchar)[], const(dchar)[],
2143 const(char[]), immutable(char[])))
2145 S s
= to
!S(",peter,paul,jerry,");
2147 auto words
= split(s
, ",");
2148 assert(words
.length
== 5, text(words
.length
));
2149 assert(cmp(words
[0], "") == 0);
2150 assert(cmp(words
[1], "peter") == 0);
2151 assert(cmp(words
[2], "paul") == 0);
2152 assert(cmp(words
[3], "jerry") == 0);
2153 assert(cmp(words
[4], "") == 0);
2155 auto s1
= s
[0 .. s
.length
- 1]; // lop off trailing ','
2156 words
= split(s1
, ",");
2157 assert(words
.length
== 4);
2158 assert(cmp(words
[3], "jerry") == 0);
2160 auto s2
= s1
[1 .. s1
.length
]; // lop off leading ','
2161 words
= split(s2
, ",");
2162 assert(words
.length
== 3);
2163 assert(cmp(words
[0], "peter") == 0);
2165 auto s3
= to
!S(",,peter,,paul,,jerry,,");
2167 words
= split(s3
, ",,");
2168 assert(words
.length
== 5);
2169 assert(cmp(words
[0], "") == 0);
2170 assert(cmp(words
[1], "peter") == 0);
2171 assert(cmp(words
[2], "paul") == 0);
2172 assert(cmp(words
[3], "jerry") == 0);
2173 assert(cmp(words
[4], "") == 0);
2175 auto s4
= s3
[0 .. s3
.length
- 2]; // lop off trailing ',,'
2176 words
= split(s4
, ",,");
2177 assert(words
.length
== 4);
2178 assert(cmp(words
[3], "jerry") == 0);
2180 auto s5
= s4
[2 .. s4
.length
]; // lop off leading ',,'
2181 words
= split(s5
, ",,");
2182 assert(words
.length
== 3);
2183 assert(cmp(words
[0], "peter") == 0);
2188 Conservative heuristic to determine if a range can be iterated cheaply.
2189 Used by `join` in decision to do an extra iteration of the range to
2190 compute the resultant length. If iteration is not cheap then precomputing
2191 length could be more expensive than using `Appender`.
2193 For now, we only assume arrays are cheap to iterate.
2195 private enum bool hasCheapIteration(R
) = isArray
!R
;
2198 Eagerly concatenates all of the ranges in `ror` together (with the GC)
2199 into one array using `sep` as the separator if present.
2202 ror = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2204 sep = An input range, or a single element, to join the ranges on
2207 An array of elements
2210 For a lazy version, see $(REF joiner, std,algorithm,iteration)
2212 ElementEncodingType
!(ElementType
!RoR
)[] join(RoR
, R
)(RoR
ror, R sep
)
2213 if (isInputRange
!RoR
&&
2214 isInputRange
!(Unqual
!(ElementType
!RoR
)) &&
2216 (is(immutable ElementType
!(ElementType
!RoR
) == immutable ElementType
!R
) ||
2217 (isSomeChar
!(ElementType
!(ElementType
!RoR
)) && isSomeChar
!(ElementType
!R
))
2220 alias RetType
= typeof(return);
2221 alias RetTypeElement
= Unqual
!(ElementEncodingType
!RetType
);
2222 alias RoRElem
= ElementType
!RoR
;
2225 return RetType
.init
;
2227 // Constraint only requires input range for sep.
2228 // This converts sep to an array (forward range) if it isn't one,
2229 // and makes sure it has the same string encoding for string types.
2230 static if (isSomeString
!RetType
&&
2231 !is(immutable ElementEncodingType
!RetType
== immutable ElementEncodingType
!R
))
2233 import std
.conv
: to
;
2234 auto sepArr
= to
!RetType(sep
);
2236 else static if (!isArray
!R
)
2237 auto sepArr
= array(sep
);
2241 static if (hasCheapIteration
!RoR
&& (hasLength
!RoRElem || isNarrowString
!RoRElem
))
2243 import core
.internal
.lifetime
: emplaceRef
;
2244 size_t length
; // length of result array
2245 size_t rorLength
; // length of range ror
2246 foreach (r
; ror.save
)
2253 length
+= (rorLength
- 1) * sepArr
.length
;
2255 auto result
= (() @trusted => uninitializedArray
!(RetTypeElement
[])(length
))();
2257 foreach (e
; ror.front
)
2258 emplaceRef(result
[len
++], e
);
2263 emplaceRef(result
[len
++], e
);
2265 emplaceRef(result
[len
++], e
);
2267 assert(len
== result
.length
);
2268 return (() @trusted => cast(RetType
) result
)();
2272 auto result
= appender
!RetType();
2273 put(result
, ror.front
);
2275 for (; !ror.empty
; ror.popFront())
2277 put(result
, sepArr
);
2278 put(result
, ror.front
);
2284 // https://issues.dlang.org/show_bug.cgi?id=14230
2287 string
[] ary
= ["","aa","bb","cc"]; // leaded by _empty_ element
2288 assert(ary
.join(" @") == " @aa @bb @cc"); // OK in 2.067b1 and olders
2291 // https://issues.dlang.org/show_bug.cgi?id=21337
2294 import std
.algorithm
.iteration
: map
;
2311 assert([1, 2].map
!"[a]".join(new Once
) == [1, 0, 2]);
2315 ElementEncodingType
!(ElementType
!RoR
)[] join(RoR
, E
)(RoR
ror, scope E sep
)
2316 if (isInputRange
!RoR
&&
2317 isInputRange
!(Unqual
!(ElementType
!RoR
)) &&
2318 ((is(E
: ElementType
!(ElementType
!RoR
))) ||
2319 (!autodecodeStrings
&& isSomeChar
!(ElementType
!(ElementType
!RoR
)) &&
2322 alias RetType
= typeof(return);
2323 alias RetTypeElement
= Unqual
!(ElementEncodingType
!RetType
);
2324 alias RoRElem
= ElementType
!RoR
;
2327 return RetType
.init
;
2329 static if (hasCheapIteration
!RoR
&& (hasLength
!RoRElem || isNarrowString
!RoRElem
))
2331 static if (isSomeChar
!E
&& isSomeChar
!RetTypeElement
&& E
.sizeof
> RetTypeElement
.sizeof
)
2333 import std
.utf
: encode
;
2334 RetTypeElement
[4 / RetTypeElement
.sizeof
] encodeSpace
;
2335 immutable size_t sepArrLength
= encode(encodeSpace
, sep
);
2336 return join(ror, encodeSpace
[0 .. sepArrLength
]);
2340 import core
.internal
.lifetime
: emplaceRef
;
2341 import std
.format
: format
;
2344 foreach (r
; ror.save
)
2351 length
+= rorLength
- 1;
2352 auto result
= uninitializedArray
!(RetTypeElement
[])(length
);
2356 foreach (e
; ror.front
)
2357 emplaceRef(result
[len
++], e
);
2361 emplaceRef(result
[len
++], sep
);
2363 emplaceRef(result
[len
++], e
);
2365 assert(len
== result
.length
, format
!
2366 "len %s must equal result.lenght %s"(len
, result
.length
));
2367 return (() @trusted => cast(RetType
) result
)();
2372 auto result
= appender
!RetType();
2373 put(result
, ror.front
);
2375 for (; !ror.empty
; ror.popFront())
2378 put(result
, ror.front
);
2384 // https://issues.dlang.org/show_bug.cgi?id=14230
2387 string
[] ary
= ["","aa","bb","cc"];
2388 assert(ary
.join('@') == "@aa@bb@cc");
2392 ElementEncodingType
!(ElementType
!RoR
)[] join(RoR
)(RoR
ror)
2393 if (isInputRange
!RoR
&&
2394 isInputRange
!(Unqual
!(ElementType
!RoR
)))
2396 alias RetType
= typeof(return);
2397 alias ConstRetTypeElement
= ElementEncodingType
!RetType
;
2398 static if (isAssignable
!(Unqual
!ConstRetTypeElement
, ConstRetTypeElement
))
2400 alias RetTypeElement
= Unqual
!ConstRetTypeElement
;
2404 alias RetTypeElement
= ConstRetTypeElement
;
2406 alias RoRElem
= ElementType
!RoR
;
2409 return RetType
.init
;
2411 static if (hasCheapIteration
!RoR
&& (hasLength
!RoRElem || isNarrowString
!RoRElem
))
2413 import core
.internal
.lifetime
: emplaceRef
;
2415 foreach (r
; ror.save
)
2418 auto result
= (() @trusted => uninitializedArray
!(RetTypeElement
[])(length
))();
2422 emplaceRef
!RetTypeElement(result
[len
++], e
);
2423 assert(len
== result
.length
,
2424 "emplaced an unexpected number of elements");
2425 return (() @trusted => cast(RetType
) result
)();
2429 auto result
= appender
!RetType();
2430 for (; !ror.empty
; ror.popFront())
2431 put(result
, ror.front
);
2437 @safe pure nothrow unittest
2439 assert(join(["hello", "silly", "world"], " ") == "hello silly world");
2440 assert(join(["hello", "silly", "world"]) == "hellosillyworld");
2442 assert(join([[1, 2, 3], [4, 5]], [72, 73]) == [1, 2, 3, 72, 73, 4, 5]);
2443 assert(join([[1, 2, 3], [4, 5]]) == [1, 2, 3, 4, 5]);
2445 const string
[] arr
= ["apple", "banana"];
2446 assert(arr
.join(",") == "apple,banana");
2447 assert(arr
.join() == "applebanana");
2452 import std
.conv
: to
;
2453 import std
.range
.primitives
: autodecodeStrings
;
2455 static foreach (T
; AliasSeq
!(string
,wstring
,dstring
))
2457 auto arr2
= "Здравствуй Мир Unicode".to
!(T
);
2458 auto arr
= ["Здравствуй", "Мир", "Unicode"].to
!(T
[]);
2459 assert(join(arr
) == "ЗдравствуйМирUnicode");
2460 static foreach (S
; AliasSeq
!(char,wchar,dchar))
2462 auto jarr
= arr
.join(to
!S(' '));
2463 static assert(is(typeof(jarr
) == T
));
2464 assert(jarr
== arr2
);
2466 static foreach (S
; AliasSeq
!(string
,wstring
,dstring
))
2468 auto jarr
= arr
.join(to
!S(" "));
2469 static assert(is(typeof(jarr
) == T
));
2470 assert(jarr
== arr2
);
2474 static foreach (T
; AliasSeq
!(string
,wstring
,dstring
))
2476 auto arr2
= "Здравствуй\u047CМир\u047CUnicode".to
!(T
);
2477 auto arr
= ["Здравствуй", "Мир", "Unicode"].to
!(T
[]);
2478 static foreach (S
; AliasSeq
!(wchar,dchar))
2480 auto jarr
= arr
.join(to
!S('\u047C'));
2481 static assert(is(typeof(jarr
) == T
));
2482 assert(jarr
== arr2
);
2486 const string
[] arr
= ["apple", "banana"];
2487 assert(arr
.join(',') == "apple,banana");
2495 auto result
= array
.join
; // can't remove constness, so don't try
2497 static assert(is(typeof(result
) == const(A
)[]));
2502 import std
.algorithm
;
2503 import std
.conv
: to
;
2506 static foreach (R
; AliasSeq
!(string
, wstring
, dstring
))
2511 R
[] words
= [word1
, word2
, word3
];
2513 auto filteredWord1
= filter
!"true"(word1
);
2514 auto filteredLenWord1
= takeExactly(filteredWord1
, word1
.walkLength());
2515 auto filteredWord2
= filter
!"true"(word2
);
2516 auto filteredLenWord2
= takeExactly(filteredWord2
, word2
.walkLength());
2517 auto filteredWord3
= filter
!"true"(word3
);
2518 auto filteredLenWord3
= takeExactly(filteredWord3
, word3
.walkLength());
2519 auto filteredWordsArr
= [filteredWord1
, filteredWord2
, filteredWord3
];
2520 auto filteredLenWordsArr
= [filteredLenWord1
, filteredLenWord2
, filteredLenWord3
];
2521 auto filteredWords
= filter
!"true"(filteredWordsArr
);
2523 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
))
2525 assert(join(filteredWords
, to
!S(", ")) == "日本語, paul, jerry");
2526 assert(join(filteredWords
, to
!(ElementType
!S
)(',')) == "日本語,paul,jerry");
2527 assert(join(filteredWordsArr
, to
!(ElementType
!(S
))(',')) == "日本語,paul,jerry");
2528 assert(join(filteredWordsArr
, to
!S(", ")) == "日本語, paul, jerry");
2529 assert(join(filteredWordsArr
, to
!(ElementType
!(S
))(',')) == "日本語,paul,jerry");
2530 assert(join(filteredLenWordsArr
, to
!S(", ")) == "日本語, paul, jerry");
2531 assert(join(filter
!"true"(words
), to
!S(", ")) == "日本語, paul, jerry");
2532 assert(join(words
, to
!S(", ")) == "日本語, paul, jerry");
2534 assert(join(filteredWords
, to
!S("")) == "日本語pauljerry");
2535 assert(join(filteredWordsArr
, to
!S("")) == "日本語pauljerry");
2536 assert(join(filteredLenWordsArr
, to
!S("")) == "日本語pauljerry");
2537 assert(join(filter
!"true"(words
), to
!S("")) == "日本語pauljerry");
2538 assert(join(words
, to
!S("")) == "日本語pauljerry");
2540 assert(join(filter
!"true"([word1
]), to
!S(", ")) == "日本語");
2541 assert(join([filteredWord1
], to
!S(", ")) == "日本語");
2542 assert(join([filteredLenWord1
], to
!S(", ")) == "日本語");
2543 assert(join(filter
!"true"([filteredWord1
]), to
!S(", ")) == "日本語");
2544 assert(join([word1
], to
!S(", ")) == "日本語");
2546 assert(join(filteredWords
, to
!S(word1
)) == "日本語日本語paul日本語jerry");
2547 assert(join(filteredWordsArr
, to
!S(word1
)) == "日本語日本語paul日本語jerry");
2548 assert(join(filteredLenWordsArr
, to
!S(word1
)) == "日本語日本語paul日本語jerry");
2549 assert(join(filter
!"true"(words
), to
!S(word1
)) == "日本語日本語paul日本語jerry");
2550 assert(join(words
, to
!S(word1
)) == "日本語日本語paul日本語jerry");
2552 auto filterComma
= filter
!"true"(to
!S(", "));
2553 assert(join(filteredWords
, filterComma
) == "日本語, paul, jerry");
2554 assert(join(filteredWordsArr
, filterComma
) == "日本語, paul, jerry");
2555 assert(join(filteredLenWordsArr
, filterComma
) == "日本語, paul, jerry");
2556 assert(join(filter
!"true"(words
), filterComma
) == "日本語, paul, jerry");
2557 assert(join(words
, filterComma
) == "日本語, paul, jerry");
2560 assert(join(filteredWords
) == "日本語pauljerry");
2561 assert(join(filteredWordsArr
) == "日本語pauljerry");
2562 assert(join(filteredLenWordsArr
) == "日本語pauljerry");
2563 assert(join(filter
!"true"(words
)) == "日本語pauljerry");
2564 assert(join(words
) == "日本語pauljerry");
2566 assert(join(filteredWords
, filter
!"true"(", ")) == "日本語, paul, jerry");
2567 assert(join(filteredWordsArr
, filter
!"true"(", ")) == "日本語, paul, jerry");
2568 assert(join(filteredLenWordsArr
, filter
!"true"(", ")) == "日本語, paul, jerry");
2569 assert(join(filter
!"true"(words
), filter
!"true"(", ")) == "日本語, paul, jerry");
2570 assert(join(words
, filter
!"true"(", ")) == "日本語, paul, jerry");
2572 assert(join(filter
!"true"(cast(typeof(filteredWordsArr
))[]), ", ").empty
);
2573 assert(join(cast(typeof(filteredWordsArr
))[], ", ").empty
);
2574 assert(join(cast(typeof(filteredLenWordsArr
))[], ", ").empty
);
2575 assert(join(filter
!"true"(cast(R
[])[]), ", ").empty
);
2576 assert(join(cast(R
[])[], ", ").empty
);
2578 assert(join(filter
!"true"(cast(typeof(filteredWordsArr
))[])).empty
);
2579 assert(join(cast(typeof(filteredWordsArr
))[]).empty
);
2580 assert(join(cast(typeof(filteredLenWordsArr
))[]).empty
);
2582 assert(join(filter
!"true"(cast(R
[])[])).empty
);
2583 assert(join(cast(R
[])[]).empty
);
2586 assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2587 assert(join([[1, 2], [41, 42]], cast(int[])[]) == [1, 2, 41, 42]);
2588 assert(join([[1, 2]], [5, 6]) == [1, 2]);
2589 assert(join(cast(int[][])[], [5, 6]).empty
);
2591 assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]);
2592 assert(join(cast(int[][])[]).empty
);
2594 alias f
= filter
!"true";
2595 assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2596 assert(join(f([[1, 2], [41, 42]]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
2597 assert(join([f([1, 2]), f([41, 42])], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2598 assert(join(f([f([1, 2]), f([41, 42])]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
2599 assert(join([[1, 2], [41, 42]], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2600 assert(join(f([[1, 2], [41, 42]]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2601 assert(join([f([1, 2]), f([41, 42])], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2602 assert(join(f([f([1, 2]), f([41, 42])]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2605 // https://issues.dlang.org/show_bug.cgi?id=10683
2608 import std
.range
: join
;
2609 import std
.typecons
: tuple
;
2610 assert([[tuple(1)]].join
== [tuple(1)]);
2611 assert([[tuple("x")]].join
== [tuple("x")]);
2614 // https://issues.dlang.org/show_bug.cgi?id=13877
2617 // Test that the range is iterated only once.
2618 import std
.algorithm
.iteration
: map
;
2620 auto j1
= [1, 2, 3].map
!(_
=> [c
++]).join
;
2622 assert(j1
== [0, 1, 2]);
2625 auto j2
= [1, 2, 3].map
!(_
=> [c
++]).join(9);
2627 assert(j2
== [0, 9, 1, 9, 2]);
2630 auto j3
= [1, 2, 3].map
!(_
=> [c
++]).join([9]);
2632 assert(j3
== [0, 9, 1, 9, 2]);
2637 Replace occurrences of `from` with `to` in `subject` in a new array.
2640 subject = the array to scan
2641 from = the item to replace
2642 to = the item to replace all instances of `from` with
2645 A new array without changing the contents of `subject`, or the original
2646 array if no match is found.
2649 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2651 E
[] replace(E
, R1
, R2
)(E
[] subject
, R1 from
, R2 to
)
2652 if ((isForwardRange
!R1
&& isForwardRange
!R2
&& (hasLength
!R2 || isSomeString
!R2
)) ||
2653 is(Unqual
!E
: Unqual
!R1
))
2656 return replace(subject
, from
, to
, changed
);
2662 assert("Hello Wörld".replace("o Wö", "o Wo") == "Hello World");
2663 assert("Hello Wörld".replace("l", "h") == "Hehho Wörhd");
2668 assert([1, 2, 3, 4, 2].replace([2], [5]) == [1, 5, 3, 4, 5]);
2669 assert([3, 3, 3].replace([3], [0]) == [0, 0, 0]);
2670 assert([3, 3, 4, 3].replace([3, 3], [1, 1, 1]) == [1, 1, 1, 4, 3]);
2673 // https://issues.dlang.org/show_bug.cgi?id=18215
2676 auto arr
= ["aaa.dd", "b"];
2677 arr
= arr
.replace("aaa.dd", ".");
2678 assert(arr
== [".", "b"]);
2680 arr
= ["_", "_", "aaa.dd", "b", "c", "aaa.dd", "e"];
2681 arr
= arr
.replace("aaa.dd", ".");
2682 assert(arr
== ["_", "_", ".", "b", "c", ".", "e"]);
2685 // https://issues.dlang.org/show_bug.cgi?id=18215
2688 assert([[0], [1, 2], [0], [3]].replace([0], [4]) == [[4], [1, 2], [4], [3]]);
2689 assert([[0], [1, 2], [0], [3], [1, 2]]
2690 .replace([1, 2], [0]) == [[0], [0], [0], [3], [0]]);
2691 assert([[0], [1, 2], [0], [3], [1, 2], [0], [1, 2]]
2692 .replace([[0], [1, 2]], [[4]]) == [[4], [0], [3], [1, 2], [4]]);
2695 // https://issues.dlang.org/show_bug.cgi?id=10930
2698 assert([0, 1, 2].replace(1, 4) == [0, 4, 2]);
2699 assert("äbö".replace('ä', 'a') == "abö");
2706 assert(replace(arr
, 1, 2) == []);
2710 Replace occurrences of `from` with `to` in `subject` in a new array.
2711 `changed` counts how many replacements took place.
2714 subject = the array to scan
2715 from = the item to replace
2716 to = the item to replace all instances of `from` with
2717 changed = the number of replacements
2720 A new array without changing the contents of `subject`, or the original
2721 array if no match is found.
2723 E
[] replace(E
, R1
, R2
)(E
[] subject
, R1 from
, R2 to
, ref size_t changed
)
2724 if ((isForwardRange
!R1
&& isForwardRange
!R2
&& (hasLength
!R2 || isSomeString
!R2
)) ||
2725 is(Unqual
!E
: Unqual
!R1
))
2727 import std
.algorithm
.searching
: find
;
2728 import std
.range
: dropOne
;
2730 static if (isInputRange
!R1
)
2732 if (from
.empty
) return subject
;
2733 alias rSave
= a
=> a
.save
;
2737 alias rSave
= a
=> a
;
2740 auto balance
= find(subject
, rSave(from
));
2744 auto app
= appender
!(E
[])();
2745 app
.put(subject
[0 .. subject
.length
- balance
.length
]);
2748 // replacing an element in an array is different to a range replacement
2749 static if (is(Unqual
!E
: Unqual
!R1
))
2750 replaceInto(app
, balance
.dropOne
, from
, to
, changed
);
2752 replaceInto(app
, balance
[from
.length
.. $], from
, to
, changed
);
2761 assert("Hello Wörld".replace("o Wö", "o Wo", changed
) == "Hello World");
2762 assert(changed
== 1);
2765 assert("Hello Wörld".replace("l", "h", changed
) == "Hehho Wörhd");
2766 import std
.stdio
: writeln
;
2768 assert(changed
== 3);
2772 Replace occurrences of `from` with `to` in `subject` and output the result into
2776 sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
2777 subject = the array to scan
2778 from = the item to replace
2779 to = the item to replace all instances of `from` with
2782 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2784 void replaceInto(E
, Sink
, R1
, R2
)(Sink sink
, E
[] subject
, R1 from
, R2 to
)
2785 if (isOutputRange
!(Sink
, E
) &&
2786 ((isForwardRange
!R1
&& isForwardRange
!R2
&& (hasLength
!R2 || isSomeString
!R2
)) ||
2787 is(Unqual
!E
: Unqual
!R1
)))
2790 replaceInto(sink
, subject
, from
, to
, changed
);
2796 auto arr
= [1, 2, 3, 4, 5];
2799 auto sink
= appender
!(int[])();
2801 replaceInto(sink
, arr
, from
, to
);
2803 assert(sink
.data
== [1, 4, 6, 4, 5]);
2809 auto sink
= appender
!(int[])();
2811 replaceInto(sink
, arr
, 1, 2);
2812 assert(sink
.data
== []);
2817 import std
.algorithm
.comparison
: cmp;
2818 import std
.conv
: to
;
2820 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
, char[], wchar[], dchar[]))
2822 static foreach (T
; AliasSeq
!(string
, wstring
, dstring
, char[], wchar[], dchar[]))
2824 auto s
= to
!S("This is a foo foo list");
2825 auto from
= to
!T("foo");
2826 auto into = to
!S("silly");
2830 r
= replace(s
, from
, into);
2831 i
= cmp(r
, "This is a silly silly list");
2834 r
= replace(s
, to
!S(""), into);
2835 i
= cmp(r
, "This is a foo foo list");
2838 assert(replace(r
, to
!S("won't find this"), to
!S("whatever")) is r
);
2842 immutable s
= "This is a foo foo list";
2843 assert(replace(s
, "foo", "silly") == "This is a silly silly list");
2848 import std
.algorithm
.searching
: skipOver
;
2849 import std
.conv
: to
;
2851 struct CheckOutput(C
)
2854 this(C
[] arr
){ desired
= arr
; }
2855 void put(C
[] part
){ assert(skipOver(desired
, part
)); }
2857 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
, char[], wchar[], dchar[]))
2859 alias Char
= ElementEncodingType
!S
;
2860 S s
= to
!S("yet another dummy text, yet another ...");
2861 S from
= to
!S("yet another");
2862 S
into = to
!S("some");
2863 replaceInto(CheckOutput
!(Char
)(to
!S("some dummy text, some ..."))
2868 // https://issues.dlang.org/show_bug.cgi?id=10930
2871 auto sink
= appender
!(int[])();
2872 replaceInto(sink
, [0, 1, 2], 1, 5);
2873 assert(sink
.data
== [0, 5, 2]);
2875 auto sink2
= appender
!(dchar[])();
2876 replaceInto(sink2
, "äbö", 'ä', 'a');
2877 assert(sink2
.data
== "abö");
2881 Replace occurrences of `from` with `to` in `subject` and output the result into
2882 `sink`. `changed` counts how many replacements took place.
2885 sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
2886 subject = the array to scan
2887 from = the item to replace
2888 to = the item to replace all instances of `from` with
2889 changed = the number of replacements
2891 void replaceInto(E
, Sink
, R1
, R2
)(Sink sink
, E
[] subject
, R1 from
, R2 to
, ref size_t changed
)
2892 if (isOutputRange
!(Sink
, E
) &&
2893 ((isForwardRange
!R1
&& isForwardRange
!R2
&& (hasLength
!R2 || isSomeString
!R2
)) ||
2894 is(Unqual
!E
: Unqual
!R1
)))
2896 import std
.algorithm
.searching
: find
;
2897 import std
.range
: dropOne
;
2899 static if (isInputRange
!R1
)
2906 alias rSave
= a
=> a
.save
;
2910 alias rSave
= a
=> a
;
2914 auto balance
= find(subject
, rSave(from
));
2920 sink
.put(subject
[0 .. subject
.length
- balance
.length
]);
2921 sink
.put(rSave(to
));
2923 // replacing an element in an array is different to a range replacement
2924 static if (is(Unqual
!E
: Unqual
!R1
))
2925 subject
= balance
.dropOne
;
2927 subject
= balance
[from
.length
.. $];
2934 auto arr
= [1, 2, 3, 4, 5];
2937 auto sink
= appender
!(int[])();
2940 replaceInto(sink
, arr
, from
, to
, changed
);
2942 assert(sink
.data
== [1, 4, 6, 4, 5]);
2943 assert(changed
== 1);
2947 Replaces elements from `array` with indices ranging from `from`
2948 (inclusive) to `to` (exclusive) with the range `stuff`.
2951 subject = the array to scan
2952 from = the starting index
2953 to = the ending index
2954 stuff = the items to replace in-between `from` and `to`
2957 A new array without changing the contents of `subject`.
2960 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2962 T
[] replace(T
, Range
)(T
[] subject
, size_t from
, size_t to
, Range stuff
)
2963 if (isInputRange
!Range
&&
2964 (is(ElementType
!Range
: T
) ||
2965 isSomeString
!(T
[]) && is(ElementType
!Range
: dchar)))
2967 static if (hasLength
!Range
&& is(ElementEncodingType
!Range
: T
))
2969 import std
.algorithm
.mutation
: copy
;
2970 assert(from
<= to
, "from must be before or equal to to");
2971 immutable sliceLen
= to
- from
;
2972 auto retval
= new Unqual
!(T
)[](subject
.length
- sliceLen
+ stuff
.length
);
2973 retval
[0 .. from
] = subject
[0 .. from
];
2976 copy(stuff
, retval
[from
.. from
+ stuff
.length
]);
2978 retval
[from
+ stuff
.length
.. $] = subject
[to
.. $];
2979 static if (is(T
== const) ||
is(T
== immutable))
2981 return () @trusted { return cast(T
[]) retval
; } ();
2985 return cast(T
[]) retval
;
2990 auto app
= appender
!(T
[])();
2991 app
.put(subject
[0 .. from
]);
2993 app
.put(subject
[to
.. $]);
3001 auto a
= [ 1, 2, 3, 4 ];
3002 auto b
= a
.replace(1, 3, [ 9, 9, 9 ]);
3003 assert(a
== [ 1, 2, 3, 4 ]);
3004 assert(b
== [ 1, 9, 9, 9, 4 ]);
3009 import core
.exception
;
3010 import std
.algorithm
.iteration
: filter
;
3011 import std
.conv
: to
;
3012 import std
.exception
;
3015 auto a
= [ 1, 2, 3, 4 ];
3016 assert(replace(a
, 0, 0, [5, 6, 7]) == [5, 6, 7, 1, 2, 3, 4]);
3017 assert(replace(a
, 0, 2, cast(int[])[]) == [3, 4]);
3018 assert(replace(a
, 0, 4, [5, 6, 7]) == [5, 6, 7]);
3019 assert(replace(a
, 0, 2, [5, 6, 7]) == [5, 6, 7, 3, 4]);
3020 assert(replace(a
, 2, 4, [5, 6, 7]) == [1, 2, 5, 6, 7]);
3022 assert(replace(a
, 0, 0, filter
!"true"([5, 6, 7])) == [5, 6, 7, 1, 2, 3, 4]);
3023 assert(replace(a
, 0, 2, filter
!"true"(cast(int[])[])) == [3, 4]);
3024 assert(replace(a
, 0, 4, filter
!"true"([5, 6, 7])) == [5, 6, 7]);
3025 assert(replace(a
, 0, 2, filter
!"true"([5, 6, 7])) == [5, 6, 7, 3, 4]);
3026 assert(replace(a
, 2, 4, filter
!"true"([5, 6, 7])) == [1, 2, 5, 6, 7]);
3027 assert(a
== [ 1, 2, 3, 4 ]);
3029 void testStr(T
, U
)(string file
= __FILE__
, size_t line
= __LINE__
)
3032 auto l
= to
!T("hello");
3033 auto r
= to
!U(" world");
3035 enforce(replace(l
, 0, 0, r
) == " worldhello",
3036 new AssertError("testStr failure 1", file
, line
));
3037 enforce(replace(l
, 0, 3, r
) == " worldlo",
3038 new AssertError("testStr failure 2", file
, line
));
3039 enforce(replace(l
, 3, l
.length
, r
) == "hel world",
3040 new AssertError("testStr failure 3", file
, line
));
3041 enforce(replace(l
, 0, l
.length
, r
) == " world",
3042 new AssertError("testStr failure 4", file
, line
));
3043 enforce(replace(l
, l
.length
, l
.length
, r
) == "hello world",
3044 new AssertError("testStr failure 5", file
, line
));
3047 testStr
!(string
, string
)();
3048 testStr
!(string
, wstring
)();
3049 testStr
!(string
, dstring
)();
3050 testStr
!(wstring
, string
)();
3051 testStr
!(wstring
, wstring
)();
3052 testStr
!(wstring
, dstring
)();
3053 testStr
!(dstring
, string
)();
3054 testStr
!(dstring
, wstring
)();
3055 testStr
!(dstring
, dstring
)();
3057 enum s
= "0123456789";
3058 enum w
= "⁰¹²³⁴⁵⁶⁷⁸⁹"w
;
3059 enum d
= "⁰¹²³⁴⁵⁶⁷⁸⁹"d
;
3061 assert(replace(s
, 0, 0, "***") == "***0123456789");
3062 assert(replace(s
, 10, 10, "***") == "0123456789***");
3063 assert(replace(s
, 3, 8, "1012") == "012101289");
3064 assert(replace(s
, 0, 5, "43210") == "4321056789");
3065 assert(replace(s
, 5, 10, "43210") == "0123443210");
3067 assert(replace(w
, 0, 0, "***"w
) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"w
);
3068 assert(replace(w
, 10, 10, "***"w
) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"w
);
3069 assert(replace(w
, 3, 8, "¹⁰¹²"w
) == "⁰¹²¹⁰¹²⁸⁹"w
);
3070 assert(replace(w
, 0, 5, "⁴³²¹⁰"w
) == "⁴³²¹⁰⁵⁶⁷⁸⁹"w
);
3071 assert(replace(w
, 5, 10, "⁴³²¹⁰"w
) == "⁰¹²³⁴⁴³²¹⁰"w
);
3073 assert(replace(d
, 0, 0, "***"d
) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"d
);
3074 assert(replace(d
, 10, 10, "***"d
) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"d
);
3075 assert(replace(d
, 3, 8, "¹⁰¹²"d
) == "⁰¹²¹⁰¹²⁸⁹"d
);
3076 assert(replace(d
, 0, 5, "⁴³²¹⁰"d
) == "⁴³²¹⁰⁵⁶⁷⁸⁹"d
);
3077 assert(replace(d
, 5, 10, "⁴³²¹⁰"d
) == "⁰¹²³⁴⁴³²¹⁰"d
);
3080 // https://issues.dlang.org/show_bug.cgi?id=18166
3083 auto str = replace("aaaaa"d
, 1, 4, "***"d
);
3084 assert(str == "a***a");
3088 Replaces elements from `array` with indices ranging from `from`
3089 (inclusive) to `to` (exclusive) with the range `stuff`. Expands or
3090 shrinks the array as needed.
3093 array = the array to scan
3094 from = the starting index
3095 to = the ending index
3096 stuff = the items to replace in-between `from` and `to`
3098 void replaceInPlace(T
, Range
)(ref T
[] array
, size_t from
, size_t to
, Range stuff
)
3099 if (is(typeof(replace(array
, from
, to
, stuff
))))
3101 static if (isDynamicArray
!Range
&&
3102 is(Unqual
!(ElementEncodingType
!Range
) == T
) &&
3103 !isNarrowString
!(T
[]))
3105 // optimized for homogeneous arrays that can be overwritten.
3106 import std
.algorithm
.mutation
: remove
;
3107 import std
.typecons
: tuple
;
3109 if (overlap(array
, stuff
).length
)
3111 // use slower/conservative method
3112 array
= array
[0 .. from
] ~ stuff
~ array
[to
.. $];
3114 else if (stuff
.length
<= to
- from
)
3116 // replacement reduces length
3117 immutable stuffEnd
= from
+ stuff
.length
;
3118 array
[from
.. stuffEnd
] = stuff
[];
3120 array
= remove(array
, tuple(stuffEnd
, to
));
3124 // replacement increases length
3125 // @@@TODO@@@: optimize this
3126 immutable replaceLen
= to
- from
;
3127 array
[from
.. to
] = stuff
[0 .. replaceLen
];
3128 insertInPlace(array
, to
, stuff
[replaceLen
.. $]);
3133 // default implementation, just do what replace does.
3134 array
= replace(array
, from
, to
, stuff
);
3141 int[] a
= [1, 4, 5];
3142 replaceInPlace(a
, 1u, 2u, [2, 3, 4]);
3143 assert(a
== [1, 2, 3, 4, 5]);
3144 replaceInPlace(a
, 1u, 2u, cast(int[])[]);
3145 assert(a
== [1, 3, 4, 5]);
3146 replaceInPlace(a
, 1u, 3u, a
[2 .. 4]);
3147 assert(a
== [1, 4, 5, 5]);
3150 // https://issues.dlang.org/show_bug.cgi?id=12889
3153 int[1][] arr
= [[0], [1], [2], [3], [4], [5], [6]];
3154 int[1][] stuff
= [[0], [1]];
3155 replaceInPlace(arr
, 4, 6, stuff
);
3156 assert(arr
== [[0], [1], [2], [3], [0], [1], [6]]);
3161 // https://issues.dlang.org/show_bug.cgi?id=14925
3162 char[] a
= "mon texte 1".dup
;
3163 char[] b
= "abc".dup
;
3164 replaceInPlace(a
, 4, 9, b
);
3165 assert(a
== "mon abc 1");
3167 // ensure we can replace in place with different encodings
3168 string unicoded
= "\U00010437";
3169 string unicodedLong
= "\U00010437aaaaa";
3170 string base
= "abcXXXxyz";
3171 string result
= "abc\U00010437xyz";
3172 string resultLong
= "abc\U00010437aaaaaxyz";
3173 size_t repstart
= 3;
3174 size_t repend
= 3 + 3;
3176 void testStringReplaceInPlace(T
, U
)()
3178 import std
.algorithm
.comparison
: equal
;
3180 auto a
= unicoded
.to
!(U
[]);
3181 auto b
= unicodedLong
.to
!(U
[]);
3183 auto test = base
.to
!(T
[]);
3185 test.replaceInPlace(repstart
, repend
, a
);
3186 assert(equal(test, result
), "Failed for types " ~ T
.stringof
~ " and " ~ U
.stringof
);
3188 test = base
.to
!(T
[]);
3190 test.replaceInPlace(repstart
, repend
, b
);
3191 assert(equal(test, resultLong
), "Failed for types " ~ T
.stringof
~ " and " ~ U
.stringof
);
3194 import std
.meta
: AliasSeq
;
3195 alias allChars
= AliasSeq
!(char, immutable(char), const(char),
3196 wchar, immutable(wchar), const(wchar),
3197 dchar, immutable(dchar), const(dchar));
3198 foreach (T
; allChars
)
3199 foreach (U
; allChars
)
3200 testStringReplaceInPlace
!(T
, U
)();
3202 void testInout(inout(int)[] a
)
3204 // will be transferred to the 'replace' function
3205 replaceInPlace(a
, 1, 2, [1,2,3]);
3211 // the constraint for the first overload used to match this, which wouldn't compile.
3212 import std
.algorithm
.comparison
: equal
;
3213 long[] a
= [1L, 2, 3];
3214 int[] b
= [4, 5, 6];
3215 a
.replaceInPlace(1, 2, b
);
3216 assert(equal(a
, [1L, 4, 5, 6, 3]));
3221 import core
.exception
;
3222 import std
.algorithm
.comparison
: equal
;
3223 import std
.algorithm
.iteration
: filter
;
3224 import std
.conv
: to
;
3225 import std
.exception
;
3228 bool test(T
, U
, V
)(T orig
, size_t from
, size_t to
, U toReplace
, V result
)
3231 static if (is(T
== typeof(T
.init
.dup
)))
3236 a
.replaceInPlace(from
, to
, toReplace
);
3237 if (!equal(a
, result
))
3241 static if (isInputRange
!U
)
3243 orig
.replaceInPlace(from
, to
, filter
!"true"(toReplace
));
3244 return equal(orig
, result
);
3250 assert(test([1, 2, 3, 4], 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4]));
3251 assert(test([1, 2, 3, 4], 0, 2, cast(int[])[], [3, 4]));
3252 assert(test([1, 2, 3, 4], 0, 4, [5, 6, 7], [5, 6, 7]));
3253 assert(test([1, 2, 3, 4], 0, 2, [5, 6, 7], [5, 6, 7, 3, 4]));
3254 assert(test([1, 2, 3, 4], 2, 4, [5, 6, 7], [1, 2, 5, 6, 7]));
3256 assert(test([1, 2, 3, 4], 0, 0, filter
!"true"([5, 6, 7]), [5, 6, 7, 1, 2, 3, 4]));
3257 assert(test([1, 2, 3, 4], 0, 2, filter
!"true"(cast(int[])[]), [3, 4]));
3258 assert(test([1, 2, 3, 4], 0, 4, filter
!"true"([5, 6, 7]), [5, 6, 7]));
3259 assert(test([1, 2, 3, 4], 0, 2, filter
!"true"([5, 6, 7]), [5, 6, 7, 3, 4]));
3260 assert(test([1, 2, 3, 4], 2, 4, filter
!"true"([5, 6, 7]), [1, 2, 5, 6, 7]));
3262 void testStr(T
, U
)(string file
= __FILE__
, size_t line
= __LINE__
)
3265 auto l
= to
!T("hello");
3266 auto r
= to
!U(" world");
3268 enforce(test(l
, 0, 0, r
, " worldhello"),
3269 new AssertError("testStr failure 1", file
, line
));
3270 enforce(test(l
, 0, 3, r
, " worldlo"),
3271 new AssertError("testStr failure 2", file
, line
));
3272 enforce(test(l
, 3, l
.length
, r
, "hel world"),
3273 new AssertError("testStr failure 3", file
, line
));
3274 enforce(test(l
, 0, l
.length
, r
, " world"),
3275 new AssertError("testStr failure 4", file
, line
));
3276 enforce(test(l
, l
.length
, l
.length
, r
, "hello world"),
3277 new AssertError("testStr failure 5", file
, line
));
3280 testStr
!(string
, string
)();
3281 testStr
!(string
, wstring
)();
3282 testStr
!(string
, dstring
)();
3283 testStr
!(wstring
, string
)();
3284 testStr
!(wstring
, wstring
)();
3285 testStr
!(wstring
, dstring
)();
3286 testStr
!(dstring
, string
)();
3287 testStr
!(dstring
, wstring
)();
3288 testStr
!(dstring
, dstring
)();
3292 Replaces the first occurrence of `from` with `to` in `subject`.
3295 subject = the array to scan
3296 from = the item to replace
3297 to = the item to replace `from` with
3300 A new array without changing the contents of `subject`, or the original
3301 array if no match is found.
3303 E
[] replaceFirst(E
, R1
, R2
)(E
[] subject
, R1 from
, R2 to
)
3304 if (isDynamicArray
!(E
[]) &&
3305 isForwardRange
!R1
&& is(typeof(appender
!(E
[])().put(from
[0 .. 1]))) &&
3306 isForwardRange
!R2
&& is(typeof(appender
!(E
[])().put(to
[0 .. 1]))))
3308 if (from
.empty
) return subject
;
3309 static if (isSomeString
!(E
[]))
3311 import std
.string
: indexOf
;
3312 immutable idx
= subject
.indexOf(from
);
3316 import std
.algorithm
.searching
: countUntil
;
3317 immutable idx
= subject
.countUntil(from
);
3322 auto app
= appender
!(E
[])();
3323 app
.put(subject
[0 .. idx
]);
3326 static if (isSomeString
!(E
[]) && isSomeString
!R1
)
3328 import std
.utf
: codeLength
;
3329 immutable fromLength
= codeLength
!(Unqual
!E
, R1
)(from
);
3332 immutable fromLength
= from
.length
;
3334 app
.put(subject
[idx
+ fromLength
.. $]);
3342 auto a
= [1, 2, 2, 3, 4, 5];
3343 auto b
= a
.replaceFirst([2], [1337]);
3344 assert(b
== [1, 1337, 2, 3, 4, 5]);
3346 auto s
= "This is a foo foo list";
3347 auto r
= s
.replaceFirst("foo", "silly");
3348 assert(r
== "This is a silly foo list");
3353 import std
.algorithm
.comparison
: cmp;
3354 import std
.conv
: to
;
3356 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
, char[], wchar[], dchar[],
3357 const(char[]), immutable(char[])))
3359 static foreach (T
; AliasSeq
!(string
, wstring
, dstring
, char[], wchar[], dchar[],
3360 const(char[]), immutable(char[])))
3362 auto s
= to
!S("This is a foo foo list");
3363 auto s2
= to
!S("Thüs is a ßöö foo list");
3364 auto from
= to
!T("foo");
3365 auto from2
= to
!T("ßöö");
3366 auto into = to
!T("silly");
3367 auto into2
= to
!T("sälly");
3369 S r1
= replaceFirst(s
, from
, into);
3370 assert(cmp(r1
, "This is a silly foo list") == 0);
3372 S r11
= replaceFirst(s2
, from2
, into2
);
3373 assert(cmp(r11
, "Thüs is a sälly foo list") == 0,
3374 to
!string(r11
) ~ " : " ~ S
.stringof
~ " " ~ T
.stringof
);
3376 S r2
= replaceFirst(r1
, from
, into);
3377 assert(cmp(r2
, "This is a silly silly list") == 0);
3379 S r3
= replaceFirst(s
, to
!T(""), into);
3380 assert(cmp(r3
, "This is a foo foo list") == 0);
3382 assert(replaceFirst(r3
, to
!T("won't find"), to
!T("whatever")) is r3
);
3387 // https://issues.dlang.org/show_bug.cgi?id=8187
3390 auto res
= ["a", "a"];
3391 assert(replace(res
, "a", "b") == ["b", "b"]);
3392 assert(replaceFirst(res
, "a", "b") == ["b", "a"]);
3396 Replaces the last occurrence of `from` with `to` in `subject`.
3399 subject = the array to scan
3400 from = the item to replace
3401 to = the item to replace `from` with
3404 A new array without changing the contents of `subject`, or the original
3405 array if no match is found.
3407 E
[] replaceLast(E
, R1
, R2
)(E
[] subject
, R1 from
, R2 to
)
3408 if (isDynamicArray
!(E
[]) &&
3409 isForwardRange
!R1
&& is(typeof(appender
!(E
[])().put(from
[0 .. 1]))) &&
3410 isForwardRange
!R2
&& is(typeof(appender
!(E
[])().put(to
[0 .. 1]))))
3412 import std
.range
: retro
;
3413 if (from
.empty
) return subject
;
3414 static if (isSomeString
!(E
[]))
3416 import std
.string
: lastIndexOf
;
3417 auto idx
= subject
.lastIndexOf(from
);
3421 import std
.algorithm
.searching
: countUntil
;
3422 auto idx
= retro(subject
).countUntil(retro(from
));
3428 static if (isSomeString
!(E
[]) && isSomeString
!R1
)
3430 import std
.utf
: codeLength
;
3431 auto fromLength
= codeLength
!(Unqual
!E
, R1
)(from
);
3434 auto fromLength
= from
.length
;
3436 auto app
= appender
!(E
[])();
3437 static if (isSomeString
!(E
[]))
3438 app
.put(subject
[0 .. idx
]);
3440 app
.put(subject
[0 .. $ - idx
- fromLength
]);
3444 static if (isSomeString
!(E
[]))
3445 app
.put(subject
[idx
+fromLength
.. $]);
3447 app
.put(subject
[$ - idx
.. $]);
3455 auto a
= [1, 2, 2, 3, 4, 5];
3456 auto b
= a
.replaceLast([2], [1337]);
3457 assert(b
== [1, 2, 1337, 3, 4, 5]);
3459 auto s
= "This is a foo foo list";
3460 auto r
= s
.replaceLast("foo", "silly");
3461 assert(r
== "This is a foo silly list", r
);
3466 import std
.algorithm
.comparison
: cmp;
3467 import std
.conv
: to
;
3469 static foreach (S
; AliasSeq
!(string
, wstring
, dstring
, char[], wchar[], dchar[],
3470 const(char[]), immutable(char[])))
3472 static foreach (T
; AliasSeq
!(string
, wstring
, dstring
, char[], wchar[], dchar[],
3473 const(char[]), immutable(char[])))
3475 auto s
= to
!S("This is a foo foo list");
3476 auto s2
= to
!S("Thüs is a ßöö ßöö list");
3477 auto from
= to
!T("foo");
3478 auto from2
= to
!T("ßöö");
3479 auto into = to
!T("silly");
3480 auto into2
= to
!T("sälly");
3482 S r1
= replaceLast(s
, from
, into);
3483 assert(cmp(r1
, "This is a foo silly list") == 0, to
!string(r1
));
3485 S r11
= replaceLast(s2
, from2
, into2
);
3486 assert(cmp(r11
, "Thüs is a ßöö sälly list") == 0,
3487 to
!string(r11
) ~ " : " ~ S
.stringof
~ " " ~ T
.stringof
);
3489 S r2
= replaceLast(r1
, from
, into);
3490 assert(cmp(r2
, "This is a silly silly list") == 0);
3492 S r3
= replaceLast(s
, to
!T(""), into);
3493 assert(cmp(r3
, "This is a foo foo list") == 0);
3495 assert(replaceLast(r3
, to
!T("won't find"), to
!T("whatever")) is r3
);
3501 Creates a new array such that the items in `slice` are replaced with the
3502 items in `replacement`. `slice` and `replacement` do not need to be the
3503 same length. The result will grow or shrink based on the items given.
3506 s = the base of the new array
3507 slice = the slice of `s` to be replaced
3508 replacement = the items to replace `slice` with
3511 A new array that is `s` with `slice` replaced by
3515 $(REF substitute, std,algorithm,iteration) for a lazy replace.
3517 inout(T
)[] replaceSlice(T
)(inout(T
)[] s
, in T
[] slice
, in T
[] replacement
)
3520 // Verify that slice[] really is a slice of s[]
3521 assert(overlap(s
, slice
) is slice
, "slice[] is not a subslice of s[]");
3525 auto result
= new T
[s
.length
- slice
.length
+ replacement
.length
];
3526 immutable so
= &slice
[0] - &s
[0];
3527 result
[0 .. so
] = s
[0 .. so
];
3528 result
[so
.. so
+ replacement
.length
] = replacement
[];
3529 result
[so
+ replacement
.length
.. result
.length
] =
3530 s
[so
+ slice
.length
.. s
.length
];
3532 return () @trusted inout {
3533 return cast(inout(T
)[]) result
;
3540 auto a
= [1, 2, 3, 4, 5];
3541 auto b
= replaceSlice(a
, a
[1 .. 4], [0, 0, 0]);
3543 assert(b
== [1, 0, 0, 0, 5]);
3548 import std
.algorithm
.comparison
: cmp;
3551 string slice
= s
[2 .. 4];
3553 auto r
= replaceSlice(s
, slice
, "bar");
3555 i
= cmp(r
, "hebaro");
3560 Implements an output range that appends data to an array. This is
3561 recommended over $(D array ~= data) when appending many elements because it is more
3562 efficient. `Appender` maintains its own array metadata locally, so it can avoid
3563 the $(DDSUBLINK spec/arrays, capacity-reserve, performance hit of looking up slice `capacity`)
3567 A = the array type to simulate.
3569 See_Also: $(LREF appender)
3572 if (isDynamicArray
!A
)
3574 import core
.memory
: GC
;
3576 private alias T
= ElementEncodingType
!A
;
3582 bool tryExtendBlock
= false;
3585 private Data
* _data
;
3588 * Constructs an `Appender` with a given array. Note that this does not copy the
3589 * data. If the array has a larger capacity as determined by `arr.capacity`,
3590 * it will be used by the appender. After initializing an appender on an array,
3591 * appending to the original array will reallocate.
3593 this(A arr
) @trusted
3595 // initialize to a given array.
3597 _data
.arr
= cast(Unqual
!T
[]) arr
; //trusted
3602 // We want to use up as much of the block the array is in as possible.
3603 // if we consume all the block that we can, then array appending is
3604 // safe WRT built-in append, and we can use the entire block.
3605 // We only do this for mutable types that can be extended.
3606 static if (isMutable
!T
&& is(typeof(arr
.length
= size_t
.max
)))
3608 immutable cap
= arr
.capacity
; //trusted
3609 // Replace with "GC.setAttr( Not Appendable )" once pure (and fixed)
3610 if (cap
> arr
.length
)
3613 _data
.capacity
= arr
.length
;
3617 * Reserve at least newCapacity elements for appending. Note that more elements
3618 * may be reserved than requested. If `newCapacity <= capacity`, then nothing is
3622 * newCapacity = the capacity the `Appender` should have
3624 void reserve(size_t newCapacity
)
3628 if (newCapacity
> _data
.capacity
)
3629 ensureAddable(newCapacity
- _data
.arr
.length
);
3633 ensureAddable(newCapacity
);
3638 * Returns: the capacity of the array (the maximum number of elements the
3639 * managed array can accommodate before triggering a reallocation). If any
3640 * appending will reallocate, `0` will be returned.
3642 @property size_t
capacity() const
3644 return _data ? _data
.capacity
: 0;
3647 /// Returns: The number of elements appended.
3648 @property size_t
length() const => _data ? _data
.arr
.length
: 0;
3651 * Use opSlice() from now on.
3652 * Returns: The managed array.
3654 @property inout(T
)[] data() inout
3660 * Returns: The managed array.
3662 @property inout(T
)[] opSlice() inout @trusted
3664 /* @trusted operation:
3665 * casting Unqual!T[] to inout(T)[]
3667 return cast(typeof(return))(_data ? _data
.arr
: null);
3670 // ensure we can add nelems elements, resizing as necessary
3671 private void ensureAddable(size_t nelems
)
3675 immutable len
= _data
.arr
.length
;
3676 immutable reqlen
= len
+ nelems
;
3678 if (_data
.capacity
>= reqlen
)
3681 // need to increase capacity
3684 static if (__traits(compiles
, new Unqual
!T
[1]))
3686 _data
.arr
.length
= reqlen
;
3690 // avoid restriction of @disable this()
3691 _data
.arr
= _data
.arr
[0 .. _data
.capacity
];
3692 foreach (i
; _data
.capacity
.. reqlen
)
3693 _data
.arr
~= Unqual
!T
.init
;
3695 _data
.arr
= _data
.arr
[0 .. len
];
3696 _data
.capacity
= reqlen
;
3700 import core
.stdc
.string
: memcpy
, memset
;
3701 // Time to reallocate.
3702 // We need to almost duplicate what's in druntime, except we
3703 // have better access to the capacity field.
3704 auto newlen
= appenderNewCapacity
!(T
.sizeof
)(_data
.capacity
, reqlen
);
3705 // first, try extending the current block
3706 if (_data
.tryExtendBlock
)
3708 immutable u
= (() @trusted => GC
.extend(_data
.arr
.ptr
, nelems
* T
.sizeof
, (newlen
- len
) * T
.sizeof
))();
3711 // extend worked, update the capacity
3712 // if the type has indirections, we need to zero any new
3713 // data that we requested, as the existing data may point
3714 // at large unused blocks.
3715 static if (hasIndirections
!T
)
3717 immutable addedSize
= u
- (_data
.capacity
* T
.sizeof
);
3718 () @trusted { memset(_data
.arr
.ptr
+ _data
.capacity
, 0, addedSize
); }();
3721 _data
.capacity
= u
/ T
.sizeof
;
3727 // didn't work, must reallocate
3728 import core
.checkedint
: mulu
;
3730 const nbytes
= mulu(newlen
, T
.sizeof
, overflow
);
3731 if (overflow
) assert(false, "the reallocation would exceed the "
3732 ~ "available pointer range");
3734 auto bi
= (() @trusted => GC
.qalloc(nbytes
, blockAttribute
!T
))();
3735 _data
.capacity
= bi
.size
/ T
.sizeof
;
3737 () @trusted { memcpy(bi
.base
, _data
.arr
.ptr
, len
* T
.sizeof
); }();
3739 _data
.arr
= (() @trusted => (cast(Unqual
!T
*) bi
.base
)[0 .. len
])();
3741 // we requested new bytes that are not in the existing
3742 // data. If T has pointers, then this new data could point at stale
3743 // objects from the last time this block was allocated. Zero that
3744 // new data out, it may point at large unused blocks!
3745 static if (hasIndirections
!T
)
3747 memset(bi
.base
+ (len
* T
.sizeof
), 0, (newlen
- len
) * T
.sizeof
);
3750 _data
.tryExtendBlock
= true;
3751 // leave the old data, for safety reasons
3755 private template canPutItem(U
)
3757 enum bool canPutItem
=
3758 is(Unqual
!U
: Unqual
!T
) ||
3759 isSomeChar
!T
&& isSomeChar
!U
;
3761 private template canPutConstRange(Range
)
3763 enum bool canPutConstRange
=
3764 isInputRange
!(Unqual
!Range
) &&
3765 !isInputRange
!Range
&&
3766 is(typeof(Appender
.init
.put(Range
.init
.front
)));
3768 private template canPutRange(Range
)
3770 enum bool canPutRange
=
3771 isInputRange
!Range
&&
3772 is(typeof(Appender
.init
.put(Range
.init
.front
)));
3776 * Appends `item` to the managed array. Performs encoding for
3777 * `char` types if `A` is a differently typed `char` array.
3780 * item = the single item to append
3785 static if (isSomeChar
!T
&& isSomeChar
!U
&& T
.sizeof
< U
.sizeof
)
3787 /* may throwable operation:
3790 // must do some transcoding around here
3791 import std
.utf
: encode
;
3792 Unqual
!T
[T
.sizeof
== 1 ?
4 : 2] encoded
;
3793 auto len
= encode(encoded
, item
);
3794 put(encoded
[0 .. len
]);
3798 import core
.lifetime
: emplace
;
3801 immutable len
= _data
.arr
.length
;
3803 auto bigData
= (() @trusted => _data
.arr
.ptr
[0 .. len
+ 1])();
3804 auto itemUnqual
= (() @trusted => & cast() item
)();
3805 emplace(&bigData
[len
], *itemUnqual
);
3806 //We do this at the end, in case of exceptions
3807 _data
.arr
= bigData
;
3811 // Const fixing hack.
3812 void put(Range
)(Range items
)
3813 if (canPutConstRange
!Range
)
3815 alias p
= put
!(Unqual
!Range
);
3820 * Appends an entire range to the managed array. Performs encoding for
3821 * `char` elements if `A` is a differently typed `char` array.
3824 * items = the range of items to append
3826 void put(Range
)(Range items
)
3827 if (canPutRange
!Range
)
3829 // note, we disable this branch for appending one type of char to
3830 // another because we can't trust the length portion.
3831 static if (!(isSomeChar
!T
&& isSomeChar
!(ElementType
!Range
) &&
3832 !is(immutable Range
== immutable T
[])) &&
3833 is(typeof(items
.length
) == size_t
))
3835 // optimization -- if this type is something other than a string,
3836 // and we are adding exactly one element, call the version for one
3838 static if (!isSomeChar
!T
)
3840 if (items
.length
== 1)
3847 // make sure we have enough space, then add the items
3848 auto bigDataFun(size_t extra
)
3850 ensureAddable(extra
);
3851 return (() @trusted => _data
.arr
.ptr
[0 .. _data
.arr
.length
+ extra
])();
3853 auto bigData
= bigDataFun(items
.length
);
3855 immutable len
= _data
.arr
.length
;
3856 immutable newlen
= bigData
.length
;
3858 alias UT
= Unqual
!T
;
3860 static if (is(typeof(_data
.arr
[] = items
[])) &&
3861 !hasElaborateAssign
!UT
&& isAssignable
!(UT
, ElementEncodingType
!Range
))
3863 bigData
[len
.. newlen
] = items
[];
3867 import core
.internal
.lifetime
: emplaceRef
;
3868 foreach (ref it
; bigData
[len
.. newlen
])
3870 emplaceRef
!T(it
, items
.front
);
3875 //We do this at the end, in case of exceptions
3876 _data
.arr
= bigData
;
3878 else static if (isSomeChar
!T
&& isSomeChar
!(ElementType
!Range
) &&
3879 !is(immutable T
== immutable ElementType
!Range
))
3881 // need to decode and encode
3882 import std
.utf
: decodeFront
;
3883 while (!items
.empty
)
3885 auto c
= items
.decodeFront
;
3891 //pragma(msg, Range.stringof);
3892 // Generic input range
3893 for (; !items
.empty
; items
.popFront())
3901 * Appends to the managed array.
3903 * See_Also: $(LREF Appender.put)
3905 alias opOpAssign(string op
: "~") = put
;
3907 // only allow overwriting data on non-immutable and non-const data
3908 static if (isMutable
!T
)
3911 * Clears the managed array. This allows the elements of the array to be reused
3914 * Note: clear is disabled for immutable or const element types, due to the
3915 * possibility that `Appender` might overwrite immutable data.
3917 void clear() @trusted pure nothrow
3921 _data
.arr
= _data
.arr
.ptr
[0 .. 0];
3926 * Shrinks the managed array to the given length.
3928 * Throws: `Exception` if newlength is greater than the current array length.
3929 * Note: shrinkTo is disabled for immutable or const element types.
3931 void shrinkTo(size_t newlength
) @trusted pure
3933 import std
.exception
: enforce
;
3936 enforce(newlength
<= _data
.arr
.length
, "Attempting to shrink Appender with newlength > length");
3937 _data
.arr
= _data
.arr
.ptr
[0 .. newlength
];
3940 enforce(newlength
== 0, "Attempting to shrink empty Appender with non-zero newlength");
3945 * Gives a string in the form of `Appender!(A)(data)`.
3948 * w = A `char` accepting
3949 * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives).
3950 * fmt = A $(REF FormatSpec, std, format) which controls how the array
3953 * A `string` if `writer` is not set; `void` otherwise.
3955 string
toString()() const
3957 import std
.format
.spec
: singleSpec
;
3959 auto app
= appender
!string();
3960 auto spec
= singleSpec("%s");
3961 immutable len
= _data ? _data
.arr
.length
: 0;
3962 // different reserve lengths because each element in a
3963 // non-string-like array uses two extra characters for `, `.
3964 static if (isSomeString
!A
)
3966 app
.reserve(len
+ 25);
3970 // Multiplying by three is a very conservative estimate of
3971 // length, as it assumes each element is only one char
3972 app
.reserve((len
* 3) + 25);
3974 toString(app
, spec
);
3978 import std
.format
.spec
: FormatSpec
;
3981 template toString(Writer
)
3982 if (isOutputRange
!(Writer
, char))
3984 void toString(ref Writer w
, scope const ref FormatSpec
!char fmt
) const
3986 import std
.format
.write
: formatValue
;
3987 import std
.range
.primitives
: put
;
3988 put(w
, Unqual
!(typeof(this)).stringof
);
3990 formatValue(w
, data
, fmt
);
3997 @safe pure nothrow unittest
3999 auto app
= appender
!string();
4000 string b
= "abcdefg";
4003 assert(app
[] == "abcdefg");
4006 auto app2
= appender(a
);
4008 assert(app2
.length
== 3);
4009 app2
.put([ 4, 5, 6 ]);
4010 assert(app2
[] == [ 1, 2, 3, 4, 5, 6 ]);
4015 import std
.format
: format
;
4016 import std
.format
.spec
: singleSpec
;
4018 auto app
= appender
!(int[])();
4022 assert("%s".format(app
) == "Appender!(int[])(%s)".format([1,2,3]));
4024 auto app2
= appender
!string();
4025 auto spec
= singleSpec("%s");
4026 app
.toString(app2
, spec
);
4027 assert(app2
[] == "Appender!(int[])([1, 2, 3])");
4029 auto app3
= appender
!string();
4030 spec
= singleSpec("%(%04d, %)");
4031 app
.toString(app3
, spec
);
4032 assert(app3
[] == "Appender!(int[])(0001, 0002, 0003)");
4035 // https://issues.dlang.org/show_bug.cgi?id=17251
4036 @safe pure nothrow unittest
4040 int front() const { return 0; }
4041 bool empty() const { return true; }
4045 auto app
= appender
!(R
[]);
4051 // https://issues.dlang.org/show_bug.cgi?id=13300
4052 @safe pure nothrow unittest
4054 static test(bool isPurePostblit
)()
4056 static if (!isPurePostblit
)
4061 @disable this(); // Without this, it works.
4062 static if (!isPurePostblit
)
4073 @property Simple
front() { return Simple(0); }
4074 void popFront() { count
++; }
4075 @property empty() { return count
< 3; }
4083 static assert(__traits(compiles
, () pure { test!true(); }));
4084 static assert(!__traits(compiles
, () pure { test!false(); }));
4087 // https://issues.dlang.org/show_bug.cgi?id=19572
4088 @safe pure nothrow unittest
4090 static struct Struct
4094 int fun() const { return 23; }
4099 Appender
!(Struct
[]) appender
;
4101 appender
.put(const(Struct
)(42));
4103 auto result
= appender
[][0];
4105 assert(result
.value
!= 23);
4110 import std
.conv
: to
;
4111 import std
.utf
: byCodeUnit
;
4112 auto str = "ウェブサイト";
4113 auto wstr
= appender
!wstring();
4114 put(wstr
, str.byCodeUnit
);
4115 assert(wstr
.data
== str.to
!wstring
);
4118 // https://issues.dlang.org/show_bug.cgi?id=21256
4121 Appender
!string app1
;
4124 Appender
!(int[]) app2
;
4128 // https://issues.dlang.org/show_bug.cgi?id=24856
4131 import core
.memory
: GC
;
4132 import std
.stdio
: writeln
;
4133 import std
.algorithm
.searching
: canFind
;
4135 scope(exit
) GC
.enable();
4137 // generate some poison blocks to allocate from.
4138 auto poison
= cast(void*) 0xdeadbeef;
4139 foreach (i
; 0 .. 10)
4141 auto blk
= new void*[7];
4150 foreach (i
; 0 .. 10)
4152 Appender
!(void*[]) app
;
4154 // if not a realloc of one of the deadbeef pointers, continue
4155 if (!freeme
.canFind(app
.data
.ptr
))
4158 assert(!app
.data
.ptr
[0 .. app
.capacity
].canFind(poison
), "Appender not zeroing data!");
4160 // just notify in the log whether this test actually could be done.
4162 writeln("WARNING: test of Appender zeroing did not occur");
4165 //Calculates an efficient growth scheme based on the old capacity
4166 //of data, and the minimum requested capacity.
4167 //arg curLen: The current length
4168 //arg reqLen: The length as requested by the user
4169 //ret sugLen: A suggested growth.
4170 private size_t
appenderNewCapacity(size_t TSizeOf
)(size_t curLen
, size_t reqLen
)
4172 import core
.bitop
: bsr;
4173 import std
.algorithm
.comparison
: max
;
4175 return max(reqLen
,8);
4176 ulong mult
= 100 + (1000UL) / (bsr(curLen
* TSizeOf
) + 1);
4177 // limit to doubling the length, we don't want to grow too much
4180 auto sugLen
= cast(size_t
)((curLen
* mult
+ 99) / 100);
4181 return max(reqLen
, sugLen
);
4185 * A version of $(LREF Appender) that can update an array in-place.
4186 * It forwards all calls to an underlying appender implementation.
4187 * Any calls made to the appender also update the pointer to the
4188 * original array passed in.
4190 * Tip: Use the `arrayPtr` overload of $(LREF appender) for construction with type-inference.
4193 * A = The array type to simulate
4195 struct RefAppender(A
)
4196 if (isDynamicArray
!A
)
4198 private alias T
= ElementEncodingType
!A
;
4207 * Constructs a `RefAppender` with a given array reference. This does not copy the
4208 * data. If the array has a larger capacity as determined by `arr.capacity`, it
4209 * will be used by the appender.
4211 * Note: Do not use built-in appending (i.e. `~=`) on the original array
4212 * until you are done with the appender, because subsequent calls to the appender
4213 * will reallocate the array data without those appends.
4216 * arr = Pointer to an array. Must not be _null.
4220 impl
= Appender
!A(*arr
);
4224 /** Wraps remaining `Appender` methods such as $(LREF put).
4226 * fn = Method name to call.
4227 * args = Arguments to pass to the method.
4229 void opDispatch(string fn
, Args
...)(Args args
)
4230 if (__traits(compiles
, (Appender
!A a
) => mixin("a." ~ fn
~ "(args)")))
4232 // we do it this way because we can't cache a void return
4233 scope(exit
) *this.arr
= impl
[];
4234 mixin("return impl." ~ fn
~ "(args);");
4238 * Appends `rhs` to the managed array.
4240 * rhs = Element or range.
4242 void opOpAssign(string op
: "~", U
)(U rhs
)
4243 if (__traits(compiles
, (Appender
!A a
){ a
.put(rhs
); }))
4245 scope(exit
) *this.arr
= impl
[];
4250 * Returns the capacity of the array (the maximum number of elements the
4251 * managed array can accommodate before triggering a reallocation). If any
4252 * appending will reallocate, `capacity` returns `0`.
4254 @property size_t
capacity() const
4256 return impl
.capacity
;
4259 /// Returns: The number of elements appended.
4260 @property size_t
length() const => impl
.length
;
4262 /* Use opSlice() instead.
4263 * Returns: the managed array.
4265 @property inout(T
)[] data() inout
4271 * Returns: the managed array.
4273 @property inout(ElementEncodingType
!A
)[] opSlice() inout
4284 auto app2
= appender(&a
);
4285 assert(app2
[] == [1, 2]);
4286 assert(a
== [1, 2]);
4288 assert(app2
.length
== 3);
4290 assert(app2
[] == [1, 2, 3, 4, 5, 6]);
4291 assert(a
== [1, 2, 3, 4, 5, 6]);
4294 assert(app2
.capacity
>= 5);
4298 Convenience function that returns an $(LREF Appender) instance,
4299 optionally initialized with `array`.
4301 Appender
!A
appender(A
)()
4302 if (isDynamicArray
!A
)
4304 return Appender
!A(null);
4307 Appender
!(E
[]) appender(A
: E
[], E
)(auto ref A array
)
4309 static assert(!isStaticArray
!A ||
__traits(isRef
, array
),
4310 "Cannot create Appender from an rvalue static array");
4312 return Appender
!(E
[])(array
);
4315 @safe pure nothrow unittest
4317 auto app
= appender
!(char[])();
4318 string b
= "abcdefg";
4319 foreach (char c
; b
) app
.put(c
);
4320 assert(app
[] == "abcdefg");
4323 @safe pure nothrow unittest
4325 auto app
= appender
!(char[])();
4326 string b
= "abcdefg";
4327 foreach (char c
; b
) app
~= c
;
4328 assert(app
[] == "abcdefg");
4331 @safe pure nothrow unittest
4334 auto app2
= appender(a
);
4335 assert(app2
[] == [ 1, 2 ]);
4337 app2
.put([ 4, 5, 6 ][]);
4338 assert(app2
[] == [ 1, 2, 3, 4, 5, 6 ]);
4340 assert(app2
[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
4343 @safe pure nothrow unittest
4345 auto app4
= appender([]);
4346 try // shrinkTo may throw
4350 catch (Exception
) assert(0);
4353 // https://issues.dlang.org/show_bug.cgi?id=5663
4354 // https://issues.dlang.org/show_bug.cgi?id=9725
4355 @safe pure nothrow unittest
4357 import std
.exception
: assertNotThrown
;
4359 static foreach (S
; AliasSeq
!(char[], const(char)[], string
))
4362 Appender
!S app5663i
;
4363 assertNotThrown(app5663i
.put("\xE3"));
4364 assert(app5663i
[] == "\xE3");
4366 Appender
!S app5663c
;
4367 assertNotThrown(app5663c
.put(cast(const(char)[])"\xE3"));
4368 assert(app5663c
[] == "\xE3");
4370 Appender
!S app5663m
;
4371 assertNotThrown(app5663m
.put("\xE3".dup
));
4372 assert(app5663m
[] == "\xE3");
4376 Appender
!S app5663i
;
4377 assertNotThrown(app5663i
~= "\xE3");
4378 assert(app5663i
[] == "\xE3");
4380 Appender
!S app5663c
;
4381 assertNotThrown(app5663c
~= cast(const(char)[])"\xE3");
4382 assert(app5663c
[] == "\xE3");
4384 Appender
!S app5663m
;
4385 assertNotThrown(app5663m
~= "\xE3".dup
);
4386 assert(app5663m
[] == "\xE3");
4391 // https://issues.dlang.org/show_bug.cgi?id=10122
4392 @safe pure nothrow unittest
4394 import std
.exception
: assertCTFEable
;
4396 static struct S10122
4401 this(int v
) @safe pure nothrow { val
= v
; }
4405 auto w
= appender
!(S10122
[])();
4407 assert(w
[].length
== 1 && w
[][0].val
== 1);
4411 @safe pure nothrow unittest
4413 import std
.exception
: assertThrown
;
4416 auto app2
= appender(a
);
4417 assert(app2
[] == [ 1, 2 ]);
4419 app2
~= [ 4, 5, 6 ][];
4420 assert(app2
[] == [ 1, 2, 3, 4, 5, 6 ]);
4422 assert(app2
[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
4425 assert(app2
.capacity
>= 5);
4427 try // shrinkTo may throw
4431 catch (Exception
) assert(0);
4432 assert(app2
[] == [ 1, 2, 3 ]);
4433 assertThrown(app2
.shrinkTo(5));
4436 assert(app3
.capacity
>= 3);
4437 assert(app3
[] == [1, 2, 3]);
4444 auto w
= appender
!string
;
4445 // pre-allocate space for at least 10 elements (this avoids costly reallocations)
4447 assert(w
.capacity
>= 10);
4449 w
.put('a'); // single elements
4450 w
.put("bc"); // multiple elements
4452 // use the append syntax
4456 assert(w
[] == "abcdef");
4459 @safe pure nothrow unittest
4461 auto w
= appender
!string();
4463 cast(void) w
.capacity
;
4469 w
.put(wc
); // decoding may throw
4470 w
.put(dc
); // decoding may throw
4472 catch (Exception
) assert(0);
4475 @safe pure nothrow unittest
4477 auto w
= appender
!(int[])();
4479 cast(void) w
.capacity
;
4488 catch (Exception
) assert(0);
4500 @property bool empty() { return true; }
4501 @property T
front() { return T
.init
; }
4508 // https://issues.dlang.org/show_bug.cgi?id=10690
4509 @safe pure nothrow unittest
4511 import std
.algorithm
.iteration
: filter
;
4512 import std
.typecons
: tuple
;
4513 [tuple(1)].filter
!(t
=> true).array
; // No error
4514 [tuple("A")].filter
!(t
=> true).array
; // error
4517 @safe pure nothrow unittest
4520 //Coverage for put(Range)
4528 auto a1
= Appender
!(S1
[])();
4529 auto a2
= Appender
!(S2
[])();
4530 auto au1
= Appender
!(const(S1
)[])();
4531 a1
.put(S1().repeat().take(10));
4532 a2
.put(S2().repeat().take(10));
4533 auto sc1
= const(S1
)();
4534 au1
.put(sc1
.repeat().take(10));
4537 @system pure unittest
4544 auto au2
= Appender
!(const(S2
)[])();
4545 auto sc2
= const(S2
)();
4546 au2
.put(sc2
.repeat().take(10));
4549 @system pure nothrow unittest
4556 auto a0
= Appender
!(S
[])();
4557 auto a1
= Appender
!(const(S
)[])();
4558 auto a2
= Appender
!(immutable(S
)[])();
4560 auto s1
= const(S
)(null);
4561 auto s2
= immutable(S
)(null);
4569 static assert(!is(typeof(a0
.put(a1
))));
4570 static assert(!is(typeof(a0
.put(a2
))));
4572 static assert(!is(typeof(a0
.put([a1
]))));
4573 static assert(!is(typeof(a0
.put([a2
]))));
4574 static assert(!is(typeof(a2
.put(a0
))));
4575 static assert(!is(typeof(a2
.put(a1
))));
4577 static assert(!is(typeof(a2
.put([a0
]))));
4578 static assert(!is(typeof(a2
.put([a1
]))));
4582 // https://issues.dlang.org/show_bug.cgi?id=9528
4583 @safe pure nothrow unittest
4585 const(E
)[] fastCopy(E
)(E
[] src
) {
4586 auto app
= appender
!(const(E
)[])();
4593 static struct S
{ const(C
) c
; }
4594 S
[] s
= [ S(new C
) ];
4596 auto t
= fastCopy(s
); // Does not compile
4597 assert(t
.length
== 1);
4600 // https://issues.dlang.org/show_bug.cgi?id=10753
4603 import std
.algorithm
.iteration
: map
;
4611 [1, 2].map
!Bar
.array
;
4614 @safe pure nothrow unittest
4616 import std
.algorithm
.comparison
: equal
;
4618 //New appender signature tests
4619 alias mutARR
= int[];
4620 alias conARR
= const(int)[];
4621 alias immARR
= immutable(int)[];
4627 auto app1
= Appender
!mutARR(mut
); //Always worked. Should work. Should not create a warning.
4629 assert(equal(app1
[], [7]));
4630 static assert(!is(typeof(Appender
!mutARR(con
)))); //Never worked. Should not work.
4631 static assert(!is(typeof(Appender
!mutARR(imm
)))); //Never worked. Should not work.
4633 auto app2
= Appender
!conARR(mut
); //Always worked. Should work. Should not create a warning.
4635 assert(equal(app2
[], [7]));
4636 auto app3
= Appender
!conARR(con
); //Didn't work. Now works. Should not create a warning.
4638 assert(equal(app3
[], [7]));
4639 auto app4
= Appender
!conARR(imm
); //Didn't work. Now works. Should not create a warning.
4641 assert(equal(app4
[], [7]));
4643 //{auto app = Appender!immARR(mut);} //Worked. Will cease to work. Creates warning.
4644 //static assert(!is(typeof(Appender!immARR(mut)))); //Worked. Will cease to work. Uncomment me after full deprecation.
4645 static assert(!is(typeof(Appender
!immARR(con
)))); //Never worked. Should not work.
4646 auto app5
= Appender
!immARR(imm
); //Didn't work. Now works. Should not create a warning.
4648 assert(equal(app5
[], [7]));
4650 //Deprecated. Please uncomment and make sure this doesn't work:
4652 //static assert(!is(typeof(Appender!string(cc))));
4654 //This should always work:
4655 auto app6
= appender
!string(null);
4656 assert(app6
[] == null);
4657 auto app7
= appender
!(const(char)[])(null);
4658 assert(app7
[] == null);
4659 auto app8
= appender
!(char[])(null);
4660 assert(app8
[] == null);
4663 @safe pure nothrow unittest //Test large allocations (for GC.extend)
4665 import std
.algorithm
.comparison
: equal
;
4667 Appender
!(char[]) app
;
4668 app
.reserve(1); //cover reserve on non-initialized
4669 foreach (_
; 0 .. 100_000)
4671 assert(equal(app
[], 'a'.repeat(100_000)));
4674 @safe pure nothrow unittest
4676 auto reference
= new ubyte[](2048 + 1); //a number big enough to have a full page (EG: the GC extends)
4677 auto arr
= reference
.dup
;
4678 auto app
= appender(arr
[0 .. 0]);
4679 app
.reserve(1); //This should not trigger a call to extend
4680 app
.put(ubyte(1)); //Don't clobber arr
4681 assert(reference
[] == arr
[]);
4684 @safe pure nothrow unittest // clear method is supported only for mutable element types
4686 Appender
!string app
;
4688 static assert(!__traits(compiles
, app
.clear()));
4689 assert(app
[] == "foo");
4692 @safe pure nothrow unittest
4694 static struct D
//dynamic
4699 static struct S
//static
4704 static assert(!is(Appender
!(char[5])));
4705 static assert(!is(Appender
!D
));
4706 static assert(!is(Appender
!S
));
4712 int[5] foo(){return a
;}
4714 static assert(!is(typeof(appender(a
))));
4715 static assert( is(typeof(appender(b
))));
4716 static assert( is(typeof(appender(d
))));
4717 static assert( is(typeof(appender(s
))));
4718 static assert(!is(typeof(appender(foo()))));
4723 // https://issues.dlang.org/show_bug.cgi?id=13077
4727 auto w
= appender
!(shared(A
)[])();
4728 w
.put(new shared A());
4732 InputRange
!(shared A
) foo()
4734 return [new shared A
].inputRangeObject
;
4736 auto res
= foo
.array
;
4737 assert(res
.length
== 1);
4741 Convenience function that returns a $(LREF RefAppender) instance initialized
4742 with `arrayPtr`. Don't use null for the array pointer, use the other
4743 version of `appender` instead.
4745 RefAppender
!(E
[]) appender(P
: E
[]*, E
)(P arrayPtr
)
4747 return RefAppender
!(E
[])(arrayPtr
);
4755 auto app2
= appender(&a
);
4756 assert(app2
[] == [1, 2]);
4757 assert(a
== [1, 2]);
4760 assert(app2
[] == [1, 2, 3, 4, 5, 6]);
4761 assert(a
== [1, 2, 3, 4, 5, 6]);
4764 assert(app2
.capacity
>= 5);
4767 @safe pure nothrow unittest
4769 auto arr
= new char[0];
4770 auto app
= appender(&arr
);
4771 string b
= "abcdefg";
4772 foreach (char c
; b
) app
.put(c
);
4773 assert(app
[] == "abcdefg");
4774 assert(arr
== "abcdefg");
4777 @safe pure nothrow unittest
4779 auto arr
= new char[0];
4780 auto app
= appender(&arr
);
4781 string b
= "abcdefg";
4782 foreach (char c
; b
) app
~= c
;
4783 assert(app
[] == "abcdefg");
4784 assert(arr
== "abcdefg");
4787 @safe pure nothrow unittest
4790 auto app2
= appender(&a
);
4791 assert(app2
[] == [ 1, 2 ]);
4792 assert(a
== [ 1, 2 ]);
4794 app2
.put([ 4, 5, 6 ][]);
4795 assert(app2
[] == [ 1, 2, 3, 4, 5, 6 ]);
4796 assert(a
== [ 1, 2, 3, 4, 5, 6 ]);
4799 @safe pure nothrow unittest
4801 import std
.exception
: assertThrown
;
4804 auto app2
= appender(&a
);
4805 assert(app2
[] == [ 1, 2 ]);
4806 assert(a
== [ 1, 2 ]);
4808 app2
~= [ 4, 5, 6 ][];
4809 assert(app2
[] == [ 1, 2, 3, 4, 5, 6 ]);
4810 assert(a
== [ 1, 2, 3, 4, 5, 6 ]);
4813 assert(app2
.capacity
>= 5);
4815 try // shrinkTo may throw
4819 catch (Exception
) assert(0);
4820 assert(app2
[] == [ 1, 2, 3 ]);
4821 assertThrown(app2
.shrinkTo(5));
4824 assert(app3
.capacity
>= 3);
4825 assert(app3
[] == [1, 2, 3]);
4828 // https://issues.dlang.org/show_bug.cgi?id=14605
4829 @safe pure nothrow unittest
4831 static assert(isOutputRange
!(Appender
!(int[]), int));
4832 static assert(isOutputRange
!(RefAppender
!(int[]), int));
4835 @safe pure nothrow unittest
4837 Appender
!(int[]) app
;
4838 short[] range
= [1, 2, 3];
4840 assert(app
[] == [1, 2, 3]);
4843 @safe pure nothrow unittest
4845 string s
= "hello".idup
;
4846 char[] a
= "hello".dup
;
4847 auto appS
= appender(s
);
4848 auto appA
= appender(a
);
4851 s
~= 'a'; //Clobbers here?
4852 a
~= 'a'; //Clobbers here?
4853 assert(appS
[] == "hellow");
4854 assert(appA
[] == "hellow");
4858 Constructs a static array from a dynamic array whose length is known at compile-time.
4859 The element type can be inferred or specified explicitly:
4861 * $(D [1, 2].staticArray) returns `int[2]`
4862 * $(D [1, 2].staticArray!float) returns `float[2]`
4864 Note: `staticArray` returns by value, so expressions involving large arrays may be inefficient.
4867 a = The input array.
4869 Returns: A static array constructed from `a`.
4871 pragma(inline
, true) T
[n
] staticArray(T
, size_t n
)(auto ref T
[n
] a
)
4876 /// static array from array literal
4877 nothrow pure @safe @nogc unittest
4879 auto a
= [0, 1].staticArray
;
4880 static assert(is(typeof(a
) == int[2]));
4881 assert(a
== [0, 1]);
4885 pragma(inline
, true) U
[n
] staticArray(U
, T
, size_t n
)(auto ref T
[n
] a
)
4886 if (!is(T
== U
) && is(T
: U
))
4888 return a
[].staticArray
!(U
[n
]);
4891 /// static array from array with implicit casting of elements
4892 nothrow pure @safe @nogc unittest
4894 auto b
= [0, 1].staticArray
!long;
4895 static assert(is(typeof(b
) == long[2]));
4896 assert(b
== [0, 1]);
4899 nothrow pure @safe @nogc unittest
4902 static immutable gold
= [1, 2, 3];
4903 [1, 2, val
].staticArray
.checkStaticArray
!int([1, 2, 3]);
4905 @nogc void checkNogc()
4907 [1, 2, val
].staticArray
.checkStaticArray
!int(gold
);
4912 [1, 2, val
].staticArray
!double.checkStaticArray
!double(gold
);
4913 [1, 2, 3].staticArray
!int.checkStaticArray
!int(gold
);
4915 [1, 2, 3].staticArray
!(const(int)).checkStaticArray
!(const(int))(gold
);
4916 [1, 2, 3].staticArray
!(const(double)).checkStaticArray
!(const(double))(gold
);
4918 const(int)[3] a2
= [1, 2, 3].staticArray
;
4921 [cast(byte) 1, cast(byte) 129].staticArray
.checkStaticArray
!byte([1, -127]);
4925 Constructs a static array from a range.
4926 When `a.length` is not known at compile time, the number of elements must be
4927 given as a template argument (e.g. `myrange.staticArray!2`).
4928 Size and type can be combined, if the source range elements are implicitly
4929 convertible to the requested element type (eg: `2.iota.staticArray!(long[2])`).
4931 When the range `a` is known at compile time, it can be given as a
4932 template argument to avoid having to specify the number of elements
4933 (e.g.: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`).
4936 a = The input range. If there are less elements than the specified length of the static array,
4937 the rest of it is default-initialized. If there are more than specified, the first elements
4938 up to the specified length are used.
4939 rangeLength = Output for the number of elements used from `a`. Optional.
4941 auto staticArray(size_t n
, T
)(scope T a
)
4944 alias U
= ElementType
!T
;
4945 return staticArray
!(U
[n
], U
, n
)(a
);
4949 auto staticArray(size_t n
, T
)(scope T a
, out size_t rangeLength
)
4952 alias U
= ElementType
!T
;
4953 return staticArray
!(U
[n
], U
, n
)(a
, rangeLength
);
4957 auto staticArray(Un
: U
[n
], U
, size_t n
, T
)(scope T a
)
4958 if (isInputRange
!T
&& is(ElementType
!T
: U
))
4960 size_t extraStackSpace
;
4961 return staticArray
!(Un
, U
, n
)(a
, extraStackSpace
);
4965 auto staticArray(Un
: U
[n
], U
, size_t n
, T
)(scope T a
, out size_t rangeLength
)
4966 if (isInputRange
!T
&& is(ElementType
!T
: U
))
4968 import std
.algorithm
.mutation
: uninitializedFill
;
4969 import std
.range
: take
;
4970 import core
.internal
.lifetime
: emplaceRef
;
4975 // Compile-time version to avoid unchecked memory access.
4977 for (auto iter
= a
.take(n
); !iter
.empty
; iter
.popFront())
4979 ret[i
] = iter
.front
;
4984 return (() @trusted => cast(U
[n
]) ret)();
4987 auto ret = (() @trusted
4989 Unqual
!U
[n
] theArray
= void;
4996 // ret was void-initialized so let's initialize the unfilled part manually.
4997 // also prevents destructors to be called on uninitialized memory if
4998 // an exception is thrown
4999 scope (exit
) ret[i
.. $].uninitializedFill(U
.init
);
5001 for (auto iter
= a
.take(n
); !iter
.empty
; iter
.popFront())
5003 emplaceRef
!U(ret[i
++], iter
.front
);
5008 return (() @trusted => cast(U
[n
]) ret)();
5011 /// static array from range + size
5012 nothrow pure @safe @nogc unittest
5014 import std
.range
: iota
;
5016 auto input
= 3.iota
;
5017 auto a
= input
.staticArray
!2;
5018 static assert(is(typeof(a
) == int[2]));
5019 assert(a
== [0, 1]);
5020 auto b
= input
.staticArray
!(long[4]);
5021 static assert(is(typeof(b
) == long[4]));
5022 assert(b
== [0, 1, 2, 0]);
5025 // Tests that code compiles when there is an elaborate destructor and exceptions
5026 // are thrown. Unfortunately can't test that memory is initialized
5027 // before having a destructor called on it.
5028 @safe nothrow unittest
5030 // exists only to allow doing something in the destructor. Not tested
5031 // at the end because value appears to depend on implementation of the.
5033 static int preventersDestroyed
= 0;
5035 static struct CopyPreventer
5040 if (on
) throw new Exception("Thou shalt not copy past me!");
5045 preventersDestroyed
++;
5050 CopyPreventer(false),
5051 CopyPreventer(false),
5052 CopyPreventer(true),
5053 CopyPreventer(false),
5054 CopyPreventer(true),
5059 auto staticArray
= normalArray
.staticArray
!5;
5062 catch (Exception e
){}
5066 nothrow pure @safe @nogc unittest
5068 auto a
= [1, 2].staticArray
;
5069 assert(is(typeof(a
) == int[2]) && a
== [1, 2]);
5071 import std
.range
: iota
;
5073 2.iota
.staticArray
!2.checkStaticArray
!int([0, 1]);
5074 2.iota
.staticArray
!(double[2]).checkStaticArray
!double([0, 1]);
5075 2.iota
.staticArray
!(long[2]).checkStaticArray
!long([0, 1]);
5078 nothrow pure @safe @nogc unittest
5080 import std
.range
: iota
;
5081 size_t copiedAmount
;
5082 2.iota
.staticArray
!1(copiedAmount
);
5083 assert(copiedAmount
== 1);
5084 2.iota
.staticArray
!3(copiedAmount
);
5085 assert(copiedAmount
== 2);
5089 auto staticArray(alias a
)()
5090 if (isInputRange
!(typeof(a
)))
5092 return .staticArray
!(size_t(a
.length
))(a
);
5096 auto staticArray(U
, alias a
)()
5097 if (isInputRange
!(typeof(a
)))
5099 return .staticArray
!(U
[size_t(a
.length
)])(a
);
5102 /// static array from CT range
5103 nothrow pure @safe @nogc unittest
5105 import std
.range
: iota
;
5107 enum a
= staticArray
!(2.iota
);
5108 static assert(is(typeof(a
) == int[2]));
5109 assert(a
== [0, 1]);
5111 enum b
= staticArray
!(long, 2.iota
);
5112 static assert(is(typeof(b
) == long[2]));
5113 assert(b
== [0, 1]);
5116 nothrow pure @safe @nogc unittest
5118 import std
.range
: iota
;
5120 enum a
= staticArray
!(2.iota
);
5121 staticArray
!(2.iota
).checkStaticArray
!int([0, 1]);
5122 staticArray
!(double, 2.iota
).checkStaticArray
!double([0, 1]);
5123 staticArray
!(long, 2.iota
).checkStaticArray
!long([0, 1]);
5126 version (StdUnittest
) private void checkStaticArray(T
, T1
, T2
)(T1 a
, T2 b
) nothrow @safe pure @nogc
5128 static assert(is(T1
== T
[T1
.length
]));
5129 assert(a
== b
, "a must be equal to b");