diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index 343f3b6..76356a4 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -21,11 +21,11 @@ module tanya.algorithm.iteration; import std.algorithm.comparison; +import std.typecons; import tanya.memory.lifetime; import tanya.meta.trait; import tanya.meta.transform; import tanya.range; -import tanya.typecons; // These predicates are used to help preserve `const` and `inout` for // ranges built on other ranges. @@ -570,7 +570,7 @@ if (isBidirectionalRange!Range) private struct SingletonByValue(E) { - private Option!E element; + private Nullable!E element; @disable this(); @@ -581,9 +581,9 @@ private struct SingletonByValue(E) } private this(U)(ref U element) - if (is(Unqual!U == Option!(Unqual!E)) || is(Unqual!U == Option!(const E))) + if (is(Unqual!U == Nullable!(Unqual!E)) || is(Unqual!U == Nullable!(const E))) { - if (!element.isNothing) + if (!element.isNull) { this.element = element.get; } @@ -600,19 +600,19 @@ private struct SingletonByValue(E) void popFront() in (!empty) { - this.element.reset(); + this.element.nullify(); } alias popBack = popFront; @property bool empty() const { - return this.element.isNothing; + return this.element.isNull; } @property size_t length() const { - return !this.element.isNothing; + return !this.element.isNull; } auto save() @@ -620,11 +620,6 @@ private struct SingletonByValue(E) return SingletonByValue!E(this.element); } - auto save() const - { - return SingletonByValue!(const E)(this.element); - } - ref inout(E) opIndex(size_t i) inout in (!empty) in (i == 0) @@ -675,11 +670,6 @@ private struct SingletonByRef(E) return typeof(this)(*this.element); } - auto save() const return - { - return SingletonByRef!(const E)(*this.element); - } - ref inout(E) opIndex(size_t i) inout return in (!empty) in (i == 0) diff --git a/source/tanya/math/random.d b/source/tanya/math/random.d index 80e9e12..8a58909 100644 --- a/source/tanya/math/random.d +++ b/source/tanya/math/random.d @@ -14,6 +14,7 @@ */ module tanya.math.random; +import std.typecons; import tanya.memory.allocator; import tanya.typecons; @@ -90,8 +91,8 @@ abstract class EntropySource * Postcondition: Returned length is less than or equal to * $(D_PARAM output) length. */ - Option!ubyte poll(out ubyte[maxGather] output) @nogc - out (length; length.isNothing || length.get <= maxGather); + Nullable!ubyte poll(out ubyte[maxGather] output) @nogc + out (length; length.isNull || length.get <= maxGather); } version (CRuntime_Bionic) @@ -151,12 +152,12 @@ version (linux) * Returns: Number of bytes that were copied to the $(D_PARAM output) * or nothing on error. */ - override Option!ubyte poll(out ubyte[maxGather] output) @nogc nothrow + override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow { // int getrandom(void *buf, size_t buflen, unsigned int flags); import mir.linux._asm.unistd : NR_getrandom; auto length = syscall(NR_getrandom, output.ptr, output.length, 0); - Option!ubyte ret; + Nullable!ubyte ret; if (length >= 0) { @@ -202,11 +203,11 @@ else version (SecureARC4Random) * Returns: Number of bytes that were copied to the $(D_PARAM output) * or nothing on error. */ - override Option!ubyte poll(out ubyte[maxGather] output) + override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow @safe { (() @trusted => arc4random_buf(output.ptr, output.length))(); - return Option!ubyte(cast(ubyte) (output.length)); + return Nullable!ubyte(cast(ubyte) (output.length)); } } } @@ -310,10 +311,10 @@ else version (Windows) * Returns: Number of bytes that were copied to the $(D_PARAM output) * or nothing on error. */ - override Option!ubyte poll(out ubyte[maxGather] output) + override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow @safe { - Option!ubyte ret; + Nullable!ubyte ret; assert(hProvider > 0, "hProvider not properly initialized"); if ((() @trusted => CryptGenRandom(hProvider, output.length, cast(PBYTE) output.ptr))()) diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d index 59d19d9..21e35d9 100644 --- a/source/tanya/net/ip.d +++ b/source/tanya/net/ip.d @@ -15,6 +15,7 @@ module tanya.net.ip; import std.algorithm.comparison; +import std.typecons; import tanya.algorithm.iteration; import tanya.algorithm.mutation; import tanya.container.string; @@ -242,7 +243,7 @@ struct Address4 * * Returns: $(D_PARAM output). */ - OR toString(OR)(OR output) const @nogc nothrow pure @safe + OR toString(OR)(OR output) const if (isOutputRange!(OR, const(char)[])) { const octets = (() @trusted => (cast(ubyte*) &this.address)[0 .. 4])(); @@ -324,10 +325,10 @@ struct Address4 * R = Input range type. * range = Stringish range containing the address. * - * Returns: $(D_PSYMBOL Option) containing the address if the parsing was + * Returns: $(D_PSYMBOL Nullable) containing the address if the parsing was * successful, or nothing otherwise. */ -Option!Address4 address4(R)(R range) +Nullable!Address4 address4(R)(R range) if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R) { Address4 result; @@ -370,10 +371,10 @@ if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R) * R = Input range type. * range = $(D_KEYWORD ubyte) range containing the address. * - * Returns: $(D_PSYMBOL Option) containing the address if the $(D_PARAM range) + * Returns: $(D_PSYMBOL Nullable) containing the address if the $(D_PARAM range) * contains exactly 4 bytes, or nothing otherwise. */ -Option!Address4 address4(R)(R range) +Nullable!Address4 address4(R)(R range) if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte)) { Address4 result; @@ -407,11 +408,11 @@ if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte)) } { ubyte[3] actual = [127, 0, 0]; - assert(address4(actual[]).isNothing); + assert(address4(actual[]).isNull); } { ubyte[5] actual = [127, 0, 0, 0, 1]; - assert(address4(actual[]).isNothing); + assert(address4(actual[]).isNull); } } @@ -795,10 +796,10 @@ in (digit < 16) * R = Input range type. * range = Stringish range containing the address. * - * Returns: $(D_PSYMBOL Option) containing the address if the parsing was + * Returns: $(D_PSYMBOL Nullable) containing the address if the parsing was * successful, or nothing otherwise. */ -Option!Address6 address6(R)(R range) +Nullable!Address6 address6(R)(R range) if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R) { if (range.empty) @@ -1006,10 +1007,10 @@ CopyTail: * range = $(D_KEYWORD ubyte) range containing the address. * scopeID = Scope ID. * - * Returns: $(D_PSYMBOL Option) containing the address if the $(D_PARAM range) + * Returns: $(D_PSYMBOL Nullable) containing the address if the $(D_PARAM range) * contains exactly 16 bytes, or nothing otherwise. */ -Option!Address6 address6(R)(R range, uint scopeID = 0) +Nullable!Address6 address6(R)(R range, uint scopeID = 0) if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte)) { Address6 result; @@ -1030,20 +1031,20 @@ if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte)) { ubyte[16] actual = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]; - assert(!address6(actual[]).isNothing); + assert(!address6(actual[]).isNull); } { ubyte[15] actual = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ]; - assert(address6(actual[]).isNothing); + assert(address6(actual[]).isNull); } { ubyte[17] actual = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 ]; - assert(address6(actual[]).isNothing); + assert(address6(actual[]).isNull); } { - assert(address6(cast(ubyte[]) []).isNothing); + assert(address6(cast(ubyte[]) []).isNull); } } diff --git a/source/tanya/typecons.d b/source/tanya/typecons.d index 984f809..255303a 100644 --- a/source/tanya/typecons.d +++ b/source/tanya/typecons.d @@ -153,274 +153,6 @@ template tuple(Names...) assert(t.two == 5); } -/** - * $(D_PSYMBOL Option) is a type that contains an optional value. - * - * Params: - * T = Type of the encapsulated value. - * - * See_Also: $(D_PSYMBOL option). - */ -struct Option(T) -{ - private bool isNothing_ = true; - private T value = void; - - /** - * Constructs a new option with $(D_PARAM value). - * - * Params: - * value = Encapsulated value. - */ - this()(ref T value) - { - this.value = value; - this.isNothing_ = false; - } - - /// ditto - this()(T value) @trusted - { - moveEmplace(value, this.value); - this.isNothing_ = false; - } - - /** - * Tells if the option is just a value or nothing. - * - * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) contains a nothing, - * $(D_KEYWORD false) if it contains a value. - */ - @property bool isNothing() const - { - return this.isNothing_; - } - - /** - * Returns the encapsulated value. - * - * Returns: Value encapsulated in this $(D_PSYMBOL Option). - * - * See_Also: $(D_PSYMBOL or). - * - * Precondition: `!isNothing`. - */ - @property ref inout(T) get() inout - in (!isNothing, "Option is nothing") - { - return this.value; - } - - /** - * Returns the encapsulated value if available or a default value - * otherwise. - * - * Note that the contained value can be returned by reference only if the - * default value is passed by reference as well. - * - * Params: - * U = Type of the default value. - * defaultValue = Default value. - * - * Returns: The value of this $(D_PSYMBOL Option) if available, - * $(D_PARAM defaultValue) otherwise. - * - * See_Also: $(D_PSYMBOL isNothing), $(D_PSYMBOL get). - */ - @property U or(U)(U defaultValue) inout - if (is(U == T) && isCopyable!T) - { - return isNothing ? defaultValue : this.value; - } - - /// ditto - @property ref inout(T) or(ref inout(T) defaultValue) inout - { - return isNothing ? defaultValue : this.value; - } - - /** - * Casts this $(D_PSYMBOL Option) to $(D_KEYWORD bool). - * - * An $(D_PSYMBOL Option) is $(D_KEYWORD true) if it contains a value, - * ($D_KEYWORD false) if it contains nothing. - * - * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) contains a value, - * ($D_KEYWORD false) if it contains nothing. - */ - bool opCast(U : bool)() - { - return !isNothing; - } - - /** - * Compares this $(D_PSYMBOL Option) with $(D_PARAM that). - * - * If both objects are options of the same type and they don't contain a - * value, they are considered equal. If only one of them contains a value, - * they aren't equal. Otherwise, the encapsulated values are compared for - * equality. - * - * If $(D_PARAM U) is a type comparable with the type encapsulated by this - * $(D_PSYMBOL Option), the value of this $(D_PSYMBOL Option) is compared - * with $(D_PARAM that), this $(D_PSYMBOL Option) must have a value then. - * - * Params: - * U = Type of the object to compare with. - * that = Object to compare with. - * - * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) and - * $(D_PARAM that) are equal, $(D_KEYWORD false) if not. - * - * Precondition: `!isNothing` if $(D_PARAM U) is equality comparable with - * $(D_PARAM T). - */ - bool opEquals(U)(auto ref const U that) const - if (is(U == Option)) - { - if (!isNothing && !that.isNothing) - { - return this.value == that.value; - } - return isNothing == that.isNothing; - } - - /// ditto - bool opEquals(U)(auto ref const U that) const - if (ifTestable!(U, a => a == T.init) && !is(U == Option)) - in (!isNothing) - { - return get == that; - } - - /** - * Resets this $(D_PSYMBOL Option) and destroys the contained value. - * - * $(D_PSYMBOL reset) can be safely called on an $(D_PSYMBOL Option) that - * doesn't contain any value. - */ - void reset() - { - static if (hasElaborateDestructor!T) - { - destroy(this.value); - } - this.isNothing_ = true; - } - - /** - * Assigns a new value. - * - * Params: - * U = Type of the new value. - * that = New value. - * - * Returns: $(D_KEYWORD this). - */ - ref typeof(this) opAssign(U)(ref U that) - if (is(U : T) && !is(U == Option)) - { - this.value = that; - this.isNothing_ = false; - return this; - } - - /// ditto - ref typeof(this) opAssign(U)(U that) - if (is(U == T)) - { - move(that, this.value); - this.isNothing_ = false; - return this; - } - - /// ditto - ref typeof(this) opAssign(U)(ref U that) - if (is(U == Option)) - { - if (that.isNothing) - { - reset(); - } - else - { - this.value = that.get; - this.isNothing_ = false; - } - return this; - } - - /// ditto - ref typeof(this) opAssign(U)(U that) - if (is(U == Option)) - { - move(that.value, this.value); - this.isNothing_ = that.isNothing_; - return this; - } - - version (D_Ddoc) - { - /** - * If $(D_PARAM T) has a `toHash()` method, $(D_PSYMBOL Option) defines - * `toHash()` which returns `T.toHash()` if it is set or 0 otherwise. - * - * Returns: Hash value. - */ - size_t toHash() const; - } - else static if (is(typeof(T.init.toHash()) == size_t)) - { - size_t toHash() const - { - return isNothing ? 0U : this.value.toHash(); - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - Option!int option; - assert(option.isNothing); - assert(option.or(8) == 8); - - option = 5; - assert(!option.isNothing); - assert(option.get == 5); - assert(option.or(8) == 5); - - option.reset(); - assert(option.isNothing); -} - -/** - * Creates a new $(D_PSYMBOL Option). - * - * Params: - * T = Option type. - * value = Initial value. - * - * See_Also: $(D_PSYMBOL Option). - */ -Option!T option(T)(auto ref T value) -{ - return Option!T(forward!value); -} - -/// ditto -Option!T option(T)() -{ - return Option!T(); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(option!int().isNothing); - assert(option(5) == 5); -} - /** * Type that can hold one of the types listed as its template parameters. * diff --git a/tests/tanya/net/tests/ip.d b/tests/tanya/net/tests/ip.d index 893a709..3c3dc0c 100644 --- a/tests/tanya/net/tests/ip.d +++ b/tests/tanya/net/tests/ip.d @@ -9,17 +9,17 @@ import tanya.range; // Rejects malformed addresses @nogc nothrow pure @safe unittest { - assert(address4("256.0.0.1").isNothing); - assert(address4(".0.0.1").isNothing); - assert(address4("0..0.1").isNothing); - assert(address4("0.0.0.").isNothing); - assert(address4("0.0.").isNothing); - assert(address4("").isNothing); + assert(address4("256.0.0.1").isNull); + assert(address4(".0.0.1").isNull); + assert(address4("0..0.1").isNull); + assert(address4("0.0.0.").isNull); + assert(address4("0.0.").isNull); + assert(address4("").isNull); } @nogc nothrow pure @safe unittest { - assert(address4(cast(ubyte[]) []).isNothing); + assert(address4(cast(ubyte[]) []).isNull); } // Assignment and comparison works @@ -106,12 +106,12 @@ import tanya.range; // Rejects malformed addresses @nogc nothrow @safe unittest { - assert(address6("").isNothing); - assert(address6(":").isNothing); - assert(address6(":a").isNothing); - assert(address6("a:").isNothing); - assert(address6("1:2:3:4::6:").isNothing); - assert(address6("fe80:2:3:4::6:7:8%").isNothing); + assert(address6("").isNull); + assert(address6(":").isNull); + assert(address6(":a").isNull); + assert(address6("a:").isNull); + assert(address6("1:2:3:4::6:").isNull); + assert(address6("fe80:2:3:4::6:7:8%").isNull); } // Parses embedded IPv4 address @@ -138,9 +138,9 @@ import tanya.range; @nogc nothrow @safe unittest { - assert(address6("0:0:0:0:0:0:1.2.3.").isNothing); - assert(address6("0:0:0:0:0:0:1.2:3.4").isNothing); - assert(address6("0:0:0:0:0:0:1.2.3.4.").isNothing); + assert(address6("0:0:0:0:0:0:1.2.3.").isNull); + assert(address6("0:0:0:0:0:0:1.2:3.4").isNull); + assert(address6("0:0:0:0:0:0:1.2.3.4.").isNull); assert(address6("fe80:0:0:0:0:0:1.2.3.4%1").get.scopeID == 1); } diff --git a/tests/tanya/tests/typecons.d b/tests/tanya/tests/typecons.d index c49c266..4765f77 100644 --- a/tests/tanya/tests/typecons.d +++ b/tests/tanya/tests/typecons.d @@ -22,120 +22,6 @@ import tanya.typecons; static assert(!is(Tuple!(int, "first", double, "second", char, "third"))); } -// Assigns a new value -@nogc nothrow pure @safe unittest -{ - Option!int option = 5; - option = 8; - assert(!option.isNothing); - assert(option == 8); -} - -@nogc nothrow pure @safe unittest -{ - Option!int option; - const int newValue = 8; - assert(option.isNothing); - option = newValue; - assert(!option.isNothing); - assert(option == newValue); -} - -@nogc nothrow pure @safe unittest -{ - Option!int option1; - Option!int option2 = 5; - assert(option1.isNothing); - option1 = option2; - assert(!option1.isNothing); - assert(option1.get == 5); -} - -// Constructs with a value passed by reference -@nogc nothrow pure @safe unittest -{ - int i = 5; - assert(Option!int(i).get == 5); -} - -// Moving -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof(Option!NonCopyable(NonCopyable())))); - // The value cannot be returned by reference because the default value - // isn't passed by reference - static assert(!is(typeof(Option!DisabledPostblit().or(NonCopyable())))); -} - -@nogc nothrow pure @safe unittest -{ - NonCopyable notCopyable; - static assert(is(typeof(Option!NonCopyable().or(notCopyable)))); -} - -@nogc nothrow pure @safe unittest -{ - Option!NonCopyable option; - assert(option.isNothing); - option = NonCopyable(); - assert(!option.isNothing); -} - -@nogc nothrow pure @safe unittest -{ - Option!NonCopyable option; - assert(option.isNothing); - option = Option!NonCopyable(NonCopyable()); - assert(!option.isNothing); -} - -// Cast to bool is done before touching the encapsulated value -@nogc nothrow pure @safe unittest -{ - assert(Option!bool(false)); -} - -// Option can be const -@nogc nothrow pure @safe unittest -{ - assert((const Option!int(5)).get == 5); - assert((const Option!int()).or(5) == 5); -} - -// Equality -@nogc nothrow pure @safe unittest -{ - assert(Option!int() == Option!int()); - assert(Option!int(0) != Option!int()); - assert(Option!int(5) == Option!int(5)); - assert(Option!int(5) == 5); - assert(Option!int(5) == cast(ubyte) 5); -} - -// Returns default value -@nogc nothrow pure @safe unittest -{ - int i = 5; - assert(((ref e) => e)(Option!int().or(i)) == 5); -} - -// Implements toHash() for nothing -@nogc nothrow pure @safe unittest -{ - alias OptionT = Option!Hashable; - assert(OptionT().toHash() == 0U); - assert(OptionT(Hashable(1U)).toHash() == 1U); -} - -// Can assign Option that is nothing -@nogc nothrow pure @safe unittest -{ - auto option1 = Option!int(5); - Option!int option2; - option1 = option2; - assert(option1.isNothing); -} - @nogc nothrow pure @safe unittest { Variant!(int, double) variant;