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 {
915915 pos: u64 = 0,
916916 size: ?u64 = null,
917917 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
920928 pub const Mode = enum {
921929 streaming,
922930 positional,
931 /// Avoid syscalls other than `read` and `readv`.
923932 streaming_reading,
933 /// Avoid syscalls other than `pread` and `preadv`.
924934 positional_reading,
935 /// Indicates reading cannot continue because of a seek failure.
925936 failure,
926937
927938 pub fn toStreaming(m: @This()) @This() {
......@@ -931,6 +942,14 @@ pub const Reader = struct {
931942 .failure => .failure,
932943 };
933944 }
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 }
934953 };
935954
936955 pub fn interface(r: *Reader) std.io.Reader {
......@@ -960,7 +979,7 @@ pub const Reader = struct {
960979 };
961980 }
962981
963 pub fn seekBy(r: *Reader, offset: i64) SeekError!void {
982 pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void {
964983 switch (r.mode) {
965984 .positional, .positional_reading => {
966985 // TODO: make += operator allow any integer types
......@@ -977,19 +996,21 @@ pub const Reader = struct {
977996 break :e err;
978997 }
979998 };
980 if (offset < 0) return seek_err;
981 var remaining = offset;
999 var remaining = std.math.cast(u64, offset) orelse return seek_err;
9821000 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 };
9841005 r.pos += n;
9851006 remaining -= n;
9861007 }
9871008 },
988 .failure => return error.Unseekable,
1009 .failure => return r.seek_err.?,
9891010 }
9901011 }
9911012
992 pub fn seekTo(r: *Reader, offset: u64) SeekError!void {
1013 pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {
9931014 switch (r.mode) {
9941015 .positional, .positional_reading => {
9951016 r.pos = offset;
......@@ -1001,7 +1022,9 @@ pub const Reader = struct {
10011022 r.seek_err = err;
10021023 return err;
10031024 };
1025 r.pos = offset;
10041026 },
1027 .failure => return r.seek_err.?,
10051028 }
10061029 }
10071030
......@@ -1015,33 +1038,29 @@ pub const Reader = struct {
10151038 limit: std.io.Limit,
10161039 ) std.io.Reader.StreamError!usize {
10171040 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();
10281047 return 0;
10291048 },
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,
10431049 },
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 }
10451064 }
10461065
10471066 fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
......@@ -1077,7 +1096,10 @@ pub const Reader = struct {
10771096 r.err = err;
10781097 return error.ReadFailed;
10791098 };
1080 if (n == 0) return error.EndOfStream;
1099 if (n == 0) {
1100 r.size = pos;
1101 return error.EndOfStream;
1102 }
10811103 r.pos = pos + n;
10821104 return n;
10831105 }
......@@ -1093,7 +1115,10 @@ pub const Reader = struct {
10931115 r.err = err;
10941116 return error.ReadFailed;
10951117 };
1096 if (n == 0) return error.EndOfStream;
1118 if (n == 0) {
1119 r.size = pos;
1120 return error.EndOfStream;
1121 }
10971122 r.pos = pos + n;
10981123 return n;
10991124 }
......@@ -1113,6 +1138,7 @@ pub const Reader = struct {
11131138 r.pos = pos + n;
11141139 return n;
11151140 },
1141 .failure => return error.ReadFailed,
11161142 }
11171143 }
11181144
......@@ -1120,7 +1146,7 @@ pub const Reader = struct {
11201146 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
11211147 error.Unseekable => {
11221148 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 {
11241150 r.mode = .failure;
11251151 return error.ReadFailed;
11261152 };
......@@ -1131,7 +1157,10 @@ pub const Reader = struct {
11311157 return error.ReadFailed;
11321158 },
11331159 };
1134 if (n == 0) return error.EndOfStream;
1160 if (n == 0) {
1161 r.size = r.pos;
1162 return error.EndOfStream;
1163 }
11351164 r.pos += n;
11361165 return n;
11371166 }
......@@ -1141,7 +1170,10 @@ pub const Reader = struct {
11411170 r.err = err;
11421171 return error.ReadFailed;
11431172 };
1144 if (n == 0) return error.EndOfStream;
1173 if (n == 0) {
1174 r.size = r.pos;
1175 return error.EndOfStream;
1176 }
11451177 r.pos += n;
11461178 return n;
11471179 }
......@@ -1167,6 +1199,10 @@ pub const Writer = struct {
11671199
11681200 pub const SendfileError = error{
11691201 UnsupportedOperation,
1202 SystemResources,
1203 InputOutput,
1204 BrokenPipe,
1205 WouldBlock,
11701206 Unexpected,
11711207 };
11721208
lib/std/http.zig+46-38
......@@ -855,13 +855,7 @@ pub const BodyWriter = struct {
855855 http_protocol_output: *std.io.BufferedWriter,
856856 state: State,
857857 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 };
865859 pub const WriteError = std.io.Writer.Error;
866860
867861 /// How many zeroes to reserve for hex-encoded chunk length.
......@@ -1043,69 +1037,83 @@ pub const BodyWriter = struct {
10431037 }
10441038
10451039 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,
10491042 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;
10531058 return n;
10541059 }
10551060 }
1056 w.err = error.UnableToElideBody;
1057 return error.WriteFailed;
1061 for (headers_and_trailers[0..headers_len]) |bytes| n += bytes.len;
1062 return n;
10581063 }
10591064
10601065 /// 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);
10651074 }
10661075
10671076 fn noneWriteFile(
10681077 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,
10721080 headers_and_trailers: []const []const u8,
10731081 headers_len: usize,
10741082 ) std.io.Writer.FileError!usize {
1075 if (limit == .nothing) return noneWriteSplat(context, headers_and_trailers, 1);
10761083 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);
10791086 }
10801087
10811088 fn contentLengthWriteFile(
10821089 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,
10861092 headers_and_trailers: []const []const u8,
10871093 headers_len: usize,
10881094 ) std.io.Writer.FileError!usize {
1089 if (limit == .nothing) return contentLengthWriteSplat(context, headers_and_trailers, 1);
10901095 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);
10931098 w.state.content_length -= n;
10941099 return n;
10951100 }
10961101
10971102 fn chunkedWriteFile(
10981103 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,
11021106 headers_and_trailers: []const []const u8,
11031107 headers_len: usize,
11041108 ) std.io.Writer.FileError!usize {
1105 if (limit == .nothing) return chunkedWriteSplat(context, headers_and_trailers, 1);
11061109 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 };
11091117 const bw = w.http_protocol_output;
11101118 const chunked = &w.state.chunked;
11111119 state: switch (chunked.*) {
......@@ -1114,7 +1122,7 @@ pub const BodyWriter = struct {
11141122 const buffered_len = bw.end - off - chunk_header_template.len;
11151123 const chunk_len = data_len + buffered_len;
11161124 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);
11181126 chunked.* = .{ .chunk_len = data_len + 2 - n };
11191127 return n;
11201128 },
......@@ -1138,7 +1146,7 @@ pub const BodyWriter = struct {
11381146 },
11391147 else => {
11401148 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);
11421150 chunked.chunk_len = chunk_len - n;
11431151 return n;
11441152 },
lib/std/io/AllocatingWriter.zig+6-3
......@@ -161,7 +161,7 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.
161161fn writeFile(
162162 context: ?*anyopaque,
163163 file_reader: *std.fs.File.Reader,
164 limit: std.io.Writer.Limit,
164 limit: std.io.Limit,
165165 headers_and_trailers_full: []const []const u8,
166166 headers_len_full: usize,
167167) std.io.Writer.FileError!usize {
......@@ -185,8 +185,11 @@ fn writeFile(
185185 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
186186 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
187187 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;
190193 if (is_end) {
191194 new_capacity = list.capacity;
192195 for (trailers) |bytes| new_capacity += bytes.len;
lib/std/io/BufferedWriter.zig+56-104
......@@ -6,6 +6,7 @@ const Writer = std.io.Writer;
66const Allocator = std.mem.Allocator;
77const testing = std.testing;
88const Limit = std.io.Limit;
9const File = std.fs.File;
910
1011/// Underlying stream to send bytes to.
1112///
......@@ -543,36 +544,31 @@ pub fn writeSliceSwap(bw: *BufferedWriter, Elem: type, slice: []const Elem) Writ
543544/// `error.Unimplemented` in the error set.
544545pub fn writeFile(
545546 bw: *BufferedWriter,
546 file: std.fs.File,
547 offset: Writer.Offset,
547 file_reader: *File.Reader,
548548 limit: Limit,
549549 headers_and_trailers: []const []const u8,
550550 headers_len: usize,
551551) 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);
553553}
554554
555pub const WriteFileReadingError = std.fs.File.PReadError || Writer.Error;
556
557555/// Returning zero bytes means end of stream.
558556///
559557/// Asserts nonzero buffer capacity.
560558pub fn writeFileReading(
561559 bw: *BufferedWriter,
562 file: std.fs.File,
563 offset: Writer.Offset,
560 file_reader: *File.Reader,
564561 limit: Limit,
565) WriteFileReadingError!usize {
562) Writer.ReadingFileError!usize {
566563 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);
568565 bw.advance(n);
569566 return n;
570567}
571568
572569fn passthruWriteFile(
573570 context: ?*anyopaque,
574 file: std.fs.File,
575 offset: Writer.Offset,
571 file_reader: *File.Reader,
576572 limit: Limit,
577573 headers_and_trailers: []const []const u8,
578574 headers_len: usize,
......@@ -581,7 +577,7 @@ fn passthruWriteFile(
581577 const buffer = bw.buffer;
582578 if (buffer.len == 0) return track(
583579 &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),
585581 );
586582 const start_end = bw.end;
587583 const headers = headers_and_trailers[0..headers_len];
......@@ -608,7 +604,7 @@ fn passthruWriteFile(
608604 @memcpy(remaining_buffers_for_trailers[0..send_trailers_len], trailers[0..send_trailers_len]);
609605 const send_headers_len = 1 + buffers_len;
610606 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);
612608 if (n < end) {
613609 @branchHint(.unlikely);
614610 const remainder = buffer[n..end];
......@@ -638,7 +634,7 @@ fn passthruWriteFile(
638634 @memcpy(remaining_buffers[0..send_trailers_len], trailers[0..send_trailers_len]);
639635 const send_headers_len = @intFromBool(end != 0);
640636 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);
642638 if (n < end) {
643639 @branchHint(.unlikely);
644640 const remainder = buffer[n..end];
......@@ -651,9 +647,6 @@ fn passthruWriteFile(
651647}
652648
653649pub 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.
657650 limit: Limit = .unlimited,
658651 /// Headers and trailers must be passed together so that in case `len` is
659652 /// zero, they can be forwarded directly to `Writer.VTable.writeSplat`.
......@@ -666,77 +659,51 @@ pub const WriteFileOptions = struct {
666659 headers_len: usize = 0,
667660};
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 {
670667 const headers_and_trailers = options.headers_and_trailers;
671668 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;
740707 }
741708}
742709
......@@ -748,28 +715,13 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp
748715/// Asserts nonzero buffer capacity.
749716pub fn writeFileReadingAll(
750717 bw: *BufferedWriter,
751 file: std.fs.File,
752 offset: Writer.Offset,
718 file_reader: *File.Reader,
753719 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 {
767721 var remaining = limit;
768722 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);
771724 if (n == 0) return;
772 bw.advance(n);
773725 remaining = remaining.subtract(n).?;
774726 }
775727}
lib/std/io/Reader.zig+1-1
......@@ -46,7 +46,7 @@ pub const VTable = struct {
4646 /// provided which is based on calling `read`, borrowing
4747 /// `BufferedReader.buffer` to construct a temporary `BufferedWriter` and
4848 /// 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,
5050};
5151
5252pub const StreamError = error{
lib/std/io/Writer.zig+12-6
......@@ -21,8 +21,8 @@ pub const VTable = struct {
2121 /// of stream via `error.WriteFailed`.
2222 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`
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`.
2626 ///
2727 /// Number of bytes actually written is returned, which may lie within
2828 /// headers, the file, trailers, or anywhere in between.
......@@ -31,9 +31,8 @@ pub const VTable = struct {
3131 /// end-of-stream. A subsequent call may return nonzero, or may signal end
3232 /// of stream via `error.WriteFailed`.
3333 ///
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.
3736 writeFile: *const fn (
3837 ctx: ?*anyopaque,
3938 file_reader: *File.Reader,
......@@ -43,7 +42,7 @@ pub const VTable = struct {
4342 /// `headers_and_trailers` do not count towards this limit.
4443 limit: Limit,
4544 /// 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.
4746 headers_and_trailers: []const []const u8,
4847 headers_len: usize,
4948 ) FileError!usize,
......@@ -54,6 +53,13 @@ pub const Error = error{
5453 WriteFailed,
5554};
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
5763pub const FileError = error{
5864 /// Detailed diagnostics are found on the `File.Reader` struct.
5965 ReadFailed,
src/deprecated.zig-34
......@@ -247,40 +247,6 @@ pub fn LinearFifo(comptime T: type) type {
247247 return bytes.len;
248248 }
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
284250 /// Make `count` items available before the current read location
285251 fn rewind(self: *Self, count: usize) void {
286252 assert(self.writableLength() >= count);