summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/tanya/conv.d132
1 files changed, 132 insertions, 0 deletions
diff --git a/source/tanya/conv.d b/source/tanya/conv.d
index a244841..8233711 100644
--- a/source/tanya/conv.d
+++ b/source/tanya/conv.d
@@ -875,3 +875,135 @@ if (is(Unqual!To == String))
static assert(is(typeof((const String("true")).to!bool)));
static assert(is(typeof(false.to!(const String) == "false")));
}
+
+/**
+ * Converts a stringish range to an integral value.
+ *
+ * Params:
+ * From = Source type.
+ * To = Target type.
+ * from = Source value.
+ *
+ * Returns: $(D_PARAM from) converted to $(D_PARAM To).
+ *
+ * Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) doesn't contain an
+ * integral value.
+ */
+To to(To, From)(auto ref From from)
+if (isInputRange!From && isSomeChar!(ElementType!From) && isIntegral!To)
+{
+ if (from.empty)
+ {
+ throw make!ConvException(defaultAllocator, "Input range is empty");
+ }
+
+ static if (isSigned!To)
+ {
+ bool negative;
+ }
+ if (from.front == '-')
+ {
+ static if (isUnsigned!To)
+ {
+ throw make!ConvException(defaultAllocator,
+ "Negative integer overflow");
+ }
+ else
+ {
+ negative = true;
+ from.popFront();
+ }
+ }
+
+ if (from.empty)
+ {
+ throw make!ConvException(defaultAllocator, "Input range is empty");
+ }
+
+ ubyte base = 10;
+ if (from.front == '0')
+ {
+ from.popFront();
+ if (from.empty)
+ {
+ return To.init;
+ }
+ else if (from.front == 'x' || from.front == 'X')
+ {
+ base = 16;
+ from.popFront();
+ }
+ else if (from.front == 'b' || from.front == 'B')
+ {
+ base = 2;
+ from.popFront();
+ }
+ else
+ {
+ base = 8;
+ }
+ }
+
+ auto unsigned = readIntegral!(Unsigned!To, From)(from, base);
+ if (!from.empty)
+ {
+ throw make!ConvException(defaultAllocator, "Integer overflow");
+ }
+
+ static if (isSigned!To)
+ {
+ if (negative)
+ {
+ auto predecessor = cast(Unsigned!To) (unsigned - 1);
+ if (predecessor > cast(Unsigned!To) To.max)
+ {
+ throw make!ConvException(defaultAllocator,
+ "Negative integer overflow");
+ }
+ return cast(To) (-(cast(Largest!(To, ptrdiff_t)) predecessor) - 1);
+ }
+ else if (unsigned > cast(Unsigned!To) To.max)
+ {
+ throw make!ConvException(defaultAllocator, "Integer overflow");
+ }
+ else
+ {
+ return unsigned;
+ }
+ }
+ else
+ {
+ return unsigned;
+ }
+}
+
+///
+@nogc pure @safe unittest
+{
+ assert("1234".to!uint() == 1234);
+ assert("1234".to!int() == 1234);
+ assert("1234".to!int() == 1234);
+
+ assert("0".to!int() == 0);
+ assert("-0".to!int() == 0);
+
+ assert("0x10".to!int() == 16);
+ assert("0X10".to!int() == 16);
+ assert("-0x10".to!int() == -16);
+
+ assert("0b10".to!int() == 2);
+ assert("0B10".to!int() == 2);
+ assert("-0b10".to!int() == -2);
+
+ assert("010".to!int() == 8);
+ assert("-010".to!int() == -8);
+
+
+ assert("-128".to!byte == cast(byte) -128);
+
+ assertThrown!ConvException(() => "".to!int);
+ assertThrown!ConvException(() => "-".to!int);
+ assertThrown!ConvException(() => "-5".to!uint);
+ assertThrown!ConvException(() => "-129".to!byte);
+ assertThrown!ConvException(() => "256".to!ubyte);
+}