Skip to content

Commit 2628d28

Browse files
committed
Reader: fix fill when implementation writes to reader directly
`r.end += stream` is the same as `r.end = r.end + stream`, which causes `r.end` to be analyzed before stream is called and that value is added to. However, the implementation is allowed to write directly to `r` (for example, inflate does this to keep a window) and update `r.end` independently, causing the outdated value to be added to.
1 parent 765825b commit 2628d28

File tree

1 file changed

+26
-7
lines changed

1 file changed

+26
-7
lines changed

lib/std/Io/Reader.zig

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,10 +1065,10 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void {
10651065
};
10661066
while (r.end < r.seek + n) {
10671067
writer.end = r.end;
1068-
r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
1068+
r.end = (r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
10691069
error.WriteFailed => unreachable,
10701070
error.ReadFailed, error.EndOfStream => |e| return e,
1071-
};
1071+
}) + r.end; // r.end must be evaluated last since r.vtable.stream may change it
10721072
}
10731073
}
10741074

@@ -1084,10 +1084,10 @@ pub fn fillMore(r: *Reader) Error!void {
10841084
.end = r.end,
10851085
.vtable = &.{ .drain = Writer.fixedDrain },
10861086
};
1087-
r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
1087+
r.end = (r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
10881088
error.WriteFailed => unreachable,
10891089
else => |e| return e,
1090-
};
1090+
}) + r.end; // r.end must be evaluated last since r.vtable.stream may change it
10911091
}
10921092

10931093
/// Returns the next byte from the stream or returns `error.EndOfStream`.
@@ -1514,9 +1514,28 @@ test discardDelimiterLimit {
15141514
}
15151515

15161516
test fill {
1517-
var r: Reader = .fixed("abc");
1518-
try r.fill(1);
1519-
try r.fill(3);
1517+
var fixed_r: Reader = .fixed("abc");
1518+
try fixed_r.fill(1);
1519+
try fixed_r.fill(3);
1520+
1521+
// Check writing directly to the reader buffer
1522+
var buf: [2]u8 = undefined;
1523+
var indirect_r: Reader = .{
1524+
.buffer = &buf,
1525+
.seek = 0,
1526+
.end = 0,
1527+
.vtable = &.{
1528+
.stream = struct {
1529+
fn stream(r: *Reader, _: *Writer, _: Limit) StreamError!usize {
1530+
@memset(r.buffer[r.seek..], 0xff);
1531+
r.end = r.buffer.len;
1532+
return 0;
1533+
}
1534+
}.stream,
1535+
},
1536+
};
1537+
try indirect_r.fill(2);
1538+
try std.testing.expectEqualSlices(u8, &.{ 0xff, 0xff }, indirect_r.buffered());
15201539
}
15211540

15221541
test takeByte {

0 commit comments

Comments
 (0)