authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-02-13 16:24:29+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-02-13 16:24:29+02:00
logfc48467a97021cb872ff2a947f96e882274c39c1
treefc86aeb23dd92fb36a15b33c4a228979f1e5e42b
parentc8dc00086e6622f3f29d7e18deb92ea102cf1074
parentb1dd4b17d89633c4d26c39952f69d7ff2efb98ce
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14548 from schmee/std-json-fixes

Some std.json fixes

2 files changed, 90 insertions(+), 4 deletions(-)

lib/std/json.zig+39-4
...@@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList;...@@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList;
1163const StringArrayHashMap = std.StringArrayHashMap;1163const StringArrayHashMap = std.StringArrayHashMap;
11641164
1165pub const ValueTree = struct {1165pub const ValueTree = struct {
1166 arena: ArenaAllocator,1166 arena: *ArenaAllocator,
1167 root: Value,1167 root: Value,
11681168
1169 pub fn deinit(self: *ValueTree) void {1169 pub fn deinit(self: *ValueTree) void {
1170 self.arena.deinit();1170 self.arena.deinit();
1171 self.arena.child_allocator.destroy(self.arena);
1171 }1172 }
1172};1173};
11731174
...@@ -1639,7 +1640,7 @@ fn parseInternal(...@@ -1639,7 +1640,7 @@ fn parseInternal(
1639 const allocator = options.allocator orelse return error.AllocatorRequired;1640 const allocator = options.allocator orelse return error.AllocatorRequired;
1640 switch (ptrInfo.size) {1641 switch (ptrInfo.size) {
1641 .One => {1642 .One => {
1642 const r: T = try allocator.create(ptrInfo.child);1643 const r: *ptrInfo.child = try allocator.create(ptrInfo.child);
1643 errdefer allocator.destroy(r);1644 errdefer allocator.destroy(r);
1644 r.* = try parseInternal(ptrInfo.child, token, tokens, options);1645 r.* = try parseInternal(ptrInfo.child, token, tokens, options);
1645 return r;1646 return r;
...@@ -1741,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {...@@ -1741,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
1741 .Struct => |structInfo| {1742 .Struct => |structInfo| {
1742 inline for (structInfo.fields) |field| {1743 inline for (structInfo.fields) |field| {
1743 if (!field.is_comptime) {1744 if (!field.is_comptime) {
1744 parseFree(field.type, @field(value, field.name), options);1745 var should_free = true;
1746 if (field.default_value) |default| {
1747 switch (@typeInfo(field.type)) {
1748 // We must not attempt to free pointers to struct default values
1749 .Pointer => |fieldPtrInfo| {
1750 const field_value = @field(value, field.name);
1751 const field_ptr = switch (fieldPtrInfo.size) {
1752 .One => field_value,
1753 .Slice => field_value.ptr,
1754 else => unreachable, // Other pointer types are not parseable
1755 };
1756 const field_addr = @ptrToInt(field_ptr);
1757
1758 const casted_default = @ptrCast(*const field.type, @alignCast(@alignOf(field.type), default)).*;
1759 const default_ptr = switch (fieldPtrInfo.size) {
1760 .One => casted_default,
1761 .Slice => casted_default.ptr,
1762 else => unreachable, // Other pointer types are not parseable
1763 };
1764 const default_addr = @ptrToInt(default_ptr);
1765
1766 if (field_addr == default_addr) {
1767 should_free = false;
1768 }
1769 },
1770 else => {},
1771 }
1772 }
1773 if (should_free) {
1774 parseFree(field.type, @field(value, field.name), options);
1775 }
1745 }1776 }
1746 }1777 }
1747 },1778 },
...@@ -1806,8 +1837,12 @@ pub const Parser = struct {...@@ -1806,8 +1837,12 @@ pub const Parser = struct {
1806 pub fn parse(p: *Parser, input: []const u8) !ValueTree {1837 pub fn parse(p: *Parser, input: []const u8) !ValueTree {
1807 var s = TokenStream.init(input);1838 var s = TokenStream.init(input);
18081839
1809 var arena = ArenaAllocator.init(p.allocator);1840 var arena = try p.allocator.create(ArenaAllocator);
1841 errdefer p.allocator.destroy(arena);
1842
1843 arena.* = ArenaAllocator.init(p.allocator);
1810 errdefer arena.deinit();1844 errdefer arena.deinit();
1845
1811 const allocator = arena.allocator();1846 const allocator = arena.allocator();
18121847
1813 while (try s.next()) |token| {1848 while (try s.next()) |token| {
lib/std/json/test.zig+51
...@@ -2238,6 +2238,39 @@ test "parse into struct with no fields" {...@@ -2238,6 +2238,39 @@ test "parse into struct with no fields" {
2238 try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));2238 try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));
2239}2239}
22402240
2241const test_const_value: usize = 123;
2242
2243test "parse into struct with default const pointer field" {
2244 const T = struct { a: *const usize = &test_const_value };
2245 var ts = TokenStream.init("{}");
2246 try testing.expectEqual(T{}, try parse(T, &ts, .{}));
2247}
2248
2249const test_default_usize: usize = 123;
2250const test_default_usize_ptr: *align(1) const usize = &test_default_usize;
2251const test_default_str: []const u8 = "test str";
2252const test_default_str_slice: [2][]const u8 = [_][]const u8{
2253 "test1",
2254 "test2",
2255};
2256
2257test "freeing parsed structs with pointers to default values" {
2258 const T = struct {
2259 int: *const usize = &test_default_usize,
2260 int_ptr: *allowzero align(1) const usize = test_default_usize_ptr,
2261 str: []const u8 = test_default_str,
2262 str_slice: []const []const u8 = &test_default_str_slice,
2263 };
2264
2265 var ts = json.TokenStream.init("{}");
2266 const options = .{ .allocator = std.heap.page_allocator };
2267 const parsed = try json.parse(T, &ts, options);
2268
2269 try testing.expectEqual(T{}, parsed);
2270
2271 json.parseFree(T, parsed, options);
2272}
2273
2241test "parse into struct where destination and source lengths mismatch" {2274test "parse into struct where destination and source lengths mismatch" {
2242 const T = struct { a: [2]u8 };2275 const T = struct { a: [2]u8 };
2243 var ts = TokenStream.init("{\"a\": \"bbb\"}");2276 var ts = TokenStream.init("{\"a\": \"bbb\"}");
...@@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" {...@@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" {
2581 try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), ""));2614 try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), ""));
2582}2615}
25832616
2617test "parse tree should not contain dangling pointers" {
2618 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
2619 defer arena_allocator.deinit();
2620
2621 var p = json.Parser.init(arena_allocator.allocator(), false);
2622 defer p.deinit();
2623
2624 var tree = try p.parse("[]");
2625 defer tree.deinit();
2626
2627 // Allocation should succeed
2628 var i: usize = 0;
2629 while (i < 100) : (i += 1) {
2630 try tree.root.Array.append(std.json.Value{ .Integer = 100 });
2631 }
2632 try testing.expectEqual(tree.root.Array.items.len, 100);
2633}
2634
2584test "integer after float has proper type" {2635test "integer after float has proper type" {
2585 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);2636 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
2586 defer arena_allocator.deinit();2637 defer arena_allocator.deinit();