authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-05-28 13:56:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:29-07:00
log3650cd3e8efd730fb6305bdefbbf8053c352bb0c
tree8e41c99024345b1cdb7774d5e9dcac2ee33da3d8
parent7b417c6caf13e07d05bf798cae2f8beeaee4fe6c

std.fs.File: update for new writeFile API


15 files changed, 160 insertions(+), 279 deletions(-)

lib/std/Build/Cache.zig+1-1
......@@ -663,7 +663,7 @@ pub const Manifest = struct {
663663 const gpa = self.cache.gpa;
664664 const input_file_count = self.files.entries.len;
665665 var manifest_reader = self.manifest_file.?.reader(); // Reads positionally from zero.
666 const limit: std.io.Reader.Limit = .limited(manifest_file_size_max);
666 const limit: std.io.Limit = .limited(manifest_file_size_max);
667667 const file_contents = manifest_reader.interface().readRemainingAlloc(gpa, limit) catch |err| switch (err) {
668668 error.OutOfMemory => return error.OutOfMemory,
669669 error.StreamTooLong => return error.OutOfMemory,
lib/std/compress/flate/Compress.zig+1-1
......@@ -768,7 +768,7 @@ fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
768768fn read(
769769 context: ?*anyopaque,
770770 bw: *std.io.BufferedWriter,
771 limit: std.io.Reader.Limit,
771 limit: std.io.Limit,
772772) std.io.Reader.RwError!usize {
773773 const c: *Compress = @ptrCast(@alignCast(context));
774774 switch (c.state) {
lib/std/compress/flate/Decompress.zig+3-3
......@@ -142,7 +142,7 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
142142pub fn read(
143143 context: ?*anyopaque,
144144 bw: *std.io.BufferedWriter,
145 limit: std.io.Reader.Limit,
145 limit: std.io.Limit,
146146) std.io.Reader.RwError!usize {
147147 const d: *Decompress = @alignCast(@ptrCast(context));
148148 return readInner(d, bw, limit) catch |err| switch (err) {
......@@ -160,7 +160,7 @@ pub fn read(
160160fn readInner(
161161 d: *Decompress,
162162 bw: *std.io.BufferedWriter,
163 limit: std.io.Reader.Limit,
163 limit: std.io.Limit,
164164) (Error || error{ WriteFailed, EndOfStream })!usize {
165165 const in = d.input;
166166 sw: switch (d.state) {
......@@ -351,7 +351,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
351351 @panic("TODO remove readVec primitive");
352352}
353353
354fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
354fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
355355 _ = context;
356356 _ = limit;
357357 // Problem here is we still need access to the output ring buffer.
lib/std/compress/zstd/Decompress.zig+12-11
......@@ -2,6 +2,7 @@ const Decompress = @This();
22const std = @import("std");
33const assert = std.debug.assert;
44const Reader = std.io.Reader;
5const Limit = std.io.Limit;
56const BufferedWriter = std.io.BufferedWriter;
67const BufferedReader = std.io.BufferedReader;
78const zstd = @import("../zstd.zig");
......@@ -77,7 +78,7 @@ pub fn reader(self: *Decompress) Reader {
7778 };
7879}
7980
80fn read(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {
81fn read(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) Reader.RwError!usize {
8182 const d: *Decompress = @ptrCast(@alignCast(context));
8283 const in = d.input;
8384
......@@ -139,7 +140,7 @@ fn initFrame(d: *Decompress, window_size_max: usize, magic: Frame.Magic) !void {
139140 }
140141}
141142
142fn readInFrame(d: *Decompress, bw: *BufferedWriter, limit: Reader.Limit, state: *State.InFrame) !usize {
143fn readInFrame(d: *Decompress, bw: *BufferedWriter, limit: Limit, state: *State.InFrame) !usize {
143144 const in = d.input;
144145
145146 const header_bytes = try in.takeArray(3);
......@@ -166,7 +167,7 @@ fn readInFrame(d: *Decompress, bw: *BufferedWriter, limit: Reader.Limit, state:
166167 var literals_buffer: [zstd.block_size_max]u8 = undefined;
167168 var sequence_buffer: [zstd.block_size_max]u8 = undefined;
168169 var decode: Frame.Zstandard.Decode = .init(&literal_fse_buffer, &match_fse_buffer, &offset_fse_buffer);
169 var remaining: Reader.Limit = .limited(block_size);
170 var remaining: Limit = .limited(block_size);
170171 const literals = try LiteralsSection.decode(in, &remaining, &literals_buffer);
171172 const sequences_header = try SequencesSection.Header.decode(in, &remaining);
172173
......@@ -446,7 +447,7 @@ pub const Frame = struct {
446447 pub fn prepare(
447448 self: *Decode,
448449 in: *BufferedReader,
449 remaining: *Reader.Limit,
450 remaining: *Limit,
450451 literals: LiteralsSection,
451452 sequences_header: SequencesSection.Header,
452453 ) PrepareError!void {
......@@ -536,7 +537,7 @@ pub const Frame = struct {
536537 fn updateFseTable(
537538 self: *Decode,
538539 in: *BufferedReader,
539 remaining: *Reader.Limit,
540 remaining: *Limit,
540541 comptime choice: DataType,
541542 mode: SequencesSection.Header.Mode,
542543 ) !void {
......@@ -857,7 +858,7 @@ pub const LiteralsSection = struct {
857858 compressed_size: ?u18,
858859
859860 /// Decode a literals section header.
860 pub fn decode(in: *BufferedReader, remaining: *Reader.Limit) !Header {
861 pub fn decode(in: *BufferedReader, remaining: *Limit) !Header {
861862 remaining.* = remaining.subtract(1) orelse return error.EndOfStream;
862863 const byte0 = try in.takeByte();
863864 const block_type: BlockType = @enumFromInt(byte0 & 0b11);
......@@ -964,7 +965,7 @@ pub const LiteralsSection = struct {
964965 MissingStartBit,
965966 };
966967
967 pub fn decode(in: *BufferedReader, remaining: *Reader.Limit) HuffmanTree.DecodeError!HuffmanTree {
968 pub fn decode(in: *BufferedReader, remaining: *Limit) HuffmanTree.DecodeError!HuffmanTree {
968969 remaining.* = remaining.subtract(1) orelse return error.EndOfStream;
969970 const header = try in.takeByte();
970971 if (header < 128) {
......@@ -976,7 +977,7 @@ pub const LiteralsSection = struct {
976977
977978 fn decodeDirect(
978979 in: *BufferedReader,
979 remaining: *Reader.Limit,
980 remaining: *Limit,
980981 encoded_symbol_count: usize,
981982 ) HuffmanTree.DecodeError!HuffmanTree {
982983 var weights: [256]u4 = undefined;
......@@ -993,7 +994,7 @@ pub const LiteralsSection = struct {
993994
994995 fn decodeFse(
995996 in: *BufferedReader,
996 remaining: *Reader.Limit,
997 remaining: *Limit,
997998 compressed_size: usize,
998999 ) HuffmanTree.DecodeError!HuffmanTree {
9991000 var weights: [256]u4 = undefined;
......@@ -1161,7 +1162,7 @@ pub const LiteralsSection = struct {
11611162 MissingStartBit,
11621163 };
11631164
1164 pub fn decode(in: *BufferedReader, remaining: *Reader.Limit, buffer: []u8) DecodeError!LiteralsSection {
1165 pub fn decode(in: *BufferedReader, remaining: *Limit, buffer: []u8) DecodeError!LiteralsSection {
11651166 const header = try Header.decode(in, remaining);
11661167 switch (header.block_type) {
11671168 .raw => {
......@@ -1232,7 +1233,7 @@ pub const SequencesSection = struct {
12321233 ReadFailed,
12331234 };
12341235
1235 pub fn decode(in: *BufferedReader, remaining: *Reader.Limit) DecodeError!Header {
1236 pub fn decode(in: *BufferedReader, remaining: *Limit) DecodeError!Header {
12361237 var sequence_count: u24 = undefined;
12371238
12381239 remaining.* = remaining.subtract(1) orelse return error.EndOfStream;
lib/std/crypto/tls/Client.zig+1-1
......@@ -1043,7 +1043,7 @@ pub fn eof(c: Client) bool {
10431043 return c.received_close_notify;
10441044}
10451045
1046fn read(context: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {
1046fn read(context: ?*anyopaque, bw: *std.io.BufferedWriter, limit: std.io.Limit) Reader.RwError!usize {
10471047 const c: *Client = @ptrCast(@alignCast(context));
10481048 if (c.eof()) return error.EndOfStream;
10491049 const input = c.input;
lib/std/fifo.zig+2-2
......@@ -247,7 +247,7 @@ pub fn LinearFifo(
247247 fn readerRead(
248248 ctx: ?*anyopaque,
249249 bw: *std.io.BufferedWriter,
250 limit: std.io.Reader.Limit,
250 limit: std.io.Limit,
251251 ) std.io.Reader.RwError!usize {
252252 const fifo: *Self = @alignCast(@ptrCast(ctx));
253253 _ = fifo;
......@@ -261,7 +261,7 @@ pub fn LinearFifo(
261261 _ = data;
262262 @panic("TODO");
263263 }
264 fn readerDiscard(ctx: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
264 fn readerDiscard(ctx: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
265265 const fifo: *Self = @alignCast(@ptrCast(ctx));
266266 _ = fifo;
267267 _ = limit;
lib/std/fs/Dir.zig+3-3
......@@ -1963,7 +1963,7 @@ pub fn readFileAlloc(
19631963 /// * The array list's length is increased by exactly one byte past `limit`.
19641964 /// * The file seek position is advanced by exactly one byte past `limit`.
19651965 /// * `error.StreamTooLong` is returned.
1966 limit: std.io.Reader.Limit,
1966 limit: std.io.Limit,
19671967) ReadFileAllocError![]u8 {
19681968 return dir.readFileAllocOptions(file_path, gpa, limit, null, .of(u8), null);
19691969}
......@@ -1982,7 +1982,7 @@ pub fn readFileAllocOptions(
19821982 /// * The array list's length is increased by exactly one byte past `limit`.
19831983 /// * The file seek position is advanced by exactly one byte past `limit`.
19841984 /// * `error.StreamTooLong` is returned.
1985 limit: std.io.Reader.Limit,
1985 limit: std.io.Limit,
19861986 /// If specified, the initial buffer size is calculated using this value,
19871987 /// otherwise the effective file size is used instead.
19881988 size_hint: ?usize,
......@@ -2020,7 +2020,7 @@ pub fn readFileIntoArrayList(
20202020 /// On other platforms, an opaque sequence of bytes with no particular encoding.
20212021 file_path: []const u8,
20222022 gpa: Allocator,
2023 limit: std.io.Reader.Limit,
2023 limit: std.io.Limit,
20242024 /// If specified, the initial buffer size is calculated using this value,
20252025 /// otherwise the effective file size is used instead.
20262026 size_hint: ?usize,
lib/std/fs/File.zig+109-198
......@@ -922,12 +922,13 @@ pub const Reader = struct {
922922 positional,
923923 streaming_reading,
924924 positional_reading,
925 failure,
925926
926927 pub fn toStreaming(m: @This()) @This() {
927928 return switch (m) {
928 .positional => .streaming,
929 .positional_reading => .streaming_reading,
930 else => unreachable,
929 .positional, .streaming => .streaming,
930 .positional_reading, .streaming_reading => .streaming_reading,
931 .failure => .failure,
931932 };
932933 }
933934 };
......@@ -936,8 +937,7 @@ pub const Reader = struct {
936937 return .{
937938 .context = r,
938939 .vtable = &.{
939 .read = Reader.read,
940 .readVec = Reader.readVec,
940 .read = Reader.stream,
941941 .discard = Reader.discard,
942942 },
943943 };
......@@ -963,12 +963,14 @@ pub const Reader = struct {
963963 pub fn seekBy(r: *Reader, offset: i64) SeekError!void {
964964 switch (r.mode) {
965965 .positional, .positional_reading => {
966 r.pos += offset;
966 // TODO: make += operator allow any integer types
967 r.pos = @intCast(@as(i64, @intCast(r.pos)) + offset);
967968 },
968969 .streaming, .streaming_reading => {
969970 const seek_err = r.seek_err orelse e: {
970971 if (posix.lseek_CUR(r.file.handle, offset)) |_| {
971 r.pos += offset;
972 // TODO: make += operator allow any integer types
973 r.pos = @intCast(@as(i64, @intCast(r.pos)) + offset);
972974 return;
973975 } else |err| {
974976 r.seek_err = err;
......@@ -983,6 +985,7 @@ pub const Reader = struct {
983985 remaining -= n;
984986 }
985987 },
988 .failure => return error.Unseekable,
986989 }
987990 }
988991
......@@ -1006,188 +1009,42 @@ pub const Reader = struct {
10061009 /// vectors through the underlying read calls as possible.
10071010 const max_buffers_len = 16;
10081011
1009 fn read(
1012 fn stream(
10101013 context: ?*anyopaque,
10111014 bw: *BufferedWriter,
1012 limit: std.io.Reader.Limit,
1013 ) std.io.Reader.RwError!usize {
1015 limit: std.io.Limit,
1016 ) std.io.Reader.StreamError!usize {
10141017 const r: *Reader = @ptrCast(@alignCast(context));
1015 const file = r.file;
1016 const pos = r.pos;
1017 switch (r.mode) {
1018 .positional => {
1019 const size = r.size orelse {
1020 if (file.getEndPos()) |size| {
1021 r.size = size;
1022 } else |err| {
1023 r.size_err = err;
1024 r.mode = .streaming;
1025 }
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;
10261024 return 0;
1027 };
1028 const new_limit = limit.min(.limited(size - pos));
1029 const n = bw.writeFile(file, .init(pos), new_limit, &.{}, 0) catch |err| switch (err) {
1030 error.WriteFailed => return error.WriteFailed,
1031 error.Unseekable => {
1032 r.mode = .streaming;
1033 if (pos != 0) @panic("TODO need to seek here");
1034 return 0;
1035 },
1036 error.Unimplemented => {
1037 r.mode = .positional_reading;
1038 return 0;
1039 },
1040 else => |e| {
1041 r.err = e;
1042 return error.ReadFailed;
1043 },
1044 };
1045 r.pos = pos + n;
1046 return n;
1047 },
1048 .streaming => {
1049 const n = bw.writeFile(file, .none, limit, &.{}, 0) catch |err| switch (err) {
1050 error.WriteFailed => return error.WriteFailed,
1051 error.Unseekable => unreachable, // Passing `Offset.none`.
1052 error.Unimplemented => {
1053 r.mode = .streaming_reading;
1054 return 0;
1055 },
1056 else => |e| {
1057 r.err = e;
1058 return error.ReadFailed;
1059 },
1060 };
1061 r.pos = pos + n;
1062 return n;
1063 },
1064 .positional_reading => {
1065 const dest = limit.slice(try bw.writableSliceGreedy(1));
1066 const n = file.pread(dest, pos) catch |err| switch (err) {
1067 error.Unseekable => {
1068 r.mode = .streaming_reading;
1069 if (pos != 0) @panic("TODO need to seek here");
1070 return 0;
1071 },
1072 else => |e| {
1073 r.err = e;
1074 return error.ReadFailed;
1075 },
1076 };
1077 if (n == 0) return error.EndOfStream;
1078 r.pos = pos + n;
1079 bw.advance(n);
1080 return n;
1081 },
1082 .streaming_reading => {
1083 const dest = limit.slice(try bw.writableSliceGreedy(1));
1084 const n = file.read(dest) catch |err| {
1085 r.err = err;
1086 return error.ReadFailed;
1087 };
1088 if (n == 0) return error.EndOfStream;
1089 r.pos = pos + n;
1090 bw.advance(n);
1091 return n;
1092 },
1093 }
1094 }
1095
1096 fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
1097 const r: *Reader = @ptrCast(@alignCast(context));
1098 const handle = r.file.handle;
1099 const pos = r.pos;
1100
1101 switch (r.mode) {
1102 .positional, .positional_reading => {
1103 if (is_windows) {
1104 // Unfortunately, `ReadFileScatter` cannot be used since it requires
1105 // page alignment, so we are stuck using only the first slice.
1106 // Avoid empty slices to prevent false positive end detections.
1107 var i: usize = 0;
1108 while (true) : (i += 1) {
1109 if (i >= data.len) return .{};
1110 if (data[i].len > 0) break;
1111 }
1112 const n = windows.ReadFile(handle, data[i], pos) catch |err| {
1113 r.err = err;
1114 return error.ReadFailed;
1115 };
1116 if (n == 0) return error.EndOfFile;
1117 r.pos = pos + n;
1025 },
1026 .streaming => {
1027 r.mode = .streaming_reading;
1028 return 0;
1029 },
1030 .positional_reading => {
1031 const dest = limit.slice(try bw.writableSliceGreedy(1));
1032 const n = try readPositional(r, dest);
1033 bw.advance(n);
11181034 return n;
1119 }
1120
1121 var iovecs: [max_buffers_len]std.posix.iovec = undefined;
1122 var iovecs_i: usize = 0;
1123 for (data) |d| {
1124 // Since the OS checks pointer address before length, we must omit
1125 // length-zero vectors.
1126 if (d.len == 0) continue;
1127 iovecs[iovecs_i] = .{ .base = d.ptr, .len = d.len };
1128 iovecs_i += 1;
1129 if (iovecs_i >= iovecs.len) break;
1130 }
1131 const send_vecs = iovecs[0..iovecs_i];
1132 if (send_vecs.len == 0) return 0; // Prevent false positive end detection on empty `data`.
1133 const n = posix.preadv(handle, send_vecs, pos) catch |err| switch (err) {
1134 error.Unseekable => {
1135 r.mode = r.mode.toStreaming();
1136 assert(pos == 0);
1137 return 0;
1138 },
1139 else => |e| {
1140 r.err = e;
1141 return error.ReadFailed;
1142 },
1143 };
1144 if (n == 0) return error.EndOfStream;
1145 r.pos = pos + n;
1146 return n;
1147 },
1148 .streaming, .streaming_reading => {
1149 if (is_windows) {
1150 // Unfortunately, `ReadFileScatter` cannot be used since it requires
1151 // page alignment, so we are stuck using only the first slice.
1152 // Avoid empty slices to prevent false positive end detections.
1153 var i: usize = 0;
1154 while (true) : (i += 1) {
1155 if (i >= data.len) return .{};
1156 if (data[i].len > 0) break;
1157 }
1158 const n = windows.ReadFile(handle, data[i], null) catch |err| {
1159 r.err = err;
1160 return error.ReadFailed;
1161 };
1162 if (n == 0) return error.EndOfFile;
1163 r.pos = pos + 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);
11641040 return n;
1165 }
1166
1167 var iovecs: [max_buffers_len]std.posix.iovec = undefined;
1168 var iovecs_i: usize = 0;
1169 for (data) |d| {
1170 // Since the OS checks pointer address before length, we must omit
1171 // length-zero vectors.
1172 if (d.len == 0) continue;
1173 iovecs[iovecs_i] = .{ .base = d.ptr, .len = d.len };
1174 iovecs_i += 1;
1175 if (iovecs_i >= iovecs.len) break;
1176 }
1177 const send_vecs = iovecs[0..iovecs_i];
1178 if (send_vecs.len == 0) return 0; // Prevent false positive end detection on empty `data`.
1179 const n = posix.readv(handle, send_vecs) catch |err| {
1180 r.err = err;
1181 return error.ReadFailed;
1182 };
1183 if (n == 0) return error.EndOfStream;
1184 r.pos = pos + n;
1185 return n;
1041 },
1042 .failure => return error.ReadFailed,
11861043 },
1187 }
1044 };
11881045 }
11891046
1190 fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
1047 fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
11911048 const r: *Reader = @ptrCast(@alignCast(context));
11921049 const file = r.file;
11931050 const pos = r.pos;
......@@ -1258,6 +1115,44 @@ pub const Reader = struct {
12581115 },
12591116 }
12601117 }
1118
1119 pub fn readPositional(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
1120 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
1121 error.Unseekable => {
1122 r.mode = r.mode.toStreaming();
1123 if (r.pos != 0) r.seekBy(r.pos) catch {
1124 r.mode = .failure;
1125 return error.ReadFailed;
1126 };
1127 return 0;
1128 },
1129 else => |e| {
1130 r.err = e;
1131 return error.ReadFailed;
1132 },
1133 };
1134 if (n == 0) return error.EndOfStream;
1135 r.pos += n;
1136 return n;
1137 }
1138
1139 pub fn readStreaming(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
1140 const n = r.file.read(dest) catch |err| {
1141 r.err = err;
1142 return error.ReadFailed;
1143 };
1144 if (n == 0) return error.EndOfStream;
1145 r.pos += n;
1146 return n;
1147 }
1148
1149 pub fn read(r: *Reader, dest: []u8) std.io.Reader.Error!usize {
1150 switch (r.mode) {
1151 .positional, .positional_reading => return readPositional(r, dest),
1152 .streaming, .streaming_reading => return readStreaming(r, dest),
1153 .failure => return error.ReadFailed,
1154 }
1155 }
12611156};
12621157
12631158pub const Writer = struct {
......@@ -1266,7 +1161,6 @@ pub const Writer = struct {
12661161 mode: Writer.Mode = .positional,
12671162 pos: u64 = 0,
12681163 sendfile_err: ?SendfileError = null,
1269 read_err: ?ReadError = null,
12701164 seek_err: ?SeekError = null,
12711165
12721166 pub const Mode = Reader.Mode;
......@@ -1359,15 +1253,14 @@ pub const Writer = struct {
13591253
13601254 pub fn writeFile(
13611255 context: ?*anyopaque,
1362 in_file: std.fs.File,
1363 in_offset: std.io.Writer.Offset,
1364 in_limit: std.io.Limit,
1256 file_reader: *Reader,
1257 limit: std.io.Limit,
13651258 headers_and_trailers: []const []const u8,
13661259 headers_len: usize,
13671260 ) std.io.Writer.FileError!usize {
13681261 const w: *Writer = @ptrCast(@alignCast(context));
13691262 const out_fd = w.file.handle;
1370 const in_fd = in_file.handle;
1263 const in_fd = file_reader.file.handle;
13711264 // TODO try using copy_file_range on Linux
13721265 // TODO try using copy_file_range on FreeBSD
13731266 // TODO try using sendfile on macOS
......@@ -1379,23 +1272,41 @@ pub const Writer = struct {
13791272 // support a streaming read from in_file.
13801273 if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1);
13811274 const max_count = 0x7ffff000; // Avoid EINVAL.
1382 const smaller_len = in_limit.minInt(max_count);
13831275 var off: std.os.linux.off_t = undefined;
1384 const off_ptr: ?*std.os.linux.off_t = if (in_offset.toInt()) |offset| b: {
1385 off = std.math.cast(std.os.linux.off_t, offset) orelse
1386 return writeSplat(context, headers_and_trailers, 1);
1387 break :b &off;
1388 } else null;
1389 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, smaller_len) catch |err| switch (err) {
1390 // Errors that imply sendfile should be avoided on the next write.
1391 error.UnsupportedOperation,
1392 error.Unexpected,
1393 => |e| {
1276 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
1277 .positional => o: {
1278 const size = file_reader.size orelse {
1279 if (file_reader.file.getEndPos()) |size| {
1280 file_reader.size = size;
1281 } else |err| {
1282 file_reader.size_err = err;
1283 file_reader.mode = .streaming;
1284 }
1285 return 0;
1286 };
1287 off = std.math.cast(std.os.linux.off_t, file_reader.pos) orelse
1288 return writeSplat(context, headers_and_trailers, 1);
1289 break :o .{ &off, @min(@intFromEnum(limit), size - file_reader.pos, max_count) };
1290 },
1291 .streaming => .{ null, limit.minInt(max_count) },
1292 .streaming_reading, .positional_reading => break :sf,
1293 .failure => return error.ReadFailed,
1294 };
1295 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, count) catch |err| switch (err) {
1296 error.Unseekable => {
1297 file_reader.mode = file_reader.mode.toStreaming();
1298 if (file_reader.pos != 0) file_reader.seekBy(@intCast(file_reader.pos)) catch {
1299 file_reader.mode = .failure;
1300 return error.ReadFailed;
1301 };
1302 return 0;
1303 },
1304 else => |e| {
13941305 w.sendfile_err = e;
1395 break :sf;
1306 return 0;
13961307 },
1397 else => |e| return e,
13981308 };
1309 file_reader.pos += n;
13991310 w.pos += n;
14001311 return n;
14011312 }
lib/std/http/Client.zig+1-1
......@@ -1631,7 +1631,7 @@ pub const FetchOptions = struct {
16311631 list: *std.ArrayListUnmanaged(u8),
16321632 /// If null then only the existing capacity will be used.
16331633 allocator: ?Allocator = null,
1634 append_limit: std.io.Reader.Limit = .unlimited,
1634 append_limit: std.io.Limit = .unlimited,
16351635 };
16361636};
16371637
lib/std/io/AllocatingWriter.zig+16-28
......@@ -160,13 +160,12 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.
160160
161161fn writeFile(
162162 context: ?*anyopaque,
163 file: std.fs.File,
164 offset: std.io.Writer.Offset,
163 file_reader: *std.fs.File.Reader,
165164 limit: std.io.Writer.Limit,
166165 headers_and_trailers_full: []const []const u8,
167166 headers_len_full: usize,
168167) std.io.Writer.FileError!usize {
169 if (std.fs.File.Handle == void) unreachable;
168 if (std.fs.File.Handle == void) return error.Unimplemented;
170169 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
171170 const gpa = aw.allocator;
172171 var list = aw.toArrayList();
......@@ -178,35 +177,24 @@ fn writeFile(
178177 break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };
179178 } else .{ headers_and_trailers_full, headers_len_full };
180179 const trailers = headers_and_trailers[headers_len..];
181 const pos = offset.toInt() orelse @panic("TODO treat file as stream");
182 const limit_int = limit.toInt() orelse {
183 var new_capacity: usize = list.capacity + std.atomic.cache_line;
184 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
185 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
186 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
187 const dest = list.items.ptr[list.items.len..list.capacity];
188 const n = try file.pread(dest, pos);
189 if (n == 0) {
190 new_capacity = list.capacity;
191 for (trailers) |bytes| new_capacity += bytes.len;
192 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
193 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
194 return list.items.len - start_len;
195 }
196 list.items.len += n;
197 return list.items.len - start_len;
198 };
199 var new_capacity: usize = list.capacity + limit_int;
180 const pos = file_reader.pos;
181
182 const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;
183 var new_capacity: usize = list.capacity + limit.minInt(additional);
200184 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
201185 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
202186 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
203 const dest = list.items.ptr[list.items.len..][0..limit_int];
204 const n = try file.pread(dest, pos);
205 list.items.len += n;
206 if (n < dest.len) {
207 return list.items.len - start_len;
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;
190 if (is_end) {
191 new_capacity = list.capacity;
192 for (trailers) |bytes| new_capacity += bytes.len;
193 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
194 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
195 } else {
196 list.items.len += n;
208197 }
209 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
210198 return list.items.len - start_len;
211199}
212200
lib/std/io/Reader.zig+1-1
......@@ -128,7 +128,7 @@ pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLo
128128/// See also:
129129/// * `readRemainingArrayList`
130130/// * `BufferedReader.readRemainingArrayList`
131pub fn readRemainingAlloc(r: Reader, gpa: Allocator, limit: Reader.Limit) LimitedAllocError![]u8 {
131pub fn readRemainingAlloc(r: Reader, gpa: Allocator, limit: Limit) LimitedAllocError![]u8 {
132132 var buffer: ArrayList(u8) = .empty;
133133 defer buffer.deinit(gpa);
134134 try readRemainingArrayList(r, gpa, null, &buffer, limit, 1);
lib/std/io/Reader/Limited.zig+4-3
......@@ -3,9 +3,10 @@ const Limited = @This();
33const std = @import("../../std.zig");
44const Reader = std.io.Reader;
55const BufferedWriter = std.io.BufferedWriter;
6const Limit = std.io.Limit;
67
78unlimited_reader: Reader,
8remaining: Reader.Limit,
9remaining: Limit,
910
1011pub fn reader(l: *Limited) Reader {
1112 return .{
......@@ -18,7 +19,7 @@ pub fn reader(l: *Limited) Reader {
1819 };
1920}
2021
21fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {
22fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) Reader.RwError!usize {
2223 const l: *Limited = @alignCast(@ptrCast(context));
2324 const combined_limit = limit.min(l.remaining);
2425 const n = try l.unlimited_reader.read(bw, combined_limit);
......@@ -26,7 +27,7 @@ fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit)
2627 return n;
2728}
2829
29fn passthruDiscard(context: ?*anyopaque, limit: Reader.Limit) Reader.Error!usize {
30fn passthruDiscard(context: ?*anyopaque, limit: Limit) Reader.Error!usize {
3031 const l: *Limited = @alignCast(@ptrCast(context));
3132 const combined_limit = limit.min(l.remaining);
3233 const n = try l.unlimited_reader.discard(combined_limit);
lib/std/net.zig+2-2
......@@ -1916,7 +1916,7 @@ pub const Stream = struct {
19161916 fn read(
19171917 context: ?*anyopaque,
19181918 bw: *std.io.BufferedWriter,
1919 limit: std.io.Reader.Limit,
1919 limit: std.io.Limit,
19201920 ) std.io.Reader.Error!usize {
19211921 const buf = limit.slice(try bw.writableSliceGreedy(1));
19221922 const n = try readVec(context, &.{buf});
......@@ -1958,7 +1958,7 @@ pub const Stream = struct {
19581958 return .{ .len = n, .end = n == 0 };
19591959 }
19601960
1961 fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
1961 fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
19621962 _ = context;
19631963 _ = limit;
19641964 @panic("TODO");
lib/std/tar.zig+2-2
......@@ -358,7 +358,7 @@ pub const Iterator = struct {
358358 };
359359 }
360360
361 fn read(context: ?*anyopaque, bw: *std.io.BufferedWriter, limit: std.io.Reader.Limit) std.io.Reader.RwError!usize {
361 fn read(context: ?*anyopaque, bw: *std.io.BufferedWriter, limit: std.io.Limit) std.io.Reader.RwError!usize {
362362 const file: *File = @ptrCast(@alignCast(context));
363363 if (file.unread_bytes.* == 0) return error.EndOfStream;
364364 const n = try file.parent_reader.read(bw, limit.min(.limited(file.unread_bytes.*)));
......@@ -374,7 +374,7 @@ pub const Iterator = struct {
374374 return n;
375375 }
376376
377 fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
377 fn discard(context: ?*anyopaque, limit: std.io.Limit) std.io.Reader.Error!usize {
378378 const file: *File = @ptrCast(@alignCast(context));
379379 const n = limit.minInt(file.unread_bytes.*);
380380 file.unread_bytes.* -= n;
lib/std/zip.zig+2-22
......@@ -175,11 +175,7 @@ pub const Decompress = union {
175175 return .{
176176 .unbuffered_reader = .{
177177 .context = d,
178 .vtable = &.{
179 .read = readStore,
180 .readVec = readVecUnimplemented,
181 .discard = discardUnimplemented,
182 },
178 .vtable = &.{ .read = readStore },
183179 },
184180 .buffer = buffer,
185181 .end = 0,
......@@ -191,11 +187,7 @@ pub const Decompress = union {
191187 return .{
192188 .unbuffered_reader = .{
193189 .context = d,
194 .vtable = &.{
195 .read = readDeflate,
196 .readVec = readVecUnimplemented,
197 .discard = discardUnimplemented,
198 },
190 .vtable = &.{ .read = readDeflate },
199191 },
200192 .buffer = buffer,
201193 .end = 0,
......@@ -223,18 +215,6 @@ pub const Decompress = union {
223215 const d: *Decompress = @ptrCast(@alignCast(context));
224216 return std.compress.flate.Decompress.read(&d.inflate, writer, limit);
225217 }
226
227 fn readVecUnimplemented(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
228 _ = context;
229 _ = data;
230 @panic("TODO remove readVec primitive");
231 }
232
233 fn discardUnimplemented(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
234 _ = context;
235 _ = limit;
236 @panic("TODO allow discard to be null");
237 }
238218};
239219
240220fn isBadFilename(filename: []const u8) bool {