authorgravatar for alluet@alluet.comAlluet <alluet@alluet.com> 2020-07-03 15:35:18-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-05 22:56:06+00:00
loga0a93f20919e523cb68fc414221b5babe2586825
tree8ad778a37dd96af0394634c338dc1d06f54ce42a
parent682fccaaf0413f4daba43b6990494866bd7b9afd

Rewrite std.fmt.parseInt


1 files changed, 61 insertions(+), 26 deletions(-)

lib/std/fmt.zig+61-26
......@@ -996,49 +996,77 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, option
996996 return fbs.pos;
997997}
998998
999pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T {
1000 if (!T.is_signed) return parseUnsigned(T, buf, radix);
1001 if (buf.len == 0) return @as(T, 0);
1002 if (buf[0] == '-') {
1003 return math.negate(try parseUnsigned(T, buf[1..], radix));
1004 } else if (buf[0] == '+') {
1005 return parseUnsigned(T, buf[1..], radix);
1006 } else {
1007 return parseUnsigned(T, buf, radix);
1008 }
1009}
1010
1011test "parseInt" {
1012 std.testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10);
1013 std.testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10);
1014 std.testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter);
1015 std.testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter);
1016 std.testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter);
1017 std.testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255);
1018 std.testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);
1019}
1020
1021pub const ParseUnsignedError = error{
999pub const ParseIntError = error{
10221000 /// The result cannot fit in the type specified
10231001 Overflow,
10241002
1025 /// The input had a byte that was not a digit
1003 /// The input was empty or had a byte that was not a digit
10261004 InvalidCharacter,
10271005};
10281006
1029pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T {
1007pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T {
1008 if (buf.len == 0) return error.InvalidCharacter;
1009 if (buf[0] == '+') return parseWithSign(T, buf[1..], radix, .Pos);
1010 if (buf[0] == '-') return parseWithSign(T, buf[1..], radix, .Neg);
1011 return parseWithSign(T, buf, radix, .Pos);
1012}
1013
1014test "parseInt" {
1015 std.testing.expect((try parseInt(i32, "-10", 10)) == -10);
1016 std.testing.expect((try parseInt(i32, "+10", 10)) == 10);
1017 std.testing.expect((try parseInt(u32, "+10", 10)) == 10);
1018 std.testing.expectError(error.Overflow, parseInt(u32, "-10", 10));
1019 std.testing.expectError(error.InvalidCharacter, parseInt(u32, " 10", 10));
1020 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "10 ", 10));
1021 std.testing.expect((try parseInt(u8, "255", 10)) == 255);
1022 std.testing.expectError(error.Overflow, parseInt(u8, "256", 10));
1023
1024 // +0 and -0 should work for unsigned
1025 std.testing.expect((try parseInt(u8, "-0", 10)) == 0);
1026 std.testing.expect((try parseInt(u8, "+0", 10)) == 0);
1027
1028 // ensure minInt is parsed correctly
1029 std.testing.expect((try parseInt(i8, "-128", 10)) == math.minInt(i8));
1030 std.testing.expect((try parseInt(i43, "-4398046511104", 10)) == math.minInt(i43));
1031
1032 // empty string or bare +- is invalid
1033 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "", 10));
1034 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "", 10));
1035 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "+", 10));
1036 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "+", 10));
1037 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "-", 10));
1038 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "-", 10));
1039}
1040
1041fn parseWithSign(
1042 comptime T: type,
1043 buf: []const u8,
1044 radix: u8,
1045 comptime sign: enum { Pos, Neg },
1046) ParseIntError!T {
1047 if (buf.len == 0) return error.InvalidCharacter;
1048
1049 const add = switch (sign) {
1050 .Pos => math.add,
1051 .Neg => math.sub,
1052 };
1053
10301054 var x: T = 0;
10311055
10321056 for (buf) |c| {
10331057 const digit = try charToDigit(c, radix);
10341058
10351059 if (x != 0) x = try math.mul(T, x, try math.cast(T, radix));
1036 x = try math.add(T, x, try math.cast(T, digit));
1060 x = try add(T, x, try math.cast(T, digit));
10371061 }
10381062
10391063 return x;
10401064}
10411065
1066pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T {
1067 return parseWithSign(T, buf, radix, .Pos);
1068}
1069
10421070test "parseUnsigned" {
10431071 std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124);
10441072 std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535);
......@@ -1064,6 +1092,13 @@ test "parseUnsigned" {
10641092 std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1);
10651093 std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3);
10661094 std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16));
1095
1096 // parseUnsigned does not expect a sign
1097 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "+0", 10));
1098 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "-0", 10));
1099
1100 // test empty string error
1101 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "", 10));
10671102}
10681103
10691104pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;