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