commit 184ac22373487aab4ff720789137bc8e27d9165b
parent 580380fa449328ed07e933e7f9140f0cf988c278
Author: Drew DeVault <sir@cmpwn.com>
Date: Fri, 12 Feb 2021 13:29:05 -0500
strconv: implement signed base conversion
Diffstat:
3 files changed, 120 insertions(+), 49 deletions(-)
diff --git a/strconv/itos.ha b/strconv/itos.ha
@@ -1,18 +1,36 @@
use bytes;
use types;
-// Converts an i64 to a string, in base 10. The return value is statically
+// Converts an i64 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn i64tos(i: i64) const str = {
- static assert(types::I64_MAX == 9223372036854775807);
+export fn i64tosb(i: i64, b: base) const str = {
if (i == types::I64_MIN) {
- return "-9223372036854775808";
+ return switch (b) {
+ base::BIN => "-1000000000000000000000000000000000000000000000000000000000000000",
+ base::OCT => "-1000000000000000000000",
+ base::DEC => "-9223372036854775808",
+ base::HEX_LOWER, base::HEX_UPPER => "-8000000000000000",
+ };
};
- static let buf: [22]u8 = [0...]; // 20 chars plus NUL and -
+ static assert(types::I64_MAX == 9223372036854775807);
+ static let buf: [66]u8 = [0...]; // 64 binary digits plus NUL and -
buf = [0...];
+ static const lut_upper: [_]rune = [
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+ ];
+ static const lut_lower: [_]rune = [
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
+ ];
+ let lut = if (b != base::HEX_LOWER) lut_upper else {
+ b = base::HEX_UPPER;
+ lut_lower;
+ };
+
let s = types::string { data = &buf, ... };
const isneg = i < 0;
@@ -26,9 +44,9 @@ export fn i64tos(i: i64) const str = {
};
for (i > 0) {
- buf[s.length] = '0': u32: u8 + (i % 10): u8;
+ buf[s.length] = lut[i % b: i64]: u32: u8;
s.length += 1;
- i /= 10;
+ i /= b: i64;
};
const x: size = if (isneg) 1 else 0;
@@ -38,25 +56,61 @@ export fn i64tos(i: i64) const str = {
return *(&s: *str);
};
-// Converts an i8 to a string, in base 10. The return value is statically
+// Converts a i32 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn i8tos(i: i8) const str = i64tos(i: i64);
+export fn i32tosb(i: i32, b: base) const str = i64tosb(i, b);
-// Converts an i16 to a string, in base 10. The return value is statically
+// Converts a i16 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn i16tos(i: i16) const str = i64tos(i: i64);
+export fn i16tosb(i: i16, b: base) const str = i64tosb(i, b);
-// Converts an i32 to a string, in base 10. The return value is statically
+// Converts a i8 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn i32tos(i: i32) const str = i64tos(i: i64);
+export fn i8tosb(i: i8, b: base) const str = i64tosb(i, b);
-// Converts an int to a string, in base 10. The return value is statically
+// Converts an int to a string in the given base. The return value is
+// statically allocated and will be overwritten on subsequent calls; see
+// [strings::dup] to duplicate the result.
+export fn itosb(i: int, b: base) const str = i64tosb(i, b);
+
+// Converts a u64 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn itos(i: int) const str = i64tos(i: i64);
+export fn i64tos(i: i64) const str = i64tosb(i, base::DEC);
+
+// Converts a u8 to a string in base 10. The return value is statically
+// allocated and will be overwritten on subsequent calls; see [strings::dup] to
+// duplicate the result.
+export fn i8tos(i: i8) const str = i64tos(i);
+
+// Converts a u16 to a string in base 10. The return value is statically
+// allocated and will be overwritten on subsequent calls; see [strings::dup] to
+// duplicate the result.
+export fn i16tos(i: i16) const str = i64tos(i);
+
+// Converts a u32 to a string in base 10. The return value is statically
+// allocated and will be overwritten on subsequent calls; see [strings::dup] to
+// duplicate the result.
+export fn i32tos(i: i32) const str = i64tos(i);
+
+// Converts a uint to a string in base 10. The return value is statically
+// allocated and will be overwritten on subsequent calls; see [strings::dup] to
+// duplicate the result.
+export fn itos(i: int) const str = i64tos(i);
+
+@test fn itosb() void = {
+ assert("11010" == i64tosb(0b11010, base::BIN));
+ assert("1234567" == i64tosb(0o1234567, base::OCT));
+ assert("123456789" == i64tosb(123456789, base::DEC));
+ assert("123456789ABCDEF" == i64tosb(0x123456789ABCDEF, base::HEX));
+ assert("123456789ABCDEF" == i64tosb(0x123456789ABCDEF, base::HEX_UPPER));
+ assert("123456789abcdef" == i64tosb(0x123456789ABCDEF, base::HEX_LOWER));
+ assert("-1000000000000000000000000000000000000000000000000000000000000000"
+ == i64tosb(types::I64_MIN, base::BIN));
+};
@test fn itos() void = {
const samples: [_]i64 = [
diff --git a/strconv/numeric.ha b/strconv/numeric.ha
@@ -4,13 +4,12 @@ use types;
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
export fn signedtosb(n: types::signed, b: base) const str = {
- assert(b == base::DEC); // TODO
return match (n) {
- i: int => itos(i),
- i: i8 => i8tos(i),
- i: i16 => i16tos(i),
- i: i32 => i32tos(i),
- i: i64 => i64tos(i),
+ i: int => itosb(i, b),
+ i: i8 => i8tosb(i, b),
+ i: i16 => i16tosb(i, b),
+ i: i32 => i32tosb(i, b),
+ i: i64 => i64tosb(i, b),
};
};
@@ -79,3 +78,19 @@ export fn numerictosb(n: types::numeric, b: base) const str = {
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
export fn numerictos(n: types::numeric) const str = numerictosb(n, base::DEC);
+
+@test fn numeric() void = {
+ const cases: [_]types::numeric = [
+ 42u8, 1337u16, 1337u32, 1337u64, 42i8, -42i8, 1337i16, -1337i16,
+ 1337i32, -1337i32, 1337i64, -1337i64, 1337i, -1337i, 1337u,
+ -1337i,
+ ];
+ const expected = [
+ "42", "1337", "1337", "1337", "42", "-42", "1337", "-1337",
+ "1337", "-1337", "1337", "-1337", "1337", "-1337", "1337",
+ "-1337",
+ ];
+ for (let i = 0z; i < len(cases); i += 1) {
+ assert(numerictos(cases[i]) == expected[i]);
+ };
+};
diff --git a/strconv/utos.ha b/strconv/utos.ha
@@ -1,11 +1,14 @@
use bytes;
use types;
-// Converts a u64 to a string, in the given base. The return value is statically
+// Converts a u64 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
export fn u64tosb(u: u64, b: base) const str = {
static assert(types::U64_MAX == 18446744073709551615);
+ static let buf: [65]u8 = [0...]; // 64 binary digits plus NUL
+ buf = [0...];
+
static const lut_upper: [_]rune = [
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
@@ -19,9 +22,6 @@ export fn u64tosb(u: u64, b: base) const str = {
lut_lower;
};
- static let buf: [21]u8 = [0...]; // 20 digits plus NUL
- buf = [0...];
-
let s = types::string { data = &buf, ... };
if (u == 0) {
buf[s.length] = '0': u32: u8;
@@ -29,7 +29,7 @@ export fn u64tosb(u: u64, b: base) const str = {
};
for (u > 0u64) {
- buf[s.length] = lut[(u % b: u64)]: u32: u8;
+ buf[s.length] = lut[u % b: u64]: u32: u8;
s.length += 1;
u /= b;
};
@@ -39,67 +39,67 @@ export fn u64tosb(u: u64, b: base) const str = {
return *(&s: *str);
};
-// Converts a u32 to a string, in the given base. The return value is statically
+// Converts a u32 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn u32tosb(u: u32, b: base) const str = u64tosb(u: u32, b);
+export fn u32tosb(u: u32, b: base) const str = u64tosb(u, b);
-// Converts a u16 to a string, in the given base. The return value is statically
+// Converts a u16 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn u16tosb(u: u16, b: base) const str = u64tosb(u: u16, b);
+export fn u16tosb(u: u16, b: base) const str = u64tosb(u, b);
-// Converts a u8 to a string, in the given base. The return value is statically
+// Converts a u8 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn u8tosb(u: u8, b: base) const str = u64tosb(u: u8, b);
+export fn u8tosb(u: u8, b: base) const str = u64tosb(u, b);
-// Converts a uint to a string, in the given base. The return value is
+// Converts a uint to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
-export fn utosb(u: uint, b: base) const str = u64tosb(u: uint, b);
+export fn utosb(u: uint, b: base) const str = u64tosb(u, b);
-// Converts a size to a string, in the given base. The return value is
+// Converts a size to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
-export fn ztosb(u: size, b: base) const str = u64tosb(u: u64, b);
+export fn ztosb(u: size, b: base) const str = u64tosb(u, b);
-// Converts a size to a string, in the given base. The return value is
+// Converts a size to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
export fn uptrtosb(uptr: uintptr, b: base) const str = u64tosb(uptr: u64, b);
-// Converts a u64 to a string, in base 10. The return value is statically
+// Converts a u64 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
export fn u64tos(u: u64) const str = u64tosb(u, base::DEC);
-// Converts a u8 to a string, in base 10. The return value is statically
+// Converts a u8 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn u8tos(u: u8) const str = u64tos(u: u64);
+export fn u8tos(u: u8) const str = u64tos(u);
-// Converts a u16 to a string, in base 10. The return value is statically
+// Converts a u16 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn u16tos(u: u16) const str = u64tos(u: u64);
+export fn u16tos(u: u16) const str = u64tos(u);
-// Converts a u32 to a string, in base 10. The return value is statically
+// Converts a u32 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn u32tos(u: u32) const str = u64tos(u: u64);
+export fn u32tos(u: u32) const str = u64tos(u);
-// Converts a uint to a string, in base 10. The return value is statically
+// Converts a uint to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
-export fn utos(u: uint) const str = u64tos(u: u64);
+export fn utos(u: uint) const str = u64tos(u);
-// Converts a size to a string, in base 10. The return value is statically
+// Converts a size to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result, or [strconv::itosb] to pass your own string buffer.
-export fn ztos(z: size) const str = u64tos(z: u64);
+export fn ztos(z: size) const str = u64tos(z);
-// Converts a uintptr to a string, in base 10. The return value is statically
+// Converts a uintptr to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result.
export fn uptrtos(uptr: uintptr) const str = u64tos(uptr: u64);
@@ -111,6 +111,8 @@ export fn uptrtos(uptr: uintptr) const str = u64tos(uptr: u64);
assert("123456789ABCDEF" == u64tosb(0x123456789ABCDEF, base::HEX));
assert("123456789ABCDEF" == u64tosb(0x123456789ABCDEF, base::HEX_UPPER));
assert("123456789abcdef" == u64tosb(0x123456789ABCDEF, base::HEX_LOWER));
+ assert("1111111111111111111111111111111111111111111111111111111111111111"
+ == u64tosb(types::U64_MAX, base::BIN));
};
@test fn utos() void = {