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