aboutsummaryrefslogtreecommitdiff
path: root/lib/std/json.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-02-18 09:33:27 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-02-18 09:33:27 -0700
commitefdc94c10712f610e7de5e49fd9cd6f88b4bbbae (patch)
tree4b66ec30176843b0efd87b73199c75aa2fba675d /lib/std/json.zig
parent06df842e4d313e81444063803deff306602e0a17 (diff)
parentc32171991b25b323cd68ff96c294bf5a6fa753b8 (diff)
downloadzig-efdc94c10712f610e7de5e49fd9cd6f88b4bbbae.tar.gz
zig-efdc94c10712f610e7de5e49fd9cd6f88b4bbbae.zip
Merge remote-tracking branch 'origin/master' into llvm16
Diffstat (limited to 'lib/std/json.zig')
-rw-r--r--lib/std/json.zig67
1 files changed, 56 insertions, 11 deletions
diff --git a/lib/std/json.zig b/lib/std/json.zig
index 92afeead90..96e41e93c2 100644
--- a/lib/std/json.zig
+++ b/lib/std/json.zig
@@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList;
const StringArrayHashMap = std.StringArrayHashMap;
pub const ValueTree = struct {
- arena: ArenaAllocator,
+ arena: *ArenaAllocator,
root: Value,
pub fn deinit(self: *ValueTree) void {
self.arena.deinit();
+ self.arena.child_allocator.destroy(self.arena);
}
};
@@ -1639,7 +1640,7 @@ fn parseInternal(
const allocator = options.allocator orelse return error.AllocatorRequired;
switch (ptrInfo.size) {
.One => {
- const r: T = try allocator.create(ptrInfo.child);
+ const r: *ptrInfo.child = try allocator.create(ptrInfo.child);
errdefer allocator.destroy(r);
r.* = try parseInternal(ptrInfo.child, token, tokens, options);
return r;
@@ -1678,19 +1679,16 @@ fn parseInternal(
if (ptrInfo.child != u8) return error.UnexpectedToken;
const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
const len = stringToken.decodedLength();
- const output = try allocator.alloc(u8, len + @boolToInt(ptrInfo.sentinel != null));
+ const output = if (ptrInfo.sentinel) |sentinel_ptr|
+ try allocator.allocSentinel(u8, len, @ptrCast(*const u8, sentinel_ptr).*)
+ else
+ try allocator.alloc(u8, len);
errdefer allocator.free(output);
switch (stringToken.escapes) {
.None => mem.copy(u8, output, source_slice),
.Some => try unescapeValidString(output, source_slice),
}
- if (ptrInfo.sentinel) |some| {
- const char = @ptrCast(*const u8, some).*;
- output[len] = char;
- return output[0..len :char];
- }
-
return output;
},
else => return error.UnexpectedToken,
@@ -1744,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
.Struct => |structInfo| {
inline for (structInfo.fields) |field| {
if (!field.is_comptime) {
- parseFree(field.type, @field(value, field.name), options);
+ var should_free = true;
+ if (field.default_value) |default| {
+ switch (@typeInfo(field.type)) {
+ // We must not attempt to free pointers to struct default values
+ .Pointer => |fieldPtrInfo| {
+ const field_value = @field(value, field.name);
+ const field_ptr = switch (fieldPtrInfo.size) {
+ .One => field_value,
+ .Slice => field_value.ptr,
+ else => unreachable, // Other pointer types are not parseable
+ };
+ const field_addr = @ptrToInt(field_ptr);
+
+ const casted_default = @ptrCast(*const field.type, @alignCast(@alignOf(field.type), default)).*;
+ const default_ptr = switch (fieldPtrInfo.size) {
+ .One => casted_default,
+ .Slice => casted_default.ptr,
+ else => unreachable, // Other pointer types are not parseable
+ };
+ const default_addr = @ptrToInt(default_ptr);
+
+ if (field_addr == default_addr) {
+ should_free = false;
+ }
+ },
+ else => {},
+ }
+ }
+ if (should_free) {
+ parseFree(field.type, @field(value, field.name), options);
+ }
}
}
},
@@ -1809,8 +1837,12 @@ pub const Parser = struct {
pub fn parse(p: *Parser, input: []const u8) !ValueTree {
var s = TokenStream.init(input);
- var arena = ArenaAllocator.init(p.allocator);
+ var arena = try p.allocator.create(ArenaAllocator);
+ errdefer p.allocator.destroy(arena);
+
+ arena.* = ArenaAllocator.init(p.allocator);
errdefer arena.deinit();
+
const allocator = arena.allocator();
while (try s.next()) |token| {
@@ -2684,3 +2716,16 @@ test "encodesTo" {
try testing.expectEqual(true, encodesTo("😂", "\\ud83d\\ude02"));
try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02"));
}
+
+test "issue 14600" {
+ const json = "\"\\n\"";
+ var token_stream = std.json.TokenStream.init(json);
+ const options = ParseOptions{ .allocator = std.testing.allocator };
+
+ // Pre-fix, this line would panic:
+ const result = try std.json.parse([:0]const u8, &token_stream, options);
+ defer std.json.parseFree([:0]const u8, result, options);
+
+ // Double-check that we're getting the right result
+ try testing.expect(mem.eql(u8, result, "\n"));
+}