summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2019-01-24 07:14:15 +0100
committerEugen Wissner <belka@caraus.de>2019-01-24 07:14:15 +0100
commit1f02ba5042d5c84740623df25ebc82636b4a0f8d (patch)
treebc261ae90dd8c4f40abdef4fb72d69cf090d7811
parent50aaa170fbd27f549eec27d782e6e51474b1ba06 (diff)
downloadtanya-1f02ba5042d5c84740623df25ebc82636b4a0f8d.tar.gz
net.ip: Add Address4 and Address6 wrapper
-rw-r--r--source/tanya/net/ip.d156
-rw-r--r--source/tanya/typecons.d189
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);
+}