mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
std.json: avoid stale pointers when parsing Value (#16864)
Closes #16861 Using `alloc_if_needed` when parsing a `Value` allows receiving a token which points to the buffer of the underlying `Reader`. This token will no longer be valid after the `Reader`'s buffer is refilled, which will happen with large values. Using `alloc_always` avoids this issue by ensuring the returned tokens always own their data independently of the underlying buffer.
This commit is contained in:
parent
5395c2786a
commit
f3f554b9b8
@ -93,11 +93,11 @@ pub const Value = union(enum) {
|
||||
stack.items[stack.items.len - 1] == .array or
|
||||
(stack.items[stack.items.len - 2] == .object and stack.items[stack.items.len - 1] == .string));
|
||||
|
||||
switch (try source.nextAlloc(allocator, .alloc_if_needed)) {
|
||||
inline .string, .allocated_string => |s| {
|
||||
switch (try source.nextAlloc(allocator, .alloc_always)) {
|
||||
.allocated_string => |s| {
|
||||
return try handleCompleteValue(&stack, allocator, source, Value{ .string = s }) orelse continue;
|
||||
},
|
||||
inline .number, .allocated_number => |slice| {
|
||||
.allocated_number => |slice| {
|
||||
return try handleCompleteValue(&stack, allocator, source, Value.parseFromNumberSlice(slice)) orelse continue;
|
||||
},
|
||||
|
||||
@ -106,9 +106,9 @@ pub const Value = union(enum) {
|
||||
.false => return try handleCompleteValue(&stack, allocator, source, Value{ .bool = false }) orelse continue,
|
||||
|
||||
.object_begin => {
|
||||
switch (try source.nextAlloc(allocator, .alloc_if_needed)) {
|
||||
switch (try source.nextAlloc(allocator, .alloc_always)) {
|
||||
.object_end => return try handleCompleteValue(&stack, allocator, source, Value{ .object = ObjectMap.init(allocator) }) orelse continue,
|
||||
inline .string, .allocated_string => |key| {
|
||||
.allocated_string => |key| {
|
||||
try stack.appendSlice(&[_]Value{
|
||||
Value{ .object = ObjectMap.init(allocator) },
|
||||
Value{ .string = key },
|
||||
@ -152,7 +152,7 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val
|
||||
|
||||
// This is an invalid state to leave the stack in,
|
||||
// so we have to process the next token before we return.
|
||||
switch (try source.nextAlloc(allocator, .alloc_if_needed)) {
|
||||
switch (try source.nextAlloc(allocator, .alloc_always)) {
|
||||
.object_end => {
|
||||
// This object is complete.
|
||||
value = stack.pop();
|
||||
@ -160,7 +160,7 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val
|
||||
if (stack.items.len == 0) return value;
|
||||
continue;
|
||||
},
|
||||
inline .string, .allocated_string => |next_key| {
|
||||
.allocated_string => |next_key| {
|
||||
// We've got another key.
|
||||
try stack.append(Value{ .string = next_key });
|
||||
// stack: [..., .object, .string]
|
||||
|
||||
@ -15,6 +15,7 @@ const parseFromValueLeaky = @import("static.zig").parseFromValueLeaky;
|
||||
const ParseOptions = @import("static.zig").ParseOptions;
|
||||
|
||||
const jsonReader = @import("scanner.zig").reader;
|
||||
const JsonReader = @import("scanner.zig").Reader;
|
||||
|
||||
test "json.parser.dynamic" {
|
||||
const s =
|
||||
@ -288,3 +289,42 @@ test "polymorphic parsing" {
|
||||
try testing.expect(tree.div.color == .blue);
|
||||
try testing.expectEqualStrings("Cancel", tree.div.children[1].button.caption);
|
||||
}
|
||||
|
||||
test "long object value" {
|
||||
const value = "01234567890123456789";
|
||||
const doc = "{\"key\":\"" ++ value ++ "\"}";
|
||||
var fbs = std.io.fixedBufferStream(doc);
|
||||
var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
|
||||
defer reader.deinit();
|
||||
var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
|
||||
defer parsed.deinit();
|
||||
|
||||
try testing.expectEqualStrings(value, parsed.value.object.get("key").?.string);
|
||||
}
|
||||
|
||||
test "many object keys" {
|
||||
const doc =
|
||||
\\{
|
||||
\\ "k1": "v1",
|
||||
\\ "k2": "v2",
|
||||
\\ "k3": "v3",
|
||||
\\ "k4": "v4",
|
||||
\\ "k5": "v5"
|
||||
\\}
|
||||
;
|
||||
var fbs = std.io.fixedBufferStream(doc);
|
||||
var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
|
||||
defer reader.deinit();
|
||||
var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
|
||||
defer parsed.deinit();
|
||||
|
||||
try testing.expectEqualStrings("v1", parsed.value.object.get("k1").?.string);
|
||||
try testing.expectEqualStrings("v2", parsed.value.object.get("k2").?.string);
|
||||
try testing.expectEqualStrings("v3", parsed.value.object.get("k3").?.string);
|
||||
try testing.expectEqualStrings("v4", parsed.value.object.get("k4").?.string);
|
||||
try testing.expectEqualStrings("v5", parsed.value.object.get("k5").?.string);
|
||||
}
|
||||
|
||||
fn smallBufferJsonReader(allocator: Allocator, io_reader: anytype) JsonReader(16, @TypeOf(io_reader)) {
|
||||
return JsonReader(16, @TypeOf(io_reader)).init(allocator, io_reader);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user