authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-05-28 18:42:11-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:29-07:00
logba684a18ca264463a29eb5dcd0666c07a6188538
tree4f466a7042a01356f7c759fafc09a1f351c2ca8f
parent2f5574ac083a4a14ab603b7f848d135fbba3c1ed

std: upgrade more API to use File.Reader


7 files changed, 193 insertions(+), 222 deletions(-)

lib/std/fs/File.zig+72-36
...@@ -915,13 +915,24 @@ pub const Reader = struct {...@@ -915,13 +915,24 @@ pub const Reader = struct {
915 pos: u64 = 0,915 pos: u64 = 0,
916 size: ?u64 = null,916 size: ?u64 = null,
917 size_err: ?GetEndPosError = null,917 size_err: ?GetEndPosError = null,
918 seek_err: ?SeekError = null,918 seek_err: ?Reader.SeekError = null,
919
920 pub const SeekError = File.SeekError || error{
921 /// Seeking fell back to reading, and reached the end before the requested seek position.
922 /// `pos` remains at the end of the file.
923 EndOfStream,
924 /// Seeking fell back to reading, which failed.
925 ReadFailed,
926 };
919927
920 pub const Mode = enum {928 pub const Mode = enum {
921 streaming,929 streaming,
922 positional,930 positional,
931 /// Avoid syscalls other than `read` and `readv`.
923 streaming_reading,932 streaming_reading,
933 /// Avoid syscalls other than `pread` and `preadv`.
924 positional_reading,934 positional_reading,
935 /// Indicates reading cannot continue because of a seek failure.
925 failure,936 failure,
926937
927 pub fn toStreaming(m: @This()) @This() {938 pub fn toStreaming(m: @This()) @This() {
...@@ -931,6 +942,14 @@ pub const Reader = struct {...@@ -931,6 +942,14 @@ pub const Reader = struct {
931 .failure => .failure,942 .failure => .failure,
932 };943 };
933 }944 }
945
946 pub fn toReading(m: @This()) @This() {
947 return switch (m) {
948 .positional, .positional_reading => .positional_reading,
949 .streaming, .streaming_reading => .streaming_reading,
950 .failure => .failure,
951 };
952 }
934 };953 };
935954
936 pub fn interface(r: *Reader) std.io.Reader {955 pub fn interface(r: *Reader) std.io.Reader {
...@@ -960,7 +979,7 @@ pub const Reader = struct {...@@ -960,7 +979,7 @@ pub const Reader = struct {
960 };979 };
961 }980 }
962981
963 pub fn seekBy(r: *Reader, offset: i64) SeekError!void {982 pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void {
964 switch (r.mode) {983 switch (r.mode) {
965 .positional, .positional_reading => {984 .positional, .positional_reading => {
966 // TODO: make += operator allow any integer types985 // TODO: make += operator allow any integer types
...@@ -977,19 +996,21 @@ pub const Reader = struct {...@@ -977,19 +996,21 @@ pub const Reader = struct {
977 break :e err;996 break :e err;
978 }997 }
979 };998 };
980 if (offset < 0) return seek_err;999 var remaining = std.math.cast(u64, offset) orelse return seek_err;
981 var remaining = offset;
982 while (remaining > 0) {1000 while (remaining > 0) {
983 const n = discard(r, .limited(remaining)) catch |err| switch (err) {};1001 const n = discard(r, .limited(remaining)) catch |err| {
1002 r.seek_err = err;
1003 return err;
1004 };
984 r.pos += n;1005 r.pos += n;
985 remaining -= n;1006 remaining -= n;
986 }1007 }
987 },1008 },
988 .failure => return error.Unseekable,1009 .failure => return r.seek_err.?,
989 }1010 }
990 }1011 }
9911012
992 pub fn seekTo(r: *Reader, offset: u64) SeekError!void {1013 pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {
993 switch (r.mode) {1014 switch (r.mode) {
994 .positional, .positional_reading => {1015 .positional, .positional_reading => {
995 r.pos = offset;1016 r.pos = offset;
...@@ -1001,7 +1022,9 @@ pub const Reader = struct {...@@ -1001,7 +1022,9 @@ pub const Reader = struct {
1001 r.seek_err = err;1022 r.seek_err = err;
1002 return err;1023 return err;
1003 };1024 };
1025 r.pos = offset;
1004 },1026 },
1027 .failure => return r.seek_err.?,
1005 }1028 }
1006 }1029 }
10071030
...@@ -1015,33 +1038,29 @@ pub const Reader = struct {...@@ -1015,33 +1038,29 @@ pub const Reader = struct {
1015 limit: std.io.Limit,1038 limit: std.io.Limit,
1016 ) std.io.Reader.StreamError!usize {1039 ) std.io.Reader.StreamError!usize {
1017 const r: *Reader = @ptrCast(@alignCast(context));1040 const r: *Reader = @ptrCast(@alignCast(context));
1018 return bw.writeFile(r, limit, &.{}, 0) catch |write_file_error| switch (write_file_error) {1041 switch (r.mode) {
1019 error.ReadFailed => return error.ReadFailed,1042 .positional, .streaming => return bw.writeFile(r, limit, &.{}, 0) catch |write_err| switch (write_err) {
1020 error.WriteFailed => return error.WriteFailed,1043 error.ReadFailed => return error.ReadFailed,
1021 error.Unimplemented => switch (r.mode) {1044 error.WriteFailed => return error.WriteFailed,
1022 .positional => {1045 error.Unimplemented => {
1023 r.mode = .positional_reading;1046 r.mode = r.mode.toReading();
1024 return 0;
1025 },
1026 .streaming => {
1027 r.mode = .streaming_reading;
1028 return 0;1047 return 0;
1029 },1048 },
1030 .positional_reading => {
1031 const dest = limit.slice(try bw.writableSliceGreedy(1));
1032 const n = try readPositional(r, dest);
1033 bw.advance(n);
1034 return n;
1035 },
1036 .streaming_reading => {
1037 const dest = limit.slice(try bw.writableSliceGreedy(1));
1038 const n = try readStreaming(r, dest);
1039 bw.advance(n);
1040 return n;
1041 },
1042 .failure => return error.ReadFailed,
1043 },1049 },
1044 };1050 .positional_reading => {
1051 const dest = limit.slice(try bw.writableSliceGreedy(1));
1052 const n = try readPositional(r, dest);
1053 bw.advance(n);
1054 return n;
1055 },
1056 .streaming_reading => {
1057 const dest = limit.slice(try bw.writableSliceGreedy(1));
1058 const n = try readStreaming(r, dest);
1059 bw.advance(n);
1060 return n;
1061 },
1062 .failure => return error.ReadFailed,
1063 }
1045 }1064 }
10461065
1047 fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {1066 fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
...@@ -1077,7 +1096,10 @@ pub const Reader = struct {...@@ -1077,7 +1096,10 @@ pub const Reader = struct {
1077 r.err = err;1096 r.err = err;
1078 return error.ReadFailed;1097 return error.ReadFailed;
1079 };1098 };
1080 if (n == 0) return error.EndOfStream;1099 if (n == 0) {
1100 r.size = pos;
1101 return error.EndOfStream;
1102 }
1081 r.pos = pos + n;1103 r.pos = pos + n;
1082 return n;1104 return n;
1083 }1105 }
...@@ -1093,7 +1115,10 @@ pub const Reader = struct {...@@ -1093,7 +1115,10 @@ pub const Reader = struct {
1093 r.err = err;1115 r.err = err;
1094 return error.ReadFailed;1116 return error.ReadFailed;
1095 };1117 };
1096 if (n == 0) return error.EndOfStream;1118 if (n == 0) {
1119 r.size = pos;
1120 return error.EndOfStream;
1121 }
1097 r.pos = pos + n;1122 r.pos = pos + n;
1098 return n;1123 return n;
1099 }1124 }
...@@ -1113,6 +1138,7 @@ pub const Reader = struct {...@@ -1113,6 +1138,7 @@ pub const Reader = struct {
1113 r.pos = pos + n;1138 r.pos = pos + n;
1114 return n;1139 return n;
1115 },1140 },
1141 .failure => return error.ReadFailed,
1116 }1142 }
1117 }1143 }
11181144
...@@ -1120,7 +1146,7 @@ pub const Reader = struct {...@@ -1120,7 +1146,7 @@ pub const Reader = struct {
1120 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {1146 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
1121 error.Unseekable => {1147 error.Unseekable => {
1122 r.mode = r.mode.toStreaming();1148 r.mode = r.mode.toStreaming();
1123 if (r.pos != 0) r.seekBy(r.pos) catch {1149 if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch {
1124 r.mode = .failure;1150 r.mode = .failure;
1125 return error.ReadFailed;1151 return error.ReadFailed;
1126 };1152 };
...@@ -1131,7 +1157,10 @@ pub const Reader = struct {...@@ -1131,7 +1157,10 @@ pub const Reader = struct {
1131 return error.ReadFailed;1157 return error.ReadFailed;
1132 },1158 },
1133 };1159 };
1134 if (n == 0) return error.EndOfStream;1160 if (n == 0) {
1161 r.size = r.pos;
1162 return error.EndOfStream;
1163 }
1135 r.pos += n;1164 r.pos += n;
1136 return n;1165 return n;
1137 }1166 }
...@@ -1141,7 +1170,10 @@ pub const Reader = struct {...@@ -1141,7 +1170,10 @@ pub const Reader = struct {
1141 r.err = err;1170 r.err = err;
1142 return error.ReadFailed;1171 return error.ReadFailed;
1143 };1172 };
1144 if (n == 0) return error.EndOfStream;1173 if (n == 0) {
1174 r.size = r.pos;
1175 return error.EndOfStream;
1176 }
1145 r.pos += n;1177 r.pos += n;
1146 return n;1178 return n;
1147 }1179 }
...@@ -1167,6 +1199,10 @@ pub const Writer = struct {...@@ -1167,6 +1199,10 @@ pub const Writer = struct {
11671199
1168 pub const SendfileError = error{1200 pub const SendfileError = error{
1169 UnsupportedOperation,1201 UnsupportedOperation,
1202 SystemResources,
1203 InputOutput,
1204 BrokenPipe,
1205 WouldBlock,
1170 Unexpected,1206 Unexpected,
1171 };1207 };
11721208
lib/std/http.zig+46-38
...@@ -855,13 +855,7 @@ pub const BodyWriter = struct {...@@ -855,13 +855,7 @@ pub const BodyWriter = struct {
855 http_protocol_output: *std.io.BufferedWriter,855 http_protocol_output: *std.io.BufferedWriter,
856 state: State,856 state: State,
857 elide: bool,857 elide: bool,
858 err: Error!void = {},
859858
860 pub const Error = error{
861 /// Attempted to write a file to the stream, an expensive operation
862 /// that should be avoided when `elide` is true.
863 UnableToElideBody,
864 };
865 pub const WriteError = std.io.Writer.Error;859 pub const WriteError = std.io.Writer.Error;
866860
867 /// How many zeroes to reserve for hex-encoded chunk length.861 /// How many zeroes to reserve for hex-encoded chunk length.
...@@ -1043,69 +1037,83 @@ pub const BodyWriter = struct {...@@ -1043,69 +1037,83 @@ pub const BodyWriter = struct {
1043 }1037 }
10441038
1045 fn elideWriteFile(1039 fn elideWriteFile(
1046 w: *BodyWriter,1040 file_reader: *std.fs.File.Reader,
1047 offset: std.io.Writer.Offset,1041 limit: std.io.Limit,
1048 limit: std.io.Writer.Limit,
1049 headers_and_trailers: []const []const u8,1042 headers_and_trailers: []const []const u8,
1050 ) WriteError!usize {1043 headers_len: usize,
1051 if (offset != .none) {1044 ) error{ReadFailed}!usize {
1052 if (countWriteFile(limit, headers_and_trailers)) |n| {1045 var source = file_reader.readable(&.{});
1046 var n = source.discard(limit) catch |err| switch (err) {
1047 error.ReadFailed => return error.ReadFailed,
1048 error.EndOfStream => {
1049 var n: usize = 0;
1050 for (headers_and_trailers) |bytes| n += bytes.len;
1051 return n;
1052 },
1053 };
1054 if (file_reader.size) |size| {
1055 if (size - file_reader.pos == 0) {
1056 // End of file reached.
1057 for (headers_and_trailers) |bytes| n += bytes.len;
1053 return n;1058 return n;
1054 }1059 }
1055 }1060 }
1056 w.err = error.UnableToElideBody;1061 for (headers_and_trailers[0..headers_len]) |bytes| n += bytes.len;
1057 return error.WriteFailed;1062 return n;
1058 }1063 }
10591064
1060 /// Returns `null` if size cannot be computed without making any syscalls.1065 /// Returns `null` if size cannot be computed without making any syscalls.
1061 fn countWriteFile(limit: std.io.Writer.Limit, headers_and_trailers: []const []const u8) ?usize {1066 fn countWriteFile(
1062 var total: usize = limit.toInt() orelse return null;1067 file_reader: *std.fs.File.Reader,
1063 for (headers_and_trailers) |buf| total += buf.len;1068 limit: std.io.Limit,
1064 return total;1069 headers_and_trailers: []const []const u8,
1070 ) ?usize {
1071 var total: u64 = @min(@intFromEnum(limit), file_reader.getSize() orelse return null);
1072 for (headers_and_trailers) |bytes| total += bytes.len;
1073 return std.math.lossyCast(usize, total);
1065 }1074 }
10661075
1067 fn noneWriteFile(1076 fn noneWriteFile(
1068 context: ?*anyopaque,1077 context: ?*anyopaque,
1069 file: std.fs.File,1078 file_reader: *std.fs.File.Reader,
1070 offset: std.io.Writer.Offset,1079 limit: std.io.Limit,
1071 limit: std.io.Writer.Limit,
1072 headers_and_trailers: []const []const u8,1080 headers_and_trailers: []const []const u8,
1073 headers_len: usize,1081 headers_len: usize,
1074 ) std.io.Writer.FileError!usize {1082 ) std.io.Writer.FileError!usize {
1075 if (limit == .nothing) return noneWriteSplat(context, headers_and_trailers, 1);
1076 const w: *BodyWriter = @alignCast(@ptrCast(context));1083 const w: *BodyWriter = @alignCast(@ptrCast(context));
1077 if (w.elide) return elideWriteFile(w, offset, limit, headers_and_trailers);1084 if (w.elide) return elideWriteFile(file_reader, limit, headers_and_trailers, headers_len);
1078 return w.http_protocol_output.writeFile(file, offset, limit, headers_and_trailers, headers_len);1085 return w.http_protocol_output.writeFile(file_reader, limit, headers_and_trailers, headers_len);
1079 }1086 }
10801087
1081 fn contentLengthWriteFile(1088 fn contentLengthWriteFile(
1082 context: ?*anyopaque,1089 context: ?*anyopaque,
1083 file: std.fs.File,1090 file_reader: *std.fs.File.Reader,
1084 offset: std.io.Writer.Offset,1091 limit: std.io.Limit,
1085 limit: std.io.Writer.Limit,
1086 headers_and_trailers: []const []const u8,1092 headers_and_trailers: []const []const u8,
1087 headers_len: usize,1093 headers_len: usize,
1088 ) std.io.Writer.FileError!usize {1094 ) std.io.Writer.FileError!usize {
1089 if (limit == .nothing) return contentLengthWriteSplat(context, headers_and_trailers, 1);
1090 const w: *BodyWriter = @alignCast(@ptrCast(context));1095 const w: *BodyWriter = @alignCast(@ptrCast(context));
1091 if (w.elide) return elideWriteFile(w, offset, limit, headers_and_trailers);1096 if (w.elide) return elideWriteFile(file_reader, limit, headers_and_trailers, headers_len);
1092 const n = try w.http_protocol_output.writeFile(file, offset, limit, headers_and_trailers, headers_len);1097 const n = try w.http_protocol_output.writeFile(file_reader, limit, headers_and_trailers, headers_len);
1093 w.state.content_length -= n;1098 w.state.content_length -= n;
1094 return n;1099 return n;
1095 }1100 }
10961101
1097 fn chunkedWriteFile(1102 fn chunkedWriteFile(
1098 context: ?*anyopaque,1103 context: ?*anyopaque,
1099 file: std.fs.File,1104 file_reader: *std.fs.File.Reader,
1100 offset: std.io.Writer.Offset,1105 limit: std.io.Limit,
1101 limit: std.io.Writer.Limit,
1102 headers_and_trailers: []const []const u8,1106 headers_and_trailers: []const []const u8,
1103 headers_len: usize,1107 headers_len: usize,
1104 ) std.io.Writer.FileError!usize {1108 ) std.io.Writer.FileError!usize {
1105 if (limit == .nothing) return chunkedWriteSplat(context, headers_and_trailers, 1);
1106 const w: *BodyWriter = @alignCast(@ptrCast(context));1109 const w: *BodyWriter = @alignCast(@ptrCast(context));
1107 if (w.elide) return elideWriteFile(w, offset, limit, headers_and_trailers);1110 if (w.elide) return elideWriteFile(file_reader, limit, headers_and_trailers, headers_len);
1108 const data_len = countWriteFile(limit, headers_and_trailers) orelse @panic("TODO");1111 if (limit == .nothing) return chunkedWriteSplat(context, headers_and_trailers, 1);
1112 const data_len = countWriteFile(file_reader, headers_and_trailers) orelse {
1113 // If the file size is unknown, we cannot lower to a `writeFile` since we would
1114 // have to flush the chunk header before knowing the chunk length.
1115 return error.Unimplemented;
1116 };
1109 const bw = w.http_protocol_output;1117 const bw = w.http_protocol_output;
1110 const chunked = &w.state.chunked;1118 const chunked = &w.state.chunked;
1111 state: switch (chunked.*) {1119 state: switch (chunked.*) {
...@@ -1114,7 +1122,7 @@ pub const BodyWriter = struct {...@@ -1114,7 +1122,7 @@ pub const BodyWriter = struct {
1114 const buffered_len = bw.end - off - chunk_header_template.len;1122 const buffered_len = bw.end - off - chunk_header_template.len;
1115 const chunk_len = data_len + buffered_len;1123 const chunk_len = data_len + buffered_len;
1116 writeHex(bw.buffer[off..][0..chunk_len_digits], chunk_len);1124 writeHex(bw.buffer[off..][0..chunk_len_digits], chunk_len);
1117 const n = try bw.writeFile(file, offset, limit, headers_and_trailers, headers_len);1125 const n = try bw.writeFile(file_reader, limit, headers_and_trailers, headers_len);
1118 chunked.* = .{ .chunk_len = data_len + 2 - n };1126 chunked.* = .{ .chunk_len = data_len + 2 - n };
1119 return n;1127 return n;
1120 },1128 },
...@@ -1138,7 +1146,7 @@ pub const BodyWriter = struct {...@@ -1138,7 +1146,7 @@ pub const BodyWriter = struct {
1138 },1146 },
1139 else => {1147 else => {
1140 const new_limit = limit.min(.limited(chunk_len - 2));1148 const new_limit = limit.min(.limited(chunk_len - 2));
1141 const n = try bw.writeFile(file, offset, new_limit, headers_and_trailers, headers_len);1149 const n = try bw.writeFile(file_reader, new_limit, headers_and_trailers, headers_len);
1142 chunked.chunk_len = chunk_len - n;1150 chunked.chunk_len = chunk_len - n;
1143 return n;1151 return n;
1144 },1152 },
lib/std/io/AllocatingWriter.zig+6-3
...@@ -161,7 +161,7 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std....@@ -161,7 +161,7 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.
161fn writeFile(161fn writeFile(
162 context: ?*anyopaque,162 context: ?*anyopaque,
163 file_reader: *std.fs.File.Reader,163 file_reader: *std.fs.File.Reader,
164 limit: std.io.Writer.Limit,164 limit: std.io.Limit,
165 headers_and_trailers_full: []const []const u8,165 headers_and_trailers_full: []const []const u8,
166 headers_len_full: usize,166 headers_len_full: usize,
167) std.io.Writer.FileError!usize {167) std.io.Writer.FileError!usize {
...@@ -185,8 +185,11 @@ fn writeFile(...@@ -185,8 +185,11 @@ fn writeFile(
185 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;185 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
186 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);186 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
187 const dest = limit.slice(list.items.ptr[list.items.len..list.capacity]);187 const dest = limit.slice(list.items.ptr[list.items.len..list.capacity]);
188 const n = try file_reader.read(dest);188 const n = file_reader.read(dest) catch |err| switch (err) {
189 const is_end = if (file_reader.getSize()) |size| n >= size - pos else n == 0;189 error.ReadFailed => return error.ReadFailed,
190 error.EndOfStream => 0,
191 };
192 const is_end = if (file_reader.getSize()) |size| n >= size - pos else |_| n == 0;
190 if (is_end) {193 if (is_end) {
191 new_capacity = list.capacity;194 new_capacity = list.capacity;
192 for (trailers) |bytes| new_capacity += bytes.len;195 for (trailers) |bytes| new_capacity += bytes.len;
lib/std/io/BufferedWriter.zig+56-104
...@@ -6,6 +6,7 @@ const Writer = std.io.Writer;...@@ -6,6 +6,7 @@ const Writer = std.io.Writer;
6const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
7const testing = std.testing;7const testing = std.testing;
8const Limit = std.io.Limit;8const Limit = std.io.Limit;
9const File = std.fs.File;
910
10/// Underlying stream to send bytes to.11/// Underlying stream to send bytes to.
11///12///
...@@ -543,36 +544,31 @@ pub fn writeSliceSwap(bw: *BufferedWriter, Elem: type, slice: []const Elem) Writ...@@ -543,36 +544,31 @@ pub fn writeSliceSwap(bw: *BufferedWriter, Elem: type, slice: []const Elem) Writ
543/// `error.Unimplemented` in the error set.544/// `error.Unimplemented` in the error set.
544pub fn writeFile(545pub fn writeFile(
545 bw: *BufferedWriter,546 bw: *BufferedWriter,
546 file: std.fs.File,547 file_reader: *File.Reader,
547 offset: Writer.Offset,
548 limit: Limit,548 limit: Limit,
549 headers_and_trailers: []const []const u8,549 headers_and_trailers: []const []const u8,
550 headers_len: usize,550 headers_len: usize,
551) Writer.FileError!usize {551) Writer.FileError!usize {
552 return passthruWriteFile(bw, file, offset, limit, headers_and_trailers, headers_len);552 return passthruWriteFile(bw, file_reader, limit, headers_and_trailers, headers_len);
553}553}
554554
555pub const WriteFileReadingError = std.fs.File.PReadError || Writer.Error;
556
557/// Returning zero bytes means end of stream.555/// Returning zero bytes means end of stream.
558///556///
559/// Asserts nonzero buffer capacity.557/// Asserts nonzero buffer capacity.
560pub fn writeFileReading(558pub fn writeFileReading(
561 bw: *BufferedWriter,559 bw: *BufferedWriter,
562 file: std.fs.File,560 file_reader: *File.Reader,
563 offset: Writer.Offset,
564 limit: Limit,561 limit: Limit,
565) WriteFileReadingError!usize {562) Writer.ReadingFileError!usize {
566 const dest = limit.slice(try bw.writableSliceGreedy(1));563 const dest = limit.slice(try bw.writableSliceGreedy(1));
567 const n = if (offset.toInt()) |pos| try file.pread(dest, pos) else try file.read(dest);564 const n = try file_reader.read(dest);
568 bw.advance(n);565 bw.advance(n);
569 return n;566 return n;
570}567}
571568
572fn passthruWriteFile(569fn passthruWriteFile(
573 context: ?*anyopaque,570 context: ?*anyopaque,
574 file: std.fs.File,571 file_reader: *File.Reader,
575 offset: Writer.Offset,
576 limit: Limit,572 limit: Limit,
577 headers_and_trailers: []const []const u8,573 headers_and_trailers: []const []const u8,
578 headers_len: usize,574 headers_len: usize,
...@@ -581,7 +577,7 @@ fn passthruWriteFile(...@@ -581,7 +577,7 @@ fn passthruWriteFile(
581 const buffer = bw.buffer;577 const buffer = bw.buffer;
582 if (buffer.len == 0) return track(578 if (buffer.len == 0) return track(
583 &bw.count,579 &bw.count,
584 try bw.unbuffered_writer.writeFile(file, offset, limit, headers_and_trailers, headers_len),580 try bw.unbuffered_writer.writeFile(file_reader, limit, headers_and_trailers, headers_len),
585 );581 );
586 const start_end = bw.end;582 const start_end = bw.end;
587 const headers = headers_and_trailers[0..headers_len];583 const headers = headers_and_trailers[0..headers_len];
...@@ -608,7 +604,7 @@ fn passthruWriteFile(...@@ -608,7 +604,7 @@ fn passthruWriteFile(
608 @memcpy(remaining_buffers_for_trailers[0..send_trailers_len], trailers[0..send_trailers_len]);604 @memcpy(remaining_buffers_for_trailers[0..send_trailers_len], trailers[0..send_trailers_len]);
609 const send_headers_len = 1 + buffers_len;605 const send_headers_len = 1 + buffers_len;
610 const send_buffers = buffers[0 .. send_headers_len + send_trailers_len];606 const send_buffers = buffers[0 .. send_headers_len + send_trailers_len];
611 const n = try bw.unbuffered_writer.writeFile(file, offset, limit, send_buffers, send_headers_len);607 const n = try bw.unbuffered_writer.writeFile(file_reader, limit, send_buffers, send_headers_len);
612 if (n < end) {608 if (n < end) {
613 @branchHint(.unlikely);609 @branchHint(.unlikely);
614 const remainder = buffer[n..end];610 const remainder = buffer[n..end];
...@@ -638,7 +634,7 @@ fn passthruWriteFile(...@@ -638,7 +634,7 @@ fn passthruWriteFile(
638 @memcpy(remaining_buffers[0..send_trailers_len], trailers[0..send_trailers_len]);634 @memcpy(remaining_buffers[0..send_trailers_len], trailers[0..send_trailers_len]);
639 const send_headers_len = @intFromBool(end != 0);635 const send_headers_len = @intFromBool(end != 0);
640 const send_buffers = buffers[1 - send_headers_len .. 1 + send_trailers_len];636 const send_buffers = buffers[1 - send_headers_len .. 1 + send_trailers_len];
641 const n = try bw.unbuffered_writer.writeFile(file, offset, limit, send_buffers, send_headers_len);637 const n = try bw.unbuffered_writer.writeFile(file_reader, limit, send_buffers, send_headers_len);
642 if (n < end) {638 if (n < end) {
643 @branchHint(.unlikely);639 @branchHint(.unlikely);
644 const remainder = buffer[n..end];640 const remainder = buffer[n..end];
...@@ -651,9 +647,6 @@ fn passthruWriteFile(...@@ -651,9 +647,6 @@ fn passthruWriteFile(
651}647}
652648
653pub const WriteFileOptions = struct {649pub const WriteFileOptions = struct {
654 offset: Writer.Offset = .none,
655 /// If the size of the source file is known, it is likely that passing the
656 /// size here will save one syscall.
657 limit: Limit = .unlimited,650 limit: Limit = .unlimited,
658 /// Headers and trailers must be passed together so that in case `len` is651 /// Headers and trailers must be passed together so that in case `len` is
659 /// zero, they can be forwarded directly to `Writer.VTable.writeSplat`.652 /// zero, they can be forwarded directly to `Writer.VTable.writeSplat`.
...@@ -666,77 +659,51 @@ pub const WriteFileOptions = struct {...@@ -666,77 +659,51 @@ pub const WriteFileOptions = struct {
666 headers_len: usize = 0,659 headers_len: usize = 0,
667};660};
668661
669pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) WriteFileReadingError!void {662pub fn writeFileAll(
663 bw: *BufferedWriter,
664 file_reader: *std.fs.File.Reader,
665 options: WriteFileOptions,
666) Writer.FileError!void {
670 const headers_and_trailers = options.headers_and_trailers;667 const headers_and_trailers = options.headers_and_trailers;
671 const headers = headers_and_trailers[0..options.headers_len];668 const headers = headers_and_trailers[0..options.headers_len];
672 switch (options.limit) {669 var remaining = options.limit;
673 .nothing => return bw.writeVecAll(headers_and_trailers),670 var i: usize = 0;
674 .unlimited => {671 while (true) {
675 // When reading the whole file, we cannot include the trailers in the672 const before_pos = file_reader.pos;
676 // call that reads from the file handle, because we have no way to673 var n = bw.writeFile(file_reader, remaining, headers_and_trailers[i..], headers.len - i) catch |err| switch (err) {
677 // determine whether a partial write is past the end of the file or674 error.ReadFailed => return error.ReadFailed,
678 // not.675 error.WriteFailed => return error.WriteFailed,
679 var i: usize = 0;676 error.Unimplemented => {
680 var offset = options.offset;677 file_reader.mode = file_reader.mode.toReading();
681 while (true) {678 try bw.writeVecAll(headers[i..]);
682 var n = bw.writeFile(file, offset, .unlimited, headers[i..], headers.len - i) catch |err| switch (err) {679 try bw.writeFileReadingAll(file_reader, remaining);
683 error.Unimplemented => {680 try bw.writeVecAll(headers_and_trailers[headers.len..]);
684 try bw.writeVecAll(headers[i..]);681 return;
685 try bw.writeFileReadingAll(file, offset, .unlimited);682 },
686 try bw.writeVecAll(headers_and_trailers[headers.len..]);683 };
687 return;684 while (i < headers.len and n >= headers[i].len) {
688 },685 n -= headers[i].len;
689 else => |e| return e,686 i += 1;
690 };687 }
691 while (i < headers.len and n >= headers[i].len) {688 if (i < headers.len) {
692 n -= headers[i].len;689 headers[i] = headers[i][n..];
693 i += 1;690 continue;
694 }691 }
695 if (i < headers.len) {692 const file_bytes_consumed = file_reader.pos - before_pos;
696 headers[i] = headers[i][n..];693 remaining = remaining.subtract(file_bytes_consumed).?;
697 continue;694 const size = file_reader.size orelse continue; // End of file not yet reached.
698 }695 if (file_reader.pos < size) continue; // End of file not yet reached.
699 if (n == 0) break;696 n -= file_bytes_consumed; // Trailers reached.
700 offset = offset.advance(n);697 while (i < headers_and_trailers.len and n >= headers_and_trailers[i].len) {
701 }698 n -= headers_and_trailers[i].len;
702 },699 i += 1;
703 else => {700 }
704 var len = options.limit.toInt().?;701 if (i < headers_and_trailers.len) {
705 var i: usize = 0;702 headers_and_trailers[i] = headers_and_trailers[i][n..];
706 var offset = options.offset;703 try bw.writeVecAll(headers_and_trailers[i..]);
707 while (true) {704 return;
708 var n = bw.writeFile(file, offset, .limited(len), headers_and_trailers[i..], headers.len - i) catch |err| switch (err) {705 }
709 error.Unimplemented => {706 return;
710 try bw.writeVecAll(headers[i..]);
711 try bw.writeFileReadingAll(file, offset, .limited(len));
712 try bw.writeVecAll(headers_and_trailers[headers.len..]);
713 return;
714 },
715 else => |e| return e,
716 };
717 while (i < headers.len and n >= headers[i].len) {
718 n -= headers[i].len;
719 i += 1;
720 }
721 if (i < headers.len) {
722 headers[i] = headers[i][n..];
723 continue;
724 }
725 if (n >= len) {
726 n -= len;
727 if (i >= headers_and_trailers.len) return;
728 while (n >= headers_and_trailers[i].len) {
729 n -= headers_and_trailers[i].len;
730 i += 1;
731 if (i >= headers_and_trailers.len) return;
732 }
733 headers_and_trailers[i] = headers_and_trailers[i][n..];
734 return bw.writeVecAll(headers_and_trailers[i..]);
735 }
736 offset = offset.advance(n);
737 len -= n;
738 }
739 },
740 }707 }
741}708}
742709
...@@ -748,28 +715,13 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp...@@ -748,28 +715,13 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp
748/// Asserts nonzero buffer capacity.715/// Asserts nonzero buffer capacity.
749pub fn writeFileReadingAll(716pub fn writeFileReadingAll(
750 bw: *BufferedWriter,717 bw: *BufferedWriter,
751 file: std.fs.File,718 file_reader: *File.Reader,
752 offset: Writer.Offset,
753 limit: Limit,719 limit: Limit,
754) WriteFileReadingError!void {720) Writer.ReadingFileError!void {
755 if (offset.toInt()) |start_pos| {
756 var remaining = limit;
757 var pos = start_pos;
758 while (remaining.nonzero()) {
759 const dest = remaining.slice(try bw.writableSliceGreedy(1));
760 const n = try file.pread(dest, pos);
761 if (n == 0) return;
762 bw.advance(n);
763 pos += n;
764 remaining = remaining.subtract(n).?;
765 }
766 }
767 var remaining = limit;721 var remaining = limit;
768 while (remaining.nonzero()) {722 while (remaining.nonzero()) {
769 const dest = remaining.slice(try bw.writableSliceGreedy(1));723 const n = try writeFileReading(bw, file_reader, remaining);
770 const n = try file.read(dest);
771 if (n == 0) return;724 if (n == 0) return;
772 bw.advance(n);
773 remaining = remaining.subtract(n).?;725 remaining = remaining.subtract(n).?;
774 }726 }
775}727}
lib/std/io/Reader.zig+1-1
...@@ -46,7 +46,7 @@ pub const VTable = struct {...@@ -46,7 +46,7 @@ pub const VTable = struct {
46 /// provided which is based on calling `read`, borrowing46 /// provided which is based on calling `read`, borrowing
47 /// `BufferedReader.buffer` to construct a temporary `BufferedWriter` and47 /// `BufferedReader.buffer` to construct a temporary `BufferedWriter` and
48 /// ignoring the written data.48 /// ignoring the written data.
49 discard: *const fn (context: ?*anyopaque, limit: Limit) Error!usize = null,49 discard: ?*const fn (context: ?*anyopaque, limit: Limit) Error!usize = null,
50};50};
5151
52pub const StreamError = error{52pub const StreamError = error{
lib/std/io/Writer.zig+12-6
...@@ -21,8 +21,8 @@ pub const VTable = struct {...@@ -21,8 +21,8 @@ pub const VTable = struct {
21 /// of stream via `error.WriteFailed`.21 /// of stream via `error.WriteFailed`.
22 writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize,22 writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize,
2323
24 /// Writes contents from an open file. `headers` are written first, then `len`24 /// Writes contents from an open file. `headers` are written first, then
25 /// bytes of `file` starting from `offset`, then `trailers`.25 /// `limit` bytes of `file` starting from `offset`, then `trailers`.
26 ///26 ///
27 /// Number of bytes actually written is returned, which may lie within27 /// Number of bytes actually written is returned, which may lie within
28 /// headers, the file, trailers, or anywhere in between.28 /// headers, the file, trailers, or anywhere in between.
...@@ -31,9 +31,8 @@ pub const VTable = struct {...@@ -31,9 +31,8 @@ pub const VTable = struct {
31 /// end-of-stream. A subsequent call may return nonzero, or may signal end31 /// end-of-stream. A subsequent call may return nonzero, or may signal end
32 /// of stream via `error.WriteFailed`.32 /// of stream via `error.WriteFailed`.
33 ///33 ///
34 /// If `error.Unimplemented` is returned, the caller should do its own34 /// `error.Unimplemented` indicates the callee cannot offer a more
35 /// reads from the file. The callee indicates it cannot offer a more35 /// efficient implementation than the caller performing its own reads.
36 /// efficient implementation.
37 writeFile: *const fn (36 writeFile: *const fn (
38 ctx: ?*anyopaque,37 ctx: ?*anyopaque,
39 file_reader: *File.Reader,38 file_reader: *File.Reader,
...@@ -43,7 +42,7 @@ pub const VTable = struct {...@@ -43,7 +42,7 @@ pub const VTable = struct {
43 /// `headers_and_trailers` do not count towards this limit.42 /// `headers_and_trailers` do not count towards this limit.
44 limit: Limit,43 limit: Limit,
45 /// Headers and trailers must be passed together so that in case `len` is44 /// Headers and trailers must be passed together so that in case `len` is
46 /// zero, they can be forwarded directly to `VTable.writeVec`.45 /// zero, they can be forwarded directly as one contiguous slice of memory.
47 headers_and_trailers: []const []const u8,46 headers_and_trailers: []const []const u8,
48 headers_len: usize,47 headers_len: usize,
49 ) FileError!usize,48 ) FileError!usize,
...@@ -54,6 +53,13 @@ pub const Error = error{...@@ -54,6 +53,13 @@ pub const Error = error{
54 WriteFailed,53 WriteFailed,
55};54};
5655
56pub const ReadingFileError = error{
57 /// Detailed diagnostics are found on the `File.Reader` struct.
58 ReadFailed,
59 /// See the `Writer` implementation for detailed diagnostics.
60 WriteFailed,
61};
62
57pub const FileError = error{63pub const FileError = error{
58 /// Detailed diagnostics are found on the `File.Reader` struct.64 /// Detailed diagnostics are found on the `File.Reader` struct.
59 ReadFailed,65 ReadFailed,
src/deprecated.zig-34
...@@ -247,40 +247,6 @@ pub fn LinearFifo(comptime T: type) type {...@@ -247,40 +247,6 @@ pub fn LinearFifo(comptime T: type) type {
247 return bytes.len;247 return bytes.len;
248 }248 }
249249
250 pub fn writer(fifo: *Self) std.io.Writer {
251 return .{
252 .context = fifo,
253 .vtable = &.{
254 .writeSplat = writerWriteSplat,
255 .writeFile = writerWriteFile,
256 },
257 };
258 }
259 fn writerWriteSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
260 const fifo: *Self = @alignCast(@ptrCast(ctx));
261 _ = fifo;
262 _ = data;
263 _ = splat;
264 @panic("TODO");
265 }
266 fn writerWriteFile(
267 ctx: ?*anyopaque,
268 file: std.fs.File,
269 offset: std.io.Writer.Offset,
270 limit: std.io.Writer.Limit,
271 headers_and_trailers: []const []const u8,
272 headers_len: usize,
273 ) std.io.Writer.Error!usize {
274 const fifo: *Self = @alignCast(@ptrCast(ctx));
275 _ = fifo;
276 _ = file;
277 _ = offset;
278 _ = limit;
279 _ = headers_and_trailers;
280 _ = headers_len;
281 @panic("TODO");
282 }
283
284 /// Make `count` items available before the current read location250 /// Make `count` items available before the current read location
285 fn rewind(self: *Self, count: usize) void {251 fn rewind(self: *Self, count: usize) void {
286 assert(self.writableLength() >= count);252 assert(self.writableLength() >= count);