1 /** Array-only overloads of Phobos algorithms. 2 * 3 * Functions are when possible `@safe pure nothrow @nogc`. 4 * Haystack parameter is when possible and relevant `scope return inout(T)[]` and DIP-1000-compliant. 5 * Needle parameter is either `scope const(T)[]` or `scope const T[]`. 6 * 7 * Provides more than twice as fast compilation for `char`-arrays (`string`s). 8 * 9 * See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 10 * See_Also: https://forum.dlang.org/thread/ybamybeakxwxwleebnwb@forum.dlang.org?page=1 11 * 12 * TODO: Merge into separate array-specializations of Phobos algorithms for less template bloat in Phobos. 13 * 14 * TODO: Move to nxt/algorithm/searching_ex.d 15 */ 16 module nxt.array_algorithm; 17 18 // version = unittestAsBetterC; // Run_As: dmd -betterC -unittest -run $(__FILE__).d 19 20 /** Array-specialization of `startsWith` with default predicate. 21 * 22 * See_Also: https://d.godbolt.org/z/ejEmrK 23 */ 24 bool startsWith(T)(scope const T[] haystack, 25 scope const T[] needle) @trusted 26 { 27 if (haystack.length < needle.length) 28 return false; 29 return haystack.ptr[0 .. needle.length] == needle; 30 } 31 /// ditto 32 bool startsWith(T)(scope const T[] haystack, 33 scope const T needle) @trusted 34 { 35 static if (is(T == char)) 36 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 37 if (haystack.length == 0) 38 return false; 39 return haystack.ptr[0] == needle; 40 } 41 42 /// 43 @safe pure nothrow @nogc unittest 44 { 45 const x = "beta version"; 46 assert(x.startsWith("beta")); 47 assert(x.startsWith('b')); 48 assert(!x.startsWith("_")); 49 } 50 51 /** Array-specialization of `all` with element needle. */ 52 bool all(T)(scope const T[] haystack, 53 scope const T needle) @trusted 54 { 55 static if (is(T == char)) 56 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 57 foreach (const offset; 0 .. haystack.length) 58 if (haystack.ptr[offset] != needle) 59 return false; 60 return true; 61 } 62 63 /// 64 @safe pure nothrow @nogc unittest 65 { 66 assert("".all('a')); // matches behaviour of `std.algorithm.searching.any` 67 assert("aaa".all('a')); 68 assert(!"aa_".all('a')); 69 } 70 71 /** Array-specialization of `any` with element needle. */ 72 bool any(T)(scope const T[] haystack, 73 scope const T needle) @trusted 74 { 75 static if (is(T == char)) 76 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 77 foreach (const offset; 0 .. haystack.length) 78 if (haystack.ptr[offset] == needle) 79 return true; 80 return false; 81 } 82 83 /// 84 @safe pure nothrow @nogc unittest 85 { 86 assert(!"".any('a')); // matches behaviour of `std.algorithm.searching.any` 87 assert("aaa".any('a')); 88 assert("aa_".any('a')); 89 assert(!"_".any('a')); 90 } 91 92 /** Array-specialization of `endsWith` with default predicate. */ 93 bool endsWith(T)(scope const T[] haystack, 94 scope const T[] needle) @trusted 95 { 96 if (haystack.length < needle.length) 97 return false; 98 return haystack.ptr[haystack.length - needle.length .. haystack.length] == needle; 99 } 100 /// ditto 101 bool endsWith(T)(scope const T[] haystack, 102 scope const T needle) @trusted 103 { 104 static if (is(T == char)) 105 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 106 if (haystack.length == 0) 107 return false; 108 return haystack.ptr[haystack.length - 1] == needle; 109 } 110 111 /// 112 @safe pure nothrow @nogc unittest 113 { 114 const x = "beta version"; 115 assert(x.endsWith("version")); 116 assert(x.endsWith('n')); 117 assert(!x.startsWith("_")); 118 } 119 120 bool endsWithEither(T)(scope const T[] haystack, 121 scope const T[][] needles) @trusted 122 { 123 foreach (const needle; needles) 124 if (haystack.endsWith(needle)) // TODO: optimize 125 return true; 126 return false; 127 } 128 /// ditto 129 bool endsWithEither(T)(scope const T[] haystack, 130 scope const T[] needles) @trusted 131 { 132 foreach (const needle; needles) 133 if (haystack.endsWith(needle)) // TODO: optimize 134 return true; 135 return false; 136 } 137 138 /// 139 @safe pure nothrow @nogc unittest 140 { 141 const x = "beta version"; 142 assert(x.endsWithEither(["version", ""])); 143 assert(x.endsWithEither(['n', ' '])); 144 assert(x.endsWithEither("n ")); 145 } 146 147 /** Array-specialization of `findSkip` with default predicate. 148 */ 149 auto findSkip(T)(scope ref inout(T)[] haystack, 150 scope const T[] needle) @trusted 151 { 152 const index = haystack.indexOf(needle); 153 if (index != -1) 154 { 155 haystack = haystack.ptr[index + needle.length .. haystack.length]; 156 return true; 157 } 158 return false; 159 } 160 /// ditto 161 auto findSkip(T)(scope ref inout(T)[] haystack, 162 scope const T needle) @trusted 163 { 164 const index = haystack.indexOf(needle); 165 if (index != -1) 166 { 167 haystack = haystack.ptr[index + 1 .. haystack.length]; 168 return true; 169 } 170 return false; 171 } 172 173 /// 174 @safe pure nothrow @nogc unittest 175 { 176 const auto x = "abc"; 177 { 178 string y = x; 179 const bool ok = y.findSkip("_"); 180 assert(!ok); 181 assert(y is x); 182 } 183 { 184 string y = x; 185 const bool ok = y.findSkip("a"); 186 assert(ok); 187 assert(y == x[1 .. $]); 188 } 189 { 190 string y = x; 191 const bool ok = y.findSkip("c"); 192 assert(ok); 193 assert(y is x[$ .. $]); 194 } 195 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].findSkip(" "); } 196 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 197 } 198 199 /// 200 @safe pure nothrow @nogc unittest 201 { 202 const auto x = "abc"; 203 { 204 string y = x; 205 const bool ok = y.findSkip('_'); 206 assert(!ok); 207 assert(y is x); 208 } 209 { 210 string y = x; 211 const bool ok = y.findSkip('a'); 212 assert(ok); 213 assert(y == x[1 .. $]); 214 } 215 { 216 string y = x; 217 const bool ok = y.findSkip('c'); 218 assert(ok); 219 assert(y is x[$ .. $]); 220 } 221 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].findSkip(' '); } 222 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 223 } 224 225 /** Array-specialization of `findSkip` with default predicate that finds the last skip. 226 */ 227 auto findLastSkip(T)(scope ref inout(T)[] haystack, 228 scope const T[] needle) @trusted 229 { 230 const index = haystack.lastIndexOf(needle); 231 if (index != -1) 232 { 233 haystack = haystack.ptr[index + needle.length .. haystack.length]; 234 return true; 235 } 236 return false; 237 } 238 /// 239 auto findLastSkip(T)(scope ref inout(T)[] haystack, 240 scope const T needle) @trusted 241 { 242 const index = haystack.lastIndexOf(needle); 243 if (index != -1) 244 { 245 haystack = haystack.ptr[index + 1 .. haystack.length]; 246 return true; 247 } 248 return false; 249 } 250 251 /// 252 @safe pure nothrow @nogc unittest 253 { 254 const auto x = "abacc"; 255 { 256 string y = x; 257 const bool ok = y.findLastSkip("_"); 258 assert(!ok); 259 assert(y is x); 260 } 261 { 262 string y = x; 263 const bool ok = y.findLastSkip("a"); 264 assert(ok); 265 assert(y == x[3 .. $]); 266 } 267 { 268 string y = x; 269 const bool ok = y.findLastSkip("c"); 270 assert(ok); 271 assert(y is x[$ .. $]); 272 } 273 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSkip(" "); } 274 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 275 } 276 277 /// 278 @safe pure nothrow @nogc unittest 279 { 280 const auto x = "abacc"; 281 { 282 string y = x; 283 const bool ok = y.findLastSkip('_'); 284 assert(!ok); 285 assert(y is x); 286 } 287 { 288 string y = x; 289 const bool ok = y.findLastSkip('a'); 290 assert(ok); 291 assert(y == x[3 .. $]); 292 } 293 { 294 string y = x; 295 const bool ok = y.findLastSkip('c'); 296 assert(ok); 297 assert(y is x[$ .. $]); 298 } 299 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSkip(' '); } 300 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 301 } 302 303 /** Array-specialization of `skipOver` with default predicate. 304 * 305 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 306 */ 307 bool skipOver(T)(scope ref inout(T)[] haystack, 308 scope const T[] needle) @trusted 309 { 310 if (!startsWith(haystack, needle)) 311 return false; 312 haystack = haystack.ptr[needle.length .. haystack.length]; 313 return true; 314 } 315 /// ditto 316 bool skipOver(T)(scope ref inout(T)[] haystack, 317 scope const T needle) @trusted 318 { 319 if (!startsWith(haystack, needle)) 320 return false; 321 haystack = haystack.ptr[1 .. haystack.length]; 322 return true; 323 } 324 325 /// 326 @safe pure nothrow @nogc unittest 327 { 328 string x = "beta version"; 329 assert(x.skipOver("beta")); 330 assert(x == " version"); 331 assert(x.skipOver(' ')); 332 assert(x == "version"); 333 } 334 335 /// constness of haystack and needle 336 @safe pure nothrow @nogc unittest 337 { 338 { 339 const(char)[] haystack; 340 string needle; 341 assert(haystack.skipOver(needle)); 342 } 343 { 344 const(char)[] haystack; 345 const(char)[] needle; 346 assert(haystack.skipOver(needle)); 347 } 348 { 349 const(char)[] haystack; 350 char[] needle; 351 assert(haystack.skipOver(needle)); 352 } 353 } 354 355 /** Array-specialization of `skipOverBack` with default predicate. 356 * 357 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 358 */ 359 bool skipOverBack(T)(scope ref inout(T)[] haystack, 360 scope const T[] needle) @trusted 361 { 362 if (!endsWith(haystack, needle)) 363 return false; 364 haystack = haystack.ptr[0 .. haystack.length - needle.length]; 365 return true; 366 } 367 /// ditto 368 bool skipOverBack(T)(scope ref inout(T)[] haystack, 369 scope const T needle) @trusted 370 { 371 if (!endsWith(haystack, needle)) 372 return false; 373 haystack = haystack.ptr[0 .. haystack.length - 1]; 374 return true; 375 } 376 377 /// 378 @safe pure nothrow @nogc unittest 379 { 380 string x = "beta version"; 381 assert(x.skipOverBack(" version")); 382 assert(x == "beta"); 383 assert(x.skipOverBack('a')); 384 assert(x == "bet"); 385 } 386 387 bool skipOverAround(T)(scope ref inout(T)[] haystack, 388 scope const T[] needleFront, 389 scope const T[] needleBack) @trusted 390 { 391 if (!startsWith(haystack, needleFront) || 392 !endsWith(haystack, needleBack)) 393 return false; 394 haystack = haystack.ptr[needleFront.length .. haystack.length - needleBack.length]; 395 return true; 396 } 397 /// ditto 398 bool skipOverAround(T)(scope ref inout(T)[] haystack, 399 scope const T needleFront, 400 scope const T needleBack) @trusted 401 { 402 if (!startsWith(haystack, needleFront) || 403 !endsWith(haystack, needleBack)) 404 return false; 405 haystack = haystack.ptr[1 .. haystack.length - 1]; 406 return true; 407 } 408 409 /// 410 @safe pure nothrow @nogc unittest 411 { 412 string x = "alpha beta_gamma"; 413 assert(x.skipOverAround("alpha", "gamma")); 414 assert(x == " beta_"); 415 assert(x.skipOverAround(' ', '_')); 416 assert(x == "beta"); 417 } 418 419 /** Array-specialization of `stripLeft` with default predicate. 420 * 421 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 422 */ 423 inout(T)[] stripLeft(T)(scope return inout(T)[] haystack, 424 scope const T needle) @trusted 425 { 426 static if (is(T == char)) 427 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 428 size_t offset = 0; 429 while (offset != haystack.length && 430 haystack.ptr[offset] == needle) // TODO: elide range-check 431 offset += 1; 432 return haystack.ptr[offset .. haystack.length]; 433 } 434 /// ditto 435 inout(char)[] stripLeft()(scope return inout(char)[] haystack) @safe pure nothrow @nogc /* template-lazy */ 436 { 437 return haystack.stripLeft(' '); 438 } 439 440 /// 441 @safe pure nothrow @nogc unittest 442 { 443 assert("beta".stripLeft(' ') == "beta"); 444 assert(" beta".stripLeft(' ') == "beta"); 445 assert(" beta".stripLeft(' ') == "beta"); 446 assert(" beta".stripLeft(' ') == "beta"); 447 assert(" beta".stripLeft() == "beta"); 448 assert(" _ beta _ ".stripLeft(' ') == "_ beta _ "); 449 assert(" _ beta _ ".stripLeft(' ') == "_ beta _ "); 450 451 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].stripLeft(' '); } 452 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 453 } 454 455 /** Array-specialization of `stripRight` with default predicate. 456 * 457 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 458 */ 459 inout(T)[] stripRight(T)(scope return inout(T)[] haystack, 460 scope const T needle) @trusted 461 { 462 static if (is(T == char)) 463 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 464 size_t offset = haystack.length; 465 while (offset != 0 && 466 haystack.ptr[offset - 1] == needle) // TODO: elide range-check 467 offset -= 1; 468 return haystack.ptr[0 .. offset]; 469 } 470 /// ditto 471 inout(char)[] stripRight()(scope return inout(char)[] haystack) @safe pure nothrow @nogc /* template-lazy */ 472 { 473 return haystack.stripRight(' '); 474 } 475 476 /// 477 @safe pure nothrow @nogc unittest 478 { 479 assert("beta".stripRight(' ') == "beta"); 480 assert("beta ".stripRight(' ') == "beta"); 481 assert("beta ".stripRight(' ') == "beta"); 482 assert("beta ".stripRight(' ') == "beta"); 483 assert("beta ".stripRight() == "beta"); 484 assert(" _ beta _ ".stripRight(' ') == " _ beta _"); 485 assert(" _ beta _ ".stripRight(' ') == " _ beta _"); 486 487 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].stripRight(' '); } 488 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 489 } 490 491 /** Array-specialization of `strip` with default predicate. 492 * 493 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 494 */ 495 inout(T)[] strip(T)(scope return inout(T)[] haystack, 496 scope const T needle) @trusted 497 { 498 static if (is(T == char)) 499 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 500 501 size_t leftOffset = 0; 502 while (leftOffset != haystack.length && 503 haystack.ptr[leftOffset] == needle) // TODO: elide range-check 504 leftOffset += 1; 505 506 size_t rightOffset = haystack.length; 507 while (rightOffset != leftOffset && 508 haystack.ptr[rightOffset - 1] == needle) // TODO: elide range-check 509 rightOffset -= 1; 510 511 return haystack.ptr[leftOffset .. rightOffset]; 512 } 513 /// ditto 514 inout(char)[] strip()(scope return inout(char)[] haystack) @safe pure nothrow @nogc /* template-lazy */ 515 { 516 return haystack.strip(' '); 517 } 518 519 /// 520 @safe pure nothrow @nogc unittest 521 { 522 assert("beta".strip(' ') == "beta"); 523 assert(" beta ".strip(' ') == "beta"); 524 assert(" beta ".strip(' ') == "beta"); 525 assert(" beta ".strip(' ') == "beta"); 526 assert(" _ beta _ ".strip(' ') == "_ beta _"); 527 assert(" _ beta _ ".strip(' ') == "_ beta _"); 528 529 char[] f()() @safe pure nothrow { char[1] x = "_"; return x[].strip(' '); } 530 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 531 } 532 533 /// 534 @safe pure nothrow @nogc unittest 535 { 536 const ubyte[3] x = [0, 42, 0]; 537 assert(x.strip(0) == x[1 .. 2]); 538 } 539 540 /** Array-specialization of `count` with default predicate. 541 * 542 * TODO: Add optimized implementation for needles with length >= 543 * `largeNeedleLength` with no repeat of elements. 544 * 545 * TODO: reuse `return haystack.indexOf(needle) != -1` in both overloads 546 */ 547 bool canFind(T)(scope const T[] haystack, 548 scope const T[] needle) @trusted 549 in(needle.length, "Cannot count occurrences of an empty range") 550 { 551 // enum largeNeedleLength = 4; 552 if (haystack.length < needle.length) 553 return false; 554 foreach (const offset; 0 .. haystack.length - needle.length + 1) 555 if (haystack.ptr[offset .. offset + needle.length] == needle) 556 return true; 557 return false; 558 } 559 /// ditto 560 bool canFind(T)(scope const T[] haystack, 561 scope const T needle) @trusted 562 { 563 static if (is(T == char)) 564 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 565 if (haystack.length == 0) 566 return false; 567 foreach (const ref element; haystack) 568 if (element == needle) 569 return true; 570 return false; 571 } 572 573 /// 574 @safe pure nothrow @nogc unittest 575 { 576 assert(!"".canFind("_")); 577 assert(!"a".canFind("_")); 578 assert("a".canFind("a")); 579 assert(!"a".canFind("ab")); 580 assert("ab".canFind("a")); 581 assert("ab".canFind("b")); 582 assert("ab".canFind("ab")); 583 assert(!"a".canFind("ab")); 584 assert(!"b".canFind("ab")); 585 } 586 587 /// 588 @safe pure nothrow @nogc unittest 589 { 590 assert(!"".canFind('_')); 591 assert(!"a".canFind('_')); 592 assert("a".canFind('a')); 593 assert("a".canFind('a')); 594 assert("ab".canFind('a')); 595 assert("ab".canFind('b')); 596 } 597 598 /** Array-specialization of `count` with default predicate. 599 */ 600 size_t count(T)(scope const T[] haystack, 601 scope const T[] needle) @trusted 602 { 603 assert(needle.length, "Cannot count occurrences of an empty range"); 604 size_t result = 0; 605 if (haystack.length < needle.length) 606 return false; 607 foreach (const offset; 0 .. haystack.length - needle.length + 1) 608 result += haystack.ptr[offset .. offset + needle.length] == needle ? 1 : 0; 609 return result; 610 } 611 /// ditto 612 size_t count(T)(scope const T[] haystack, 613 scope const T needle) 614 { 615 static if (is(T == char)) 616 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 617 size_t result; 618 foreach (const ref element; haystack) 619 result += element == needle ? 1 : 0; 620 return result; 621 } 622 623 /// 624 @safe pure nothrow @nogc unittest 625 { 626 // import std.algorithm.searching : count; 627 assert("".count("_") == 0); 628 assert("".count(" ") == 0); 629 assert(" ".count(" ") == 1); 630 assert("abc_abc".count("a") == 2); 631 assert("abc_abc".count("abc") == 2); 632 assert("_a_a_".count("_") == 3); 633 assert("_aaa_".count("a") == 3); 634 // assert("".count("") == 0); 635 // assert("_a_a_".count("") == 5); 636 } 637 638 /// 639 @safe pure nothrow @nogc unittest 640 { 641 assert("".count('_') == 0); 642 assert("abc_abc".count('a') == 2); 643 assert("_abc_abc_".count('_') == 3); 644 } 645 646 /** Array-specialization of `count` with default predicate and no needle. 647 */ 648 size_t count(T)(scope const T[] haystack) => haystack.length; 649 650 /// 651 @safe pure nothrow @nogc unittest 652 { 653 assert("abc_abc".count == 7); 654 } 655 656 /** Array-specialization of `indexOf` with default predicate. 657 * 658 * TODO: Add optimized implementation for needles with length >= 659 * `largeNeedleLength` with no repeat of elements. 660 */ 661 ptrdiff_t indexOf(T)(scope inout(T)[] haystack, 662 scope const(T)[] needle) @trusted 663 { 664 // enum largeNeedleLength = 4; 665 if (haystack.length < needle.length) 666 return -1; 667 foreach (const offset; 0 .. haystack.length - needle.length + 1) 668 if (haystack.ptr[offset .. offset + needle.length] == needle) 669 return offset; 670 return -1; 671 } 672 /// ditto 673 ptrdiff_t indexOf(T)(scope inout(T)[] haystack, 674 scope const T needle) 675 { 676 static if (is(T == char)) 677 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 678 foreach (const offset, const ref element; haystack) 679 if (element == needle) 680 return offset; 681 return -1; 682 } 683 684 /// 685 @safe pure nothrow @nogc unittest 686 { 687 assert("_abc_abc_".indexOf("abc") == 1); 688 assert("__abc_".indexOf("abc") == 2); 689 assert("a".indexOf("a") == 0); 690 assert("abc".indexOf("abc") == 0); 691 assert("_".indexOf("a") == -1); 692 assert("_".indexOf("__") == -1); 693 assert("__".indexOf("a") == -1); 694 } 695 696 /// 697 @safe pure nothrow @nogc unittest 698 { 699 assert("_".indexOf('a') == -1); 700 assert("a".indexOf('a') == 0); 701 assert("_a".indexOf('a') == 1); 702 assert("__a".indexOf('a') == 2); 703 } 704 705 /// ditto 706 ptrdiff_t indexOfEither(T)(scope inout(T)[] haystack, 707 scope const T[] needles) 708 { 709 if (needles.length == 0) 710 return -1; 711 foreach (const offset, const ref element; haystack) 712 foreach (const needle; needles) 713 if (element == needle) 714 return offset; 715 return -1; 716 } 717 alias indexOfAny = indexOfEither; // Compliance with `std.string.indexOfAny`. 718 719 /// 720 @safe pure nothrow @nogc unittest 721 { 722 assert("_".indexOfEither("a") == -1); 723 assert("_a".indexOfEither("a") == 1); 724 assert("_a".indexOfEither("ab") == 1); 725 assert("_b".indexOfEither("ab") == 1); 726 assert("_b".indexOfEither("_") == 0); 727 assert("_b".indexOfEither("xy") == -1); 728 } 729 730 /** Array-specialization of `lastIndexOf` with default predicate. 731 */ 732 ptrdiff_t lastIndexOf(T)(scope inout(T)[] haystack, 733 scope const(T)[] needle) @trusted 734 { 735 if (haystack.length < needle.length) 736 return -1; 737 foreach_reverse (const offset; 0 .. haystack.length - needle.length + 1) 738 if (haystack.ptr[offset .. offset + needle.length] == needle) 739 return offset; 740 return -1; 741 } 742 /// ditto 743 ptrdiff_t lastIndexOf(T)(scope inout(T)[] haystack, 744 scope const T needle) 745 { 746 static if (is(T == char)) 747 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 748 foreach_reverse (const offset, const ref element; haystack) 749 if (element == needle) 750 return offset; 751 return -1; 752 } 753 754 /// 755 @safe pure nothrow @nogc unittest 756 { 757 assert("_abc_abc_".lastIndexOf("abc") == 5); 758 assert("__abc_".lastIndexOf("abc") == 2); 759 assert("a".lastIndexOf("a") == 0); 760 assert("aa".lastIndexOf("a") == 1); 761 assert("abc".lastIndexOf("abc") == 0); 762 assert("_".lastIndexOf("a") == -1); 763 assert("_".lastIndexOf("__") == -1); 764 assert("__".lastIndexOf("a") == -1); 765 } 766 767 /// 768 @safe pure nothrow @nogc unittest 769 { 770 assert("_".lastIndexOf('a') == -1); 771 assert("a".lastIndexOf('a') == 0); 772 assert("_a".lastIndexOf('a') == 1); 773 assert("__a".lastIndexOf('a') == 2); 774 assert("a__a".lastIndexOf('a') == 3); 775 } 776 777 /** Array-specialization of `findSplit` with default predicate. 778 * 779 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 780 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 781 */ 782 auto findSplit(T)(scope return inout(T)[] haystack, 783 scope const(T)[] needle) 784 { 785 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 786 { 787 private T[] _haystack; 788 private size_t _offset; // hit offset 789 private size_t _length; // hit length 790 pragma(inline, true): 791 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 792 inout(T)[] separator() @trusted inout => _haystack.ptr[_offset .. _offset + _length]; 793 inout(T)[] post() @trusted inout => _haystack.ptr[_offset + _length .. _haystack.length]; 794 bool opCast(T : bool)() @safe const => _haystack.length != _offset; 795 } 796 797 assert(needle.length, "Cannot find occurrence of an empty range"); 798 const index = haystack.indexOf(needle); 799 if (index >= 0) 800 return inout(Result)(haystack, index, needle.length); 801 return inout(Result)(haystack, haystack.length, 0); // miss 802 } 803 /// ditto 804 auto findSplit(T)(scope return inout(T)[] haystack, 805 scope const T needle) 806 { 807 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 808 { 809 private T[] _haystack; 810 private size_t _offset; // hit offset 811 pragma(inline, true): 812 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 813 inout(T)[] separator() @trusted inout => !empty ? _haystack.ptr[_offset .. _offset + 1] : _haystack[$ .. $]; 814 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset + 1 .. _haystack.length] : _haystack[$ .. $]; 815 bool opCast(T : bool)() const => !empty; 816 private bool empty() const @property => _haystack.length == _offset; 817 } 818 819 static if (is(T == char)) 820 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 821 const index = haystack.indexOf(needle); 822 if (index >= 0) 823 return inout(Result)(haystack, index); 824 return inout(Result)(haystack, haystack.length); 825 } 826 827 /// 828 @safe pure nothrow @nogc unittest 829 { 830 const h = "a**b"; 831 const r = h.findSplit("**"); 832 assert(r); 833 assert(r.pre is h[0 .. 1]); 834 assert(r.separator is h[1 .. 3]); 835 assert(r.post is h[3 .. 4]); 836 837 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplit(" "); } 838 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 839 } 840 841 /// 842 @safe pure nothrow @nogc unittest 843 { 844 const h = "a**b"; 845 const r = h.findSplit("_"); 846 static assert(r.sizeof == 2 * 2 * size_t.sizeof); 847 assert(!r); 848 assert(r.pre is h); 849 assert(r.separator is h[$ .. $]); 850 assert(r.post is h[$ .. $]); 851 } 852 853 /// 854 version(none) 855 @safe pure nothrow @nogc unittest 856 { 857 import std.algorithm.searching : findSplit; 858 const h = "a**b"; 859 const r = h.findSplit("_"); 860 static assert(r.sizeof == 3 * 2 * size_t.sizeof); 861 assert(!r); 862 assert(r[0] is h); 863 assert(r[1] is h[$ .. $]); 864 assert(r[2] is h[$ .. $]); 865 } 866 867 /// 868 @safe pure nothrow @nogc unittest 869 { 870 const r = "a*b".findSplit('*'); 871 static assert(r.sizeof == 3 * size_t.sizeof); 872 assert(r); 873 assert(r.pre == "a"); 874 assert(r.separator == "*"); 875 assert(r.post == "b"); 876 877 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplit(' '); } 878 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 879 } 880 881 /// DIP-1000 scope analysis 882 version(none) // TODO. enable 883 @safe pure nothrow @nogc unittest 884 { 885 char[] f() @safe pure nothrow 886 { 887 char[3] haystack = "a*b"; 888 auto r = haystack[].findSplit('*'); 889 static assert(is(typeof(r.pre()) == char[])); 890 return r.pre(); // TODO: this should fail 891 } 892 f(); 893 } 894 895 /** Array-specialization of `findLastSplit` with default predicate. 896 */ 897 auto findLastSplit(T)(scope return inout(T)[] haystack, 898 scope const(T)[] needle) 899 { 900 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 901 { 902 private T[] _haystack; 903 private size_t _offset; // hit offset 904 private size_t _length; // hit length 905 pragma(inline, true): 906 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 907 inout(T)[] separator() @trusted inout => _haystack.ptr[_offset .. _offset + _length]; 908 inout(T)[] post() @trusted inout => _haystack.ptr[_offset + _length .. _haystack.length]; 909 bool opCast(T : bool)() @safe const => _haystack.length != _offset; 910 } 911 912 assert(needle.length, "Cannot find occurrence of an empty range"); 913 const index = haystack.lastIndexOf(needle); 914 if (index >= 0) 915 return inout(Result)(haystack, index, needle.length); 916 return inout(Result)(haystack, haystack.length, 0); // miss 917 } 918 /// ditto 919 auto findLastSplit(T)(scope return inout(T)[] haystack, 920 scope const T needle) 921 { 922 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 923 { 924 private T[] _haystack; 925 private size_t _offset; // hit offset 926 pragma(inline, true): 927 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 928 inout(T)[] separator() @trusted inout => !empty ? _haystack.ptr[_offset .. _offset + 1] : _haystack[$ .. $]; 929 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset + 1 .. _haystack.length] : _haystack[$ .. $]; 930 bool opCast(T : bool)() const => !empty; 931 private bool empty() const @property => _haystack.length == _offset; 932 } 933 934 static if (is(T == char)) 935 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 936 const index = haystack.lastIndexOf(needle); 937 if (index >= 0) 938 return inout(Result)(haystack, index); 939 return inout(Result)(haystack, haystack.length); 940 } 941 942 /// 943 @safe pure nothrow @nogc unittest 944 { 945 const h = "a**b**c"; 946 const r = h.findLastSplit("**"); 947 assert(r); 948 assert(r.pre is h[0 .. 4]); 949 assert(r.separator is h[4 .. 6]); 950 assert(r.post is h[6 .. 7]); 951 952 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSplit(" "); } 953 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 954 } 955 956 /// 957 @safe pure nothrow @nogc unittest 958 { 959 const h = "a**b**c"; 960 const r = h.findLastSplit("_"); 961 static assert(r.sizeof == 2 * 2 * size_t.sizeof); 962 assert(!r); 963 assert(r.pre is h); 964 assert(r.separator is h[$ .. $]); 965 assert(r.post is h[$ .. $]); 966 } 967 968 /// 969 @safe pure nothrow @nogc unittest 970 { 971 const r = "a*b*c".findLastSplit('*'); 972 static assert(r.sizeof == 3 * size_t.sizeof); 973 assert(r); 974 assert(r.pre == "a*b"); 975 assert(r.separator == "*"); 976 assert(r.post == "c"); 977 978 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSplit(' '); } 979 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 980 } 981 982 /// DIP-1000 scope analysis 983 version(none) // TODO: enable 984 @safe pure nothrow @nogc unittest 985 { 986 char[] f() @safe pure nothrow 987 { 988 char[3] haystack = "a*b"; 989 auto r = haystack[].findLastSplit('*'); 990 static assert(is(typeof(r.pre()) == char[])); 991 return r.pre(); // TODO: this should fail 992 } 993 f(); 994 } 995 996 /** Array-specialization of `findSplitBefore` with default predicate. 997 * 998 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 999 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 1000 */ 1001 auto findSplitBefore(T)(scope return inout(T)[] haystack, 1002 scope const T needle) 1003 { 1004 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 1005 { 1006 private T[] _haystack; 1007 private size_t _offset; 1008 pragma(inline, true): 1009 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 1010 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset .. _haystack.length] : _haystack[$ .. $]; 1011 bool opCast(T : bool)() const => !empty; 1012 private bool empty() const @property => _haystack.length == _offset; 1013 } 1014 1015 static if (is(T == char)) 1016 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 1017 foreach (const offset, const ref element; haystack) 1018 if (element == needle) 1019 return inout(Result)(haystack, offset); 1020 return inout(Result)(haystack, haystack.length); 1021 } 1022 1023 /// 1024 @safe pure nothrow @nogc unittest 1025 { 1026 char[] haystack; 1027 auto r = haystack.findSplitBefore('_'); 1028 static assert(is(typeof(r.pre()) == char[])); 1029 static assert(is(typeof(r.post()) == char[])); 1030 assert(!r); 1031 assert(!r.pre); 1032 assert(!r.post); 1033 1034 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplitBefore(' '); } 1035 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 1036 } 1037 1038 /// 1039 @safe pure nothrow @nogc unittest 1040 { 1041 const(char)[] haystack; 1042 auto r = haystack.findSplitBefore('_'); 1043 static assert(is(typeof(r.pre()) == const(char)[])); 1044 static assert(is(typeof(r.post()) == const(char)[])); 1045 assert(!r); 1046 assert(!r.pre); 1047 assert(!r.post); 1048 } 1049 1050 /// 1051 @safe pure nothrow @nogc unittest 1052 { 1053 const r = "a*b".findSplitBefore('*'); 1054 assert(r); 1055 assert(r.pre == "a"); 1056 assert(r.post == "*b"); 1057 } 1058 1059 /// 1060 @safe pure nothrow @nogc unittest 1061 { 1062 const r = "a*b".findSplitBefore('_'); 1063 assert(!r); 1064 assert(r.pre == "a*b"); 1065 assert(r.post == ""); 1066 } 1067 1068 /** Array-specialization of `findSplitBefore` with explicit needle-only predicate `needlePred`. 1069 * 1070 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 1071 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 1072 */ 1073 auto findSplitBefore(alias needlePred, T)(scope return inout(T)[] haystack) 1074 { 1075 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 1076 { 1077 private T[] _haystack; 1078 private size_t _offset; 1079 pragma(inline, true): 1080 inout(T)[] pre() @trusted inout => _haystack.ptr[0 .. _offset]; 1081 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset .. _haystack.length] : _haystack[$ .. $]; 1082 bool opCast(T : bool)() const => !empty; 1083 private bool empty() const @property => _haystack.length == _offset; 1084 } 1085 1086 foreach (const offset, const ref element; haystack) 1087 if (needlePred(element)) 1088 return inout(Result)(haystack, offset); 1089 return inout(Result)(haystack, haystack.length); 1090 } 1091 1092 /// 1093 @safe pure nothrow @nogc unittest 1094 { 1095 char[] haystack; 1096 auto r = haystack.findSplitBefore!(_ => _ == '_'); 1097 static assert(is(typeof(r.pre()) == char[])); 1098 static assert(is(typeof(r.post()) == char[])); 1099 assert(!r); 1100 assert(!r.pre); 1101 assert(!r.post); 1102 1103 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplitBefore!(_ => _ == ' '); } 1104 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 1105 } 1106 1107 /// 1108 @safe pure nothrow @nogc unittest 1109 { 1110 const(char)[] haystack; 1111 auto r = haystack.findSplitBefore!(_ => _ == '_'); 1112 static assert(is(typeof(r.pre()) == const(char)[])); 1113 static assert(is(typeof(r.post()) == const(char)[])); 1114 assert(!r); 1115 assert(!r.pre); 1116 assert(!r.post); 1117 } 1118 1119 /// 1120 @safe pure nothrow @nogc unittest 1121 { 1122 const r = "a*b".findSplitBefore!(_ => _ == '*'); 1123 assert(r); 1124 assert(r.pre == "a"); 1125 assert(r.post == "*b"); 1126 } 1127 1128 /// 1129 @safe pure nothrow @nogc unittest 1130 { 1131 const r = "a*b".findSplitBefore!(_ => _ == '*' || _ == '+'); 1132 assert(r); 1133 assert(r.pre == "a"); 1134 assert(r.post == "*b"); 1135 } 1136 1137 /// 1138 @safe pure nothrow @nogc unittest 1139 { 1140 const r = "a+b".findSplitBefore!(_ => _ == '*' || _ == '+'); 1141 assert(r); 1142 assert(r.pre == "a"); 1143 assert(r.post == "+b"); 1144 } 1145 1146 /// 1147 @safe pure nothrow @nogc unittest 1148 { 1149 const r = "a*b".findSplitBefore!(_ => _ == '_'); 1150 assert(!r); 1151 assert(r.pre == "a*b"); 1152 assert(r.post == ""); 1153 } 1154 1155 /** Array-specialization of `findSplitAfter` with default predicate. 1156 * 1157 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 1158 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 1159 */ 1160 auto findSplitAfter(T)(scope return inout(T)[] haystack, 1161 scope const T needle) @trusted 1162 { 1163 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 1164 { 1165 private T[] _haystack; 1166 private size_t _offset; 1167 pragma(inline, true): 1168 inout(T)[] pre() @trusted inout => !empty ? _haystack.ptr[0 .. _offset + 1] : _haystack[$ .. $]; 1169 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset + 1 .. _haystack.length] : _haystack[0 .. $]; 1170 bool opCast(T : bool)() const => !empty; 1171 private bool empty() const @property => _haystack.length == _offset; 1172 } 1173 1174 static if (is(T == char)) 1175 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 1176 foreach (const offset, const ref element; haystack) 1177 if (element == needle) 1178 return inout(Result)(haystack, offset); 1179 return inout(Result)(haystack, haystack.length); 1180 } 1181 1182 /// 1183 @safe pure nothrow @nogc unittest 1184 { 1185 char[] haystack; 1186 auto r = haystack.findSplitAfter('_'); 1187 static assert(is(typeof(r.pre()) == char[])); 1188 static assert(is(typeof(r.post()) == char[])); 1189 assert(!r); 1190 assert(!r.pre); 1191 assert(!r.post); 1192 1193 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findSplitAfter(' '); } 1194 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 1195 } 1196 1197 /// 1198 @safe pure nothrow @nogc unittest 1199 { 1200 const(char)[] haystack; 1201 auto r = haystack.findSplitAfter('_'); 1202 static assert(is(typeof(r.pre()) == const(char)[])); 1203 static assert(is(typeof(r.post()) == const(char)[])); 1204 assert(!r); 1205 assert(!r.pre); 1206 assert(!r.post); 1207 } 1208 1209 /// 1210 @safe pure nothrow @nogc unittest 1211 { 1212 auto r = "a*b".findSplitAfter('*'); 1213 static assert(is(typeof(r.pre()) == string)); 1214 static assert(is(typeof(r.post()) == string)); 1215 assert(r); 1216 assert(r.pre == "a*"); 1217 assert(r.post == "b"); 1218 } 1219 1220 /** Array-specialization of `findLastSplitAfter` with default predicate. 1221 * 1222 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org 1223 * See_Also: https://forum.dlang.org/post/zhgajqdhybtbufeiiofp@forum.dlang.org 1224 */ 1225 auto findLastSplitAfter(T)(scope return inout(T)[] haystack, 1226 scope const T needle) @trusted 1227 { 1228 static struct Result // NOTE `static` qualifier is needed for `inout` to propagate correctly 1229 { 1230 private T[] _haystack; 1231 private size_t _offset; 1232 pragma(inline, true): 1233 inout(T)[] pre() @trusted inout => !empty ? _haystack.ptr[0 .. _offset + 1] : _haystack[$ .. $]; 1234 inout(T)[] post() @trusted inout => !empty ? _haystack.ptr[_offset + 1 .. _haystack.length] : _haystack[0 .. $]; 1235 bool opCast(T : bool)() const => !empty; 1236 private bool empty() const @property => _haystack.length == _offset; 1237 } 1238 1239 static if (is(T == char)) 1240 assert(needle < 128); // See_Also: https://forum.dlang.org/post/sjirukypxmmcgdmqbcpe@forum.dlang.org 1241 const index = haystack.lastIndexOf(needle); 1242 if (index >= 0) 1243 return inout(Result)(haystack, index); 1244 return inout(Result)(haystack, haystack.length); // miss 1245 } 1246 1247 /// 1248 @safe pure nothrow @nogc unittest 1249 { 1250 char[] haystack; 1251 auto r = haystack.findLastSplitAfter('_'); 1252 static assert(is(typeof(r.pre()) == char[])); 1253 static assert(is(typeof(r.post()) == char[])); 1254 assert(!r); 1255 assert(!r.pre); 1256 assert(!r.post); 1257 1258 auto f()() @safe pure nothrow { char[1] x = "_"; return x[].findLastSplitAfter(' '); } 1259 static if (hasPreviewDIP1000) static assert(!__traits(compiles, { auto _ = f(); })); 1260 } 1261 1262 /// 1263 @safe pure nothrow @nogc unittest 1264 { 1265 const(char)[] haystack; 1266 auto r = haystack.findLastSplitAfter('_'); 1267 static assert(is(typeof(r.pre()) == const(char)[])); 1268 static assert(is(typeof(r.post()) == const(char)[])); 1269 assert(!r); 1270 assert(!r.pre); 1271 assert(!r.post); 1272 } 1273 1274 /// 1275 @safe pure nothrow @nogc unittest 1276 { 1277 auto r = "a*b*c".findLastSplitAfter('*'); 1278 static assert(is(typeof(r.pre()) == string)); 1279 static assert(is(typeof(r.post()) == string)); 1280 assert(r); 1281 assert(r.pre == "a*b*"); 1282 assert(r.post == "c"); 1283 } 1284 1285 import std.range.primitives : isInputRange, isInfinite, ElementType; 1286 1287 // Use until `std.algorithm.comparison.equal` supports uncopyable parameters. 1288 bool equal(T, U)(scope T a, scope U b) 1289 if (!(isInfinite!T && 1290 isInfinite!U)) 1291 { 1292 static if ((is(T == TE[m], TE, size_t m)) && 1293 (is(U == UE[n], UE, size_t n)) && 1294 is(typeof(TE.init == UE.init) : bool)) // static array 1295 { 1296 static if (m != n) 1297 return false; 1298 else 1299 { 1300 foreach (const i; 0 .. m) 1301 if (a[i] != b[i]) 1302 return false; 1303 return true; 1304 } 1305 } 1306 else static if ((is(T == TA[], TA)) && 1307 (is(U == UA[], UA)) && 1308 is(typeof(TA.init == UA.init) : bool)) // dynamic array 1309 { 1310 if (a.length != b.length) 1311 return false; 1312 const n = a.length; 1313 foreach (const i; 0 .. n) 1314 if (a[i] != b[i]) 1315 return false; 1316 return true; 1317 } 1318 else static if (is(typeof(ElementType!T.init == ElementType!U.init) : bool)) 1319 { 1320 static if (is(typeof(a[size_t.init]))) 1321 { 1322 import std.algorithm.mutation : move; 1323 size_t i = 0; 1324 foreach (const ref be; move(b)) 1325 if (a[i++] != be) 1326 return false; 1327 return true; 1328 } 1329 else static if (is(typeof(b[size_t.init]))) 1330 { 1331 import std.algorithm.mutation : move; 1332 size_t i = 0; 1333 foreach (const ref ae; move(a)) 1334 if (ae != b[i++]) 1335 return false; 1336 return true; 1337 } 1338 else 1339 { 1340 while (true) 1341 { 1342 if (a.empty()) 1343 return b.empty(); 1344 if (b.empty()) 1345 return a.empty(); 1346 if (a.front != b.front) 1347 return false; 1348 a.popFront(); 1349 b.popFront(); 1350 } 1351 } 1352 } 1353 else 1354 static assert(0, "Cannot compare " ~ T.stringof ~ "with" ~ U.stringof); 1355 } 1356 /// ditto 1357 bool equal(T, U)(scope const(T)[] a, scope const(U)[] b) 1358 if (is(typeof(T.init == U.init) : bool)) 1359 { 1360 } 1361 1362 /// dynamic arrays 1363 @safe pure nothrow @nogc unittest 1364 { 1365 assert(!equal([1, 2 ].s[], [1, 2, 3].s[])); 1366 assert(!equal([1, 2, 3].s[], [1, 2, ].s[])); 1367 assert( equal([1, 2, 3].s[], [1, 2, 3].s[])); 1368 } 1369 1370 /// static arrays 1371 @safe pure nothrow @nogc unittest 1372 { 1373 assert(!equal([1, 2 ].s, [1, 2, 3].s)); 1374 assert(!equal([1, 2, 3].s, [1, 2, ].s)); 1375 assert( equal([1, 2, 3].s, [1, 2, 3].s)); 1376 } 1377 1378 version(unittest) 1379 { 1380 import nxt.dip_traits : hasPreviewDIP1000; 1381 import nxt.array_help : s; 1382 } 1383 1384 // See_Also: https://dlang.org/spec/betterc.html#unittests 1385 version(unittestAsBetterC) 1386 extern(C) void main() 1387 { 1388 static foreach (u; __traits(getUnitTests, __traits(parent, main))) 1389 { 1390 u(); 1391 } 1392 }