1 2 /** 3 * Dynamic array implementation. 4 * 5 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 6 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/array.d, root/_array.d) 9 * Documentation: https://dlang.org/phobos/dmd_root_array.html 10 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/array.d 11 */ 12 13 module dmd.root.array; 14 15 import core.stdc.stdlib : _compare_fp_t; 16 import core.stdc.string; 17 18 import dmd.root.rmem; 19 import dmd.root.string; 20 21 // `qsort` is only `nothrow` since 2.081.0 22 private extern(C) void qsort(scope void* base, size_t nmemb, size_t size, _compare_fp_t compar) nothrow @nogc; 23 24 25 debug 26 { 27 debug = stomp; // flush out dangling pointer problems by stomping on unused memory 28 } 29 30 extern (C++) struct Array(T) 31 { 32 size_t length; 33 34 private: 35 T[] data; 36 enum SMALLARRAYCAP = 1; 37 T[SMALLARRAYCAP] smallarray; // inline storage for small arrays 38 39 public: 40 /******************* 41 * Params: 42 * dim = initial length of array 43 */ 44 this(size_t dim) pure nothrow scope 45 { 46 reserve(dim); 47 this.length = dim; 48 } 49 50 @disable this(this); 51 52 ~this() pure nothrow 53 { 54 debug (stomp) memset(data.ptr, 0xFF, data.length); 55 if (data.ptr != &smallarray[0]) 56 mem.xfree(data.ptr); 57 } 58 ///returns elements comma separated in [] 59 extern(D) const(char)[] toString() const 60 { 61 static const(char)[] toStringImpl(alias toStringFunc, Array)(Array* a, bool quoted = false) 62 { 63 const(char)[][] buf = (cast(const(char)[]*)mem.xcalloc((char[]).sizeof, a.length))[0 .. a.length]; 64 size_t len = 2; // [ and ] 65 const seplen = quoted ? 3 : 1; // ',' or null terminator and optionally '"' 66 if (a.length == 0) 67 len += 1; // null terminator 68 else 69 { 70 foreach (u; 0 .. a.length) 71 { 72 static if (is(typeof(a.data[u] is null))) 73 { 74 if (a.data[u] is null) 75 buf[u] = "null"; 76 else 77 buf[u] = toStringFunc(a.data[u]); 78 } 79 else 80 { 81 buf[u] = toStringFunc(a.data[u]); 82 } 83 84 len += buf[u].length + seplen; 85 } 86 } 87 char[] str = (cast(char*)mem.xmalloc_noscan(len))[0..len]; 88 89 str[0] = '['; 90 char* p = str.ptr + 1; 91 foreach (u; 0 .. a.length) 92 { 93 if (u) 94 *p++ = ','; 95 if (quoted) 96 *p++ = '"'; 97 memcpy(p, buf[u].ptr, buf[u].length); 98 p += buf[u].length; 99 if (quoted) 100 *p++ = '"'; 101 } 102 *p++ = ']'; 103 *p = 0; 104 assert(p - str.ptr == str.length - 1); // null terminator 105 mem.xfree(buf.ptr); 106 return str[0 .. $-1]; 107 } 108 109 static if (is(typeof(T.init.toString()))) 110 { 111 return toStringImpl!(a => a.toString)(&this); 112 } 113 else static if (is(typeof(T.init.toDString()))) 114 { 115 return toStringImpl!(a => a.toDString)(&this, true); 116 } 117 else 118 { 119 assert(0); 120 } 121 } 122 ///ditto 123 const(char)* toChars() const 124 { 125 return toString.ptr; 126 } 127 128 ref Array push(T ptr) return pure nothrow 129 { 130 reserve(1); 131 data[length++] = ptr; 132 return this; 133 } 134 135 extern (D) ref Array pushSlice(T[] a) return pure nothrow 136 { 137 const oldLength = length; 138 setDim(oldLength + a.length); 139 memcpy(data.ptr + oldLength, a.ptr, a.length * T.sizeof); 140 return this; 141 } 142 143 ref Array append(typeof(this)* a) return pure nothrow 144 { 145 insert(length, a); 146 return this; 147 } 148 149 void reserve(size_t nentries) pure nothrow 150 { 151 //printf("Array::reserve: length = %d, data.length = %d, nentries = %d\n", cast(int)length, cast(int)data.length, cast(int)nentries); 152 153 // Cold path 154 void enlarge(size_t nentries) 155 { 156 pragma(inline, false); // never inline cold path 157 if (data.length == 0) 158 { 159 // Not properly initialized, someone memset it to zero 160 if (nentries <= SMALLARRAYCAP) 161 { 162 data = SMALLARRAYCAP ? smallarray[] : null; 163 } 164 else 165 { 166 auto p = cast(T*)mem.xmalloc(nentries * T.sizeof); 167 data = p[0 .. nentries]; 168 } 169 } 170 else if (data.length == SMALLARRAYCAP) 171 { 172 const allocdim = length + nentries; 173 auto p = cast(T*)mem.xmalloc(allocdim * T.sizeof); 174 memcpy(p, smallarray.ptr, length * T.sizeof); 175 data = p[0 .. allocdim]; 176 } 177 else 178 { 179 /* Increase size by 1.5x to avoid excessive memory fragmentation 180 */ 181 auto increment = length / 2; 182 if (nentries > increment) // if 1.5 is not enough 183 increment = nentries; 184 const allocdim = length + increment; 185 debug (stomp) 186 { 187 // always move using allocate-copy-stomp-free 188 auto p = cast(T*)mem.xmalloc(allocdim * T.sizeof); 189 memcpy(p, data.ptr, length * T.sizeof); 190 memset(data.ptr, 0xFF, data.length * T.sizeof); 191 mem.xfree(data.ptr); 192 } 193 else 194 auto p = cast(T*)mem.xrealloc(data.ptr, allocdim * T.sizeof); 195 data = p[0 .. allocdim]; 196 } 197 198 debug (stomp) 199 { 200 if (length < data.length) 201 memset(data.ptr + length, 0xFF, (data.length - length) * T.sizeof); 202 } 203 else 204 { 205 if (mem.isGCEnabled) 206 if (length < data.length) 207 memset(data.ptr + length, 0xFF, (data.length - length) * T.sizeof); 208 } 209 } 210 211 if (data.length - length < nentries) // false means hot path 212 enlarge(nentries); 213 } 214 215 void remove(size_t i) pure nothrow @nogc 216 { 217 if (length - i - 1) 218 memmove(data.ptr + i, data.ptr + i + 1, (length - i - 1) * T.sizeof); 219 length--; 220 debug (stomp) memset(data.ptr + length, 0xFF, T.sizeof); 221 } 222 223 void insert(size_t index, typeof(this)* a) pure nothrow 224 { 225 if (a) 226 { 227 size_t d = a.length; 228 reserve(d); 229 if (length != index) 230 memmove(data.ptr + index + d, data.ptr + index, (length - index) * T.sizeof); 231 memcpy(data.ptr + index, a.data.ptr, d * T.sizeof); 232 length += d; 233 } 234 } 235 236 extern (D) void insert(size_t index, T[] a) pure nothrow 237 { 238 size_t d = a.length; 239 reserve(d); 240 if (length != index) 241 memmove(data.ptr + index + d, data.ptr + index, (length - index) * T.sizeof); 242 memcpy(data.ptr + index, a.ptr, d * T.sizeof); 243 length += d; 244 } 245 246 void insert(size_t index, T ptr) pure nothrow 247 { 248 reserve(1); 249 memmove(data.ptr + index + 1, data.ptr + index, (length - index) * T.sizeof); 250 data[index] = ptr; 251 length++; 252 } 253 254 /// Insert 'count' copies of 'value' at 'index' position 255 void insert(size_t index, size_t count, T value) pure nothrow 256 { 257 if (count == 0) 258 return; 259 reserve(count); 260 if (length != index) 261 memmove(data.ptr + index + count, data.ptr + index, (length - index) * T.sizeof); 262 data[index .. index + count] = value; 263 length += count; 264 } 265 266 void setDim(size_t newdim) pure nothrow 267 { 268 if (length < newdim) 269 { 270 reserve(newdim - length); 271 } 272 length = newdim; 273 } 274 275 size_t find(T ptr) const nothrow pure 276 { 277 foreach (i; 0 .. length) 278 if (data[i] is ptr) 279 return i; 280 return size_t.max; 281 } 282 283 bool contains(T ptr) const nothrow pure 284 { 285 return find(ptr) != size_t.max; 286 } 287 288 ref inout(T) opIndex(size_t i) inout nothrow pure 289 { 290 debug 291 // This is called so often the array bounds become expensive 292 return data[i]; 293 else 294 return data.ptr[i]; 295 } 296 297 inout(T)* tdata() inout pure nothrow @nogc @trusted 298 { 299 return data.ptr; 300 } 301 302 Array!T* copy() const pure nothrow 303 { 304 auto a = new Array!T(); 305 a.setDim(length); 306 memcpy(a.data.ptr, data.ptr, length * T.sizeof); 307 return a; 308 } 309 310 void shift(T ptr) pure nothrow 311 { 312 reserve(1); 313 memmove(data.ptr + 1, data.ptr, length * T.sizeof); 314 data[0] = ptr; 315 length++; 316 } 317 318 void zero() nothrow pure @nogc 319 { 320 data[0 .. length] = T.init; 321 } 322 323 T pop() nothrow pure @nogc 324 { 325 debug (stomp) 326 { 327 assert(length); 328 auto result = data[length - 1]; 329 remove(length - 1); 330 return result; 331 } 332 else 333 return data[--length]; 334 } 335 336 extern (D) inout(T)[] opSlice() inout nothrow pure @nogc 337 { 338 return data[0 .. length]; 339 } 340 341 extern (D) inout(T)[] opSlice(size_t a, size_t b) inout nothrow pure @nogc 342 { 343 assert(a <= b && b <= length); 344 return data[a .. b]; 345 } 346 347 /** 348 * Sort the elements of an array 349 * 350 * This function relies on `qsort`. 351 * 352 * Params: 353 * pred = Predicate to use. Should be a function of type 354 * `int function(scope const T* e1, scope const T* e2) nothrow`. 355 * The return value of this function should follow the 356 * usual C rule: `e1 >= e2 ? (e1 > e2) : -1`. 357 * The function can have D linkage. 358 * 359 * Returns: 360 * A reference to this, for easy chaining. 361 */ 362 extern(D) ref typeof(this) sort (alias pred) () nothrow 363 { 364 if (this.length < 2) 365 return this; 366 qsort(this.data.ptr, this.length, T.sizeof, &arraySortWrapper!(T, pred)); 367 return this; 368 } 369 370 /// Ditto, but use `opCmp` by default 371 extern(D) ref typeof(this) sort () () nothrow 372 if (is(typeof(this.data[0].opCmp(this.data[1])) : int)) 373 { 374 return this.sort!(function (scope const(T)* pe1, scope const(T)* pe2) => pe1.opCmp(*pe2)); 375 } 376 377 alias opDollar = length; 378 379 deprecated("use `.length` instead") 380 extern(D) size_t dim() const @nogc nothrow pure @safe { return length; } 381 } 382 383 unittest 384 { 385 // Test for objects implementing toString() 386 static struct S 387 { 388 int s = -1; 389 string toString() const 390 { 391 return "S"; 392 } 393 } 394 auto array = Array!S(4); 395 assert(array.toString() == "[S,S,S,S]"); 396 array.setDim(0); 397 assert(array.toString() == "[]"); 398 399 // Test for toDString() 400 auto strarray = Array!(const(char)*)(2); 401 strarray[0] = "hello"; 402 strarray[1] = "world"; 403 auto str = strarray.toString(); 404 assert(str == `["hello","world"]`); 405 // Test presence of null terminator. 406 assert(str.ptr[str.length] == '\0'); 407 408 // Test printing an array of classes, which can be null 409 static class C 410 { 411 override string toString() const 412 { 413 return "x"; 414 } 415 } 416 auto nullarray = Array!C(2); 417 nullarray[0] = new C(); 418 nullarray[1] = null; 419 assert(nullarray.toString() == `[x,null]`); 420 } 421 422 unittest 423 { 424 auto array = Array!double(4); 425 array.shift(10); 426 array.push(20); 427 array[2] = 15; 428 assert(array[0] == 10); 429 assert(array.find(10) == 0); 430 assert(array.find(20) == 5); 431 assert(!array.contains(99)); 432 array.remove(1); 433 assert(array.length == 5); 434 assert(array[1] == 15); 435 assert(array.pop() == 20); 436 assert(array.length == 4); 437 array.insert(1, 30); 438 assert(array[1] == 30); 439 assert(array[2] == 15); 440 } 441 442 unittest 443 { 444 auto arrayA = Array!int(0); 445 int[3] buf = [10, 15, 20]; 446 arrayA.pushSlice(buf); 447 assert(arrayA[] == buf[]); 448 auto arrayPtr = arrayA.copy(); 449 assert(arrayPtr); 450 assert((*arrayPtr)[] == arrayA[]); 451 assert(arrayPtr.tdata != arrayA.tdata); 452 453 arrayPtr.setDim(0); 454 int[2] buf2 = [100, 200]; 455 arrayPtr.pushSlice(buf2); 456 457 arrayA.append(arrayPtr); 458 assert(arrayA[3..$] == buf2[]); 459 arrayA.insert(0, arrayPtr); 460 assert(arrayA[] == [100, 200, 10, 15, 20, 100, 200]); 461 462 arrayA.zero(); 463 foreach(e; arrayA) 464 assert(e == 0); 465 466 arrayA.setDim(0); 467 arrayA.pushSlice([5, 6]); 468 arrayA.insert(1, [1, 2]); 469 assert(arrayA[] == [5, 1, 2, 6]); 470 arrayA.insert(0, [7, 8]); 471 arrayA.insert(arrayA.length, [0, 9]); 472 assert(arrayA[] == [7, 8, 5, 1, 2, 6, 0, 9]); 473 arrayA.insert(4, 3, 8); 474 assert(arrayA[] == [7, 8, 5, 1, 8, 8, 8, 2, 6, 0, 9]); 475 arrayA.insert(0, 3, 8); 476 assert(arrayA[] == [8, 8, 8, 7, 8, 5, 1, 8, 8, 8, 2, 6, 0, 9]); 477 arrayA.insert(arrayA.length, 3, 8); 478 assert(arrayA[] == [8, 8, 8, 7, 8, 5, 1, 8, 8, 8, 2, 6, 0, 9, 8, 8, 8]); 479 } 480 481 /** 482 * Exposes the given root Array as a standard D array. 483 * Params: 484 * array = the array to expose. 485 * Returns: 486 * The given array exposed to a standard D array. 487 */ 488 @property inout(T)[] peekSlice(T)(inout(Array!T)* array) pure nothrow @nogc 489 { 490 return array ? (*array)[] : null; 491 } 492 493 /** 494 * Splits the array at $(D index) and expands it to make room for $(D length) 495 * elements by shifting everything past $(D index) to the right. 496 * Params: 497 * array = the array to split. 498 * index = the index to split the array from. 499 * length = the number of elements to make room for starting at $(D index). 500 */ 501 void split(T)(ref Array!T array, size_t index, size_t length) pure nothrow 502 { 503 if (length > 0) 504 { 505 auto previousDim = array.length; 506 array.setDim(array.length + length); 507 for (size_t i = previousDim; i > index;) 508 { 509 i--; 510 array[i + length] = array[i]; 511 } 512 } 513 } 514 unittest 515 { 516 auto array = Array!int(); 517 array.split(0, 0); 518 assert([] == array[]); 519 array.push(1).push(3); 520 array.split(1, 1); 521 array[1] = 2; 522 assert([1, 2, 3] == array[]); 523 array.split(2, 3); 524 array[2] = 8; 525 array[3] = 20; 526 array[4] = 4; 527 assert([1, 2, 8, 20, 4, 3] == array[]); 528 array.split(0, 0); 529 assert([1, 2, 8, 20, 4, 3] == array[]); 530 array.split(0, 1); 531 array[0] = 123; 532 assert([123, 1, 2, 8, 20, 4, 3] == array[]); 533 array.split(0, 3); 534 array[0] = 123; 535 array[1] = 421; 536 array[2] = 910; 537 assert([123, 421, 910, 123, 1, 2, 8, 20, 4, 3] == (&array).peekSlice()); 538 } 539 540 /** 541 * Reverse an array in-place. 542 * Params: 543 * a = array 544 * Returns: 545 * reversed a[] 546 */ 547 T[] reverse(T)(T[] a) pure nothrow @nogc @safe 548 { 549 if (a.length > 1) 550 { 551 const mid = (a.length + 1) >> 1; 552 foreach (i; 0 .. mid) 553 { 554 T e = a[i]; 555 a[i] = a[$ - 1 - i]; 556 a[$ - 1 - i] = e; 557 } 558 } 559 return a; 560 } 561 562 unittest 563 { 564 int[] a1 = []; 565 assert(reverse(a1) == []); 566 int[] a2 = [2]; 567 assert(reverse(a2) == [2]); 568 int[] a3 = [2,3]; 569 assert(reverse(a3) == [3,2]); 570 int[] a4 = [2,3,4]; 571 assert(reverse(a4) == [4,3,2]); 572 int[] a5 = [2,3,4,5]; 573 assert(reverse(a5) == [5,4,3,2]); 574 } 575 576 unittest 577 { 578 //test toString/toChars. Identifier is a simple object that has a usable .toString 579 import dmd.identifier : Identifier; 580 import core.stdc.string : strcmp; 581 582 auto array = Array!Identifier(); 583 array.push(new Identifier("id1")); 584 array.push(new Identifier("id2")); 585 586 string expected = "[id1,id2]"; 587 assert(array.toString == expected); 588 assert(strcmp(array.toChars, expected.ptr) == 0); 589 } 590 591 /// Predicate to wrap a D function passed to `qsort` 592 private template arraySortWrapper(T, alias fn) 593 { 594 pragma(mangle, "arraySortWrapper_" ~ T.mangleof ~ "_" ~ fn.mangleof) 595 extern(C) int arraySortWrapper(scope const void* e1, scope const void* e2) 596 { 597 return fn(cast(const(T*))e1, cast(const(T*))e2); 598 } 599 } 600 601 // Test sorting 602 unittest 603 { 604 Array!(const(char)*) strings; 605 strings.push("World"); 606 strings.push("Foo"); 607 strings.push("baguette"); 608 strings.push("Avocado"); 609 strings.push("Hello"); 610 // Newer frontend versions will work with (e1, e2) and infer the type 611 strings.sort!(function (scope const char** e1, scope const char** e2) => strcmp(*e1, *e2)); 612 assert(strings[0] == "Avocado"); 613 assert(strings[1] == "Foo"); 614 assert(strings[2] == "Hello"); 615 assert(strings[3] == "World"); 616 assert(strings[4] == "baguette"); 617 618 /// opCmp automatically supported 619 static struct MyStruct 620 { 621 int a; 622 623 int opCmp(const ref MyStruct other) const nothrow 624 { 625 // Reverse order 626 return other.a - this.a; 627 } 628 } 629 630 Array!MyStruct arr1; 631 arr1.push(MyStruct(2)); 632 arr1.push(MyStruct(4)); 633 arr1.push(MyStruct(256)); 634 arr1.push(MyStruct(42)); 635 arr1.sort(); 636 assert(arr1[0].a == 256); 637 assert(arr1[1].a == 42); 638 assert(arr1[2].a == 4); 639 assert(arr1[3].a == 2); 640 641 /// But only if user defined 642 static struct OtherStruct 643 { 644 int a; 645 646 static int pred (scope const OtherStruct* pe1, scope const OtherStruct* pe2) 647 nothrow @nogc pure @safe 648 { 649 return pe1.a - pe2.a; 650 } 651 } 652 653 static assert (!is(typeof(Array!(OtherStruct).init.sort()))); 654 static assert (!is(typeof(Array!(OtherStruct).init.sort!pred))); 655 } 656 657 /** 658 * Iterates the given array and calls the given callable for each element. 659 * 660 * Use this instead of `foreach` when the array may expand during iteration. 661 * 662 * Params: 663 * callable = the callable to call for each element 664 * array = the array to iterate 665 * 666 * See_Also: $(REF foreachDsymbol, dmd, dsymbol) 667 */ 668 template each(alias callable, T) 669 if (is(ReturnType!(typeof((T t) => callable(t))) == void)) 670 { 671 void each(ref Array!T array) 672 { 673 // Do not use foreach, as the size of the array may expand during iteration 674 for (size_t i = 0; i < array.length; ++i) 675 callable(array[i]); 676 } 677 678 void each(Array!T* array) 679 { 680 if (array) 681 each!callable(*array); 682 } 683 } 684 685 /// 686 @("iterate over an Array") unittest 687 { 688 static immutable expected = [2, 3, 4, 5]; 689 690 Array!int array; 691 692 foreach (e ; expected) 693 array.push(e); 694 695 int[] result; 696 array.each!((e) { 697 result ~= e; 698 }); 699 700 assert(result == expected); 701 } 702 703 @("iterate over a pointer to an Array") unittest 704 { 705 static immutable expected = [2, 3, 4, 5]; 706 707 auto array = new Array!int; 708 709 foreach (e ; expected) 710 array.push(e); 711 712 int[] result; 713 array.each!((e) { 714 result ~= e; 715 }); 716 717 assert(result == expected); 718 } 719 720 @("iterate while appending to the array being iterated") unittest 721 { 722 static immutable expected = [2, 3, 4, 5]; 723 724 Array!int array; 725 726 foreach (e ; expected[0 .. $ - 1]) 727 array.push(e); 728 729 int[] result; 730 731 array.each!((e) { 732 if (e == 2) 733 array.push(5); 734 735 result ~= e; 736 }); 737 738 assert(array[] == expected); 739 assert(result == expected); 740 } 741 742 /** 743 * Iterates the given array and calls the given callable for each element. 744 * 745 * If `callable` returns `!= 0`, it will stop the iteration and return that 746 * value, otherwise it will return 0. 747 * 748 * Use this instead of `foreach` when the array may expand during iteration. 749 * 750 * Params: 751 * callable = the callable to call for each element 752 * array = the array to iterate 753 * 754 * Returns: the last value returned by `callable` 755 * See_Also: $(REF foreachDsymbol, dmd, dsymbol) 756 */ 757 template each(alias callable, T) 758 if (is(ReturnType!(typeof((T t) => callable(t))) == int)) 759 { 760 int each(ref Array!T array) 761 { 762 // Do not use foreach, as the size of the array may expand during iteration 763 for (size_t i = 0; i < array.length; ++i) 764 { 765 if (const result = callable(array[i])) 766 return result; 767 } 768 769 return 0; 770 } 771 772 int each(Array!T* array) 773 { 774 return array ? each!callable(*array) : 0; 775 } 776 } 777 778 /// 779 @("iterate over an Array and stop the iteration") unittest 780 { 781 Array!int array; 782 783 foreach (e ; [2, 3, 4, 5]) 784 array.push(e); 785 786 int[] result; 787 const returnValue = array.each!((e) { 788 result ~= e; 789 790 if (e == 3) 791 return 8; 792 793 return 0; 794 }); 795 796 assert(result == [2, 3]); 797 assert(returnValue == 8); 798 } 799 800 @("iterate over an Array") unittest 801 { 802 static immutable expected = [2, 3, 4, 5]; 803 804 Array!int array; 805 806 foreach (e ; expected) 807 array.push(e); 808 809 int[] result; 810 const returnValue = array.each!((e) { 811 result ~= e; 812 return 0; 813 }); 814 815 assert(result == expected); 816 assert(returnValue == 0); 817 } 818 819 @("iterate over a pointer to an Array and stop the iteration") unittest 820 { 821 auto array = new Array!int; 822 823 foreach (e ; [2, 3, 4, 5]) 824 array.push(e); 825 826 int[] result; 827 const returnValue = array.each!((e) { 828 result ~= e; 829 830 if (e == 3) 831 return 9; 832 833 return 0; 834 }); 835 836 assert(result == [2, 3]); 837 assert(returnValue == 9); 838 } 839 840 @("iterate while appending to the array being iterated and stop the iteration") unittest 841 { 842 Array!int array; 843 844 foreach (e ; [2, 3]) 845 array.push(e); 846 847 int[] result; 848 849 const returnValue = array.each!((e) { 850 if (e == 2) 851 array.push(1); 852 853 result ~= e; 854 855 if (e == 1) 856 return 7; 857 858 return 0; 859 }); 860 861 static immutable expected = [2, 3, 1]; 862 863 assert(array[] == expected); 864 assert(result == expected); 865 assert(returnValue == 7); 866 } 867 868 /// Returns: A static array constructed from `array`. 869 pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] array) 870 { 871 return array; 872 } 873 874 /// 875 pure nothrow @safe @nogc unittest 876 { 877 enum a = [0, 1].staticArray; 878 static assert(is(typeof(a) == int[2])); 879 static assert(a == [0, 1]); 880 } 881 882 /// Returns: `true` if the two given ranges are equal 883 bool equal(Range1, Range2)(Range1 range1, Range2 range2) 884 { 885 template isArray(T) 886 { 887 static if (is(T U : U[])) 888 enum isArray = true; 889 890 else 891 enum isArray = false; 892 } 893 894 static if (isArray!Range1 && isArray!Range2 && is(typeof(range1 == range2))) 895 return range1 == range2; 896 897 else 898 { 899 static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length))) 900 { 901 if (range1.length != range2.length) 902 return false; 903 } 904 905 for (; !range1.empty; range1.popFront(), range2.popFront()) 906 { 907 if (range2.empty) 908 return false; 909 910 if (range1.front != range2.front) 911 return false; 912 } 913 914 return range2.empty; 915 } 916 } 917 918 /// 919 pure nothrow @nogc @safe unittest 920 { 921 enum a = [ 1, 2, 4, 3 ].staticArray; 922 static assert(!equal(a[], a[1..$])); 923 static assert(equal(a[], a[])); 924 925 // different types 926 enum b = [ 1.0, 2, 4, 3].staticArray; 927 static assert(!equal(a[], b[1..$])); 928 static assert(equal(a[], b[])); 929 } 930 931 pure nothrow @safe unittest 932 { 933 static assert(equal([1, 2, 3].map!(x => x * 2), [1, 2, 3].map!(x => x * 2))); 934 935 static assert(!equal([1, 2].map!(x => x * 2), [1, 2, 3].map!(x => x * 2))); 936 } 937 938 /** 939 * Lazily filters the given range based on the given predicate. 940 * 941 * Returns: a range containing only elements for which the predicate returns 942 * `true` 943 */ 944 auto filter(alias predicate, Range)(Range range) 945 if (isInputRange!(Unqual!Range) && isPredicateOf!(predicate, ElementType!Range)) 946 { 947 return Filter!(predicate, Range)(range); 948 } 949 950 /// 951 pure nothrow @safe @nogc unittest 952 { 953 enum a = [1, 2, 3, 4].staticArray; 954 enum result = a[].filter!(e => e > 2); 955 956 enum expected = [3, 4].staticArray; 957 static assert(result.equal(expected[])); 958 } 959 960 private struct Filter(alias predicate, Range) 961 { 962 private Range range; 963 private bool primed; 964 965 private void prime() 966 { 967 if (primed) 968 return; 969 970 while (!range.empty && !predicate(range.front)) 971 range.popFront(); 972 973 primed = true; 974 } 975 976 @property bool empty() 977 { 978 prime(); 979 return range.empty; 980 } 981 982 @property auto front() 983 { 984 assert(!range.empty); 985 prime(); 986 return range.front; 987 } 988 989 void popFront() 990 { 991 assert(!range.empty); 992 prime(); 993 994 do 995 { 996 range.popFront(); 997 } while (!range.empty && !predicate(range.front)); 998 } 999 1000 auto opSlice() 1001 { 1002 return this; 1003 } 1004 } 1005 1006 /** 1007 * Lazily iterates the given range and calls the given callable for each element. 1008 * 1009 * Returns: a range containing the result of each call to `callable` 1010 */ 1011 auto map(alias callable, Range)(Range range) 1012 if (isInputRange!(Unqual!Range) && isCallableWith!(callable, ElementType!Range)) 1013 { 1014 return Map!(callable, Range)(range); 1015 } 1016 1017 /// 1018 pure nothrow @safe @nogc unittest 1019 { 1020 enum a = [1, 2, 3, 4].staticArray; 1021 enum expected = [2, 4, 6, 8].staticArray; 1022 1023 enum result = a[].map!(e => e * 2); 1024 static assert(result.equal(expected[])); 1025 } 1026 1027 private struct Map(alias callable, Range) 1028 { 1029 private Range range; 1030 1031 @property bool empty() 1032 { 1033 return range.empty; 1034 } 1035 1036 @property auto front() 1037 { 1038 assert(!range.empty); 1039 return callable(range.front); 1040 } 1041 1042 void popFront() 1043 { 1044 assert(!range.empty); 1045 range.popFront(); 1046 } 1047 1048 static if (hasLength!Range) 1049 { 1050 @property auto length() 1051 { 1052 return range.length; 1053 } 1054 1055 alias opDollar = length; 1056 } 1057 } 1058 1059 /// Returns: the length of the given range. 1060 auto walkLength(Range)(Range range) 1061 if (isInputRange!Range ) 1062 { 1063 static if (hasLength!Range) 1064 return range.length; 1065 else 1066 { 1067 size_t result; 1068 for (; !range.empty; range.popFront()) 1069 ++result; 1070 return result; 1071 } 1072 } 1073 1074 /// 1075 pure nothrow @safe @nogc unittest 1076 { 1077 enum a = [1, 2, 3, 4].staticArray; 1078 static assert(a[].walkLength == 4); 1079 1080 enum c = a[].filter!(e => e > 2); 1081 static assert(c.walkLength == 2); 1082 } 1083 1084 /// Evaluates to the element type of `R`. 1085 template ElementType(R) 1086 { 1087 static if (is(typeof(R.init.front.init) T)) 1088 alias ElementType = T; 1089 else 1090 alias ElementType = void; 1091 } 1092 1093 /// Evaluates to `true` if the given type satisfy the input range interface. 1094 enum isInputRange(R) = 1095 is(typeof(R.init) == R) 1096 && is(ReturnType!(typeof((R r) => r.empty)) == bool) 1097 && is(typeof((return ref R r) => r.front)) 1098 && !is(ReturnType!(typeof((R r) => r.front)) == void) 1099 && is(typeof((R r) => r.popFront)); 1100 1101 /// Evaluates to `true` if `func` can be called with a value of `T` and returns 1102 /// a value that is convertible to `bool`. 1103 enum isPredicateOf(alias func, T) = is(typeof((T t) => !func(t))); 1104 1105 /// Evaluates to `true` if `func` be called withl a value of `T`. 1106 enum isCallableWith(alias func, T) = 1107 __traits(compiles, { auto _ = (T t) => func(t); }); 1108 1109 private: 1110 1111 template ReturnType(T) 1112 { 1113 static if (is(T R == return)) 1114 alias ReturnType = R; 1115 else 1116 static assert(false, "argument is not a function"); 1117 } 1118 1119 alias Unqual(T) = ReturnType!(typeof((T t) => cast() t)); 1120 1121 template hasLength(Range) 1122 { 1123 static if (is(typeof(((Range* r) => r.length)(null)) Length)) 1124 enum hasLength = is(Length == size_t); 1125 else 1126 enum hasLength = false; 1127 } 1128 1129 /// Implements the range interface primitive `front` for built-in arrays. 1130 @property ref inout(T) front(T)(return scope inout(T)[] a) pure nothrow @nogc @safe 1131 { 1132 assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); 1133 return a[0]; 1134 } 1135 1136 /// 1137 pure nothrow @nogc @safe unittest 1138 { 1139 enum a = [1, 2, 3].staticArray; 1140 static assert(a[].front == 1); 1141 } 1142 1143 /// Implements the range interface primitive `empty` for types that obey $(LREF hasLength) property 1144 @property bool empty(T)(auto ref scope T a) 1145 if (is(typeof(a.length) : size_t)) 1146 { 1147 return !a.length; 1148 } 1149 1150 /// 1151 pure nothrow @nogc @safe unittest 1152 { 1153 enum a = [1, 2, 3].staticArray; 1154 1155 static assert(!a.empty); 1156 static assert(a[3 .. $].empty); 1157 } 1158 1159 pure nothrow @safe unittest 1160 { 1161 int[string] b; 1162 assert(b.empty); 1163 b["zero"] = 0; 1164 assert(!b.empty); 1165 } 1166 1167 /// Implements the range interface primitive `popFront` for built-in arrays. 1168 void popFront(T)(/*scope*/ ref inout(T)[] array) pure nothrow @nogc @safe 1169 { // does not compile with GDC 9 if this is `scope` 1170 assert(array.length, "Attempting to popFront() past the end of an array of " ~ T.stringof); 1171 array = array[1 .. $]; 1172 } 1173 1174 /// 1175 pure nothrow @nogc @safe unittest 1176 { 1177 auto a = [1, 2, 3].staticArray; 1178 auto b = a[]; 1179 auto expected = [2, 3].staticArray; 1180 1181 b.popFront(); 1182 assert(b == expected[]); 1183 }