readIntegral: Support base between 2 and 36
This commit is contained in:
parent
7561b964d3
commit
173ae115ee
@ -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)
|
* Converts a string $(D_PARAM range) into an integral value of type
|
||||||
&& isIntegral!T && isUnsigned!T)
|
* $(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;
|
assert(base >= 2);
|
||||||
|
assert(base <= 36);
|
||||||
enum T boundary = T.max / 10;
|
}
|
||||||
|
do
|
||||||
|
{
|
||||||
|
T boundary = cast(T) (T.max / base);
|
||||||
if (range.empty)
|
if (range.empty)
|
||||||
{
|
{
|
||||||
return false;
|
return T.init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T n;
|
||||||
while (n <= boundary)
|
while (n <= boundary)
|
||||||
{
|
{
|
||||||
if (!range.front.isDigit)
|
int digit;
|
||||||
|
if (range.front >= 'a')
|
||||||
{
|
{
|
||||||
return false;
|
digit = range.front - 'W';
|
||||||
}
|
}
|
||||||
n = cast(T) (n * 10 + (range.front - '0'));
|
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 n;
|
||||||
|
}
|
||||||
|
n = cast(T) (n * base + digit);
|
||||||
range.popFront();
|
range.popFront();
|
||||||
|
|
||||||
if (range.empty)
|
if (range.empty)
|
||||||
{
|
{
|
||||||
return true;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (range.length > 1)
|
if (range.length > 1)
|
||||||
{
|
{
|
||||||
return false;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
int digit = range.front - '0';
|
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
|
// reads ubyte.max
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
ubyte n;
|
|
||||||
string number = "255";
|
string number = "255";
|
||||||
assert(readString(number, n));
|
assert(readIntegral!ubyte(number) == 255);
|
||||||
assert(n == 255);
|
|
||||||
assert(number.empty);
|
assert(number.empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// detects integer overflow
|
// detects integer overflow
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
ubyte n;
|
|
||||||
string number = "500";
|
string number = "500";
|
||||||
assert(!readString(number, n));
|
readIntegral!ubyte(number);
|
||||||
assert(number.front == '0');
|
assert(number.front == '0');
|
||||||
assert(number.length == 1);
|
assert(number.length == 1);
|
||||||
}
|
}
|
||||||
@ -330,38 +367,49 @@ if (isForwardRange!R && isSomeChar!(ElementType!R)
|
|||||||
// stops on a non-digit
|
// stops on a non-digit
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
ubyte n;
|
|
||||||
string number = "10-";
|
string number = "10-";
|
||||||
assert(!readString(number, n));
|
readIntegral!ubyte(number);
|
||||||
assert(number.front == '-');
|
assert(number.front == '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns false if the number string is empty
|
// returns false if the number string is empty
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
ubyte n;
|
|
||||||
string number = "";
|
string number = "";
|
||||||
assert(!readString(number, n));
|
readIntegral!ubyte(number);
|
||||||
assert(number.empty);
|
assert(number.empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
ubyte n;
|
|
||||||
string number = "29";
|
string number = "29";
|
||||||
assert(readString(number, n));
|
assert(readIntegral!ubyte(number) == 29);
|
||||||
assert(n == 29);
|
|
||||||
assert(number.empty);
|
assert(number.empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
ubyte n;
|
|
||||||
string number = "25467";
|
string number = "25467";
|
||||||
assert(!readString(number, n));
|
readIntegral!ubyte(number);
|
||||||
assert(number.front == '6');
|
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
|
* 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
|
* equal, does nothing. If $(D_PARAM From) can be implicitly converted to
|
||||||
|
@ -305,8 +305,14 @@ struct URL
|
|||||||
*/
|
*/
|
||||||
private bool parsePort(const(char)[] port) @nogc nothrow pure @safe
|
private bool parsePort(const(char)[] port) @nogc nothrow pure @safe
|
||||||
{
|
{
|
||||||
auto portNumber = port[1 .. $];
|
auto unparsed = port[1 .. $];
|
||||||
return readString(portNumber, this.port) || portNumber[0] == '/';
|
auto parsed = readIntegral!ushort(unparsed);
|
||||||
|
if (unparsed.length == 0 || unparsed[0] == '/')
|
||||||
|
{
|
||||||
|
this.port = parsed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user