Add range.primitive
This commit is contained in:
		| @@ -19,11 +19,11 @@ import core.exception; | ||||
| import std.algorithm.comparison; | ||||
| import std.algorithm.mutation; | ||||
| import std.conv; | ||||
| import std.range.primitives; | ||||
| import std.meta; | ||||
| import tanya.memory; | ||||
| import tanya.meta.trait; | ||||
| import tanya.meta.transform; | ||||
| import tanya.range.primitive; | ||||
|  | ||||
| /** | ||||
|  * Random-access range for the $(D_PSYMBOL Array). | ||||
|   | ||||
| @@ -17,10 +17,11 @@ module tanya.container.list; | ||||
| import std.algorithm.comparison; | ||||
| import std.algorithm.mutation; | ||||
| import std.algorithm.searching; | ||||
| import std.range.primitives; | ||||
| import tanya.container.entry; | ||||
| import tanya.memory; | ||||
| import tanya.meta.trait; | ||||
| import tanya.range.array; | ||||
| import tanya.range.primitive; | ||||
|  | ||||
| /** | ||||
|  * Forward range for the $(D_PSYMBOL SList). | ||||
|   | ||||
| @@ -702,7 +702,7 @@ private @nogc unittest | ||||
| // Static checks. | ||||
| private unittest | ||||
| { | ||||
|     import std.range.primitives; | ||||
|     import tanya.range.primitive; | ||||
|  | ||||
|     static assert(isBidirectionalRange!(Set!int.ConstRange)); | ||||
|     static assert(isBidirectionalRange!(Set!int.Range)); | ||||
|   | ||||
| @@ -18,9 +18,9 @@ import core.exception; | ||||
| import std.algorithm.iteration; | ||||
| import std.algorithm.mutation; | ||||
| import std.conv; | ||||
| import std.range; | ||||
| public import tanya.memory.allocator; | ||||
| import tanya.memory.mmappool; | ||||
| import tanya.range.primitive; | ||||
| import tanya.meta.trait; | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -21,9 +21,9 @@ import core.exception; | ||||
| import std.algorithm.comparison; | ||||
| import std.algorithm.mutation; | ||||
| import std.conv; | ||||
| import std.range; | ||||
| import tanya.memory; | ||||
| import tanya.meta.trait; | ||||
| import tanya.range.primitive; | ||||
|  | ||||
| private template Payload(T) | ||||
| { | ||||
|   | ||||
| @@ -15,9 +15,9 @@ | ||||
| module tanya.net.inet; | ||||
|  | ||||
| import std.math; | ||||
| import std.range.primitives; | ||||
| import tanya.meta.trait; | ||||
| import tanya.meta.transform; | ||||
| import tanya.range.primitive; | ||||
|  | ||||
| /** | ||||
|  * Represents an unsigned integer as an $(D_KEYWORD ubyte) range. | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||
|  | ||||
| /** | ||||
|  * This package contains generic function and templates to be used with D | ||||
|  * This package contains generic functions and templates to be used with D | ||||
|  * ranges. | ||||
|  * | ||||
|  * Copyright: Eugene Wissner 2017. | ||||
| @@ -16,3 +16,4 @@ | ||||
| module tanya.range; | ||||
|  | ||||
| public import tanya.range.array; | ||||
| public import tanya.range.primitive; | ||||
|   | ||||
							
								
								
									
										908
									
								
								source/tanya/range/primitive.d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										908
									
								
								source/tanya/range/primitive.d
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,908 @@ | ||||
| /* This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||
|  | ||||
| /** | ||||
|  * This module defines primitives for working with ranges. | ||||
|  * | ||||
|  * Copyright: Eugene Wissner 2017. | ||||
|  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, | ||||
|  *                  Mozilla Public License, v. 2.0). | ||||
|  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) | ||||
|  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/primitive.d, | ||||
|  *                 tanya/range/primitive.d) | ||||
|  */ | ||||
| module tanya.range.primitive; | ||||
|  | ||||
| import tanya.meta.trait; | ||||
|  | ||||
| /** | ||||
|  * Returns the element type of the range $(D_PARAM R). | ||||
|  * | ||||
|  * Element type is the return type of such primitives like | ||||
|  * $(D_INLINECODE R.front) and (D_INLINECODE R.back) or the array base type. | ||||
|  * | ||||
|  * If $(D_PARAM R) is a string, $(D_PSYMBOL ElementType) doesn't distinguish | ||||
|  * between narrow and wide strings, it just returns the base type of the | ||||
|  * underlying array ($(D_KEYWORD char), $(D_KEYWORD wchar) or | ||||
|  * $(D_KEYWORD dchar)). | ||||
|  * | ||||
|  * Params: | ||||
|  *  R = Any range type. | ||||
|  * | ||||
|  * Returns: Element type of the range $(D_PARAM R). | ||||
|  */ | ||||
| template ElementType(R) | ||||
| if (isInputRange!R) | ||||
| { | ||||
|     static if (is(R U : U[])) | ||||
|     { | ||||
|         alias ElementType = U; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         alias ElementType = ReturnType!((R r) => r.front()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Detects whether $(D_PARAM R) has a length property. | ||||
|  * | ||||
|  * $(D_PARAM R) does not have to be a range to support the length. | ||||
|  * | ||||
|  * Length mustn't be a $(D_KEYWORD @property) or a function, it can be a member | ||||
|  * variable or $(D_KEYWORD enum). But its type (or the type returned by the | ||||
|  * appropriate function) should be $(D_KEYWORD size_t), otherwise | ||||
|  * $(D_PSYMBOL hasLength) is $(D_KEYWORD false). | ||||
|  * | ||||
|  * All dynamic arrays except $(D_KEYWORD void)-arrays have length. | ||||
|  * | ||||
|  * Params: | ||||
|  *  R = A type. | ||||
|  * | ||||
|  * Returns: $(D_KEYWORD true) if $(D_PARAM R) has a length property, | ||||
|  *          $(D_KEYWORD false) otherwise. | ||||
|  * | ||||
|  * See_Also: $(D_PSYMBOL isInfinite). | ||||
|  */ | ||||
| template hasLength(R) | ||||
| { | ||||
|     enum bool hasLength = is(ReturnType!((R r) => r.length) == size_t) | ||||
|                        && !is(ElementType!R == void); | ||||
| } | ||||
|  | ||||
| /// | ||||
| pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static assert(hasLength!(char[])); | ||||
|     static assert(hasLength!(int[])); | ||||
|     static assert(hasLength!(const(int)[])); | ||||
|  | ||||
|     struct A | ||||
|     { | ||||
|         enum size_t length = 1; | ||||
|     } | ||||
|     static assert(hasLength!(A)); | ||||
|  | ||||
|     struct B | ||||
|     { | ||||
|         @property size_t length() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     static assert(hasLength!(B)); | ||||
|  | ||||
|     struct C | ||||
|     { | ||||
|         @property const(size_t) length() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     static assert(!hasLength!(C)); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Determines whether $(D_PARAM R) is a forward range with slicing support | ||||
|  * ($(D_INLINECODE R[i .. j])). | ||||
|  * | ||||
|  * For finite ranges, the result of `opSlice()` must be of the same type as the | ||||
|  * original range. If the range defines opDollar, it must support subtraction. | ||||
|  * | ||||
|  * For infinite ranges, the result of `opSlice()` must be of the same type as | ||||
|  * the original range only if it defines `opDollar()`. Otherwise it can be any | ||||
|  * forward range. | ||||
|  * | ||||
|  * For both finite and infinite ranges, the result of `opSlice()` must have | ||||
|  * length. | ||||
|  * | ||||
|  * Params: | ||||
|  *  R = The type to be tested. | ||||
|  * | ||||
|  * Returns: $(D_KEYWORD true) if $(D_PARAM R) supports slicing, | ||||
|  *          $(D_KEYWORD false) otherwise. | ||||
|  */ | ||||
| template hasSlicing(R) | ||||
| { | ||||
|     private enum bool hasDollar = is(typeof((R r) => r[0 .. $])); | ||||
|     private enum bool subDollar = !hasDollar | ||||
|                                || isInfinite!R | ||||
|                                || is(ReturnType!((R r) => r[0 .. $ - 1]) == R); | ||||
|  | ||||
|     static if (isForwardRange!R | ||||
|             && is(ReturnType!((R r) => r[0 .. 0]) T) | ||||
|             && (!hasDollar || is(ReturnType!((R r) => r[0 .. $]) == R)) | ||||
|             && subDollar | ||||
|             && isForwardRange!(ReturnType!((ref R r) => r[0 .. 0]))) | ||||
|     { | ||||
|         enum bool hasSlicing = (is(T == R) || isInfinite!R) | ||||
|                             && hasLength!T; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         enum bool hasSlicing = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// | ||||
| pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static assert(hasSlicing!(int[])); | ||||
|     static assert(hasSlicing!(const(int)[])); | ||||
|     static assert(hasSlicing!(dstring)); | ||||
|     static assert(hasSlicing!(string)); | ||||
|     static assert(!hasSlicing!(const int[])); | ||||
|     static assert(!hasSlicing!(void[])); | ||||
|  | ||||
|     struct A | ||||
|     { | ||||
|         int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         @property size_t length() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         typeof(this) opSlice(const size_t i, const size_t j) | ||||
|         pure nothrow  @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|     static assert(hasSlicing!A); | ||||
|  | ||||
|     struct B | ||||
|     { | ||||
|         struct Dollar | ||||
|         { | ||||
|         } | ||||
|         int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         @property size_t length() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         @property Dollar opDollar() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return Dollar(); | ||||
|         } | ||||
|         typeof(this) opSlice(const size_t i, const Dollar j) | ||||
|         pure nothrow  @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|     static assert(!hasSlicing!B); | ||||
|  | ||||
|     struct C | ||||
|     { | ||||
|         int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         enum bool empty = false; | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         typeof(this) opSlice(const size_t i, const size_t j) | ||||
|         pure nothrow  @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|     static assert(!hasSlicing!C); | ||||
|  | ||||
|     struct D | ||||
|     { | ||||
|         struct Range | ||||
|         { | ||||
|             int front() pure nothrow @safe @nogc | ||||
|             { | ||||
|                 return 0; | ||||
|             } | ||||
|             void popFront() pure nothrow @safe @nogc | ||||
|             { | ||||
|             } | ||||
|             bool empty() const pure nothrow @safe @nogc | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|             typeof(this) save() pure nothrow @safe @nogc | ||||
|             { | ||||
|                 return this; | ||||
|             } | ||||
|             @property size_t length() const pure nothrow @safe @nogc | ||||
|             { | ||||
|                 return 0; | ||||
|             } | ||||
|         } | ||||
|         int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         enum bool empty = false; | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         Range opSlice(const size_t i, const size_t j) | ||||
|         pure nothrow  @safe @nogc | ||||
|         { | ||||
|             return Range(); | ||||
|         } | ||||
|     } | ||||
|     static assert(hasSlicing!D); | ||||
| } | ||||
|  | ||||
| version (TanyaPhobos) | ||||
| { | ||||
|     public import std.range.primitives : isInputRange, | ||||
|                                          isForwardRange, | ||||
|                                          isBidirectionalRange, | ||||
|                                          isRandomAccessRange, | ||||
|                                          isInfinite; | ||||
| } | ||||
| else: | ||||
|  | ||||
| import tanya.meta.transform; | ||||
|  | ||||
| version (unittest) | ||||
| { | ||||
|     mixin template InputRangeStub() | ||||
|     { | ||||
|         @property int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         @property bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|     } | ||||
|     mixin template BidirectionalRangeStub() | ||||
|     { | ||||
|         @property int back() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         void popBack() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| private template isDynamicArrayRange(R) | ||||
| { | ||||
|     static if (is(R E : E[])) | ||||
|     { | ||||
|         enum bool isDynamicArrayRange = !is(E == void); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         enum bool isDynamicArrayRange = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Determines whether $(D_PARAM R) is an input range. | ||||
|  * | ||||
|  * An input range should define following primitives: | ||||
|  * | ||||
|  * $(UL | ||||
|  *  $(LI front) | ||||
|  *  $(LI empty) | ||||
|  *  $(LI popFront) | ||||
|  * ) | ||||
|  * | ||||
|  * Params: | ||||
|  *  R = The type to be tested. | ||||
|  * | ||||
|  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an input range, | ||||
|  *          $(D_KEYWORD false) otherwise. | ||||
|  */ | ||||
| template isInputRange(R) | ||||
| { | ||||
|     static if (is(ReturnType!((R r) => r.front()) U) | ||||
|             && is(ReturnType!((R r) => r.empty) == bool)) | ||||
|     { | ||||
|         enum bool isInputRange = !is(U == void) | ||||
|                               && is(typeof(R.popFront())); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         enum bool isInputRange = isDynamicArrayRange!R; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// | ||||
| pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static struct Range | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     static assert(isInputRange!Range); | ||||
|     static assert(isInputRange!(int[])); | ||||
|     static assert(!isInputRange!(void[])); | ||||
| } | ||||
|  | ||||
| private pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static struct Range1(T) | ||||
|     { | ||||
|         void popFront() | ||||
|         { | ||||
|         } | ||||
|         int front() | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         T empty() const | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     static assert(!isInputRange!(Range1!int)); | ||||
|     static assert(!isInputRange!(Range1!(const bool))); | ||||
|  | ||||
|     static struct Range2 | ||||
|     { | ||||
|         int popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 100; | ||||
|         } | ||||
|         int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 100; | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     static assert(isInputRange!Range2); | ||||
|  | ||||
|     static struct Range3 | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         void front() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     static assert(!isInputRange!Range3); | ||||
|  | ||||
|     static struct Range4 | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         enum bool empty = false; | ||||
|     } | ||||
|     static assert(isInputRange!Range4); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Determines whether $(D_PARAM R) is a forward range. | ||||
|  * | ||||
|  * A forward range is an input range that also defines: | ||||
|  * | ||||
|  * $(UL | ||||
|  *  $(LI save) | ||||
|  * ) | ||||
|  * | ||||
|  * Params: | ||||
|  *  R = The type to be tested. | ||||
|  * | ||||
|  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a forward range, | ||||
|  *          $(D_KEYWORD false) otherwise. | ||||
|  * | ||||
|  * See_Also: $(D_PSYMBOL isInputRange). | ||||
|  */ | ||||
| template isForwardRange(R) | ||||
| { | ||||
|     static if (is(ReturnType!((R r) => r.save()) U)) | ||||
|     { | ||||
|         enum bool isForwardRange = isInputRange!R && is(U == R); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         enum bool isForwardRange = isDynamicArrayRange!R; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// | ||||
| pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static struct Range | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|     static assert(isForwardRange!Range); | ||||
|     static assert(isForwardRange!(int[])); | ||||
|     static assert(!isForwardRange!(void[])); | ||||
| } | ||||
|  | ||||
| private pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static struct Range1 | ||||
|     { | ||||
|     } | ||||
|     static struct Range2 | ||||
|     { | ||||
|         mixin InputRangeStub; | ||||
|         Range1 save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return Range1(); | ||||
|         } | ||||
|     } | ||||
|     static assert(!isForwardRange!Range2); | ||||
|  | ||||
|     static struct Range3 | ||||
|     { | ||||
|         mixin InputRangeStub; | ||||
|         const(typeof(this)) save() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|     static assert(!isForwardRange!Range3); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Determines whether $(D_PARAM R) is a bidirectional range. | ||||
|  * | ||||
|  * A bidirectional range is a forward range that also defines: | ||||
|  * | ||||
|  * $(UL | ||||
|  *  $(LI back) | ||||
|  *  $(LI popBack) | ||||
|  * ) | ||||
|  * | ||||
|  * Params: | ||||
|  *  R = The type to be tested. | ||||
|  * | ||||
|  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a bidirectional range, | ||||
|  *          $(D_KEYWORD false) otherwise. | ||||
|  * | ||||
|  * See_Also: $(D_PSYMBOL isForwardRange). | ||||
|  */ | ||||
| template isBidirectionalRange(R) | ||||
| { | ||||
|     static if (is(ReturnType!((R r) => r.back()) U)) | ||||
|     { | ||||
|         enum bool isBidirectionalRange = isForwardRange!R | ||||
|                                       && is(U == ReturnType!((R r) => r.front())) | ||||
|                                       && is(typeof(R.popBack())); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         enum bool isBidirectionalRange = isDynamicArrayRange!R; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// | ||||
| pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static struct Range | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         void popBack() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         @property int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         @property int back() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|         Range save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|     static assert(isBidirectionalRange!Range); | ||||
|     static assert(isBidirectionalRange!(int[])); | ||||
|     static assert(!isBidirectionalRange!(void[])); | ||||
| } | ||||
|  | ||||
| private nothrow  @safe @nogc unittest | ||||
| { | ||||
|     static struct Range(T, U) | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         void popBack() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         @property T front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return T.init; | ||||
|         } | ||||
|         @property U back() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return U.init; | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|         Range save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
|     static assert(!isBidirectionalRange!(Range!(int, uint))); | ||||
|     static assert(!isBidirectionalRange!(Range!(int, const int))); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Determines whether $(D_PARAM R) is a random-access range. | ||||
|  * | ||||
|  * A random-access range is a range that allows random access to its | ||||
|  * elements by index using $(D_INLINECODE [])-operator (defined with | ||||
|  * $(D_INLINECODE opIndex())). Further a random access range should be a | ||||
|  * bidirectional range that also has a length or an infinite forward range. | ||||
|  * | ||||
|  * Params: | ||||
|  *  R = The type to be tested. | ||||
|  * | ||||
|  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a random-access range, | ||||
|  *          $(D_KEYWORD false) otherwise. | ||||
|  * | ||||
|  * See_Also: $(D_PSYMBOL isBidirectionalRange), | ||||
|  *           $(D_PSYMBOL isForwardRange), | ||||
|  *           $(D_PSYMBOL isInfinite), | ||||
|  *           $(D_PSYMBOL hasLength). | ||||
|  */ | ||||
| template isRandomAccessRange(R) | ||||
| { | ||||
|     static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U)) | ||||
|     { | ||||
|         private enum bool isBidirectional = isBidirectionalRange!R | ||||
|                                          && hasLength!R; | ||||
|         private enum bool isForward = isInfinite!R && isForwardRange!R; | ||||
|         enum bool isRandomAccessRange = (isBidirectional || isForward) | ||||
|                                      && is(U == ReturnType!((R r) => r.front())); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         enum bool isRandomAccessRange = isDynamicArrayRange!R; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// | ||||
| pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static struct A | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         void popBack() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         @property int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         @property int back() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         int opIndex(const size_t pos) pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         size_t length() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     static assert(isRandomAccessRange!A); | ||||
|     static assert(isRandomAccessRange!(int[])); | ||||
|     static assert(!isRandomAccessRange!(void[])); | ||||
|  | ||||
|     static struct B | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         @property int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         enum bool empty = false; | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         int opIndex(const size_t pos) pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     static assert(isRandomAccessRange!B); | ||||
| } | ||||
|  | ||||
| private pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static struct Range1 | ||||
|     { | ||||
|         mixin InputRangeStub; | ||||
|         mixin BidirectionalRangeStub; | ||||
|  | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         int opIndex(const size_t pos) pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     static assert(!isRandomAccessRange!Range1); | ||||
|  | ||||
|     static struct Range2(Args...) | ||||
|     { | ||||
|         mixin InputRangeStub; | ||||
|         mixin BidirectionalRangeStub; | ||||
|  | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         int opIndex(Args) pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         size_t length() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     static assert(isRandomAccessRange!(Range2!size_t)); | ||||
|     static assert(!isRandomAccessRange!(Range2!())); | ||||
|     static assert(!isRandomAccessRange!(Range2!(size_t, size_t))); | ||||
|  | ||||
|     static struct Range3 | ||||
|     { | ||||
|         mixin InputRangeStub; | ||||
|         mixin BidirectionalRangeStub; | ||||
|  | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         int opIndex(const size_t pos1, const size_t pos2 = 0) | ||||
|         pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         size_t length() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     static assert(isRandomAccessRange!Range3); | ||||
|  | ||||
|     static struct Range4 | ||||
|     { | ||||
|         mixin InputRangeStub; | ||||
|         mixin BidirectionalRangeStub; | ||||
|  | ||||
|         typeof(this) save() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return this; | ||||
|         } | ||||
|         int opIndex(const size_t pos1) pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         size_t opDollar() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     static assert(!isRandomAccessRange!Range4); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Determines whether $(D_PARAM R) is an infinite range. | ||||
|  * | ||||
|  * An infinite range is an input range whose `empty` member is defined as | ||||
|  * $(D_KEYWORD enum) which is always $(D_KEYWORD false). | ||||
|  * | ||||
|  * Params: | ||||
|  *  R = A type. | ||||
|  * | ||||
|  * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an infinite range, | ||||
|  *          $(D_KEYWORD false) otherwise. | ||||
|  */ | ||||
| template isInfinite(R) | ||||
| { | ||||
|     static if (isInputRange!R && is(typeof({enum bool e = R.empty;}))) | ||||
|     { | ||||
|         enum bool isInfinite = R.empty == false; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         enum bool isInfinite = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// | ||||
| pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static assert(!isInfinite!int); | ||||
|  | ||||
|     static struct NotRange | ||||
|     { | ||||
|         enum bool empty = false; | ||||
|     } | ||||
|     static assert(!isInfinite!NotRange); | ||||
|  | ||||
|     static struct InfiniteRange | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         @property int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         enum bool empty = false; | ||||
|     } | ||||
|     static assert(isInfinite!InfiniteRange); | ||||
|  | ||||
|     static struct InputRange | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         @property int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         @property bool empty() const pure nothrow @safe @nogc | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     static assert(!isInfinite!InputRange); | ||||
| } | ||||
|  | ||||
| private pure nothrow @safe @nogc unittest | ||||
| { | ||||
|     static struct StaticConstRange | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         @property int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         static bool empty = false; | ||||
|     } | ||||
|     static assert(!isInfinite!StaticConstRange); | ||||
|  | ||||
|     static struct TrueRange | ||||
|     { | ||||
|         void popFront() pure nothrow @safe @nogc | ||||
|         { | ||||
|         } | ||||
|         @property int front() pure nothrow @safe @nogc | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|         static const bool empty = true; | ||||
|     } | ||||
|     static assert(!isInfinite!TrueRange); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user