authorgravatar for garrettlennoxbeck@gmail.comGarrett Beck <garrettlennoxbeck@gmail.com> 2023-07-22 17:33:50-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-07-22 22:33:50+00:00
log04c7b55de49b3f5129098e079e6cde7704c70293
tree31e54e9d524ab92d46c5f7bf6ba22938d021383f
parent32a175740c8750a7db550011795a4c22c0d0bc93
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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


2 files changed, 29 insertions(+), 6 deletions(-)

lib/std/json/hashmap.zig+1-6
......@@ -24,15 +24,10 @@ pub fn ArrayHashMap(comptime T: type) type {
2424
2525 if (.object_begin != try source.next()) return error.UnexpectedToken;
2626 while (true) {
27 const token = try source.nextAlloc(allocator, .alloc_if_needed);
27 const token = try source.nextAlloc(allocator, options.allocate.?);
2828 switch (token) {
2929 inline .string, .allocated_string => |k| {
3030 const gop = try map.getOrPut(allocator, k);
31 if (token == .allocated_string) {
32 // Free the key before recursing in case we're using an allocator
33 // that optimizes freeing the last allocated object.
34 allocator.free(k);
35 }
3631 if (gop.found_existing) {
3732 switch (options.duplicate_field_behavior) {
3833 .use_first => {
lib/std/json/hashmap_test.zig+28
......@@ -5,10 +5,13 @@ const ArrayHashMap = @import("hashmap.zig").ArrayHashMap;
55
66const parseFromSlice = @import("static.zig").parseFromSlice;
77const parseFromSliceLeaky = @import("static.zig").parseFromSliceLeaky;
8const parseFromTokenSource = @import("static.zig").parseFromTokenSource;
89const parseFromValue = @import("static.zig").parseFromValue;
910const stringifyAlloc = @import("stringify.zig").stringifyAlloc;
1011const Value = @import("dynamic.zig").Value;
1112
13const jsonReader = @import("./scanner.zig").reader;
14
1215const T = struct {
1316 i: i32,
1417 s: []const u8,
......@@ -29,6 +32,31 @@ test "parse json hashmap" {
2932 try testing.expectEqual(@as(i32, 1), parsed.value.map.get("xyz").?.i);
3033}
3134
35test "parse json hashmap while streaming" {
36 const doc =
37 \\{
38 \\ "abc": {"i": 0, "s": "d"},
39 \\ "xyz": {"i": 1, "s": "w"}
40 \\}
41 ;
42 var stream = std.io.fixedBufferStream(doc);
43 var json_reader = jsonReader(testing.allocator, stream.reader());
44
45 var parsed = try parseFromTokenSource(
46 ArrayHashMap(T),
47 testing.allocator,
48 &json_reader,
49 .{},
50 );
51 defer parsed.deinit();
52 // Deinit our reader to invalidate its buffer
53 json_reader.deinit();
54
55 try testing.expectEqual(@as(usize, 2), parsed.value.map.count());
56 try testing.expectEqualStrings("d", parsed.value.map.get("abc").?.s);
57 try testing.expectEqual(@as(i32, 1), parsed.value.map.get("xyz").?.i);
58}
59
3260test "parse json hashmap duplicate fields" {
3361 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
3462 defer arena.deinit();