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;
11631163const StringArrayHashMap = std.StringArrayHashMap;
11641164
11651165pub const ValueTree = struct {
1166 arena: ArenaAllocator,
1166 arena: *ArenaAllocator,
11671167 root: Value,
11681168
11691169 pub fn deinit(self: *ValueTree) void {
11701170 self.arena.deinit();
1171 self.arena.child_allocator.destroy(self.arena);
11711172 }
11721173};
11731174
......@@ -1639,7 +1640,7 @@ fn parseInternal(
16391640 const allocator = options.allocator orelse return error.AllocatorRequired;
16401641 switch (ptrInfo.size) {
16411642 .One => {
1642 const r: T = try allocator.create(ptrInfo.child);
1643 const r: *ptrInfo.child = try allocator.create(ptrInfo.child);
16431644 errdefer allocator.destroy(r);
16441645 r.* = try parseInternal(ptrInfo.child, token, tokens, options);
16451646 return r;
......@@ -1741,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
17411742 .Struct => |structInfo| {
17421743 inline for (structInfo.fields) |field| {
17431744 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 }
17451776 }
17461777 }
17471778 },
......@@ -1806,8 +1837,12 @@ pub const Parser = struct {
18061837 pub fn parse(p: *Parser, input: []const u8) !ValueTree {
18071838 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);
18101844 errdefer arena.deinit();
1845
18111846 const allocator = arena.allocator();
18121847
18131848 while (try s.next()) |token| {
lib/std/json/test.zig+51
......@@ -2238,6 +2238,39 @@ test "parse into struct with no fields" {
22382238 try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));
22392239}
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
22412274test "parse into struct where destination and source lengths mismatch" {
22422275 const T = struct { a: [2]u8 };
22432276 var ts = TokenStream.init("{\"a\": \"bbb\"}");
......@@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" {
25812614 try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), ""));
25822615}
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
25842635test "integer after float has proper type" {
25852636 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
25862637 defer arena_allocator.deinit();