1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * This module defines primitives for working with ranges. 7 * 8 * Copyright: Eugene Wissner 2017-2020. 9 * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 * Mozilla Public License, v. 2.0). 11 * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/primitive.d, 13 * tanya/range/primitive.d) 14 */ 15 module tanya.range.primitive; 16 17 import std.algorithm.comparison; 18 import tanya.memory.lifetime; 19 import tanya.meta.trait; 20 import tanya.meta.transform; 21 import tanya.range.array; 22 23 /** 24 * Returns the element type of the range $(D_PARAM R). 25 * 26 * Element type is the return type of such primitives like 27 * $(D_INLINECODE R.front) and (D_INLINECODE R.back) or the array base type. 28 * If $(D_PARAM R) is not a range, its element type is $(D_KEYWORD void). 29 * 30 * If $(D_PARAM R) is a string, $(D_PSYMBOL ElementType) doesn't distinguish 31 * between narrow and wide strings, it just returns the base type of the 32 * underlying array ($(D_KEYWORD char), $(D_KEYWORD wchar) or 33 * $(D_KEYWORD dchar)). 34 * 35 * Params: 36 * R = Range type. 37 * 38 * Returns: Element type of the range $(D_PARAM R). 39 */ 40 template ElementType(R) 41 { 42 static if (is(R U : U[])) 43 { 44 alias ElementType = U; 45 } 46 else static if (isInputRange!R) 47 { 48 alias ElementType = ReturnType!((R r) => r.front()); 49 } 50 else 51 { 52 alias ElementType = void; 53 } 54 } 55 56 /** 57 * Detects whether $(D_PARAM R) has a length property. 58 * 59 * $(D_PARAM R) does not have to be a range to support the length. 60 * 61 * Length mustn't be a $(D_KEYWORD @property) or a function, it can be a member 62 * variable or $(D_KEYWORD enum). But its type (or the type returned by the 63 * appropriate function) should be $(D_KEYWORD size_t), otherwise 64 * $(D_PSYMBOL hasLength) is $(D_KEYWORD false). 65 * 66 * All dynamic arrays except $(D_KEYWORD void)-arrays have length. 67 * 68 * Params: 69 * R = A type. 70 * 71 * Returns: $(D_KEYWORD true) if $(D_PARAM R) has a length property, 72 * $(D_KEYWORD false) otherwise. 73 * 74 * See_Also: $(D_PSYMBOL isInfinite). 75 */ 76 enum bool hasLength(R) = is(ReturnType!((R r) => r.length) == size_t); 77 78 /// 79 @nogc nothrow pure @safe unittest 80 { 81 static assert(hasLength!(char[])); 82 static assert(hasLength!(int[])); 83 static assert(hasLength!(const(int)[])); 84 85 struct A 86 { 87 enum size_t length = 1; 88 } 89 static assert(hasLength!(A)); 90 91 struct B 92 { 93 @property size_t length() const @nogc nothrow pure @safe 94 { 95 return 0; 96 } 97 } 98 static assert(hasLength!(B)); 99 100 struct C 101 { 102 @property const(size_t) length() const @nogc nothrow pure @safe 103 { 104 return 0; 105 } 106 } 107 static assert(!hasLength!C); 108 } 109 110 /** 111 * Determines whether $(D_PARAM R) is a forward range with slicing support 112 * ($(D_INLINECODE R[i .. j])). 113 * 114 * For finite ranges, the result of `opSlice()` must be of the same type as the 115 * original range. If the range defines opDollar, it must support subtraction. 116 * 117 * For infinite ranges, the result of `opSlice()` must be of the same type as 118 * the original range only if it defines `opDollar()`. Otherwise it can be any 119 * forward range. 120 * 121 * For both finite and infinite ranges, the result of `opSlice()` must have 122 * length. 123 * 124 * Params: 125 * R = The type to be tested. 126 * 127 * Returns: $(D_KEYWORD true) if $(D_PARAM R) supports slicing, 128 * $(D_KEYWORD false) otherwise. 129 */ 130 template hasSlicing(R) 131 { 132 private enum bool hasDollar = is(typeof((R r) => r[0 .. $])); 133 private enum bool subDollar = !hasDollar 134 || isInfinite!R 135 || is(ReturnType!((R r) => r[0 .. $ - 1]) == R); 136 137 static if (isForwardRange!R 138 && is(ReturnType!((R r) => r[0 .. 0]) T) 139 && (!hasDollar || is(ReturnType!((R r) => r[0 .. $]) == R)) 140 && subDollar 141 && isForwardRange!(ReturnType!((ref R r) => r[0 .. 0]))) 142 { 143 enum bool hasSlicing = (is(T == R) || isInfinite!R) 144 && hasLength!T; 145 } 146 else 147 { 148 enum bool hasSlicing = false; 149 } 150 } 151 152 /// 153 @nogc nothrow pure @safe unittest 154 { 155 static assert(hasSlicing!(int[])); 156 static assert(hasSlicing!(const(int)[])); 157 static assert(hasSlicing!(dstring)); 158 static assert(hasSlicing!(string)); 159 static assert(!hasSlicing!(const int[])); 160 static assert(!hasSlicing!(void[])); 161 162 struct A 163 { 164 int front() @nogc nothrow pure @safe 165 { 166 return 0; 167 } 168 void popFront() @nogc nothrow pure @safe 169 { 170 } 171 bool empty() const @nogc nothrow pure @safe 172 { 173 return false; 174 } 175 typeof(this) save() @nogc nothrow pure @safe 176 { 177 return this; 178 } 179 @property size_t length() const @nogc nothrow pure @safe 180 { 181 return 0; 182 } 183 typeof(this) opSlice(const size_t i, const size_t j) 184 pure nothrow @safe @nogc 185 { 186 return this; 187 } 188 } 189 static assert(hasSlicing!A); 190 191 struct B 192 { 193 struct Dollar 194 { 195 } 196 int front() @nogc nothrow pure @safe 197 { 198 return 0; 199 } 200 void popFront() @nogc nothrow pure @safe 201 { 202 } 203 bool empty() const @nogc nothrow pure @safe 204 { 205 return false; 206 } 207 typeof(this) save() @nogc nothrow pure @safe 208 { 209 return this; 210 } 211 @property size_t length() const @nogc nothrow pure @safe 212 { 213 return 0; 214 } 215 @property Dollar opDollar() const @nogc nothrow pure @safe 216 { 217 return Dollar(); 218 } 219 typeof(this) opSlice(const size_t i, const Dollar j) 220 pure nothrow @safe @nogc 221 { 222 return this; 223 } 224 } 225 static assert(!hasSlicing!B); 226 227 struct C 228 { 229 int front() @nogc nothrow pure @safe 230 { 231 return 0; 232 } 233 void popFront() @nogc nothrow pure @safe 234 { 235 } 236 enum bool empty = false; 237 typeof(this) save() @nogc nothrow pure @safe 238 { 239 return this; 240 } 241 typeof(this) opSlice(const size_t i, const size_t j) 242 pure nothrow @safe @nogc 243 { 244 return this; 245 } 246 } 247 static assert(!hasSlicing!C); 248 249 struct D 250 { 251 struct Range 252 { 253 int front() @nogc nothrow pure @safe 254 { 255 return 0; 256 } 257 void popFront() @nogc nothrow pure @safe 258 { 259 } 260 bool empty() const @nogc nothrow pure @safe 261 { 262 return true; 263 } 264 typeof(this) save() @nogc nothrow pure @safe 265 { 266 return this; 267 } 268 @property size_t length() const @nogc nothrow pure @safe 269 { 270 return 0; 271 } 272 } 273 int front() @nogc nothrow pure @safe 274 { 275 return 0; 276 } 277 void popFront() @nogc nothrow pure @safe 278 { 279 } 280 enum bool empty = false; 281 typeof(this) save() @nogc nothrow pure @safe 282 { 283 return this; 284 } 285 Range opSlice(const size_t i, const size_t j) 286 pure nothrow @safe @nogc 287 { 288 return Range(); 289 } 290 } 291 static assert(hasSlicing!D); 292 } 293 294 private template isDynamicArrayRange(R) 295 { 296 static if (is(R E : E[])) 297 { 298 enum bool isDynamicArrayRange = !is(E == void); 299 } 300 else 301 { 302 enum bool isDynamicArrayRange = false; 303 } 304 } 305 306 private struct Primitive(Candidate, string primitive) 307 { 308 auto ref returnType(ref Candidate candidate) 309 { 310 mixin("return candidate." ~ primitive ~ ";"); 311 } 312 313 alias ReturnType = .ReturnType!returnType; 314 static assert(!is(ReturnType == void)); 315 316 enum uint attributes = functionAttributes!returnType 317 & FunctionAttribute.ref_; 318 319 bool opEquals(That)(That) const 320 { 321 return is(ReturnType == That.ReturnType) 322 && attributes == That.attributes; 323 } 324 } 325 326 /** 327 * Determines whether $(D_PARAM R) is an input range. 328 * 329 * An input range should define following primitives: 330 * 331 * $(UL 332 * $(LI front) 333 * $(LI empty) 334 * $(LI popFront) 335 * ) 336 * 337 * Params: 338 * R = The type to be tested. 339 * 340 * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an input range, 341 * $(D_KEYWORD false) otherwise. 342 */ 343 template isInputRange(R) 344 { 345 static if (is(Primitive!(R, "front()") U) 346 && is(ReturnType!((R r) => r.empty) == bool) 347 && is(typeof(R.popFront()))) 348 { 349 enum bool isInputRange = true; 350 } 351 else 352 { 353 enum bool isInputRange = isDynamicArrayRange!R; 354 } 355 } 356 357 /// 358 @nogc nothrow pure @safe unittest 359 { 360 static struct Range 361 { 362 void popFront() @nogc nothrow pure @safe 363 { 364 } 365 366 int front() @nogc nothrow pure @safe 367 { 368 return 0; 369 } 370 371 bool empty() const @nogc nothrow pure @safe 372 { 373 return true; 374 } 375 } 376 static assert(isInputRange!Range); 377 static assert(isInputRange!(int[])); 378 static assert(!isInputRange!(void[])); 379 } 380 381 /** 382 * Determines whether $(D_PARAM R) is a forward range. 383 * 384 * A forward range is an input range that also defines: 385 * 386 * $(UL 387 * $(LI save) 388 * ) 389 * 390 * Params: 391 * R = The type to be tested. 392 * 393 * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a forward range, 394 * $(D_KEYWORD false) otherwise. 395 * 396 * See_Also: $(D_PSYMBOL isInputRange). 397 */ 398 template isForwardRange(R) 399 { 400 static if (is(ReturnType!((R r) => r.save()) U)) 401 { 402 enum bool isForwardRange = isInputRange!R && is(U == R); 403 } 404 else 405 { 406 enum bool isForwardRange = false; 407 } 408 } 409 410 /// 411 @nogc nothrow pure @safe unittest 412 { 413 static struct Range 414 { 415 void popFront() @nogc nothrow pure @safe 416 { 417 } 418 419 int front() @nogc nothrow pure @safe 420 { 421 return 0; 422 } 423 424 bool empty() const @nogc nothrow pure @safe 425 { 426 return true; 427 } 428 429 typeof(this) save() @nogc nothrow pure @safe 430 { 431 return this; 432 } 433 } 434 static assert(isForwardRange!Range); 435 static assert(isForwardRange!(int[])); 436 static assert(!isForwardRange!(void[])); 437 } 438 439 /** 440 * Determines whether $(D_PARAM R) is a bidirectional range. 441 * 442 * A bidirectional range is a forward range that also defines: 443 * 444 * $(UL 445 * $(LI back) 446 * $(LI popBack) 447 * ) 448 * 449 * Params: 450 * R = The type to be tested. 451 * 452 * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a bidirectional range, 453 * $(D_KEYWORD false) otherwise. 454 * 455 * See_Also: $(D_PSYMBOL isForwardRange). 456 */ 457 template isBidirectionalRange(R) 458 { 459 static if (is(Primitive!(R, "back()") U) 460 && is(typeof(R.popBack()))) 461 { 462 enum bool isBidirectionalRange = isForwardRange!R 463 && (U() == Primitive!(R, "front()")()); 464 } 465 else 466 { 467 enum bool isBidirectionalRange = isDynamicArrayRange!R; 468 } 469 } 470 471 /// 472 @nogc nothrow pure @safe unittest 473 { 474 static struct Range 475 { 476 void popFront() @nogc nothrow pure @safe 477 { 478 } 479 480 void popBack() @nogc nothrow pure @safe 481 { 482 } 483 484 @property int front() @nogc nothrow pure @safe 485 { 486 return 0; 487 } 488 489 @property int back() @nogc nothrow pure @safe 490 { 491 return 0; 492 } 493 494 bool empty() const @nogc nothrow pure @safe 495 { 496 return true; 497 } 498 499 Range save() @nogc nothrow pure @safe 500 { 501 return this; 502 } 503 } 504 static assert(isBidirectionalRange!Range); 505 static assert(isBidirectionalRange!(int[])); 506 static assert(!isBidirectionalRange!(void[])); 507 } 508 509 /** 510 * Determines whether $(D_PARAM R) is a random-access range. 511 * 512 * A random-access range is a range that allows random access to its 513 * elements by index using $(D_INLINECODE [])-operator (defined with 514 * $(D_INLINECODE opIndex())). Further a random access range should 515 * have a length or be infinite. 516 * 517 * Params: 518 * R = The type to be tested. 519 * 520 * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a random-access range, 521 * $(D_KEYWORD false) otherwise. 522 * 523 * See_Also: $(D_PSYMBOL isInfinite), 524 * $(D_PSYMBOL hasLength). 525 * 526 * Note: This definition differs from `std.range.primitives.isRandomAccessRange` 527 * in the D standard library in that it does not also require $(D_PARAM R) to 528 * be a forward range and a bidirectional range. Those properties may be tested 529 * separately with $(D_PSYMBOL isForwardRange) and 530 * $(D_PSYMBOL isBidirectionalRange). 531 */ 532 template isRandomAccessRange(R) 533 { 534 static if (is(Primitive!(R, "opIndex(size_t.init)") U)) 535 { 536 enum bool isRandomAccessRange = isInputRange!R 537 && (hasLength!R || isInfinite!R) 538 && (U() == Primitive!(R, "front()")()); 539 } 540 else 541 { 542 enum bool isRandomAccessRange = isDynamicArrayRange!R; 543 } 544 } 545 546 /// 547 @nogc nothrow pure @safe unittest 548 { 549 static struct A 550 { 551 void popFront() @nogc nothrow pure @safe 552 { 553 } 554 555 @property int front() @nogc nothrow pure @safe 556 { 557 return 0; 558 } 559 560 bool empty() const @nogc nothrow pure @safe 561 { 562 return true; 563 } 564 565 int opIndex(size_t) @nogc nothrow pure @safe 566 { 567 return 0; 568 } 569 570 size_t length() const @nogc nothrow pure @safe 571 { 572 return 0; 573 } 574 } 575 static assert(isRandomAccessRange!A); 576 static assert(isRandomAccessRange!(int[])); 577 static assert(!isRandomAccessRange!(void[])); 578 579 static struct B 580 { 581 void popFront() @nogc nothrow pure @safe 582 { 583 } 584 585 @property int front() @nogc nothrow pure @safe 586 { 587 return 0; 588 } 589 590 enum bool empty = false; 591 592 int opIndex(const size_t pos) @nogc nothrow pure @safe 593 { 594 return 0; 595 } 596 } 597 static assert(isRandomAccessRange!B); 598 } 599 600 /** 601 * Puts $(D_PARAM e) into the $(D_PARAM range). 602 * 603 * $(D_PSYMBOL R) should be an output range for $(D_PARAM E), i.e. at least one 604 * of the following conditions should met: 605 * 606 * $(OL 607 * $(LI $(D_PARAM e) can be put into $(D_PARAM range) using 608 * $(D_INLINECODE range(e)) 609 * $(LI $(D_PARAM e) can be assigned to $(D_INLINECODE range.front)) 610 * ) 611 * ) 612 * 613 * The method to put $(D_PARAM e) into $(D_PARAM range) is chosen based on the 614 * order specified above. 615 * 616 * If $(D_PARAM E) is an input range and $(D_PARAM R) is an output range for 617 * its elements as well, use $(D_PSYMBOL tanya.algorithm.mutation.copy) 618 * instead. 619 * 620 * $(D_PARAM range) is advanced after putting an element into it if it is an 621 * input range that doesn't define a `put`-method. 622 * 623 * Params: 624 * R = Target range type. 625 * E = Source element type. 626 * range = Target range. 627 * e = Source element. 628 * 629 * See_Also: $(D_PSYMBOL isOutputRange). 630 */ 631 void put(R, E)(ref R range, auto ref E e) 632 { 633 static if (is(typeof((R r, E e) => r(e)))) 634 { 635 range(e); 636 } 637 else static if (isInputRange!R 638 && is(typeof((R r, E e) => r.front = e))) 639 { 640 range.front = e; 641 range.popFront(); 642 } 643 else 644 { 645 static assert(false, R.stringof ~ " is not an output range for " 646 ~ E.stringof); 647 } 648 } 649 650 /// 651 @nogc nothrow pure @safe unittest 652 { 653 int[2] actual; 654 auto slice = actual[]; 655 656 put(slice, 2); 657 assert(actual == [2, 0]); 658 } 659 660 /// 661 @nogc nothrow pure @safe unittest 662 { 663 static struct OpCall 664 { 665 int e; 666 667 void opCall(int e) 668 { 669 this.e = e; 670 } 671 } 672 OpCall oc; 673 put(oc, 2); 674 assert(oc.e == 2); 675 } 676 677 /** 678 * Determines whether $(D_PARAM R) is an output range for the elemens of type 679 * $(D_PARAM E). 680 * 681 * If $(D_PARAM R) is an output range for the elements of type $(D_PARAM E) 682 * if an element `e` of type $(D_PARAM E) can be put into the range instance 683 * `r` in one of the following ways: 684 * 685 * $(TABLE 686 * $(TR 687 * $(TH Code) 688 * $(TH Scenario) 689 * ) 690 * $(TR 691 * $(TD r(e)) 692 * $(TD $(D_PARAM R) defines `opCall` for $(D_PARAM E).) 693 * ) 694 * $(TR 695 * $(TD r.front = e) 696 * $(TD $(D_PARAM R) is an input range with assignable elements of type 697 * $(D_PARAM E).) 698 * ) 699 * ) 700 * 701 * Output ranges don't have element type (so $(D_PSYMBOL ElementType) returns 702 * $(D_KEYWORD void) when applied to an output range). It is because an output 703 * range can support puting differently typed elements into it. 704 * 705 * Params: 706 * R = The type to be tested. 707 * E = Element type should be tested for. 708 * 709 * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the 710 * elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise. 711 * 712 * See_Also: $(D_PSYMBOL put). 713 */ 714 template isOutputRange(R, E) 715 { 716 static if (is(typeof((R r, E e) => put(r, e)))) 717 { 718 enum bool isOutputRange = true; 719 } 720 else static if (isInputRange!E) 721 { 722 pragma(msg, "Deprecation. An input range whose element type is " 723 ~ "supported by the output range isn't considered itself to " 724 ~ "be a source for such an output range. Don't rely on this " 725 ~ "behavior and use tanya.algorithm.copy() to write one " 726 ~ "range into another one."); 727 alias ET = ElementType!E; 728 enum bool isOutputRange = is(typeof((R r, ET e) => put(r, e))); 729 } 730 else 731 { 732 enum bool isOutputRange = false; 733 } 734 } 735 736 /// 737 @nogc nothrow pure @safe unittest 738 { 739 static struct R1 740 { 741 void opCall(int) @nogc nothrow pure @safe 742 { 743 } 744 } 745 static assert(isOutputRange!(R1, int)); 746 747 static struct R2 748 { 749 int value; 750 751 void popFront() @nogc nothrow pure @safe 752 { 753 } 754 755 ref int front() @nogc nothrow pure @safe 756 { 757 return value; 758 } 759 760 bool empty() const @nogc nothrow pure @safe 761 { 762 return true; 763 } 764 } 765 static assert(isOutputRange!(R2, int)); 766 767 static struct R3 768 { 769 void popFront() @nogc nothrow pure @safe 770 { 771 } 772 773 int front() @nogc nothrow pure @safe 774 { 775 return 0; 776 } 777 778 bool empty() const @nogc nothrow pure @safe 779 { 780 return true; 781 } 782 } 783 static assert(!isOutputRange!(R3, int)); 784 } 785 786 /** 787 * Determines whether $(D_PARAM R) is an infinite range. 788 * 789 * An infinite range is an input range whose `empty` member is defined as 790 * $(D_KEYWORD enum) which is always $(D_KEYWORD false). 791 * 792 * Params: 793 * R = A type. 794 * 795 * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an infinite range, 796 * $(D_KEYWORD false) otherwise. 797 */ 798 template isInfinite(R) 799 { 800 static if (isInputRange!R && is(typeof({enum bool e = R.empty;}))) 801 { 802 enum bool isInfinite = R.empty == false; 803 } 804 else 805 { 806 enum bool isInfinite = false; 807 } 808 } 809 810 /// 811 @nogc nothrow pure @safe unittest 812 { 813 static assert(!isInfinite!int); 814 815 static struct NotRange 816 { 817 enum bool empty = false; 818 } 819 static assert(!isInfinite!NotRange); 820 821 static struct InfiniteRange 822 { 823 void popFront() @nogc nothrow pure @safe 824 { 825 } 826 @property int front() @nogc nothrow pure @safe 827 { 828 return 0; 829 } 830 enum bool empty = false; 831 } 832 static assert(isInfinite!InfiniteRange); 833 834 static struct InputRange 835 { 836 void popFront() @nogc nothrow pure @safe 837 { 838 } 839 @property int front() @nogc nothrow pure @safe 840 { 841 return 0; 842 } 843 @property bool empty() const @nogc nothrow pure @safe 844 { 845 return false; 846 } 847 } 848 static assert(!isInfinite!InputRange); 849 } 850 851 /** 852 * Removes exactly $(D_PARAM count) first elements from the input range 853 * $(D_PARAM range). 854 * 855 * $(D_PARAM R) must have length or be infinite. 856 * 857 * Params: 858 * R = Range type. 859 * range = Some input range. 860 * count = Number of elements to remove. 861 * 862 * See_Also: $(D_PSYMBOL popBackExactly), 863 * $(D_PSYMBOL popFrontN), 864 * $(D_PSYMBOL isInputRange), 865 * $(D_PSYMBOL hasLength), 866 * $(D_PSYMBOL isInfinite). 867 * 868 * Precondition: If $(D_PARAM R) has length, it must be less than or equal to 869 * $(D_PARAM count). 870 */ 871 void popFrontExactly(R)(ref R range, size_t count) 872 if (isInputRange!R && (hasLength!R || isInfinite!R)) 873 in 874 { 875 static if (hasLength!R) 876 { 877 assert(count <= range.length); 878 } 879 } 880 do 881 { 882 static if (hasSlicing!R) 883 { 884 range = range[count .. $]; 885 } 886 else 887 { 888 while (count-- != 0) 889 { 890 range.popFront(); 891 } 892 } 893 } 894 895 /// 896 @nogc nothrow pure @safe unittest 897 { 898 int[5] a = [1, 2, 3, 4, 5]; 899 auto slice = a[]; 900 901 popFrontExactly(slice, 3); 902 assert(slice.length == 2); 903 assert(slice[0] == 4); 904 assert(slice[$ - 1] == 5); 905 906 popFrontExactly(slice, 2); 907 assert(slice.length == 0); 908 } 909 910 /** 911 * Removes exactly $(D_PARAM count) last elements from the bidirectional range 912 * $(D_PARAM range). 913 * 914 * $(D_PARAM R) must have length or be infinite. 915 * 916 * Params: 917 * R = Range type. 918 * range = Some bidirectional range. 919 * count = Number of elements to remove. 920 * 921 * See_Also: $(D_PSYMBOL popFrontExactly), 922 * $(D_PSYMBOL popBackN), 923 * $(D_PSYMBOL isBidirectionalRange), 924 * $(D_PSYMBOL hasLength), 925 * $(D_PSYMBOL isInfinite). 926 * 927 * Precondition: If $(D_PARAM R) has length, it must be less than or equal to 928 * $(D_PARAM count). 929 */ 930 void popBackExactly(R)(ref R range, size_t count) 931 if (isBidirectionalRange!R && (hasLength!R || isInfinite!R)) 932 in 933 { 934 static if (hasLength!R) 935 { 936 assert(count <= range.length); 937 } 938 } 939 do 940 { 941 static if (hasSlicing!R) 942 { 943 range = range[0 .. $ - count]; 944 } 945 else 946 { 947 while (count-- != 0) 948 { 949 range.popBack(); 950 } 951 } 952 } 953 954 /// 955 @nogc nothrow pure @safe unittest 956 { 957 int[5] a = [1, 2, 3, 4, 5]; 958 auto slice = a[]; 959 960 popBackExactly(slice, 3); 961 assert(slice.length == 2); 962 assert(slice[0] == 1); 963 assert(slice[$ - 1] == 2); 964 965 popBackExactly(slice, 2); 966 assert(slice.length == 0); 967 } 968 969 /** 970 * Removes maximum $(D_PARAM count) first elements from the input range 971 * $(D_PARAM range). 972 * 973 * Params: 974 * R = Range type. 975 * range = Some input range. 976 * count = Number of elements to remove. 977 * 978 * See_Also: $(D_PSYMBOL popBackN), 979 * $(D_PSYMBOL popFrontExactly), 980 * $(D_PSYMBOL isInputRange). 981 */ 982 void popFrontN(R)(ref R range, size_t count) 983 if (isInputRange!R) 984 { 985 static if (hasLength!R && hasSlicing!R) 986 { 987 range = range[min(count, range.length) .. $]; 988 } 989 else static if (hasLength!R) 990 { 991 size_t length = min(count, range.length); 992 while (length--) 993 { 994 range.popFront(); 995 } 996 } 997 else 998 { 999 while (count-- != 0 && !range.empty) 1000 { 1001 range.popFront(); 1002 } 1003 } 1004 } 1005 1006 /// 1007 @nogc nothrow pure @safe unittest 1008 { 1009 int[5] a = [1, 2, 3, 4, 5]; 1010 auto slice = a[]; 1011 1012 popFrontN(slice, 3); 1013 assert(slice.length == 2); 1014 assert(slice[0] == 4); 1015 assert(slice[$ - 1] == 5); 1016 1017 popFrontN(slice, 20); 1018 assert(slice.length == 0); 1019 } 1020 1021 /** 1022 * Removes maximum $(D_PARAM count) last elements from the bidirectional range 1023 * $(D_PARAM range). 1024 * 1025 * Params: 1026 * R = Range type. 1027 * range = Some bidirectional range. 1028 * count = Number of elements to remove. 1029 * 1030 * See_Also: $(D_PSYMBOL popFrontN), 1031 * $(D_PSYMBOL popBackExactly), 1032 * $(D_PSYMBOL isBidirectionalRange). 1033 */ 1034 void popBackN(R)(ref R range, size_t count) 1035 if (isBidirectionalRange!R) 1036 { 1037 static if (hasLength!R && hasSlicing!R) 1038 { 1039 range = range[0 .. $ - min(count, range.length)]; 1040 } 1041 else static if (hasLength!R) 1042 { 1043 size_t length = min(count, range.length); 1044 while (length--) 1045 { 1046 range.popBack(); 1047 } 1048 } 1049 else 1050 { 1051 while (count-- != 0 && !range.empty) 1052 { 1053 range.popBack(); 1054 } 1055 } 1056 } 1057 1058 /// 1059 @nogc nothrow pure @safe unittest 1060 { 1061 int[5] a = [1, 2, 3, 4, 5]; 1062 auto slice = a[]; 1063 1064 popBackN(slice, 3); 1065 assert(slice.length == 2); 1066 assert(slice[0] == 1); 1067 assert(slice[$ - 1] == 2); 1068 1069 popBackN(slice, 20); 1070 assert(slice.length == 0); 1071 } 1072 1073 /** 1074 * Moves the front element of an input range. 1075 * 1076 * The front element is left in a valid but unspecified state. 1077 * $(D_PSYMBOL moveFront) doesn't advances the range, so `popFront` should be 1078 * probably called after this function. 1079 * 1080 * Params: 1081 * R = Type of the range. 1082 * range = Input range. 1083 * 1084 * Returns: The front element of the $(D_PSYMBOL range). 1085 * 1086 * See_Also: $(D_PSYMBOL move). 1087 */ 1088 ElementType!R moveFront(R)(R range) 1089 if (isInputRange!R) 1090 { 1091 static if (!hasElaborateCopyConstructor!(ElementType!R)) 1092 { 1093 return range.front; 1094 } 1095 else static if (is(typeof(((ref ElementType!R e) => e)(range.front)))) 1096 { 1097 return move(range.front); 1098 } 1099 else 1100 { 1101 static assert(false, "Front element cannot be moved"); 1102 } 1103 } 1104 1105 /// 1106 @nogc nothrow pure @safe unittest 1107 { 1108 // Has elements without a postblit constructor. 1109 int[2] a = 5; 1110 1111 assert(moveFront(a[]) == 5); 1112 } 1113 1114 /** 1115 * Moves the back element of a bidirectional range. 1116 * 1117 * The back element is left in a valid but unspecified state. 1118 * $(D_PSYMBOL moveBack) doesn't advances the range, so `popBack` should be 1119 * probably called after this function. 1120 * 1121 * Params: 1122 * R = Type of the range. 1123 * range = Bidirectional range. 1124 * 1125 * Returns: The back element of the $(D_PSYMBOL range). 1126 * 1127 * See_Also: $(D_PSYMBOL move). 1128 */ 1129 ElementType!R moveBack(R)(R range) 1130 if (isBidirectionalRange!R) 1131 { 1132 static if (!hasElaborateCopyConstructor!(ElementType!R)) 1133 { 1134 return range.back; 1135 } 1136 else static if (is(typeof(((ref ElementType!R e) => e)(range.back)))) 1137 { 1138 return move(range.back); 1139 } 1140 else 1141 { 1142 static assert(false, "Back element cannot be moved"); 1143 } 1144 } 1145 1146 /// 1147 @nogc nothrow pure @safe unittest 1148 { 1149 // Has elements without a postblit constructor. 1150 int[2] a = 5; 1151 1152 assert(moveBack(a[]) == 5); 1153 } 1154 1155 /** 1156 * Moves the element at the position $(D_PARAM n) out of the range. 1157 * 1158 * The moved element is left in a valid but unspecified state. 1159 * 1160 * Params: 1161 * R = Range type. 1162 * range = Random-access range. 1163 * n = Element position. 1164 * 1165 * Returns: The element at the position $(D_PARAM n). 1166 * 1167 * See_Also: $(D_PSYMBOL move). 1168 */ 1169 ElementType!R moveAt(R)(R range, size_t n) 1170 if (isRandomAccessRange!R) 1171 { 1172 static if (!hasElaborateCopyConstructor!(ElementType!R)) 1173 { 1174 return range[n]; 1175 } 1176 else static if (is(typeof(((ref ElementType!R e) => e)(range[0])))) 1177 { 1178 return move(range[n]); 1179 } 1180 else 1181 { 1182 static assert(false, "Random element cannot be moved"); 1183 } 1184 } 1185 1186 /// 1187 @nogc nothrow pure @safe unittest 1188 { 1189 // Has elements without a postblit constructor. 1190 int[3] a = 5; 1191 1192 assert(moveAt(a[], 1) == 5); 1193 } 1194 1195 /** 1196 * Determines whether $(D_PSYMBOL R) is a range containing mobile elements, 1197 * i.e. elements that can be moved out of the range. 1198 * 1199 * Having mobile elements means for an input range to support 1200 * $(D_PSYMBOL moveFront), for a bidirectional range - both, 1201 * $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveBack), for a random-access 1202 * range - $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveAt). 1203 * 1204 * Params: 1205 * R = Range type. 1206 * 1207 * Returns: $(D_KEYWORD true) if $(D_PSYMBOL R) has mobile elements, 1208 * $(D_KEYWORD false) otherwise. 1209 * 1210 * See_Also: $(D_PSYMBOL moveFront), $(D_PSYMBOL moveBack), 1211 * $(D_PSYMBOL moveAt). 1212 */ 1213 template hasMobileElements(R) 1214 { 1215 static if (isRandomAccessRange!R) 1216 { 1217 enum bool hasMobileElements = is(typeof((R r) => moveFront(r))) 1218 && is(typeof((R r) => moveAt(r, 0))); 1219 } 1220 else static if (isBidirectionalRange!R) 1221 { 1222 enum bool hasMobileElements = is(typeof((R r) => moveFront(r))) 1223 && is(typeof((R r) => moveBack(r))); 1224 } 1225 else 1226 { 1227 enum bool hasMobileElements = is(typeof((R r) => moveFront(r))); 1228 } 1229 } 1230 1231 /// 1232 @nogc nothrow pure @safe unittest 1233 { 1234 static assert(hasMobileElements!(int[])); 1235 } 1236 1237 /// 1238 @nogc nothrow pure @safe unittest 1239 { 1240 static struct Element 1241 { 1242 this(this) @nogc nothrow pure @safe 1243 { 1244 } 1245 } 1246 1247 static struct R1 1248 { 1249 enum bool empty = false; 1250 1251 Element front() @nogc nothrow pure @safe 1252 { 1253 return Element(); 1254 } 1255 1256 void popFront() @nogc nothrow pure @safe 1257 { 1258 } 1259 } 1260 static assert(!hasMobileElements!R1); 1261 1262 static struct R2 1263 { 1264 enum bool empty = false; 1265 private Element front_; 1266 1267 ref Element front() @nogc nothrow pure @safe 1268 { 1269 return front_; 1270 } 1271 1272 void popFront() @nogc nothrow pure @safe 1273 { 1274 } 1275 } 1276 static assert(hasMobileElements!R2); 1277 } 1278 1279 /** 1280 * Determines whether $(D_PARAM R) provides access to its elements by 1281 * reference. 1282 * 1283 * Params: 1284 * R = Range type. 1285 * 1286 * Returns: $(D_KEYWORD true) if $(D_PARAM R) has lvalue elements, 1287 * $(D_KEYWORD false) otherwise. 1288 */ 1289 template hasLvalueElements(R) 1290 { 1291 private alias refDg = (ref ElementType!R e) => &e; 1292 1293 static if (isRandomAccessRange!R) 1294 { 1295 enum bool hasLvalueElements = is(typeof(refDg(R.init.front))) 1296 && is(typeof(refDg(R.init[0]))); 1297 } 1298 else static if (isBidirectionalRange!R) 1299 { 1300 enum bool hasLvalueElements = is(typeof(refDg(R.init.front))) 1301 && is(typeof(refDg(R.init.back))); 1302 } 1303 else 1304 { 1305 enum bool hasLvalueElements = is(typeof(refDg(R.init.front))); 1306 } 1307 } 1308 1309 /// 1310 @nogc nothrow pure @safe unittest 1311 { 1312 static struct R1 1313 { 1314 enum bool empty = false; 1315 1316 int front() @nogc nothrow pure @safe 1317 { 1318 return 5; 1319 } 1320 1321 void popFront() @nogc nothrow pure @safe 1322 { 1323 } 1324 } 1325 static assert(!hasLvalueElements!R1); 1326 1327 static struct R2 1328 { 1329 int element; 1330 enum bool empty = false; 1331 1332 ref const(int) front() const @nogc nothrow pure @safe 1333 { 1334 return element; 1335 } 1336 1337 void popFront() @nogc nothrow pure @safe 1338 { 1339 } 1340 1341 ref const(int) opIndex(size_t) const @nogc nothrow pure @safe 1342 { 1343 return element; 1344 } 1345 } 1346 static assert(hasLvalueElements!R2); 1347 } 1348 1349 /** 1350 * Determines whether the elements of $(D_PARAM R) are assignable. 1351 * 1352 * Params: 1353 * R = Range type. 1354 * 1355 * Returns: $(D_KEYWORD true) if the elements of $(D_PARAM R) are assignable 1356 * $(D_KEYWORD false) otherwise. 1357 */ 1358 template hasAssignableElements(R) 1359 { 1360 static if (isRandomAccessRange!R) 1361 { 1362 enum bool assignable = is(typeof({R.init.front = R.init.front;})) 1363 && is(typeof({R.init[0] = R.init[0];})); 1364 } 1365 else static if (isBidirectionalRange!R) 1366 { 1367 enum bool assignable = is(typeof({R.init.front = R.init.front;})) 1368 && is(typeof({R.init.back = R.init.back;})); 1369 } 1370 else 1371 { 1372 enum bool assignable = is(typeof({R.init.front = R.init.front;})); 1373 } 1374 enum bool hasAssignableElements = assignable; 1375 } 1376 1377 /// 1378 @nogc nothrow pure @safe unittest 1379 { 1380 static struct R1 1381 { 1382 int element; 1383 enum bool empty = false; 1384 1385 ref int front() @nogc nothrow pure @safe 1386 { 1387 return element; 1388 } 1389 alias back = front; 1390 1391 void popFront() @nogc nothrow pure @safe 1392 { 1393 } 1394 alias popBack = popFront; 1395 1396 R1 save() const @nogc nothrow pure @safe 1397 { 1398 return this; 1399 } 1400 } 1401 static assert(hasAssignableElements!R1); 1402 1403 static struct R2 1404 { 1405 int element; 1406 enum bool empty = false; 1407 1408 ref const(int) front() const @nogc nothrow pure @safe 1409 { 1410 return element; 1411 } 1412 alias back = front; 1413 1414 void popFront() @nogc nothrow pure @safe 1415 { 1416 } 1417 alias popBack = popFront; 1418 1419 R2 save() const @nogc nothrow pure @safe 1420 { 1421 return this; 1422 } 1423 } 1424 static assert(!hasAssignableElements!R2); 1425 } 1426 1427 /** 1428 * Determines whether the elements of $(D_PSYMBOL R) can be swapped with 1429 * $(D_PSYMBOL swap). 1430 * 1431 * Params: 1432 * R = Range type. 1433 * 1434 * Returns: $(D_KEYWORD true) if $(D_PARAM R) has swappable elements, 1435 * $(D_KEYWORD false) otherwise. 1436 */ 1437 template hasSwappableElements(R) 1438 { 1439 static if (isRandomAccessRange!R) 1440 { 1441 enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front))) 1442 && is(typeof(swap(R.init[0], R.init[0]))); 1443 } 1444 else static if (isBidirectionalRange!R) 1445 { 1446 enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front))) 1447 && is(typeof(swap(R.init.back, R.init.back))); 1448 } 1449 else 1450 { 1451 enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front))); 1452 } 1453 } 1454 1455 /// 1456 @nogc nothrow pure @safe unittest 1457 { 1458 static struct R1 1459 { 1460 int element; 1461 enum bool empty = false; 1462 1463 ref int front() @nogc nothrow pure @safe 1464 { 1465 return element; 1466 } 1467 alias back = front; 1468 1469 void popFront() @nogc nothrow pure @safe 1470 { 1471 } 1472 alias popBack = popFront; 1473 1474 R1 save() const @nogc nothrow pure @safe 1475 { 1476 return this; 1477 } 1478 } 1479 static assert(hasSwappableElements!R1); 1480 1481 static struct R2 1482 { 1483 int element; 1484 enum bool empty = false; 1485 1486 int front() const @nogc nothrow pure @safe 1487 { 1488 return element; 1489 } 1490 alias back = front; 1491 1492 void popFront() @nogc nothrow pure @safe 1493 { 1494 } 1495 alias popBack = popFront; 1496 1497 R2 save() const @nogc nothrow pure @safe 1498 { 1499 return this; 1500 } 1501 } 1502 static assert(!hasSwappableElements!R2); 1503 } 1504 1505 /** 1506 * Determines whether `r1.front` and `r2.front` point to the same element. 1507 * 1508 * Params: 1509 * r1 = First range. 1510 * r2 = Second range. 1511 * 1512 * Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) have the same 1513 * head, $(D_KEYWORD false) otherwise. 1514 */ 1515 bool sameHead(Range)(Range r1, Range r2) @trusted 1516 if (isInputRange!Range && hasLvalueElements!Range) 1517 { 1518 return &r1.front is &r2.front; 1519 } 1520 1521 /// 1522 @nogc nothrow pure @safe unittest 1523 { 1524 const int[2] array; 1525 1526 auto r1 = array[]; 1527 auto r2 = array[]; 1528 1529 assert(sameHead(r1, r2)); 1530 } 1531 1532 /// 1533 @nogc nothrow pure @safe unittest 1534 { 1535 const int[2] array; 1536 1537 auto r1 = array[]; 1538 auto r2 = array[1 .. $]; 1539 1540 assert(!sameHead(r1, r2)); 1541 } 1542 1543 /** 1544 * Returns the first element and advances the range. 1545 * 1546 * If $(D_PARAM range) has lvalue elements, then $(D_PSYMBOL getAndPopFront) 1547 * returns by reference, otherwise the returned element is copied. 1548 * 1549 * Params: 1550 * R = Input range type. 1551 * range = Input range. 1552 * 1553 * Returns: Front range element. 1554 * 1555 * See_Also: $(D_PSYMBOL getAndPopBack). 1556 */ 1557 ElementType!R getAndPopFront(R)(ref R range) 1558 if (isInputRange!R) 1559 in 1560 { 1561 assert(!range.empty); 1562 } 1563 do 1564 { 1565 static if (hasLvalueElements!R) 1566 { 1567 if (false) 1568 { 1569 // This code is removed by the compiler but ensures that 1570 // this function isn't @safe if range.front isn't @safe. 1571 auto _ = range.front(); 1572 } 1573 auto el = (() @trusted => &range.front())(); 1574 } 1575 else 1576 { 1577 auto el = range.front; 1578 } 1579 range.popFront(); 1580 static if (hasLvalueElements!R) 1581 { 1582 return *el; 1583 } 1584 else 1585 { 1586 return el; 1587 } 1588 } 1589 1590 /// 1591 @nogc nothrow pure @safe unittest 1592 { 1593 int[3] array = [1, 2, 3]; 1594 auto slice = array[]; 1595 1596 assert(getAndPopFront(slice) == 1); 1597 assert(slice.length == 2); 1598 } 1599 1600 /** 1601 * Returns the last element and removes it from the range. 1602 * 1603 * If $(D_PARAM range) has lvalue elements, then $(D_PSYMBOL getAndPopBack) 1604 * returns by reference, otherwise the returned element is copied. 1605 * 1606 * Params: 1607 * R = Bidirectional range type. 1608 * range = Bidirectional range. 1609 * 1610 * Returns: Last range element. 1611 * 1612 * See_Also: $(D_PSYMBOL getAndPopFront). 1613 */ 1614 auto ref getAndPopBack(R)(ref R range) 1615 if (isBidirectionalRange!R) 1616 in 1617 { 1618 assert(!range.empty); 1619 } 1620 do 1621 { 1622 static if (hasLvalueElements!R) 1623 { 1624 if (false) 1625 { 1626 // This code is removed by the compiler but ensures that 1627 // this function isn't @safe if range.back isn't @safe. 1628 auto _ = range.back(); 1629 } 1630 auto el = (() @trusted => &range.back())(); 1631 } 1632 else 1633 { 1634 auto el = range.back; 1635 } 1636 range.popBack(); 1637 static if (hasLvalueElements!R) 1638 { 1639 return *el; 1640 } 1641 else 1642 { 1643 return el; 1644 } 1645 } 1646 1647 /// 1648 @nogc nothrow pure @trusted unittest 1649 { 1650 int[3] array = [1, 2, 3]; 1651 auto slice = array[]; 1652 1653 assert(getAndPopBack(slice) == 3); 1654 assert(slice.length == 2); 1655 }