net.ip: Add Address4 and Address6 wrapper
This commit is contained in:
parent
50aaa170fb
commit
1f02ba5042
@ -5,7 +5,7 @@
|
|||||||
/**
|
/**
|
||||||
* Internet Protocol implementation.
|
* Internet Protocol implementation.
|
||||||
*
|
*
|
||||||
* Copyright: Eugene Wissner 2018.
|
* Copyright: Eugene Wissner 2018-2019.
|
||||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||||
* Mozilla Public License, v. 2.0).
|
* Mozilla Public License, v. 2.0).
|
||||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
* 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);
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* This module contains templates that allow to build new types from the
|
* This module contains templates that allow to build new types from the
|
||||||
* available ones.
|
* available ones.
|
||||||
*
|
*
|
||||||
* Copyright: Eugene Wissner 2017-2018.
|
* Copyright: Eugene Wissner 2017-2019.
|
||||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||||
* Mozilla Public License, v. 2.0).
|
* Mozilla Public License, v. 2.0).
|
||||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||||
@ -541,3 +541,190 @@ Option!T option(T)()
|
|||||||
assert(option!int().isNothing);
|
assert(option!int().isNothing);
|
||||||
assert(option(5) == 5);
|
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);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user