From 889efddd1a2b12d3348f06900436b9ba8c823a01 Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Fri, 15 Jul 2022 22:05:35 +0100 Subject: [PATCH] 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. --- lib/std/json.zig | 40 +++++++++++++++++++++++++++------------- lib/std/json/test.zig | 5 +++++ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index 9f678fb4a..baf93af47 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1422,23 +1422,37 @@ fn parseInternal( }; }, .Float, .ComptimeFloat => { - const numberToken = switch (token) { - .Number => |n| n, + switch (token) { + .Number => |numberToken| return try std.fmt.parseFloat(T, numberToken.slice(tokens.slice, tokens.i - 1)), + .String => |stringToken| return try std.fmt.parseFloat(T, stringToken.slice(tokens.slice, tokens.i - 1)), else => return error.UnexpectedToken, - }; - return try std.fmt.parseFloat(T, numberToken.slice(tokens.slice, tokens.i - 1)); + } }, .Int, .ComptimeInt => { - const numberToken = switch (token) { - .Number => |n| n, + switch (token) { + .Number => |numberToken| { + if (numberToken.is_integer) + return try std.fmt.parseInt(T, numberToken.slice(tokens.slice, tokens.i - 1), 10); + const float = try std.fmt.parseFloat(f128, numberToken.slice(tokens.slice, tokens.i - 1)); + if (@round(float) != float) return error.InvalidNumber; + if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow; + return @floatToInt(T, float); + }, + .String => |stringToken| { + return std.fmt.parseInt(T, stringToken.slice(tokens.slice, tokens.i - 1), 10) catch |err| { + switch (err) { + error.Overflow => return err, + error.InvalidCharacter => { + const float = try std.fmt.parseFloat(f128, stringToken.slice(tokens.slice, tokens.i - 1)); + if (@round(float) != float) return error.InvalidNumber; + if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow; + return @floatToInt(T, float); + }, + } + }; + }, else => return error.UnexpectedToken, - }; - if (numberToken.is_integer) - return try std.fmt.parseInt(T, numberToken.slice(tokens.slice, tokens.i - 1), 10); - const float = try std.fmt.parseFloat(f128, numberToken.slice(tokens.slice, tokens.i - 1)); - if (@round(float) != float) return error.InvalidNumber; - if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow; - return @floatToInt(T, float); + } }, .Optional => |optionalInfo| { if (token == .Null) { diff --git a/lib/std/json/test.zig b/lib/std/json/test.zig index df0403cf5..2a590fdf1 100644 --- a/lib/std/json/test.zig +++ b/lib/std/json/test.zig @@ -2046,6 +2046,11 @@ test "parse" { try testing.expectEqual(@as([3]u8, "foo".*), try parse([3]u8, &ts, ParseOptions{})); ts = TokenStream.init("[]"); try testing.expectEqual(@as([0]u8, undefined), try parse([0]u8, &ts, ParseOptions{})); + + ts = TokenStream.init("\"12345678901234567890\""); + try testing.expectEqual(@as(u64, 12345678901234567890), try parse(u64, &ts, ParseOptions{})); + ts = TokenStream.init("\"123.456\""); + try testing.expectEqual(@as(f64, 123.456), try parse(f64, &ts, ParseOptions{})); } test "parse into enum" {