1 // Written in the D programming language.
2 /**
3 Functions and types that manipulate built-in arrays and associative arrays.
4
5 This module provides all kinds of functions to create, manipulate or convert arrays:
6
7 $(SCRIPT inhibitQuickIndex = 1;)
8 $(DIVC quickindex,
9 $(BOOKTABLE ,
10 $(TR $(TH Function Name) $(TH Description)
11 )
12 $(TR $(TD $(LREF array))
13 $(TD Returns a copy of the input in a newly allocated dynamic array.
14 ))
15 $(TR $(TD $(LREF appender))
16 $(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given array.
17 ))
18 $(TR $(TD $(LREF assocArray))
19 $(TD Returns a newly allocated associative array from a range of key/value tuples.
20 ))
21 $(TR $(TD $(LREF byPair))
22 $(TD Construct a range iterating over an associative array by key/value tuples.
23 ))
24 $(TR $(TD $(LREF insertInPlace))
25 $(TD Inserts into an existing array at a given position.
26 ))
27 $(TR $(TD $(LREF join))
28 $(TD Concatenates a range of ranges into one array.
29 ))
30 $(TR $(TD $(LREF minimallyInitializedArray))
31 $(TD Returns a new array of type `T`.
32 ))
33 $(TR $(TD $(LREF replace))
34 $(TD Returns a new array with all occurrences of a certain subrange replaced.
35 ))
36 $(TR $(TD $(LREF replaceFirst))
37 $(TD Returns a new array with the first occurrence of a certain subrange replaced.
38 ))
39 $(TR $(TD $(LREF replaceInPlace))
40 $(TD Replaces all occurrences of a certain subrange and puts the result into a given array.
41 ))
42 $(TR $(TD $(LREF replaceInto))
43 $(TD Replaces all occurrences of a certain subrange and puts the result into an output range.
44 ))
45 $(TR $(TD $(LREF replaceLast))
46 $(TD Returns a new array with the last occurrence of a certain subrange replaced.
47 ))
48 $(TR $(TD $(LREF replaceSlice))
49 $(TD Returns a new array with a given slice replaced.
50 ))
51 $(TR $(TD $(LREF replicate))
52 $(TD Creates a new array out of several copies of an input array or range.
53 ))
54 $(TR $(TD $(LREF sameHead))
55 $(TD Checks if the initial segments of two arrays refer to the same
56 place in memory.
57 ))
58 $(TR $(TD $(LREF sameTail))
59 $(TD Checks if the final segments of two arrays refer to the same place
60 in memory.
61 ))
62 $(TR $(TD $(LREF split))
63 $(TD Eagerly split a range or string into an array.
64 ))
65 $(TR $(TD $(LREF staticArray))
66 $(TD Creates a new static array from given data.
67 ))
68 $(TR $(TD $(LREF uninitializedArray))
69 $(TD Returns a new array of type `T` without initializing its elements.
70 ))
71 ))
72
73 Copyright: Copyright Andrei Alexandrescu 2008- and Jonathan M Davis 2011-.
74
75 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
76
77 Authors: $(HTTP erdani.org, Andrei Alexandrescu) and
78 $(HTTP jmdavisprog.com, Jonathan M Davis)
79
80 Source: $(PHOBOSSRC std/array.d)
81 */
82 module std.array;
83
84 import std.functional;
85 import std.meta;
86 import std.traits;
87
88 import std.range.primitives;
89 public import std.range.primitives : save, empty, popFront, popBack, front, back;
90
91 /**
92 * Allocates an array and initializes it with copies of the elements
93 * of range `r`.
94 *
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.
98 *
99 * Params:
100 * r = range (or aggregate with `opApply` function) whose elements are copied into the allocated array
101 * Returns:
102 * allocated and initialized array
103 */
104 ForeachType!Range[] array(Range)(Range r)
105 if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range)
106 {
107 if (__ctfe)
108 {
109 // Compile-time version to avoid memcpy calls.
110 // Also used to infer attributes of array().
111 typeof(return) result;
112 foreach (e; r)
113 result ~= e;
114 return result;
115 }
116
117 alias E = ForeachType!Range;
118 static if (hasLength!Range)
119 {
120 const length = r.length;
121 if (length == 0)
122 return null;
123
124 import core.internal.lifetime : emplaceRef;
125
126 auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))();
127
128 // Every element of the uninitialized array must be initialized
129 size_t cnt; //Number of elements that have been initialized
130 try
131 {
foreach(e;r)132 foreach (e; r)
133 {
134 emplaceRef!E(result[cnt], e);
135 ++cnt;
136 }
catch(Exception e)137 } catch (Exception e)
138 {
139 //https://issues.dlang.org/show_bug.cgi?id=22185
140 //Make any uninitialized elements safely destructible.
141 foreach (ref elem; result[cnt..$])
142 {
143 import core.internal.lifetime : emplaceInitializer;
144 emplaceInitializer(elem);
145 }
146 throw e;
147 }
148 /*
149 https://issues.dlang.org/show_bug.cgi?id=22673
150
151 We preallocated an array, we should ensure that enough range elements
152 were gathered such that every slot in the array is filled. If not, the GC
153 will collect the allocated array, leading to the `length - cnt` left over elements
154 being collected too - despite their contents having no guarantee of destructibility.
155 */
156 assert(length == cnt,
157 "Range .length property was not equal to the length yielded by the range before becoming empty");
158 return (() @trusted => cast(E[]) result)();
159 }
160 else
161 {
162 auto a = appender!(E[])();
foreach(e;r)163 foreach (e; r)
164 {
165 a.put(e);
166 }
167 return a.data;
168 }
169 }
170
171 /// ditto
172 ForeachType!(PointerTarget!Range)[] array(Range)(Range r)
173 if (isPointer!Range && isIterable!(PointerTarget!Range) && !isAutodecodableString!Range && !isInfinite!Range)
174 {
175 return array(*r);
176 }
177
178 ///
179 @safe pure nothrow unittest
180 {
181 auto a = array([1, 2, 3, 4, 5][]);
182 assert(a == [ 1, 2, 3, 4, 5 ]);
183 }
184
185 @safe pure nothrow unittest
186 {
187 import std.algorithm.comparison : equal;
188 struct Foo
189 {
190 int a;
191 }
192 auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
193 assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
194 }
195
196 @safe pure nothrow unittest
197 {
198 struct MyRange
199 {
200 enum front = 123;
201 enum empty = true;
popFrontMyRange202 void popFront() {}
203 }
204
205 auto arr = (new MyRange).array;
206 assert(arr.empty);
207 }
208
209 @safe pure nothrow unittest
210 {
211 immutable int[] a = [1, 2, 3, 4];
212 auto b = (&a).array;
213 assert(b == a);
214 }
215
216 @safe pure nothrow unittest
217 {
218 import std.algorithm.comparison : equal;
219 struct Foo
220 {
221 int a;
opAssignFoo222 noreturn opAssign(Foo)
223 {
224 assert(0);
225 }
opEqualsFoo226 auto opEquals(Foo foo)
227 {
228 return a == foo.a;
229 }
230 }
231 auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
232 assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
233 }
234
235 // https://issues.dlang.org/show_bug.cgi?id=12315
236 @safe pure nothrow unittest
237 {
238 static struct Bug12315 { immutable int i; }
239 enum bug12315 = [Bug12315(123456789)].array();
240 static assert(bug12315[0].i == 123456789);
241 }
242
243 @safe pure nothrow unittest
244 {
245 import std.range;
246 static struct S{int* p;}
247 auto a = array(immutable(S).init.repeat(5));
248 assert(a.length == 5);
249 }
250
251 // https://issues.dlang.org/show_bug.cgi?id=18995
252 @system unittest
253 {
254 import core.memory : __delete;
255 int nAlive = 0;
256 struct S
257 {
258 bool alive;
thisS259 this(int) { alive = true; ++nAlive; }
thisS260 this(this) { nAlive += alive; }
~thisS261 ~this() { nAlive -= alive; alive = false; }
262 }
263
264 import std.algorithm.iteration : map;
265 import std.range : iota;
266
267 auto arr = iota(3).map!(a => S(a)).array;
268 assert(nAlive == 3);
269
270 // No good way to ensure the GC frees this, just call the lifetime function
271 // directly.
272 __delete(arr);
273
274 assert(nAlive == 0);
275 }
276
277 @safe pure nothrow @nogc unittest
278 {
279 //Turn down infinity:
280 static assert(!is(typeof(
281 repeat(1).array()
282 )));
283 }
284
285 // https://issues.dlang.org/show_bug.cgi?id=20937
286 @safe pure nothrow unittest
287 {
288 struct S {int* x;}
289 struct R
290 {
291 immutable(S) front;
292 bool empty;
popFront()293 @safe pure nothrow void popFront(){empty = true;}
294 }
295 R().array;
296 }
297
298 /**
299 Convert a narrow autodecoding string to an array type that fully supports
300 random access. This is handled as a special case and always returns an array
301 of `dchar`
302
303 NOTE: This function is never used when autodecoding is turned off.
304
305 Params:
306 str = `isNarrowString` to be converted to an array of `dchar`
307 Returns:
308 a `dchar[]`, `const(dchar)[]`, or `immutable(dchar)[]` depending on the constness of
309 the input.
310 */
311 CopyTypeQualifiers!(ElementType!String,dchar)[] array(String)(scope String str)
312 if (isAutodecodableString!String)
313 {
314 import std.utf : toUTF32;
315 auto temp = str.toUTF32;
316 /* Unsafe cast. Allowed because toUTF32 makes a new array
317 and copies all the elements.
318 */
319 return () @trusted { return cast(CopyTypeQualifiers!(ElementType!String, dchar)[]) temp; } ();
320 }
321
322 ///
323 @safe pure nothrow unittest
324 {
325 import std.range.primitives : isRandomAccessRange;
326 import std.traits : isAutodecodableString;
327
328 // note that if autodecoding is turned off, `array` will not transcode these.
329 static if (isAutodecodableString!string)
330 assert("Hello D".array == "Hello D"d);
331 else
332 assert("Hello D".array == "Hello D");
333
334 static if (isAutodecodableString!wstring)
335 assert("Hello D"w.array == "Hello D"d);
336 else
337 assert("Hello D"w.array == "Hello D"w);
338
339 static assert(isRandomAccessRange!dstring == true);
340 }
341
342 @safe unittest
343 {
344 import std.conv : to;
345
toStringTestArray346 static struct TestArray { int x; string toString() @safe { return to!string(x); } }
347
348 static struct OpAssign
349 {
350 uint num;
this(uint num)351 this(uint num) { this.num = num; }
352
353 // Templating opAssign to make sure the bugs with opAssign being
354 // templated are fixed.
opAssign(T)355 void opAssign(T)(T rhs) { this.num = rhs.num; }
356 }
357
358 static struct OpApply
359 {
opApplyOpApply360 int opApply(scope int delegate(ref int) @safe dg)
361 {
362 int res;
363 foreach (i; 0 .. 10)
364 {
365 res = dg(i);
366 if (res) break;
367 }
368
369 return res;
370 }
371 }
372
373 auto a = array([1, 2, 3, 4, 5][]);
374 assert(a == [ 1, 2, 3, 4, 5 ]);
375
376 auto b = array([TestArray(1), TestArray(2)][]);
377 assert(b == [TestArray(1), TestArray(2)]);
378
379 class C
380 {
381 int x;
this(int y)382 this(int y) { x = y; }
toString()383 override string toString() const @safe { return to!string(x); }
384 }
385 auto c = array([new C(1), new C(2)][]);
386 assert(c[0].x == 1);
387 assert(c[1].x == 2);
388
389 auto d = array([1.0, 2.2, 3][]);
390 assert(is(typeof(d) == double[]));
391 assert(d == [1.0, 2.2, 3]);
392
393 auto e = [OpAssign(1), OpAssign(2)];
394 auto f = array(e);
395 assert(e == f);
396
397 assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
398 static if (isAutodecodableString!string)
399 {
400 assert(array("ABC") == "ABC"d);
401 assert(array("ABC".dup) == "ABC"d.dup);
402 }
403 }
404
405 // https://issues.dlang.org/show_bug.cgi?id=8233
406 @safe pure nothrow unittest
407 {
408 assert(array("hello world"d) == "hello world"d);
409 immutable a = [1, 2, 3, 4, 5];
410 assert(array(a) == a);
411 const b = a;
412 assert(array(b) == a);
413
414 //To verify that the opAssign branch doesn't get screwed up by using Unqual.
415 //EDIT: array no longer calls opAssign.
416 struct S
417 {
opAssignS418 ref S opAssign(S)(const ref S rhs)
419 {
420 assert(0);
421 }
422
423 int i;
424 }
425
426 static foreach (T; AliasSeq!(S, const S, immutable S))
427 {{
428 auto arr = [T(1), T(2), T(3), T(4)];
429 assert(array(arr) == arr);
430 }}
431 }
432
433 // https://issues.dlang.org/show_bug.cgi?id=9824
434 @safe pure nothrow unittest
435 {
436 static struct S
437 {
438 @disable void opAssign(S);
439 int i;
440 }
441 auto arr = [S(0), S(1), S(2)];
442 arr.array();
443 }
444
445 // https://issues.dlang.org/show_bug.cgi?id=10220
446 @safe pure nothrow unittest
447 {
448 import std.algorithm.comparison : equal;
449 import std.exception;
450 import std.range : repeat;
451
452 static struct S
453 {
454 int val;
455
456 @disable this();
thisS457 this(int v) { val = v; }
458 }
459 assertCTFEable!(
460 {
461 auto r = S(1).repeat(2).array();
462 assert(equal(r, [S(1), S(1)]));
463 });
464 }
465 //https://issues.dlang.org/show_bug.cgi?id=22673
466 @system unittest
467 {
468 struct LyingRange
469 {
470 enum size_t length = 100;
471 enum theRealLength = 50;
472 size_t idx = 0;
emptyLyingRange473 bool empty()
474 {
475 return idx <= theRealLength;
476 }
popFrontLyingRange477 void popFront()
478 {
479 ++idx;
480 }
frontLyingRange481 size_t front()
482 {
483 return idx;
484 }
485 }
486 static assert(hasLength!LyingRange);
487 LyingRange rng;
488 import std.exception : assertThrown;
489 assertThrown!Error(array(rng));
490 }
491 //https://issues.dlang.org/show_bug.cgi?id=22185
492 @system unittest
493 {
494 import std.stdio;
495 static struct ThrowingCopy
496 {
497 int x = 420;
thisThrowingCopy498 this(ref return scope ThrowingCopy rhs)
499 {
500 rhs.x = 420;
501 //
502 throw new Exception("This throws");
503 }
~thisThrowingCopy504 ~this()
505 {
506 /*
507 Any time this destructor runs, it should be running on "valid"
508 data. This is is mimicked by having a .init other than 0 (the value the memory
509 practically will be from the GC).
510 */
511 if (x != 420)
512 {
513 //This will only trigger during GC finalization so avoid writefln for now.
514 printf("Destructor failure in ThrowingCopy(%d) @ %p", x, &this);
515 assert(x == 420, "unittest destructor failed");
516 }
517 }
518 }
519 static struct LyingThrowingRange
520 {
521 enum size_t length = 100;
522 enum size_t evilRealLength = 50;
523 size_t idx;
front()524 ThrowingCopy front()
525 {
526 return ThrowingCopy(12);
527 }
empty()528 bool empty()
529 {
530 return idx == evilRealLength;
531 }
popFront()532 void popFront()
533 {
534 ++idx;
535 }
536 }
537 static assert(hasLength!LyingThrowingRange);
538 import std.exception : assertThrown;
539 {
540 assertThrown(array(LyingThrowingRange()));
541 }
542 import core.memory : GC;
543 /*
544 Force a collection early. Doesn't always actually finalize the bad objects
545 but trying to collect soon after the allocation is thrown away means any potential failures
546 will happen earlier.
547 */
548 GC.collect();
549 }
550
551 /**
552 Returns a newly allocated associative array from a range of key/value tuples
553 or from a range of keys and a range of values.
554
555 Params:
556 r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
557 of tuples of keys and values.
558 keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys
559 values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values
560
561 Returns:
562
563 A newly allocated associative array out of elements of the input
564 range, which must be a range of tuples (Key, Value) or
565 a range of keys and a range of values. If given two ranges of unequal
566 lengths after the elements of the shorter are exhausted the remaining
567 elements of the longer will not be considered.
568 Returns a null associative array reference when given an empty range.
569 Duplicates: Associative arrays have unique keys. If r contains duplicate keys,
570 then the result will contain the value of the last pair for that key in r.
571
572 See_Also: $(REF Tuple, std,typecons), $(REF zip, std,range)
573 */
574
575 auto assocArray(Range)(Range r)
576 if (isInputRange!Range)
577 {
578 import std.typecons : isTuple;
579
580 alias E = ElementType!Range;
581 static assert(isTuple!E, "assocArray: argument must be a range of tuples,"
582 ~" but was a range of "~E.stringof);
583 static assert(E.length == 2, "assocArray: tuple dimension must be 2");
584 alias KeyType = E.Types[0];
585 alias ValueType = E.Types[1];
586 static assert(isMutable!ValueType, "assocArray: value type must be mutable");
587
588 ValueType[KeyType] aa;
589 foreach (t; r)
590 aa[t[0]] = t[1];
591 return aa;
592 }
593
594 /// ditto
595 auto assocArray(Keys, Values)(Keys keys, Values values)
596 if (isInputRange!Values && isInputRange!Keys)
597 {
598 static if (isDynamicArray!Keys && isDynamicArray!Values
599 && !isNarrowString!Keys && !isNarrowString!Values)
600 {
601 void* aa;
602 {
603 // aaLiteral is nothrow when the destructors don't throw
604 static if (is(typeof(() nothrow
605 {
606 import std.range : ElementType;
607 import std.traits : hasElaborateDestructor;
608 alias KeyElement = ElementType!Keys;
609 static if (hasElaborateDestructor!KeyElement)
610 KeyElement.init.__xdtor();
611
612 alias ValueElement = ElementType!Values;
613 static if (hasElaborateDestructor!ValueElement)
614 ValueElement.init.__xdtor();
615 })))
616 {
617 scope(failure) assert(false, "aaLiteral must not throw");
618 }
619 if (values.length > keys.length)
620 values = values[0 .. keys.length];
621 else if (keys.length > values.length)
622 keys = keys[0 .. values.length];
623 aa = aaLiteral(keys, values);
624 }
625 alias Key = typeof(keys[0]);
626 alias Value = typeof(values[0]);
627 return (() @trusted => cast(Value[Key]) aa)();
628 }
629 else
630 {
631 // zip is not always able to infer nothrow
632 alias Key = ElementType!Keys;
633 alias Value = ElementType!Values;
634 static assert(isMutable!Value, "assocArray: value type must be mutable");
635 Value[Key] aa;
foreach(key;keys)636 foreach (key; keys)
637 {
638 if (values.empty) break;
639
640 // aa[key] is incorrectly not @safe if the destructor throws
641 // https://issues.dlang.org/show_bug.cgi?id=18592
642 static if (is(typeof(() @safe
643 {
644 import std.range : ElementType;
645 import std.traits : hasElaborateDestructor;
646 alias KeyElement = ElementType!Keys;
647 static if (hasElaborateDestructor!KeyElement)
648 KeyElement.init.__xdtor();
649
650 alias ValueElement = ElementType!Values;
651 static if (hasElaborateDestructor!ValueElement)
652 ValueElement.init.__xdtor();
653 })))
654 {
655 () @trusted {
656 aa[key] = values.front;
657 }();
658 }
659 else
660 {
661 aa[key] = values.front;
662 }
663 values.popFront();
664 }
665 return aa;
666 }
667 }
668
669 ///
670 @safe pure /*nothrow*/ unittest
671 {
672 import std.range : repeat, zip;
673 import std.typecons : tuple;
674 import std.range.primitives : autodecodeStrings;
675 auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap
676 static assert(is(typeof(a) == string[int]));
677 assert(a == [0:"a", 1:"b", 2:"c"]);
678
679 auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
680 static assert(is(typeof(b) == string[string]));
681 assert(b == ["foo":"bar", "baz":"quux"]);
682
683 static if (autodecodeStrings)
684 alias achar = dchar;
685 else
686 alias achar = immutable(char);
687 auto c = assocArray("ABCD", true.repeat);
688 static assert(is(typeof(c) == bool[achar]));
689 bool[achar] expected = ['D':true, 'A':true, 'B':true, 'C':true];
690 assert(c == expected);
691 }
692
693 // Cannot be version (StdUnittest) - recursive instantiation error
694 // https://issues.dlang.org/show_bug.cgi?id=11053
695 @safe pure nothrow unittest
696 {
697 import std.typecons;
698 static assert(!__traits(compiles, [ 1, 2, 3 ].assocArray()));
699 static assert(!__traits(compiles, [ tuple("foo", "bar", "baz") ].assocArray()));
700 static assert(!__traits(compiles, [ tuple("foo") ].assocArray()));
701 assert([ tuple("foo", "bar") ].assocArray() == ["foo": "bar"]);
702 }
703
704 // https://issues.dlang.org/show_bug.cgi?id=13909
705 @safe pure nothrow unittest
706 {
707 import std.typecons;
708 auto a = [tuple!(const string, string)("foo", "bar")];
709 auto b = [tuple!(string, const string)("foo", "bar")];
710 assert(a == b);
711 assert(assocArray(a) == [cast(const(string)) "foo": "bar"]);
712 static assert(!__traits(compiles, assocArray(b)));
713 }
714
715 // https://issues.dlang.org/show_bug.cgi?id=5502
716 @safe pure nothrow unittest
717 {
718 auto a = assocArray([0, 1, 2], ["a", "b", "c"]);
719 static assert(is(typeof(a) == string[int]));
720 assert(a == [0:"a", 1:"b", 2:"c"]);
721
722 auto b = assocArray([0, 1, 2], [3L, 4, 5]);
723 static assert(is(typeof(b) == long[int]));
724 assert(b == [0: 3L, 1: 4, 2: 5]);
725 }
726
727 // https://issues.dlang.org/show_bug.cgi?id=5502
728 @safe pure unittest
729 {
730 import std.algorithm.iteration : filter, map;
731 import std.range : enumerate;
732 import std.range.primitives : autodecodeStrings;
733
734 auto r = "abcde".enumerate.filter!(a => a.index == 2);
735 auto a = assocArray(r.map!(a => a.value), r.map!(a => a.index));
736 static if (autodecodeStrings)
737 alias achar = dchar;
738 else
739 alias achar = immutable(char);
740 static assert(is(typeof(a) == size_t[achar]));
741 assert(a == [achar('c'): size_t(2)]);
742 }
743
744 @safe nothrow pure unittest
745 {
746 import std.range : iota;
747 auto b = assocArray(3.iota, 3.iota(6));
748 static assert(is(typeof(b) == int[int]));
749 assert(b == [0: 3, 1: 4, 2: 5]);
750
751 b = assocArray([0, 1, 2], [3, 4, 5]);
752 assert(b == [0: 3, 1: 4, 2: 5]);
753 }
754
755 @safe unittest
756 {
757 struct ThrowingElement
758 {
759 int i;
760 static bool b;
~thisThrowingElement761 ~this(){
762 if (b)
763 throw new Exception("");
764 }
765 }
766 static assert(!__traits(compiles, () nothrow { assocArray([ThrowingElement()], [0]);}));
767 assert(assocArray([ThrowingElement()], [0]) == [ThrowingElement(): 0]);
768
769 static assert(!__traits(compiles, () nothrow { assocArray([0], [ThrowingElement()]);}));
770 assert(assocArray([0], [ThrowingElement()]) == [0: ThrowingElement()]);
771
772 import std.range : iota;
773 static assert(!__traits(compiles, () nothrow { assocArray(1.iota, [ThrowingElement()]);}));
774 assert(assocArray(1.iota, [ThrowingElement()]) == [0: ThrowingElement()]);
775 }
776
777 @system unittest
778 {
779 import std.range : iota;
780 struct UnsafeElement
781 {
782 int i;
783 static bool b;
~thisUnsafeElement784 ~this(){
785 int[] arr;
786 void* p = arr.ptr + 1; // unsafe
787 }
788 }
789 static assert(!__traits(compiles, () @safe { assocArray(1.iota, [UnsafeElement()]);}));
790 assert(assocArray(1.iota, [UnsafeElement()]) == [0: UnsafeElement()]);
791 }
792
793 /**
794 Construct a range iterating over an associative array by key/value tuples.
795
796 Params:
797 aa = The associative array to iterate over.
798
799 Returns: A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
800 of Tuple's of key and value pairs from the given associative array. The members
801 of each pair can be accessed by name (`.key` and `.value`). or by integer
802 index (0 and 1 respectively).
803 */
804 auto byPair(AA)(AA aa)
805 if (isAssociativeArray!AA)
806 {
807 import std.algorithm.iteration : map;
808 import std.typecons : tuple;
809
810 return aa.byKeyValue
811 .map!(pair => tuple!("key", "value")(pair.key, pair.value));
812 }
813
814 ///
815 @safe pure nothrow unittest
816 {
817 import std.algorithm.sorting : sort;
818 import std.typecons : tuple, Tuple;
819
820 auto aa = ["a": 1, "b": 2, "c": 3];
821 Tuple!(string, int)[] pairs;
822
823 // Iteration over key/value pairs.
824 foreach (pair; aa.byPair)
825 {
826 if (pair.key == "b")
827 pairs ~= tuple("B", pair.value);
828 else
829 pairs ~= pair;
830 }
831
832 // Iteration order is implementation-dependent, so we should sort it to get
833 // a fixed order.
834 pairs.sort();
835 assert(pairs == [
836 tuple("B", 2),
837 tuple("a", 1),
838 tuple("c", 3)
839 ]);
840 }
841
842 @safe pure nothrow unittest
843 {
844 import std.typecons : tuple, Tuple;
845 import std.meta : AliasSeq;
846
847 auto aa = ["a":2];
848 auto pairs = aa.byPair();
849
850 alias PT = typeof(pairs.front);
851 static assert(is(PT : Tuple!(string,int)));
852 static assert(PT.fieldNames == AliasSeq!("key", "value"));
853 static assert(isForwardRange!(typeof(pairs)));
854
855 assert(!pairs.empty);
856 assert(pairs.front == tuple("a", 2));
857
858 auto savedPairs = pairs.save;
859
860 pairs.popFront();
861 assert(pairs.empty);
862 assert(!savedPairs.empty);
863 assert(savedPairs.front == tuple("a", 2));
864 }
865
866 // https://issues.dlang.org/show_bug.cgi?id=17711
867 @safe pure nothrow unittest
868 {
869 const(int[string]) aa = [ "abc": 123 ];
870
871 // Ensure that byKeyValue is usable with a const AA.
872 auto kv = aa.byKeyValue;
873 assert(!kv.empty);
874 assert(kv.front.key == "abc" && kv.front.value == 123);
875 kv.popFront();
876 assert(kv.empty);
877
878 // Ensure byPair is instantiable with const AA.
879 auto r = aa.byPair;
880 static assert(isInputRange!(typeof(r)));
881 assert(!r.empty && r.front[0] == "abc" && r.front[1] == 123);
882 r.popFront();
883 assert(r.empty);
884 }
885
blockAttribute(T)886 private template blockAttribute(T)
887 {
888 import core.memory;
889 static if (hasIndirections!(T) || is(T == void))
890 {
891 enum blockAttribute = 0;
892 }
893 else
894 {
895 enum blockAttribute = GC.BlkAttr.NO_SCAN;
896 }
897 }
898
899 @safe unittest
900 {
901 import core.memory : UGC = GC;
902 static assert(!(blockAttribute!void & UGC.BlkAttr.NO_SCAN));
903 }
904
905 // Returns the number of dimensions in an array T.
nDimensions(T)906 private template nDimensions(T)
907 {
908 static if (isArray!T)
909 {
910 enum nDimensions = 1 + nDimensions!(typeof(T.init[0]));
911 }
912 else
913 {
914 enum nDimensions = 0;
915 }
916 }
917
918 @safe unittest
919 {
920 static assert(nDimensions!(uint[]) == 1);
921 static assert(nDimensions!(float[][]) == 2);
922 }
923
924 /++
925 Returns a new array of type `T` allocated on the garbage collected heap
926 without initializing its elements. This can be a useful optimization if every
927 element will be immediately initialized. `T` may be a multidimensional
928 array. In this case sizes may be specified for any number of dimensions from 0
929 to the number in `T`.
930
931 uninitializedArray is `nothrow` and weakly `pure`.
932
933 uninitializedArray is `@system` if the uninitialized element type has pointers.
934
935 Params:
936 T = The type of the resulting array elements
937 sizes = The length dimension(s) of the resulting array
938 Returns:
939 An array of `T` with `I.length` dimensions.
940 +/
941 auto uninitializedArray(T, I...)(I sizes) nothrow @system
942 if (isDynamicArray!T && allSatisfy!(isIntegral, I) && hasIndirections!(ElementEncodingType!T))
943 {
944 enum isSize_t(E) = is (E : size_t);
945 alias toSize_t(E) = size_t;
946
947 static assert(allSatisfy!(isSize_t, I),
948 "Argument types in "~I.stringof~" are not all convertible to size_t: "
949 ~Filter!(templateNot!(isSize_t), I).stringof);
950
951 //Eagerlly transform non-size_t into size_t to avoid template bloat
952 alias ST = staticMap!(toSize_t, I);
953
954 return arrayAllocImpl!(false, T, ST)(sizes);
955 }
956
957 /// ditto
958 auto uninitializedArray(T, I...)(I sizes) nothrow @trusted
959 if (isDynamicArray!T && allSatisfy!(isIntegral, I) && !hasIndirections!(ElementEncodingType!T))
960 {
961 enum isSize_t(E) = is (E : size_t);
962 alias toSize_t(E) = size_t;
963
964 static assert(allSatisfy!(isSize_t, I),
965 "Argument types in "~I.stringof~" are not all convertible to size_t: "
966 ~Filter!(templateNot!(isSize_t), I).stringof);
967
968 //Eagerlly transform non-size_t into size_t to avoid template bloat
969 alias ST = staticMap!(toSize_t, I);
970
971 return arrayAllocImpl!(false, T, ST)(sizes);
972 }
973 ///
974 @system nothrow pure unittest
975 {
976 double[] arr = uninitializedArray!(double[])(100);
977 assert(arr.length == 100);
978
979 double[][] matrix = uninitializedArray!(double[][])(42, 31);
980 assert(matrix.length == 42);
981 assert(matrix[0].length == 31);
982
983 char*[] ptrs = uninitializedArray!(char*[])(100);
984 assert(ptrs.length == 100);
985 }
986
987 /++
988 Returns a new array of type `T` allocated on the garbage collected heap.
989
990 Partial initialization is done for types with indirections, for preservation
991 of memory safety. Note that elements will only be initialized to 0, but not
992 necessarily the element type's `.init`.
993
994 minimallyInitializedArray is `nothrow` and weakly `pure`.
995
996 Params:
997 T = The type of the array elements
998 sizes = The length dimension(s) of the resulting array
999 Returns:
1000 An array of `T` with `I.length` dimensions.
1001 +/
1002 auto minimallyInitializedArray(T, I...)(I sizes) nothrow @trusted
1003 if (isDynamicArray!T && allSatisfy!(isIntegral, I))
1004 {
1005 enum isSize_t(E) = is (E : size_t);
1006 alias toSize_t(E) = size_t;
1007
1008 static assert(allSatisfy!(isSize_t, I),
1009 "Argument types in "~I.stringof~" are not all convertible to size_t: "
1010 ~Filter!(templateNot!(isSize_t), I).stringof);
1011 //Eagerlly transform non-size_t into size_t to avoid template bloat
1012 alias ST = staticMap!(toSize_t, I);
1013
1014 return arrayAllocImpl!(true, T, ST)(sizes);
1015 }
1016
1017 ///
1018 @safe pure nothrow unittest
1019 {
1020 import std.algorithm.comparison : equal;
1021 import std.range : repeat;
1022
1023 auto arr = minimallyInitializedArray!(int[])(42);
1024 assert(arr.length == 42);
1025
1026 // Elements aren't necessarily initialized to 0, so don't do this:
1027 // assert(arr.equal(0.repeat(42)));
1028 // If that is needed, initialize the array normally instead:
1029 auto arr2 = new int[42];
1030 assert(arr2.equal(0.repeat(42)));
1031 }
1032
1033 @safe pure nothrow unittest
1034 {
1035 cast(void) minimallyInitializedArray!(int[][][][][])();
1036 double[] arr = minimallyInitializedArray!(double[])(100);
1037 assert(arr.length == 100);
1038
1039 double[][] matrix = minimallyInitializedArray!(double[][])(42);
1040 assert(matrix.length == 42);
foreach(elem;matrix)1041 foreach (elem; matrix)
1042 {
1043 assert(elem.ptr is null);
1044 }
1045 }
1046
1047 // from rt/lifetime.d
1048 private extern(C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow;
1049
1050 // from rt/tracegc.d
1051 version (D_ProfileGC)
1052 private extern (C) void[] _d_newarrayUTrace(string file, size_t line,
1053 string funcname, const scope TypeInfo ti, size_t length) pure nothrow;
1054
1055 private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
1056 {
1057 static assert(I.length <= nDimensions!T,
1058 I.length.stringof~"dimensions specified for a "~nDimensions!T.stringof~" dimensional array.");
1059
1060 alias E = ElementEncodingType!T;
1061
1062 E[] ret;
1063
1064 static if (I.length != 0)
1065 {
1066 static assert(is(I[0] == size_t), "I[0] must be of type size_t not "
1067 ~ I[0].stringof);
1068 alias size = sizes[0];
1069 }
1070
1071 static if (I.length == 1)
1072 {
1073 if (__ctfe)
1074 {
1075 static if (__traits(compiles, new E[](size)))
1076 ret = new E[](size);
1077 else static if (__traits(compiles, ret ~= E.init))
1078 {
1079 try
1080 {
1081 //Issue: if E has an impure postblit, then all of arrayAllocImpl
1082 //Will be impure, even during non CTFE.
1083 foreach (i; 0 .. size)
1084 ret ~= E.init;
1085 }
1086 catch (Exception e)
1087 assert(0, e.msg);
1088 }
1089 else
1090 assert(0, "No postblit nor default init on " ~ E.stringof ~
1091 ": At least one is required for CTFE.");
1092 }
1093 else
1094 {
1095 import core.stdc.string : memset;
1096
1097 /+
1098 NOTES:
1099 _d_newarrayU is part of druntime, and creates an uninitialized
1100 block, just like GC.malloc. However, it also sets the appropriate
1101 bits, and sets up the block as an appendable array of type E[],
1102 which will inform the GC how to destroy the items in the block
1103 when it gets collected.
1104
1105 _d_newarrayU returns a void[], but with the length set according
1106 to E.sizeof.
1107 +/
1108 version (D_ProfileGC)
1109 {
1110 // FIXME: file, line, function should be propagated from the
1111 // caller, not here.
1112 *(cast(void[]*)&ret) = _d_newarrayUTrace(__FILE__, __LINE__,
1113 __FUNCTION__, typeid(E[]), size);
1114 }
1115 else
1116 *(cast(void[]*)&ret) = _d_newarrayU(typeid(E[]), size);
1117 static if (minimallyInitialized && hasIndirections!E)
1118 // _d_newarrayU would have asserted if the multiplication below
1119 // had overflowed, so we don't have to check it again.
1120 memset(ret.ptr, 0, E.sizeof * ret.length);
1121 }
1122 }
1123 else static if (I.length > 1)
1124 {
1125 ret = arrayAllocImpl!(false, E[])(size);
1126 foreach (ref elem; ret)
1127 elem = arrayAllocImpl!(minimallyInitialized, E)(sizes[1..$]);
1128 }
1129
1130 return ret;
1131 }
1132
1133 @safe nothrow pure unittest
1134 {
1135 auto s1 = uninitializedArray!(int[])();
1136 auto s2 = minimallyInitializedArray!(int[])();
1137 assert(s1.length == 0);
1138 assert(s2.length == 0);
1139 }
1140
1141 // https://issues.dlang.org/show_bug.cgi?id=9803
1142 @safe nothrow pure unittest
1143 {
1144 auto a = minimallyInitializedArray!(int*[])(1);
1145 assert(a[0] == null);
1146 auto b = minimallyInitializedArray!(int[][])(1);
1147 assert(b[0].empty);
1148 auto c = minimallyInitializedArray!(int*[][])(1, 1);
1149 assert(c[0][0] == null);
1150 }
1151
1152 // https://issues.dlang.org/show_bug.cgi?id=10637
1153 @safe pure nothrow unittest
1154 {
1155 static struct S
1156 {
1157 static struct I{int i; alias i this;}
1158 int* p;
1159 this() @disable;
thisS1160 this(int i)
1161 {
1162 p = &(new I(i)).i;
1163 }
thisS1164 this(this)
1165 {
1166 p = &(new I(*p)).i;
1167 }
~thisS1168 ~this()
1169 {
1170 // note, this assert is invalid -- a struct should always be able
1171 // to run its dtor on the .init value, I'm leaving it here
1172 // commented out because the original test case had it. I'm not
1173 // sure what it's trying to prove.
1174 //
1175 // What happens now that minimallyInitializedArray adds the
1176 // destructor run to the GC, is that this assert would fire in the
1177 // GC, which triggers an invalid memory operation.
1178 //assert(p != null);
1179 }
1180 }
1181 auto a = minimallyInitializedArray!(S[])(1);
1182 assert(a[0].p == null);
1183 enum b = minimallyInitializedArray!(S[])(1);
1184 assert(b[0].p == null);
1185 }
1186
1187 @safe pure nothrow unittest
1188 {
1189 static struct S1
1190 {
1191 this() @disable;
1192 this(this) @disable;
1193 }
1194 auto a1 = minimallyInitializedArray!(S1[][])(2, 2);
1195 assert(a1);
1196 static struct S2
1197 {
1198 this() @disable;
1199 //this(this) @disable;
1200 }
1201 auto a2 = minimallyInitializedArray!(S2[][])(2, 2);
1202 assert(a2);
1203 enum b2 = minimallyInitializedArray!(S2[][])(2, 2);
1204 assert(b2);
1205 static struct S3
1206 {
1207 //this() @disable;
1208 this(this) @disable;
1209 }
1210 auto a3 = minimallyInitializedArray!(S3[][])(2, 2);
1211 assert(a3);
1212 enum b3 = minimallyInitializedArray!(S3[][])(2, 2);
1213 assert(b3);
1214 }
1215
1216 /++
1217 Returns the overlapping portion, if any, of two arrays. Unlike `equal`,
1218 `overlap` only compares the pointers and lengths in the
1219 ranges, not the values referred by them. If `r1` and `r2` have an
1220 overlapping slice, returns that slice. Otherwise, returns the null
1221 slice.
1222
1223 Params:
1224 a = The first array to compare
1225 b = The second array to compare
1226 Returns:
1227 The overlapping portion of the two arrays.
1228 +/
1229 CommonType!(T[], U[]) overlap(T, U)(T[] a, U[] b) @trusted
1230 if (is(typeof(a.ptr < b.ptr) == bool))
1231 {
1232 import std.algorithm.comparison : min;
1233
1234 auto end = min(a.ptr + a.length, b.ptr + b.length);
1235 // CTFE requires pairing pointer comparisons, which forces a
1236 // slightly inefficient implementation.
1237 if (a.ptr <= b.ptr && b.ptr < a.ptr + a.length)
1238 {
1239 return b.ptr[0 .. end - b.ptr];
1240 }
1241
1242 if (b.ptr <= a.ptr && a.ptr < b.ptr + b.length)
1243 {
1244 return a.ptr[0 .. end - a.ptr];
1245 }
1246
1247 return null;
1248 }
1249
1250 ///
1251 @safe pure nothrow unittest
1252 {
1253 int[] a = [ 10, 11, 12, 13, 14 ];
1254 int[] b = a[1 .. 3];
1255 assert(overlap(a, b) == [ 11, 12 ]);
1256 b = b.dup;
1257 // overlap disappears even though the content is the same
1258 assert(overlap(a, b).empty);
1259
test()1260 static test()() @nogc
1261 {
1262 auto a = "It's three o'clock"d;
1263 auto b = a[5 .. 10];
1264 return b.overlap(a);
1265 }
1266
1267 //works at compile-time
1268 static assert(test == "three"d);
1269 }
1270
1271 @safe pure nothrow unittest
1272 {
test(L,R)1273 static void test(L, R)(L l, R r)
1274 {
1275 assert(overlap(l, r) == [ 100, 12 ]);
1276
1277 assert(overlap(l, l[0 .. 2]) is l[0 .. 2]);
1278 assert(overlap(l, l[3 .. 5]) is l[3 .. 5]);
1279 assert(overlap(l[0 .. 2], l) is l[0 .. 2]);
1280 assert(overlap(l[3 .. 5], l) is l[3 .. 5]);
1281 }
1282
1283 int[] a = [ 10, 11, 12, 13, 14 ];
1284 int[] b = a[1 .. 3];
1285 a[1] = 100;
1286
1287 immutable int[] c = a.idup;
1288 immutable int[] d = c[1 .. 3];
1289
1290 test(a, b);
1291 assert(overlap(a, b.dup).empty);
1292 test(c, d);
1293 assert(overlap(c, d.dup.idup).empty);
1294 }
1295
1296 // https://issues.dlang.org/show_bug.cgi?id=9836
1297 @safe pure nothrow unittest
1298 {
1299 // range primitives for array should work with alias this types
1300 struct Wrapper
1301 {
1302 int[] data;
1303 alias data this;
1304
saveWrapper1305 @property Wrapper save() { return this; }
1306 }
1307 auto w = Wrapper([1,2,3,4]);
1308 std.array.popFront(w); // should work
1309
1310 static assert(isInputRange!Wrapper);
1311 static assert(isForwardRange!Wrapper);
1312 static assert(isBidirectionalRange!Wrapper);
1313 static assert(isRandomAccessRange!Wrapper);
1314 }
1315
copyBackwards(T)1316 private void copyBackwards(T)(T[] src, T[] dest)
1317 {
1318 import core.stdc.string : memmove;
1319 import std.format : format;
1320
1321 assert(src.length == dest.length, format!
1322 "src.length %s must equal dest.length %s"(src.length, dest.length));
1323
1324 if (!__ctfe || hasElaborateCopyConstructor!T)
1325 {
1326 /* insertInPlace relies on dest being uninitialized, so no postblits allowed,
1327 * as this is a MOVE that overwrites the destination, not a COPY.
1328 * BUG: insertInPlace will not work with ctfe and postblits
1329 */
1330 memmove(dest.ptr, src.ptr, src.length * T.sizeof);
1331 }
1332 else
1333 {
1334 immutable len = src.length;
1335 for (size_t i = len; i-- > 0;)
1336 {
1337 dest[i] = src[i];
1338 }
1339 }
1340 }
1341
1342 /++
1343 Inserts `stuff` (which must be an input range or any number of
1344 implicitly convertible items) in `array` at position `pos`.
1345
1346 Params:
1347 array = The array that `stuff` will be inserted into.
1348 pos = The position in `array` to insert the `stuff`.
1349 stuff = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives),
1350 or any number of implicitly convertible items to insert into `array`.
1351 +/
1352 void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
1353 if (!isSomeString!(T[])
1354 && allSatisfy!(isInputRangeOrConvertible!T, U) && U.length > 0)
1355 {
1356 static if (allSatisfy!(isInputRangeWithLengthOrConvertible!T, U))
1357 {
1358 import core.internal.lifetime : emplaceRef;
1359
1360 immutable oldLen = array.length;
1361
1362 size_t to_insert = 0;
foreach(i,E;U)1363 foreach (i, E; U)
1364 {
1365 static if (is(E : T)) //a single convertible value, not a range
1366 to_insert += 1;
1367 else
1368 to_insert += stuff[i].length;
1369 }
1370 if (to_insert)
1371 {
1372 array.length += to_insert;
1373
1374 // Takes arguments array, pos, stuff
1375 // Spread apart array[] at pos by moving elements
1376 (() @trusted { copyBackwards(array[pos .. oldLen], array[pos+to_insert..$]); })();
1377
1378 // Initialize array[pos .. pos+to_insert] with stuff[]
1379 auto j = 0;
foreach(i,E;U)1380 foreach (i, E; U)
1381 {
1382 static if (is(E : T))
1383 {
1384 emplaceRef!T(array[pos + j++], stuff[i]);
1385 }
1386 else
1387 {
1388 foreach (v; stuff[i])
1389 {
1390 emplaceRef!T(array[pos + j++], v);
1391 }
1392 }
1393 }
1394 }
1395 }
1396 else
1397 {
1398 // stuff has some InputRanges in it that don't have length
1399 // assume that stuff to be inserted is typically shorter
1400 // then the array that can be arbitrary big
1401 // TODO: needs a better implementation as there is no need to build an _array_
1402 // a singly-linked list of memory blocks (rope, etc.) will do
1403 auto app = appender!(T[])();
1404 foreach (i, E; U)
1405 app.put(stuff[i]);
1406 insertInPlace(array, pos, app.data);
1407 }
1408 }
1409
1410 /// Ditto
1411 void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
1412 if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U))
1413 {
1414 static if (is(Unqual!T == T)
1415 && allSatisfy!(isInputRangeWithLengthOrConvertible!dchar, U))
1416 {
1417 import std.utf : codeLength, byDchar;
1418 // mutable, can do in place
1419 //helper function: re-encode dchar to Ts and store at *ptr
putDChar(T * ptr,dchar ch)1420 static T* putDChar(T* ptr, dchar ch)
1421 {
1422 static if (is(T == dchar))
1423 {
1424 *ptr++ = ch;
1425 return ptr;
1426 }
1427 else
1428 {
1429 import std.utf : encode;
1430 T[dchar.sizeof/T.sizeof] buf;
1431 immutable len = encode(buf, ch);
1432 final switch (len)
1433 {
1434 static if (T.sizeof == char.sizeof)
1435 {
1436 case 4:
1437 ptr[3] = buf[3];
1438 goto case;
1439 case 3:
1440 ptr[2] = buf[2];
1441 goto case;
1442 }
1443 case 2:
1444 ptr[1] = buf[1];
1445 goto case;
1446 case 1:
1447 ptr[0] = buf[0];
1448 }
1449 ptr += len;
1450 return ptr;
1451 }
1452 }
1453 size_t to_insert = 0;
1454 //count up the number of *codeunits* to insert
1455 foreach (i, E; U)
1456 to_insert += codeLength!T(stuff[i]);
1457 array.length += to_insert;
1458
moveToRight(T[]arr,size_t gap)1459 @trusted static void moveToRight(T[] arr, size_t gap)
1460 {
1461 static assert(!hasElaborateCopyConstructor!T,
1462 "T must not have an elaborate copy constructor");
1463 import core.stdc.string : memmove;
1464 if (__ctfe)
1465 {
1466 for (size_t i = arr.length - gap; i; --i)
1467 arr[gap + i - 1] = arr[i - 1];
1468 }
1469 else
1470 memmove(arr.ptr + gap, arr.ptr, (arr.length - gap) * T.sizeof);
1471 }
1472 moveToRight(array[pos .. $], to_insert);
1473 auto ptr = array.ptr + pos;
foreach(i,E;U)1474 foreach (i, E; U)
1475 {
1476 static if (is(E : dchar))
1477 {
1478 ptr = putDChar(ptr, stuff[i]);
1479 }
1480 else
1481 {
1482 foreach (ch; stuff[i].byDchar)
1483 ptr = putDChar(ptr, ch);
1484 }
1485 }
1486 assert(ptr == array.ptr + pos + to_insert, "(ptr == array.ptr + pos + to_insert) is false");
1487 }
1488 else
1489 {
1490 // immutable/const, just construct a new array
1491 auto app = appender!(T[])();
1492 app.put(array[0 .. pos]);
1493 foreach (i, E; U)
1494 app.put(stuff[i]);
1495 app.put(array[pos..$]);
1496 array = app.data;
1497 }
1498 }
1499
1500 ///
1501 @safe pure unittest
1502 {
1503 int[] a = [ 1, 2, 3, 4 ];
1504 a.insertInPlace(2, [ 1, 2 ]);
1505 assert(a == [ 1, 2, 1, 2, 3, 4 ]);
1506 a.insertInPlace(3, 10u, 11);
1507 assert(a == [ 1, 2, 1, 10, 11, 2, 3, 4]);
1508
1509 union U
1510 {
1511 float a = 3.0;
1512 int b;
1513 }
1514
1515 U u1 = { b : 3 };
1516 U u2 = { b : 4 };
1517 U u3 = { b : 5 };
1518 U[] unionArr = [u2, u3];
1519 unionArr.insertInPlace(2, [u1]);
1520 assert(unionArr == [u2, u3, u1]);
1521 unionArr.insertInPlace(0, [u3, u2]);
1522 assert(unionArr == [u3, u2, u2, u3, u1]);
1523
1524 static class C
1525 {
1526 int a;
1527 float b;
1528
this(int a,float b)1529 this(int a, float b) { this.a = a; this.b = b; }
1530 }
1531
1532 C c1 = new C(42, 1.0);
1533 C c2 = new C(0, 0.0);
1534 C c3 = new C(int.max, float.init);
1535
1536 C[] classArr = [c1, c2, c3];
1537 insertInPlace(classArr, 3, [c2, c3]);
1538 C[5] classArr1 = classArr;
1539 assert(classArr1 == [c1, c2, c3, c2, c3]);
1540 insertInPlace(classArr, 0, c3, c1);
1541 C[7] classArr2 = classArr;
1542 assert(classArr2 == [c3, c1, c1, c2, c3, c2, c3]);
1543 }
1544
1545 //constraint helpers
isInputRangeWithLengthOrConvertible(E)1546 private template isInputRangeWithLengthOrConvertible(E)
1547 {
1548 template isInputRangeWithLengthOrConvertible(R)
1549 {
1550 //hasLength not defined for char[], wchar[] and dchar[]
1551 enum isInputRangeWithLengthOrConvertible =
1552 (isInputRange!R && is(typeof(R.init.length))
1553 && is(ElementType!R : E)) || is(R : E);
1554 }
1555 }
1556
1557 //ditto
isCharOrStringOrDcharRange(T)1558 private template isCharOrStringOrDcharRange(T)
1559 {
1560 enum isCharOrStringOrDcharRange = isSomeString!T || isSomeChar!T ||
1561 (isInputRange!T && is(ElementType!T : dchar));
1562 }
1563
1564 //ditto
isInputRangeOrConvertible(E)1565 private template isInputRangeOrConvertible(E)
1566 {
1567 template isInputRangeOrConvertible(R)
1568 {
1569 enum isInputRangeOrConvertible =
1570 (isInputRange!R && is(ElementType!R : E)) || is(R : E);
1571 }
1572 }
1573
1574 @system unittest
1575 {
1576 // @system due to insertInPlace
1577 import core.exception;
1578 import std.algorithm.comparison : equal;
1579 import std.algorithm.iteration : filter;
1580 import std.conv : to;
1581 import std.exception;
1582
1583
test(T,U,V)1584 bool test(T, U, V)(T orig, size_t pos, U toInsert, V result)
1585 {
1586 {
1587 static if (is(T == typeof(T.init.dup)))
1588 auto a = orig.dup;
1589 else
1590 auto a = orig.idup;
1591
1592 a.insertInPlace(pos, toInsert);
1593 if (!equal(a, result))
1594 return false;
1595 }
1596
1597 static if (isInputRange!U)
1598 {
1599 orig.insertInPlace(pos, filter!"true"(toInsert));
1600 return equal(orig, result);
1601 }
1602 else
1603 return true;
1604 }
1605
1606
1607 assert(test([1, 2, 3, 4], 0, [6, 7], [6, 7, 1, 2, 3, 4]));
1608 assert(test([1, 2, 3, 4], 2, [8, 9], [1, 2, 8, 9, 3, 4]));
1609 assert(test([1, 2, 3, 4], 4, [10, 11], [1, 2, 3, 4, 10, 11]));
1610
1611 assert(test([1, 2, 3, 4], 0, 22, [22, 1, 2, 3, 4]));
1612 assert(test([1, 2, 3, 4], 2, 23, [1, 2, 23, 3, 4]));
1613 assert(test([1, 2, 3, 4], 4, 24, [1, 2, 3, 4, 24]));
1614
testStr(T,U)1615 void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
1616 {
1617
1618 auto l = to!T("hello");
1619 auto r = to!U(" વિશ્વ");
1620
1621 enforce(test(l, 0, r, " વિશ્વhello"),
1622 new AssertError("testStr failure 1", file, line));
1623 enforce(test(l, 3, r, "hel વિશ્વlo"),
1624 new AssertError("testStr failure 2", file, line));
1625 enforce(test(l, l.length, r, "hello વિશ્વ"),
1626 new AssertError("testStr failure 3", file, line));
1627 }
1628
1629 static foreach (T; AliasSeq!(char, wchar, dchar,
1630 immutable(char), immutable(wchar), immutable(dchar)))
1631 {
1632 static foreach (U; AliasSeq!(char, wchar, dchar,
1633 immutable(char), immutable(wchar), immutable(dchar)))
1634 {
1635 testStr!(T[], U[])();
1636 }
1637
1638 }
1639
1640 // variadic version
testVar(T,U...)1641 bool testVar(T, U...)(T orig, size_t pos, U args)
1642 {
1643 static if (is(T == typeof(T.init.dup)))
1644 auto a = orig.dup;
1645 else
1646 auto a = orig.idup;
1647 auto result = args[$-1];
1648
1649 a.insertInPlace(pos, args[0..$-1]);
1650 if (!equal(a, result))
1651 return false;
1652 return true;
1653 }
1654 assert(testVar([1, 2, 3, 4], 0, 6, 7u, [6, 7, 1, 2, 3, 4]));
1655 assert(testVar([1L, 2, 3, 4], 2, 8, 9L, [1, 2, 8, 9, 3, 4]));
1656 assert(testVar([1L, 2, 3, 4], 4, 10L, 11, [1, 2, 3, 4, 10, 11]));
1657 assert(testVar([1L, 2, 3, 4], 4, [10, 11], 40L, 42L,
1658 [1, 2, 3, 4, 10, 11, 40, 42]));
1659 assert(testVar([1L, 2, 3, 4], 4, 10, 11, [40L, 42],
1660 [1, 2, 3, 4, 10, 11, 40, 42]));
1661 assert(testVar("t".idup, 1, 'e', 's', 't', "test"));
1662 assert(testVar("!!"w.idup, 1, "\u00e9ll\u00f4", 'x', "TTT"w, 'y',
1663 "!\u00e9ll\u00f4xTTTy!"));
1664 assert(testVar("flipflop"d.idup, 4, '_',
1665 "xyz"w, '\U00010143', '_', "abc"d, "__",
1666 "flip_xyz\U00010143_abc__flop"));
1667 }
1668
1669 @system unittest
1670 {
1671 import std.algorithm.comparison : equal;
1672 // insertInPlace interop with postblit
1673 static struct Int
1674 {
1675 int* payload;
thisInt1676 this(int k)
1677 {
1678 payload = new int;
1679 *payload = k;
1680 }
thisInt1681 this(this)
1682 {
1683 int* np = new int;
1684 *np = *payload;
1685 payload = np;
1686 }
~thisInt1687 ~this()
1688 {
1689 if (payload)
1690 *payload = 0; //'destroy' it
1691 }
getPayloadInt1692 @property int getPayload(){ return *payload; }
1693 alias getPayload this;
1694 }
1695
1696 Int[] arr = [Int(1), Int(4), Int(5)];
1697 assert(arr[0] == 1);
1698 insertInPlace(arr, 1, Int(2), Int(3));
1699 assert(equal(arr, [1, 2, 3, 4, 5])); //check it works with postblit
1700 }
1701
1702 @safe unittest
1703 {
1704 import std.exception;
1705 assertCTFEable!(
1706 {
1707 int[] a = [1, 2];
1708 a.insertInPlace(2, 3);
1709 a.insertInPlace(0, -1, 0);
1710 return a == [-1, 0, 1, 2, 3];
1711 });
1712 }
1713
1714 // https://issues.dlang.org/show_bug.cgi?id=6874
1715 @system unittest
1716 {
1717 import core.memory;
1718 // allocate some space
1719 byte[] a;
1720 a.length = 1;
1721
1722 // fill it
1723 a.length = a.capacity;
1724
1725 // write beyond
1726 byte[] b = a[$ .. $];
1727 b.insertInPlace(0, a);
1728
1729 // make sure that reallocation has happened
1730 assert(GC.addrOf(&b[0]) == GC.addrOf(&b[$-1]));
1731 }
1732
1733
1734 /++
1735 Returns whether the `front`s of `lhs` and `rhs` both refer to the
1736 same place in memory, making one of the arrays a slice of the other which
1737 starts at index `0`.
1738
1739 Params:
1740 lhs = the first array to compare
1741 rhs = the second array to compare
1742 Returns:
1743 `true` if $(D lhs.ptr == rhs.ptr), `false` otherwise.
1744 +/
1745 @safe
sameHead(T)1746 pure nothrow @nogc bool sameHead(T)(in T[] lhs, in T[] rhs)
1747 {
1748 return lhs.ptr == rhs.ptr;
1749 }
1750
1751 ///
1752 @safe pure nothrow unittest
1753 {
1754 auto a = [1, 2, 3, 4, 5];
1755 auto b = a[0 .. 2];
1756
1757 assert(a.sameHead(b));
1758 }
1759
1760
1761 /++
1762 Returns whether the `back`s of `lhs` and `rhs` both refer to the
1763 same place in memory, making one of the arrays a slice of the other which
1764 end at index `$`.
1765
1766 Params:
1767 lhs = the first array to compare
1768 rhs = the second array to compare
1769 Returns:
1770 `true` if both arrays are the same length and $(D lhs.ptr == rhs.ptr),
1771 `false` otherwise.
1772 +/
1773 @trusted
sameTail(T)1774 pure nothrow @nogc bool sameTail(T)(in T[] lhs, in T[] rhs)
1775 {
1776 return lhs.ptr + lhs.length == rhs.ptr + rhs.length;
1777 }
1778
1779 ///
1780 @safe pure nothrow unittest
1781 {
1782 auto a = [1, 2, 3, 4, 5];
1783 auto b = a[3..$];
1784
1785 assert(a.sameTail(b));
1786 }
1787
1788 @safe pure nothrow unittest
1789 {
1790 static foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
1791 {{
1792 T a = [1, 2, 3, 4, 5];
1793 T b = a;
1794 T c = a[1 .. $];
1795 T d = a[0 .. 1];
1796 T e = null;
1797
1798 assert(sameHead(a, a));
1799 assert(sameHead(a, b));
1800 assert(!sameHead(a, c));
1801 assert(sameHead(a, d));
1802 assert(!sameHead(a, e));
1803
1804 assert(sameTail(a, a));
1805 assert(sameTail(a, b));
1806 assert(sameTail(a, c));
1807 assert(!sameTail(a, d));
1808 assert(!sameTail(a, e));
1809
1810 //verifies R-value compatibilty
1811 assert(a.sameHead(a[0 .. 0]));
1812 assert(a.sameTail(a[$ .. $]));
1813 }}
1814 }
1815
1816 /**
1817 Params:
1818 s = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
1819 or a dynamic array
1820 n = number of times to repeat `s`
1821
1822 Returns:
1823 An array that consists of `s` repeated `n` times. This function allocates, fills, and
1824 returns a new array.
1825
1826 See_Also:
1827 For a lazy version, refer to $(REF repeat, std,range).
1828 */
1829 ElementEncodingType!S[] replicate(S)(S s, size_t n)
1830 if (isDynamicArray!S)
1831 {
1832 alias RetType = ElementEncodingType!S[];
1833
1834 // Optimization for return join(std.range.repeat(s, n));
1835 if (n == 0)
1836 return RetType.init;
1837 if (n == 1)
1838 return cast(RetType) s;
1839 auto r = new Unqual!(typeof(s[0]))[n * s.length];
1840 if (s.length == 1)
1841 r[] = s[0];
1842 else
1843 {
1844 immutable len = s.length, nlen = n * len;
1845 for (size_t i = 0; i < nlen; i += len)
1846 {
1847 r[i .. i + len] = s[];
1848 }
1849 }
1850 return r;
1851 }
1852
1853 /// ditto
1854 ElementType!S[] replicate(S)(S s, size_t n)
1855 if (isInputRange!S && !isDynamicArray!S)
1856 {
1857 import std.range : repeat;
1858 return join(std.range.repeat(s, n));
1859 }
1860
1861
1862 ///
1863 @safe unittest
1864 {
1865 auto a = "abc";
1866 auto s = replicate(a, 3);
1867
1868 assert(s == "abcabcabc");
1869
1870 auto b = [1, 2, 3];
1871 auto c = replicate(b, 3);
1872
1873 assert(c == [1, 2, 3, 1, 2, 3, 1, 2, 3]);
1874
1875 auto d = replicate(b, 0);
1876
1877 assert(d == []);
1878 }
1879
1880 @safe unittest
1881 {
1882 import std.conv : to;
1883
1884 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
1885 {{
1886 immutable S t = "abc";
1887
1888 assert(replicate(to!S("1234"), 0) is null);
1889 assert(replicate(to!S("1234"), 0) is null);
1890 assert(replicate(to!S("1234"), 1) == "1234");
1891 assert(replicate(to!S("1234"), 2) == "12341234");
1892 assert(replicate(to!S("1"), 4) == "1111");
1893 assert(replicate(t, 3) == "abcabcabc");
1894 assert(replicate(cast(S) null, 4) is null);
1895 }}
1896 }
1897
1898 /++
1899 Eagerly splits `range` into an array, using `sep` as the delimiter.
1900
1901 When no delimiter is provided, strings are split into an array of words,
1902 using whitespace as delimiter.
1903 Runs of whitespace are merged together (no empty words are produced).
1904
1905 The `range` must be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives).
1906 The separator can be a value of the same type as the elements in `range`
1907 or it can be another forward `range`.
1908
1909 Params:
1910 s = the string to split by word if no separator is given
1911 range = the range to split
1912 sep = a value of the same type as the elements of `range` or another
1913 isTerminator = a predicate that splits the range when it returns `true`.
1914
1915 Returns:
1916 An array containing the divided parts of `range` (or the words of `s`).
1917
1918 See_Also:
1919 $(REF splitter, std,algorithm,iteration) for a lazy version without allocating memory.
1920
1921 $(REF splitter, std,regex) for a version that splits using a regular
1922 expression defined separator.
1923 +/
1924 S[] split(S)(S s) @safe pure
1925 if (isSomeString!S)
1926 {
1927 size_t istart;
1928 bool inword = false;
1929 auto result = appender!(S[]);
1930
foreach(i,dchar c;s)1931 foreach (i, dchar c ; s)
1932 {
1933 import std.uni : isWhite;
1934 if (isWhite(c))
1935 {
1936 if (inword)
1937 {
1938 put(result, s[istart .. i]);
1939 inword = false;
1940 }
1941 }
1942 else
1943 {
1944 if (!inword)
1945 {
1946 istart = i;
1947 inword = true;
1948 }
1949 }
1950 }
1951 if (inword)
1952 put(result, s[istart .. $]);
1953 return result.data;
1954 }
1955
1956 ///
1957 @safe unittest
1958 {
1959 import std.uni : isWhite;
1960 assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]);
1961 assert("Learning D is fun".split!isWhite == ["Learning", "D", "is", "fun"]);
1962 assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]);
1963 }
1964
1965 ///
1966 @safe unittest
1967 {
1968 string str = "Hello World!";
1969 assert(str.split == ["Hello", "World!"]);
1970
1971 string str2 = "Hello\t\tWorld\t!";
1972 assert(str2.split == ["Hello", "World", "!"]);
1973 }
1974
1975 @safe unittest
1976 {
1977 import std.conv : to;
1978 import std.format : format;
1979 import std.typecons;
1980
makeEntry(S)1981 static auto makeEntry(S)(string l, string[] r)
1982 {return tuple(l.to!S(), r.to!(S[])());}
1983
1984 static foreach (S; AliasSeq!(string, wstring, dstring,))
1985 {{
1986 auto entries =
1987 [
1988 makeEntry!S("", []),
1989 makeEntry!S(" ", []),
1990 makeEntry!S("hello", ["hello"]),
1991 makeEntry!S(" hello ", ["hello"]),
1992 makeEntry!S(" h e l l o ", ["h", "e", "l", "l", "o"]),
1993 makeEntry!S("peter\t\npaul\rjerry", ["peter", "paul", "jerry"]),
1994 makeEntry!S(" \t\npeter paul\tjerry \n", ["peter", "paul", "jerry"]),
1995 makeEntry!S("\u2000日\u202F本\u205F語\u3000", ["日", "本", "語"]),
1996 makeEntry!S(" 哈・郎博尔德} ___一个", ["哈・郎博尔德}", "___一个"])
1997 ];
1998 foreach (entry; entries)
1999 assert(entry[0].split() == entry[1], format("got: %s, expected: %s.", entry[0].split(), entry[1]));
2000 }}
2001
2002 //Just to test that an immutable is split-able
2003 immutable string s = " \t\npeter paul\tjerry \n";
2004 assert(split(s) == ["peter", "paul", "jerry"]);
2005 }
2006
2007 @safe unittest //purity, ctfe ...
2008 {
2009 import std.exception;
dg()2010 void dg() @safe pure {
2011 assert(split("hello world"c) == ["hello"c, "world"c]);
2012 assert(split("hello world"w) == ["hello"w, "world"w]);
2013 assert(split("hello world"d) == ["hello"d, "world"d]);
2014 }
2015 dg();
2016 assertCTFEable!dg;
2017 }
2018
2019 ///
2020 @safe unittest
2021 {
2022 assert(split("hello world") == ["hello","world"]);
2023 assert(split("192.168.0.1", ".") == ["192", "168", "0", "1"]);
2024
2025 auto a = split([1, 2, 3, 4, 5, 1, 2, 3, 4, 5], [2, 3]);
2026 assert(a == [[1], [4, 5, 1], [4, 5]]);
2027 }
2028
2029 ///ditto
2030 auto split(Range, Separator)(Range range, Separator sep)
2031 if (isForwardRange!Range && (
2032 is(typeof(ElementType!Range.init == Separator.init)) ||
2033 is(typeof(ElementType!Range.init == ElementType!Separator.init)) && isForwardRange!Separator
2034 ))
2035 {
2036 import std.algorithm.iteration : splitter;
2037 return range.splitter(sep).array;
2038 }
2039 ///ditto
2040 auto split(alias isTerminator, Range)(Range range)
2041 if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(range.front))))
2042 {
2043 import std.algorithm.iteration : splitter;
2044 return range.splitter!isTerminator.array;
2045 }
2046
2047 @safe unittest
2048 {
2049 import std.algorithm.comparison : cmp;
2050 import std.conv;
2051
2052 static foreach (S; AliasSeq!(string, wstring, dstring,
2053 immutable(string), immutable(wstring), immutable(dstring),
2054 char[], wchar[], dchar[],
2055 const(char)[], const(wchar)[], const(dchar)[],
2056 const(char[]), immutable(char[])))
2057 {{
2058 S s = to!S(",peter,paul,jerry,");
2059
2060 auto words = split(s, ",");
2061 assert(words.length == 5, text(words.length));
2062 assert(cmp(words[0], "") == 0);
2063 assert(cmp(words[1], "peter") == 0);
2064 assert(cmp(words[2], "paul") == 0);
2065 assert(cmp(words[3], "jerry") == 0);
2066 assert(cmp(words[4], "") == 0);
2067
2068 auto s1 = s[0 .. s.length - 1]; // lop off trailing ','
2069 words = split(s1, ",");
2070 assert(words.length == 4);
2071 assert(cmp(words[3], "jerry") == 0);
2072
2073 auto s2 = s1[1 .. s1.length]; // lop off leading ','
2074 words = split(s2, ",");
2075 assert(words.length == 3);
2076 assert(cmp(words[0], "peter") == 0);
2077
2078 auto s3 = to!S(",,peter,,paul,,jerry,,");
2079
2080 words = split(s3, ",,");
2081 assert(words.length == 5);
2082 assert(cmp(words[0], "") == 0);
2083 assert(cmp(words[1], "peter") == 0);
2084 assert(cmp(words[2], "paul") == 0);
2085 assert(cmp(words[3], "jerry") == 0);
2086 assert(cmp(words[4], "") == 0);
2087
2088 auto s4 = s3[0 .. s3.length - 2]; // lop off trailing ',,'
2089 words = split(s4, ",,");
2090 assert(words.length == 4);
2091 assert(cmp(words[3], "jerry") == 0);
2092
2093 auto s5 = s4[2 .. s4.length]; // lop off leading ',,'
2094 words = split(s5, ",,");
2095 assert(words.length == 3);
2096 assert(cmp(words[0], "peter") == 0);
2097 }}
2098 }
2099
2100 /+
2101 Conservative heuristic to determine if a range can be iterated cheaply.
2102 Used by `join` in decision to do an extra iteration of the range to
2103 compute the resultant length. If iteration is not cheap then precomputing
2104 length could be more expensive than using `Appender`.
2105
2106 For now, we only assume arrays are cheap to iterate.
2107 +/
2108 private enum bool hasCheapIteration(R) = isArray!R;
2109
2110 /++
2111 Eagerly concatenates all of the ranges in `ror` together (with the GC)
2112 into one array using `sep` as the separator if present.
2113
2114 Params:
2115 ror = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2116 of input ranges
2117 sep = An input range, or a single element, to join the ranges on
2118
2119 Returns:
2120 An array of elements
2121
2122 See_Also:
2123 For a lazy version, see $(REF joiner, std,algorithm,iteration)
2124 +/
2125 ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep)
2126 if (isInputRange!RoR &&
2127 isInputRange!(Unqual!(ElementType!RoR)) &&
2128 isInputRange!R &&
2129 (is(immutable ElementType!(ElementType!RoR) == immutable ElementType!R) ||
2130 (isSomeChar!(ElementType!(ElementType!RoR)) && isSomeChar!(ElementType!R))
2131 ))
2132 {
2133 alias RetType = typeof(return);
2134 alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
2135 alias RoRElem = ElementType!RoR;
2136
2137 if (ror.empty)
2138 return RetType.init;
2139
2140 // Constraint only requires input range for sep.
2141 // This converts sep to an array (forward range) if it isn't one,
2142 // and makes sure it has the same string encoding for string types.
2143 static if (isSomeString!RetType &&
2144 !is(immutable ElementEncodingType!RetType == immutable ElementEncodingType!R))
2145 {
2146 import std.conv : to;
2147 auto sepArr = to!RetType(sep);
2148 }
2149 else static if (!isArray!R)
2150 auto sepArr = array(sep);
2151 else
2152 alias sepArr = sep;
2153
2154 static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
2155 {
2156 import core.internal.lifetime : emplaceRef;
2157 size_t length; // length of result array
2158 size_t rorLength; // length of range ror
2159 foreach (r; ror.save)
2160 {
2161 length += r.length;
2162 ++rorLength;
2163 }
2164 if (!rorLength)
2165 return null;
2166 length += (rorLength - 1) * sepArr.length;
2167
2168 auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))();
2169 size_t len;
2170 foreach (e; ror.front)
2171 emplaceRef(result[len++], e);
2172 ror.popFront();
foreach(r;ror)2173 foreach (r; ror)
2174 {
2175 foreach (e; sepArr)
2176 emplaceRef(result[len++], e);
2177 foreach (e; r)
2178 emplaceRef(result[len++], e);
2179 }
2180 assert(len == result.length);
2181 return (() @trusted => cast(RetType) result)();
2182 }
2183 else
2184 {
2185 auto result = appender!RetType();
2186 put(result, ror.front);
2187 ror.popFront();
2188 for (; !ror.empty; ror.popFront())
2189 {
2190 put(result, sepArr);
2191 put(result, ror.front);
2192 }
2193 return result.data;
2194 }
2195 }
2196
2197 // https://issues.dlang.org/show_bug.cgi?id=14230
2198 @safe unittest
2199 {
2200 string[] ary = ["","aa","bb","cc"]; // leaded by _empty_ element
2201 assert(ary.join(" @") == " @aa @bb @cc"); // OK in 2.067b1 and olders
2202 }
2203
2204 // https://issues.dlang.org/show_bug.cgi?id=21337
2205 @system unittest
2206 {
2207 import std.algorithm.iteration : map;
2208
2209 static class Once
2210 {
2211 bool empty;
2212
popFront()2213 void popFront()
2214 {
2215 empty = true;
2216 }
2217
front()2218 int front()
2219 {
2220 return 0;
2221 }
2222 }
2223
2224 assert([1, 2].map!"[a]".join(new Once) == [1, 0, 2]);
2225 }
2226
2227 /// Ditto
2228 ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, scope E sep)
2229 if (isInputRange!RoR &&
2230 isInputRange!(Unqual!(ElementType!RoR)) &&
2231 ((is(E : ElementType!(ElementType!RoR))) ||
2232 (!autodecodeStrings && isSomeChar!(ElementType!(ElementType!RoR)) &&
2233 isSomeChar!E)))
2234 {
2235 alias RetType = typeof(return);
2236 alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
2237 alias RoRElem = ElementType!RoR;
2238
2239 if (ror.empty)
2240 return RetType.init;
2241
2242 static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
2243 {
2244 static if (isSomeChar!E && isSomeChar!RetTypeElement && E.sizeof > RetTypeElement.sizeof)
2245 {
2246 import std.utf : encode;
2247 RetTypeElement[4 / RetTypeElement.sizeof] encodeSpace;
2248 immutable size_t sepArrLength = encode(encodeSpace, sep);
2249 return join(ror, encodeSpace[0 .. sepArrLength]);
2250 }
2251 else
2252 {
2253 import core.internal.lifetime : emplaceRef;
2254 import std.format : format;
2255 size_t length;
2256 size_t rorLength;
2257 foreach (r; ror.save)
2258 {
2259 length += r.length;
2260 ++rorLength;
2261 }
2262 if (!rorLength)
2263 return null;
2264 length += rorLength - 1;
2265 auto result = uninitializedArray!(RetTypeElement[])(length);
2266
2267
2268 size_t len;
2269 foreach (e; ror.front)
2270 emplaceRef(result[len++], e);
2271 ror.popFront();
foreach(r;ror)2272 foreach (r; ror)
2273 {
2274 emplaceRef(result[len++], sep);
2275 foreach (e; r)
2276 emplaceRef(result[len++], e);
2277 }
2278 assert(len == result.length, format!
2279 "len %s must equal result.lenght %s"(len, result.length));
2280 return (() @trusted => cast(RetType) result)();
2281 }
2282 }
2283 else
2284 {
2285 auto result = appender!RetType();
2286 put(result, ror.front);
2287 ror.popFront();
2288 for (; !ror.empty; ror.popFront())
2289 {
2290 put(result, sep);
2291 put(result, ror.front);
2292 }
2293 return result.data;
2294 }
2295 }
2296
2297 // https://issues.dlang.org/show_bug.cgi?id=10895
2298 @safe unittest
2299 {
2300 class A
2301 {
2302 string name;
2303 alias name this;
this(string name)2304 this(string name) { this.name = name; }
2305 }
2306 auto a = [new A(`foo`)];
2307 assert(a[0].length == 3);
2308 auto temp = join(a, " ");
2309 assert(a[0].length == 3);
2310 assert(temp.length == 3);
2311 }
2312
2313 // https://issues.dlang.org/show_bug.cgi?id=14230
2314 @safe unittest
2315 {
2316 string[] ary = ["","aa","bb","cc"];
2317 assert(ary.join('@') == "@aa@bb@cc");
2318 }
2319
2320 /// Ditto
2321 ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror)
2322 if (isInputRange!RoR &&
2323 isInputRange!(Unqual!(ElementType!RoR)))
2324 {
2325 alias RetType = typeof(return);
2326 alias ConstRetTypeElement = ElementEncodingType!RetType;
2327 static if (isAssignable!(Unqual!ConstRetTypeElement, ConstRetTypeElement))
2328 {
2329 alias RetTypeElement = Unqual!ConstRetTypeElement;
2330 }
2331 else
2332 {
2333 alias RetTypeElement = ConstRetTypeElement;
2334 }
2335 alias RoRElem = ElementType!RoR;
2336
2337 if (ror.empty)
2338 return RetType.init;
2339
2340 static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
2341 {
2342 import core.internal.lifetime : emplaceRef;
2343 size_t length;
2344 foreach (r; ror.save)
2345 length += r.length;
2346
2347 auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))();
2348 size_t len;
2349 foreach (r; ror)
2350 foreach (e; r)
2351 emplaceRef!RetTypeElement(result[len++], e);
2352 assert(len == result.length,
2353 "emplaced an unexpected number of elements");
2354 return (() @trusted => cast(RetType) result)();
2355 }
2356 else
2357 {
2358 auto result = appender!RetType();
2359 for (; !ror.empty; ror.popFront())
2360 put(result, ror.front);
2361 return result.data;
2362 }
2363 }
2364
2365 ///
2366 @safe pure nothrow unittest
2367 {
2368 assert(join(["hello", "silly", "world"], " ") == "hello silly world");
2369 assert(join(["hello", "silly", "world"]) == "hellosillyworld");
2370
2371 assert(join([[1, 2, 3], [4, 5]], [72, 73]) == [1, 2, 3, 72, 73, 4, 5]);
2372 assert(join([[1, 2, 3], [4, 5]]) == [1, 2, 3, 4, 5]);
2373
2374 const string[] arr = ["apple", "banana"];
2375 assert(arr.join(",") == "apple,banana");
2376 assert(arr.join() == "applebanana");
2377 }
2378
2379 @safe pure unittest
2380 {
2381 import std.conv : to;
2382 import std.range.primitives : autodecodeStrings;
2383
2384 static foreach (T; AliasSeq!(string,wstring,dstring))
2385 {{
2386 auto arr2 = "Здравствуй Мир Unicode".to!(T);
2387 auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]);
2388 assert(join(arr) == "ЗдравствуйМирUnicode");
2389 static foreach (S; AliasSeq!(char,wchar,dchar))
2390 {{
2391 auto jarr = arr.join(to!S(' '));
2392 static assert(is(typeof(jarr) == T));
2393 assert(jarr == arr2);
2394 }}
2395 static foreach (S; AliasSeq!(string,wstring,dstring))
2396 {{
2397 auto jarr = arr.join(to!S(" "));
2398 static assert(is(typeof(jarr) == T));
2399 assert(jarr == arr2);
2400 }}
2401 }}
2402
2403 static foreach (T; AliasSeq!(string,wstring,dstring))
2404 {{
2405 auto arr2 = "Здравствуй\u047CМир\u047CUnicode".to!(T);
2406 auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]);
2407 static foreach (S; AliasSeq!(wchar,dchar))
2408 {{
2409 auto jarr = arr.join(to!S('\u047C'));
2410 static assert(is(typeof(jarr) == T));
2411 assert(jarr == arr2);
2412 }}
2413 }}
2414
2415 const string[] arr = ["apple", "banana"];
2416 assert(arr.join(',') == "apple,banana");
2417 }
2418
2419 @safe unittest
2420 {
2421 class A { }
2422
2423 const A[][] array;
2424 auto result = array.join; // can't remove constness, so don't try
2425
2426 static assert(is(typeof(result) == const(A)[]));
2427 }
2428
2429 @safe unittest
2430 {
2431 import std.algorithm;
2432 import std.conv : to;
2433 import std.range;
2434
2435 static foreach (R; AliasSeq!(string, wstring, dstring))
2436 {{
2437 R word1 = "日本語";
2438 R word2 = "paul";
2439 R word3 = "jerry";
2440 R[] words = [word1, word2, word3];
2441
2442 auto filteredWord1 = filter!"true"(word1);
2443 auto filteredLenWord1 = takeExactly(filteredWord1, word1.walkLength());
2444 auto filteredWord2 = filter!"true"(word2);
2445 auto filteredLenWord2 = takeExactly(filteredWord2, word2.walkLength());
2446 auto filteredWord3 = filter!"true"(word3);
2447 auto filteredLenWord3 = takeExactly(filteredWord3, word3.walkLength());
2448 auto filteredWordsArr = [filteredWord1, filteredWord2, filteredWord3];
2449 auto filteredLenWordsArr = [filteredLenWord1, filteredLenWord2, filteredLenWord3];
2450 auto filteredWords = filter!"true"(filteredWordsArr);
2451
2452 static foreach (S; AliasSeq!(string, wstring, dstring))
2453 {{
2454 assert(join(filteredWords, to!S(", ")) == "日本語, paul, jerry");
2455 assert(join(filteredWords, to!(ElementType!S)(',')) == "日本語,paul,jerry");
2456 assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry");
2457 assert(join(filteredWordsArr, to!S(", ")) == "日本語, paul, jerry");
2458 assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry");
2459 assert(join(filteredLenWordsArr, to!S(", ")) == "日本語, paul, jerry");
2460 assert(join(filter!"true"(words), to!S(", ")) == "日本語, paul, jerry");
2461 assert(join(words, to!S(", ")) == "日本語, paul, jerry");
2462
2463 assert(join(filteredWords, to!S("")) == "日本語pauljerry");
2464 assert(join(filteredWordsArr, to!S("")) == "日本語pauljerry");
2465 assert(join(filteredLenWordsArr, to!S("")) == "日本語pauljerry");
2466 assert(join(filter!"true"(words), to!S("")) == "日本語pauljerry");
2467 assert(join(words, to!S("")) == "日本語pauljerry");
2468
2469 assert(join(filter!"true"([word1]), to!S(", ")) == "日本語");
2470 assert(join([filteredWord1], to!S(", ")) == "日本語");
2471 assert(join([filteredLenWord1], to!S(", ")) == "日本語");
2472 assert(join(filter!"true"([filteredWord1]), to!S(", ")) == "日本語");
2473 assert(join([word1], to!S(", ")) == "日本語");
2474
2475 assert(join(filteredWords, to!S(word1)) == "日本語日本語paul日本語jerry");
2476 assert(join(filteredWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry");
2477 assert(join(filteredLenWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry");
2478 assert(join(filter!"true"(words), to!S(word1)) == "日本語日本語paul日本語jerry");
2479 assert(join(words, to!S(word1)) == "日本語日本語paul日本語jerry");
2480
2481 auto filterComma = filter!"true"(to!S(", "));
2482 assert(join(filteredWords, filterComma) == "日本語, paul, jerry");
2483 assert(join(filteredWordsArr, filterComma) == "日本語, paul, jerry");
2484 assert(join(filteredLenWordsArr, filterComma) == "日本語, paul, jerry");
2485 assert(join(filter!"true"(words), filterComma) == "日本語, paul, jerry");
2486 assert(join(words, filterComma) == "日本語, paul, jerry");
2487 }}
2488
2489 assert(join(filteredWords) == "日本語pauljerry");
2490 assert(join(filteredWordsArr) == "日本語pauljerry");
2491 assert(join(filteredLenWordsArr) == "日本語pauljerry");
2492 assert(join(filter!"true"(words)) == "日本語pauljerry");
2493 assert(join(words) == "日本語pauljerry");
2494
2495 assert(join(filteredWords, filter!"true"(", ")) == "日本語, paul, jerry");
2496 assert(join(filteredWordsArr, filter!"true"(", ")) == "日本語, paul, jerry");
2497 assert(join(filteredLenWordsArr, filter!"true"(", ")) == "日本語, paul, jerry");
2498 assert(join(filter!"true"(words), filter!"true"(", ")) == "日本語, paul, jerry");
2499 assert(join(words, filter!"true"(", ")) == "日本語, paul, jerry");
2500
2501 assert(join(filter!"true"(cast(typeof(filteredWordsArr))[]), ", ").empty);
2502 assert(join(cast(typeof(filteredWordsArr))[], ", ").empty);
2503 assert(join(cast(typeof(filteredLenWordsArr))[], ", ").empty);
2504 assert(join(filter!"true"(cast(R[])[]), ", ").empty);
2505 assert(join(cast(R[])[], ", ").empty);
2506
2507 assert(join(filter!"true"(cast(typeof(filteredWordsArr))[])).empty);
2508 assert(join(cast(typeof(filteredWordsArr))[]).empty);
2509 assert(join(cast(typeof(filteredLenWordsArr))[]).empty);
2510
2511 assert(join(filter!"true"(cast(R[])[])).empty);
2512 assert(join(cast(R[])[]).empty);
2513 }}
2514
2515 assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2516 assert(join([[1, 2], [41, 42]], cast(int[])[]) == [1, 2, 41, 42]);
2517 assert(join([[1, 2]], [5, 6]) == [1, 2]);
2518 assert(join(cast(int[][])[], [5, 6]).empty);
2519
2520 assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]);
2521 assert(join(cast(int[][])[]).empty);
2522
2523 alias f = filter!"true";
2524 assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2525 assert(join(f([[1, 2], [41, 42]]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
2526 assert(join([f([1, 2]), f([41, 42])], [5, 6]) == [1, 2, 5, 6, 41, 42]);
2527 assert(join(f([f([1, 2]), f([41, 42])]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
2528 assert(join([[1, 2], [41, 42]], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2529 assert(join(f([[1, 2], [41, 42]]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2530 assert(join([f([1, 2]), f([41, 42])], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2531 assert(join(f([f([1, 2]), f([41, 42])]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
2532 }
2533
2534 // https://issues.dlang.org/show_bug.cgi?id=10683
2535 @safe unittest
2536 {
2537 import std.range : join;
2538 import std.typecons : tuple;
2539 assert([[tuple(1)]].join == [tuple(1)]);
2540 assert([[tuple("x")]].join == [tuple("x")]);
2541 }
2542
2543 // https://issues.dlang.org/show_bug.cgi?id=13877
2544 @safe unittest
2545 {
2546 // Test that the range is iterated only once.
2547 import std.algorithm.iteration : map;
2548 int c = 0;
2549 auto j1 = [1, 2, 3].map!(_ => [c++]).join;
2550 assert(c == 3);
2551 assert(j1 == [0, 1, 2]);
2552
2553 c = 0;
2554 auto j2 = [1, 2, 3].map!(_ => [c++]).join(9);
2555 assert(c == 3);
2556 assert(j2 == [0, 9, 1, 9, 2]);
2557
2558 c = 0;
2559 auto j3 = [1, 2, 3].map!(_ => [c++]).join([9]);
2560 assert(c == 3);
2561 assert(j3 == [0, 9, 1, 9, 2]);
2562 }
2563
2564
2565 /++
2566 Replace occurrences of `from` with `to` in `subject` in a new array.
2567
2568 Params:
2569 subject = the array to scan
2570 from = the item to replace
2571 to = the item to replace all instances of `from` with
2572
2573 Returns:
2574 A new array without changing the contents of `subject`, or the original
2575 array if no match is found.
2576
2577 See_Also:
2578 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2579 +/
2580 E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to)
2581 if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
2582 is(Unqual!E : Unqual!R1))
2583 {
2584 import std.algorithm.searching : find;
2585 import std.range : dropOne;
2586
2587 static if (isInputRange!R1)
2588 {
2589 if (from.empty) return subject;
2590 alias rSave = a => a.save;
2591 }
2592 else
2593 {
2594 alias rSave = a => a;
2595 }
2596
2597 auto balance = find(subject, rSave(from));
2598 if (balance.empty)
2599 return subject;
2600
2601 auto app = appender!(E[])();
2602 app.put(subject[0 .. subject.length - balance.length]);
2603 app.put(rSave(to));
2604 // replacing an element in an array is different to a range replacement
2605 static if (is(Unqual!E : Unqual!R1))
2606 replaceInto(app, balance.dropOne, from, to);
2607 else
2608 replaceInto(app, balance[from.length .. $], from, to);
2609
2610 return app.data;
2611 }
2612
2613 ///
2614 @safe unittest
2615 {
2616 assert("Hello Wörld".replace("o Wö", "o Wo") == "Hello World");
2617 assert("Hello Wörld".replace("l", "h") == "Hehho Wörhd");
2618 }
2619
2620 @safe unittest
2621 {
2622 assert([1, 2, 3, 4, 2].replace([2], [5]) == [1, 5, 3, 4, 5]);
2623 assert([3, 3, 3].replace([3], [0]) == [0, 0, 0]);
2624 assert([3, 3, 4, 3].replace([3, 3], [1, 1, 1]) == [1, 1, 1, 4, 3]);
2625 }
2626
2627 // https://issues.dlang.org/show_bug.cgi?id=18215
2628 @safe unittest
2629 {
2630 auto arr = ["aaa.dd", "b"];
2631 arr = arr.replace("aaa.dd", ".");
2632 assert(arr == [".", "b"]);
2633
2634 arr = ["_", "_", "aaa.dd", "b", "c", "aaa.dd", "e"];
2635 arr = arr.replace("aaa.dd", ".");
2636 assert(arr == ["_", "_", ".", "b", "c", ".", "e"]);
2637 }
2638
2639 // https://issues.dlang.org/show_bug.cgi?id=18215
2640 @safe unittest
2641 {
2642 assert([[0], [1, 2], [0], [3]].replace([0], [4]) == [[4], [1, 2], [4], [3]]);
2643 assert([[0], [1, 2], [0], [3], [1, 2]]
2644 .replace([1, 2], [0]) == [[0], [0], [0], [3], [0]]);
2645 assert([[0], [1, 2], [0], [3], [1, 2], [0], [1, 2]]
2646 .replace([[0], [1, 2]], [[4]]) == [[4], [0], [3], [1, 2], [4]]);
2647 }
2648
2649 // https://issues.dlang.org/show_bug.cgi?id=10930
2650 @safe unittest
2651 {
2652 assert([0, 1, 2].replace(1, 4) == [0, 4, 2]);
2653 assert("äbö".replace('ä', 'a') == "abö");
2654 }
2655
2656 // empty array
2657 @safe unittest
2658 {
2659 int[] arr;
2660 assert(replace(arr, 1, 2) == []);
2661 }
2662
2663 /++
2664 Replace occurrences of `from` with `to` in `subject` and output the result into
2665 `sink`.
2666
2667 Params:
2668 sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
2669 subject = the array to scan
2670 from = the item to replace
2671 to = the item to replace all instances of `from` with
2672
2673 See_Also:
2674 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2675 +/
2676 void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to)
2677 if (isOutputRange!(Sink, E) &&
2678 ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
2679 is(Unqual!E : Unqual!R1)))
2680 {
2681 import std.algorithm.searching : find;
2682 import std.range : dropOne;
2683
2684 static if (isInputRange!R1)
2685 {
2686 if (from.empty)
2687 {
2688 sink.put(subject);
2689 return;
2690 }
2691 alias rSave = a => a.save;
2692 }
2693 else
2694 {
2695 alias rSave = a => a;
2696 }
2697 for (;;)
2698 {
2699 auto balance = find(subject, rSave(from));
2700 if (balance.empty)
2701 {
2702 sink.put(subject);
2703 break;
2704 }
2705 sink.put(subject[0 .. subject.length - balance.length]);
2706 sink.put(rSave(to));
2707 // replacing an element in an array is different to a range replacement
2708 static if (is(Unqual!E : Unqual!R1))
2709 subject = balance.dropOne;
2710 else
2711 subject = balance[from.length .. $];
2712 }
2713 }
2714
2715 ///
2716 @safe unittest
2717 {
2718 auto arr = [1, 2, 3, 4, 5];
2719 auto from = [2, 3];
2720 auto to = [4, 6];
2721 auto sink = appender!(int[])();
2722
2723 replaceInto(sink, arr, from, to);
2724
2725 assert(sink.data == [1, 4, 6, 4, 5]);
2726 }
2727
2728 // empty array
2729 @safe unittest
2730 {
2731 auto sink = appender!(int[])();
2732 int[] arr;
2733 replaceInto(sink, arr, 1, 2);
2734 assert(sink.data == []);
2735 }
2736
2737 @safe unittest
2738 {
2739 import std.algorithm.comparison : cmp;
2740 import std.conv : to;
2741
2742 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
2743 {
2744 static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
2745 {{
2746 auto s = to!S("This is a foo foo list");
2747 auto from = to!T("foo");
2748 auto into = to!S("silly");
2749 S r;
2750 int i;
2751
2752 r = replace(s, from, into);
2753 i = cmp(r, "This is a silly silly list");
2754 assert(i == 0);
2755
2756 r = replace(s, to!S(""), into);
2757 i = cmp(r, "This is a foo foo list");
2758 assert(i == 0);
2759
2760 assert(replace(r, to!S("won't find this"), to!S("whatever")) is r);
2761 }}
2762 }
2763
2764 immutable s = "This is a foo foo list";
2765 assert(replace(s, "foo", "silly") == "This is a silly silly list");
2766 }
2767
2768 @safe unittest
2769 {
2770 import std.algorithm.searching : skipOver;
2771 import std.conv : to;
2772
CheckOutput(C)2773 struct CheckOutput(C)
2774 {
2775 C[] desired;
2776 this(C[] arr){ desired = arr; }
2777 void put(C[] part){ assert(skipOver(desired, part)); }
2778 }
2779 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
2780 {{
2781 alias Char = ElementEncodingType!S;
2782 S s = to!S("yet another dummy text, yet another ...");
2783 S from = to!S("yet another");
2784 S into = to!S("some");
2785 replaceInto(CheckOutput!(Char)(to!S("some dummy text, some ..."))
2786 , s, from, into);
2787 }}
2788 }
2789
2790 // https://issues.dlang.org/show_bug.cgi?id=10930
2791 @safe unittest
2792 {
2793 auto sink = appender!(int[])();
2794 replaceInto(sink, [0, 1, 2], 1, 5);
2795 assert(sink.data == [0, 5, 2]);
2796
2797 auto sink2 = appender!(dchar[])();
2798 replaceInto(sink2, "äbö", 'ä', 'a');
2799 assert(sink2.data == "abö");
2800 }
2801
2802 /++
2803 Replaces elements from `array` with indices ranging from `from`
2804 (inclusive) to `to` (exclusive) with the range `stuff`.
2805
2806 Params:
2807 subject = the array to scan
2808 from = the starting index
2809 to = the ending index
2810 stuff = the items to replace in-between `from` and `to`
2811
2812 Returns:
2813 A new array without changing the contents of `subject`.
2814
2815 See_Also:
2816 $(REF substitute, std,algorithm,iteration) for a lazy replace.
2817 +/
2818 T[] replace(T, Range)(T[] subject, size_t from, size_t to, Range stuff)
2819 if (isInputRange!Range &&
2820 (is(ElementType!Range : T) ||
2821 isSomeString!(T[]) && is(ElementType!Range : dchar)))
2822 {
2823 static if (hasLength!Range && is(ElementEncodingType!Range : T))
2824 {
2825 import std.algorithm.mutation : copy;
2826 assert(from <= to, "from must be before or equal to to");
2827 immutable sliceLen = to - from;
2828 auto retval = new Unqual!(T)[](subject.length - sliceLen + stuff.length);
2829 retval[0 .. from] = subject[0 .. from];
2830
2831 if (!stuff.empty)
2832 copy(stuff, retval[from .. from + stuff.length]);
2833
2834 retval[from + stuff.length .. $] = subject[to .. $];
2835 static if (is(T == const) || is(T == immutable))
2836 {
2837 return () @trusted { return cast(T[]) retval; } ();
2838 }
2839 else
2840 {
2841 return cast(T[]) retval;
2842 }
2843 }
2844 else
2845 {
2846 auto app = appender!(T[])();
2847 app.put(subject[0 .. from]);
2848 app.put(stuff);
2849 app.put(subject[to .. $]);
2850 return app.data;
2851 }
2852 }
2853
2854 ///
2855 @safe unittest
2856 {
2857 auto a = [ 1, 2, 3, 4 ];
2858 auto b = a.replace(1, 3, [ 9, 9, 9 ]);
2859 assert(a == [ 1, 2, 3, 4 ]);
2860 assert(b == [ 1, 9, 9, 9, 4 ]);
2861 }
2862
2863 @system unittest
2864 {
2865 import core.exception;
2866 import std.algorithm.iteration : filter;
2867 import std.conv : to;
2868 import std.exception;
2869
2870
2871 auto a = [ 1, 2, 3, 4 ];
2872 assert(replace(a, 0, 0, [5, 6, 7]) == [5, 6, 7, 1, 2, 3, 4]);
2873 assert(replace(a, 0, 2, cast(int[])[]) == [3, 4]);
2874 assert(replace(a, 0, 4, [5, 6, 7]) == [5, 6, 7]);
2875 assert(replace(a, 0, 2, [5, 6, 7]) == [5, 6, 7, 3, 4]);
2876 assert(replace(a, 2, 4, [5, 6, 7]) == [1, 2, 5, 6, 7]);
2877
2878 assert(replace(a, 0, 0, filter!"true"([5, 6, 7])) == [5, 6, 7, 1, 2, 3, 4]);
2879 assert(replace(a, 0, 2, filter!"true"(cast(int[])[])) == [3, 4]);
2880 assert(replace(a, 0, 4, filter!"true"([5, 6, 7])) == [5, 6, 7]);
2881 assert(replace(a, 0, 2, filter!"true"([5, 6, 7])) == [5, 6, 7, 3, 4]);
2882 assert(replace(a, 2, 4, filter!"true"([5, 6, 7])) == [1, 2, 5, 6, 7]);
2883 assert(a == [ 1, 2, 3, 4 ]);
2884
testStr(T,U)2885 void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
2886 {
2887
2888 auto l = to!T("hello");
2889 auto r = to!U(" world");
2890
2891 enforce(replace(l, 0, 0, r) == " worldhello",
2892 new AssertError("testStr failure 1", file, line));
2893 enforce(replace(l, 0, 3, r) == " worldlo",
2894 new AssertError("testStr failure 2", file, line));
2895 enforce(replace(l, 3, l.length, r) == "hel world",
2896 new AssertError("testStr failure 3", file, line));
2897 enforce(replace(l, 0, l.length, r) == " world",
2898 new AssertError("testStr failure 4", file, line));
2899 enforce(replace(l, l.length, l.length, r) == "hello world",
2900 new AssertError("testStr failure 5", file, line));
2901 }
2902
2903 testStr!(string, string)();
2904 testStr!(string, wstring)();
2905 testStr!(string, dstring)();
2906 testStr!(wstring, string)();
2907 testStr!(wstring, wstring)();
2908 testStr!(wstring, dstring)();
2909 testStr!(dstring, string)();
2910 testStr!(dstring, wstring)();
2911 testStr!(dstring, dstring)();
2912
2913 enum s = "0123456789";
2914 enum w = "⁰¹²³⁴⁵⁶⁷⁸⁹"w;
2915 enum d = "⁰¹²³⁴⁵⁶⁷⁸⁹"d;
2916
2917 assert(replace(s, 0, 0, "***") == "***0123456789");
2918 assert(replace(s, 10, 10, "***") == "0123456789***");
2919 assert(replace(s, 3, 8, "1012") == "012101289");
2920 assert(replace(s, 0, 5, "43210") == "4321056789");
2921 assert(replace(s, 5, 10, "43210") == "0123443210");
2922
2923 assert(replace(w, 0, 0, "***"w) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"w);
2924 assert(replace(w, 10, 10, "***"w) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"w);
2925 assert(replace(w, 3, 8, "¹⁰¹²"w) == "⁰¹²¹⁰¹²⁸⁹"w);
2926 assert(replace(w, 0, 5, "⁴³²¹⁰"w) == "⁴³²¹⁰⁵⁶⁷⁸⁹"w);
2927 assert(replace(w, 5, 10, "⁴³²¹⁰"w) == "⁰¹²³⁴⁴³²¹⁰"w);
2928
2929 assert(replace(d, 0, 0, "***"d) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"d);
2930 assert(replace(d, 10, 10, "***"d) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"d);
2931 assert(replace(d, 3, 8, "¹⁰¹²"d) == "⁰¹²¹⁰¹²⁸⁹"d);
2932 assert(replace(d, 0, 5, "⁴³²¹⁰"d) == "⁴³²¹⁰⁵⁶⁷⁸⁹"d);
2933 assert(replace(d, 5, 10, "⁴³²¹⁰"d) == "⁰¹²³⁴⁴³²¹⁰"d);
2934 }
2935
2936 // https://issues.dlang.org/show_bug.cgi?id=18166
2937 @safe pure unittest
2938 {
2939 auto str = replace("aaaaa"d, 1, 4, "***"d);
2940 assert(str == "a***a");
2941 }
2942
2943 /++
2944 Replaces elements from `array` with indices ranging from `from`
2945 (inclusive) to `to` (exclusive) with the range `stuff`. Expands or
2946 shrinks the array as needed.
2947
2948 Params:
2949 array = the array to scan
2950 from = the starting index
2951 to = the ending index
2952 stuff = the items to replace in-between `from` and `to`
2953 +/
2954 void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
2955 if (is(typeof(replace(array, from, to, stuff))))
2956 {
2957 static if (isDynamicArray!Range &&
2958 is(Unqual!(ElementEncodingType!Range) == T) &&
2959 !isNarrowString!(T[]))
2960 {
2961 // optimized for homogeneous arrays that can be overwritten.
2962 import std.algorithm.mutation : remove;
2963 import std.typecons : tuple;
2964
2965 if (overlap(array, stuff).length)
2966 {
2967 // use slower/conservative method
2968 array = array[0 .. from] ~ stuff ~ array[to .. $];
2969 }
2970 else if (stuff.length <= to - from)
2971 {
2972 // replacement reduces length
2973 immutable stuffEnd = from + stuff.length;
2974 array[from .. stuffEnd] = stuff[];
2975 if (stuffEnd < to)
2976 array = remove(array, tuple(stuffEnd, to));
2977 }
2978 else
2979 {
2980 // replacement increases length
2981 // @@@TODO@@@: optimize this
2982 immutable replaceLen = to - from;
2983 array[from .. to] = stuff[0 .. replaceLen];
2984 insertInPlace(array, to, stuff[replaceLen .. $]);
2985 }
2986 }
2987 else
2988 {
2989 // default implementation, just do what replace does.
2990 array = replace(array, from, to, stuff);
2991 }
2992 }
2993
2994 ///
2995 @safe unittest
2996 {
2997 int[] a = [1, 4, 5];
2998 replaceInPlace(a, 1u, 2u, [2, 3, 4]);
2999 assert(a == [1, 2, 3, 4, 5]);
3000 replaceInPlace(a, 1u, 2u, cast(int[])[]);
3001 assert(a == [1, 3, 4, 5]);
3002 replaceInPlace(a, 1u, 3u, a[2 .. 4]);
3003 assert(a == [1, 4, 5, 5]);
3004 }
3005
3006 // https://issues.dlang.org/show_bug.cgi?id=12889
3007 @safe unittest
3008 {
3009 int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]];
3010 int[1][] stuff = [[0], [1]];
3011 replaceInPlace(arr, 4, 6, stuff);
3012 assert(arr == [[0], [1], [2], [3], [0], [1], [6]]);
3013 }
3014
3015 @system unittest
3016 {
3017 // https://issues.dlang.org/show_bug.cgi?id=14925
3018 char[] a = "mon texte 1".dup;
3019 char[] b = "abc".dup;
3020 replaceInPlace(a, 4, 9, b);
3021 assert(a == "mon abc 1");
3022
3023 // ensure we can replace in place with different encodings
3024 string unicoded = "\U00010437";
3025 string unicodedLong = "\U00010437aaaaa";
3026 string base = "abcXXXxyz";
3027 string result = "abc\U00010437xyz";
3028 string resultLong = "abc\U00010437aaaaaxyz";
3029 size_t repstart = 3;
3030 size_t repend = 3 + 3;
3031
testStringReplaceInPlace(T,U)3032 void testStringReplaceInPlace(T, U)()
3033 {
3034 import std.algorithm.comparison : equal;
3035 import std.conv;
3036 auto a = unicoded.to!(U[]);
3037 auto b = unicodedLong.to!(U[]);
3038
3039 auto test = base.to!(T[]);
3040
3041 test.replaceInPlace(repstart, repend, a);
3042 assert(equal(test, result), "Failed for types " ~ T.stringof ~ " and " ~ U.stringof);
3043
3044 test = base.to!(T[]);
3045
3046 test.replaceInPlace(repstart, repend, b);
3047 assert(equal(test, resultLong), "Failed for types " ~ T.stringof ~ " and " ~ U.stringof);
3048 }
3049
3050 import std.meta : AliasSeq;
3051 alias allChars = AliasSeq!(char, immutable(char), const(char),
3052 wchar, immutable(wchar), const(wchar),
3053 dchar, immutable(dchar), const(dchar));
3054 foreach (T; allChars)
3055 foreach (U; allChars)
3056 testStringReplaceInPlace!(T, U)();
3057
testInout(inout (int)[]a)3058 void testInout(inout(int)[] a)
3059 {
3060 // will be transferred to the 'replace' function
3061 replaceInPlace(a, 1, 2, [1,2,3]);
3062 }
3063 }
3064
3065 @safe unittest
3066 {
3067 // the constraint for the first overload used to match this, which wouldn't compile.
3068 import std.algorithm.comparison : equal;
3069 long[] a = [1L, 2, 3];
3070 int[] b = [4, 5, 6];
3071 a.replaceInPlace(1, 2, b);
3072 assert(equal(a, [1L, 4, 5, 6, 3]));
3073 }
3074
3075 @system unittest
3076 {
3077 import core.exception;
3078 import std.algorithm.comparison : equal;
3079 import std.algorithm.iteration : filter;
3080 import std.conv : to;
3081 import std.exception;
3082
3083
test(T,U,V)3084 bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result)
3085 {
3086 {
3087 static if (is(T == typeof(T.init.dup)))
3088 auto a = orig.dup;
3089 else
3090 auto a = orig.idup;
3091
3092 a.replaceInPlace(from, to, toReplace);
3093 if (!equal(a, result))
3094 return false;
3095 }
3096
3097 static if (isInputRange!U)
3098 {
3099 orig.replaceInPlace(from, to, filter!"true"(toReplace));
3100 return equal(orig, result);
3101 }
3102 else
3103 return true;
3104 }
3105
3106 assert(test([1, 2, 3, 4], 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4]));
3107 assert(test([1, 2, 3, 4], 0, 2, cast(int[])[], [3, 4]));
3108 assert(test([1, 2, 3, 4], 0, 4, [5, 6, 7], [5, 6, 7]));
3109 assert(test([1, 2, 3, 4], 0, 2, [5, 6, 7], [5, 6, 7, 3, 4]));
3110 assert(test([1, 2, 3, 4], 2, 4, [5, 6, 7], [1, 2, 5, 6, 7]));
3111
3112 assert(test([1, 2, 3, 4], 0, 0, filter!"true"([5, 6, 7]), [5, 6, 7, 1, 2, 3, 4]));
3113 assert(test([1, 2, 3, 4], 0, 2, filter!"true"(cast(int[])[]), [3, 4]));
3114 assert(test([1, 2, 3, 4], 0, 4, filter!"true"([5, 6, 7]), [5, 6, 7]));
3115 assert(test([1, 2, 3, 4], 0, 2, filter!"true"([5, 6, 7]), [5, 6, 7, 3, 4]));
3116 assert(test([1, 2, 3, 4], 2, 4, filter!"true"([5, 6, 7]), [1, 2, 5, 6, 7]));
3117
testStr(T,U)3118 void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
3119 {
3120
3121 auto l = to!T("hello");
3122 auto r = to!U(" world");
3123
3124 enforce(test(l, 0, 0, r, " worldhello"),
3125 new AssertError("testStr failure 1", file, line));
3126 enforce(test(l, 0, 3, r, " worldlo"),
3127 new AssertError("testStr failure 2", file, line));
3128 enforce(test(l, 3, l.length, r, "hel world"),
3129 new AssertError("testStr failure 3", file, line));
3130 enforce(test(l, 0, l.length, r, " world"),
3131 new AssertError("testStr failure 4", file, line));
3132 enforce(test(l, l.length, l.length, r, "hello world"),
3133 new AssertError("testStr failure 5", file, line));
3134 }
3135
3136 testStr!(string, string)();
3137 testStr!(string, wstring)();
3138 testStr!(string, dstring)();
3139 testStr!(wstring, string)();
3140 testStr!(wstring, wstring)();
3141 testStr!(wstring, dstring)();
3142 testStr!(dstring, string)();
3143 testStr!(dstring, wstring)();
3144 testStr!(dstring, dstring)();
3145 }
3146
3147 /++
3148 Replaces the first occurrence of `from` with `to` in `subject`.
3149
3150 Params:
3151 subject = the array to scan
3152 from = the item to replace
3153 to = the item to replace `from` with
3154
3155 Returns:
3156 A new array without changing the contents of `subject`, or the original
3157 array if no match is found.
3158 +/
3159 E[] replaceFirst(E, R1, R2)(E[] subject, R1 from, R2 to)
3160 if (isDynamicArray!(E[]) &&
3161 isForwardRange!R1 && is(typeof(appender!(E[])().put(from[0 .. 1]))) &&
3162 isForwardRange!R2 && is(typeof(appender!(E[])().put(to[0 .. 1]))))
3163 {
3164 if (from.empty) return subject;
3165 static if (isSomeString!(E[]))
3166 {
3167 import std.string : indexOf;
3168 immutable idx = subject.indexOf(from);
3169 }
3170 else
3171 {
3172 import std.algorithm.searching : countUntil;
3173 immutable idx = subject.countUntil(from);
3174 }
3175 if (idx == -1)
3176 return subject;
3177
3178 auto app = appender!(E[])();
3179 app.put(subject[0 .. idx]);
3180 app.put(to);
3181
3182 static if (isSomeString!(E[]) && isSomeString!R1)
3183 {
3184 import std.utf : codeLength;
3185 immutable fromLength = codeLength!(Unqual!E, R1)(from);
3186 }
3187 else
3188 immutable fromLength = from.length;
3189
3190 app.put(subject[idx + fromLength .. $]);
3191
3192 return app.data;
3193 }
3194
3195 ///
3196 @safe unittest
3197 {
3198 auto a = [1, 2, 2, 3, 4, 5];
3199 auto b = a.replaceFirst([2], [1337]);
3200 assert(b == [1, 1337, 2, 3, 4, 5]);
3201
3202 auto s = "This is a foo foo list";
3203 auto r = s.replaceFirst("foo", "silly");
3204 assert(r == "This is a silly foo list");
3205 }
3206
3207 @safe unittest
3208 {
3209 import std.algorithm.comparison : cmp;
3210 import std.conv : to;
3211
3212 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
3213 const(char[]), immutable(char[])))
3214 {
3215 static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
3216 const(char[]), immutable(char[])))
3217 {{
3218 auto s = to!S("This is a foo foo list");
3219 auto s2 = to!S("Thüs is a ßöö foo list");
3220 auto from = to!T("foo");
3221 auto from2 = to!T("ßöö");
3222 auto into = to!T("silly");
3223 auto into2 = to!T("sälly");
3224
3225 S r1 = replaceFirst(s, from, into);
3226 assert(cmp(r1, "This is a silly foo list") == 0);
3227
3228 S r11 = replaceFirst(s2, from2, into2);
3229 assert(cmp(r11, "Thüs is a sälly foo list") == 0,
3230 to!string(r11) ~ " : " ~ S.stringof ~ " " ~ T.stringof);
3231
3232 S r2 = replaceFirst(r1, from, into);
3233 assert(cmp(r2, "This is a silly silly list") == 0);
3234
3235 S r3 = replaceFirst(s, to!T(""), into);
3236 assert(cmp(r3, "This is a foo foo list") == 0);
3237
3238 assert(replaceFirst(r3, to!T("won't find"), to!T("whatever")) is r3);
3239 }}
3240 }
3241 }
3242
3243 // https://issues.dlang.org/show_bug.cgi?id=8187
3244 @safe unittest
3245 {
3246 auto res = ["a", "a"];
3247 assert(replace(res, "a", "b") == ["b", "b"]);
3248 assert(replaceFirst(res, "a", "b") == ["b", "a"]);
3249 }
3250
3251 /++
3252 Replaces the last occurrence of `from` with `to` in `subject`.
3253
3254 Params:
3255 subject = the array to scan
3256 from = the item to replace
3257 to = the item to replace `from` with
3258
3259 Returns:
3260 A new array without changing the contents of `subject`, or the original
3261 array if no match is found.
3262 +/
3263 E[] replaceLast(E, R1, R2)(E[] subject, R1 from , R2 to)
3264 if (isDynamicArray!(E[]) &&
3265 isForwardRange!R1 && is(typeof(appender!(E[])().put(from[0 .. 1]))) &&
3266 isForwardRange!R2 && is(typeof(appender!(E[])().put(to[0 .. 1]))))
3267 {
3268 import std.range : retro;
3269 if (from.empty) return subject;
3270 static if (isSomeString!(E[]))
3271 {
3272 import std.string : lastIndexOf;
3273 auto idx = subject.lastIndexOf(from);
3274 }
3275 else
3276 {
3277 import std.algorithm.searching : countUntil;
3278 auto idx = retro(subject).countUntil(retro(from));
3279 }
3280
3281 if (idx == -1)
3282 return subject;
3283
3284 static if (isSomeString!(E[]) && isSomeString!R1)
3285 {
3286 import std.utf : codeLength;
3287 auto fromLength = codeLength!(Unqual!E, R1)(from);
3288 }
3289 else
3290 auto fromLength = from.length;
3291
3292 auto app = appender!(E[])();
3293 static if (isSomeString!(E[]))
3294 app.put(subject[0 .. idx]);
3295 else
3296 app.put(subject[0 .. $ - idx - fromLength]);
3297
3298 app.put(to);
3299
3300 static if (isSomeString!(E[]))
3301 app.put(subject[idx+fromLength .. $]);
3302 else
3303 app.put(subject[$ - idx .. $]);
3304
3305 return app.data;
3306 }
3307
3308 ///
3309 @safe unittest
3310 {
3311 auto a = [1, 2, 2, 3, 4, 5];
3312 auto b = a.replaceLast([2], [1337]);
3313 assert(b == [1, 2, 1337, 3, 4, 5]);
3314
3315 auto s = "This is a foo foo list";
3316 auto r = s.replaceLast("foo", "silly");
3317 assert(r == "This is a foo silly list", r);
3318 }
3319
3320 @safe unittest
3321 {
3322 import std.algorithm.comparison : cmp;
3323 import std.conv : to;
3324
3325 static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
3326 const(char[]), immutable(char[])))
3327 {
3328 static foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
3329 const(char[]), immutable(char[])))
3330 {{
3331 auto s = to!S("This is a foo foo list");
3332 auto s2 = to!S("Thüs is a ßöö ßöö list");
3333 auto from = to!T("foo");
3334 auto from2 = to!T("ßöö");
3335 auto into = to!T("silly");
3336 auto into2 = to!T("sälly");
3337
3338 S r1 = replaceLast(s, from, into);
3339 assert(cmp(r1, "This is a foo silly list") == 0, to!string(r1));
3340
3341 S r11 = replaceLast(s2, from2, into2);
3342 assert(cmp(r11, "Thüs is a ßöö sälly list") == 0,
3343 to!string(r11) ~ " : " ~ S.stringof ~ " " ~ T.stringof);
3344
3345 S r2 = replaceLast(r1, from, into);
3346 assert(cmp(r2, "This is a silly silly list") == 0);
3347
3348 S r3 = replaceLast(s, to!T(""), into);
3349 assert(cmp(r3, "This is a foo foo list") == 0);
3350
3351 assert(replaceLast(r3, to!T("won't find"), to!T("whatever")) is r3);
3352 }}
3353 }
3354 }
3355
3356 /++
3357 Creates a new array such that the items in `slice` are replaced with the
3358 items in `replacement`. `slice` and `replacement` do not need to be the
3359 same length. The result will grow or shrink based on the items given.
3360
3361 Params:
3362 s = the base of the new array
3363 slice = the slice of `s` to be replaced
3364 replacement = the items to replace `slice` with
3365
3366 Returns:
3367 A new array that is `s` with `slice` replaced by
3368 `replacement[]`.
3369
3370 See_Also:
3371 $(REF substitute, std,algorithm,iteration) for a lazy replace.
3372 +/
inout(T)3373 inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement)
3374 in
3375 {
3376 // Verify that slice[] really is a slice of s[]
3377 assert(overlap(s, slice) is slice, "slice[] is not a subslice of s[]");
3378 }
3379 do
3380 {
3381 auto result = new T[s.length - slice.length + replacement.length];
3382 immutable so = &slice[0] - &s[0];
3383 result[0 .. so] = s[0 .. so];
3384 result[so .. so + replacement.length] = replacement[];
3385 result[so + replacement.length .. result.length] =
3386 s[so + slice.length .. s.length];
3387
3388 return () @trusted inout {
3389 return cast(inout(T)[]) result;
3390 }();
3391 }
3392
3393 ///
3394 @safe unittest
3395 {
3396 auto a = [1, 2, 3, 4, 5];
3397 auto b = replaceSlice(a, a[1 .. 4], [0, 0, 0]);
3398
3399 assert(b == [1, 0, 0, 0, 5]);
3400 }
3401
3402 @safe unittest
3403 {
3404 import std.algorithm.comparison : cmp;
3405
3406 string s = "hello";
3407 string slice = s[2 .. 4];
3408
3409 auto r = replaceSlice(s, slice, "bar");
3410 int i;
3411 i = cmp(r, "hebaro");
3412 assert(i == 0);
3413 }
3414
3415 /**
3416 Implements an output range that appends data to an array. This is
3417 recommended over $(D array ~= data) when appending many elements because it is more
3418 efficient. `Appender` maintains its own array metadata locally, so it can avoid
3419 global locking for each append where $(LREF capacity) is non-zero.
3420
3421 Params:
3422 A = the array type to simulate.
3423
3424 See_Also: $(LREF appender)
3425 */
3426 struct Appender(A)
3427 if (isDynamicArray!A)
3428 {
3429 import core.memory : GC;
3430
3431 private alias T = ElementEncodingType!A;
3432
3433 private struct Data
3434 {
3435 size_t capacity;
3436 Unqual!T[] arr;
3437 bool tryExtendBlock = false;
3438 }
3439
3440 private Data* _data;
3441
3442 /**
3443 * Constructs an `Appender` with a given array. Note that this does not copy the
3444 * data. If the array has a larger capacity as determined by `arr.capacity`,
3445 * it will be used by the appender. After initializing an appender on an array,
3446 * appending to the original array will reallocate.
3447 */
this(A arr)3448 this(A arr) @trusted
3449 {
3450 // initialize to a given array.
3451 _data = new Data;
3452 _data.arr = cast(Unqual!T[]) arr; //trusted
3453
3454 if (__ctfe)
3455 return;
3456
3457 // We want to use up as much of the block the array is in as possible.
3458 // if we consume all the block that we can, then array appending is
3459 // safe WRT built-in append, and we can use the entire block.
3460 // We only do this for mutable types that can be extended.
3461 static if (isMutable!T && is(typeof(arr.length = size_t.max)))
3462 {
3463 immutable cap = arr.capacity; //trusted
3464 // Replace with "GC.setAttr( Not Appendable )" once pure (and fixed)
3465 if (cap > arr.length)
3466 arr.length = cap;
3467 }
3468 _data.capacity = arr.length;
3469 }
3470
3471 /**
3472 * Reserve at least newCapacity elements for appending. Note that more elements
3473 * may be reserved than requested. If `newCapacity <= capacity`, then nothing is
3474 * done.
3475 *
3476 * Params:
3477 * newCapacity = the capacity the `Appender` should have
3478 */
reserve(size_t newCapacity)3479 void reserve(size_t newCapacity)
3480 {
3481 if (_data)
3482 {
3483 if (newCapacity > _data.capacity)
3484 ensureAddable(newCapacity - _data.arr.length);
3485 }
3486 else
3487 {
3488 ensureAddable(newCapacity);
3489 }
3490 }
3491
3492 /**
3493 * Returns: the capacity of the array (the maximum number of elements the
3494 * managed array can accommodate before triggering a reallocation). If any
3495 * appending will reallocate, `0` will be returned.
3496 */
capacity()3497 @property size_t capacity() const
3498 {
3499 return _data ? _data.capacity : 0;
3500 }
3501
3502 /**
3503 * Use opSlice() from now on.
3504 * Returns: The managed array.
3505 */
inout(T)3506 @property inout(T)[] data() inout @trusted
3507 {
3508 return this[];
3509 }
3510
3511 /**
3512 * Returns: The managed array.
3513 */
inout(T)3514 @property inout(T)[] opSlice() inout @trusted
3515 {
3516 /* @trusted operation:
3517 * casting Unqual!T[] to inout(T)[]
3518 */
3519 return cast(typeof(return))(_data ? _data.arr : null);
3520 }
3521
3522 // ensure we can add nelems elements, resizing as necessary
ensureAddable(size_t nelems)3523 private void ensureAddable(size_t nelems)
3524 {
3525 if (!_data)
3526 _data = new Data;
3527 immutable len = _data.arr.length;
3528 immutable reqlen = len + nelems;
3529
3530 if (_data.capacity >= reqlen)
3531 return;
3532
3533 // need to increase capacity
3534 if (__ctfe)
3535 {
3536 static if (__traits(compiles, new Unqual!T[1]))
3537 {
3538 _data.arr.length = reqlen;
3539 }
3540 else
3541 {
3542 // avoid restriction of @disable this()
3543 _data.arr = _data.arr[0 .. _data.capacity];
3544 foreach (i; _data.capacity .. reqlen)
3545 _data.arr ~= Unqual!T.init;
3546 }
3547 _data.arr = _data.arr[0 .. len];
3548 _data.capacity = reqlen;
3549 }
3550 else
3551 {
3552 // Time to reallocate.
3553 // We need to almost duplicate what's in druntime, except we
3554 // have better access to the capacity field.
3555 auto newlen = appenderNewCapacity!(T.sizeof)(_data.capacity, reqlen);
3556 // first, try extending the current block
3557 if (_data.tryExtendBlock)
3558 {
3559 immutable u = (() @trusted => GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof))();
3560 if (u)
3561 {
3562 // extend worked, update the capacity
3563 _data.capacity = u / T.sizeof;
3564 return;
3565 }
3566 }
3567
3568
3569 // didn't work, must reallocate
3570 import core.checkedint : mulu;
3571 bool overflow;
3572 const nbytes = mulu(newlen, T.sizeof, overflow);
3573 if (overflow) assert(false, "the reallocation would exceed the "
3574 ~ "available pointer range");
3575
3576 auto bi = (() @trusted => GC.qalloc(nbytes, blockAttribute!T))();
3577 _data.capacity = bi.size / T.sizeof;
3578 import core.stdc.string : memcpy;
3579 if (len)
3580 () @trusted { memcpy(bi.base, _data.arr.ptr, len * T.sizeof); }();
3581 _data.arr = (() @trusted => (cast(Unqual!T*) bi.base)[0 .. len])();
3582 _data.tryExtendBlock = true;
3583 // leave the old data, for safety reasons
3584 }
3585 }
3586
canPutItem(U)3587 private template canPutItem(U)
3588 {
3589 enum bool canPutItem =
3590 isImplicitlyConvertible!(Unqual!U, Unqual!T) ||
3591 isSomeChar!T && isSomeChar!U;
3592 }
canPutConstRange(Range)3593 private template canPutConstRange(Range)
3594 {
3595 enum bool canPutConstRange =
3596 isInputRange!(Unqual!Range) &&
3597 !isInputRange!Range &&
3598 is(typeof(Appender.init.put(Range.init.front)));
3599 }
canPutRange(Range)3600 private template canPutRange(Range)
3601 {
3602 enum bool canPutRange =
3603 isInputRange!Range &&
3604 is(typeof(Appender.init.put(Range.init.front)));
3605 }
3606
3607 /**
3608 * Appends `item` to the managed array. Performs encoding for
3609 * `char` types if `A` is a differently typed `char` array.
3610 *
3611 * Params:
3612 * item = the single item to append
3613 */
3614 void put(U)(U item) if (canPutItem!U)
3615 {
3616 static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof)
3617 {
3618 /* may throwable operation:
3619 * - std.utf.encode
3620 */
3621 // must do some transcoding around here
3622 import std.utf : encode;
3623 Unqual!T[T.sizeof == 1 ? 4 : 2] encoded;
3624 auto len = encode(encoded, item);
3625 put(encoded[0 .. len]);
3626 }
3627 else
3628 {
3629 import core.lifetime : emplace;
3630
3631 ensureAddable(1);
3632 immutable len = _data.arr.length;
3633
3634 auto bigData = (() @trusted => _data.arr.ptr[0 .. len + 1])();
3635 auto itemUnqual = (() @trusted => & cast() item)();
3636 emplace(&bigData[len], *itemUnqual);
3637 //We do this at the end, in case of exceptions
3638 _data.arr = bigData;
3639 }
3640 }
3641
3642 // Const fixing hack.
3643 void put(Range)(Range items) if (canPutConstRange!Range)
3644 {
3645 alias p = put!(Unqual!Range);
3646 p(items);
3647 }
3648
3649 /**
3650 * Appends an entire range to the managed array. Performs encoding for
3651 * `char` elements if `A` is a differently typed `char` array.
3652 *
3653 * Params:
3654 * items = the range of items to append
3655 */
3656 void put(Range)(Range items) if (canPutRange!Range)
3657 {
3658 // note, we disable this branch for appending one type of char to
3659 // another because we can't trust the length portion.
3660 static if (!(isSomeChar!T && isSomeChar!(ElementType!Range) &&
3661 !is(immutable Range == immutable T[])) &&
3662 is(typeof(items.length) == size_t))
3663 {
3664 // optimization -- if this type is something other than a string,
3665 // and we are adding exactly one element, call the version for one
3666 // element.
3667 static if (!isSomeChar!T)
3668 {
3669 if (items.length == 1)
3670 {
3671 put(items.front);
3672 return;
3673 }
3674 }
3675
3676 // make sure we have enough space, then add the items
bigDataFun(size_t extra)3677 auto bigDataFun(size_t extra)
3678 {
3679 ensureAddable(extra);
3680 return (() @trusted => _data.arr.ptr[0 .. _data.arr.length + extra])();
3681 }
3682 auto bigData = bigDataFun(items.length);
3683
3684 immutable len = _data.arr.length;
3685 immutable newlen = bigData.length;
3686
3687 alias UT = Unqual!T;
3688
3689 static if (is(typeof(_data.arr[] = items[])) &&
3690 !hasElaborateAssign!UT && isAssignable!(UT, ElementEncodingType!Range))
3691 {
3692 bigData[len .. newlen] = items[];
3693 }
3694 else
3695 {
3696 import core.internal.lifetime : emplaceRef;
foreach(ref it;bigData[len..newlen])3697 foreach (ref it ; bigData[len .. newlen])
3698 {
3699 emplaceRef!T(it, items.front);
3700 items.popFront();
3701 }
3702 }
3703
3704 //We do this at the end, in case of exceptions
3705 _data.arr = bigData;
3706 }
3707 else static if (isSomeChar!T && isSomeChar!(ElementType!Range) &&
3708 !is(immutable T == immutable ElementType!Range))
3709 {
3710 // need to decode and encode
3711 import std.utf : decodeFront;
3712 while (!items.empty)
3713 {
3714 auto c = items.decodeFront;
3715 put(c);
3716 }
3717 }
3718 else
3719 {
3720 //pragma(msg, Range.stringof);
3721 // Generic input range
3722 for (; !items.empty; items.popFront())
3723 {
3724 put(items.front);
3725 }
3726 }
3727 }
3728
3729 /**
3730 * Appends to the managed array.
3731 *
3732 * See_Also: $(LREF Appender.put)
3733 */
3734 alias opOpAssign(string op : "~") = put;
3735
3736 // only allow overwriting data on non-immutable and non-const data
3737 static if (isMutable!T)
3738 {
3739 /**
3740 * Clears the managed array. This allows the elements of the array to be reused
3741 * for appending.
3742 *
3743 * Note: clear is disabled for immutable or const element types, due to the
3744 * possibility that `Appender` might overwrite immutable data.
3745 */
clear()3746 void clear() @trusted pure nothrow
3747 {
3748 if (_data)
3749 {
3750 _data.arr = _data.arr.ptr[0 .. 0];
3751 }
3752 }
3753
3754 /**
3755 * Shrinks the managed array to the given length.
3756 *
3757 * Throws: `Exception` if newlength is greater than the current array length.
3758 * Note: shrinkTo is disabled for immutable or const element types.
3759 */
shrinkTo(size_t newlength)3760 void shrinkTo(size_t newlength) @trusted pure
3761 {
3762 import std.exception : enforce;
3763 if (_data)
3764 {
3765 enforce(newlength <= _data.arr.length, "Attempting to shrink Appender with newlength > length");
3766 _data.arr = _data.arr.ptr[0 .. newlength];
3767 }
3768 else
3769 enforce(newlength == 0, "Attempting to shrink empty Appender with non-zero newlength");
3770 }
3771 }
3772
3773 /**
3774 * Gives a string in the form of `Appender!(A)(data)`.
3775 *
3776 * Params:
3777 * w = A `char` accepting
3778 * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives).
3779 * fmt = A $(REF FormatSpec, std, format) which controls how the array
3780 * is formatted.
3781 * Returns:
3782 * A `string` if `writer` is not set; `void` otherwise.
3783 */
toString()3784 string toString()() const
3785 {
3786 import std.format.spec : singleSpec;
3787
3788 auto app = appender!string();
3789 auto spec = singleSpec("%s");
3790 immutable len = _data ? _data.arr.length : 0;
3791 // different reserve lengths because each element in a
3792 // non-string-like array uses two extra characters for `, `.
3793 static if (isSomeString!A)
3794 {
3795 app.reserve(len + 25);
3796 }
3797 else
3798 {
3799 // Multiplying by three is a very conservative estimate of
3800 // length, as it assumes each element is only one char
3801 app.reserve((len * 3) + 25);
3802 }
3803 toString(app, spec);
3804 return app.data;
3805 }
3806
3807 import std.format.spec : FormatSpec;
3808
3809 /// ditto
3810 template toString(Writer)
3811 if (isOutputRange!(Writer, char))
3812 {
3813 void toString(ref Writer w, scope const ref FormatSpec!char fmt) const
3814 {
3815 import std.format.write : formatValue;
3816 import std.range.primitives : put;
3817 put(w, Unqual!(typeof(this)).stringof);
3818 put(w, '(');
3819 formatValue(w, data, fmt);
3820 put(w, ')');
3821 }
3822 }
3823 }
3824
3825 ///
3826 @safe pure nothrow unittest
3827 {
3828 auto app = appender!string();
3829 string b = "abcdefg";
3830 foreach (char c; b)
3831 app.put(c);
3832 assert(app[] == "abcdefg");
3833
3834 int[] a = [ 1, 2 ];
3835 auto app2 = appender(a);
3836 app2.put(3);
3837 app2.put([ 4, 5, 6 ]);
3838 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
3839 }
3840
3841 @safe pure unittest
3842 {
3843 import std.format : format;
3844 import std.format.spec : singleSpec;
3845
3846 auto app = appender!(int[])();
3847 app.put(1);
3848 app.put(2);
3849 app.put(3);
3850 assert("%s".format(app) == "Appender!(int[])(%s)".format([1,2,3]));
3851
3852 auto app2 = appender!string();
3853 auto spec = singleSpec("%s");
3854 app.toString(app2, spec);
3855 assert(app2[] == "Appender!(int[])([1, 2, 3])");
3856
3857 auto app3 = appender!string();
3858 spec = singleSpec("%(%04d, %)");
3859 app.toString(app3, spec);
3860 assert(app3[] == "Appender!(int[])(0001, 0002, 0003)");
3861 }
3862
3863 // https://issues.dlang.org/show_bug.cgi?id=17251
3864 @safe pure nothrow unittest
3865 {
3866 static struct R
3867 {
frontR3868 int front() const { return 0; }
emptyR3869 bool empty() const { return true; }
popFrontR3870 void popFront() {}
3871 }
3872
3873 auto app = appender!(R[]);
3874 const(R)[1] r;
3875 app.put(r[0]);
3876 app.put(r[]);
3877 }
3878
3879 // https://issues.dlang.org/show_bug.cgi?id=13300
3880 @safe pure nothrow unittest
3881 {
test(bool isPurePostblit)3882 static test(bool isPurePostblit)()
3883 {
3884 static if (!isPurePostblit)
3885 static int i;
3886
3887 struct Simple
3888 {
3889 @disable this(); // Without this, it works.
3890 static if (!isPurePostblit)
3891 this(this) { i++; }
3892 else
3893 pure this(this) { }
3894
3895 private:
3896 this(int tmp) { }
3897 }
3898
3899 struct Range
3900 {
3901 @property Simple front() { return Simple(0); }
3902 void popFront() { count++; }
3903 @property empty() { return count < 3; }
3904 size_t count;
3905 }
3906
3907 Range r;
3908 auto a = r.array();
3909 }
3910
3911 static assert(__traits(compiles, () pure { test!true(); }));
3912 static assert(!__traits(compiles, () pure { test!false(); }));
3913 }
3914
3915 // https://issues.dlang.org/show_bug.cgi?id=19572
3916 @safe pure nothrow unittest
3917 {
3918 static struct Struct
3919 {
3920 int value;
3921
funStruct3922 int fun() const { return 23; }
3923
3924 alias fun this;
3925 }
3926
3927 Appender!(Struct[]) appender;
3928
3929 appender.put(const(Struct)(42));
3930
3931 auto result = appender[][0];
3932
3933 assert(result.value != 23);
3934 }
3935
3936 @safe pure unittest
3937 {
3938 import std.conv : to;
3939 import std.utf : byCodeUnit;
3940 auto str = "ウェブサイト";
3941 auto wstr = appender!wstring();
3942 put(wstr, str.byCodeUnit);
3943 assert(wstr.data == str.to!wstring);
3944 }
3945
3946 // https://issues.dlang.org/show_bug.cgi?id=21256
3947 @safe pure unittest
3948 {
3949 Appender!string app1;
3950 app1.toString();
3951
3952 Appender!(int[]) app2;
3953 app2.toString();
3954 }
3955
3956 //Calculates an efficient growth scheme based on the old capacity
3957 //of data, and the minimum requested capacity.
3958 //arg curLen: The current length
3959 //arg reqLen: The length as requested by the user
3960 //ret sugLen: A suggested growth.
appenderNewCapacity(size_t TSizeOf)3961 private size_t appenderNewCapacity(size_t TSizeOf)(size_t curLen, size_t reqLen)
3962 {
3963 import core.bitop : bsr;
3964 import std.algorithm.comparison : max;
3965 if (curLen == 0)
3966 return max(reqLen,8);
3967 ulong mult = 100 + (1000UL) / (bsr(curLen * TSizeOf) + 1);
3968 // limit to doubling the length, we don't want to grow too much
3969 if (mult > 200)
3970 mult = 200;
3971 auto sugLen = cast(size_t)((curLen * mult + 99) / 100);
3972 return max(reqLen, sugLen);
3973 }
3974
3975 /**
3976 * A version of $(LREF Appender) that can update an array in-place.
3977 * It forwards all calls to an underlying appender implementation.
3978 * Any calls made to the appender also update the pointer to the
3979 * original array passed in.
3980 *
3981 * Tip: Use the `arrayPtr` overload of $(LREF appender) for construction with type-inference.
3982 *
3983 * Params:
3984 * A = The array type to simulate
3985 */
3986 struct RefAppender(A)
3987 if (isDynamicArray!A)
3988 {
3989 private alias T = ElementEncodingType!A;
3990
3991 private
3992 {
3993 Appender!A impl;
3994 A* arr;
3995 }
3996
3997 /**
3998 * Constructs a `RefAppender` with a given array reference. This does not copy the
3999 * data. If the array has a larger capacity as determined by `arr.capacity`, it
4000 * will be used by the appender.
4001 *
4002 * Note: Do not use built-in appending (i.e. `~=`) on the original array
4003 * until you are done with the appender, because subsequent calls to the appender
4004 * will reallocate the array data without those appends.
4005 *
4006 * Params:
4007 * arr = Pointer to an array. Must not be _null.
4008 */
this(A * arr)4009 this(A* arr)
4010 {
4011 impl = Appender!A(*arr);
4012 this.arr = arr;
4013 }
4014
4015 /** Wraps remaining `Appender` methods such as $(LREF put).
4016 * Params:
4017 * fn = Method name to call.
4018 * args = Arguments to pass to the method.
4019 */
4020 void opDispatch(string fn, Args...)(Args args)
4021 if (__traits(compiles, (Appender!A a) => mixin("a." ~ fn ~ "(args)")))
4022 {
4023 // we do it this way because we can't cache a void return
4024 scope(exit) *this.arr = impl[];
4025 mixin("return impl." ~ fn ~ "(args);");
4026 }
4027
4028 /**
4029 * Appends `rhs` to the managed array.
4030 * Params:
4031 * rhs = Element or range.
4032 */
4033 void opOpAssign(string op : "~", U)(U rhs)
4034 if (__traits(compiles, (Appender!A a){ a.put(rhs); }))
4035 {
4036 scope(exit) *this.arr = impl[];
4037 impl.put(rhs);
4038 }
4039
4040 /**
4041 * Returns the capacity of the array (the maximum number of elements the
4042 * managed array can accommodate before triggering a reallocation). If any
4043 * appending will reallocate, `capacity` returns `0`.
4044 */
capacity()4045 @property size_t capacity() const
4046 {
4047 return impl.capacity;
4048 }
4049
4050 /* Use opSlice() instead.
4051 * Returns: the managed array.
4052 */
inout(T)4053 @property inout(T)[] data() inout
4054 {
4055 return impl[];
4056 }
4057
4058 /**
4059 * Returns: the managed array.
4060 */
opSlice()4061 @property inout(ElementEncodingType!A)[] opSlice() inout
4062 {
4063 return impl[];
4064 }
4065 }
4066
4067 ///
4068 @safe pure nothrow
4069 unittest
4070 {
4071 int[] a = [1, 2];
4072 auto app2 = appender(&a);
4073 assert(app2[] == [1, 2]);
4074 assert(a == [1, 2]);
4075 app2 ~= 3;
4076 app2 ~= [4, 5, 6];
4077 assert(app2[] == [1, 2, 3, 4, 5, 6]);
4078 assert(a == [1, 2, 3, 4, 5, 6]);
4079
4080 app2.reserve(5);
4081 assert(app2.capacity >= 5);
4082 }
4083
4084 /++
4085 Convenience function that returns an $(LREF Appender) instance,
4086 optionally initialized with `array`.
4087 +/
4088 Appender!A appender(A)()
4089 if (isDynamicArray!A)
4090 {
4091 return Appender!A(null);
4092 }
4093 /// ditto
4094 Appender!(E[]) appender(A : E[], E)(auto ref A array)
4095 {
4096 static assert(!isStaticArray!A || __traits(isRef, array),
4097 "Cannot create Appender from an rvalue static array");
4098
4099 return Appender!(E[])(array);
4100 }
4101
4102 @safe pure nothrow unittest
4103 {
4104 auto app = appender!(char[])();
4105 string b = "abcdefg";
4106 foreach (char c; b) app.put(c);
4107 assert(app[] == "abcdefg");
4108 }
4109
4110 @safe pure nothrow unittest
4111 {
4112 auto app = appender!(char[])();
4113 string b = "abcdefg";
4114 foreach (char c; b) app ~= c;
4115 assert(app[] == "abcdefg");
4116 }
4117
4118 @safe pure nothrow unittest
4119 {
4120 int[] a = [ 1, 2 ];
4121 auto app2 = appender(a);
4122 assert(app2[] == [ 1, 2 ]);
4123 app2.put(3);
4124 app2.put([ 4, 5, 6 ][]);
4125 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
4126 app2.put([ 7 ]);
4127 assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
4128 }
4129
4130 @safe pure nothrow unittest
4131 {
4132 auto app4 = appender([]);
4133 try // shrinkTo may throw
4134 {
4135 app4.shrinkTo(0);
4136 }
4137 catch (Exception) assert(0);
4138 }
4139
4140 // https://issues.dlang.org/show_bug.cgi?id=5663
4141 // https://issues.dlang.org/show_bug.cgi?id=9725
4142 @safe pure nothrow unittest
4143 {
4144 import std.exception : assertNotThrown;
4145
4146 static foreach (S; AliasSeq!(char[], const(char)[], string))
4147 {
4148 {
4149 Appender!S app5663i;
4150 assertNotThrown(app5663i.put("\xE3"));
4151 assert(app5663i[] == "\xE3");
4152
4153 Appender!S app5663c;
4154 assertNotThrown(app5663c.put(cast(const(char)[])"\xE3"));
4155 assert(app5663c[] == "\xE3");
4156
4157 Appender!S app5663m;
4158 assertNotThrown(app5663m.put("\xE3".dup));
4159 assert(app5663m[] == "\xE3");
4160 }
4161 // ditto for ~=
4162 {
4163 Appender!S app5663i;
4164 assertNotThrown(app5663i ~= "\xE3");
4165 assert(app5663i[] == "\xE3");
4166
4167 Appender!S app5663c;
4168 assertNotThrown(app5663c ~= cast(const(char)[])"\xE3");
4169 assert(app5663c[] == "\xE3");
4170
4171 Appender!S app5663m;
4172 assertNotThrown(app5663m ~= "\xE3".dup);
4173 assert(app5663m[] == "\xE3");
4174 }
4175 }
4176 }
4177
4178 // https://issues.dlang.org/show_bug.cgi?id=10122
4179 @safe pure nothrow unittest
4180 {
4181 import std.exception : assertCTFEable;
4182
4183 static struct S10122
4184 {
4185 int val;
4186
4187 @disable this();
thisS101224188 this(int v) @safe pure nothrow { val = v; }
4189 }
4190 assertCTFEable!(
4191 {
4192 auto w = appender!(S10122[])();
4193 w.put(S10122(1));
4194 assert(w[].length == 1 && w[][0].val == 1);
4195 });
4196 }
4197
4198 @safe pure nothrow unittest
4199 {
4200 import std.exception : assertThrown;
4201
4202 int[] a = [ 1, 2 ];
4203 auto app2 = appender(a);
4204 assert(app2[] == [ 1, 2 ]);
4205 app2 ~= 3;
4206 app2 ~= [ 4, 5, 6 ][];
4207 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
4208 app2 ~= [ 7 ];
4209 assert(app2[] == [ 1, 2, 3, 4, 5, 6, 7 ]);
4210
4211 app2.reserve(5);
4212 assert(app2.capacity >= 5);
4213
4214 try // shrinkTo may throw
4215 {
4216 app2.shrinkTo(3);
4217 }
4218 catch (Exception) assert(0);
4219 assert(app2[] == [ 1, 2, 3 ]);
4220 assertThrown(app2.shrinkTo(5));
4221
4222 const app3 = app2;
4223 assert(app3.capacity >= 3);
4224 assert(app3[] == [1, 2, 3]);
4225 }
4226
4227 ///
4228 @safe pure nothrow
4229 unittest
4230 {
4231 auto w = appender!string;
4232 // pre-allocate space for at least 10 elements (this avoids costly reallocations)
4233 w.reserve(10);
4234 assert(w.capacity >= 10);
4235
4236 w.put('a'); // single elements
4237 w.put("bc"); // multiple elements
4238
4239 // use the append syntax
4240 w ~= 'd';
4241 w ~= "ef";
4242
4243 assert(w[] == "abcdef");
4244 }
4245
4246 @safe pure nothrow unittest
4247 {
4248 auto w = appender!string();
4249 w.reserve(4);
4250 cast(void) w.capacity;
4251 cast(void) w[];
4252 try
4253 {
4254 wchar wc = 'a';
4255 dchar dc = 'a';
4256 w.put(wc); // decoding may throw
4257 w.put(dc); // decoding may throw
4258 }
4259 catch (Exception) assert(0);
4260 }
4261
4262 @safe pure nothrow unittest
4263 {
4264 auto w = appender!(int[])();
4265 w.reserve(4);
4266 cast(void) w.capacity;
4267 cast(void) w[];
4268 w.put(10);
4269 w.put([10]);
4270 w.clear();
4271 try
4272 {
4273 w.shrinkTo(0);
4274 }
4275 catch (Exception) assert(0);
4276
4277 struct N
4278 {
4279 int payload;
4280 alias payload this;
4281 }
4282 w.put(N(1));
4283 w.put([N(2)]);
4284
S(T)4285 struct S(T)
4286 {
4287 @property bool empty() { return true; }
4288 @property T front() { return T.init; }
4289 void popFront() {}
4290 }
4291 S!int r;
4292 w.put(r);
4293 }
4294
4295 // https://issues.dlang.org/show_bug.cgi?id=10690
4296 @safe pure nothrow unittest
4297 {
4298 import std.algorithm.iteration : filter;
4299 import std.typecons : tuple;
4300 [tuple(1)].filter!(t => true).array; // No error
4301 [tuple("A")].filter!(t => true).array; // error
4302 }
4303
4304 @safe pure nothrow unittest
4305 {
4306 import std.range;
4307 //Coverage for put(Range)
4308 struct S1
4309 {
4310 }
4311 struct S2
4312 {
opAssign(S2)4313 void opAssign(S2){}
4314 }
4315 auto a1 = Appender!(S1[])();
4316 auto a2 = Appender!(S2[])();
4317 auto au1 = Appender!(const(S1)[])();
4318 a1.put(S1().repeat().take(10));
4319 a2.put(S2().repeat().take(10));
4320 auto sc1 = const(S1)();
4321 au1.put(sc1.repeat().take(10));
4322 }
4323
4324 @system pure unittest
4325 {
4326 import std.range;
4327 struct S2
4328 {
opAssignS24329 void opAssign(S2){}
4330 }
4331 auto au2 = Appender!(const(S2)[])();
4332 auto sc2 = const(S2)();
4333 au2.put(sc2.repeat().take(10));
4334 }
4335
4336 @system pure nothrow unittest
4337 {
4338 struct S
4339 {
4340 int* p;
4341 }
4342
4343 auto a0 = Appender!(S[])();
4344 auto a1 = Appender!(const(S)[])();
4345 auto a2 = Appender!(immutable(S)[])();
4346 auto s0 = S(null);
4347 auto s1 = const(S)(null);
4348 auto s2 = immutable(S)(null);
4349 a1.put(s0);
4350 a1.put(s1);
4351 a1.put(s2);
4352 a1.put([s0]);
4353 a1.put([s1]);
4354 a1.put([s2]);
4355 a0.put(s0);
4356 static assert(!is(typeof(a0.put(a1))));
4357 static assert(!is(typeof(a0.put(a2))));
4358 a0.put([s0]);
4359 static assert(!is(typeof(a0.put([a1]))));
4360 static assert(!is(typeof(a0.put([a2]))));
4361 static assert(!is(typeof(a2.put(a0))));
4362 static assert(!is(typeof(a2.put(a1))));
4363 a2.put(s2);
4364 static assert(!is(typeof(a2.put([a0]))));
4365 static assert(!is(typeof(a2.put([a1]))));
4366 a2.put([s2]);
4367 }
4368
4369 // https://issues.dlang.org/show_bug.cgi?id=9528
4370 @safe pure nothrow unittest
4371 {
fastCopy(E)4372 const(E)[] fastCopy(E)(E[] src) {
4373 auto app = appender!(const(E)[])();
4374 foreach (i, e; src)
4375 app.put(e);
4376 return app[];
4377 }
4378
4379 class C {}
4380 struct S { const(C) c; }
4381 S[] s = [ S(new C) ];
4382
4383 auto t = fastCopy(s); // Does not compile
4384 assert(t.length == 1);
4385 }
4386
4387 // https://issues.dlang.org/show_bug.cgi?id=10753
4388 @safe pure unittest
4389 {
4390 import std.algorithm.iteration : map;
4391 struct Foo {
4392 immutable dchar d;
4393 }
4394 struct Bar {
4395 immutable int x;
4396 }
4397 "12".map!Foo.array;
4398 [1, 2].map!Bar.array;
4399 }
4400
4401 @safe pure nothrow unittest
4402 {
4403 import std.algorithm.comparison : equal;
4404
4405 //New appender signature tests
4406 alias mutARR = int[];
4407 alias conARR = const(int)[];
4408 alias immARR = immutable(int)[];
4409
4410 mutARR mut;
4411 conARR con;
4412 immARR imm;
4413
4414 auto app1 = Appender!mutARR(mut); //Always worked. Should work. Should not create a warning.
4415 app1.put(7);
4416 assert(equal(app1[], [7]));
4417 static assert(!is(typeof(Appender!mutARR(con)))); //Never worked. Should not work.
4418 static assert(!is(typeof(Appender!mutARR(imm)))); //Never worked. Should not work.
4419
4420 auto app2 = Appender!conARR(mut); //Always worked. Should work. Should not create a warning.
4421 app2.put(7);
4422 assert(equal(app2[], [7]));
4423 auto app3 = Appender!conARR(con); //Didn't work. Now works. Should not create a warning.
4424 app3.put(7);
4425 assert(equal(app3[], [7]));
4426 auto app4 = Appender!conARR(imm); //Didn't work. Now works. Should not create a warning.
4427 app4.put(7);
4428 assert(equal(app4[], [7]));
4429
4430 //{auto app = Appender!immARR(mut);} //Worked. Will cease to work. Creates warning.
4431 //static assert(!is(typeof(Appender!immARR(mut)))); //Worked. Will cease to work. Uncomment me after full deprecation.
4432 static assert(!is(typeof(Appender!immARR(con)))); //Never worked. Should not work.
4433 auto app5 = Appender!immARR(imm); //Didn't work. Now works. Should not create a warning.
4434 app5.put(7);
4435 assert(equal(app5[], [7]));
4436
4437 //Deprecated. Please uncomment and make sure this doesn't work:
4438 //char[] cc;
4439 //static assert(!is(typeof(Appender!string(cc))));
4440
4441 //This should always work:
4442 auto app6 = appender!string(null);
4443 assert(app6[] == null);
4444 auto app7 = appender!(const(char)[])(null);
4445 assert(app7[] == null);
4446 auto app8 = appender!(char[])(null);
4447 assert(app8[] == null);
4448 }
4449
4450 @safe pure nothrow unittest //Test large allocations (for GC.extend)
4451 {
4452 import std.algorithm.comparison : equal;
4453 import std.range;
4454 Appender!(char[]) app;
4455 app.reserve(1); //cover reserve on non-initialized
4456 foreach (_; 0 .. 100_000)
4457 app.put('a');
4458 assert(equal(app[], 'a'.repeat(100_000)));
4459 }
4460
4461 @safe pure nothrow unittest
4462 {
4463 auto reference = new ubyte[](2048 + 1); //a number big enough to have a full page (EG: the GC extends)
4464 auto arr = reference.dup;
4465 auto app = appender(arr[0 .. 0]);
4466 app.reserve(1); //This should not trigger a call to extend
4467 app.put(ubyte(1)); //Don't clobber arr
4468 assert(reference[] == arr[]);
4469 }
4470
4471 @safe pure nothrow unittest // clear method is supported only for mutable element types
4472 {
4473 Appender!string app;
4474 app.put("foo");
4475 static assert(!__traits(compiles, app.clear()));
4476 assert(app[] == "foo");
4477 }
4478
4479 @safe pure nothrow unittest
4480 {
4481 static struct D//dynamic
4482 {
4483 int[] i;
4484 alias i this;
4485 }
4486 static struct S//static
4487 {
4488 int[5] i;
4489 alias i this;
4490 }
4491 static assert(!is(Appender!(char[5])));
4492 static assert(!is(Appender!D));
4493 static assert(!is(Appender!S));
4494
4495 enum int[5] a = [];
4496 int[5] b;
4497 D d;
4498 S s;
foo()4499 int[5] foo(){return a;}
4500
4501 static assert(!is(typeof(appender(a))));
4502 static assert( is(typeof(appender(b))));
4503 static assert( is(typeof(appender(d))));
4504 static assert( is(typeof(appender(s))));
4505 static assert(!is(typeof(appender(foo()))));
4506 }
4507
4508 @system unittest
4509 {
4510 // https://issues.dlang.org/show_bug.cgi?id=13077
4511 static class A {}
4512
4513 // reduced case
4514 auto w = appender!(shared(A)[])();
4515 w.put(new shared A());
4516
4517 // original case
4518 import std.range;
4519 InputRange!(shared A) foo()
4520 {
4521 return [new shared A].inputRangeObject;
4522 }
4523 auto res = foo.array;
4524 assert(res.length == 1);
4525 }
4526
4527 /++
4528 Convenience function that returns a $(LREF RefAppender) instance initialized
4529 with `arrayPtr`. Don't use null for the array pointer, use the other
4530 version of `appender` instead.
4531 +/
4532 RefAppender!(E[]) appender(P : E[]*, E)(P arrayPtr)
4533 {
4534 return RefAppender!(E[])(arrayPtr);
4535 }
4536
4537 ///
4538 @safe pure nothrow
4539 unittest
4540 {
4541 int[] a = [1, 2];
4542 auto app2 = appender(&a);
4543 assert(app2[] == [1, 2]);
4544 assert(a == [1, 2]);
4545 app2 ~= 3;
4546 app2 ~= [4, 5, 6];
4547 assert(app2[] == [1, 2, 3, 4, 5, 6]);
4548 assert(a == [1, 2, 3, 4, 5, 6]);
4549
4550 app2.reserve(5);
4551 assert(app2.capacity >= 5);
4552 }
4553
4554 @safe pure nothrow unittest
4555 {
4556 auto arr = new char[0];
4557 auto app = appender(&arr);
4558 string b = "abcdefg";
4559 foreach (char c; b) app.put(c);
4560 assert(app[] == "abcdefg");
4561 assert(arr == "abcdefg");
4562 }
4563
4564 @safe pure nothrow unittest
4565 {
4566 auto arr = new char[0];
4567 auto app = appender(&arr);
4568 string b = "abcdefg";
4569 foreach (char c; b) app ~= c;
4570 assert(app[] == "abcdefg");
4571 assert(arr == "abcdefg");
4572 }
4573
4574 @safe pure nothrow unittest
4575 {
4576 int[] a = [ 1, 2 ];
4577 auto app2 = appender(&a);
4578 assert(app2[] == [ 1, 2 ]);
4579 assert(a == [ 1, 2 ]);
4580 app2.put(3);
4581 app2.put([ 4, 5, 6 ][]);
4582 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
4583 assert(a == [ 1, 2, 3, 4, 5, 6 ]);
4584 }
4585
4586 @safe pure nothrow unittest
4587 {
4588 import std.exception : assertThrown;
4589
4590 int[] a = [ 1, 2 ];
4591 auto app2 = appender(&a);
4592 assert(app2[] == [ 1, 2 ]);
4593 assert(a == [ 1, 2 ]);
4594 app2 ~= 3;
4595 app2 ~= [ 4, 5, 6 ][];
4596 assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
4597 assert(a == [ 1, 2, 3, 4, 5, 6 ]);
4598
4599 app2.reserve(5);
4600 assert(app2.capacity >= 5);
4601
4602 try // shrinkTo may throw
4603 {
4604 app2.shrinkTo(3);
4605 }
4606 catch (Exception) assert(0);
4607 assert(app2[] == [ 1, 2, 3 ]);
4608 assertThrown(app2.shrinkTo(5));
4609
4610 const app3 = app2;
4611 assert(app3.capacity >= 3);
4612 assert(app3[] == [1, 2, 3]);
4613 }
4614
4615 // https://issues.dlang.org/show_bug.cgi?id=14605
4616 @safe pure nothrow unittest
4617 {
4618 static assert(isOutputRange!(Appender!(int[]), int));
4619 static assert(isOutputRange!(RefAppender!(int[]), int));
4620 }
4621
4622 @safe pure nothrow unittest
4623 {
4624 Appender!(int[]) app;
4625 short[] range = [1, 2, 3];
4626 app.put(range);
4627 assert(app[] == [1, 2, 3]);
4628 }
4629
4630 @safe pure nothrow unittest
4631 {
4632 string s = "hello".idup;
4633 char[] a = "hello".dup;
4634 auto appS = appender(s);
4635 auto appA = appender(a);
4636 put(appS, 'w');
4637 put(appA, 'w');
4638 s ~= 'a'; //Clobbers here?
4639 a ~= 'a'; //Clobbers here?
4640 assert(appS[] == "hellow");
4641 assert(appA[] == "hellow");
4642 }
4643
4644 /++
4645 Constructs a static array from `a`.
4646 The type of elements can be specified implicitly so that $(D [1, 2].staticArray) results in `int[2]`,
4647 or explicitly, e.g. $(D [1, 2].staticArray!float) returns `float[2]`.
4648 When `a` is a range whose length is not known at compile time, the number of elements must be
4649 given as template argument (e.g. `myrange.staticArray!2`).
4650 Size and type can be combined, if the source range elements are implicitly
4651 convertible to the requested element type (eg: `2.iota.staticArray!(long[2])`).
4652 When the range `a` is known at compile time, it can also be specified as a
4653 template argument to avoid having to specify the number of elements
4654 (e.g.: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`).
4655
4656 Note: `staticArray` returns by value, so expressions involving large arrays may be inefficient.
4657
4658 Params:
4659 a = The input elements. If there are less elements than the specified length of the static array,
4660 the rest of it is default-initialized. If there are more than specified, the first elements
4661 up to the specified length are used.
4662 rangeLength = outputs the number of elements used from `a` to it. Optional.
4663
4664 Returns: A static array constructed from `a`.
4665 +/
pragma(inline,true)4666 pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] a)
4667 {
4668 return a;
4669 }
4670
4671 /// static array from array literal
4672 nothrow pure @safe @nogc unittest
4673 {
4674 auto a = [0, 1].staticArray;
4675 static assert(is(typeof(a) == int[2]));
4676 assert(a == [0, 1]);
4677 }
4678
4679 pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a)
4680 if (!is(T == U) && is(T : U))
4681 {
4682 return a[].staticArray!(U[n]);
4683 }
4684
4685 /// static array from array with implicit casting of elements
4686 nothrow pure @safe @nogc unittest
4687 {
4688 auto b = [0, 1].staticArray!long;
4689 static assert(is(typeof(b) == long[2]));
4690 assert(b == [0, 1]);
4691 }
4692
4693 nothrow pure @safe @nogc unittest
4694 {
4695 int val = 3;
4696 static immutable gold = [1, 2, 3];
4697 [1, 2, val].staticArray.checkStaticArray!int([1, 2, 3]);
4698
4699 @nogc void checkNogc()
4700 {
4701 [1, 2, val].staticArray.checkStaticArray!int(gold);
4702 }
4703
4704 checkNogc();
4705
4706 [1, 2, val].staticArray!double.checkStaticArray!double(gold);
4707 [1, 2, 3].staticArray!int.checkStaticArray!int(gold);
4708
4709 [1, 2, 3].staticArray!(const(int)).checkStaticArray!(const(int))(gold);
4710 [1, 2, 3].staticArray!(const(double)).checkStaticArray!(const(double))(gold);
4711 {
4712 const(int)[3] a2 = [1, 2, 3].staticArray;
4713 }
4714
4715 [cast(byte) 1, cast(byte) 129].staticArray.checkStaticArray!byte([1, -127]);
4716 }
4717
4718 /// ditto
4719 auto staticArray(size_t n, T)(scope T a)
4720 if (isInputRange!T)
4721 {
4722 alias U = ElementType!T;
4723 return staticArray!(U[n], U, n)(a);
4724 }
4725
4726 /// ditto
4727 auto staticArray(size_t n, T)(scope T a, out size_t rangeLength)
4728 if (isInputRange!T)
4729 {
4730 alias U = ElementType!T;
4731 return staticArray!(U[n], U, n)(a, rangeLength);
4732 }
4733
4734 /// ditto
4735 auto staticArray(Un : U[n], U, size_t n, T)(scope T a)
4736 if (isInputRange!T && is(ElementType!T : U))
4737 {
4738 size_t extraStackSpace;
4739 return staticArray!(Un, U, n)(a, extraStackSpace);
4740 }
4741
4742 /// ditto
4743 auto staticArray(Un : U[n], U, size_t n, T)(scope T a, out size_t rangeLength)
4744 if (isInputRange!T && is(ElementType!T : U))
4745 {
4746 import std.algorithm.mutation : uninitializedFill;
4747 import std.range : take;
4748 import core.internal.lifetime : emplaceRef;
4749
4750 if (__ctfe)
4751 {
4752 size_t i;
4753 // Compile-time version to avoid unchecked memory access.
4754 Unqual!U[n] ret;
4755 for (auto iter = a.take(n); !iter.empty; iter.popFront())
4756 {
4757 ret[i] = iter.front;
4758 i++;
4759 }
4760
4761 rangeLength = i;
4762 return (() @trusted => cast(U[n]) ret)();
4763 }
4764
4765 auto ret = (() @trusted
4766 {
4767 Unqual!U[n] theArray = void;
4768 return theArray;
4769 }());
4770
4771 size_t i;
4772 if (true)
4773 {
4774 // ret was void-initialized so let's initialize the unfilled part manually.
4775 // also prevents destructors to be called on uninitialized memory if
4776 // an exception is thrown
4777 scope (exit) ret[i .. $].uninitializedFill(U.init);
4778
4779 for (auto iter = a.take(n); !iter.empty; iter.popFront())
4780 {
4781 emplaceRef!U(ret[i++], iter.front);
4782 }
4783 }
4784
4785 rangeLength = i;
4786 return (() @trusted => cast(U[n]) ret)();
4787 }
4788
4789 /// static array from range + size
4790 nothrow pure @safe @nogc unittest
4791 {
4792 import std.range : iota;
4793
4794 auto input = 3.iota;
4795 auto a = input.staticArray!2;
4796 static assert(is(typeof(a) == int[2]));
4797 assert(a == [0, 1]);
4798 auto b = input.staticArray!(long[4]);
4799 static assert(is(typeof(b) == long[4]));
4800 assert(b == [0, 1, 2, 0]);
4801 }
4802
4803 // Tests that code compiles when there is an elaborate destructor and exceptions
4804 // are thrown. Unfortunately can't test that memory is initialized
4805 // before having a destructor called on it.
4806 @safe nothrow unittest
4807 {
4808 // exists only to allow doing something in the destructor. Not tested
4809 // at the end because value appears to depend on implementation of the.
4810 // function.
4811 static int preventersDestroyed = 0;
4812
4813 static struct CopyPreventer
4814 {
4815 bool on = false;
thisCopyPreventer4816 this(this)
4817 {
4818 if (on) throw new Exception("Thou shalt not copy past me!");
4819 }
4820
~thisCopyPreventer4821 ~this()
4822 {
4823 preventersDestroyed++;
4824 }
4825 }
4826 auto normalArray =
4827 [
4828 CopyPreventer(false),
4829 CopyPreventer(false),
4830 CopyPreventer(true),
4831 CopyPreventer(false),
4832 CopyPreventer(true),
4833 ];
4834
4835 try
4836 {
4837 auto staticArray = normalArray.staticArray!5;
4838 assert(false);
4839 }
catch(Exception e)4840 catch (Exception e){}
4841 }
4842
4843
4844 nothrow pure @safe @nogc unittest
4845 {
4846 auto a = [1, 2].staticArray;
4847 assert(is(typeof(a) == int[2]) && a == [1, 2]);
4848
4849 import std.range : iota;
4850
4851 2.iota.staticArray!2.checkStaticArray!int([0, 1]);
4852 2.iota.staticArray!(double[2]).checkStaticArray!double([0, 1]);
4853 2.iota.staticArray!(long[2]).checkStaticArray!long([0, 1]);
4854 }
4855
4856 nothrow pure @safe @nogc unittest
4857 {
4858 import std.range : iota;
4859 size_t copiedAmount;
4860 2.iota.staticArray!1(copiedAmount);
4861 assert(copiedAmount == 1);
4862 2.iota.staticArray!3(copiedAmount);
4863 assert(copiedAmount == 2);
4864 }
4865
4866 /// ditto
4867 auto staticArray(alias a)()
4868 if (isInputRange!(typeof(a)))
4869 {
4870 return .staticArray!(size_t(a.length))(a);
4871 }
4872
4873 /// ditto
4874 auto staticArray(U, alias a)()
4875 if (isInputRange!(typeof(a)))
4876 {
4877 return .staticArray!(U[size_t(a.length)])(a);
4878 }
4879
4880 /// static array from CT range
4881 nothrow pure @safe @nogc unittest
4882 {
4883 import std.range : iota;
4884
4885 enum a = staticArray!(2.iota);
4886 static assert(is(typeof(a) == int[2]));
4887 assert(a == [0, 1]);
4888
4889 enum b = staticArray!(long, 2.iota);
4890 static assert(is(typeof(b) == long[2]));
4891 assert(b == [0, 1]);
4892 }
4893
4894 nothrow pure @safe @nogc unittest
4895 {
4896 import std.range : iota;
4897
4898 enum a = staticArray!(2.iota);
4899 staticArray!(2.iota).checkStaticArray!int([0, 1]);
4900 staticArray!(double, 2.iota).checkStaticArray!double([0, 1]);
4901 staticArray!(long, 2.iota).checkStaticArray!long([0, 1]);
4902 }
4903
version(StdUnittest)4904 version (StdUnittest) private void checkStaticArray(T, T1, T2)(T1 a, T2 b) nothrow @safe pure @nogc
4905 {
4906 static assert(is(T1 == T[T1.length]));
4907 assert(a == b, "a must be equal to b");
4908 }
4909