| author | |
| committer | |
| log | ba684a18ca264463a29eb5dcd0666c07a6188538 |
| tree | 4f466a7042a01356f7c759fafc09a1f351c2ca8f |
| parent | 2f5574ac083a4a14ab603b7f848d135fbba3c1ed |
7 files changed, 193 insertions(+), 222 deletions(-)
lib/std/fs/File.zig+72-36| ... | ... | @@ -915,13 +915,24 @@ pub const Reader = struct { |
| 915 | 915 | pos: u64 = 0, |
| 916 | 916 | size: ?u64 = null, |
| 917 | 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 | }; | |
| 919 | 927 | |
| 920 | 928 | pub const Mode = enum { |
| 921 | 929 | streaming, |
| 922 | 930 | positional, |
| 931 | /// Avoid syscalls other than `read` and `readv`. | |
| 923 | 932 | streaming_reading, |
| 933 | /// Avoid syscalls other than `pread` and `preadv`. | |
| 924 | 934 | positional_reading, |
| 935 | /// Indicates reading cannot continue because of a seek failure. | |
| 925 | 936 | failure, |
| 926 | 937 | |
| 927 | 938 | pub fn toStreaming(m: @This()) @This() { |
| ... | ... | @@ -931,6 +942,14 @@ pub const Reader = struct { |
| 931 | 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 | }; |
| 935 | 954 | |
| 936 | 955 | pub fn interface(r: *Reader) std.io.Reader { |
| ... | ... | @@ -960,7 +979,7 @@ pub const Reader = struct { |
| 960 | 979 | }; |
| 961 | 980 | } |
| 962 | 981 | |
| 963 | pub fn seekBy(r: *Reader, offset: i64) SeekError!void { | |
| 982 | pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void { | |
| 964 | 983 | switch (r.mode) { |
| 965 | 984 | .positional, .positional_reading => { |
| 966 | 985 | // TODO: make += operator allow any integer types |
| ... | ... | @@ -977,19 +996,21 @@ pub const Reader = struct { |
| 977 | 996 | break :e err; |
| 978 | 997 | } |
| 979 | 998 | }; |
| 980 | if (offset < 0) return seek_err; | |
| 981 | var remaining = offset; | |
| 999 | var remaining = std.math.cast(u64, offset) orelse return seek_err; | |
| 982 | 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 | 1005 | r.pos += n; |
| 985 | 1006 | remaining -= n; |
| 986 | 1007 | } |
| 987 | 1008 | }, |
| 988 | .failure => return error.Unseekable, | |
| 1009 | .failure => return r.seek_err.?, | |
| 989 | 1010 | } |
| 990 | 1011 | } |
| 991 | 1012 | |
| 992 | pub fn seekTo(r: *Reader, offset: u64) SeekError!void { | |
| 1013 | pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void { | |
| 993 | 1014 | switch (r.mode) { |
| 994 | 1015 | .positional, .positional_reading => { |
| 995 | 1016 | r.pos = offset; |
| ... | ... | @@ -1001,7 +1022,9 @@ pub const Reader = struct { |
| 1001 | 1022 | r.seek_err = err; |
| 1002 | 1023 | return err; |
| 1003 | 1024 | }; |
| 1025 | r.pos = offset; | |
| 1004 | 1026 | }, |
| 1027 | .failure => return r.seek_err.?, | |
| 1005 | 1028 | } |
| 1006 | 1029 | } |
| 1007 | 1030 | |
| ... | ... | @@ -1015,33 +1038,29 @@ pub const Reader = struct { |
| 1015 | 1038 | limit: std.io.Limit, |
| 1016 | 1039 | ) std.io.Reader.StreamError!usize { |
| 1017 | 1040 | const r: *Reader = @ptrCast(@alignCast(context)); |
| 1018 | return bw.writeFile(r, limit, &.{}, 0) catch |write_file_error| switch (write_file_error) { | |
| 1019 | error.ReadFailed => return error.ReadFailed, | |
| 1020 | error.WriteFailed => return error.WriteFailed, | |
| 1021 | error.Unimplemented => switch (r.mode) { | |
| 1022 | .positional => { | |
| 1023 | r.mode = .positional_reading; | |
| 1024 | return 0; | |
| 1025 | }, | |
| 1026 | .streaming => { | |
| 1027 | r.mode = .streaming_reading; | |
| 1041 | switch (r.mode) { | |
| 1042 | .positional, .streaming => return bw.writeFile(r, limit, &.{}, 0) catch |write_err| switch (write_err) { | |
| 1043 | error.ReadFailed => return error.ReadFailed, | |
| 1044 | error.WriteFailed => return error.WriteFailed, | |
| 1045 | error.Unimplemented => { | |
| 1046 | r.mode = r.mode.toReading(); | |
| 1028 | 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 | } |
| 1046 | 1065 | |
| 1047 | 1066 | fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize { |
| ... | ... | @@ -1077,7 +1096,10 @@ pub const Reader = struct { |
| 1077 | 1096 | r.err = err; |
| 1078 | 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 | 1103 | r.pos = pos + n; |
| 1082 | 1104 | return n; |
| 1083 | 1105 | } |
| ... | ... | @@ -1093,7 +1115,10 @@ pub const Reader = struct { |
| 1093 | 1115 | r.err = err; |
| 1094 | 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 | 1122 | r.pos = pos + n; |
| 1098 | 1123 | return n; |
| 1099 | 1124 | } |
| ... | ... | @@ -1113,6 +1138,7 @@ pub const Reader = struct { |
| 1113 | 1138 | r.pos = pos + n; |
| 1114 | 1139 | return n; |
| 1115 | 1140 | }, |
| 1141 | .failure => return error.ReadFailed, | |
| 1116 | 1142 | } |
| 1117 | 1143 | } |
| 1118 | 1144 | |
| ... | ... | @@ -1120,7 +1146,7 @@ pub const Reader = struct { |
| 1120 | 1146 | const n = r.file.pread(dest, r.pos) catch |err| switch (err) { |
| 1121 | 1147 | error.Unseekable => { |
| 1122 | 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 | 1150 | r.mode = .failure; |
| 1125 | 1151 | return error.ReadFailed; |
| 1126 | 1152 | }; |
| ... | ... | @@ -1131,7 +1157,10 @@ pub const Reader = struct { |
| 1131 | 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 | 1164 | r.pos += n; |
| 1136 | 1165 | return n; |
| 1137 | 1166 | } |
| ... | ... | @@ -1141,7 +1170,10 @@ pub const Reader = struct { |
| 1141 | 1170 | r.err = err; |
| 1142 | 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 | 1177 | r.pos += n; |
| 1146 | 1178 | return n; |
| 1147 | 1179 | } |
| ... | ... | @@ -1167,6 +1199,10 @@ pub const Writer = struct { |
| 1167 | 1199 | |
| 1168 | 1200 | pub const SendfileError = error{ |
| 1169 | 1201 | UnsupportedOperation, |
| 1202 | SystemResources, | |
| 1203 | InputOutput, | |
| 1204 | BrokenPipe, | |
| 1205 | WouldBlock, | |
| 1170 | 1206 | Unexpected, |
| 1171 | 1207 | }; |
| 1172 | 1208 |
lib/std/http.zig+46-38| ... | ... | @@ -855,13 +855,7 @@ pub const BodyWriter = struct { |
| 855 | 855 | http_protocol_output: *std.io.BufferedWriter, |
| 856 | 856 | state: State, |
| 857 | 857 | elide: bool, |
| 858 | err: Error!void = {}, | |
| 859 | 858 | |
| 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 | 859 | pub const WriteError = std.io.Writer.Error; |
| 866 | 860 | |
| 867 | 861 | /// How many zeroes to reserve for hex-encoded chunk length. |
| ... | ... | @@ -1043,69 +1037,83 @@ pub const BodyWriter = struct { |
| 1043 | 1037 | } |
| 1044 | 1038 | |
| 1045 | 1039 | fn elideWriteFile( |
| 1046 | w: *BodyWriter, | |
| 1047 | offset: std.io.Writer.Offset, | |
| 1048 | limit: std.io.Writer.Limit, | |
| 1040 | file_reader: *std.fs.File.Reader, | |
| 1041 | limit: std.io.Limit, | |
| 1049 | 1042 | headers_and_trailers: []const []const u8, |
| 1050 | ) WriteError!usize { | |
| 1051 | if (offset != .none) { | |
| 1052 | if (countWriteFile(limit, headers_and_trailers)) |n| { | |
| 1043 | headers_len: usize, | |
| 1044 | ) error{ReadFailed}!usize { | |
| 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 | 1058 | return n; |
| 1054 | 1059 | } |
| 1055 | 1060 | } |
| 1056 | w.err = error.UnableToElideBody; | |
| 1057 | return error.WriteFailed; | |
| 1061 | for (headers_and_trailers[0..headers_len]) |bytes| n += bytes.len; | |
| 1062 | return n; | |
| 1058 | 1063 | } |
| 1059 | 1064 | |
| 1060 | 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 { | |
| 1062 | var total: usize = limit.toInt() orelse return null; | |
| 1063 | for (headers_and_trailers) |buf| total += buf.len; | |
| 1064 | return total; | |
| 1066 | fn countWriteFile( | |
| 1067 | file_reader: *std.fs.File.Reader, | |
| 1068 | limit: std.io.Limit, | |
| 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 | } |
| 1066 | 1075 | |
| 1067 | 1076 | fn noneWriteFile( |
| 1068 | 1077 | context: ?*anyopaque, |
| 1069 | file: std.fs.File, | |
| 1070 | offset: std.io.Writer.Offset, | |
| 1071 | limit: std.io.Writer.Limit, | |
| 1078 | file_reader: *std.fs.File.Reader, | |
| 1079 | limit: std.io.Limit, | |
| 1072 | 1080 | headers_and_trailers: []const []const u8, |
| 1073 | 1081 | headers_len: usize, |
| 1074 | 1082 | ) std.io.Writer.FileError!usize { |
| 1075 | if (limit == .nothing) return noneWriteSplat(context, headers_and_trailers, 1); | |
| 1076 | 1083 | const w: *BodyWriter = @alignCast(@ptrCast(context)); |
| 1077 | if (w.elide) return elideWriteFile(w, offset, limit, headers_and_trailers); | |
| 1078 | return w.http_protocol_output.writeFile(file, offset, limit, headers_and_trailers, headers_len); | |
| 1084 | if (w.elide) return elideWriteFile(file_reader, limit, headers_and_trailers, headers_len); | |
| 1085 | return w.http_protocol_output.writeFile(file_reader, limit, headers_and_trailers, headers_len); | |
| 1079 | 1086 | } |
| 1080 | 1087 | |
| 1081 | 1088 | fn contentLengthWriteFile( |
| 1082 | 1089 | context: ?*anyopaque, |
| 1083 | file: std.fs.File, | |
| 1084 | offset: std.io.Writer.Offset, | |
| 1085 | limit: std.io.Writer.Limit, | |
| 1090 | file_reader: *std.fs.File.Reader, | |
| 1091 | limit: std.io.Limit, | |
| 1086 | 1092 | headers_and_trailers: []const []const u8, |
| 1087 | 1093 | headers_len: usize, |
| 1088 | 1094 | ) std.io.Writer.FileError!usize { |
| 1089 | if (limit == .nothing) return contentLengthWriteSplat(context, headers_and_trailers, 1); | |
| 1090 | 1095 | const w: *BodyWriter = @alignCast(@ptrCast(context)); |
| 1091 | if (w.elide) return elideWriteFile(w, offset, limit, headers_and_trailers); | |
| 1092 | const n = try w.http_protocol_output.writeFile(file, offset, limit, headers_and_trailers, headers_len); | |
| 1096 | if (w.elide) return elideWriteFile(file_reader, limit, headers_and_trailers, headers_len); | |
| 1097 | const n = try w.http_protocol_output.writeFile(file_reader, limit, headers_and_trailers, headers_len); | |
| 1093 | 1098 | w.state.content_length -= n; |
| 1094 | 1099 | return n; |
| 1095 | 1100 | } |
| 1096 | 1101 | |
| 1097 | 1102 | fn chunkedWriteFile( |
| 1098 | 1103 | context: ?*anyopaque, |
| 1099 | file: std.fs.File, | |
| 1100 | offset: std.io.Writer.Offset, | |
| 1101 | limit: std.io.Writer.Limit, | |
| 1104 | file_reader: *std.fs.File.Reader, | |
| 1105 | limit: std.io.Limit, | |
| 1102 | 1106 | headers_and_trailers: []const []const u8, |
| 1103 | 1107 | headers_len: usize, |
| 1104 | 1108 | ) std.io.Writer.FileError!usize { |
| 1105 | if (limit == .nothing) return chunkedWriteSplat(context, headers_and_trailers, 1); | |
| 1106 | 1109 | const w: *BodyWriter = @alignCast(@ptrCast(context)); |
| 1107 | if (w.elide) return elideWriteFile(w, offset, limit, headers_and_trailers); | |
| 1108 | const data_len = countWriteFile(limit, headers_and_trailers) orelse @panic("TODO"); | |
| 1110 | if (w.elide) return elideWriteFile(file_reader, limit, headers_and_trailers, headers_len); | |
| 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 | 1117 | const bw = w.http_protocol_output; |
| 1110 | 1118 | const chunked = &w.state.chunked; |
| 1111 | 1119 | state: switch (chunked.*) { |
| ... | ... | @@ -1114,7 +1122,7 @@ pub const BodyWriter = struct { |
| 1114 | 1122 | const buffered_len = bw.end - off - chunk_header_template.len; |
| 1115 | 1123 | const chunk_len = data_len + buffered_len; |
| 1116 | 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 | 1126 | chunked.* = .{ .chunk_len = data_len + 2 - n }; |
| 1119 | 1127 | return n; |
| 1120 | 1128 | }, |
| ... | ... | @@ -1138,7 +1146,7 @@ pub const BodyWriter = struct { |
| 1138 | 1146 | }, |
| 1139 | 1147 | else => { |
| 1140 | 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 | 1150 | chunked.chunk_len = chunk_len - n; |
| 1143 | 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 | 161 | fn writeFile( |
| 162 | 162 | context: ?*anyopaque, |
| 163 | 163 | file_reader: *std.fs.File.Reader, |
| 164 | limit: std.io.Writer.Limit, | |
| 164 | limit: std.io.Limit, | |
| 165 | 165 | headers_and_trailers_full: []const []const u8, |
| 166 | 166 | headers_len_full: usize, |
| 167 | 167 | ) std.io.Writer.FileError!usize { |
| ... | ... | @@ -185,8 +185,11 @@ fn writeFile( |
| 185 | 185 | list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed; |
| 186 | 186 | for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes); |
| 187 | 187 | const dest = limit.slice(list.items.ptr[list.items.len..list.capacity]); |
| 188 | const n = try file_reader.read(dest); | |
| 189 | const is_end = if (file_reader.getSize()) |size| n >= size - pos else n == 0; | |
| 188 | const n = file_reader.read(dest) catch |err| switch (err) { | |
| 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 | 193 | if (is_end) { |
| 191 | 194 | new_capacity = list.capacity; |
| 192 | 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 | const Allocator = std.mem.Allocator; |
| 7 | 7 | const testing = std.testing; |
| 8 | 8 | const Limit = std.io.Limit; |
| 9 | const File = std.fs.File; | |
| 9 | 10 | |
| 10 | 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 | 544 | /// `error.Unimplemented` in the error set. |
| 544 | 545 | pub fn writeFile( |
| 545 | 546 | bw: *BufferedWriter, |
| 546 | file: std.fs.File, | |
| 547 | offset: Writer.Offset, | |
| 547 | file_reader: *File.Reader, | |
| 548 | 548 | limit: Limit, |
| 549 | 549 | headers_and_trailers: []const []const u8, |
| 550 | 550 | headers_len: usize, |
| 551 | 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 | } |
| 554 | 554 | |
| 555 | pub const WriteFileReadingError = std.fs.File.PReadError || Writer.Error; | |
| 556 | ||
| 557 | 555 | /// Returning zero bytes means end of stream. |
| 558 | 556 | /// |
| 559 | 557 | /// Asserts nonzero buffer capacity. |
| 560 | 558 | pub fn writeFileReading( |
| 561 | 559 | bw: *BufferedWriter, |
| 562 | file: std.fs.File, | |
| 563 | offset: Writer.Offset, | |
| 560 | file_reader: *File.Reader, | |
| 564 | 561 | limit: Limit, |
| 565 | ) WriteFileReadingError!usize { | |
| 562 | ) Writer.ReadingFileError!usize { | |
| 566 | 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 | 565 | bw.advance(n); |
| 569 | 566 | return n; |
| 570 | 567 | } |
| 571 | 568 | |
| 572 | 569 | fn passthruWriteFile( |
| 573 | 570 | context: ?*anyopaque, |
| 574 | file: std.fs.File, | |
| 575 | offset: Writer.Offset, | |
| 571 | file_reader: *File.Reader, | |
| 576 | 572 | limit: Limit, |
| 577 | 573 | headers_and_trailers: []const []const u8, |
| 578 | 574 | headers_len: usize, |
| ... | ... | @@ -581,7 +577,7 @@ fn passthruWriteFile( |
| 581 | 577 | const buffer = bw.buffer; |
| 582 | 578 | if (buffer.len == 0) return track( |
| 583 | 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 | 582 | const start_end = bw.end; |
| 587 | 583 | const headers = headers_and_trailers[0..headers_len]; |
| ... | ... | @@ -608,7 +604,7 @@ fn passthruWriteFile( |
| 608 | 604 | @memcpy(remaining_buffers_for_trailers[0..send_trailers_len], trailers[0..send_trailers_len]); |
| 609 | 605 | const send_headers_len = 1 + buffers_len; |
| 610 | 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 | 608 | if (n < end) { |
| 613 | 609 | @branchHint(.unlikely); |
| 614 | 610 | const remainder = buffer[n..end]; |
| ... | ... | @@ -638,7 +634,7 @@ fn passthruWriteFile( |
| 638 | 634 | @memcpy(remaining_buffers[0..send_trailers_len], trailers[0..send_trailers_len]); |
| 639 | 635 | const send_headers_len = @intFromBool(end != 0); |
| 640 | 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 | 638 | if (n < end) { |
| 643 | 639 | @branchHint(.unlikely); |
| 644 | 640 | const remainder = buffer[n..end]; |
| ... | ... | @@ -651,9 +647,6 @@ fn passthruWriteFile( |
| 651 | 647 | } |
| 652 | 648 | |
| 653 | 649 | pub 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 | 650 | limit: Limit = .unlimited, |
| 658 | 651 | /// Headers and trailers must be passed together so that in case `len` is |
| 659 | 652 | /// zero, they can be forwarded directly to `Writer.VTable.writeSplat`. |
| ... | ... | @@ -666,77 +659,51 @@ pub const WriteFileOptions = struct { |
| 666 | 659 | headers_len: usize = 0, |
| 667 | 660 | }; |
| 668 | 661 | |
| 669 | pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) WriteFileReadingError!void { | |
| 662 | pub fn writeFileAll( | |
| 663 | bw: *BufferedWriter, | |
| 664 | file_reader: *std.fs.File.Reader, | |
| 665 | options: WriteFileOptions, | |
| 666 | ) Writer.FileError!void { | |
| 670 | 667 | const headers_and_trailers = options.headers_and_trailers; |
| 671 | 668 | const headers = headers_and_trailers[0..options.headers_len]; |
| 672 | switch (options.limit) { | |
| 673 | .nothing => return bw.writeVecAll(headers_and_trailers), | |
| 674 | .unlimited => { | |
| 675 | // When reading the whole file, we cannot include the trailers in the | |
| 676 | // call that reads from the file handle, because we have no way to | |
| 677 | // determine whether a partial write is past the end of the file or | |
| 678 | // not. | |
| 679 | var i: usize = 0; | |
| 680 | var offset = options.offset; | |
| 681 | while (true) { | |
| 682 | var n = bw.writeFile(file, offset, .unlimited, headers[i..], headers.len - i) catch |err| switch (err) { | |
| 683 | error.Unimplemented => { | |
| 684 | try bw.writeVecAll(headers[i..]); | |
| 685 | try bw.writeFileReadingAll(file, offset, .unlimited); | |
| 686 | try bw.writeVecAll(headers_and_trailers[headers.len..]); | |
| 687 | return; | |
| 688 | }, | |
| 689 | else => |e| return e, | |
| 690 | }; | |
| 691 | while (i < headers.len and n >= headers[i].len) { | |
| 692 | n -= headers[i].len; | |
| 693 | i += 1; | |
| 694 | } | |
| 695 | if (i < headers.len) { | |
| 696 | headers[i] = headers[i][n..]; | |
| 697 | continue; | |
| 698 | } | |
| 699 | if (n == 0) break; | |
| 700 | offset = offset.advance(n); | |
| 701 | } | |
| 702 | }, | |
| 703 | else => { | |
| 704 | var len = options.limit.toInt().?; | |
| 705 | var i: usize = 0; | |
| 706 | var offset = options.offset; | |
| 707 | while (true) { | |
| 708 | var n = bw.writeFile(file, offset, .limited(len), headers_and_trailers[i..], headers.len - i) catch |err| switch (err) { | |
| 709 | error.Unimplemented => { | |
| 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 | }, | |
| 669 | var remaining = options.limit; | |
| 670 | var i: usize = 0; | |
| 671 | while (true) { | |
| 672 | const before_pos = file_reader.pos; | |
| 673 | var n = bw.writeFile(file_reader, remaining, headers_and_trailers[i..], headers.len - i) catch |err| switch (err) { | |
| 674 | error.ReadFailed => return error.ReadFailed, | |
| 675 | error.WriteFailed => return error.WriteFailed, | |
| 676 | error.Unimplemented => { | |
| 677 | file_reader.mode = file_reader.mode.toReading(); | |
| 678 | try bw.writeVecAll(headers[i..]); | |
| 679 | try bw.writeFileReadingAll(file_reader, remaining); | |
| 680 | try bw.writeVecAll(headers_and_trailers[headers.len..]); | |
| 681 | return; | |
| 682 | }, | |
| 683 | }; | |
| 684 | while (i < headers.len and n >= headers[i].len) { | |
| 685 | n -= headers[i].len; | |
| 686 | i += 1; | |
| 687 | } | |
| 688 | if (i < headers.len) { | |
| 689 | headers[i] = headers[i][n..]; | |
| 690 | continue; | |
| 691 | } | |
| 692 | const file_bytes_consumed = file_reader.pos - before_pos; | |
| 693 | remaining = remaining.subtract(file_bytes_consumed).?; | |
| 694 | const size = file_reader.size orelse continue; // End of file not yet reached. | |
| 695 | if (file_reader.pos < size) continue; // End of file not yet reached. | |
| 696 | n -= file_bytes_consumed; // Trailers reached. | |
| 697 | while (i < headers_and_trailers.len and n >= headers_and_trailers[i].len) { | |
| 698 | n -= headers_and_trailers[i].len; | |
| 699 | i += 1; | |
| 700 | } | |
| 701 | if (i < headers_and_trailers.len) { | |
| 702 | headers_and_trailers[i] = headers_and_trailers[i][n..]; | |
| 703 | try bw.writeVecAll(headers_and_trailers[i..]); | |
| 704 | return; | |
| 705 | } | |
| 706 | return; | |
| 740 | 707 | } |
| 741 | 708 | } |
| 742 | 709 | |
| ... | ... | @@ -748,28 +715,13 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp |
| 748 | 715 | /// Asserts nonzero buffer capacity. |
| 749 | 716 | pub fn writeFileReadingAll( |
| 750 | 717 | bw: *BufferedWriter, |
| 751 | file: std.fs.File, | |
| 752 | offset: Writer.Offset, | |
| 718 | file_reader: *File.Reader, | |
| 753 | 719 | limit: Limit, |
| 754 | ) WriteFileReadingError!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 | } | |
| 720 | ) Writer.ReadingFileError!void { | |
| 767 | 721 | var remaining = limit; |
| 768 | 722 | while (remaining.nonzero()) { |
| 769 | const dest = remaining.slice(try bw.writableSliceGreedy(1)); | |
| 770 | const n = try file.read(dest); | |
| 723 | const n = try writeFileReading(bw, file_reader, remaining); | |
| 771 | 724 | if (n == 0) return; |
| 772 | bw.advance(n); | |
| 773 | 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 | 46 | /// provided which is based on calling `read`, borrowing |
| 47 | 47 | /// `BufferedReader.buffer` to construct a temporary `BufferedWriter` and |
| 48 | 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 | }; |
| 51 | 51 | |
| 52 | 52 | pub const StreamError = error{ |
lib/std/io/Writer.zig+12-6| ... | ... | @@ -21,8 +21,8 @@ pub const VTable = struct { |
| 21 | 21 | /// of stream via `error.WriteFailed`. |
| 22 | 22 | writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize, |
| 23 | 23 | |
| 24 | /// Writes contents from an open file. `headers` are written first, then `len` | |
| 25 | /// bytes of `file` starting from `offset`, then `trailers`. | |
| 24 | /// Writes contents from an open file. `headers` are written first, then | |
| 25 | /// `limit` bytes of `file` starting from `offset`, then `trailers`. | |
| 26 | 26 | /// |
| 27 | 27 | /// Number of bytes actually written is returned, which may lie within |
| 28 | 28 | /// headers, the file, trailers, or anywhere in between. |
| ... | ... | @@ -31,9 +31,8 @@ pub const VTable = struct { |
| 31 | 31 | /// end-of-stream. A subsequent call may return nonzero, or may signal end |
| 32 | 32 | /// of stream via `error.WriteFailed`. |
| 33 | 33 | /// |
| 34 | /// If `error.Unimplemented` is returned, the caller should do its own | |
| 35 | /// reads from the file. The callee indicates it cannot offer a more | |
| 36 | /// efficient implementation. | |
| 34 | /// `error.Unimplemented` indicates the callee cannot offer a more | |
| 35 | /// efficient implementation than the caller performing its own reads. | |
| 37 | 36 | writeFile: *const fn ( |
| 38 | 37 | ctx: ?*anyopaque, |
| 39 | 38 | file_reader: *File.Reader, |
| ... | ... | @@ -43,7 +42,7 @@ pub const VTable = struct { |
| 43 | 42 | /// `headers_and_trailers` do not count towards this limit. |
| 44 | 43 | limit: Limit, |
| 45 | 44 | /// 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 | 46 | headers_and_trailers: []const []const u8, |
| 48 | 47 | headers_len: usize, |
| 49 | 48 | ) FileError!usize, |
| ... | ... | @@ -54,6 +53,13 @@ pub const Error = error{ |
| 54 | 53 | WriteFailed, |
| 55 | 54 | }; |
| 56 | 55 | |
| 56 | pub 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 | ||
| 57 | 63 | pub const FileError = error{ |
| 58 | 64 | /// Detailed diagnostics are found on the `File.Reader` struct. |
| 59 | 65 | ReadFailed, |
src/deprecated.zig-34| ... | ... | @@ -247,40 +247,6 @@ pub fn LinearFifo(comptime T: type) type { |
| 247 | 247 | return bytes.len; |
| 248 | 248 | } |
| 249 | 249 | |
| 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 | 250 | /// Make `count` items available before the current read location |
| 285 | 251 | fn rewind(self: *Self, count: usize) void { |
| 286 | 252 | assert(self.writableLength() >= count); |