Skip to content

Commit df46ee6

Browse files
committed
std.Io.Writer.Allocating: configurable bump amount
1 parent 5f7a0bb commit df46ee6

File tree

2 files changed

+15
-3
lines changed

2 files changed

+15
-3
lines changed

lib/std/Io/Writer.zig

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,6 +2497,10 @@ pub fn Hashing(comptime Hasher: type) type {
24972497
pub 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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
7680
pub 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

236241
fn 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" {
12461253
fn 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
12571265
fn 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

0 commit comments

Comments
 (0)