Skip to content

Commit 5f14aca

Browse files
committed
Reader.defaultDiscard: Fix for use with an indirect reader
If a Reader implementation implements `stream` by ignoring the Writer, writing directly to its internal buffer, and returning 0, then `defaultDiscard` would not update `seek` and also return 0, which is incorrect and can cause `discardShort` to violate the contract of `VTable.discard` by calling into `vtable.discard` with a non-empty buffer. This commit fixes the problem by advancing seek up to the limit after the stream call. This logic could likely be somewhat simplified in the future depending on how #25170 is resolved.
1 parent f785e47 commit 5f14aca

File tree

1 file changed

+19
-1
lines changed

1 file changed

+19
-1
lines changed

lib/std/Io/Reader.zig

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,17 @@ pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {
200200
r.seek = 0;
201201
r.end = 0;
202202
var d: Writer.Discarding = .init(r.buffer);
203-
const n = r.stream(&d.writer, limit) catch |err| switch (err) {
203+
var n = r.stream(&d.writer, limit) catch |err| switch (err) {
204204
error.WriteFailed => unreachable,
205205
error.ReadFailed => return error.ReadFailed,
206206
error.EndOfStream => return error.EndOfStream,
207207
};
208+
// If `stream` wrote to `r.buffer` without going through the writer,
209+
// we need to discard as much of the buffered data as possible.
210+
const remaining = @intFromEnum(limit) - n;
211+
const buffered_n_to_discard = @min(remaining, r.end - r.seek);
212+
n += buffered_n_to_discard;
213+
r.seek += buffered_n_to_discard;
208214
assert(n <= @intFromEnum(limit));
209215
return n;
210216
}
@@ -1720,6 +1726,18 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize {
17201726
return error.ReadFailed;
17211727
}
17221728

1729+
test "discardAll that has to call discard multiple times on an indirect reader" {
1730+
var fr: Reader = .fixed("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
1731+
var indirect_buffer: [3]u8 = undefined;
1732+
var tri: std.testing.ReaderIndirect = .init(&fr, &indirect_buffer);
1733+
const r = &tri.interface;
1734+
1735+
try r.discardAll(10);
1736+
var remaining_buf: [16]u8 = undefined;
1737+
try r.readSliceAll(&remaining_buf);
1738+
try std.testing.expectEqualStrings(fr.buffer[10..], remaining_buf[0..]);
1739+
}
1740+
17231741
test "readAlloc when the backing reader provides one byte at a time" {
17241742
const str = "This is a test";
17251743
var tiny_buffer: [1]u8 = undefined;

0 commit comments

Comments
 (0)