std.json: fix key allocation in HashMap for streaming json parsing (#16422)

This commit is contained in:
Garrett 2023-07-22 17:33:50 -05:00 committed by GitHub
parent 32a175740c
commit 04c7b55de4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 6 deletions

View File

@ -24,15 +24,10 @@ pub fn ArrayHashMap(comptime T: type) type {
if (.object_begin != try source.next()) return error.UnexpectedToken; if (.object_begin != try source.next()) return error.UnexpectedToken;
while (true) { while (true) {
const token = try source.nextAlloc(allocator, .alloc_if_needed); const token = try source.nextAlloc(allocator, options.allocate.?);
switch (token) { switch (token) {
inline .string, .allocated_string => |k| { inline .string, .allocated_string => |k| {
const gop = try map.getOrPut(allocator, k); const gop = try map.getOrPut(allocator, k);
if (token == .allocated_string) {
// Free the key before recursing in case we're using an allocator
// that optimizes freeing the last allocated object.
allocator.free(k);
}
if (gop.found_existing) { if (gop.found_existing) {
switch (options.duplicate_field_behavior) { switch (options.duplicate_field_behavior) {
.use_first => { .use_first => {

View File

@ -5,10 +5,13 @@ const ArrayHashMap = @import("hashmap.zig").ArrayHashMap;
const parseFromSlice = @import("static.zig").parseFromSlice; const parseFromSlice = @import("static.zig").parseFromSlice;
const parseFromSliceLeaky = @import("static.zig").parseFromSliceLeaky; const parseFromSliceLeaky = @import("static.zig").parseFromSliceLeaky;
const parseFromTokenSource = @import("static.zig").parseFromTokenSource;
const parseFromValue = @import("static.zig").parseFromValue; const parseFromValue = @import("static.zig").parseFromValue;
const stringifyAlloc = @import("stringify.zig").stringifyAlloc; const stringifyAlloc = @import("stringify.zig").stringifyAlloc;
const Value = @import("dynamic.zig").Value; const Value = @import("dynamic.zig").Value;
const jsonReader = @import("./scanner.zig").reader;
const T = struct { const T = struct {
i: i32, i: i32,
s: []const u8, s: []const u8,
@ -29,6 +32,31 @@ test "parse json hashmap" {
try testing.expectEqual(@as(i32, 1), parsed.value.map.get("xyz").?.i); try testing.expectEqual(@as(i32, 1), parsed.value.map.get("xyz").?.i);
} }
test "parse json hashmap while streaming" {
const doc =
\\{
\\ "abc": {"i": 0, "s": "d"},
\\ "xyz": {"i": 1, "s": "w"}
\\}
;
var stream = std.io.fixedBufferStream(doc);
var json_reader = jsonReader(testing.allocator, stream.reader());
var parsed = try parseFromTokenSource(
ArrayHashMap(T),
testing.allocator,
&json_reader,
.{},
);
defer parsed.deinit();
// Deinit our reader to invalidate its buffer
json_reader.deinit();
try testing.expectEqual(@as(usize, 2), parsed.value.map.count());
try testing.expectEqualStrings("d", parsed.value.map.get("abc").?.s);
try testing.expectEqual(@as(i32, 1), parsed.value.map.get("xyz").?.i);
}
test "parse json hashmap duplicate fields" { test "parse json hashmap duplicate fields" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator); var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit(); defer arena.deinit();