authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-08 15:28:36-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-09 12:04:10-07:00
log5c0ac90721ec45c6712535a99cb90df48e59e121
tree02a2b6fe745af5d17fdffdf65307c917a69cac71
parentacd6ffdf69332d61b51f64f4707b472c147b7034

std: fix File.Writer sendfile with buffered contents

* File.Writer.seekBy passed wrong offset to setPosAdjustingBuffer. * File.Writer.sendFile incorrectly used non-logical position. Related to 1d764c1fdf04829cec5974d82cec901825a80e49 Test case provided by: Co-authored-by: Kendall Condon <goon.pri.low@gmail.com>

2 files changed, 40 insertions(+), 9 deletions(-)

lib/std/fs/File.zig+9-9
......@@ -1242,7 +1242,7 @@ pub const Reader = struct {
12421242 pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void {
12431243 switch (r.mode) {
12441244 .positional, .positional_reading => {
1245 setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset));
1245 setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
12461246 },
12471247 .streaming, .streaming_reading => {
12481248 if (posix.SEEK == void) {
......@@ -1251,7 +1251,7 @@ pub const Reader = struct {
12511251 }
12521252 const seek_err = r.seek_err orelse e: {
12531253 if (posix.lseek_CUR(r.file.handle, offset)) |_| {
1254 setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset));
1254 setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
12551255 return;
12561256 } else |err| {
12571257 r.seek_err = err;
......@@ -1275,16 +1275,16 @@ pub const Reader = struct {
12751275 pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {
12761276 switch (r.mode) {
12771277 .positional, .positional_reading => {
1278 setPosAdjustingBuffer(r, offset);
1278 setLogicalPos(r, offset);
12791279 },
12801280 .streaming, .streaming_reading => {
1281 if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos));
1281 if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - logicalPos(r)));
12821282 if (r.seek_err) |err| return err;
12831283 posix.lseek_SET(r.file.handle, offset) catch |err| {
12841284 r.seek_err = err;
12851285 return err;
12861286 };
1287 setPosAdjustingBuffer(r, offset);
1287 setLogicalPos(r, offset);
12881288 },
12891289 .failure => return r.seek_err.?,
12901290 }
......@@ -1294,7 +1294,7 @@ pub const Reader = struct {
12941294 return r.pos - r.interface.bufferedLen();
12951295 }
12961296
1297 fn setPosAdjustingBuffer(r: *Reader, offset: u64) void {
1297 fn setLogicalPos(r: *Reader, offset: u64) void {
12981298 const logical_pos = logicalPos(r);
12991299 if (offset < logical_pos or offset >= r.pos) {
13001300 r.interface.seek = 0;
......@@ -1855,7 +1855,7 @@ pub const Writer = struct {
18551855 return error.EndOfStream;
18561856 }
18571857 const consumed = io_w.consume(@intCast(sbytes));
1858 file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;
1858 file_reader.seekBy(@intCast(consumed)) catch return error.ReadFailed;
18591859 return consumed;
18601860 }
18611861
......@@ -1916,7 +1916,7 @@ pub const Writer = struct {
19161916 return error.EndOfStream;
19171917 }
19181918 const consumed = io_w.consume(@bitCast(len));
1919 file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;
1919 file_reader.seekBy(@intCast(consumed)) catch return error.ReadFailed;
19201920 return consumed;
19211921 }
19221922
......@@ -2049,7 +2049,7 @@ pub const Writer = struct {
20492049 reader_buffered: []const u8,
20502050 ) std.Io.Writer.FileError!usize {
20512051 const n = try drain(io_w, &.{reader_buffered}, 1);
2052 file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;
2052 file_reader.seekBy(@intCast(n)) catch return error.ReadFailed;
20532053 return n;
20542054 }
20552055
lib/std/fs/test.zig+31
......@@ -2145,3 +2145,34 @@ test "seekBy" {
21452145 try testing.expectEqual(15, n);
21462146 try testing.expectEqualStrings("t's test seekBy", buffer[0..15]);
21472147}
2148
2149test "File.Writer sendfile with buffered contents" {
2150 var tmp_dir = testing.tmpDir(.{});
2151 defer tmp_dir.cleanup();
2152
2153 {
2154 try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" });
2155 const in = try tmp_dir.dir.openFile("a", .{});
2156 defer in.close();
2157 const out = try tmp_dir.dir.createFile("b", .{});
2158 defer out.close();
2159
2160 var in_buf: [2]u8 = undefined;
2161 var in_r = in.reader(&in_buf);
2162 _ = try in_r.getSize(); // Catch seeks past end by populating size
2163 try in_r.interface.fill(2);
2164
2165 var out_buf: [1]u8 = undefined;
2166 var out_w = out.writerStreaming(&out_buf);
2167 try out_w.interface.writeByte('a');
2168 try testing.expectEqual(3, try out_w.interface.sendFileAll(&in_r, .unlimited));
2169 try out_w.interface.flush();
2170 }
2171
2172 var check = try tmp_dir.dir.openFile("b", .{});
2173 defer check.close();
2174 var check_buf: [4]u8 = undefined;
2175 var check_r = check.reader(&check_buf);
2176 try testing.expectEqualStrings("abcd", try check_r.interface.take(4));
2177 try testing.expectError(error.EndOfStream, check_r.interface.takeByte());
2178}