Implement IPv6 parser, fix #49
This commit is contained in:
		@@ -17,9 +17,11 @@ module tanya.net.ip;
 | 
				
			|||||||
import tanya.algorithm.mutation;
 | 
					import tanya.algorithm.mutation;
 | 
				
			||||||
import tanya.container.string;
 | 
					import tanya.container.string;
 | 
				
			||||||
import tanya.conv;
 | 
					import tanya.conv;
 | 
				
			||||||
 | 
					import tanya.encoding.ascii;
 | 
				
			||||||
import tanya.format;
 | 
					import tanya.format;
 | 
				
			||||||
import tanya.meta.trait;
 | 
					import tanya.meta.trait;
 | 
				
			||||||
import tanya.meta.transform;
 | 
					import tanya.meta.transform;
 | 
				
			||||||
 | 
					import tanya.net.iface;
 | 
				
			||||||
import tanya.net.inet;
 | 
					import tanya.net.inet;
 | 
				
			||||||
import tanya.range;
 | 
					import tanya.range;
 | 
				
			||||||
import tanya.typecons;
 | 
					import tanya.typecons;
 | 
				
			||||||
@@ -158,7 +160,7 @@ struct Address4
 | 
				
			|||||||
     * Returns: $(D_KEYWORD true) if this is a multicast address,
 | 
					     * Returns: $(D_KEYWORD true) if this is a multicast address,
 | 
				
			||||||
     *          $(D_KEYWORD false) otherwise.
 | 
					     *          $(D_KEYWORD false) otherwise.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * See_Also: $(D_PSYMBOL isMulticast).
 | 
					     * See_Also: $(D_PSYMBOL isUnicast).
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    bool isMulticast() const @nogc nothrow pure @safe
 | 
					    bool isMulticast() const @nogc nothrow pure @safe
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -274,7 +276,7 @@ struct Address4
 | 
				
			|||||||
 *          successful, or nothing otherwise.
 | 
					 *          successful, or nothing otherwise.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
Option!Address4 address4(R)(R range)
 | 
					Option!Address4 address4(R)(R range)
 | 
				
			||||||
if (isInputRange!R && isSomeChar!(ElementType!R))
 | 
					if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Address4 result;
 | 
					    Address4 result;
 | 
				
			||||||
    version (LittleEndian)
 | 
					    version (LittleEndian)
 | 
				
			||||||
