Commit 04c7b55de4

Garrett <138411610+garrettlennoxbeck@users.noreply.github.com>
2023-07-23 00:33:50
std.json: fix key allocation in HashMap for streaming json parsing (#16422)
1 parent 32a1757
Changed files (2)
lib/std/json/hashmap.zig
@@ -24,15 +24,10 @@ pub fn ArrayHashMap(comptime T: type) type {
 
             if (.object_begin != try source.next()) return error.UnexpectedToken;
             while (true) {
-                const token = try source.nextAlloc(allocator, .alloc_if_needed);
+                const token = try source.nextAlloc(allocator, options.allocate.?);
                 switch (token) {
                     inline .string, .allocated_string => |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) {
                             switch (options.duplicate_field_behavior) {
                                 .use_first => {
lib/std/json/hashmap_test.zig
@@ -5,10 +5,13 @@ const ArrayHashMap = @import("hashmap.zig").ArrayHashMap;
 
 const parseFromSlice = @import("static.zig").parseFromSlice;
 const parseFromSliceLeaky = @import("static.zig").parseFromSliceLeaky;
+const parseFromTokenSource = @import("static.zig").parseFromTokenSource;
 const parseFromValue = @import("static.zig").parseFromValue;
 const stringifyAlloc = @import("stringify.zig").stringifyAlloc;
 const Value = @import("dynamic.zig").Value;
 
+const jsonReader = @import("./scanner.zig").reader;
+
 const T = struct {
     i: i32,
     s: []const u8,
@@ -29,6 +32,31 @@ test "parse json hashmap" {
     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" {
     var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
     defer arena.deinit();