authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-08 16:43:24-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-08 19:22:08-07:00
logdf46ee61c4a853c0cf43007306bdc44e578d0702
tree4dc63def2bc489ea9aeada8da5e5b7cd4f267d36
parent5f7a0bbabfde4eeb0ff4f40f0942ef710b6104a1

std.Io.Writer.Allocating: configurable bump amount


2 files changed, 15 insertions(+), 3 deletions(-)

lib/std/Io/Writer.zig+6-3
......@@ -2497,6 +2497,10 @@ pub fn Hashing(comptime Hasher: type) type {
24972497pub const Allocating = struct {
24982498 allocator: Allocator,
24992499 writer: Writer,
2500 /// Every call to `drain` ensures at least this amount of unused capacity
2501 /// before it returns. This prevents an infinite loop in interface logic
2502 /// that calls `drain`.
2503 minimum_unused_capacity: usize = 1,
25002504
25012505 pub fn init(allocator: Allocator) Allocating {
25022506 return .{
......@@ -2607,14 +2611,13 @@ pub const Allocating = struct {
26072611 const gpa = a.allocator;
26082612 const pattern = data[data.len - 1];
26092613 const splat_len = pattern.len * splat;
2614 const bump = a.minimum_unused_capacity;
26102615 var list = a.toArrayList();
26112616 defer setArrayList(a, list);
26122617 const start_len = list.items.len;
2613 // Even if we append no data, this function needs to ensure there is more
2614 // capacity in the buffer to avoid infinite loop, hence the +1 in this loop.
26152618 assert(data.len != 0);
26162619 for (data) |bytes| {
2617 list.ensureUnusedCapacity(gpa, bytes.len + splat_len + 1) catch return error.WriteFailed;
2620 list.ensureUnusedCapacity(gpa, bytes.len + splat_len + bump) catch return error.WriteFailed;
26182621 list.appendSliceAssumeCapacity(bytes);
26192622 }
26202623 if (splat == 0) {
lib/std/compress/flate/Decompress.zig+9
......@@ -73,7 +73,12 @@ const indirect_vtable: Reader.VTable = .{
7373 .readVec = readVec,
7474};
7575
76/// `input` buffer is asserted to be at least 10 bytes, or EOF before then.
77///
78/// If `buffer` is provided then asserted to have `flate.max_window_len`
79/// capacity, as well as `flate.history_len` unused capacity on every write.
7680pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {
81 if (buffer.len != 0) assert(buffer.len >= flate.max_window_len);
7782 return .{
7883 .reader = .{
7984 .vtable = if (buffer.len == 0) &direct_vtable else &indirect_vtable,
......@@ -234,6 +239,8 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
234239}
235240
236241fn streamDirect(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
242 assert(w.buffer.len >= flate.max_window_len);
243 assert(w.unusedCapacityLen() >= flate.history_len);
237244 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
238245 return streamFallible(d, w, limit);
239246}
......@@ -1246,6 +1253,7 @@ test "zlib should not overshoot" {
12461253fn testFailure(container: Container, in: []const u8, expected_err: anyerror) !void {
12471254 var reader: Reader = .fixed(in);
12481255 var aw: Writer.Allocating = .init(testing.allocator);
1256 aw.minimum_unused_capacity = flate.history_len;
12491257 try aw.ensureUnusedCapacity(flate.max_window_len);
12501258 defer aw.deinit();
12511259
......@@ -1257,6 +1265,7 @@ fn testFailure(container: Container, in: []const u8, expected_err: anyerror) !vo
12571265fn testDecompress(container: Container, compressed: []const u8, expected_plain: []const u8) !void {
12581266 var in: std.Io.Reader = .fixed(compressed);
12591267 var aw: std.Io.Writer.Allocating = .init(testing.allocator);
1268 aw.minimum_unused_capacity = flate.history_len;
12601269 try aw.ensureUnusedCapacity(flate.max_window_len);
12611270 defer aw.deinit();
12621271