diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index c3ee06958b..38d90cea17 100644 --- a/lib/std/fs/File.zig +++ b/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; } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 65e86e4c2e..03d62942a6 100644 --- a/lib/std/fs/test.zig +++ b/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()); +}