From 7561b964d32ab9cfbd1543e78dd7292abb7b3d23 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 5 Jun 2018 20:23:39 +0200 Subject: [PATCH] Make intToString -> readString more generic Make readString work with any char range and unsigned integral type. --- source/tanya/conv.d | 100 +++++++++++++++++++++++++++++++++++------ source/tanya/net/uri.d | 3 +- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/source/tanya/conv.d b/source/tanya/conv.d index 2cd40b3..aa6f847 100644 --- a/source/tanya/conv.d +++ b/source/tanya/conv.d @@ -20,6 +20,8 @@ import tanya.memory; import tanya.memory.op; import tanya.meta.trait; import tanya.meta.transform; +import tanya.range.array; +import tanya.range.primitive; version (unittest) { @@ -264,28 +266,100 @@ final class ConvException : Exception } } -package bool stringToInt(R)(R range, ref ushort n) +package bool readString(T, R)(ref R range, out T n) +if (isForwardRange!R && isSomeChar!(ElementType!R) + && isIntegral!T && isUnsigned!T) { - import tanya.encoding.ascii; - import tanya.range.array; + import tanya.encoding.ascii : isDigit; - size_t i = 1; - uint lPort; - - for (; !range.empty && range.front.isDigit() && i <= 6; ++i, range.popFront()) + enum T boundary = T.max / 10; + if (range.empty) { - lPort = lPort * 10 + (range.front - '0'); + return false; } - if (i != 1 && (range.empty || range.front == '/')) + + while (n <= boundary) { - if (lPort > ushort.max) + if (!range.front.isDigit) { return false; } - n = cast(ushort) lPort; - return true; + n = cast(T) (n * 10 + (range.front - '0')); + range.popFront(); + + if (range.empty) + { + return true; + } } - return false; + if (range.length > 1) + { + return false; + } + + int digit = range.front - '0'; + if (n > cast(T) ((T.max - digit) / 10)) + { + return false; + } + n = cast(T) (n * 10 + digit); + + return true; +} + +// reads ubyte.max +@nogc nothrow pure @safe unittest +{ + ubyte n; + string number = "255"; + assert(readString(number, n)); + assert(n == 255); + assert(number.empty); +} + +// detects integer overflow +@nogc nothrow pure @safe unittest +{ + ubyte n; + string number = "500"; + assert(!readString(number, n)); + assert(number.front == '0'); + assert(number.length == 1); +} + +// stops on a non-digit +@nogc nothrow pure @safe unittest +{ + ubyte n; + string number = "10-"; + assert(!readString(number, n)); + assert(number.front == '-'); +} + +// returns false if the number string is empty +@nogc nothrow pure @safe unittest +{ + ubyte n; + string number = ""; + assert(!readString(number, n)); + assert(number.empty); +} + +@nogc nothrow pure @safe unittest +{ + ubyte n; + string number = "29"; + assert(readString(number, n)); + assert(n == 29); + assert(number.empty); +} + +@nogc nothrow pure @safe unittest +{ + ubyte n; + string number = "25467"; + assert(!readString(number, n)); + assert(number.front == '6'); } /** diff --git a/source/tanya/net/uri.d b/source/tanya/net/uri.d index 8b05f8e..021b4b4 100644 --- a/source/tanya/net/uri.d +++ b/source/tanya/net/uri.d @@ -305,7 +305,8 @@ struct URL */ private bool parsePort(const(char)[] port) @nogc nothrow pure @safe { - return stringToInt(port[1 .. $], this.port); + auto portNumber = port[1 .. $]; + return readString(portNumber, this.port) || portNumber[0] == '/'; } }