@@ -394,16 +396,237 @@ struct Address6
 | 
				
			|||||||
    // Raw bytes
 | 
					    // Raw bytes
 | 
				
			||||||
    private ubyte[16] address;
 | 
					    private ubyte[16] address;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Scope ID.
 | 
				
			||||||
 | 
					    uint scopeID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Constructs an $(D_PSYMBOL Address6) from an unsigned integer in host
 | 
					     * Constructs an $(D_PSYMBOL Address6) from an array containing raw bytes
 | 
				
			||||||
     * byte order.
 | 
					     * in network byte order and scope ID.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * Params:
 | 
					     * Params:
 | 
				
			||||||
     *  address = The address as an unsigned integer in host byte order.
 | 
					     *  address = The address as an unsigned integer in host byte order.
 | 
				
			||||||
 | 
					     *  scopeID = Scope ID.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this(ulong address)
 | 
					    this(ubyte[16] address, uint scopeID = 0) @nogc nothrow pure @safe
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        copy(NetworkOrder!8(address), this.address[]);
 | 
					        copy(address[], this.address[]);
 | 
				
			||||||
 | 
					        this.scopeID = scopeID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow pure @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const ubyte[16] expected = [ 0, 1, 0, 2, 0, 3, 0, 4,
 | 
				
			||||||
 | 
					                                     0, 5, 0, 6, 0, 7, 0, 8 ];
 | 
				
			||||||
 | 
					        auto actual = Address6(expected, 1);
 | 
				
			||||||
 | 
					        assert(actual.toBytes() == expected);
 | 
				
			||||||
 | 
					        assert(actual.scopeID == 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns object that represents ::.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: Object that represents any address.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static Address6 any() @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Address6();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow pure @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(Address6.any().isAny());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns object that represents ::1.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: Object that represents the Loopback address.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static Address6 loopback() @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        typeof(return) address;
 | 
				
			||||||
 | 
					        address.address[$ - 1] = 1;
 | 
				
			||||||
 | 
					        return address;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow pure @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(Address6.loopback().isLoopback());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * :: can represent any address. This function checks whether this
 | 
				
			||||||
 | 
					     * address is ::.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: $(D_KEYWORD true) if this is an unspecified address,
 | 
				
			||||||
 | 
					     *          $(D_KEYWORD false) otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    bool isAny() const @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return this.address == any.address;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(address6("::").isAny());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Loopback address is ::1.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: $(D_KEYWORD true) if this is a loopback address,
 | 
				
			||||||
 | 
					     *          $(D_KEYWORD false) otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    bool isLoopback() const @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return this.address == loopback.address;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(address6("::1").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 isUnicast).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    bool isMulticast() const @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return this.address[0] == 0xff;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(address6("ff00::").isMulticast());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Determines whether this address' destination is a single endpoint.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: $(D_KEYWORD true) if this is a multicast address,
 | 
				
			||||||
 | 
					     *          $(D_KEYWORD false) otherwise.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * See_Also: $(D_PSYMBOL isMulticast).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    bool isUnicast() const @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return !isMulticast();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(address6("::1").isUnicast());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Determines whether this address is a link-local unicast address.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: $(D_KEYWORD true) if this is a link-local address,
 | 
				
			||||||
 | 
					     *          $(D_KEYWORD false) otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    bool isLinkLocal() const @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return this.address[0] == 0xfe && (this.address[1] & 0xc0) == 0x80;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(address6("fe80::1").isLinkLocal());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Determines whether this address is an Unique Local Address (ULA).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: $(D_KEYWORD true) if this is an Unique Local Address,
 | 
				
			||||||
 | 
					     *          $(D_KEYWORD false) otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    bool isUniqueLocal() const @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return this.address[0] == 0xfc || this.address[0] == 0xfd;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(address6("fd80:124e:34f3::1").isUniqueLocal());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns text representation of this address.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: text representation of this address.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String stringify() const @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        String output;
 | 
				
			||||||
 | 
					        foreach (i, b; this.address)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ubyte low = b & 0xf;
 | 
				
			||||||
 | 
					            ubyte high = b >> 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (high < 10)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                output.insertBack(cast(char) (high + '0'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                output.insertBack(cast(char) (high - 10 + 'a'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (low < 10)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                output.insertBack(cast(char) (low + '0'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                output.insertBack(cast(char) (low - 10 + 'a'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (i % 2 != 0 && i != (this.address.length - 1))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                output.insertBack(':');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return output;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        import tanya.algorithm.comparison : equal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert(equal(address6("1:2:3:4:5:6:7:8").stringify()[],
 | 
				
			||||||
 | 
					               "0001:0002:0003:0004:0005:0006:0007:0008"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Produces a byte array containing this address in network byte order.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Returns: This address as raw bytes in network byte order.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ubyte[16] toBytes() const @nogc nothrow pure @safe
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return this.address;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    @nogc nothrow @safe unittest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        auto actual = address6("1:2:3:4:5:6:7:8");
 | 
				
			||||||
 | 
					        ubyte[16] expected = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8];
 | 
				
			||||||
 | 
					        assert(actual.toBytes() == expected);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -417,6 +640,25 @@ private void write2Bytes(R)(ref R range, ubyte[] address)
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Parses a string containing an IPv6 address.
 | 
					 * Parses a string containing an IPv6 address.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * This function isn't pure since an IPv6 address can contain interface name
 | 
				
			||||||
 | 
					 * or interface ID (separated from the address by `%`). If an interface name
 | 
				
			||||||
 | 
					 * is specified (i.e. first character after `%` is not a digit), the parser
 | 
				
			||||||
 | 
					 * tries to convert it to the ID of that interface. If the interface with the
 | 
				
			||||||
 | 
					 * given name can't be found, the parser doesn't fail, but just ignores the
 | 
				
			||||||
 | 
					 * invalid interface name.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * If an ID is given (i.e. first character after `%` is a digit),
 | 
				
			||||||
 | 
					 * $(D_PSYMBOL address6) just stores it in $(D_PSYMBOL Address6.scopeID) without
 | 
				
			||||||
 | 
					 * checking whether an interface with this ID really exists. If the ID is
 | 
				
			||||||
 | 
					 * invalid (if it is too long or contains non decimal characters), parsing
 | 
				
			||||||
 | 
					 * and nothing is returned.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * If neither an ID nor a name is given, $(D_PSYMBOL Address6.scopeID) is set
 | 
				
			||||||
 | 
					 * to `0`.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The parser doesn't support notation with an embedded IPv4 address (e.g.
 | 
				
			||||||
 | 
					 * ::1.2.3.4).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * Params:
 | 
					 * Params:
 | 
				
			||||||
 *  R     = Input range type.
 | 
					 *  R     = Input range type.
 | 
				
			||||||
 *  range = Stringish range containing the address.
 | 
					 *  range = Stringish range containing the address.
 | 
				
			||||||
@@ -425,15 +667,19 @@ private void write2Bytes(R)(ref R range, ubyte[] address)
 | 
				
			|||||||
 *          successful, or nothing otherwise.
 | 
					 *          successful, or nothing otherwise.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
Option!Address6 address6(R)(R range)
 | 
					Option!Address6 address6(R)(R range)
 | 
				
			||||||
if (isInputRange!R && isSomeChar!(ElementType!R))
 | 
					if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (range.empty)
 | 
					    if (range.empty)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return typeof(return)();
 | 
					        return typeof(return)();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Address6 result;
 | 
					    Address6 result;
 | 
				
			||||||
 | 
					    ubyte[12] tail;
 | 
				
			||||||
    size_t i;
 | 
					    size_t i;
 | 
				
			||||||
 | 
					    size_t j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // An address begins with a number, not ':'. But there is a special case
 | 
				
			||||||
 | 
					    // if the address begins with '::'.
 | 
				
			||||||
    if (range.front == ':')
 | 
					    if (range.front == ':')
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        range.popFront();
 | 
					        range.popFront();
 | 
				
			||||||
@@ -445,6 +691,8 @@ if (isInputRange!R && isSomeChar!(ElementType!R))
 | 
				
			|||||||
        goto ParseTail;
 | 
					        goto ParseTail;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Parse the address before '::'.
 | 
				
			||||||
 | 
					    // This loop parses the whole address if it doesn't contain '::'.
 | 
				
			||||||
    for (; i < 13; i += 2)
 | 
					    for (; i < 13; i += 2)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        write2Bytes(range, result.address[i .. $]);
 | 
					        write2Bytes(range, result.address[i .. $]);
 | 
				
			||||||
@@ -465,15 +713,50 @@ if (isInputRange!R && isSomeChar!(ElementType!R))
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    write2Bytes(range, result.address[14 .. $]);
 | 
					    write2Bytes(range, result.address[14 .. $]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return range.empty ? typeof(return)(result) : typeof(return)();
 | 
					    if (range.empty)
 | 
				
			||||||
 | 
					 | 
				
			||||||
ParseTail:
 | 
					 | 
				
			||||||
    ubyte[12] tail;
 | 
					 | 
				
			||||||
    size_t j;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (; !range.empty; i += 2, j += 2, range.popFront())
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (i > 11 || range.front == ':')
 | 
					        return typeof(return)(result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (range.front == '%')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        goto ParseIface;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return typeof(return)();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ParseTail: // after ::
 | 
				
			||||||
 | 
					    // Normally the address can't end with ':', but a special case is if the
 | 
				
			||||||
 | 
					    // address ends with '::'. So the first iteration of the loop below is
 | 
				
			||||||
 | 
					    // unrolled to check whether the address contains something after '::' at
 | 
				
			||||||
 | 
					    // all.
 | 
				
			||||||
 | 
					    if (range.empty)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return typeof(return)(result); // ends with ::
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (range.front == ':')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return typeof(return)();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    write2Bytes(range, tail[j .. $]);
 | 
				
			||||||
 | 
					    if (range.empty)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        goto CopyTail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (range.front == '%')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        goto ParseIface;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (range.front != ':')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return typeof(return)();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    range.popFront();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i = 2, j = 2; i <= 11; i += 2, j += 2, range.popFront())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (range.empty || range.front == ':')
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return typeof(return)();
 | 
					            return typeof(return)();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -481,19 +764,47 @@ ParseTail:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (range.empty)
 | 
					        if (range.empty)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            break;
 | 
					            goto CopyTail;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (range.front != ':')
 | 
					        else if (range.front == '%')
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            goto ParseIface;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (range.front != ':')
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return typeof(return)();
 | 
					            return typeof(return)();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    copy(tail[0 .. j + 2], result.address[$ - j - 2 .. $]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ParseIface: // Scope name or ID
 | 
				
			||||||
 | 
					    range.popFront();
 | 
				
			||||||
 | 
					    if (range.empty)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return typeof(return)();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (isDigit(range.front))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const scopeID = readIntegral!uint(range);
 | 
				
			||||||
 | 
					        if (range.empty)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            result.scopeID = scopeID;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return typeof(return)();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        result.scopeID = nameToIndex(range);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CopyTail:
 | 
				
			||||||
 | 
					    copy(tail[0 .. j + 2], result.address[$ - j - 2 .. $]);
 | 
				
			||||||
    return typeof(return)(result);
 | 
					    return typeof(return)(result);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@nogc nothrow pure @safe unittest
 | 
					@nogc nothrow @safe unittest
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ubyte[16] expected = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8];
 | 
					        ubyte[16] expected = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8];
 | 
				
			||||||
@@ -518,10 +829,62 @@ ParseTail:
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Rejects malformed addresses
 | 
					// Rejects malformed addresses
 | 
				
			||||||
@nogc nothrow pure @safe unittest
 | 
					@nogc nothrow @safe unittest
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    assert(address6("").isNothing);
 | 
					    assert(address6("").isNothing);
 | 
				
			||||||
    assert(address6(":").isNothing);
 | 
					    assert(address6(":").isNothing);
 | 
				
			||||||
    assert(address6(":a").isNothing);
 | 
					    assert(address6(":a").isNothing);
 | 
				
			||||||
    assert(address6("a:").isNothing);
 | 
					    assert(address6("a:").isNothing);
 | 
				
			||||||
 | 
					    assert(address6("1:2:3:4::6:").isNothing);
 | 
				
			||||||
 | 
					    assert(address6("1:2:3:4::6:7:8%").isNothing);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Constructs an $(D_PSYMBOL Address6) from raw bytes in network byte order and
 | 
				
			||||||
 | 
					 * the scope ID.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Params:
 | 
				
			||||||
 | 
					 *  R       = Input range type.
 | 
				
			||||||
 | 
					 *  range   = $(D_KEYWORD ubyte) range containing the address.
 | 
				
			||||||
 | 
					 *  scopeID = Scope ID.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns: $(D_PSYMBOL Option) containing the address if the $(D_PARAM range)
 | 
				
			||||||
 | 
					 *          contains exactly 16 bytes, or nothing otherwise.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					Option!Address6 address6(R)(R range, uint scopeID = 0)
 | 
				
			||||||
 | 
					if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte))
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Address6 result;
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (; i < 16 && !range.empty; ++i, range.popFront())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        result.address[i] = range.front;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    result.scopeID = scopeID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return range.empty && i == 16 ? typeof(return)(result) : typeof(return)();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					@nogc nothrow pure @safe unittest
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ubyte[16] actual = [ 1, 2, 3, 4, 5, 6, 7, 8,
 | 
				
			||||||
 | 
					                             9, 10, 11, 12, 13, 14, 15, 16 ];
 | 
				
			||||||
 | 
					        assert(!address6(actual[]).isNothing);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ubyte[15] actual = [ 1, 2, 3, 4, 5, 6, 7, 8,
 | 
				
			||||||
 | 
					                             9, 10, 11, 12, 13, 14, 15 ];
 | 
				
			||||||
 | 
					        assert(address6(actual[]).isNothing);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        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(cast(ubyte[]) []).isNothing);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user