From 1f02ba5042d5c84740623df25ebc82636b4a0f8d Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 24 Jan 2019 07:14:15 +0100 Subject: [PATCH] net.ip: Add Address4 and Address6 wrapper --- source/tanya/net/ip.d | 156 ++++++++++++++++++++++++++++++++- source/tanya/typecons.d | 189 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 343 insertions(+), 2 deletions(-) diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d index e8093be..bf0c349 100644 --- a/source/tanya/net/ip.d +++ b/source/tanya/net/ip.d @@ -5,7 +5,7 @@ /** * Internet Protocol implementation. * - * Copyright: Eugene Wissner 2018. + * Copyright: Eugene Wissner 2018-2019. * 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) @@ -1041,3 +1041,157 @@ if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte)) assert(address6(cast(ubyte[]) []).isNothing); } } + +/** + * Address storage, that can hold either an IPv4 or IPv6 address. + */ +struct Address +{ + private Variant!(Address4, Address6) address; + + /** + * Initializes the addres with an IPv4 address. + * + * Params: + * address = IPv6 address. + */ + this(Address4 address) @nogc nothrow pure @safe + { + this.address = address; + } + + /** + * Initializes the addres with an IPv4 address. + * + * Params: + * address = IPv6 address. + */ + this(Address6 address) @nogc nothrow pure @safe + { + this.address = address; + } + + /** + * Determines whether this is an IPv4 address. + * + * Returns: $(D_KEYWORD true) if this is an IPv4 address, + * $(D_KEYWORD false) otherwise. + */ + bool isV4() const @nogc nothrow pure @safe + { + return this.address.peek!Address4; + } + + /** + * Determines whether this is an IPv6 address. + * + * Returns: $(D_KEYWORD true) if this is an IPv6 address, + * $(D_KEYWORD false) otherwise. + */ + bool isV6() const @nogc nothrow pure @safe + { + return this.address.peek!Address6; + } + + /** + * Get the address as an IPv4 address. + * + * This method doesn't convert the address, so the address should be + * already an IPv4 one. + * + * Returns: IPv4 address. + * + * Precondition: This is an IPv4 address. + */ + Address4 toV4() const @nogc nothrow pure @safe + in (this.address.peek!Address4) + { + return this.address.get!Address4; + } + + /** + * Get the address as an IPv6 address. + * + * This method doesn't convert the address, so the address should be + * already an IPv6 one. + * + * Returns: IPv6 address. + * + * Precondition: This is an IPv6 address. + */ + Address6 toV6() const @nogc nothrow pure @safe + in (this.address.peek!Address6) + { + return this.address.get!Address6; + } + + /** + * Determines whether this is a loopback address. + * + * Returns: $(D_KEYWORD true) if this is a loopback address, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL Address4.loopback), + * $(D_PSYMBOL Address6.loopback). + */ + bool isLoopback() const @nogc nothrow pure @safe + in (this.address.hasValue) + { + if (this.address.peek!Address4) + { + return this.address.get!Address4.isLoopback(); + } + return this.address.get!Address6.isLoopback(); + } + + /// + @nogc nothrow pure @safe unittest + { + assert(Address(Address4.loopback()).isLoopback()); + assert(Address(Address6.loopback()).isLoopback()); + } + + /** + * Determines whether this address' destination is a group of endpoints. + * + * Returns: $(D_KEYWORD true) if this is a multicast address, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL Address4.isMulticast), + * $(D_PSYMBOL Address6.isMulticast). + */ + bool isMulticast() const @nogc nothrow pure @safe + in (this.address.hasValue) + { + if (this.address.peek!Address4) + { + return this.address.get!Address4.isMulticast(); + } + return this.address.get!Address6.isMulticast(); + } + + /** + * Determines whether this is an unspecified address. + * + * Returns: $(D_KEYWORD true) if this is an unspecified address, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL Address4.isAny), $(D_PSYMBOL Address6.isAny). + */ + bool isAny() const @nogc nothrow pure @safe + in (this.address.hasValue) + { + if (this.address.peek!Address4) + { + return this.address.get!Address4.isAny(); + } + return this.address.get!Address6.isAny(); + } + + /// + @nogc nothrow pure @safe unittest + { + assert(Address(Address4.any()).isAny()); + assert(Address(Address6.any()).isAny()); + } +} diff --git a/source/tanya/typecons.d b/source/tanya/typecons.d index 5904a7a..64b2fef 100644 --- a/source/tanya/typecons.d +++ b/source/tanya/typecons.d @@ -8,7 +8,7 @@ * This module contains templates that allow to build new types from the * available ones. * - * Copyright: Eugene Wissner 2017-2018. + * Copyright: Eugene Wissner 2017-2019. * 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) @@ -541,3 +541,190 @@ Option!T option(T)() assert(option!int().isNothing); assert(option(5) == 5); } + +private struct VariantAccessorInfo +{ + string accessor; + size_t tag; +} + +/** + * Tagged union. + * + * Params: + * Specs = Types of the union members. + */ +template Variant(Specs...) +if (isTypeTuple!Specs) +{ + union AlignedUnion(Args...) + { + static if (Args.length > 0) + { + Args[0] value; + } + static if (Args.length > 1) + { + AlignedUnion!(Args[1 .. $]) rest; + } + } + + template accessor(T, Union) + { + enum VariantAccessorInfo info = accessorImpl!(T, Union, 1); + enum accessor = VariantAccessorInfo("this.values" ~ info.accessor, info.tag); + } + + template accessorImpl(T, Union, size_t tag) + { + static if (is(T == typeof(Union.value))) + { + enum accessorImpl = VariantAccessorInfo(".value", tag); + } + else + { + enum VariantAccessorInfo info = accessorImpl!(T, typeof(Union.rest), tag + 1); + enum accessorImpl = VariantAccessorInfo(".rest" ~ info.accessor, info.tag); + } + } + + struct Variant + { + /// Types can be present in this $(D_PSYMBOL Variant). + alias Types = Specs; + + private ptrdiff_t tag = -1; + private AlignedUnion!Types values; + + this(T)(auto ref T value) + if (canFind!(T, Types)) + { + opAssign!T(forward!value); + } + + bool hasValue() const + { + return this.tag != -1; + } + + bool peek(T)() const + if (canFind!(T, Types)) + { + return this.tag == staticIndexOf!(T, Types); + } + + ref inout(T) get(T)() inout + if (canFind!(T, Types)) + in (this.tag == staticIndexOf!(T, Types), "Variant isn't initialized") + { + mixin("return " ~ accessor!(T, AlignedUnion!Types).accessor ~ ";"); + } + + typeof(this) opAssign(T)(auto ref T value) + if (canFind!(T, Types)) + { + this.tag = staticIndexOf!(T, Types); + mixin(accessor!(T, AlignedUnion!Types).accessor ~ " = forward!value;"); + return this; + } + + TypeInfo type() + { + static foreach (i, Type; Types) + { + if (this.tag == i) + { + return typeid(Type); + } + } + assert(false, "Variant isn't initialized"); + } + + bool opEquals()(auto ref inout Variant that) inout + { + if (this.tag != that.tag) + { + return false; + } + static foreach (i, Type; Types) + { + if (this.tag == i) + { + return get!Type == that.get!Type; + } + } + return true; + } + } +} + +@nogc nothrow pure @safe unittest +{ + Variant!(int, double) variant; + variant = 5; + assert(variant.peek!int); +} + +@nogc nothrow pure @safe unittest +{ + Variant!(int, double) variant; + variant = 5.0; + assert(!variant.peek!int); +} + +@nogc nothrow pure @safe unittest +{ + Variant!(int, double) variant = 5; + assert(variant.get!int == 5); +} + +@nogc nothrow pure @safe unittest +{ + static assert(is(Variant!(int, float))); + static assert(is(Variant!int)); +} + +@nogc nothrow pure @safe unittest +{ + static struct WithDestructorAndCopy + { + this(this) @nogc nothrow pure @safe + { + } + + ~this() @nogc nothrow pure @safe + { + } + } + static assert(is(Variant!WithDestructorAndCopy)); +} + +// Equality compares the underlying objects +@nogc nothrow pure @safe unittest +{ + Variant!(int, double) variant1 = 5; + Variant!(int, double) variant2 = 5; + assert(variant1 == variant2); +} + +@nogc nothrow pure @safe unittest +{ + Variant!(int, double) variant1 = 5; + Variant!(int, double) variant2 = 6; + assert(variant1 != variant2); +} + +// Differently typed variants aren't equal +@nogc nothrow pure @safe unittest +{ + Variant!(int, double) variant1 = 5; + Variant!(int, double) variant2 = 5.0; + assert(variant1 != variant2); +} + +// Uninitialized variants are equal +@nogc nothrow pure @safe unittest +{ + Variant!(int, double) variant1, variant2; + assert(variant1 == variant2); +}