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 {...@@ -2497,6 +2497,10 @@ pub fn Hashing(comptime Hasher: type) type {
2497pub const Allocating = struct {2497pub const Allocating = struct {
2498 allocator: Allocator,2498 allocator: Allocator,
2499 writer: Writer,2499 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
2501 pub fn init(allocator: Allocator) Allocating {2505 pub fn init(allocator: Allocator) Allocating {
2502 return .{2506 return .{
...@@ -2607,14 +2611,13 @@ pub const Allocating = struct {...@@ -2607,14 +2611,13 @@ pub const Allocating = struct {
2607 const gpa = a.allocator;2611 const gpa = a.allocator;
2608 const pattern = data[data.len - 1];2612 const pattern = data[data.len - 1];
2609 const splat_len = pattern.len * splat;2613 const splat_len = pattern.len * splat;
2614 const bump = a.minimum_unused_capacity;
2610 var list = a.toArrayList();2615 var list = a.toArrayList();
2611 defer setArrayList(a, list);2616 defer setArrayList(a, list);
2612 const start_len = list.items.len;2617 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.
2615 assert(data.len != 0);2618 assert(data.len != 0);
2616 for (data) |bytes| {2619 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;
2618 list.appendSliceAssumeCapacity(bytes);2621 list.appendSliceAssumeCapacity(bytes);
2619 }2622 }
2620 if (splat == 0) {2623 if (splat == 0) {
lib/std/compress/flate/Decompress.zig+9
...@@ -73,7 +73,12 @@ const indirect_vtable: Reader.VTable = .{...@@ -73,7 +73,12 @@ const indirect_vtable: Reader.VTable = .{
73 .readVec = readVec,73 .readVec = readVec,
74};74};
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.
76pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {80pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {
81 if (buffer.len != 0) assert(buffer.len >= flate.max_window_len);
77 return .{82 return .{
78 .reader = .{83 .reader = .{
79 .vtable = if (buffer.len == 0) &direct_vtable else &indirect_vtable,84 .vtable = if (buffer.len == 0) &direct_vtable else &indirect_vtable,
...@@ -234,6 +239,8 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {...@@ -234,6 +239,8 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
234}239}
235240
236fn streamDirect(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {241fn 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);
237 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));244 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
238 return streamFallible(d, w, limit);245 return streamFallible(d, w, limit);
239}246}
...@@ -1246,6 +1253,7 @@ test "zlib should not overshoot" {...@@ -1246,6 +1253,7 @@ test "zlib should not overshoot" {
1246fn testFailure(container: Container, in: []const u8, expected_err: anyerror) !void {1253fn testFailure(container: Container, in: []const u8, expected_err: anyerror) !void {
1247 var reader: Reader = .fixed(in);1254 var reader: Reader = .fixed(in);
1248 var aw: Writer.Allocating = .init(testing.allocator);1255 var aw: Writer.Allocating = .init(testing.allocator);
1256 aw.minimum_unused_capacity = flate.history_len;
1249 try aw.ensureUnusedCapacity(flate.max_window_len);1257 try aw.ensureUnusedCapacity(flate.max_window_len);
1250 defer aw.deinit();1258 defer aw.deinit();
12511259
...@@ -1257,6 +1265,7 @@ fn testFailure(container: Container, in: []const u8, expected_err: anyerror) !vo...@@ -1257,6 +1265,7 @@ fn testFailure(container: Container, in: []const u8, expected_err: anyerror) !vo
1257fn testDecompress(container: Container, compressed: []const u8, expected_plain: []const u8) !void {1265fn testDecompress(container: Container, compressed: []const u8, expected_plain: []const u8) !void {
1258 var in: std.Io.Reader = .fixed(compressed);1266 var in: std.Io.Reader = .fixed(compressed);
1259 var aw: std.Io.Writer.Allocating = .init(testing.allocator);1267 var aw: std.Io.Writer.Allocating = .init(testing.allocator);
1268 aw.minimum_unused_capacity = flate.history_len;
1260 try aw.ensureUnusedCapacity(flate.max_window_len);1269 try aw.ensureUnusedCapacity(flate.max_window_len);
1261 defer aw.deinit();1270 defer aw.deinit();
12621271