Commit 889efddd1a

ominitay <37453713+ominitay@users.noreply.github.com>
2022-07-15 23:05:35
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.
1 parent a8bfddf
Changed files (2)
lib
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" {
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) {