net.ip.Address6.toString() recommended notation

Fix #65.
This commit is contained in:
Eugen Wissner 2019-03-01 08:28:36 +01:00
parent 8fd0452cd0
commit d7dfa3f6f1

View File

@ -15,6 +15,7 @@
module tanya.net.ip; module tanya.net.ip;
import tanya.algorithm.comparison; import tanya.algorithm.comparison;
import tanya.algorithm.iteration;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.container.string; import tanya.container.string;
import tanya.conv; import tanya.conv;
@ -683,33 +684,8 @@ struct Address6
String stringify() const @nogc nothrow pure @safe String stringify() const @nogc nothrow pure @safe
{ {
String output; 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(':');
}
}
toString(backInserter(output));
return output; return output;
} }
@ -725,32 +701,59 @@ struct Address6
OR toString(OR)(OR output) const OR toString(OR)(OR output) const
if (isOutputRange!(OR, const(char)[])) if (isOutputRange!(OR, const(char)[]))
{ {
foreach (i, b; this.address) ptrdiff_t largestGroupIndex = -1;
{ size_t largestGroupSize;
ubyte low = b & 0xf; size_t zeroesInGroup;
ubyte high = b >> 4; size_t groupIndex;
if (high < 10) // Look for the longest group of zeroes
for (size_t i; i < this.address.length; i += 2)
{ {
put(output, cast(char) (high + '0')); if (this.address[i] == 0 && this.address[i + 1] == 0)
{
if (zeroesInGroup++ == 0)
{
groupIndex = i;
}
} }
else else
{ {
put(output, cast(char) (high - 10 + 'a')); zeroesInGroup = 0;
} }
if (low < 10) if (zeroesInGroup > largestGroupSize && zeroesInGroup > 1)
{ {
put(output, cast(char) (low + '0')); largestGroupSize = zeroesInGroup;
largestGroupIndex = groupIndex;
} }
else }
// Write the address
size_t i;
if (largestGroupIndex != 0)
{ {
put(output, cast(char) (low - 10 + 'a')); writeGroup(output, i);
} }
if (i % 2 != 0 && i != (this.address.length - 1)) if (largestGroupIndex != -1)
{ {
put(output, ':'); while (i < largestGroupIndex)
{
put(output, ":");
writeGroup(output, i);
}
put(output, "::");
i += largestGroupSize + 2;
if (i < (this.address.length - 1))
{
writeGroup(output, i);
} }
} }
while (i < this.address.length - 1)
{
put(output, ":");
writeGroup(output, i);
}
return output; return output;
} }
@ -761,9 +764,70 @@ struct Address6
import tanya.range : backInserter; import tanya.range : backInserter;
String actual; String actual;
address6("1:2:3:4:5:6:7:8").get.toString(backInserter(actual));
assert(actual == "0001:0002:0003:0004:0005:0006:0007:0008"); address6("1:2:3:4:5:6:7:8").get.toString(backInserter(actual));
assert(actual == "1:2:3:4:5:6:7:8");
}
@nogc nothrow @safe unittest
{
char[18] actual;
address6("ff00:2:3:4:5:6:7:8").get.toString(arrayInserter(actual));
assert(actual[] == "ff00:2:3:4:5:6:7:8");
}
// Skips zero group in the middle
@nogc nothrow @safe unittest
{
char[12] actual;
address6("1::4:5:6:7:8").get.toString(arrayInserter(actual));
assert(actual[] == "1::4:5:6:7:8");
}
// Doesn't replace lonely zeroes
@nogc nothrow @safe unittest
{
char[15] actual;
address6("0:1:0:2:3:0:4:0").get.toString(arrayInserter(actual));
assert(actual[] == "0:1:0:2:3:0:4:0");
}
// Skips zero group at the beginning
@nogc nothrow @safe unittest
{
char[13] actual;
address6("::3:4:5:6:7:8").get.toString(arrayInserter(actual));
assert(actual[] == "::3:4:5:6:7:8");
}
// Skips zero group at the end
@nogc nothrow @safe unittest
{
char[13] actual;
address6("1:2:3:4:5:6::").get.toString(arrayInserter(actual));
assert(actual[] == "1:2:3:4:5:6::");
}
private void writeGroup(OR)(ref OR output, ref size_t i) const
{
ubyte low = this.address[i] & 0xf;
ubyte high = this.address[i] >> 4;
bool groupStarted = writeHexDigit!OR(output, high);
groupStarted = writeHexDigit!OR(output, low, groupStarted);
++i;
low = this.address[i] & 0xf;
high = this.address[i] >> 4;
writeHexDigit!OR(output, high, groupStarted);
put(output, low.toHexDigit.singleton);
++i;
} }
/** /**
@ -785,13 +849,32 @@ struct Address6
} }
} }
private void write2Bytes(R)(ref R range, ubyte[] address) private void read2Bytes(R)(ref R range, ubyte[] address)
{ {
ushort group = readIntegral!ushort(range, 16); ushort group = readIntegral!ushort(range, 16);
address[0] = cast(ubyte) (group >> 8); address[0] = cast(ubyte) (group >> 8);
address[1] = group & 0xff; address[1] = group & 0xff;
} }
private char toHexDigit(ubyte digit) @nogc nothrow pure @safe
in (digit < 16)
{
return cast(char) (digit >= 10 ? (digit - 10 + 'a') : (digit + '0'));
}
private bool writeHexDigit(OR)(ref OR output,
ubyte digit,
bool groupStarted = false)
in (digit < 16)
{
if (digit != 0 || groupStarted)
{
put(output, digit.toHexDigit.singleton);
return true;
}
return groupStarted;
}
/** /**
* Parses a string containing an IPv6 address. * Parses a string containing an IPv6 address.
* *
@ -851,7 +934,7 @@ if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
{ {
auto state = range.save(); auto state = range.save();
} }
write2Bytes(range, result.address[i * 2 .. $]); read2Bytes(range, result.address[i * 2 .. $]);
if (range.empty) if (range.empty)
{ {
return typeof(return)(); return typeof(return)();
@ -880,7 +963,7 @@ if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
} }
} }
} }
write2Bytes(range, result.address[14 .. $]); read2Bytes(range, result.address[14 .. $]);
if (range.empty) if (range.empty)
{ {
@ -911,7 +994,7 @@ ParseTail: // after ::
{ // To make "state" definition local { // To make "state" definition local
auto state = range.save(); auto state = range.save();
write2Bytes(range, tail[j .. $]); read2Bytes(range, tail[j .. $]);
if (range.empty) if (range.empty)
{ {
goto CopyTail; goto CopyTail;
@ -940,7 +1023,7 @@ ParseTail: // after ::
return typeof(return)(); return typeof(return)();
} }
auto state = range.save(); auto state = range.save();
write2Bytes(range, tail[j .. $]); read2Bytes(range, tail[j .. $]);
if (range.empty) if (range.empty)
{ {