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