| author | |
| committer | |
| log | 22aa2bd1e775d5ec144711642ad5c0d7c99b4398 |
| tree | 31c1a4fc31f7bf64f225b6fbec8e9c05468018ef |
| parent | 420c1994b37a612fc37f011713ea01ee0497739a |
Fixes `std.json` so `ParseOptions.max_value_len` is honored when parsing sentinel-terminated strings that require allocation/copying.
Previously, the sentinel string path used `allocNextIntoArrayList`, which did not use the caller-provided `max_value_len`. This meant sentinel-terminated string parsing did not consistently follow the same allocation limit behavior as other allocating string paths.
`max_value_len` is an allocation/copy limit, not a general limit on the length of returned values. Values that can be returned as references to the input buffer, or parsed without intermediate allocation, are not limited by it.
Fixes #30579
Verified with:
```sh
zig test lib/std/std.zig --zig-lib-dir lib --test-filter max_value_len
```
Co-authored-by: nash1111 <nash1111@users.noreply.github.com>
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30782
Reviewed-by: Ryan Liptak <squeek502@noreply.codeberg.org>2 files changed, 36 insertions(+), 2 deletions(-)
lib/std/json/static.zig+2-1| ... | ... | @@ -32,6 +32,7 @@ pub const ParseOptions = struct { |
| 32 | 32 | /// The default for `parseFromSlice` or `parseFromTokenSource` with a `*std.json.Scanner` input |
| 33 | 33 | /// is the length of the input slice, which means `error.ValueTooLong` will never be returned. |
| 34 | 34 | /// The default for `parseFromTokenSource` with a `*std.json.Reader` is `std.json.default_max_value_len`. |
| 35 | /// Ignored for values that don't need allocation or are not copied (see `allocate`). | |
| 35 | 36 | /// Ignored for `parseFromValue` and `parseFromValueLeaky`. |
| 36 | 37 | max_value_len: ?usize = null, |
| 37 | 38 | |
| ... | ... | @@ -495,7 +496,7 @@ pub fn innerParse( |
| 495 | 496 | if (ptrInfo.sentinel()) |s| { |
| 496 | 497 | // Use our own array list so we can append the sentinel. |
| 497 | 498 | var value_list = ArrayList(u8).init(allocator); |
| 498 | _ = try source.allocNextIntoArrayList(&value_list, .alloc_always); | |
| 499 | _ = try source.allocNextIntoArrayListMax(&value_list, .alloc_always, options.max_value_len.?); | |
| 499 | 500 | return try value_list.toOwnedSliceSentinel(s); |
| 500 | 501 | } |
| 501 | 502 | if (ptrInfo.attrs.@"const") { |
lib/std/json/static_test.zig+34-1| ... | ... | @@ -772,7 +772,40 @@ test "parseFromTokenSource" { |
| 772 | 772 | } |
| 773 | 773 | |
| 774 | 774 | test "max_value_len" { |
| 775 | try testing.expectError(error.ValueTooLong, parseFromSlice([]u8, testing.allocator, "\"0123456789\"", .{ .max_value_len = 5 })); | |
| 775 | try testMaxValueLen([]u8, .alloc_if_needed); | |
| 776 | try testMaxValueLen([]const u8, .alloc_always); | |
| 777 | try testMaxValueLen([:0]u8, .alloc_if_needed); | |
| 778 | try testMaxValueLen([:0]const u8, .alloc_if_needed); | |
| 779 | ||
| 780 | // If the value can be returned as a reference to the buffer, max_value_len doesn't apply. | |
| 781 | { | |
| 782 | const parsed = try parseFromSlice([]const u8, testing.allocator, "\"123\"", .{ .max_value_len = 1 }); | |
| 783 | defer parsed.deinit(); | |
| 784 | try testing.expectEqualStrings("123", parsed.value); | |
| 785 | } | |
| 786 | // If the value is returned as a number without needing intermediate allocations, max_value_len doesn't apply. | |
| 787 | { | |
| 788 | const parsed = try parseFromSlice(u32, testing.allocator, "\"001\"", .{ .max_value_len = 1 }); | |
| 789 | defer parsed.deinit(); | |
| 790 | try testing.expectEqual(1, parsed.value); | |
| 791 | } | |
| 792 | } | |
| 793 | ||
| 794 | fn testMaxValueLen(comptime T: type, when: Scanner.AllocWhen) !void { | |
| 795 | const parsed = try parseFromSlice(T, testing.allocator, "\"12345\"", .{ | |
| 796 | .max_value_len = 5, | |
| 797 | .allocate = when, | |
| 798 | }); | |
| 799 | defer parsed.deinit(); | |
| 800 | try testing.expectEqualStrings("12345", parsed.value); | |
| 801 | ||
| 802 | try testing.expectError( | |
| 803 | error.ValueTooLong, | |
| 804 | parseFromSlice(T, testing.allocator, "\"123456\"", .{ | |
| 805 | .max_value_len = 5, | |
| 806 | .allocate = when, | |
| 807 | }), | |
| 808 | ); | |
| 776 | 809 | } |
| 777 | 810 | |
| 778 | 811 | test "parse into vector" { |