authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-05 11:26:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-05 11:26:38-07:00
log1d764c1fdf04829cec5974d82cec901825a80e49
treebe7ef463f79228b9396b5a7c32ccac85e067536e
parentac42eaaadd0650ffc281f9a1ed1a642fde8984b7

Revert "Merge pull request #24905 from gooncreeper/file-reader-buffered"

This reverts commit ac42eaaadd0650ffc281f9a1ed1a642fde8984b7, reversing changes made to 9fa2394f8c00d060931d69fb6f342f7f2e3d826e. I would like a chance to review this, please. I already spotted some issues.

3 files changed, 30 insertions(+), 102 deletions(-)

lib/std/Io/Writer.zig+13-51
......@@ -921,8 +921,7 @@ pub fn sendFileHeader(
921921/// Asserts nonzero buffer capacity.
922922pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) FileReadingError!usize {
923923 const dest = limit.slice(try w.writableSliceGreedy(1));
924 const n = try file_reader.interface.readSliceShort(dest);
925 if (n == 0) return error.EndOfStream;
924 const n = try file_reader.read(dest);
926925 w.advance(n);
927926 return n;
928927}
......@@ -935,24 +934,17 @@ pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) File
935934///
936935/// Asserts nonzero buffer capacity.
937936pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize {
938 // The fallback case uses `stream`. For `File.Reader`, this requires a minumum buffer size of
939 // one since it uses `writableSliceGreedy(1)`. Asserting this here ensures that this will be
940 // hit even when the fallback is not needed.
937 // The fallback sendFileReadingAll() path asserts non-zero buffer capacity.
938 // Explicitly assert it here as well to ensure the assert is hit even if
939 // the fallback path is not taken.
941940 assert(w.buffer.len > 0);
942
943941 var remaining = @intFromEnum(limit);
944942 while (remaining > 0) {
945943 const n = sendFile(w, file_reader, .limited(remaining)) catch |err| switch (err) {
946944 error.EndOfStream => break,
947945 error.Unimplemented => {
948946 file_reader.mode = file_reader.mode.toReading();
949 while (remaining > 0) {
950 remaining -= file_reader.interface.stream(w, .limited(remaining)) catch |e| switch (e) {
951 error.EndOfStream => break,
952 error.ReadFailed => return error.ReadFailed,
953 error.WriteFailed => return error.WriteFailed,
954 };
955 }
947 remaining -= try w.sendFileReadingAll(file_reader, .limited(remaining));
956948 break;
957949 },
958950 else => |e| return e,
......@@ -2284,12 +2276,6 @@ pub const Discarding = struct {
22842276 const d: *Discarding = @alignCast(@fieldParentPtr("writer", w));
22852277 d.count += w.end;
22862278 w.end = 0;
2287 const buffered_n = limit.minInt64(file_reader.interface.bufferedLen());
2288 if (buffered_n != 0) {
2289 file_reader.interface.toss(buffered_n);
2290 d.count += buffered_n;
2291 return buffered_n;
2292 }
22932279 if (limit == .nothing) return 0;
22942280 if (file_reader.getSize()) |size| {
22952281 const n = limit.minInt64(size - file_reader.pos);
......@@ -2781,9 +2767,7 @@ pub const Allocating = struct {
27812767 if (additional == 0) return error.EndOfStream;
27822768 a.ensureUnusedCapacity(limit.minInt64(additional)) catch return error.WriteFailed;
27832769 const dest = limit.slice(a.writer.buffer[a.writer.end..]);
2784 const n = try file_reader.interface.readSliceShort(dest);
2785 // If it was a short read, then EOF has been reached and `file_reader.size`
2786 // has been set and the EOF case will be hit on subsequent calls.
2770 const n = try file_reader.read(dest);
27872771 a.writer.end += n;
27882772 return n;
27892773 }
......@@ -2834,18 +2818,18 @@ test "discarding sendFile" {
28342818
28352819 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
28362820 defer file.close();
2837 var r_buffer: [2]u8 = undefined;
2821 var r_buffer: [256]u8 = undefined;
28382822 var file_writer: std.fs.File.Writer = .init(file, &r_buffer);
2839 try file_writer.interface.writeAll("abcd");
2823 try file_writer.interface.writeByte('h');
28402824 try file_writer.interface.flush();
28412825
28422826 var file_reader = file_writer.moveToReader();
28432827 try file_reader.seekTo(0);
2844 try file_reader.interface.fill(2);
28452828
28462829 var w_buffer: [256]u8 = undefined;
28472830 var discarding: Writer.Discarding = .init(&w_buffer);
2848 try testing.expectEqual(4, discarding.writer.sendFileAll(&file_reader, .unlimited));
2831
2832 _ = try file_reader.interface.streamRemaining(&discarding.writer);
28492833}
28502834
28512835test "allocating sendFile" {
......@@ -2854,40 +2838,18 @@ test "allocating sendFile" {
28542838
28552839 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
28562840 defer file.close();
2857 var r_buffer: [2]u8 = undefined;
2841 var r_buffer: [256]u8 = undefined;
28582842 var file_writer: std.fs.File.Writer = .init(file, &r_buffer);
2859 try file_writer.interface.writeAll("abcd");
2843 try file_writer.interface.writeByte('h');
28602844 try file_writer.interface.flush();
28612845
28622846 var file_reader = file_writer.moveToReader();
28632847 try file_reader.seekTo(0);
2864 try file_reader.interface.fill(2);
28652848
28662849 var allocating: Writer.Allocating = .init(testing.allocator);
28672850 defer allocating.deinit();
2868 try allocating.ensureUnusedCapacity(1);
2869 try testing.expectEqual(4, allocating.writer.sendFileAll(&file_reader, .unlimited));
2870 try testing.expectEqualStrings("abcd", allocating.writer.buffered());
2871}
28722851
2873test sendFileReading {
2874 var tmp_dir = testing.tmpDir(.{});
2875 defer tmp_dir.cleanup();
2876
2877 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
2878 defer file.close();
2879 var r_buffer: [2]u8 = undefined;
2880 var file_writer: std.fs.File.Writer = .init(file, &r_buffer);
2881 try file_writer.interface.writeAll("abcd");
2882 try file_writer.interface.flush();
2883
2884 var file_reader = file_writer.moveToReader();
2885 try file_reader.seekTo(0);
2886 try file_reader.interface.fill(2);
2887
2888 var w_buffer: [1]u8 = undefined;
2889 var discarding: Writer.Discarding = .init(&w_buffer);
2890 try testing.expectEqual(4, discarding.writer.sendFileReadingAll(&file_reader, .unlimited));
2852 _ = try file_reader.interface.streamRemaining(&allocating.writer);
28912853}
28922854
28932855test writeStruct {
lib/std/fs/File.zig+17-22
......@@ -1154,7 +1154,6 @@ pub const Reader = struct {
11541154 };
11551155 }
11561156
1157 /// If `error.EndOfStream` has been hit, this cannot fail.
11581157 pub fn getSize(r: *Reader) SizeError!u64 {
11591158 return r.size orelse {
11601159 if (r.size_err) |err| return err;
......@@ -1441,7 +1440,7 @@ pub const Reader = struct {
14411440 }
14421441 }
14431442
1444 fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1443 pub fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
14451444 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
14461445 error.Unseekable => {
14471446 r.mode = r.mode.toStreaming();
......@@ -1468,7 +1467,7 @@ pub const Reader = struct {
14681467 return n;
14691468 }
14701469
1471 fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1470 pub fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
14721471 const n = r.file.read(dest) catch |err| {
14731472 r.err = err;
14741473 return error.ReadFailed;
......@@ -1481,6 +1480,14 @@ pub const Reader = struct {
14811480 return n;
14821481 }
14831482
1483 pub fn read(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1484 switch (r.mode) {
1485 .positional, .positional_reading => return readPositional(r, dest),
1486 .streaming, .streaming_reading => return readStreaming(r, dest),
1487 .failure => return error.ReadFailed,
1488 }
1489 }
1490
14841491 pub fn atEnd(r: *Reader) bool {
14851492 // Even if stat fails, size is set when end is encountered.
14861493 const size = r.size orelse return false;
......@@ -1796,15 +1803,9 @@ pub const Writer = struct {
17961803 file_reader.size = file_reader.pos;
17971804 return error.EndOfStream;
17981805 }
1799 const n = io_w.consume(@intCast(sbytes));
1800 if (n <= file_reader.interface.bufferedLen()) {
1801 file_reader.interface.toss(n);
1802 } else {
1803 const direct_n = n - file_reader.interface.bufferedLen();
1804 file_reader.interface.tossBuffered();
1805 file_reader.seekBy(@intCast(direct_n)) catch return error.ReadFailed;
1806 }
1807 return n;
1806 const consumed = io_w.consume(@intCast(sbytes));
1807 file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;
1808 return consumed;
18081809 }
18091810
18101811 if (native_os.isDarwin() and w.mode == .streaming) sf: {
......@@ -1863,15 +1864,9 @@ pub const Writer = struct {
18631864 file_reader.size = file_reader.pos;
18641865 return error.EndOfStream;
18651866 }
1866 const n = io_w.consume(@bitCast(len));
1867 if (n <= file_reader.interface.bufferedLen()) {
1868 file_reader.interface.toss(n);
1869 } else {
1870 const direct_n = n - file_reader.interface.bufferedLen();
1871 file_reader.interface.tossBuffered();
1872 file_reader.seekBy(@intCast(direct_n)) catch return error.ReadFailed;
1873 }
1874 return n;
1867 const consumed = io_w.consume(@bitCast(len));
1868 file_reader.seekTo(file_reader.pos + consumed) catch return error.ReadFailed;
1869 return consumed;
18751870 }
18761871
18771872 if (native_os == .linux and w.mode == .streaming) sf: {
......@@ -2003,7 +1998,7 @@ pub const Writer = struct {
20031998 reader_buffered: []const u8,
20041999 ) std.Io.Writer.FileError!usize {
20052000 const n = try drain(io_w, &.{reader_buffered}, 1);
2006 file_reader.interface.toss(n);
2001 file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;
20072002 return n;
20082003 }
20092004
lib/std/fs/test.zig-29
......@@ -2180,32 +2180,3 @@ test "seekTo flushes buffered data" {
21802180 try file_reader.interface.readSliceAll(&buf);
21812181 try std.testing.expectEqualStrings(contents, &buf);
21822182}
2183
2184test "File.Writer sendfile with buffered contents" {
2185 var tmp_dir = testing.tmpDir(.{});
2186 defer tmp_dir.cleanup();
2187
2188 try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" });
2189 const in = try tmp_dir.dir.openFile("a", .{});
2190 defer in.close();
2191 const out = try tmp_dir.dir.createFile("b", .{});
2192 defer out.close();
2193
2194 var in_buf: [2]u8 = undefined;
2195 var in_r = in.reader(&in_buf);
2196 _ = try in_r.getSize(); // Catch seeks past end by populating size
2197 try in_r.interface.fill(2);
2198
2199 var out_buf: [1]u8 = undefined;
2200 var out_w = out.writerStreaming(&out_buf);
2201 try out_w.interface.writeByte('a');
2202 try testing.expectEqual(3, try out_w.interface.sendFileAll(&in_r, .unlimited));
2203 try out_w.interface.flush();
2204
2205 var check = try tmp_dir.dir.openFile("b", .{});
2206 defer check.close();
2207 var check_buf: [4]u8 = undefined;
2208 var check_r = check.reader(&check_buf);
2209 try testing.expectEqualStrings("abcd", try check_r.interface.take(4));
2210 try testing.expectError(error.EndOfStream, check_r.interface.takeByte());
2211}