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

Some std.json fixes
This commit is contained in:
Veikka Tuominen
2023-02-13 16:24:29 +02:00
committed by GitHub
2 changed files with 90 additions and 4 deletions

View File

@@ -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;
@@ -1741,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);
}
}
}
},
@@ -1806,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| {

View File

@@ -2238,6 +2238,39 @@ test "parse into struct with no fields" {
try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));
}
const test_const_value: usize = 123;
test "parse into struct with default const pointer field" {
const T = struct { a: *const usize = &test_const_value };
var ts = TokenStream.init("{}");
try testing.expectEqual(T{}, try parse(T, &ts, .{}));
}
const test_default_usize: usize = 123;
const test_default_usize_ptr: *align(1) const usize = &test_default_usize;
const test_default_str: []const u8 = "test str";
const test_default_str_slice: [2][]const u8 = [_][]const u8{
"test1",
"test2",
};
test "freeing parsed structs with pointers to default values" {
const T = struct {
int: *const usize = &test_default_usize,
int_ptr: *allowzero align(1) const usize = test_default_usize_ptr,
str: []const u8 = test_default_str,
str_slice: []const []const u8 = &test_default_str_slice,
};
var ts = json.TokenStream.init("{}");
const options = .{ .allocator = std.heap.page_allocator };
const parsed = try json.parse(T, &ts, options);
try testing.expectEqual(T{}, parsed);
json.parseFree(T, parsed, options);
}
test "parse into struct where destination and source lengths mismatch" {
const T = struct { a: [2]u8 };
var ts = TokenStream.init("{\"a\": \"bbb\"}");
@@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" {
try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), ""));
}
test "parse tree should not contain dangling pointers" {
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_allocator.deinit();
var p = json.Parser.init(arena_allocator.allocator(), false);
defer p.deinit();
var tree = try p.parse("[]");
defer tree.deinit();
// Allocation should succeed
var i: usize = 0;
while (i < 100) : (i += 1) {
try tree.root.Array.append(std.json.Value{ .Integer = 100 });
}
try testing.expectEqual(tree.root.Array.items.len, 100);
}
test "integer after float has proper type" {
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_allocator.deinit();