/** This module is a submodule of $(MREF std, range). It defines the bidirectional and forward range primitives for arrays: $(LREF empty), $(LREF front), $(LREF back), $(LREF popFront), $(LREF popBack) and $(LREF save). It provides basic range functionality by defining several templates for testing whether a given object is a range, and what kind of range it is: $(SCRIPT inhibitQuickIndex = 1;) $(DIVC quickindex, $(BOOKTABLE , $(TR $(TD $(LREF isInputRange)) $(TD Tests if something is an $(I input range), defined to be something from which one can sequentially read data using the primitives `front`, `popFront`, and `empty`. )) $(TR $(TD $(LREF isOutputRange)) $(TD Tests if something is an $(I output range), defined to be something to which one can sequentially write data using the $(LREF put) primitive. )) $(TR $(TD $(LREF isForwardRange)) $(TD Tests if something is a $(I forward range), defined to be an input range with the additional capability that one can save one's current position with the `save` primitive, thus allowing one to iterate over the same range multiple times. )) $(TR $(TD $(LREF isBidirectionalRange)) $(TD Tests if something is a $(I bidirectional range), that is, a forward range that allows reverse traversal using the primitives $(D back) and `popBack`. )) $(TR $(TD $(LREF isRandomAccessRange)) $(TD Tests if something is a $(I random access range), which is a bidirectional range that also supports the array subscripting operation via the primitive `opIndex`. )) )) It also provides number of templates that test for various range capabilities: $(BOOKTABLE , $(TR $(TD $(LREF hasMobileElements)) $(TD Tests if a given range's elements can be moved around using the primitives `moveFront`, `moveBack`, or `moveAt`. )) $(TR $(TD $(LREF ElementType)) $(TD Returns the element type of a given range. )) $(TR $(TD $(LREF ElementEncodingType)) $(TD Returns the encoding element type of a given range. )) $(TR $(TD $(LREF hasSwappableElements)) $(TD Tests if a range is a forward range with swappable elements. )) $(TR $(TD $(LREF hasAssignableElements)) $(TD Tests if a range is a forward range with mutable elements. )) $(TR $(TD $(LREF hasLvalueElements)) $(TD Tests if a range is a forward range with elements that can be passed by reference and have their address taken. )) $(TR $(TD $(LREF hasLength)) $(TD Tests if a given range has the `length` attribute. )) $(TR $(TD $(LREF isInfinite)) $(TD Tests if a given range is an $(I infinite range). )) $(TR $(TD $(LREF hasSlicing)) $(TD Tests if a given range supports the array slicing operation $(D R[x .. y]). )) ) Finally, it includes some convenience functions for manipulating ranges: $(BOOKTABLE , $(TR $(TD $(LREF popFrontN)) $(TD Advances a given range by up to $(I n) elements. )) $(TR $(TD $(LREF popBackN)) $(TD Advances a given bidirectional range from the right by up to $(I n) elements. )) $(TR $(TD $(LREF popFrontExactly)) $(TD Advances a given range by up exactly $(I n) elements. )) $(TR $(TD $(LREF popBackExactly)) $(TD Advances a given bidirectional range from the right by exactly $(I n) elements. )) $(TR $(TD $(LREF moveFront)) $(TD Removes the front element of a range. )) $(TR $(TD $(LREF moveBack)) $(TD Removes the back element of a bidirectional range. )) $(TR $(TD $(LREF moveAt)) $(TD Removes the $(I i)'th element of a random-access range. )) $(TR $(TD $(LREF walkLength)) $(TD Computes the length of any range in O(n) time. )) $(TR $(TD $(LREF put)) $(TD Outputs element `e` to a range. )) ) Source: $(PHOBOSSRC std/range/primitives.d) License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas in building this module goes to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). */ module std.range.primitives; import std.traits; /** Returns `true` if `R` is an input range. An input range must define the primitives `empty`, `popFront`, and `front`. The following code should compile for any input range. ---- R r; // can define a range object if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range of non-void type ---- The following are rules of input ranges are assumed to hold true in all Phobos code. These rules are not checkable at compile-time, so not conforming to these rules when writing ranges or range based code will result in undefined behavior. $(UL $(LI `r.empty` returns `false` if and only if there is more data available in the range.) $(LI `r.empty` evaluated multiple times, without calling `r.popFront`, or otherwise mutating the range object or the underlying data, yields the same result for every evaluation.) $(LI `r.front` returns the current element in the range. It may return by value or by reference.) $(LI `r.front` can be legally evaluated if and only if evaluating `r.empty` has, or would have, equaled `false`.) $(LI `r.front` evaluated multiple times, without calling `r.popFront`, or otherwise mutating the range object or the underlying data, yields the same result for every evaluation.) $(LI `r.popFront` advances to the next element in the range.) $(LI `r.popFront` can be called if and only if evaluating `r.empty` has, or would have, equaled `false`.) ) Also, note that Phobos code assumes that the primitives `r.front` and `r.empty` are $(BIGOH 1) time complexity wise or "cheap" in terms of running time. $(BIGOH) statements in the documentation of range functions are made with this assumption. See_Also: The header of $(MREF std,range) for tutorials on ranges. Params: R = type to be tested E = if present, the elements of the range must be $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) to this type Returns: `true` if R is an input range (possibly with element type `E`), `false` if not */ enum bool isInputRange(R) = is(typeof(R.init) == R) && is(typeof((R r) { return r.empty; } (R.init)) == bool) && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front))) && !is(typeof((R r) { return r.front; } (R.init)) == void) && is(typeof((R r) => r.popFront)); /// ditto enum bool isInputRange(R, E) = .isInputRange!R && isQualifierConvertible!(ElementType!R, E); /// @safe unittest { struct A {} struct B { void popFront(); @property bool empty(); @property int front(); } static assert(!isInputRange!A); static assert( isInputRange!B); static assert( isInputRange!(int[])); static assert( isInputRange!(char[])); static assert(!isInputRange!(char[4])); static assert( isInputRange!(inout(int)[])); static assert(!isInputRange!(int[], string)); static assert( isInputRange!(int[], int)); static assert( isInputRange!(int[], const int)); static assert(!isInputRange!(int[], immutable int)); static assert(!isInputRange!(const(int)[], int)); static assert( isInputRange!(const(int)[], const int)); static assert(!isInputRange!(const(int)[], immutable int)); static assert(!isInputRange!(immutable(int)[], int)); static assert( isInputRange!(immutable(int)[], const int)); static assert( isInputRange!(immutable(int)[], immutable int)); static struct NotDefaultConstructible { @disable this(); void popFront(); @property bool empty(); @property int front(); } static assert( isInputRange!NotDefaultConstructible); static struct NotDefaultConstructibleOrCopyable { @disable this(); @disable this(this); void popFront(); @property bool empty(); @property int front(); } static assert(isInputRange!NotDefaultConstructibleOrCopyable); static struct Frontless { void popFront(); @property bool empty(); } static assert(!isInputRange!Frontless); static struct VoidFront { void popFront(); @property bool empty(); void front(); } static assert(!isInputRange!VoidFront); } // https://issues.dlang.org/show_bug.cgi?id=16034 @safe unittest { struct One { int entry = 1; @disable this(this); } assert(isInputRange!(One[])); } @safe unittest { import std.algorithm.comparison : equal; static struct R { static struct Front { R* impl; @property int value() { return impl._front; } alias value this; } int _front; @property bool empty() { return _front >= 3; } @property auto front() { return Front(&this); } void popFront() { _front++; } } R r; static assert(isInputRange!R); assert(r.equal([ 0, 1, 2 ])); } /+ puts the whole raw element `e` into `r`. doPut will not attempt to iterate, slice or transcode `e` in any way shape or form. It will $(B only) call the correct primitive (`r.put(e)`, $(D r.front = e) or `r(e)` once. This can be important when `e` needs to be placed in `r` unchanged. Furthermore, it can be useful when working with `InputRange`s, as doPut guarantees that no more than a single element will be placed. +/ private void doPut(R, E)(ref R r, auto ref E e) { static if (is(PointerTarget!R == struct)) enum usingPut = hasMember!(PointerTarget!R, "put"); else enum usingPut = hasMember!(R, "put"); static if (usingPut) { static assert(is(typeof(r.put(e))), "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.put(e); } else static if (isNarrowString!R && is(const(E) == const(typeof(r[0])))) { // one character, we can put it r[0] = e; r = r[1 .. $]; } else static if (isNarrowString!R && isNarrowString!E && is(typeof(r[] = e))) { // slice assign. Note that this is a duplicate from put, but because // putChar uses doPut exclusively, we have to copy it here. immutable len = e.length; r[0 .. len] = e; r = r[len .. $]; } else static if (isInputRange!R) { static assert(is(typeof(r.front = e)), "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.front = e; r.popFront(); } else static if (is(typeof(r(e)))) { r(e); } else { static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); } } @safe unittest { static assert(!isNativeOutputRange!(int, int)); static assert( isNativeOutputRange!(int[], int)); static assert(!isNativeOutputRange!(int[][], int)); static assert(!isNativeOutputRange!(int, int[])); static assert(!isNativeOutputRange!(int[], int[])); static assert( isNativeOutputRange!(int[][], int[])); static assert(!isNativeOutputRange!(int, int[][])); static assert(!isNativeOutputRange!(int[], int[][])); static assert(!isNativeOutputRange!(int[][], int[][])); static assert(!isNativeOutputRange!(int[4], int)); static assert( isNativeOutputRange!(int[4][], int)); //Scary! static assert( isNativeOutputRange!(int[4][], int[4])); static assert( isNativeOutputRange!( char[], char)); static assert(!isNativeOutputRange!( char[], dchar)); static assert( isNativeOutputRange!(dchar[], char)); static assert( isNativeOutputRange!(dchar[], dchar)); } /++ Outputs `e` to `r`. The exact effect is dependent upon the two types. Several cases are accepted, as described below. The code snippets are attempted in order, and the first to compile "wins" and gets evaluated. In this table "doPut" is a method that places `e` into `r`, using the correct primitive: `r.put(e)` if `R` defines `put`, $(D r.front = e) if `r` is an input range (followed by `r.popFront()`), or `r(e)` otherwise. $(BOOKTABLE , $(TR $(TH Code Snippet) $(TH Scenario) ) $(TR $(TD `r.doPut(e);`) $(TD `R` specifically accepts an `E`.) ) $(TR $(TD $(D r.doPut([ e ]);)) $(TD `R` specifically accepts an `E[]`.) ) $(TR $(TD `r.putChar(e);`) $(TD `R` accepts some form of string or character. put will transcode the character `e` accordingly.) ) $(TR $(TD $(D for (; !e.empty; e.popFront()) put(r, e.front);)) $(TD Copying range `E` into `R`.) ) ) Tip: `put` should $(I not) be used "UFCS-style", e.g. `r.put(e)`. Doing this may call `R.put` directly, by-passing any transformation feature provided by `Range.put`. $(D put(r, e)) is prefered. +/ void put(R, E)(ref R r, E e) { //First level: simply straight up put. static if (is(typeof(doPut(r, e)))) { doPut(r, e); } //Optional optimization block for straight up array to array copy. else static if (isDynamicArray!R && !isAutodecodableString!R && isDynamicArray!E && is(typeof(r[] = e[]))) { immutable len = e.length; r[0 .. len] = e[]; r = r[len .. $]; } //Accepts E[] ? else static if (is(typeof(doPut(r, [e]))) && !isDynamicArray!R) { if (__ctfe) { E[1] arr = [e]; doPut(r, arr[]); } else doPut(r, (ref e) @trusted { return (&e)[0 .. 1]; }(e)); } //special case for char to string. else static if (isSomeChar!E && is(typeof(putChar(r, e)))) { putChar(r, e); } //Extract each element from the range //We can use "put" here, so we can recursively test a RoR of E. else static if (isInputRange!E && is(typeof(put(r, e.front)))) { //Special optimization: If E is a narrow string, and r accepts characters no-wider than the string's //Then simply feed the characters 1 by 1. static if (isAutodecodableString!E && !isAggregateType!E && ( (is(E : const char[]) && is(typeof(doPut(r, char.max))) && !is(typeof(doPut(r, dchar.max))) && !is(typeof(doPut(r, wchar.max)))) || (is(E : const wchar[]) && is(typeof(doPut(r, wchar.max))) && !is(typeof(doPut(r, dchar.max)))) ) ) { foreach (c; e) doPut(r, c); } else { for (; !e.empty; e.popFront()) put(r, e.front); } } else { static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); } } /** * When an output range's `put` method only accepts elements of type * `T`, use the global `put` to handle outputting a `T[]` to the range * or vice-versa. */ @safe pure unittest { import std.traits : isSomeChar; static struct A { string data; void put(C)(C c) if (isSomeChar!C) { data ~= c; } } static assert(isOutputRange!(A, char)); auto a = A(); put(a, "Hello"); assert(a.data == "Hello"); } /** * `put` treats dynamic arrays as array slices, and will call `popFront` * on the slice after an element has been copied. * * Be sure to save the position of the array before calling `put`. */ @safe pure nothrow unittest { int[] a = [1, 2, 3], b = [10, 20]; auto c = a; put(a, b); assert(c == [10, 20, 3]); // at this point, a was advanced twice, so it only contains // its last element while c represents the whole array assert(a == [3]); } /** * It's also possible to `put` any width strings or characters into narrow * strings -- put does the conversion for you. * * Note that putting the same width character as the target buffer type is * `nothrow`, but transcoding can throw a $(REF UTFException, std, utf). */ @safe pure unittest { // the elements must be mutable, so using string or const(char)[] // won't compile char[] s1 = new char[13]; auto r1 = s1; put(r1, "Hello, World!"w); assert(s1 == "Hello, World!"); } @safe pure nothrow unittest { // same thing, just using same character width. char[] s1 = new char[13]; auto r1 = s1; put(r1, "Hello, World!"); assert(s1 == "Hello, World!"); } @safe pure nothrow @nogc unittest { static struct R() { void put(scope const(char)[]) {} } R!() r; put(r, 'a'); } //Helper function to handle chars as quickly and as elegantly as possible //Assumes r.put(e)/r(e) has already been tested private void putChar(R, E)(ref R r, E e) if (isSomeChar!E) { // https://issues.dlang.org/show_bug.cgi?id=9186: Can't use (E[]).init enum csCond = is(typeof(doPut(r, (){ ref const( char)[] cstringInit(); return cstringInit(); }()))); enum wsCond = is(typeof(doPut(r, (){ ref const(wchar)[] wstringInit(); return wstringInit(); }()))); enum dsCond = is(typeof(doPut(r, (){ ref const(dchar)[] dstringInit(); return dstringInit(); }()))); //Use "max" to avoid static type demotion enum ccCond = is(typeof(doPut(r, char.max))); enum wcCond = is(typeof(doPut(r, wchar.max))); //enum dcCond = is(typeof(doPut(r, dchar.max))); //Fast transform a narrow char into a wider string static if ((wsCond && E.sizeof < wchar.sizeof) || (dsCond && E.sizeof < dchar.sizeof)) { enum w = wsCond && E.sizeof < wchar.sizeof; Select!(w, wchar, dchar) c = e; typeof(c)[1] arr = [c]; doPut(r, arr[]); } //Encode a wide char into a narrower string else static if (wsCond || csCond) { import std.utf : encode; /+static+/ Select!(wsCond, wchar[2], char[4]) buf; //static prevents purity. doPut(r, buf[0 .. encode(buf, e)]); } //Slowly encode a wide char into a series of narrower chars else static if (wcCond || ccCond) { import std.encoding : encode; alias C = Select!(wcCond, wchar, char); encode!(C, R)(e, r); } else { static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); } } pure @safe unittest { auto f = delegate (const(char)[]) {}; putChar(f, cast(dchar)'a'); } @safe pure unittest { static struct R() { void put(scope const(char)[]) {} } R!() r; putChar(r, 'a'); } @safe unittest { struct A {} static assert(!isInputRange!(A)); struct B { void put(int) {} } B b; put(b, 5); } @safe unittest { int[] a = new int[10]; int b; static assert(isInputRange!(typeof(a))); put(a, b); } @safe unittest { void myprint(scope const(char)[] s) { } auto r = &myprint; put(r, 'a'); } @safe unittest { int[] a = new int[10]; static assert(!__traits(compiles, put(a, 1.0L))); put(a, 1); assert(a.length == 9); /* * a[0] = 65; // OK * a[0] = 'A'; // OK * a[0] = "ABC"[0]; // OK * put(a, "ABC"); // OK */ put(a, "ABC"); assert(a.length == 6); } @safe unittest { char[] a = new char[10]; static assert(!__traits(compiles, put(a, 1.0L))); static assert(!__traits(compiles, put(a, 1))); //char[] is now an output range for char, wchar, dchar, and ranges of such. static assert(__traits(compiles, putChar(a, 'a'))); static assert(__traits(compiles, put(a, wchar('a')))); static assert(__traits(compiles, put(a, dchar('a')))); static assert(__traits(compiles, put(a, "ABC"))); static assert(__traits(compiles, put(a, "ABC"w))); static assert(__traits(compiles, put(a, "ABC"d))); } @safe unittest { // attempt putting into narrow strings by transcoding char[] a = new char[10]; auto b = a; put(a, "ABC"w); assert(b[0 .. 3] == "ABC"); assert(a.length == 7); a = b; // reset put(a, 'λ'); assert(b[0 .. 2] == "λ"); assert(a.length == 8); a = b; // reset put(a, "ABC"d); assert(b[0 .. 3] == "ABC"); assert(a.length == 7); a = b; // reset put(a, '𐐷'); assert(b[0 .. 4] == "𐐷"); assert(a.length == 6); wchar[] aw = new wchar[10]; auto bw = aw; put(aw, "ABC"); assert(bw[0 .. 3] == "ABC"w); assert(aw.length == 7); aw = bw; // reset put(aw, 'λ'); assert(bw[0 .. 1] == "λ"w); assert(aw.length == 9); aw = bw; // reset put(aw, "ABC"d); assert(bw[0 .. 3] == "ABC"w); assert(aw.length == 7); aw = bw; // reset put(aw, '𐐷'); assert(bw[0 .. 2] == "𐐷"w); assert(aw.length == 8); aw = bw; // reset put(aw, "𐐷"); // try transcoding from char[] assert(bw[0 .. 2] == "𐐷"w); assert(aw.length == 8); } @safe unittest { int[][] a = new int[][10]; int[] b = new int[10]; int c; put(b, c); assert(b.length == 9); put(a, b); assert(a.length == 9); static assert(!__traits(compiles, put(a, c))); } @safe unittest { int[][] a = new int[][](3); int[] b = [1]; auto aa = a; put(aa, b); assert(aa == [[], []]); assert(a == [[1], [], []]); int[][3] c = [2]; aa = a; put(aa, c[]); assert(aa.empty); assert(a == [[2], [2], [2]]); } @safe unittest { // Test fix for bug 7476. struct LockingTextWriter { void put(dchar c){} } struct RetroResult { bool end = false; @property bool empty() const { return end; } @property dchar front(){ return 'a'; } void popFront(){ end = true; } } LockingTextWriter w; RetroResult re; put(w, re); } @system unittest { import std.conv : to; import std.meta : AliasSeq; import std.typecons : tuple; static struct PutC(C) { string result; void put(const(C) c) { result ~= to!string((&c)[0 .. 1]); } } static struct PutS(C) { string result; void put(const(C)[] s) { result ~= to!string(s); } } static struct PutSS(C) { string result; void put(const(C)[][] ss) { foreach (s; ss) result ~= to!string(s); } } PutS!char p; putChar(p, cast(dchar)'a'); //Source Char static foreach (SC; AliasSeq!(char, wchar, dchar)) {{ SC ch = 'I'; dchar dh = '♥'; immutable(SC)[] s = "日本語!"; immutable(SC)[][] ss = ["日本語", "が", "好き", "ですか", "?"]; //Target Char static foreach (TC; AliasSeq!(char, wchar, dchar)) { //Testing PutC and PutS static foreach (Type; AliasSeq!(PutC!TC, PutS!TC)) {{ Type type; auto sink = new Type(); //Testing put and sink foreach (value ; tuple(type, sink)) { put(value, ch); assert(value.result == "I"); put(value, dh); assert(value.result == "I♥"); put(value, s); assert(value.result == "I♥日本語!"); put(value, ss); assert(value.result == "I♥日本語!日本語が好きですか?"); } }} } }} } @safe unittest { static struct CharRange { char c; enum empty = false; void popFront(){} ref char front() return @property { return c; } } CharRange c; put(c, cast(dchar)'H'); put(c, "hello"d); } // https://issues.dlang.org/show_bug.cgi?id=9823 @system unittest { const(char)[] r; void delegate(const(char)[]) dg = (s) { r = s; }; put(dg, ["ABC"]); assert(r == "ABC"); } // https://issues.dlang.org/show_bug.cgi?id=10571 @safe unittest { import std.format.write : formattedWrite; string buf; formattedWrite((scope const(char)[] s) { buf ~= s; }, "%s", "hello"); assert(buf == "hello"); } @safe unittest { import std.format.write : formattedWrite; import std.meta : AliasSeq; struct PutC(C) { void put(C){} } struct PutS(C) { void put(const(C)[]){} } struct CallC(C) { void opCall(C){} } struct CallS(C) { void opCall(const(C)[]){} } struct FrontC(C) { enum empty = false; auto front()@property{return C.init;} void front(C)@property{} void popFront(){} } struct FrontS(C) { enum empty = false; auto front()@property{return C[].init;} void front(const(C)[])@property{} void popFront(){} } void foo() { static foreach (C; AliasSeq!(char, wchar, dchar)) {{ formattedWrite((C c){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); formattedWrite((const(C)[]){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); formattedWrite(PutC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); formattedWrite(PutS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); CallC!C callC; CallS!C callS; formattedWrite(callC, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); formattedWrite(callS, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); formattedWrite(FrontC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); formattedWrite(FrontS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); }} formattedWrite((dchar[]).init, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); } } /+ Returns `true` if `R` is a native output range for elements of type `E`. An output range is defined functionally as a range that supports the operation $(D doPut(r, e)) as defined above. if $(D doPut(r, e)) is valid, then `put(r,e)` will have the same behavior. The two guarantees isNativeOutputRange gives over the larger `isOutputRange` are: 1: `e` is $(B exactly) what will be placed (not `[e]`, for example). 2: if `E` is a non $(empty) `InputRange`, then placing `e` is guaranteed to not overflow the range. +/ package(std) enum bool isNativeOutputRange(R, E) = is(typeof(doPut(lvalueOf!R, lvalueOf!E))); @safe unittest { int[] r = new int[](4); static assert(isInputRange!(int[])); static assert( isNativeOutputRange!(int[], int)); static assert(!isNativeOutputRange!(int[], int[])); static assert( isOutputRange!(int[], int[])); if (!r.empty) put(r, 1); //guaranteed to succeed if (!r.empty) put(r, [1, 2]); //May actually error out. } /++ Returns `true` if `R` is an output range for elements of type `E`. An output range is defined functionally as a range that supports the operation $(D put(r, e)) as defined above. See_Also: The header of $(MREF std,range) for tutorials on ranges. +/ enum bool isOutputRange(R, E) = is(typeof(put(lvalueOf!R, lvalueOf!E))); /// @safe unittest { void myprint(scope const(char)[] s) { } static assert(isOutputRange!(typeof(&myprint), char)); static assert( isOutputRange!(char[], char)); static assert( isOutputRange!(dchar[], wchar)); static assert( isOutputRange!(dchar[], dchar)); } @safe unittest { import std.array; import std.stdio : writeln; auto app = appender!string(); string s; static assert( isOutputRange!(Appender!string, string)); static assert( isOutputRange!(Appender!string*, string)); static assert(!isOutputRange!(Appender!string, int)); static assert( isOutputRange!(wchar[], wchar)); static assert( isOutputRange!(dchar[], char)); static assert( isOutputRange!(dchar[], string)); static assert( isOutputRange!(dchar[], wstring)); static assert( isOutputRange!(dchar[], dstring)); static assert(!isOutputRange!(const(int)[], int)); static assert(!isOutputRange!(inout(int)[], int)); } /** Returns `true` if `R` is a forward range. A forward range is an input range `r` that can save "checkpoints" by saving `r.save` to another value of type `R`. Notable examples of input ranges that are $(I not) forward ranges are file/socket ranges; copying such a range will not save the position in the stream, and they most likely reuse an internal buffer as the entire stream does not sit in memory. Subsequently, advancing either the original or the copy will advance the stream, so the copies are not independent. The following code should compile for any forward range. ---- static assert(isInputRange!R); R r1; auto s1 = r1.save; static assert(is(typeof(s1) == R)); ---- Saving a range is not duplicating it; in the example above, `r1` and `r2` still refer to the same underlying data. They just navigate that data independently. The semantics of a forward range (not checkable during compilation) are the same as for an input range, with the additional requirement that backtracking must be possible by saving a copy of the range object with `save` and using it later. `save` behaves in many ways like a copy constructor, and its implementation typically is done using copy construction. The existence of a copy constructor, however, does not imply the range is a forward range. For example, a range that reads from a TTY consumes its input and cannot save its place and read it again, and so cannot be a forward range and cannot have a `save` function. See_Also: The header of $(MREF std,range) for tutorials on ranges. Params: R = type to be tested E = if present, the elements of the range must be $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) to this type Returns: `true` if R is a forward range (possibly with element type `E`), `false` if not */ enum bool isForwardRange(R) = isInputRange!R && is(typeof((R r) { return r.save; } (R.init)) == R); /// ditto enum bool isForwardRange(R, E) = .isForwardRange!R && isQualifierConvertible!(ElementType!R, E); /// @safe unittest { static assert(!isForwardRange!(int)); static assert( isForwardRange!(int[])); static assert( isForwardRange!(inout(int)[])); static assert( isForwardRange!(int[], const int)); static assert(!isForwardRange!(int[], immutable int)); static assert(!isForwardRange!(const(int)[], int)); static assert( isForwardRange!(const(int)[], const int)); static assert(!isForwardRange!(const(int)[], immutable int)); static assert(!isForwardRange!(immutable(int)[], int)); static assert( isForwardRange!(immutable(int)[], const int)); static assert( isForwardRange!(immutable(int)[], immutable int)); } @safe unittest { // BUG 14544 struct R14544 { int front() { return 0;} void popFront() {} bool empty() { return false; } R14544 save() {return this;} } static assert( isForwardRange!R14544 ); } /** Returns `true` if `R` is a bidirectional range. A bidirectional range is a forward range that also offers the primitives `back` and `popBack`. The following code should compile for any bidirectional range. The semantics of a bidirectional range (not checkable during compilation) are assumed to be the following (`r` is an object of type `R`): $(UL $(LI `r.back` returns (possibly a reference to) the last element in the range. Calling `r.back` is allowed only if calling `r.empty` has, or would have, returned `false`.)) See_Also: The header of $(MREF std,range) for tutorials on ranges. Params: R = type to be tested E = if present, the elements of the range must be $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) to this type Returns: `true` if R is a bidirectional range (possibly with element type `E`), `false` if not */ enum bool isBidirectionalRange(R) = isForwardRange!R && is(typeof((R r) => r.popBack)) && (is(typeof((return ref R r) => r.back)) || is(typeof(ref (return ref R r) => r.back))) && is(typeof(R.init.back.init) == ElementType!R); /// ditto enum bool isBidirectionalRange(R, E) = .isBidirectionalRange!R && isQualifierConvertible!(ElementType!R, E); /// @safe unittest { alias R = int[]; R r = [0,1]; static assert(isForwardRange!R); // is forward range r.popBack(); // can invoke popBack auto t = r.back; // can get the back of the range auto w = r.front; static assert(is(typeof(t) == typeof(w))); // same type for front and back // Checking the element type static assert( isBidirectionalRange!(int[], const int)); static assert(!isBidirectionalRange!(int[], immutable int)); static assert(!isBidirectionalRange!(const(int)[], int)); static assert( isBidirectionalRange!(const(int)[], const int)); static assert(!isBidirectionalRange!(const(int)[], immutable int)); static assert(!isBidirectionalRange!(immutable(int)[], int)); static assert( isBidirectionalRange!(immutable(int)[], const int)); static assert( isBidirectionalRange!(immutable(int)[], immutable int)); } @safe unittest { struct A {} struct B { void popFront(); @property bool empty(); @property int front(); } struct C { @property bool empty(); @property C save(); void popFront(); @property int front(); void popBack(); @property int back(); } static assert(!isBidirectionalRange!(A)); static assert(!isBidirectionalRange!(B)); static assert( isBidirectionalRange!(C)); static assert( isBidirectionalRange!(int[])); static assert( isBidirectionalRange!(char[])); static assert( isBidirectionalRange!(inout(int)[])); } /** Returns `true` if `R` is a random-access range. A random-access range is a bidirectional range that also offers the primitive $(D opIndex), OR an infinite forward range that offers `opIndex`. In either case, the range must either offer `length` or be infinite. The following code should compile for any random-access range. The semantics of a random-access range (not checkable during compilation) are assumed to be the following (`r` is an object of type `R`): $(UL $(LI `r.opIndex(n)` returns a reference to the `n`th element in the range.)) Although `char[]` and `wchar[]` (as well as their qualified versions including `string` and `wstring`) are arrays, $(D isRandomAccessRange) yields `false` for them because they use variable-length encodings (UTF-8 and UTF-16 respectively). These types are bidirectional ranges only. See_Also: The header of $(MREF std,range) for tutorials on ranges. Params: R = type to be tested E = if present, the elements of the range must be $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) to this type Returns: `true` if R is a random-access range (possibly with element type `E`), `false` if not */ enum bool isRandomAccessRange(R) = is(typeof(lvalueOf!R[1]) == ElementType!R) && !(isAutodecodableString!R && !isAggregateType!R) && isForwardRange!R && (isBidirectionalRange!R || isInfinite!R) && (hasLength!R || isInfinite!R) && (isInfinite!R || !is(typeof(lvalueOf!R[$ - 1])) || is(typeof(lvalueOf!R[$ - 1]) == ElementType!R)); /// ditto enum bool isRandomAccessRange(R, E) = .isRandomAccessRange!R && isQualifierConvertible!(ElementType!R, E); /// @safe unittest { import std.traits : isAggregateType, isAutodecodableString; alias R = int[]; // range is finite and bidirectional or infinite and forward. static assert(isBidirectionalRange!R || isForwardRange!R && isInfinite!R); R r = [0,1]; auto e = r[1]; // can index auto f = r.front; static assert(is(typeof(e) == typeof(f))); // same type for indexed and front static assert(!(isAutodecodableString!R && !isAggregateType!R)); // narrow strings cannot be indexed as ranges static assert(hasLength!R || isInfinite!R); // must have length or be infinite // $ must work as it does with arrays if opIndex works with $ static if (is(typeof(r[$]))) { static assert(is(typeof(f) == typeof(r[$]))); // $ - 1 doesn't make sense with infinite ranges but needs to work // with finite ones. static if (!isInfinite!R) static assert(is(typeof(f) == typeof(r[$ - 1]))); } // Checking the element type static assert( isRandomAccessRange!(int[], const int)); static assert(!isRandomAccessRange!(int[], immutable int)); static assert(!isRandomAccessRange!(const(int)[], int)); static assert( isRandomAccessRange!(const(int)[], const int)); static assert(!isRandomAccessRange!(const(int)[], immutable int)); static assert(!isRandomAccessRange!(immutable(int)[], int)); static assert( isRandomAccessRange!(immutable(int)[], const int)); static assert( isRandomAccessRange!(immutable(int)[], immutable int)); } @safe unittest { struct A {} struct B { void popFront(); @property bool empty(); @property int front(); } struct C { void popFront(); @property bool empty(); @property int front(); void popBack(); @property int back(); } struct D { @property bool empty(); @property D save(); @property int front(); void popFront(); @property int back(); void popBack(); ref int opIndex(uint); @property size_t length(); alias opDollar = length; //int opSlice(uint, uint); } struct E { bool empty(); E save(); int front(); void popFront(); int back(); void popBack(); ref int opIndex(uint); size_t length(); alias opDollar = length; //int opSlice(uint, uint); } static assert(!isRandomAccessRange!(A)); static assert(!isRandomAccessRange!(B)); static assert(!isRandomAccessRange!(C)); static assert( isRandomAccessRange!(D)); static assert( isRandomAccessRange!(E)); static assert( isRandomAccessRange!(int[])); static assert( isRandomAccessRange!(inout(int)[])); } @safe unittest { // Test fix for bug 6935. struct R { @disable this(); @property bool empty() const { return false; } @property int front() const { return 0; } void popFront() {} @property R save() { return this; } @property int back() const { return 0; } void popBack(){} int opIndex(size_t n) const { return 0; } @property size_t length() const { return 0; } alias opDollar = length; void put(int e){ } } static assert(isInputRange!R); static assert(isForwardRange!R); static assert(isBidirectionalRange!R); static assert(isRandomAccessRange!R); static assert(isOutputRange!(R, int)); } /** Returns `true` iff `R` is an input range that supports the `moveFront` primitive, as well as `moveBack` and `moveAt` if it's a bidirectional or random access range. These may be explicitly implemented, or may work via the default behavior of the module level functions `moveFront` and friends. The following code should compile for any range with mobile elements. ---- alias E = ElementType!R; R r; static assert(isInputRange!R); static assert(is(typeof(moveFront(r)) == E)); static if (isBidirectionalRange!R) static assert(is(typeof(moveBack(r)) == E)); static if (isRandomAccessRange!R) static assert(is(typeof(moveAt(r, 0)) == E)); ---- */ enum bool hasMobileElements(R) = isInputRange!R && is(typeof(moveFront(lvalueOf!R)) == ElementType!R) && (!isBidirectionalRange!R || is(typeof(moveBack(lvalueOf!R)) == ElementType!R)) && (!isRandomAccessRange!R || is(typeof(moveAt(lvalueOf!R, 0)) == ElementType!R)); /// @safe unittest { import std.algorithm.iteration : map; import std.range : iota, repeat; static struct HasPostblit { this(this) {} } auto nonMobile = map!"a"(repeat(HasPostblit.init)); static assert(!hasMobileElements!(typeof(nonMobile))); static assert( hasMobileElements!(int[])); static assert( hasMobileElements!(inout(int)[])); static assert( hasMobileElements!(typeof(iota(1000)))); static assert( hasMobileElements!( string)); static assert( hasMobileElements!(dstring)); static assert( hasMobileElements!( char[])); static assert( hasMobileElements!(dchar[])); } /** The element type of `R`. `R` does not have to be a range. The element type is determined as the type yielded by `r.front` for an object `r` of type `R`. For example, `ElementType!(T[])` is `T` if `T[]` isn't a narrow string; if it is, the element type is `dchar`. If `R` doesn't have `front`, `ElementType!R` is `void`. */ template ElementType(R) { static if (is(typeof(R.init.front.init) T)) alias ElementType = T; else alias ElementType = void; } /// @safe unittest { import std.range : iota; // Standard arrays: returns the type of the elements of the array static assert(is(ElementType!(int[]) == int)); // Accessing .front retrieves the decoded dchar static assert(is(ElementType!(char[]) == dchar)); // rvalue static assert(is(ElementType!(dchar[]) == dchar)); // lvalue // Ditto static assert(is(ElementType!(string) == dchar)); static assert(is(ElementType!(dstring) == immutable(dchar))); // For ranges it gets the type of .front. auto range = iota(0, 10); static assert(is(ElementType!(typeof(range)) == int)); } @safe unittest { static assert(is(ElementType!(byte[]) == byte)); static assert(is(ElementType!(wchar[]) == dchar)); // rvalue static assert(is(ElementType!(wstring) == dchar)); } @safe unittest { enum XYZ : string { a = "foo" } auto x = XYZ.a.front; immutable char[3] a = "abc"; int[] i; void[] buf; static assert(is(ElementType!(XYZ) == dchar)); static assert(is(ElementType!(typeof(a)) == dchar)); static assert(is(ElementType!(typeof(i)) == int)); static assert(is(ElementType!(typeof(buf)) == void)); static assert(is(ElementType!(inout(int)[]) == inout(int))); static assert(is(ElementType!(inout(int[])) == inout(int))); } @safe unittest { static assert(is(ElementType!(int[5]) == int)); static assert(is(ElementType!(int[0]) == int)); static assert(is(ElementType!(char[5]) == dchar)); static assert(is(ElementType!(char[0]) == dchar)); } // https://issues.dlang.org/show_bug.cgi?id=11336 @safe unittest { static struct S { this(this) @disable; } static assert(is(ElementType!(S[]) == S)); } // https://issues.dlang.org/show_bug.cgi?id=11401 @safe unittest { // ElementType should also work for non-@propety 'front' struct E { ushort id; } struct R { E front() { return E.init; } } static assert(is(ElementType!R == E)); } /** The encoding element type of `R`. For narrow strings (`char[]`, `wchar[]` and their qualified variants including `string` and `wstring`), `ElementEncodingType` is the character type of the string. For all other types, `ElementEncodingType` is the same as `ElementType`. */ template ElementEncodingType(R) { static if (is(StringTypeOf!R) && is(R : E[], E)) alias ElementEncodingType = E; else alias ElementEncodingType = ElementType!R; } /// @safe unittest { import std.range : iota; // internally the range stores the encoded type static assert(is(ElementEncodingType!(char[]) == char)); static assert(is(ElementEncodingType!(wstring) == immutable(wchar))); static assert(is(ElementEncodingType!(byte[]) == byte)); auto range = iota(0, 10); static assert(is(ElementEncodingType!(typeof(range)) == int)); } @safe unittest { static assert(is(ElementEncodingType!(wchar[]) == wchar)); static assert(is(ElementEncodingType!(dchar[]) == dchar)); static assert(is(ElementEncodingType!(string) == immutable(char))); static assert(is(ElementEncodingType!(dstring) == immutable(dchar))); static assert(is(ElementEncodingType!(int[]) == int)); } @safe unittest { enum XYZ : string { a = "foo" } auto x = XYZ.a.front; immutable char[3] a = "abc"; int[] i; void[] buf; static assert(is(ElementType!(XYZ) : dchar)); static assert(is(ElementEncodingType!(char[]) == char)); static assert(is(ElementEncodingType!(string) == immutable char)); static assert(is(ElementType!(typeof(a)) : dchar)); static assert(is(ElementType!(typeof(i)) == int)); static assert(is(ElementEncodingType!(typeof(i)) == int)); static assert(is(ElementType!(typeof(buf)) : void)); static assert(is(ElementEncodingType!(inout char[]) : inout(char))); } @safe unittest { static assert(is(ElementEncodingType!(int[5]) == int)); static assert(is(ElementEncodingType!(int[0]) == int)); static assert(is(ElementEncodingType!(char[5]) == char)); static assert(is(ElementEncodingType!(char[0]) == char)); } /** Returns `true` if `R` is an input range and has swappable elements. The following code should compile for any range with swappable elements. ---- R r; static assert(isInputRange!R); swap(r.front, r.front); static if (isBidirectionalRange!R) swap(r.back, r.front); static if (isRandomAccessRange!R) swap(r[0], r.front); ---- */ template hasSwappableElements(R) { import std.algorithm.mutation : swap; enum bool hasSwappableElements = isInputRange!R && is(typeof((ref R r) => swap(r.front, r.front))) && (!isBidirectionalRange!R || is(typeof((ref R r) => swap(r.back, r.front)))) && (!isRandomAccessRange!R || is(typeof((ref R r) => swap(r[0], r.front)))); } /// @safe unittest { static assert(!hasSwappableElements!(const int[])); static assert(!hasSwappableElements!(const(int)[])); static assert(!hasSwappableElements!(inout(int)[])); static assert( hasSwappableElements!(int[])); static assert(!hasSwappableElements!( string)); static assert(!hasSwappableElements!(dstring)); static assert(!hasSwappableElements!( char[])); static assert( hasSwappableElements!(dchar[])); } /** Returns `true` if `R` is an input range and has mutable elements. The following code should compile for any range with assignable elements. ---- R r; static assert(isInputRange!R); r.front = r.front; static if (isBidirectionalRange!R) r.back = r.front; static if (isRandomAccessRange!R) r[0] = r.front; ---- */ enum bool hasAssignableElements(R) = isInputRange!R && is(typeof(lvalueOf!R.front = lvalueOf!R.front)) && (!isBidirectionalRange!R || is(typeof(lvalueOf!R.back = lvalueOf!R.back))) && (!isRandomAccessRange!R || is(typeof(lvalueOf!R[0] = lvalueOf!R.front))); /// @safe unittest { static assert(!hasAssignableElements!(const int[])); static assert(!hasAssignableElements!(const(int)[])); static assert( hasAssignableElements!(int[])); static assert(!hasAssignableElements!(inout(int)[])); static assert(!hasAssignableElements!( string)); static assert(!hasAssignableElements!(dstring)); static assert(!hasAssignableElements!( char[])); static assert( hasAssignableElements!(dchar[])); } /** Tests whether the range `R` has lvalue elements. These are defined as elements that can be passed by reference and have their address taken. The following code should compile for any range with lvalue elements. ---- void passByRef(ref ElementType!R stuff); ... static assert(isInputRange!R); passByRef(r.front); static if (isBidirectionalRange!R) passByRef(r.back); static if (isRandomAccessRange!R) passByRef(r[0]); ---- */ enum bool hasLvalueElements(R) = isInputRange!R && is(typeof(isLvalue(lvalueOf!R.front))) && (!isBidirectionalRange!R || is(typeof(isLvalue(lvalueOf!R.back)))) && (!isRandomAccessRange!R || is(typeof(isLvalue(lvalueOf!R[0])))); /* Compile successfully if argument of type T is an lvalue */ private void isLvalue(T)(T) if (0); private void isLvalue(T)(ref T) if (1); /// @safe unittest { import std.range : iota, chain; static assert( hasLvalueElements!(int[])); static assert( hasLvalueElements!(const(int)[])); static assert( hasLvalueElements!(inout(int)[])); static assert( hasLvalueElements!(immutable(int)[])); static assert(!hasLvalueElements!(typeof(iota(3)))); static assert(!hasLvalueElements!( string)); static assert( hasLvalueElements!(dstring)); static assert(!hasLvalueElements!( char[])); static assert( hasLvalueElements!(dchar[])); auto c = chain([1, 2, 3], [4, 5, 6]); static assert( hasLvalueElements!(typeof(c))); } @safe unittest { // bugfix 6336 struct S { immutable int value; } static assert( isInputRange!(S[])); static assert( hasLvalueElements!(S[])); } /** Yields `true` if `R` has a `length` member that returns a value of `size_t` type. `R` does not have to be a range. If `R` is a range, algorithms in the standard library are only guaranteed to support `length` with type `size_t`. Note that `length` is an optional primitive as no range must implement it. Some ranges do not store their length explicitly, some cannot compute it without actually exhausting the range (e.g. socket streams), and some other ranges may be infinite. Although narrow string types (`char[]`, `wchar[]`, and their qualified derivatives) do define a `length` property, `hasLength` yields `false` for them. This is because a narrow string's length does not reflect the number of characters, but instead the number of encoding units, and as such is not useful with range-oriented algorithms. To use strings as random-access ranges with length, use $(REF representation, std, string) or $(REF byCodeUnit, std, utf). */ template hasLength(R) { static if (is(typeof(((R* r) => r.length)(null)) Length)) enum bool hasLength = is(Length == size_t) && !(isAutodecodableString!R && !isAggregateType!R); else enum bool hasLength = false; } /// @safe unittest { static assert(!hasLength!(char[])); static assert( hasLength!(int[])); static assert( hasLength!(inout(int)[])); struct A { size_t length() { return 0; } } struct B { @property size_t length() { return 0; } } static assert( hasLength!(A)); static assert( hasLength!(B)); } // test combinations which are invalid on some platforms @safe unittest { struct A { ulong length; } struct B { @property uint length() { return 0; } } static if (is(size_t == uint)) { static assert(!hasLength!(A)); static assert(hasLength!(B)); } else static if (is(size_t == ulong)) { static assert(hasLength!(A)); static assert(!hasLength!(B)); } } // test combinations which are invalid on all platforms @safe unittest { struct A { long length; } struct B { int length; } struct C { ubyte length; } struct D { char length; } static assert(!hasLength!(A)); static assert(!hasLength!(B)); static assert(!hasLength!(C)); static assert(!hasLength!(D)); } /** Returns `true` if `R` is an infinite input range. An infinite input range is an input range that has a statically-defined enumerated member called `empty` that is always `false`, for example: ---- struct MyInfiniteRange { enum bool empty = false; ... } ---- */ template isInfinite(R) { static if (isInputRange!R && __traits(compiles, { enum e = R.empty; })) enum bool isInfinite = !R.empty; else enum bool isInfinite = false; } /// @safe unittest { import std.range : Repeat; static assert(!isInfinite!(int[])); static assert( isInfinite!(Repeat!(int))); } /** Returns `true` if `R` offers a slicing operator with integral boundaries that returns a forward range type. For finite ranges, the result of `opSlice` must be of the same type as the original range type. If the range defines `opDollar`, then it must support subtraction. For infinite ranges, when $(I not) using `opDollar`, the result of `opSlice` may be a forward range of any type. However, when using `opDollar`, the result of `opSlice` must be of the same type as the original range type. The following expression must be true for `hasSlicing` to be `true`: ---- isForwardRange!R && !(isAutodecodableString!R && !isAggregateType!R) && is(typeof((R r) { return r[1 .. 1].length; } (R.init)) == size_t) && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R) && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R)) && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R || is(typeof(lvalueOf!R[0 .. $ - 1]) == R)) && is(typeof((ref R r) { static assert(isForwardRange!(typeof(r[1 .. 2]))); })); ---- */ enum bool hasSlicing(R) = isForwardRange!R && !(isAutodecodableString!R && !isAggregateType!R) && is(typeof((R r) { return r[1 .. 1].length; } (R.init)) == size_t) && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R) && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R)) && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R || is(typeof(lvalueOf!R[0 .. $ - 1]) == R)) && is(typeof((ref R r) { static assert(isForwardRange!(typeof(r[1 .. 2]))); })); /// @safe unittest { import std.range : takeExactly; static assert( hasSlicing!(int[])); static assert( hasSlicing!(const(int)[])); static assert(!hasSlicing!(const int[])); static assert( hasSlicing!(inout(int)[])); static assert(!hasSlicing!(inout int [])); static assert( hasSlicing!(immutable(int)[])); static assert(!hasSlicing!(immutable int[])); static assert(!hasSlicing!string); static assert( hasSlicing!dstring); enum rangeFuncs = "@property int front();" ~ "void popFront();" ~ "@property bool empty();" ~ "@property auto save() { return this; }" ~ "@property size_t length();"; struct A { mixin(rangeFuncs); int opSlice(size_t, size_t); } struct B { mixin(rangeFuncs); B opSlice(size_t, size_t); } struct C { mixin(rangeFuncs); @disable this(); C opSlice(size_t, size_t); } struct D { mixin(rangeFuncs); int[] opSlice(size_t, size_t); } static assert(!hasSlicing!(A)); static assert( hasSlicing!(B)); static assert( hasSlicing!(C)); static assert(!hasSlicing!(D)); struct InfOnes { enum empty = false; void popFront() {} @property int front() { return 1; } @property InfOnes save() { return this; } auto opSlice(size_t i, size_t j) { return takeExactly(this, j - i); } auto opSlice(size_t i, Dollar d) { return this; } struct Dollar {} Dollar opDollar() const { return Dollar.init; } } static assert(hasSlicing!InfOnes); } // https://issues.dlang.org/show_bug.cgi?id=24348 @safe unittest { static struct Slice { size_t length; bool empty() => length == 0; int front() => 0; void popFront() { --length; } Slice save() => this; } static struct InfZeros { enum empty = false; int front() => 0; void popFront() {} InfZeros save() => this; Slice opIndex(size_t[2] bounds) { size_t i = bounds[0], j = bounds[1]; size_t length = i <= j ? j - i : 0; return Slice(length); } size_t[2] opSlice(size_t dim : 0)(size_t i, size_t j) => [i, j]; } static assert(hasSlicing!InfZeros); } /** This is a best-effort implementation of `length` for any kind of range. If `hasLength!Range`, simply returns `range.length` without checking `upTo` (when specified). Otherwise, walks the range through its length and returns the number of elements seen. Performes $(BIGOH n) evaluations of `range.empty` and `range.popFront()`, where `n` is the effective length of $(D range). The `upTo` parameter is useful to "cut the losses" in case the interest is in seeing whether the range has at least some number of elements. If the parameter `upTo` is specified, stops if $(D upTo) steps have been taken and returns `upTo`. Infinite ranges are compatible, provided the parameter `upTo` is specified, in which case the implementation simply returns upTo. */ auto walkLength(Range)(Range range) if (isInputRange!Range && !isInfinite!Range) { static if (hasLength!Range) return range.length; else { size_t result; static if (autodecodeStrings && isNarrowString!Range) { import std.utf : codeUnitLimit; result = range.length; foreach (const i, const c; range) { if (c >= codeUnitLimit!Range) { result = i; break; } } range = range[result .. $]; } for ( ; !range.empty ; range.popFront() ) ++result; return result; } } /// ditto auto walkLength(Range)(Range range, const size_t upTo) if (isInputRange!Range) { static if (hasLength!Range) return range.length; else static if (isInfinite!Range) return upTo; else { size_t result; static if (autodecodeStrings && isNarrowString!Range) { import std.utf : codeUnitLimit; result = upTo > range.length ? range.length : upTo; foreach (const i, const c; range[0 .. result]) { if (c >= codeUnitLimit!Range) { result = i; break; } } range = range[result .. $]; } for ( ; result < upTo && !range.empty ; range.popFront() ) ++result; return result; } } /// @safe unittest { import std.range : iota; assert(10.iota.walkLength == 10); // iota has a length function, and therefore the // doesn't have to be walked, and the upTo // parameter is ignored assert(10.iota.walkLength(5) == 10); } @safe unittest { import std.algorithm.iteration : filter; import std.range : recurrence, take; //hasLength Range int[] a = [ 1, 2, 3 ]; assert(walkLength(a) == 3); assert(walkLength(a, 0) == 3); assert(walkLength(a, 2) == 3); assert(walkLength(a, 4) == 3); //Forward Range auto b = filter!"true"([1, 2, 3, 4]); assert(b.walkLength() == 4); assert(b.walkLength(0) == 0); assert(b.walkLength(2) == 2); assert(b.walkLength(4) == 4); assert(b.walkLength(6) == 4); //Infinite Range auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); assert(!__traits(compiles, fibs.walkLength())); assert(fibs.take(10).walkLength() == 10); assert(fibs.walkLength(55) == 55); } /** `popFrontN` eagerly advances `r` itself (not a copy) up to `n` times (by calling `r.popFront`). `popFrontN` takes `r` by `ref`, so it mutates the original range. Completes in $(BIGOH 1) steps for ranges that support slicing and have length. Completes in $(BIGOH n) time for all other ranges. `popBackN` behaves the same as `popFrontN` but instead removes elements from the back of the (bidirectional) range instead of the front. Returns: How much `r` was actually advanced, which may be less than `n` if `r` did not have at least `n` elements. See_Also: $(REF drop, std, range), $(REF dropBack, std, range) */ size_t popFrontN(Range)(ref Range r, size_t n) if (isInputRange!Range) { static if (hasLength!Range) { n = cast(size_t) (n < r.length ? n : r.length); } static if (hasSlicing!Range && is(typeof(r = r[n .. $]))) { r = r[n .. $]; } else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. { r = r[n .. r.length]; } else { static if (hasLength!Range) { foreach (i; 0 .. n) r.popFront(); } else { foreach (i; 0 .. n) { if (r.empty) return i; r.popFront(); } } } return n; } /// ditto size_t popBackN(Range)(ref Range r, size_t n) if (isBidirectionalRange!Range) { static if (hasLength!Range) { n = cast(size_t) (n < r.length ? n : r.length); } static if (hasSlicing!Range && is(typeof(r = r[0 .. $ - n]))) { r = r[0 .. $ - n]; } else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. { r = r[0 .. r.length - n]; } else { static if (hasLength!Range) { foreach (i; 0 .. n) r.popBack(); } else { foreach (i; 0 .. n) { if (r.empty) return i; r.popBack(); } } } return n; } /// @safe unittest { int[] a = [ 1, 2, 3, 4, 5 ]; a.popFrontN(2); assert(a == [ 3, 4, 5 ]); a.popFrontN(7); assert(a == [ ]); } /// @safe unittest { import std.algorithm.comparison : equal; import std.range : iota; auto LL = iota(1L, 7L); auto r = popFrontN(LL, 2); assert(equal(LL, [3L, 4L, 5L, 6L])); assert(r == 2); } /// @safe unittest { int[] a = [ 1, 2, 3, 4, 5 ]; a.popBackN(2); assert(a == [ 1, 2, 3 ]); a.popBackN(7); assert(a == [ ]); } /// @safe unittest { import std.algorithm.comparison : equal; import std.range : iota; auto LL = iota(1L, 7L); auto r = popBackN(LL, 2); assert(equal(LL, [1L, 2L, 3L, 4L])); assert(r == 2); } /** Eagerly advances `r` itself (not a copy) exactly `n` times (by calling `r.popFront`). `popFrontExactly` takes `r` by `ref`, so it mutates the original range. Completes in $(BIGOH 1) steps for ranges that support slicing, and have either length or are infinite. Completes in $(BIGOH n) time for all other ranges. Note: Unlike $(LREF popFrontN), `popFrontExactly` will assume that the range holds at least `n` elements. This makes `popFrontExactly` faster than `popFrontN`, but it also means that if `range` does not contain at least `n` elements, it will attempt to call `popFront` on an empty range, which is undefined behavior. So, only use `popFrontExactly` when it is guaranteed that `range` holds at least `n` elements. `popBackExactly` will behave the same but instead removes elements from the back of the (bidirectional) range instead of the front. See_Also: $(REF dropExactly, std, range), $(REF dropBackExactly, std, range) */ void popFrontExactly(Range)(ref Range r, size_t n) if (isInputRange!Range) { static if (hasLength!Range) assert(n <= r.length, "range is smaller than amount of items to pop"); static if (hasSlicing!Range && is(typeof(r = r[n .. $]))) r = r[n .. $]; else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. r = r[n .. r.length]; else foreach (i; 0 .. n) r.popFront(); } /// ditto void popBackExactly(Range)(ref Range r, size_t n) if (isBidirectionalRange!Range) { static if (hasLength!Range) assert(n <= r.length, "range is smaller than amount of items to pop"); static if (hasSlicing!Range && is(typeof(r = r[0 .. $ - n]))) r = r[0 .. $ - n]; else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar. r = r[0 .. r.length - n]; else foreach (i; 0 .. n) r.popBack(); } /// @safe unittest { import std.algorithm.comparison : equal; import std.algorithm.iteration : filterBidirectional; auto a = [1, 2, 3]; a.popFrontExactly(1); assert(a == [2, 3]); a.popBackExactly(1); assert(a == [2]); string s = "日本語"; s.popFrontExactly(1); assert(s == "本語"); s.popBackExactly(1); assert(s == "本"); auto bd = filterBidirectional!"true"([1, 2, 3]); bd.popFrontExactly(1); assert(bd.equal([2, 3])); bd.popBackExactly(1); assert(bd.equal([2])); } /** Moves the front of `r` out and returns it. If `r.front` is a struct with a destructor or copy constructor defined, it is reset to its `.init` value after its value is moved. Otherwise, it is left unchanged. In either case, `r.front` is left in a destroyable state that does not allocate any resources. */ ElementType!R moveFront(R)(R r) { static if (is(typeof(&r.moveFront))) { return r.moveFront(); } else static if (!hasElaborateCopyConstructor!(ElementType!R)) { return r.front; } else static if (is(typeof(&(r.front())) == ElementType!R*)) { import std.algorithm.mutation : move; return move(r.front); } else { static assert(0, "Cannot move front of a range with a postblit and an rvalue front."); } } /// @safe unittest { auto a = [ 1, 2, 3 ]; assert(moveFront(a) == 1); assert(a.length == 3); // define a perfunctory input range struct InputRange { enum bool empty = false; enum int front = 7; void popFront() {} int moveFront() { return 43; } } InputRange r; // calls r.moveFront assert(moveFront(r) == 43); } @safe unittest { struct R { @property ref int front() { static int x = 42; return x; } this(this){} } R r; assert(moveFront(r) == 42); } /** Moves the back of `r` out and returns it. Leaves `r.back` in a destroyable state that does not allocate any resources (usually equal to its `.init` value). */ ElementType!R moveBack(R)(R r) { static if (is(typeof(&r.moveBack))) { return r.moveBack(); } else static if (!hasElaborateCopyConstructor!(ElementType!R)) { return r.back; } else static if (is(typeof(&(r.back())) == ElementType!R*)) { import std.algorithm.mutation : move; return move(r.back); } else { static assert(0, "Cannot move back of a range with a postblit and an rvalue back."); } } /// @safe unittest { struct TestRange { int payload = 5; @property bool empty() { return false; } @property TestRange save() { return this; } @property ref int front() return { return payload; } @property ref int back() return { return payload; } void popFront() { } void popBack() { } } static assert(isBidirectionalRange!TestRange); TestRange r; auto x = moveBack(r); assert(x == 5); } /** Moves element at index `i` of `r` out and returns it. Leaves $(D r[i]) in a destroyable state that does not allocate any resources (usually equal to its `.init` value). */ ElementType!R moveAt(R)(R r, size_t i) { static if (is(typeof(&r.moveAt))) { return r.moveAt(i); } else static if (!hasElaborateCopyConstructor!(ElementType!(R))) { return r[i]; } else static if (is(typeof(&r[i]) == ElementType!R*)) { import std.algorithm.mutation : move; return move(r[i]); } else { static assert(0, "Cannot move element of a range with a postblit and rvalue elements."); } } /// @safe unittest { auto a = [1,2,3,4]; foreach (idx, it; a) { assert(it == moveAt(a, idx)); } } @safe unittest { import std.internal.test.dummyrange; foreach (DummyType; AllDummyRanges) { auto d = DummyType.init; assert(moveFront(d) == 1); static if (isBidirectionalRange!DummyType) { assert(moveBack(d) == 10); } static if (isRandomAccessRange!DummyType) { assert(moveAt(d, 2) == 3); } } } /** Implements the range interface primitive `empty` for types that obey $(LREF hasLength) property and for narrow strings. Due to the fact that nonmember functions can be called with the first argument using the dot notation, `a.empty` is equivalent to `empty(a)`. */ @property bool empty(T)(auto ref scope T a) if (is(typeof(a.length) : size_t)) { return !a.length; } /// @safe pure nothrow unittest { auto a = [ 1, 2, 3 ]; assert(!a.empty); assert(a[3 .. $].empty); int[string] b; assert(b.empty); b["zero"] = 0; assert(!b.empty); } /** Implements the range interface primitive `save` for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, `array.save` is equivalent to `save(array)`. The function does not duplicate the content of the array, it simply returns its argument. */ @property inout(T)[] save(T)(return scope inout(T)[] a) @safe pure nothrow @nogc { return a; } /// @safe pure nothrow unittest { auto a = [ 1, 2, 3 ]; auto b = a.save; assert(b is a); } /** Implements the range interface primitive `popFront` for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, `array.popFront` is equivalent to `popFront(array)`. For $(GLOSSARY narrow strings), `popFront` automatically advances to the next $(GLOSSARY code point). */ void popFront(T)(scope ref inout(T)[] a) @safe pure nothrow @nogc if (!isAutodecodableString!(T[]) && !is(T[] == void[])) { assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof); a = a[1 .. $]; } /// @safe pure nothrow unittest { auto a = [ 1, 2, 3 ]; a.popFront(); assert(a == [ 2, 3 ]); } @safe unittest { static assert(!is(typeof({ int[4] a; popFront(a); }))); static assert(!is(typeof({ immutable int[] a; popFront(a); }))); static assert(!is(typeof({ void[] a; popFront(a); }))); } /// ditto void popFront(C)(scope ref inout(C)[] str) @trusted pure nothrow if (isAutodecodableString!(C[])) { import std.algorithm.comparison : min; assert(str.length, "Attempting to popFront() past the end of an array of " ~ C.stringof); static if (is(immutable C == immutable char)) { static immutable ubyte[] charWidthTab = [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 ]; immutable c = str[0]; immutable charWidth = c < 192 ? 1 : charWidthTab.ptr[c - 192]; str = str.ptr[min(str.length, charWidth) .. str.length]; } else static if (is(immutable C == immutable wchar)) { immutable u = str[0]; immutable seqLen = 1 + (u >= 0xD800 && u <= 0xDBFF); str = str.ptr[min(seqLen, str.length) .. str.length]; } else static assert(0, "Bad template constraint."); } @safe pure unittest { import std.meta : AliasSeq; static foreach (S; AliasSeq!(string, wstring, dstring)) {{ S s = "\xC2\xA9hello"; s.popFront(); assert(s == "hello"); S str = "hello\U00010143\u0100\U00010143"; foreach (dchar c; ['h', 'e', 'l', 'l', 'o', '\U00010143', '\u0100', '\U00010143']) { assert(str.front == c); str.popFront(); } assert(str.empty); static assert(!is(typeof({ immutable S a; popFront(a); }))); static assert(!is(typeof({ typeof(S.init[0])[4] a; popFront(a); }))); }} C[] _eatString(C)(C[] str) { while (!str.empty) str.popFront(); return str; } enum checkCTFE = _eatString("ウェブサイト@La_Verité.com"); static assert(checkCTFE.empty); enum checkCTFEW = _eatString("ウェブサイト@La_Verité.com"w); static assert(checkCTFEW.empty); } // https://issues.dlang.org/show_bug.cgi?id=16090 @safe unittest { string s = "\u00E4"; assert(s.length == 2); s = s[0 .. 1]; assert(s.length == 1); s.popFront; assert(s.empty); } @safe unittest { wstring s = "\U00010000"; assert(s.length == 2); s = s[0 .. 1]; assert(s.length == 1); s.popFront; assert(s.empty); } /** Implements the range interface primitive `popBack` for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, `array.popBack` is equivalent to `popBack(array)`. For $(GLOSSARY narrow strings), $(D popFront) automatically eliminates the last $(GLOSSARY code point). */ void popBack(T)(scope ref inout(T)[] a) @safe pure nothrow @nogc if (!isAutodecodableString!(T[]) && !is(T[] == void[])) { assert(a.length); a = a[0 .. $ - 1]; } /// @safe pure nothrow unittest { auto a = [ 1, 2, 3 ]; a.popBack(); assert(a == [ 1, 2 ]); } @safe unittest { static assert(!is(typeof({ immutable int[] a; popBack(a); }))); static assert(!is(typeof({ int[4] a; popBack(a); }))); static assert(!is(typeof({ void[] a; popBack(a); }))); } /// ditto void popBack(T)(scope ref inout(T)[] a) @safe pure if (isAutodecodableString!(T[])) { import std.utf : strideBack; assert(a.length, "Attempting to popBack() past the front of an array of " ~ T.stringof); a = a[0 .. $ - strideBack(a, $)]; } @safe pure unittest { import std.meta : AliasSeq; static foreach (S; AliasSeq!(string, wstring, dstring)) {{ S s = "hello\xE2\x89\xA0"; s.popBack(); assert(s == "hello"); S s3 = "\xE2\x89\xA0"; auto c = s3.back; assert(c == cast(dchar)'\u2260'); s3.popBack(); assert(s3 == ""); S str = "\U00010143\u0100\U00010143hello"; foreach (dchar ch; ['o', 'l', 'l', 'e', 'h', '\U00010143', '\u0100', '\U00010143']) { assert(str.back == ch); str.popBack(); } assert(str.empty); static assert(!is(typeof({ immutable S a; popBack(a); }))); static assert(!is(typeof({ typeof(S.init[0])[4] a; popBack(a); }))); }} } /** EXPERIMENTAL: to try out removing autodecoding, set the version `NoAutodecodeStrings`. Most things are expected to fail with this version currently. */ version (NoAutodecodeStrings) { enum autodecodeStrings = false; } else { /// enum autodecodeStrings = true; } /** Implements the range interface primitive `front` for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, `array.front` is equivalent to `front(array)`. For $(GLOSSARY narrow strings), $(D front) automatically returns the first $(GLOSSARY code point) as _a $(D dchar). */ @property ref inout(T) front(T)(return scope inout(T)[] a) @safe pure nothrow @nogc if (!isAutodecodableString!(T[]) && !is(T[] == void[])) { assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); return a[0]; } /// @safe pure nothrow unittest { int[] a = [ 1, 2, 3 ]; assert(a.front == 1); } @safe pure nothrow unittest { auto a = [ 1, 2 ]; a.front = 4; assert(a.front == 4); assert(a == [ 4, 2 ]); immutable b = [ 1, 2 ]; assert(b.front == 1); int[2] c = [ 1, 2 ]; assert(c.front == 1); } /// ditto @property dchar front(T)(scope const(T)[] a) @safe pure if (isAutodecodableString!(T[])) { import std.utf : decode; assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); size_t i = 0; return decode(a, i); } /** Implements the range interface primitive `back` for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, `array.back` is equivalent to `back(array)`. For $(GLOSSARY narrow strings), $(D back) automatically returns the last $(GLOSSARY code point) as _a $(D dchar). */ @property ref inout(T) back(T)(return scope inout(T)[] a) @safe pure nothrow @nogc if (!isAutodecodableString!(T[]) && !is(T[] == void[])) { assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof); return a[$ - 1]; } /// @safe pure nothrow unittest { int[] a = [ 1, 2, 3 ]; assert(a.back == 3); a.back += 4; assert(a.back == 7); } @safe pure nothrow unittest { immutable b = [ 1, 2, 3 ]; assert(b.back == 3); int[3] c = [ 1, 2, 3 ]; assert(c.back == 3); } /// ditto // Specialization for strings @property dchar back(T)(scope const(T)[] a) @safe pure if (isAutodecodableString!(T[])) { import std.utf : decode, strideBack; assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof); size_t i = a.length - strideBack(a, a.length); return decode(a, i); } /* Implements `length` for a range by forwarding it to `member`. */ package(std) mixin template ImplementLength(alias member) { static if (hasLength!(typeof(member))) { @property auto length() { return member.length; } alias opDollar = length; } } @safe unittest { import std.meta : AliasSeq; foreach (alias E; AliasSeq!(noreturn, const(noreturn), immutable(noreturn) )) { alias R = E[]; static assert(isInputRange!R); static assert(isForwardRange!R); static assert(isBidirectionalRange!R); static assert(isRandomAccessRange!R); } static assert(isOutputRange!(noreturn[], noreturn)); }