| author | |
| committer | |
| log | 4dc3a9444cd94d2d10b427d8dd7a3515f9dc96ea |
| tree | ec83f3652cf7b3c81ac1afed8d74084ad50c0052 |
| parent | 19b82ca7ab7c44c3a1f4d0c6cbaf43bc0bded43a |
6 files changed, 251 insertions(+), 82 deletions(-)
lib/std/fs/File.zig+89-72| ... | ... | @@ -931,7 +931,8 @@ pub const WriteFileError = PReadError || WriteError; |
| 931 | 931 | |
| 932 | 932 | pub fn writeFileAll(self: File, in_file: File, options: BufferedWriter.WriteFileOptions) WriteFileError!void { |
| 933 | 933 | var file_writer = self.writer(); |
| 934 | var bw = file_writer.interface().buffered(&.{}); | |
| 934 | var buffer: [2000]u8 = undefined; | |
| 935 | var bw = file_writer.interface().buffered(&buffer); | |
| 935 | 936 | bw.writeFileAll(in_file, options) catch |err| switch (err) { |
| 936 | 937 | error.WriteFailed => if (file_writer.err) |_| unreachable else |e| return e, |
| 937 | 938 | else => |e| return e, |
| ... | ... | @@ -940,14 +941,27 @@ pub fn writeFileAll(self: File, in_file: File, options: BufferedWriter.WriteFile |
| 940 | 941 | |
| 941 | 942 | pub const Reader = struct { |
| 942 | 943 | file: File, |
| 943 | err: ReadError!void = {}, | |
| 944 | err: ?ReadError = null, | |
| 944 | 945 | mode: Reader.Mode = .positional, |
| 945 | 946 | pos: u64 = 0, |
| 946 | 947 | size: ?u64 = null, |
| 947 | size_err: GetEndPosError!void = {}, | |
| 948 | seek_err: SeekError!void = {}, | |
| 949 | ||
| 950 | pub const Mode = enum { streaming, positional }; | |
| 948 | size_err: ?GetEndPosError = null, | |
| 949 | seek_err: ?SeekError = null, | |
| 950 | ||
| 951 | pub const Mode = enum { | |
| 952 | streaming, | |
| 953 | positional, | |
| 954 | streaming_reading, | |
| 955 | positional_reading, | |
| 956 | ||
| 957 | pub fn toStreaming(m: @This()) @This() { | |
| 958 | return switch (m) { | |
| 959 | .positional => .streaming, | |
| 960 | .positional_reading => .streaming_reading, | |
| 961 | else => unreachable, | |
| 962 | }; | |
| 963 | } | |
| 964 | }; | |
| 951 | 965 | |
| 952 | 966 | pub fn interface(r: *Reader) std.io.Reader { |
| 953 | 967 | return .{ |
| ... | ... | @@ -975,7 +989,7 @@ pub const Reader = struct { |
| 975 | 989 | switch (r.mode) { |
| 976 | 990 | .positional => { |
| 977 | 991 | const size = r.size orelse { |
| 978 | if (r.file.getEndPos()) |size| { | |
| 992 | if (file.getEndPos()) |size| { | |
| 979 | 993 | r.size = size; |
| 980 | 994 | } else |err| { |
| 981 | 995 | r.size_err = err; |
| ... | ... | @@ -991,6 +1005,10 @@ pub const Reader = struct { |
| 991 | 1005 | assert(pos == 0); |
| 992 | 1006 | return 0; |
| 993 | 1007 | }, |
| 1008 | error.Unimplemented => { | |
| 1009 | r.mode = .positional_reading; | |
| 1010 | return 0; | |
| 1011 | }, | |
| 994 | 1012 | else => |e| { |
| 995 | 1013 | r.err = e; |
| 996 | 1014 | return error.ReadFailed; |
| ... | ... | @@ -1003,12 +1021,45 @@ pub const Reader = struct { |
| 1003 | 1021 | const n = bw.writeFile(file, .none, limit, &.{}, 0) catch |err| switch (err) { |
| 1004 | 1022 | error.WriteFailed => return error.WriteFailed, |
| 1005 | 1023 | error.Unseekable => unreachable, // Passing `Offset.none`. |
| 1024 | error.Unimplemented => { | |
| 1025 | r.mode = .streaming_reading; | |
| 1026 | return 0; | |
| 1027 | }, | |
| 1028 | else => |e| { | |
| 1029 | r.err = e; | |
| 1030 | return error.ReadFailed; | |
| 1031 | }, | |
| 1032 | }; | |
| 1033 | r.pos = pos + n; | |
| 1034 | return n; | |
| 1035 | }, | |
| 1036 | .positional_reading => { | |
| 1037 | const dest = limit.slice(try bw.writableSliceGreedy(1)); | |
| 1038 | const n = file.pread(dest, pos) catch |err| switch (err) { | |
| 1039 | error.Unseekable => { | |
| 1040 | r.mode = .streaming_reading; | |
| 1041 | assert(pos == 0); | |
| 1042 | return 0; | |
| 1043 | }, | |
| 1006 | 1044 | else => |e| { |
| 1007 | 1045 | r.err = e; |
| 1008 | 1046 | return error.ReadFailed; |
| 1009 | 1047 | }, |
| 1010 | 1048 | }; |
| 1049 | if (n == 0) return error.EndOfStream; | |
| 1050 | r.pos = pos + n; | |
| 1051 | bw.advance(n); | |
| 1052 | return n; | |
| 1053 | }, | |
| 1054 | .streaming_reading => { | |
| 1055 | const dest = limit.slice(try bw.writableSliceGreedy(1)); | |
| 1056 | const n = file.read(dest) catch |err| { | |
| 1057 | r.err = err; | |
| 1058 | return error.ReadFailed; | |
| 1059 | }; | |
| 1060 | if (n == 0) return error.EndOfStream; | |
| 1011 | 1061 | r.pos = pos + n; |
| 1062 | bw.advance(n); | |
| 1012 | 1063 | return n; |
| 1013 | 1064 | }, |
| 1014 | 1065 | } |
| ... | ... | @@ -1020,7 +1071,7 @@ pub const Reader = struct { |
| 1020 | 1071 | const pos = r.pos; |
| 1021 | 1072 | |
| 1022 | 1073 | switch (r.mode) { |
| 1023 | .positional => { | |
| 1074 | .positional, .positional_reading => { | |
| 1024 | 1075 | if (is_windows) { |
| 1025 | 1076 | // Unfortunately, `ReadFileScatter` cannot be used since it requires |
| 1026 | 1077 | // page alignment, so we are stuck using only the first slice. |
| ... | ... | @@ -1053,7 +1104,7 @@ pub const Reader = struct { |
| 1053 | 1104 | if (send_vecs.len == 0) return 0; // Prevent false positive end detection on empty `data`. |
| 1054 | 1105 | const n = posix.preadv(handle, send_vecs, pos) catch |err| switch (err) { |
| 1055 | 1106 | error.Unseekable => { |
| 1056 | r.mode = .streaming; | |
| 1107 | r.mode = r.mode.toStreaming(); | |
| 1057 | 1108 | assert(pos == 0); |
| 1058 | 1109 | return 0; |
| 1059 | 1110 | }, |
| ... | ... | @@ -1066,7 +1117,7 @@ pub const Reader = struct { |
| 1066 | 1117 | r.pos = pos + n; |
| 1067 | 1118 | return n; |
| 1068 | 1119 | }, |
| 1069 | .streaming => { | |
| 1120 | .streaming, .streaming_reading => { | |
| 1070 | 1121 | if (is_windows) { |
| 1071 | 1122 | // Unfortunately, `ReadFileScatter` cannot be used since it requires |
| 1072 | 1123 | // page alignment, so we are stuck using only the first slice. |
| ... | ... | @@ -1113,13 +1164,13 @@ pub const Reader = struct { |
| 1113 | 1164 | const file = r.file; |
| 1114 | 1165 | const pos = r.pos; |
| 1115 | 1166 | switch (r.mode) { |
| 1116 | .positional => { | |
| 1167 | .positional, .positional_reading => { | |
| 1117 | 1168 | const size = r.size orelse { |
| 1118 | 1169 | if (file.getEndPos()) |size| { |
| 1119 | 1170 | r.size = size; |
| 1120 | 1171 | } else |err| { |
| 1121 | 1172 | r.size_err = err; |
| 1122 | r.mode = .streaming; | |
| 1173 | r.mode = r.mode.toStreaming(); | |
| 1123 | 1174 | } |
| 1124 | 1175 | return 0; |
| 1125 | 1176 | }; |
| ... | ... | @@ -1127,17 +1178,13 @@ pub const Reader = struct { |
| 1127 | 1178 | r.pos = pos + delta; |
| 1128 | 1179 | return delta; |
| 1129 | 1180 | }, |
| 1130 | .streaming => { | |
| 1181 | .streaming, .streaming_reading => { | |
| 1131 | 1182 | // Unfortunately we can't seek forward without knowing the |
| 1132 | 1183 | // size because the seek syscalls provided to us will not |
| 1133 | 1184 | // return the true end position if a seek would exceed the |
| 1134 | 1185 | // end. |
| 1135 | 1186 | fallback: { |
| 1136 | if (r.size_err) |_| { | |
| 1137 | if (r.seek_err) |_| { | |
| 1138 | break :fallback; | |
| 1139 | } else |_| {} | |
| 1140 | } else |_| {} | |
| 1187 | if (r.size_err == null and r.seek_err == null) break :fallback; | |
| 1141 | 1188 | var trash_buffer: [std.atomic.cache_line]u8 = undefined; |
| 1142 | 1189 | const trash = &trash_buffer; |
| 1143 | 1190 | if (is_windows) { |
| ... | ... | @@ -1188,9 +1235,16 @@ pub const Writer = struct { |
| 1188 | 1235 | err: WriteError!void = {}, |
| 1189 | 1236 | mode: Writer.Mode = .positional, |
| 1190 | 1237 | pos: u64 = 0, |
| 1238 | sendfile_err: ?SendfileError = null, | |
| 1239 | read_err: ?ReadError = null, | |
| 1191 | 1240 | |
| 1192 | 1241 | pub const Mode = Reader.Mode; |
| 1193 | 1242 | |
| 1243 | pub const SendfileError = error{ | |
| 1244 | UnsupportedOperation, | |
| 1245 | Unexpected, | |
| 1246 | }; | |
| 1247 | ||
| 1194 | 1248 | /// Number of slices to store on the stack, when trying to send as many byte |
| 1195 | 1249 | /// vectors through the underlying write calls as possible. |
| 1196 | 1250 | const max_buffers_len = 16; |
| ... | ... | @@ -1269,75 +1323,38 @@ pub const Writer = struct { |
| 1269 | 1323 | const w: *Writer = @ptrCast(@alignCast(context)); |
| 1270 | 1324 | const out_fd = w.file.handle; |
| 1271 | 1325 | const in_fd = in_file.handle; |
| 1272 | const len_int = switch (in_limit) { | |
| 1273 | .nothing => return writeSplat(context, headers_and_trailers, 1), | |
| 1274 | .unlimited => 0, | |
| 1275 | else => in_limit.toInt().?, | |
| 1276 | }; | |
| 1277 | // TODO try using copy_file_range on linux | |
| 1278 | // TODO try using copy_file_range on freebsd | |
| 1279 | if (native_os == .linux) sf: { | |
| 1326 | // TODO try using copy_file_range on Linux | |
| 1327 | // TODO try using copy_file_range on FreeBSD | |
| 1328 | // TODO try using sendfile on macOS | |
| 1329 | // TODO try using sendfile on FreeBSD | |
| 1330 | if (native_os == .linux and w.mode == .streaming) sf: { | |
| 1331 | // Try using sendfile on Linux. | |
| 1332 | if (w.sendfile_err != null) break :sf; | |
| 1280 | 1333 | // Linux sendfile does not support headers or trailers but it does |
| 1281 | 1334 | // support a streaming read from in_file. |
| 1282 | 1335 | if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1); |
| 1283 | 1336 | const max_count = 0x7ffff000; // Avoid EINVAL. |
| 1284 | const smaller_len = if (len_int == 0) max_count else @min(len_int, max_count); | |
| 1337 | const smaller_len = in_limit.minInt(max_count); | |
| 1285 | 1338 | var off: std.os.linux.off_t = undefined; |
| 1286 | 1339 | const off_ptr: ?*std.os.linux.off_t = if (in_offset.toInt()) |offset| b: { |
| 1287 | 1340 | off = std.math.cast(std.os.linux.off_t, offset) orelse |
| 1288 | 1341 | return writeSplat(context, headers_and_trailers, 1); |
| 1289 | 1342 | break :b &off; |
| 1290 | 1343 | } else null; |
| 1291 | if (true) @panic("TODO"); | |
| 1292 | 1344 | const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, smaller_len) catch |err| switch (err) { |
| 1293 | error.UnsupportedOperation => break :sf, | |
| 1294 | error.Unseekable => break :sf, | |
| 1295 | error.Unexpected => break :sf, | |
| 1345 | // Errors that imply sendfile should be avoided on the next write. | |
| 1346 | error.UnsupportedOperation, | |
| 1347 | error.Unexpected, | |
| 1348 | => |e| { | |
| 1349 | w.sendfile_err = e; | |
| 1350 | break :sf; | |
| 1351 | }, | |
| 1296 | 1352 | else => |e| return e, |
| 1297 | 1353 | }; |
| 1298 | if (in_offset.toInt()) |offset| { | |
| 1299 | assert(n == off - offset); | |
| 1300 | } else if (n == 0 and len_int == 0) { | |
| 1301 | // The caller wouldn't be able to tell that the file transfer is | |
| 1302 | // done and would incorrectly repeat the same call. | |
| 1303 | return writeSplat(context, headers_and_trailers, 1); | |
| 1304 | } | |
| 1354 | w.pos += n; | |
| 1305 | 1355 | return n; |
| 1306 | 1356 | } |
| 1307 | var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined; | |
| 1308 | const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, headers_and_trailers.len)]; | |
| 1309 | for (iovecs, headers_and_trailers[0..iovecs.len]) |*v, d| v.* = .{ .base = d.ptr, .len = d.len }; | |
| 1310 | const headers = iovecs[0..@min(headers_len, iovecs.len)]; | |
| 1311 | const trailers = iovecs[headers.len..]; | |
| 1312 | const flags = 0; | |
| 1313 | return posix.sendfile(out_fd, in_fd, in_offset, len_int, headers, trailers, flags) catch |err| switch (err) { | |
| 1314 | error.Unseekable, | |
| 1315 | error.FastOpenAlreadyInProgress, | |
| 1316 | error.MessageTooBig, | |
| 1317 | error.FileDescriptorNotASocket, | |
| 1318 | error.NetworkUnreachable, | |
| 1319 | error.NetworkSubsystemFailed, | |
| 1320 | => return writeFileUnseekable(out_fd, in_fd, in_offset, in_limit, headers_and_trailers, headers_len), | |
| 1321 | ||
| 1322 | else => |e| return e, | |
| 1323 | }; | |
| 1324 | } | |
| 1325 | ||
| 1326 | fn writeFileUnseekable( | |
| 1327 | out_fd: Handle, | |
| 1328 | in_fd: Handle, | |
| 1329 | in_offset: u64, | |
| 1330 | in_limit: std.io.Writer.Limit, | |
| 1331 | headers_and_trailers: []const []const u8, | |
| 1332 | headers_len: usize, | |
| 1333 | ) std.io.Writer.FileError!usize { | |
| 1334 | _ = out_fd; | |
| 1335 | _ = in_fd; | |
| 1336 | _ = in_offset; | |
| 1337 | _ = in_limit; | |
| 1338 | _ = headers_and_trailers; | |
| 1339 | _ = headers_len; | |
| 1340 | @panic("TODO writeFileUnseekable"); | |
| 1357 | return error.Unimplemented; | |
| 1341 | 1358 | } |
| 1342 | 1359 | }; |
| 1343 | 1360 |
lib/std/io/BufferedReader.zig+1-1| ... | ... | @@ -99,7 +99,7 @@ pub fn readVecLimit(br: *BufferedReader, data: []const []u8, limit: Reader.Limit |
| 99 | 99 | |
| 100 | 100 | fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize { |
| 101 | 101 | const br: *BufferedReader = @alignCast(@ptrCast(context)); |
| 102 | const buffer = limit.slice(br.buffer[br.end..br.seek]); | |
| 102 | const buffer = limit.slice(br.buffer[br.seek..br.end]); | |
| 103 | 103 | if (buffer.len > 0) { |
| 104 | 104 | const n = try bw.write(buffer); |
| 105 | 105 | br.seek += n; |
lib/std/io/BufferedWriter.zig+78-3| ... | ... | @@ -480,6 +480,14 @@ pub fn writeSliceSwap(bw: *BufferedWriter, Elem: type, slice: []const Elem) Writ |
| 480 | 480 | /// Unlike `writeSplat` and `writeVec`, this function will call into the |
| 481 | 481 | /// underlying writer even if there is enough buffer capacity for the file |
| 482 | 482 | /// contents. |
| 483 | /// | |
| 484 | /// Although it would be possible to eliminate `error.Unimplemented` from | |
| 485 | /// the error set by reading directly into the buffer in such case, | |
| 486 | /// this is not done because it is more efficient to do it in `writeFileAll` | |
| 487 | /// so that the error does not occur with each write. | |
| 488 | /// | |
| 489 | /// See `writeFileReading` for an alternative that does not have | |
| 490 | /// `error.Unimplemented` in the error set. | |
| 483 | 491 | pub fn writeFile( |
| 484 | 492 | bw: *BufferedWriter, |
| 485 | 493 | file: std.fs.File, |
| ... | ... | @@ -491,6 +499,23 @@ pub fn writeFile( |
| 491 | 499 | return passthruWriteFile(bw, file, offset, limit, headers_and_trailers, headers_len); |
| 492 | 500 | } |
| 493 | 501 | |
| 502 | pub const WriteFileReadingError = std.fs.File.PReadError || Writer.Error; | |
| 503 | ||
| 504 | /// Returning zero bytes means end of stream. | |
| 505 | /// | |
| 506 | /// Asserts nonzero buffer capacity. | |
| 507 | pub fn writeFileReading( | |
| 508 | bw: *BufferedWriter, | |
| 509 | file: std.fs.File, | |
| 510 | offset: Writer.Offset, | |
| 511 | limit: Writer.Limit, | |
| 512 | ) WriteFileReadingError!usize { | |
| 513 | const dest = limit.slice(try bw.writableSliceGreedy(1)); | |
| 514 | const n = if (offset.toInt()) |pos| try file.pread(dest, pos) else try file.read(dest); | |
| 515 | bw.advance(n); | |
| 516 | return n; | |
| 517 | } | |
| 518 | ||
| 494 | 519 | fn passthruWriteFile( |
| 495 | 520 | context: ?*anyopaque, |
| 496 | 521 | file: std.fs.File, |
| ... | ... | @@ -588,7 +613,7 @@ pub const WriteFileOptions = struct { |
| 588 | 613 | headers_len: usize = 0, |
| 589 | 614 | }; |
| 590 | 615 | |
| 591 | pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) Writer.FileError!void { | |
| 616 | pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) WriteFileReadingError!void { | |
| 592 | 617 | const headers_and_trailers = options.headers_and_trailers; |
| 593 | 618 | const headers = headers_and_trailers[0..options.headers_len]; |
| 594 | 619 | switch (options.limit) { |
| ... | ... | @@ -601,7 +626,15 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp |
| 601 | 626 | var i: usize = 0; |
| 602 | 627 | var offset = options.offset; |
| 603 | 628 | while (true) { |
| 604 | var n = try bw.writeFile(file, offset, .unlimited, headers[i..], headers.len - i); | |
| 629 | var n = bw.writeFile(file, offset, .unlimited, headers[i..], headers.len - i) catch |err| switch (err) { | |
| 630 | error.Unimplemented => { | |
| 631 | try bw.writeVecAll(headers[i..]); | |
| 632 | try bw.writeFileReadingAll(file, offset, .unlimited); | |
| 633 | try bw.writeVecAll(headers_and_trailers[headers.len..]); | |
| 634 | return; | |
| 635 | }, | |
| 636 | else => |e| return e, | |
| 637 | }; | |
| 605 | 638 | while (i < headers.len and n >= headers[i].len) { |
| 606 | 639 | n -= headers[i].len; |
| 607 | 640 | i += 1; |
| ... | ... | @@ -619,7 +652,15 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp |
| 619 | 652 | var i: usize = 0; |
| 620 | 653 | var offset = options.offset; |
| 621 | 654 | while (true) { |
| 622 | var n = try bw.writeFile(file, offset, .limited(len), headers_and_trailers[i..], headers.len - i); | |
| 655 | var n = bw.writeFile(file, offset, .limited(len), headers_and_trailers[i..], headers.len - i) catch |err| switch (err) { | |
| 656 | error.Unimplemented => { | |
| 657 | try bw.writeVecAll(headers[i..]); | |
| 658 | try bw.writeFileReadingAll(file, offset, .limited(len)); | |
| 659 | try bw.writeVecAll(headers_and_trailers[headers.len..]); | |
| 660 | return; | |
| 661 | }, | |
| 662 | else => |e| return e, | |
| 663 | }; | |
| 623 | 664 | while (i < headers.len and n >= headers[i].len) { |
| 624 | 665 | n -= headers[i].len; |
| 625 | 666 | i += 1; |
| ... | ... | @@ -646,6 +687,40 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp |
| 646 | 687 | } |
| 647 | 688 | } |
| 648 | 689 | |
| 690 | /// Equivalent to `writeFileAll` but uses direct `pread` and `read` calls on | |
| 691 | /// `file` rather than `Writer.writeFile`. This is generally used as a fallback | |
| 692 | /// when the underlying implementation returns `error.Unimplemented`, which is | |
| 693 | /// why that error code does not appear in this function's error set. | |
| 694 | /// | |
| 695 | /// Asserts nonzero buffer capacity. | |
| 696 | pub fn writeFileReadingAll( | |
| 697 | bw: *BufferedWriter, | |
| 698 | file: std.fs.File, | |
| 699 | offset: Writer.Offset, | |
| 700 | limit: Writer.Limit, | |
| 701 | ) WriteFileReadingError!void { | |
| 702 | if (offset.toInt()) |start_pos| { | |
| 703 | var remaining = limit; | |
| 704 | var pos = start_pos; | |
| 705 | while (remaining.nonzero()) { | |
| 706 | const dest = remaining.slice(try bw.writableSliceGreedy(1)); | |
| 707 | const n = try file.pread(dest, pos); | |
| 708 | if (n == 0) return; | |
| 709 | bw.advance(n); | |
| 710 | pos += n; | |
| 711 | remaining = remaining.subtract(n).?; | |
| 712 | } | |
| 713 | } | |
| 714 | var remaining = limit; | |
| 715 | while (remaining.nonzero()) { | |
| 716 | const dest = remaining.slice(try bw.writableSliceGreedy(1)); | |
| 717 | const n = try file.read(dest); | |
| 718 | if (n == 0) return; | |
| 719 | bw.advance(n); | |
| 720 | remaining = remaining.subtract(n).?; | |
| 721 | } | |
| 722 | } | |
| 723 | ||
| 649 | 724 | pub fn alignBuffer( |
| 650 | 725 | bw: *BufferedWriter, |
| 651 | 726 | buffer: []const u8, |
lib/std/io/Writer.zig+18-4| ... | ... | @@ -30,12 +30,20 @@ pub const VTable = struct { |
| 30 | 30 | /// Number of bytes returned may be zero, which does not mean |
| 31 | 31 | /// end-of-stream. A subsequent call may return nonzero, or may signal end |
| 32 | 32 | /// of stream via `error.WriteFailed`. |
| 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. | |
| 33 | 37 | writeFile: *const fn ( |
| 34 | 38 | ctx: ?*anyopaque, |
| 35 | 39 | file: std.fs.File, |
| 36 | /// If this is `none`, `file` will be streamed, affecting the seek | |
| 37 | /// position. Otherwise, it will be read positionally without affecting | |
| 38 | /// the seek position. | |
| 40 | /// If this is `Offset.none`, `file` will be streamed, affecting the | |
| 41 | /// seek position. Otherwise, it will be read positionally without | |
| 42 | /// affecting the seek position. `error.Unseekable` is only possible | |
| 43 | /// when reading positionally. | |
| 44 | /// | |
| 45 | /// An offset past the end of the file is treated the same as an offset | |
| 46 | /// equal to the end of the file. | |
| 39 | 47 | offset: Offset, |
| 40 | 48 | /// Maximum amount of bytes to read from the file. Implementations may |
| 41 | 49 | /// assume that the file size does not exceed this amount. |
| ... | ... | @@ -52,7 +60,13 @@ pub const Error = error{ |
| 52 | 60 | WriteFailed, |
| 53 | 61 | }; |
| 54 | 62 | |
| 55 | pub const FileError = Error || std.fs.File.PReadError; | |
| 63 | pub const FileError = std.fs.File.PReadError || error{ | |
| 64 | /// See the `Writer` implementation for detailed diagnostics. | |
| 65 | WriteFailed, | |
| 66 | /// Indicates the caller should do its own file reading; the callee cannot | |
| 67 | /// offer a more efficient implementation. | |
| 68 | Unimplemented, | |
| 69 | }; | |
| 56 | 70 | |
| 57 | 71 | pub const Limit = std.io.Reader.Limit; |
| 58 | 72 |
lib/std/os/linux.zig+64-1| ... | ... | @@ -9420,4 +9420,67 @@ pub const msghdr_const = extern struct { |
| 9420 | 9420 | control: ?*const anyopaque, |
| 9421 | 9421 | controllen: usize, |
| 9422 | 9422 | flags: u32, |
| 9423 | }; | |
| \ No newline at end of file | ||
| 9423 | }; | |
| 9424 | ||
| 9425 | /// The syscalls, but with Zig error sets, going through libc if linking libc, | |
| 9426 | /// and with some footguns eliminated. | |
| 9427 | pub const wrapped = struct { | |
| 9428 | pub const lfs64_abi = builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid()); | |
| 9429 | const system = if (builtin.link_libc) std.c else std.os.linux; | |
| 9430 | ||
| 9431 | pub const SendfileError = std.posix.UnexpectedError || error{ | |
| 9432 | /// `out_fd` is an unconnected socket, or out_fd closed its read end. | |
| 9433 | BrokenPipe, | |
| 9434 | /// Descriptor is not valid or locked, or an mmap(2)-like operation is not available for in_fd. | |
| 9435 | UnsupportedOperation, | |
| 9436 | /// Nonblocking I/O has been selected but the write would block. | |
| 9437 | WouldBlock, | |
| 9438 | /// Unspecified error while reading from in_fd. | |
| 9439 | InputOutput, | |
| 9440 | /// Insufficient kernel memory to read from in_fd. | |
| 9441 | SystemResources, | |
| 9442 | /// `offset` is not `null` but the input file is not seekable. | |
| 9443 | Unseekable, | |
| 9444 | }; | |
| 9445 | ||
| 9446 | pub fn sendfile( | |
| 9447 | out_fd: fd_t, | |
| 9448 | in_fd: fd_t, | |
| 9449 | in_offset: ?*off_t, | |
| 9450 | in_len: usize, | |
| 9451 | ) SendfileError!usize { | |
| 9452 | const adjusted_len = @min(in_len, 0x7ffff000); // Prevents EOVERFLOW. | |
| 9453 | const sendfileSymbol = if (lfs64_abi) system.sendfile64 else system.sendfile; | |
| 9454 | const rc = sendfileSymbol(out_fd, in_fd, in_offset, adjusted_len); | |
| 9455 | switch (errno(rc)) { | |
| 9456 | .SUCCESS => return @bitCast(rc), | |
| 9457 | .BADF => return invalidApiUsage(), // Always a race condition. | |
| 9458 | .FAULT => return invalidApiUsage(), // Segmentation fault. | |
| 9459 | .OVERFLOW => return unexpectedErrno(.OVERFLOW), // We avoid passing too large of a `count`. | |
| 9460 | .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket | |
| 9461 | .INVAL => return error.UnsupportedOperation, | |
| 9462 | .AGAIN => return error.WouldBlock, | |
| 9463 | .IO => return error.InputOutput, | |
| 9464 | .PIPE => return error.BrokenPipe, | |
| 9465 | .NOMEM => return error.SystemResources, | |
| 9466 | .NXIO => return error.Unseekable, | |
| 9467 | .SPIPE => return error.Unseekable, | |
| 9468 | else => |err| return unexpectedErrno(err), | |
| 9469 | } | |
| 9470 | } | |
| 9471 | ||
| 9472 | const unexpectedErrno = std.posix.unexpectedErrno; | |
| 9473 | ||
| 9474 | fn invalidApiUsage() error{Unexpected} { | |
| 9475 | if (builtin.mode == .Debug) @panic("invalid API usage"); | |
| 9476 | return error.Unexpected; | |
| 9477 | } | |
| 9478 | ||
| 9479 | fn errno(rc: anytype) E { | |
| 9480 | if (builtin.link_libc) { | |
| 9481 | return if (rc == -1) @enumFromInt(std.c._errno().*) else .SUCCESS; | |
| 9482 | } else { | |
| 9483 | return errnoFromSyscall(rc); | |
| 9484 | } | |
| 9485 | } | |
| 9486 | }; |
lib/std/posix.zig+1-1| ... | ... | @@ -7553,7 +7553,7 @@ pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void { |
| 7553 | 7553 | } |
| 7554 | 7554 | } |
| 7555 | 7555 | |
| 7556 | const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid()); | |
| 7556 | const lfs64_abi = native_os == .linux and linux.wrapped.lfs64_abi; | |
| 7557 | 7557 | |
| 7558 | 7558 | /// Whether or not `error.Unexpected` will print its value and a stack trace. |
| 7559 | 7559 | /// |