summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2018-06-06 21:05:35 +0200
committerEugen Wissner <belka@caraus.de>2018-06-08 21:05:35 +0200
commit173ae115eedeb3e241dd96641b0da6822bb38b80 (patch)
treef1fda2f6d4c366065a849da9ac16ba0c497ef73c
parent7561b964d32ab9cfbd1543e78dd7292abb7b3d23 (diff)
downloadtanya-173ae115eedeb3e241dd96641b0da6822bb38b80.tar.gz
readIntegral: Support base between 2 and 36
-rw-r--r--source/tanya/conv.d108
-rw-r--r--source/tanya/net/uri.d10
2 files changed, 86 insertions, 32 deletions
diff --git a/source/tanya/conv.d b/source/tanya/conv.d
index aa6f847..70e97fa 100644
--- a/source/tanya/conv.d
+++ b/source/tanya/conv.d
@@ -266,63 +266,100 @@ final class ConvException : Exception
}
}
-package bool readString(T, R)(ref R range, out T n)
-if (isForwardRange!R && isSomeChar!(ElementType!R)
- && isIntegral!T && isUnsigned!T)
+/*
+ * Converts a string $(D_PARAM range) into an integral value of type
+ * $(D_PARAM T) in $(D_PARAM base).
+ *
+ * The convertion stops when $(D_PARAM range) is empty of if the next character
+ * cannot be converted because it is not a digit (with respect to the
+ * $(D_PARAM base)) or if the reading the next character would cause integer
+ * overflow. The function returns the value converted so far then. The front
+ * element of the $(D_PARAM range) points to the first character cannot be
+ * converted or $(D_PARAM range) is empty if the whole string could be
+ * converted.
+ *
+ * Base must be between 2 and 36 inclursive. Default base is 10.
+ *
+ * The function doesn't handle the sign (+ or -) or number prefixes (like 0x).
+ */
+package T readIntegral(T, R)(ref R range, ubyte base = 10)
+if (isForwardRange!R
+ && isSomeChar!(ElementType!R)
+ && isIntegral!T
+ && isUnsigned!T)
+in
{
- import tanya.encoding.ascii : isDigit;
-
- enum T boundary = T.max / 10;
+ assert(base >= 2);
+ assert(base <= 36);
+}
+do
+{
+ T boundary = cast(T) (T.max / base);
if (range.empty)
{
- return false;
+ return T.init;
}
+ T n;
while (n <= boundary)
{
- if (!range.front.isDigit)
+ int digit;
+ if (range.front >= 'a')
+ {
+ digit = range.front - 'W';
+ }
+ else if (range.front >= 'A')
+ {
+ digit = range.front - '7';
+ }
+ else if (range.front >= '0')
+ {
+ digit = range.front - '0';
+ }
+ else
+ {
+ return n;
+ }
+ if (digit >= base)
{
- return false;
+ return n;
}
- n = cast(T) (n * 10 + (range.front - '0'));
+ n = cast(T) (n * base + digit);
range.popFront();
if (range.empty)
{
- return true;
+ return n;
}
}
if (range.length > 1)
{
- return false;
+ return n;
}
int digit = range.front - '0';
- if (n > cast(T) ((T.max - digit) / 10))
+ if (n > cast(T) ((T.max - digit) / base))
{
- return false;
+ return n;
}
- n = cast(T) (n * 10 + digit);
+ n = cast(T) (n * base + digit);
- return true;
+ return n;
}
// reads ubyte.max
@nogc nothrow pure @safe unittest
{
- ubyte n;
string number = "255";
- assert(readString(number, n));
- assert(n == 255);
+ assert(readIntegral!ubyte(number) == 255);
assert(number.empty);
}
// detects integer overflow
@nogc nothrow pure @safe unittest
{
- ubyte n;
string number = "500";
- assert(!readString(number, n));
+ readIntegral!ubyte(number);
assert(number.front == '0');
assert(number.length == 1);
}
@@ -330,38 +367,49 @@ if (isForwardRange!R && isSomeChar!(ElementType!R)
// stops on a non-digit
@nogc nothrow pure @safe unittest
{
- ubyte n;
string number = "10-";
- assert(!readString(number, n));
+ readIntegral!ubyte(number);
assert(number.front == '-');
}
// returns false if the number string is empty
@nogc nothrow pure @safe unittest
{
- ubyte n;
string number = "";
- assert(!readString(number, n));
+ readIntegral!ubyte(number);
assert(number.empty);
}
@nogc nothrow pure @safe unittest
{
- ubyte n;
string number = "29";
- assert(readString(number, n));
- assert(n == 29);
+ assert(readIntegral!ubyte(number) == 29);
assert(number.empty);
}
@nogc nothrow pure @safe unittest
{
- ubyte n;
string number = "25467";
- assert(!readString(number, n));
+ readIntegral!ubyte(number);
assert(number.front == '6');
}
+// Converts lower case hexadecimals
+@nogc nothrow pure @safe unittest
+{
+ string number = "a";
+ assert(readIntegral!ubyte(number, 16) == 10);
+ assert(number.empty);
+}
+
+// Converts upper case hexadecimals
+@nogc nothrow pure @safe unittest
+{
+ string number = "FF";
+ assert(readIntegral!ubyte(number, 16) == 255);
+ assert(number.empty);
+}
+
/**
* If the source type $(D_PARAM From) and the target type $(D_PARAM To) are
* equal, does nothing. If $(D_PARAM From) can be implicitly converted to
diff --git a/source/tanya/net/uri.d b/source/tanya/net/uri.d
index 021b4b4..30df565 100644
--- a/source/tanya/net/uri.d
+++ b/source/tanya/net/uri.d
@@ -305,8 +305,14 @@ struct URL
*/
private bool parsePort(const(char)[] port) @nogc nothrow pure @safe
{
- auto portNumber = port[1 .. $];
- return readString(portNumber, this.port) || portNumber[0] == '/';
+ auto unparsed = port[1 .. $];
+ auto parsed = readIntegral!ushort(unparsed);
+ if (unparsed.length == 0 || unparsed[0] == '/')
+ {
+ this.port = parsed;
+ return true;
+ }
+ return false;
}
}