From 2a67ccd9542d2d61117caefb2f59f5fdf205fbc2 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 27 Aug 2025 13:52:11 +0200 Subject: [PATCH] Use standard range API --- README.md | 3 - source/tanya/algorithm/iteration.d | 1 + source/tanya/algorithm/mutation.d | 1 + source/tanya/container/array.d | 1 + source/tanya/container/hashtable.d | 1 + source/tanya/container/list.d | 1 + source/tanya/container/set.d | 1 + source/tanya/container/string.d | 1 + source/tanya/conv.d | 1 + source/tanya/format.d | 1 + source/tanya/hash/lookup.d | 1 + source/tanya/net/iface.d | 1 + source/tanya/net/inet.d | 1 + source/tanya/net/ip.d | 1 + source/tanya/range/adapter.d | 1 + source/tanya/range/primitive.d | 1364 +---------------------- tests/tanya/algorithm/tests/iteration.d | 1 + tests/tanya/container/tests/hashtable.d | 3 +- tests/tanya/container/tests/set.d | 8 - tests/tanya/net/tests/inet.d | 2 +- tests/tanya/range/tests/adapter.d | 1 + tests/tanya/range/tests/primitive.d | 387 ------- tests/tanya/tests/format.d | 1 + 23 files changed, 21 insertions(+), 1763 deletions(-) delete mode 100644 tests/tanya/range/tests/primitive.d diff --git a/README.md b/README.md index 690714f..9b0cf29 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ programming in D. Tanya consists of the following packages and (top-level) modules: * `algorithm`: Collection of generic algorithms. -* `bitmanip`: Bit manipulation. * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 string, Set, Hash table. * `conv`: This module provides functions for converting between different @@ -36,8 +35,6 @@ arguments. * `os`: Platform-independent interfaces to operating system functionality. * `range`: Generic functions and templates for D ranges. * `test`: Test suite for unittest-blocks. -* `typecons`: Templates that allow to build new types based on the available -ones. ## NogcD diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index df1027d..b4e1403 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -20,6 +20,7 @@ */ module tanya.algorithm.iteration; +import std.range : isBidirectionalRange; import std.traits; import std.typecons; import tanya.memory.lifetime; diff --git a/source/tanya/algorithm/mutation.d b/source/tanya/algorithm/mutation.d index 1a1bb56..956ee4a 100644 --- a/source/tanya/algorithm/mutation.d +++ b/source/tanya/algorithm/mutation.d @@ -14,6 +14,7 @@ */ module tanya.algorithm.mutation; +import std.range : hasLvalueElements, isInputRange, isOutputRange, put; import std.traits; static import tanya.memory.lifetime; static import tanya.memory.op; diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d index b6c9c5d..aa6dc76 100644 --- a/source/tanya/container/array.d +++ b/source/tanya/container/array.d @@ -18,6 +18,7 @@ import core.checkedint; import std.algorithm.comparison; import std.algorithm.iteration; import std.algorithm.mutation : bringToFront; +import std.range : isInfinite, isInputRange; import std.traits; import tanya.algorithm.mutation; import tanya.memory.allocator; diff --git a/source/tanya/container/hashtable.d b/source/tanya/container/hashtable.d index 5e9a4e4..f1f8666 100644 --- a/source/tanya/container/hashtable.d +++ b/source/tanya/container/hashtable.d @@ -15,6 +15,7 @@ module tanya.container.hashtable; import std.algorithm.iteration; +import std.range : isInfinite, isForwardRange; import std.traits; import tanya.algorithm.mutation; import tanya.container.array; diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index ddef391..513ea49 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -17,6 +17,7 @@ module tanya.container.list; import std.algorithm.comparison; import std.algorithm.iteration; +import std.range : isInfinite, isInputRange; import std.traits; import tanya.container.entry; import tanya.memory.allocator; diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d index 5a4f64c..a8d9572 100644 --- a/source/tanya/container/set.d +++ b/source/tanya/container/set.d @@ -15,6 +15,7 @@ */ module tanya.container.set; +import std.range : isInfinite, isForwardRange; import std.traits; import tanya.container.array; import tanya.container.entry; diff --git a/source/tanya/container/string.d b/source/tanya/container/string.d index 10db553..0401881 100644 --- a/source/tanya/container/string.d +++ b/source/tanya/container/string.d @@ -28,6 +28,7 @@ module tanya.container.string; import std.algorithm.comparison; import std.algorithm.mutation : bringToFront; +import std.range : isInfinite, popFrontN, isInputRange; import std.traits; import tanya.algorithm.mutation; import tanya.hash.lookup; diff --git a/source/tanya/conv.d b/source/tanya/conv.d index 3a75d62..e373d07 100644 --- a/source/tanya/conv.d +++ b/source/tanya/conv.d @@ -14,6 +14,7 @@ */ module tanya.conv; +import std.range : isInputRange; import std.traits; import tanya.container.string; import tanya.memory.allocator; diff --git a/source/tanya/format.d b/source/tanya/format.d index 089ed49..18d6a76 100644 --- a/source/tanya/format.d +++ b/source/tanya/format.d @@ -51,6 +51,7 @@ import std.algorithm.comparison; import std.ascii; import std.math : signbit; import std.meta; +import std.range : isInfinite, popFrontExactly, isInputRange, isOutputRange, put; import std.traits; import tanya.container.string; import tanya.math; diff --git a/source/tanya/hash/lookup.d b/source/tanya/hash/lookup.d index eb26caa..2b5116d 100644 --- a/source/tanya/hash/lookup.d +++ b/source/tanya/hash/lookup.d @@ -14,6 +14,7 @@ */ module tanya.hash.lookup; +import std.range : isInfinite, isInputRange; import std.traits; import tanya.meta; import tanya.range.primitive; diff --git a/source/tanya/net/iface.d b/source/tanya/net/iface.d index c910b29..6c5a962 100644 --- a/source/tanya/net/iface.d +++ b/source/tanya/net/iface.d @@ -14,6 +14,7 @@ */ module tanya.net.iface; +import std.range : isInputRange; import std.traits; import tanya.algorithm.mutation; import tanya.container.string; diff --git a/source/tanya/net/inet.d b/source/tanya/net/inet.d index 141fd77..a919c9d 100644 --- a/source/tanya/net/inet.d +++ b/source/tanya/net/inet.d @@ -14,6 +14,7 @@ */ module tanya.net.inet; +import std.range : isInfinite, isInputRange; import std.traits; import tanya.meta; import tanya.range; diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d index 87268bd..4baa6e2 100644 --- a/source/tanya/net/ip.d +++ b/source/tanya/net/ip.d @@ -16,6 +16,7 @@ module tanya.net.ip; import std.algorithm.comparison; import std.ascii; +import std.range : isForwardRange, isInputRange, isOutputRange, put; import std.sumtype; import std.typecons; import std.traits; diff --git a/source/tanya/range/adapter.d b/source/tanya/range/adapter.d index 287e868..f342d2d 100644 --- a/source/tanya/range/adapter.d +++ b/source/tanya/range/adapter.d @@ -14,6 +14,7 @@ */ module tanya.range.adapter; +import std.range : isInputRange, isOutputRange, put; import std.traits; import tanya.algorithm.mutation; import tanya.memory.lifetime; diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d index d8abe3d..ec1eb0b 100644 --- a/source/tanya/range/primitive.d +++ b/source/tanya/range/primitive.d @@ -15,6 +15,7 @@ module tanya.range.primitive; import std.algorithm.comparison; +import std.range : isInfinite, hasSlicing, hasLvalueElements, isInputRange, isBidirectionalRange; import std.traits; import tanya.memory.lifetime; import tanya.meta; @@ -107,190 +108,6 @@ enum bool hasLength(R) = is(ReturnType!((R r) => r.length) == size_t); 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; - } -} - -/// -@nogc nothrow pure @safe 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() @nogc nothrow pure @safe - { - return 0; - } - void popFront() @nogc nothrow pure @safe - { - } - bool empty() const @nogc nothrow pure @safe - { - return false; - } - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } - @property size_t length() const @nogc nothrow pure @safe - { - 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() @nogc nothrow pure @safe - { - return 0; - } - void popFront() @nogc nothrow pure @safe - { - } - bool empty() const @nogc nothrow pure @safe - { - return false; - } - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } - @property size_t length() const @nogc nothrow pure @safe - { - return 0; - } - @property Dollar opDollar() const @nogc nothrow pure @safe - { - 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() @nogc nothrow pure @safe - { - return 0; - } - void popFront() @nogc nothrow pure @safe - { - } - enum bool empty = false; - typeof(this) save() @nogc nothrow pure @safe - { - 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() @nogc nothrow pure @safe - { - return 0; - } - void popFront() @nogc nothrow pure @safe - { - } - bool empty() const @nogc nothrow pure @safe - { - return true; - } - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } - @property size_t length() const @nogc nothrow pure @safe - { - return 0; - } - } - int front() @nogc nothrow pure @safe - { - return 0; - } - void popFront() @nogc nothrow pure @safe - { - } - enum bool empty = false; - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } - Range opSlice(const size_t i, const size_t j) - pure nothrow @safe @nogc - { - return Range(); - } - } - static assert(hasSlicing!D); -} - private template isDynamicArrayRange(R) { static if (is(R E : E[])) @@ -323,1185 +140,6 @@ private struct Primitive(Candidate, string primitive) } } -/** - * 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(Primitive!(R, "front()") U) - && is(ReturnType!((R r) => r.empty) == bool) - && is(typeof(R.popFront()))) - { - enum bool isInputRange = true; - } - else - { - enum bool isInputRange = isDynamicArrayRange!R; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct Range - { - void popFront() @nogc nothrow pure @safe - { - } - - int front() @nogc nothrow pure @safe - { - return 0; - } - - bool empty() const @nogc nothrow pure @safe - { - return true; - } - } - static assert(isInputRange!Range); - static assert(isInputRange!(int[])); - static assert(!isInputRange!(void[])); -} - -/** - * 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 = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct Range - { - void popFront() @nogc nothrow pure @safe - { - } - - int front() @nogc nothrow pure @safe - { - return 0; - } - - bool empty() const @nogc nothrow pure @safe - { - return true; - } - - typeof(this) save() @nogc nothrow pure @safe - { - return this; - } - } - static assert(isForwardRange!Range); - static assert(isForwardRange!(int[])); - static assert(!isForwardRange!(void[])); -} - -/** - * 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(Primitive!(R, "back()") U) - && is(typeof(R.popBack()))) - { - enum bool isBidirectionalRange = isForwardRange!R - && (U() == Primitive!(R, "front()")()); - } - else - { - enum bool isBidirectionalRange = isDynamicArrayRange!R; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct Range - { - void popFront() @nogc nothrow pure @safe - { - } - - void popBack() @nogc nothrow pure @safe - { - } - - @property int front() @nogc nothrow pure @safe - { - return 0; - } - - @property int back() @nogc nothrow pure @safe - { - return 0; - } - - bool empty() const @nogc nothrow pure @safe - { - return true; - } - - Range save() @nogc nothrow pure @safe - { - return this; - } - } - static assert(isBidirectionalRange!Range); - static assert(isBidirectionalRange!(int[])); - static assert(!isBidirectionalRange!(void[])); -} - -/** - * 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 - * have a length or be infinite. - * - * 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 isInfinite), - * $(D_PSYMBOL hasLength). - * - * Note: This definition differs from `std.range.primitives.isRandomAccessRange` - * in the D standard library in that it does not also require $(D_PARAM R) to - * be a forward range and a bidirectional range. Those properties may be tested - * separately with $(D_PSYMBOL isForwardRange) and - * $(D_PSYMBOL isBidirectionalRange). - */ -template isRandomAccessRange(R) -{ - static if (is(Primitive!(R, "opIndex(size_t.init)") U)) - { - enum bool isRandomAccessRange = isInputRange!R - && (hasLength!R || isInfinite!R) - && (U() == Primitive!(R, "front()")()); - } - else - { - enum bool isRandomAccessRange = isDynamicArrayRange!R; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct A - { - void popFront() @nogc nothrow pure @safe - { - } - - @property int front() @nogc nothrow pure @safe - { - return 0; - } - - bool empty() const @nogc nothrow pure @safe - { - return true; - } - - int opIndex(size_t) @nogc nothrow pure @safe - { - return 0; - } - - size_t length() const @nogc nothrow pure @safe - { - return 0; - } - } - static assert(isRandomAccessRange!A); - static assert(isRandomAccessRange!(int[])); - static assert(!isRandomAccessRange!(void[])); - - static struct B - { - void popFront() @nogc nothrow pure @safe - { - } - - @property int front() @nogc nothrow pure @safe - { - return 0; - } - - enum bool empty = false; - - int opIndex(const size_t pos) @nogc nothrow pure @safe - { - return 0; - } - } - static assert(isRandomAccessRange!B); -} - -/** - * Puts $(D_PARAM e) into the $(D_PARAM range). - * - * $(D_PSYMBOL R) should be an output range for $(D_PARAM E), i.e. at least one - * of the following conditions should met: - * - * $(OL - * $(LI $(D_PARAM e) can be put into $(D_PARAM range) using - * $(D_INLINECODE range(e)) - * $(LI $(D_PARAM e) can be assigned to $(D_INLINECODE range.front)) - * ) - * ) - * - * The method to put $(D_PARAM e) into $(D_PARAM range) is chosen based on the - * order specified above. - * - * If $(D_PARAM E) is an input range and $(D_PARAM R) is an output range for - * its elements as well, use $(D_PSYMBOL tanya.algorithm.mutation.copy) - * instead. - * - * $(D_PARAM range) is advanced after putting an element into it if it is an - * input range that doesn't define a `put`-method. - * - * Params: - * R = Target range type. - * E = Source element type. - * range = Target range. - * e = Source element. - * - * See_Also: $(D_PSYMBOL isOutputRange). - */ -void put(R, E)(ref R range, auto ref E e) -{ - static if (is(typeof((R r, E e) => r(e)))) - { - range(e); - } - else static if (isInputRange!R - && is(typeof((R r, E e) => r.front = e))) - { - range.front = e; - range.popFront(); - } - else - { - static assert(false, R.stringof ~ " is not an output range for " - ~ E.stringof); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int[2] actual; - auto slice = actual[]; - - put(slice, 2); - assert(actual == [2, 0]); -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct OpCall - { - int e; - - void opCall(int e) - { - this.e = e; - } - } - OpCall oc; - put(oc, 2); - assert(oc.e == 2); -} - -/** - * Determines whether $(D_PARAM R) is an output range for the elemens of type - * $(D_PARAM E). - * - * If $(D_PARAM R) is an output range for the elements of type $(D_PARAM E) - * if an element `e` of type $(D_PARAM E) can be put into the range instance - * `r` in one of the following ways: - * - * $(TABLE - * $(TR - * $(TH Code) - * $(TH Scenario) - * ) - * $(TR - * $(TD r(e)) - * $(TD $(D_PARAM R) defines `opCall` for $(D_PARAM E).) - * ) - * $(TR - * $(TD r.front = e) - * $(TD $(D_PARAM R) is an input range with assignable elements of type - * $(D_PARAM E).) - * ) - * ) - * - * Output ranges don't have element type (so $(D_PSYMBOL ElementType) returns - * $(D_KEYWORD void) when applied to an output range). It is because an output - * range can support puting differently typed elements into it. - * - * Params: - * R = The type to be tested. - * E = Element type should be tested for. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the - * elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL put). - */ -template isOutputRange(R, E) -{ - static if (is(typeof((R r, E e) => put(r, e)))) - { - enum bool isOutputRange = true; - } - else static if (isInputRange!E) - { - pragma(msg, "Deprecation. An input range whose element type is " - ~ "supported by the output range isn't considered itself to " - ~ "be a source for such an output range. Don't rely on this " - ~ "behavior and use tanya.algorithm.copy() to write one " - ~ "range into another one."); - alias ET = ElementType!E; - enum bool isOutputRange = is(typeof((R r, ET e) => put(r, e))); - } - else - { - enum bool isOutputRange = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct R1 - { - void opCall(int) @nogc nothrow pure @safe - { - } - } - static assert(isOutputRange!(R1, int)); - - static struct R2 - { - int value; - - void popFront() @nogc nothrow pure @safe - { - } - - ref int front() @nogc nothrow pure @safe - { - return value; - } - - bool empty() const @nogc nothrow pure @safe - { - return true; - } - } - static assert(isOutputRange!(R2, int)); - - static struct R3 - { - void popFront() @nogc nothrow pure @safe - { - } - - int front() @nogc nothrow pure @safe - { - return 0; - } - - bool empty() const @nogc nothrow pure @safe - { - return true; - } - } - static assert(!isOutputRange!(R3, int)); -} - -/** - * 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; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(!isInfinite!int); - - static struct NotRange - { - enum bool empty = false; - } - static assert(!isInfinite!NotRange); - - static struct InfiniteRange - { - void popFront() @nogc nothrow pure @safe - { - } - @property int front() @nogc nothrow pure @safe - { - return 0; - } - enum bool empty = false; - } - static assert(isInfinite!InfiniteRange); - - static struct InputRange - { - void popFront() @nogc nothrow pure @safe - { - } - @property int front() @nogc nothrow pure @safe - { - return 0; - } - @property bool empty() const @nogc nothrow pure @safe - { - return false; - } - } - static assert(!isInfinite!InputRange); -} - -/** - * Removes exactly $(D_PARAM count) first elements from the input range - * $(D_PARAM range). - * - * $(D_PARAM R) must have length or be infinite. - * - * Params: - * R = Range type. - * range = Some input range. - * count = Number of elements to remove. - * - * See_Also: $(D_PSYMBOL popBackExactly), - * $(D_PSYMBOL popFrontN), - * $(D_PSYMBOL isInputRange), - * $(D_PSYMBOL hasLength), - * $(D_PSYMBOL isInfinite). - * - * Precondition: If $(D_PARAM R) has length, it must be less than or equal to - * $(D_PARAM count). - */ -void popFrontExactly(R)(ref R range, size_t count) -if (isInputRange!R && (hasLength!R || isInfinite!R)) -in -{ - static if (hasLength!R) - { - assert(count <= range.length); - } -} -do -{ - static if (hasSlicing!R) - { - range = range[count .. $]; - } - else - { - while (count-- != 0) - { - range.popFront(); - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int[5] a = [1, 2, 3, 4, 5]; - auto slice = a[]; - - popFrontExactly(slice, 3); - assert(slice.length == 2); - assert(slice[0] == 4); - assert(slice[$ - 1] == 5); - - popFrontExactly(slice, 2); - assert(slice.length == 0); -} - -/** - * Removes exactly $(D_PARAM count) last elements from the bidirectional range - * $(D_PARAM range). - * - * $(D_PARAM R) must have length or be infinite. - * - * Params: - * R = Range type. - * range = Some bidirectional range. - * count = Number of elements to remove. - * - * See_Also: $(D_PSYMBOL popFrontExactly), - * $(D_PSYMBOL popBackN), - * $(D_PSYMBOL isBidirectionalRange), - * $(D_PSYMBOL hasLength), - * $(D_PSYMBOL isInfinite). - * - * Precondition: If $(D_PARAM R) has length, it must be less than or equal to - * $(D_PARAM count). - */ -void popBackExactly(R)(ref R range, size_t count) -if (isBidirectionalRange!R && (hasLength!R || isInfinite!R)) -in -{ - static if (hasLength!R) - { - assert(count <= range.length); - } -} -do -{ - static if (hasSlicing!R) - { - range = range[0 .. $ - count]; - } - else - { - while (count-- != 0) - { - range.popBack(); - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int[5] a = [1, 2, 3, 4, 5]; - auto slice = a[]; - - popBackExactly(slice, 3); - assert(slice.length == 2); - assert(slice[0] == 1); - assert(slice[$ - 1] == 2); - - popBackExactly(slice, 2); - assert(slice.length == 0); -} - -/** - * Removes maximum $(D_PARAM count) first elements from the input range - * $(D_PARAM range). - * - * Params: - * R = Range type. - * range = Some input range. - * count = Number of elements to remove. - * - * See_Also: $(D_PSYMBOL popBackN), - * $(D_PSYMBOL popFrontExactly), - * $(D_PSYMBOL isInputRange). - */ -void popFrontN(R)(ref R range, size_t count) -if (isInputRange!R) -{ - static if (hasLength!R && hasSlicing!R) - { - range = range[min(count, range.length) .. $]; - } - else static if (hasLength!R) - { - size_t length = min(count, range.length); - while (length--) - { - range.popFront(); - } - } - else - { - while (count-- != 0 && !range.empty) - { - range.popFront(); - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int[5] a = [1, 2, 3, 4, 5]; - auto slice = a[]; - - popFrontN(slice, 3); - assert(slice.length == 2); - assert(slice[0] == 4); - assert(slice[$ - 1] == 5); - - popFrontN(slice, 20); - assert(slice.length == 0); -} - -/** - * Removes maximum $(D_PARAM count) last elements from the bidirectional range - * $(D_PARAM range). - * - * Params: - * R = Range type. - * range = Some bidirectional range. - * count = Number of elements to remove. - * - * See_Also: $(D_PSYMBOL popFrontN), - * $(D_PSYMBOL popBackExactly), - * $(D_PSYMBOL isBidirectionalRange). - */ -void popBackN(R)(ref R range, size_t count) -if (isBidirectionalRange!R) -{ - static if (hasLength!R && hasSlicing!R) - { - range = range[0 .. $ - min(count, range.length)]; - } - else static if (hasLength!R) - { - size_t length = min(count, range.length); - while (length--) - { - range.popBack(); - } - } - else - { - while (count-- != 0 && !range.empty) - { - range.popBack(); - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int[5] a = [1, 2, 3, 4, 5]; - auto slice = a[]; - - popBackN(slice, 3); - assert(slice.length == 2); - assert(slice[0] == 1); - assert(slice[$ - 1] == 2); - - popBackN(slice, 20); - assert(slice.length == 0); -} - -/** - * Moves the front element of an input range. - * - * The front element is left in a valid but unspecified state. - * $(D_PSYMBOL moveFront) doesn't advances the range, so `popFront` should be - * probably called after this function. - * - * Params: - * R = Type of the range. - * range = Input range. - * - * Returns: The front element of the $(D_PSYMBOL range). - * - * See_Also: $(D_PSYMBOL move). - */ -ElementType!R moveFront(R)(R range) -if (isInputRange!R) -{ - static if (!hasElaborateCopyConstructor!(ElementType!R)) - { - return range.front; - } - else static if (is(typeof(((ref ElementType!R e) => e)(range.front)))) - { - return move(range.front); - } - else - { - static assert(false, "Front element cannot be moved"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - // Has elements without a postblit constructor. - int[2] a = 5; - - assert(moveFront(a[]) == 5); -} - -/** - * Moves the back element of a bidirectional range. - * - * The back element is left in a valid but unspecified state. - * $(D_PSYMBOL moveBack) doesn't advances the range, so `popBack` should be - * probably called after this function. - * - * Params: - * R = Type of the range. - * range = Bidirectional range. - * - * Returns: The back element of the $(D_PSYMBOL range). - * - * See_Also: $(D_PSYMBOL move). - */ -ElementType!R moveBack(R)(R range) -if (isBidirectionalRange!R) -{ - static if (!hasElaborateCopyConstructor!(ElementType!R)) - { - return range.back; - } - else static if (is(typeof(((ref ElementType!R e) => e)(range.back)))) - { - return move(range.back); - } - else - { - static assert(false, "Back element cannot be moved"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - // Has elements without a postblit constructor. - int[2] a = 5; - - assert(moveBack(a[]) == 5); -} - -/** - * Moves the element at the position $(D_PARAM n) out of the range. - * - * The moved element is left in a valid but unspecified state. - * - * Params: - * R = Range type. - * range = Random-access range. - * n = Element position. - * - * Returns: The element at the position $(D_PARAM n). - * - * See_Also: $(D_PSYMBOL move). - */ -ElementType!R moveAt(R)(R range, size_t n) -if (isRandomAccessRange!R) -{ - static if (!hasElaborateCopyConstructor!(ElementType!R)) - { - return range[n]; - } - else static if (is(typeof(((ref ElementType!R e) => e)(range[0])))) - { - return move(range[n]); - } - else - { - static assert(false, "Random element cannot be moved"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - // Has elements without a postblit constructor. - int[3] a = 5; - - assert(moveAt(a[], 1) == 5); -} - -/** - * Determines whether $(D_PSYMBOL R) is a range containing mobile elements, - * i.e. elements that can be moved out of the range. - * - * Having mobile elements means for an input range to support - * $(D_PSYMBOL moveFront), for a bidirectional range - both, - * $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveBack), for a random-access - * range - $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveAt). - * - * Params: - * R = Range type. - * - * Returns: $(D_KEYWORD true) if $(D_PSYMBOL R) has mobile elements, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL moveFront), $(D_PSYMBOL moveBack), - * $(D_PSYMBOL moveAt). - */ -template hasMobileElements(R) -{ - static if (isRandomAccessRange!R) - { - enum bool hasMobileElements = is(typeof((R r) => moveFront(r))) - && is(typeof((R r) => moveAt(r, 0))); - } - else static if (isBidirectionalRange!R) - { - enum bool hasMobileElements = is(typeof((R r) => moveFront(r))) - && is(typeof((R r) => moveBack(r))); - } - else - { - enum bool hasMobileElements = is(typeof((R r) => moveFront(r))); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(hasMobileElements!(int[])); -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct Element - { - this(this) @nogc nothrow pure @safe - { - } - } - - static struct R1 - { - enum bool empty = false; - - Element front() @nogc nothrow pure @safe - { - return Element(); - } - - void popFront() @nogc nothrow pure @safe - { - } - } - static assert(!hasMobileElements!R1); - - static struct R2 - { - enum bool empty = false; - private Element front_; - - ref Element front() @nogc nothrow pure @safe - { - return front_; - } - - void popFront() @nogc nothrow pure @safe - { - } - } - static assert(hasMobileElements!R2); -} - -/** - * Determines whether $(D_PARAM R) provides access to its elements by - * reference. - * - * Params: - * R = Range type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM R) has lvalue elements, - * $(D_KEYWORD false) otherwise. - */ -template hasLvalueElements(R) -{ - private alias refDg = (ref ElementType!R e) => &e; - - static if (isRandomAccessRange!R) - { - enum bool hasLvalueElements = is(typeof(refDg(R.init.front))) - && is(typeof(refDg(R.init[0]))); - } - else static if (isBidirectionalRange!R) - { - enum bool hasLvalueElements = is(typeof(refDg(R.init.front))) - && is(typeof(refDg(R.init.back))); - } - else - { - enum bool hasLvalueElements = is(typeof(refDg(R.init.front))); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct R1 - { - enum bool empty = false; - - int front() @nogc nothrow pure @safe - { - return 5; - } - - void popFront() @nogc nothrow pure @safe - { - } - } - static assert(!hasLvalueElements!R1); - - static struct R2 - { - int element; - enum bool empty = false; - - ref const(int) front() const @nogc nothrow pure @safe - { - return element; - } - - void popFront() @nogc nothrow pure @safe - { - } - - ref const(int) opIndex(size_t) const @nogc nothrow pure @safe - { - return element; - } - } - static assert(hasLvalueElements!R2); -} - -/** - * Determines whether the elements of $(D_PARAM R) are assignable. - * - * Params: - * R = Range type. - * - * Returns: $(D_KEYWORD true) if the elements of $(D_PARAM R) are assignable - * $(D_KEYWORD false) otherwise. - */ -template hasAssignableElements(R) -{ - static if (isRandomAccessRange!R) - { - enum bool assignable = is(typeof({R.init.front = R.init.front;})) - && is(typeof({R.init[0] = R.init[0];})); - } - else static if (isBidirectionalRange!R) - { - enum bool assignable = is(typeof({R.init.front = R.init.front;})) - && is(typeof({R.init.back = R.init.back;})); - } - else - { - enum bool assignable = is(typeof({R.init.front = R.init.front;})); - } - enum bool hasAssignableElements = assignable; -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct R1 - { - int element; - enum bool empty = false; - - ref int front() @nogc nothrow pure @safe - { - return element; - } - alias back = front; - - void popFront() @nogc nothrow pure @safe - { - } - alias popBack = popFront; - - R1 save() const @nogc nothrow pure @safe - { - return this; - } - } - static assert(hasAssignableElements!R1); - - static struct R2 - { - int element; - enum bool empty = false; - - ref const(int) front() const @nogc nothrow pure @safe - { - return element; - } - alias back = front; - - void popFront() @nogc nothrow pure @safe - { - } - alias popBack = popFront; - - R2 save() const @nogc nothrow pure @safe - { - return this; - } - } - static assert(!hasAssignableElements!R2); -} - -/** - * Determines whether the elements of $(D_PSYMBOL R) can be swapped with - * $(D_PSYMBOL swap). - * - * Params: - * R = Range type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM R) has swappable elements, - * $(D_KEYWORD false) otherwise. - */ -template hasSwappableElements(R) -{ - static if (isRandomAccessRange!R) - { - enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front))) - && is(typeof(swap(R.init[0], R.init[0]))); - } - else static if (isBidirectionalRange!R) - { - enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front))) - && is(typeof(swap(R.init.back, R.init.back))); - } - else - { - enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front))); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct R1 - { - int element; - enum bool empty = false; - - ref int front() @nogc nothrow pure @safe - { - return element; - } - alias back = front; - - void popFront() @nogc nothrow pure @safe - { - } - alias popBack = popFront; - - R1 save() const @nogc nothrow pure @safe - { - return this; - } - } - static assert(hasSwappableElements!R1); - - static struct R2 - { - int element; - enum bool empty = false; - - int front() const @nogc nothrow pure @safe - { - return element; - } - alias back = front; - - void popFront() @nogc nothrow pure @safe - { - } - alias popBack = popFront; - - R2 save() const @nogc nothrow pure @safe - { - return this; - } - } - static assert(!hasSwappableElements!R2); -} - /** * Determines whether `r1.front` and `r2.front` point to the same element. * diff --git a/tests/tanya/algorithm/tests/iteration.d b/tests/tanya/algorithm/tests/iteration.d index 9594d5e..2821cf9 100644 --- a/tests/tanya/algorithm/tests/iteration.d +++ b/tests/tanya/algorithm/tests/iteration.d @@ -4,6 +4,7 @@ module tanya.algorithm.tests.iteration; +import std.range : isBidirectionalRange, isRandomAccessRange; import tanya.algorithm.iteration; import tanya.range; import tanya.test.stub; diff --git a/tests/tanya/container/tests/hashtable.d b/tests/tanya/container/tests/hashtable.d index 98c9c53..8c0dc92 100644 --- a/tests/tanya/container/tests/hashtable.d +++ b/tests/tanya/container/tests/hashtable.d @@ -8,7 +8,8 @@ import tanya.test.stub; @nogc nothrow pure @safe unittest { - import tanya.range.primitive : isForwardRange; + import std.range : isForwardRange; + static assert(is(HashTable!(string, int) a)); static assert(is(const HashTable!(string, int))); static assert(isForwardRange!(HashTable!(string, int).Range)); diff --git a/tests/tanya/container/tests/set.d b/tests/tanya/container/tests/set.d index 67259d9..94ba1ba 100644 --- a/tests/tanya/container/tests/set.d +++ b/tests/tanya/container/tests/set.d @@ -41,14 +41,6 @@ import tanya.test.stub; // Static checks. @nogc nothrow pure @safe unittest { - import tanya.range.primitive; - - static assert(isBidirectionalRange!(Set!int.ConstRange)); - static assert(isBidirectionalRange!(Set!int.Range)); - - static assert(!isInfinite!(Set!int.Range)); - static assert(!hasLength!(Set!int.Range)); - static assert(is(Set!uint)); static assert(is(Set!long)); static assert(is(Set!ulong)); diff --git a/tests/tanya/net/tests/inet.d b/tests/tanya/net/tests/inet.d index 798015a..61b6ecc 100644 --- a/tests/tanya/net/tests/inet.d +++ b/tests/tanya/net/tests/inet.d @@ -4,7 +4,7 @@ module tanya.net.tests.inet; import tanya.net.inet; -import tanya.range; +import std.range; // Static tests @nogc nothrow pure @safe unittest diff --git a/tests/tanya/range/tests/adapter.d b/tests/tanya/range/tests/adapter.d index cdd90a1..225aa5f 100644 --- a/tests/tanya/range/tests/adapter.d +++ b/tests/tanya/range/tests/adapter.d @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ module tanya.range.tests.adapter; +import std.range : isOutputRange; import tanya.range; private struct Container diff --git a/tests/tanya/range/tests/primitive.d b/tests/tanya/range/tests/primitive.d deleted file mode 100644 index bcd5241..0000000 --- a/tests/tanya/range/tests/primitive.d +++ /dev/null @@ -1,387 +0,0 @@ -/* 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/. */ -module tanya.range.tests.primitive; - -import tanya.range; -import tanya.test.stub; - -private struct AssertPostblit -{ - this(this) @nogc nothrow pure @safe - { - assert(false); - } -} - -@nogc nothrow pure @safe unittest -{ - static struct Range1(T) - { - mixin InputRangeStub; - - T empty() const - { - return true; - } - } - static assert(!isInputRange!(Range1!int)); - static assert(!isInputRange!(Range1!(const bool))); - - static struct Range2 - { - mixin InputRangeStub; - - int popFront() @nogc nothrow pure @safe - { - return 100; - } - } - static assert(isInputRange!Range2); - - static struct Range3 - { - mixin InputRangeStub; - - void front() @nogc nothrow pure @safe - { - } - } - static assert(!isInputRange!Range3); - - static struct Range4 - { - mixin InputRangeStub; - - enum bool empty = false; - } - static assert(isInputRange!Range4); -} - -// Ranges with non-copyable elements can be input ranges -@nogc nothrow pure @safe unittest -{ - @WithLvalueElements - static struct R - { - mixin InputRangeStub!NonCopyable; - } - static assert(isInputRange!R); -} - -// Ranges with const non-copyable elements can be input ranges -@nogc nothrow pure @safe unittest -{ - @WithLvalueElements - static struct R - { - mixin InputRangeStub!(const(NonCopyable)); - } - static assert(isInputRange!R); -} - -@nogc nothrow pure @safe unittest -{ - static struct Range1 - { - } - static struct Range2 - { - mixin InputRangeStub; - - Range1 save() @nogc nothrow pure @safe - { - return Range1(); - } - } - static assert(!isForwardRange!Range2); - - static struct Range3 - { - mixin InputRangeStub; - - const(typeof(this)) save() const @nogc nothrow pure @safe - { - return this; - } - } - static assert(!isForwardRange!Range3); -} - -@nogc nothrow pure @safe unittest -{ - static struct Range(T, U) - { - mixin BidirectionalRangeStub; - - @property T front() @nogc nothrow pure @safe - { - return T.init; - } - - @property U back() @nogc nothrow pure @safe - { - return U.init; - } - } - static assert(!isBidirectionalRange!(Range!(int, uint))); - static assert(!isBidirectionalRange!(Range!(int, const int))); -} - -// Ranges with non-copyable elements can be bidirectional ranges -@nogc nothrow pure @safe unittest -{ - @WithLvalueElements - static struct R - { - mixin BidirectionalRangeStub!NonCopyable; - } - static assert(isBidirectionalRange!R); -} - -@nogc nothrow pure @safe unittest -{ - static struct Range1 - { - mixin BidirectionalRangeStub; - mixin RandomAccessRangeStub; - } - static assert(!isRandomAccessRange!Range1); - - @Length - static struct Range2(Args...) - { - mixin BidirectionalRangeStub; - - int opIndex(Args) @nogc nothrow pure @safe - { - return 0; - } - } - static assert(isRandomAccessRange!(Range2!size_t)); - static assert(!isRandomAccessRange!(Range2!())); - static assert(!isRandomAccessRange!(Range2!(size_t, size_t))); - - @Length - static struct Range3 - { - mixin BidirectionalRangeStub; - - int opIndex(const size_t pos1, const size_t pos2 = 0) - @nogc nothrow pure @safe - { - return 0; - } - } - static assert(isRandomAccessRange!Range3); - - static struct Range4 - { - mixin BidirectionalRangeStub; - mixin RandomAccessRangeStub; - - size_t opDollar() const @nogc nothrow pure @safe - { - return 0; - } - } - static assert(!isRandomAccessRange!Range4); -} - -// Ranges with non-copyable elements can be random-access ranges -@nogc nothrow pure @safe unittest -{ - @WithLvalueElements @Infinite - static struct R - { - mixin RandomAccessRangeStub!NonCopyable; - } - static assert(isRandomAccessRange!R); -} - -@nogc nothrow pure @safe unittest -{ - @Infinite - static struct StaticConstRange - { - mixin InputRangeStub; - - static bool empty = false; - } - static assert(!isInfinite!StaticConstRange); - - @Infinite - static struct TrueRange - { - mixin InputRangeStub; - - static const bool empty = true; - } - static assert(!isInfinite!TrueRange); -} - -@nogc nothrow pure @safe unittest -{ - @Infinite - static struct InfiniteRange - { - mixin ForwardRangeStub; - private int i; - - void popFront() @nogc nothrow pure @safe - { - ++this.i; - } - - void popBack() @nogc nothrow pure @safe - { - --this.i; - } - - @property int front() const @nogc nothrow pure @safe - { - return this.i; - } - - @property int back() const @nogc nothrow pure @safe - { - return this.i; - } - } - { - InfiniteRange range; - popFrontExactly(range, 2); - assert(range.front == 2); - popFrontN(range, 2); - assert(range.front == 4); - } - { - InfiniteRange range; - popBackExactly(range, 2); - assert(range.back == -2); - popBackN(range, 2); - assert(range.back == -4); - } -} - -@nogc nothrow pure @safe unittest -{ - static struct Range - { - private int[5] a = [1, 2, 3, 4, 5]; - private size_t begin = 0, end = 5; - - Range save() @nogc nothrow pure @safe - { - return this; - } - - void popFront() @nogc nothrow pure @safe - { - ++this.begin; - } - - void popBack() @nogc nothrow pure @safe - { - --this.end; - } - - @property int front() const @nogc nothrow pure @safe - { - return this.a[this.begin]; - } - - @property int back() const @nogc nothrow pure @safe - { - return this.a[this.end - 1]; - } - - @property bool empty() const @nogc nothrow pure @safe - { - return this.begin >= this.end; - } - } - { - Range range; - - popFrontN(range, 3); - assert(range.front == 4); - assert(range.back == 5); - - popFrontN(range, 20); - assert(range.empty); - } - { - Range range; - - popBackN(range, 3); - assert(range.front == 1); - assert(range.back == 2); - - popBackN(range, 20); - assert(range.empty); - } -} - -@nogc nothrow pure @safe unittest -{ - // Returns its elements by reference. - @Infinite @WithLvalueElements - static struct R1 - { - mixin InputRangeStub!AssertPostblit; - } - static assert(is(typeof(moveFront(R1())))); - - // Returns elements with a postblit constructor by value. moveFront fails. - @Infinite - static struct R2 - { - mixin InputRangeStub!AssertPostblit; - } - static assert(!is(typeof(moveFront(R2())))); -} - -@nogc nothrow pure @safe unittest -{ - // Returns its elements by reference. - @Infinite @WithLvalueElements - static struct R1 - { - mixin BidirectionalRangeStub!AssertPostblit; - } - static assert(is(typeof(moveBack(R1())))); - - // Returns elements with a postblit constructor by value. moveBack fails. - @Infinite - static struct R2 - { - mixin BidirectionalRangeStub!AssertPostblit; - } - static assert(!is(typeof(moveBack(R2())))); -} - -@nogc nothrow pure @safe unittest -{ - // Returns its elements by reference. - @Infinite @WithLvalueElements - static struct R1 - { - mixin RandomAccessRangeStub!AssertPostblit; - } - static assert(is(typeof(moveAt(R1(), 0)))); - - // Returns elements with a postblit constructor by value. moveAt fails. - @Infinite - static struct R2 - { - mixin RandomAccessRangeStub!AssertPostblit; - } - static assert(!is(typeof(moveAt(R2(), 0)))); -} - -// Works with non-copyable elements -@nogc nothrow pure @safe unittest -{ - static assert(hasLvalueElements!(NonCopyable[])); -} diff --git a/tests/tanya/tests/format.d b/tests/tanya/tests/format.d index 84b84d5..34039db 100644 --- a/tests/tanya/tests/format.d +++ b/tests/tanya/tests/format.d @@ -1,5 +1,6 @@ module tanya.tests.format; +import std.range : put; import tanya.format; import tanya.range;