| author | |
| committer | |
| log | fc48467a97021cb872ff2a947f96e882274c39c1 |
| tree | fc86aeb23dd92fb36a15b33c4a228979f1e5e42b |
| parent | c8dc00086e6622f3f29d7e18deb92ea102cf1074 |
| parent | b1dd4b17d89633c4d26c39952f69d7ff2efb98ce |
| signature |
Some std.json fixes2 files changed, 90 insertions(+), 4 deletions(-)
lib/std/json.zig+39-4| ... | ... | @@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList; |
| 1163 | 1163 | const StringArrayHashMap = std.StringArrayHashMap; |
| 1164 | 1164 | |
| 1165 | 1165 | pub const ValueTree = struct { |
| 1166 | arena: ArenaAllocator, | |
| 1166 | arena: *ArenaAllocator, | |
| 1167 | 1167 | root: Value, |
| 1168 | 1168 | |
| 1169 | 1169 | pub fn deinit(self: *ValueTree) void { |
| 1170 | 1170 | self.arena.deinit(); |
| 1171 | self.arena.child_allocator.destroy(self.arena); | |
| 1171 | 1172 | } |
| 1172 | 1173 | }; |
| 1173 | 1174 | |
| ... | ... | @@ -1639,7 +1640,7 @@ fn parseInternal( |
| 1639 | 1640 | const allocator = options.allocator orelse return error.AllocatorRequired; |
| 1640 | 1641 | switch (ptrInfo.size) { |
| 1641 | 1642 | .One => { |
| 1642 | const r: T = try allocator.create(ptrInfo.child); | |
| 1643 | const r: *ptrInfo.child = try allocator.create(ptrInfo.child); | |
| 1643 | 1644 | errdefer allocator.destroy(r); |
| 1644 | 1645 | r.* = try parseInternal(ptrInfo.child, token, tokens, options); |
| 1645 | 1646 | return r; |
| ... | ... | @@ -1741,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void { |
| 1741 | 1742 | .Struct => |structInfo| { |
| 1742 | 1743 | inline for (structInfo.fields) |field| { |
| 1743 | 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 | 1837 | pub fn parse(p: *Parser, input: []const u8) !ValueTree { |
| 1807 | 1838 | var s = TokenStream.init(input); |
| 1808 | 1839 | |
| 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 | 1844 | errdefer arena.deinit(); |
| 1845 | ||
| 1811 | 1846 | const allocator = arena.allocator(); |
| 1812 | 1847 | |
| 1813 | 1848 | while (try s.next()) |token| { |
lib/std/json/test.zig+51| ... | ... | @@ -2238,6 +2238,39 @@ test "parse into struct with no fields" { |
| 2238 | 2238 | try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{})); |
| 2239 | 2239 | } |
| 2240 | 2240 | |
| 2241 | const test_const_value: usize = 123; | |
| 2242 | ||
| 2243 | test "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 | ||
| 2249 | const test_default_usize: usize = 123; | |
| 2250 | const test_default_usize_ptr: *align(1) const usize = &test_default_usize; | |
| 2251 | const test_default_str: []const u8 = "test str"; | |
| 2252 | const test_default_str_slice: [2][]const u8 = [_][]const u8{ | |
| 2253 | "test1", | |
| 2254 | "test2", | |
| 2255 | }; | |
| 2256 | ||
| 2257 | test "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 | ||
| 2241 | 2274 | test "parse into struct where destination and source lengths mismatch" { |
| 2242 | 2275 | const T = struct { a: [2]u8 }; |
| 2243 | 2276 | var ts = TokenStream.init("{\"a\": \"bbb\"}"); |
| ... | ... | @@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" { |
| 2581 | 2614 | try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), "")); |
| 2582 | 2615 | } |
| 2583 | 2616 | |
| 2617 | test "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 | ||
| 2584 | 2635 | test "integer after float has proper type" { |
| 2585 | 2636 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); |
| 2586 | 2637 | defer arena_allocator.deinit(); |