authorgravatar for 37453713+ominitay@users.noreply.github.comominitay <37453713+ominitay@users.noreply.github.com> 2022-07-15 22:05:35+01:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-23 13:52:16+03:00
log889efddd1a2b12d3348f06900436b9ba8c823a01
tree16e0500940e8273e718ab704f07711bb4089f993
parenta8bfddfaeae4f48c044fd134aac1e977e6a161f8

std.json: Fix parsing of large numbers

Numbers greater than about 2^53 are encoded as strings in JSON. std.json.parseInternal previously errored out in this condition.

2 files changed, 32 insertions(+), 13 deletions(-)

lib/std/json.zig+27-13
...@@ -1422,23 +1422,37 @@ fn parseInternal(...@@ -1422,23 +1422,37 @@ fn parseInternal(
1422 };1422 };
1423 },1423 },
1424 .Float, .ComptimeFloat => {1424 .Float, .ComptimeFloat => {
1425 const numberToken = switch (token) {1425 switch (token) {
1426 .Number => |n| n,1426 .Number => |numberToken| return try std.fmt.parseFloat(T, numberToken.slice(tokens.slice, tokens.i - 1)),
1427 .String => |stringToken| return try std.fmt.parseFloat(T, stringToken.slice(tokens.slice, tokens.i - 1)),
1427 else => return error.UnexpectedToken,1428 else => return error.UnexpectedToken,
1428 };1429 }
1429 return try std.fmt.parseFloat(T, numberToken.slice(tokens.slice, tokens.i - 1));
1430 },1430 },
1431 .Int, .ComptimeInt => {1431 .Int, .ComptimeInt => {
1432 const numberToken = switch (token) {1432 switch (token) {
1433 .Number => |n| n,1433 .Number => |numberToken| {
1434 if (numberToken.is_integer)
1435 return try std.fmt.parseInt(T, numberToken.slice(tokens.slice, tokens.i - 1), 10);
1436 const float = try std.fmt.parseFloat(f128, numberToken.slice(tokens.slice, tokens.i - 1));
1437 if (@round(float) != float) return error.InvalidNumber;
1438 if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow;
1439 return @floatToInt(T, float);
1440 },
1441 .String => |stringToken| {
1442 return std.fmt.parseInt(T, stringToken.slice(tokens.slice, tokens.i - 1), 10) catch |err| {
1443 switch (err) {
1444 error.Overflow => return err,
1445 error.InvalidCharacter => {
1446 const float = try std.fmt.parseFloat(f128, stringToken.slice(tokens.slice, tokens.i - 1));
1447 if (@round(float) != float) return error.InvalidNumber;
1448 if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow;
1449 return @floatToInt(T, float);
1450 },
1451 }
1452 };
1453 },
1434 else => return error.UnexpectedToken,1454 else => return error.UnexpectedToken,
1435 };1455 }
1436 if (numberToken.is_integer)
1437 return try std.fmt.parseInt(T, numberToken.slice(tokens.slice, tokens.i - 1), 10);
1438 const float = try std.fmt.parseFloat(f128, numberToken.slice(tokens.slice, tokens.i - 1));
1439 if (@round(float) != float) return error.InvalidNumber;
1440 if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow;
1441 return @floatToInt(T, float);
1442 },1456 },
1443 .Optional => |optionalInfo| {1457 .Optional => |optionalInfo| {
1444 if (token == .Null) {1458 if (token == .Null) {
lib/std/json/test.zig+5
...@@ -2046,6 +2046,11 @@ test "parse" {...@@ -2046,6 +2046,11 @@ test "parse" {
2046 try testing.expectEqual(@as([3]u8, "foo".*), try parse([3]u8, &ts, ParseOptions{}));2046 try testing.expectEqual(@as([3]u8, "foo".*), try parse([3]u8, &ts, ParseOptions{}));
2047 ts = TokenStream.init("[]");2047 ts = TokenStream.init("[]");
2048 try testing.expectEqual(@as([0]u8, undefined), try parse([0]u8, &ts, ParseOptions{}));2048 try testing.expectEqual(@as([0]u8, undefined), try parse([0]u8, &ts, ParseOptions{}));
2049
2050 ts = TokenStream.init("\"12345678901234567890\"");
2051 try testing.expectEqual(@as(u64, 12345678901234567890), try parse(u64, &ts, ParseOptions{}));
2052 ts = TokenStream.init("\"123.456\"");
2053 try testing.expectEqual(@as(f64, 123.456), try parse(f64, &ts, ParseOptions{}));
2049}2054}
20502055
2051test "parse into enum" {2056test "parse into enum" {