Commit 7687b916fd

Kendall Condon <goon.pri.low@gmail.com>
2025-08-19 00:37:14
File.Writer.sendFile: properly update reader pos
seekBy affects the reader's physical position, not its logical position.
1 parent d94e061
Changed files (2)
lib
lib/std/fs/File.zig
@@ -1803,9 +1803,15 @@ pub const Writer = struct {
                 file_reader.size = file_reader.pos;
                 return error.EndOfStream;
             }
-            const consumed = io_w.consume(@intCast(sbytes));
-            file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;
-            return consumed;
+            const n = io_w.consume(@intCast(sbytes));
+            if (n <= file_reader.interface.bufferedLen()) {
+                file_reader.interface.toss(n);
+            } else {
+                const direct_n = n - file_reader.interface.bufferedLen();
+                file_reader.interface.tossBuffered();
+                file_reader.seekBy(@intCast(direct_n)) catch return error.ReadFailed;
+            }
+            return n;
         }
 
         if (native_os.isDarwin() and w.mode == .streaming) sf: {
@@ -1864,9 +1870,15 @@ pub const Writer = struct {
                 file_reader.size = file_reader.pos;
                 return error.EndOfStream;
             }
-            const consumed = io_w.consume(@bitCast(len));
-            file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;
-            return consumed;
+            const n = io_w.consume(@bitCast(len));
+            if (n <= file_reader.interface.bufferedLen()) {
+                file_reader.interface.toss(n);
+            } else {
+                const direct_n = n - file_reader.interface.bufferedLen();
+                file_reader.interface.tossBuffered();
+                file_reader.seekBy(@intCast(direct_n)) catch return error.ReadFailed;
+            }
+            return n;
         }
 
         if (native_os == .linux and w.mode == .streaming) sf: {
@@ -1998,7 +2010,7 @@ pub const Writer = struct {
         reader_buffered: []const u8,
     ) std.Io.Writer.FileError!usize {
         const n = try drain(io_w, &.{reader_buffered}, 1);
-        file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;
+        file_reader.interface.toss(n);
         return n;
     }
 
lib/std/fs/test.zig
@@ -2180,3 +2180,32 @@ test "seekTo flushes buffered data" {
     try file_reader.interface.readSliceAll(&buf);
     try std.testing.expectEqualStrings(contents, &buf);
 }
+
+test "File.Writer sendfile with buffered contents" {
+    var tmp_dir = testing.tmpDir(.{});
+    defer tmp_dir.cleanup();
+
+    try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" });
+    const in = try tmp_dir.dir.openFile("a", .{});
+    defer in.close();
+    const out = try tmp_dir.dir.createFile("b", .{});
+    defer out.close();
+
+    var in_buf: [2]u8 = undefined;
+    var in_r = in.reader(&in_buf);
+    _ = try in_r.getSize(); // Catch seeks past end by populating size
+    try in_r.interface.fill(2);
+
+    var out_buf: [1]u8 = undefined;
+    var out_w = out.writerStreaming(&out_buf);
+    try out_w.interface.writeByte('a');
+    try testing.expectEqual(3, try out_w.interface.sendFileAll(&in_r, .unlimited));
+    try out_w.interface.flush();
+
+    var check = try tmp_dir.dir.openFile("b", .{});
+    defer check.close();
+    var check_buf: [4]u8 = undefined;
+    var check_r = check.reader(&check_buf);
+    try testing.expectEqualStrings("abcd", try check_r.interface.take(4));
+    try testing.expectError(error.EndOfStream, check_r.interface.takeByte());
+}