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 {...@@ -663,7 +663,7 @@ pub const Manifest = struct {
663 const gpa = self.cache.gpa;663 const gpa = self.cache.gpa;
664 const input_file_count = self.files.entries.len;664 const input_file_count = self.files.entries.len;
665 var manifest_reader = self.manifest_file.?.reader(); // Reads positionally from zero.665 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);
667 const file_contents = manifest_reader.interface().readRemainingAlloc(gpa, limit) catch |err| switch (err) {667 const file_contents = manifest_reader.interface().readRemainingAlloc(gpa, limit) catch |err| switch (err) {
668 error.OutOfMemory => return error.OutOfMemory,668 error.OutOfMemory => return error.OutOfMemory,
669 error.StreamTooLong => return error.OutOfMemory,669 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 {...@@ -768,7 +768,7 @@ fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
768fn read(768fn read(
769 context: ?*anyopaque,769 context: ?*anyopaque,
770 bw: *std.io.BufferedWriter,770 bw: *std.io.BufferedWriter,
771 limit: std.io.Reader.Limit,771 limit: std.io.Limit,
772) std.io.Reader.RwError!usize {772) std.io.Reader.RwError!usize {
773 const c: *Compress = @ptrCast(@alignCast(context));773 const c: *Compress = @ptrCast(@alignCast(context));
774 switch (c.state) {774 switch (c.state) {
lib/std/compress/flate/Decompress.zig+3-3
...@@ -142,7 +142,7 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {...@@ -142,7 +142,7 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
142pub fn read(142pub fn read(
143 context: ?*anyopaque,143 context: ?*anyopaque,
144 bw: *std.io.BufferedWriter,144 bw: *std.io.BufferedWriter,
145 limit: std.io.Reader.Limit,145 limit: std.io.Limit,
146) std.io.Reader.RwError!usize {146) std.io.Reader.RwError!usize {
147 const d: *Decompress = @alignCast(@ptrCast(context));147 const d: *Decompress = @alignCast(@ptrCast(context));
148 return readInner(d, bw, limit) catch |err| switch (err) {148 return readInner(d, bw, limit) catch |err| switch (err) {
...@@ -160,7 +160,7 @@ pub fn read(...@@ -160,7 +160,7 @@ pub fn read(
160fn readInner(160fn readInner(
161 d: *Decompress,161 d: *Decompress,
162 bw: *std.io.BufferedWriter,162 bw: *std.io.BufferedWriter,
163 limit: std.io.Reader.Limit,163 limit: std.io.Limit,
164) (Error || error{ WriteFailed, EndOfStream })!usize {164) (Error || error{ WriteFailed, EndOfStream })!usize {
165 const in = d.input;165 const in = d.input;
166 sw: switch (d.state) {166 sw: switch (d.state) {
...@@ -351,7 +351,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {...@@ -351,7 +351,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
351 @panic("TODO remove readVec primitive");351 @panic("TODO remove readVec primitive");
352}352}
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 {
355 _ = context;355 _ = context;
356 _ = limit;356 _ = limit;
357 // Problem here is we still need access to the output ring buffer.357 // 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();...@@ -2,6 +2,7 @@ const Decompress = @This();
2const std = @import("std");2const std = @import("std");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const Reader = std.io.Reader;4const Reader = std.io.Reader;
5const Limit = std.io.Limit;
5const BufferedWriter = std.io.BufferedWriter;6const BufferedWriter = std.io.BufferedWriter;
6const BufferedReader = std.io.BufferedReader;7const BufferedReader = std.io.BufferedReader;
7const zstd = @import("../zstd.zig");8const zstd = @import("../zstd.zig");
...@@ -77,7 +78,7 @@ pub fn reader(self: *Decompress) Reader {...@@ -77,7 +78,7 @@ pub fn reader(self: *Decompress) Reader {
77 };78 };
78}79}
7980
80fn read(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {81fn read(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) Reader.RwError!usize {
81 const d: *Decompress = @ptrCast(@alignCast(context));82 const d: *Decompress = @ptrCast(@alignCast(context));
82 const in = d.input;83 const in = d.input;
8384
...@@ -139,7 +140,7 @@ fn initFrame(d: *Decompress, window_size_max: usize, magic: Frame.Magic) !void {...@@ -139,7 +140,7 @@ fn initFrame(d: *Decompress, window_size_max: usize, magic: Frame.Magic) !void {
139 }140 }
140}141}
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 {
143 const in = d.input;144 const in = d.input;
144145
145 const header_bytes = try in.takeArray(3);146 const header_bytes = try in.takeArray(3);
...@@ -166,7 +167,7 @@ fn readInFrame(d: *Decompress, bw: *BufferedWriter, limit: Reader.Limit, state:...@@ -166,7 +167,7 @@ fn readInFrame(d: *Decompress, bw: *BufferedWriter, limit: Reader.Limit, state:
166 var literals_buffer: [zstd.block_size_max]u8 = undefined;167 var literals_buffer: [zstd.block_size_max]u8 = undefined;
167 var sequence_buffer: [zstd.block_size_max]u8 = undefined;168 var sequence_buffer: [zstd.block_size_max]u8 = undefined;
168 var decode: Frame.Zstandard.Decode = .init(&literal_fse_buffer, &match_fse_buffer, &offset_fse_buffer);169 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);
170 const literals = try LiteralsSection.decode(in, &remaining, &literals_buffer);171 const literals = try LiteralsSection.decode(in, &remaining, &literals_buffer);
171 const sequences_header = try SequencesSection.Header.decode(in, &remaining);172 const sequences_header = try SequencesSection.Header.decode(in, &remaining);
172173
...@@ -446,7 +447,7 @@ pub const Frame = struct {...@@ -446,7 +447,7 @@ pub const Frame = struct {
446 pub fn prepare(447 pub fn prepare(
447 self: *Decode,448 self: *Decode,
448 in: *BufferedReader,449 in: *BufferedReader,
449 remaining: *Reader.Limit,450 remaining: *Limit,
450 literals: LiteralsSection,451 literals: LiteralsSection,
451 sequences_header: SequencesSection.Header,452 sequences_header: SequencesSection.Header,
452 ) PrepareError!void {453 ) PrepareError!void {
...@@ -536,7 +537,7 @@ pub const Frame = struct {...@@ -536,7 +537,7 @@ pub const Frame = struct {
536 fn updateFseTable(537 fn updateFseTable(
537 self: *Decode,538 self: *Decode,
538 in: *BufferedReader,539 in: *BufferedReader,
539 remaining: *Reader.Limit,540 remaining: *Limit,
540 comptime choice: DataType,541 comptime choice: DataType,
541 mode: SequencesSection.Header.Mode,542 mode: SequencesSection.Header.Mode,
542 ) !void {543 ) !void {
...@@ -857,7 +858,7 @@ pub const LiteralsSection = struct {...@@ -857,7 +858,7 @@ pub const LiteralsSection = struct {
857 compressed_size: ?u18,858 compressed_size: ?u18,
858859
859 /// Decode a literals section header.860 /// Decode a literals section header.
860 pub fn decode(in: *BufferedReader, remaining: *Reader.Limit) !Header {861 pub fn decode(in: *BufferedReader, remaining: *Limit) !Header {
861 remaining.* = remaining.subtract(1) orelse return error.EndOfStream;862 remaining.* = remaining.subtract(1) orelse return error.EndOfStream;
862 const byte0 = try in.takeByte();863 const byte0 = try in.takeByte();
863 const block_type: BlockType = @enumFromInt(byte0 & 0b11);864 const block_type: BlockType = @enumFromInt(byte0 & 0b11);
...@@ -964,7 +965,7 @@ pub const LiteralsSection = struct {...@@ -964,7 +965,7 @@ pub const LiteralsSection = struct {
964 MissingStartBit,965 MissingStartBit,
965 };966 };
966967
967 pub fn decode(in: *BufferedReader, remaining: *Reader.Limit) HuffmanTree.DecodeError!HuffmanTree {968 pub fn decode(in: *BufferedReader, remaining: *Limit) HuffmanTree.DecodeError!HuffmanTree {
968 remaining.* = remaining.subtract(1) orelse return error.EndOfStream;969 remaining.* = remaining.subtract(1) orelse return error.EndOfStream;
969 const header = try in.takeByte();970 const header = try in.takeByte();
970 if (header < 128) {971 if (header < 128) {
...@@ -976,7 +977,7 @@ pub const LiteralsSection = struct {...@@ -976,7 +977,7 @@ pub const LiteralsSection = struct {
976977
977 fn decodeDirect(978 fn decodeDirect(
978 in: *BufferedReader,979 in: *BufferedReader,
979 remaining: *Reader.Limit,980 remaining: *Limit,
980 encoded_symbol_count: usize,981 encoded_symbol_count: usize,
981 ) HuffmanTree.DecodeError!HuffmanTree {982 ) HuffmanTree.DecodeError!HuffmanTree {
982 var weights: [256]u4 = undefined;983 var weights: [256]u4 = undefined;
...@@ -993,7 +994,7 @@ pub const LiteralsSection = struct {...@@ -993,7 +994,7 @@ pub const LiteralsSection = struct {
993994
994 fn decodeFse(995 fn decodeFse(
995 in: *BufferedReader,996 in: *BufferedReader,
996 remaining: *Reader.Limit,997 remaining: *Limit,
997 compressed_size: usize,998 compressed_size: usize,
998 ) HuffmanTree.DecodeError!HuffmanTree {999 ) HuffmanTree.DecodeError!HuffmanTree {
999 var weights: [256]u4 = undefined;1000 var weights: [256]u4 = undefined;
...@@ -1161,7 +1162,7 @@ pub const LiteralsSection = struct {...@@ -1161,7 +1162,7 @@ pub const LiteralsSection = struct {
1161 MissingStartBit,1162 MissingStartBit,
1162 };1163 };
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 {
1165 const header = try Header.decode(in, remaining);1166 const header = try Header.decode(in, remaining);
1166 switch (header.block_type) {1167 switch (header.block_type) {
1167 .raw => {1168 .raw => {
...@@ -1232,7 +1233,7 @@ pub const SequencesSection = struct {...@@ -1232,7 +1233,7 @@ pub const SequencesSection = struct {
1232 ReadFailed,1233 ReadFailed,
1233 };1234 };
12341235
1235 pub fn decode(in: *BufferedReader, remaining: *Reader.Limit) DecodeError!Header {1236 pub fn decode(in: *BufferedReader, remaining: *Limit) DecodeError!Header {
1236 var sequence_count: u24 = undefined;1237 var sequence_count: u24 = undefined;
12371238
1238 remaining.* = remaining.subtract(1) orelse return error.EndOfStream;1239 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 {...@@ -1043,7 +1043,7 @@ pub fn eof(c: Client) bool {
1043 return c.received_close_notify;1043 return c.received_close_notify;
1044}1044}
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 {
1047 const c: *Client = @ptrCast(@alignCast(context));1047 const c: *Client = @ptrCast(@alignCast(context));
1048 if (c.eof()) return error.EndOfStream;1048 if (c.eof()) return error.EndOfStream;
1049 const input = c.input;1049 const input = c.input;
lib/std/fifo.zig+2-2
...@@ -247,7 +247,7 @@ pub fn LinearFifo(...@@ -247,7 +247,7 @@ pub fn LinearFifo(
247 fn readerRead(247 fn readerRead(
248 ctx: ?*anyopaque,248 ctx: ?*anyopaque,
249 bw: *std.io.BufferedWriter,249 bw: *std.io.BufferedWriter,
250 limit: std.io.Reader.Limit,250 limit: std.io.Limit,
251 ) std.io.Reader.RwError!usize {251 ) std.io.Reader.RwError!usize {
252 const fifo: *Self = @alignCast(@ptrCast(ctx));252 const fifo: *Self = @alignCast(@ptrCast(ctx));
253 _ = fifo;253 _ = fifo;
...@@ -261,7 +261,7 @@ pub fn LinearFifo(...@@ -261,7 +261,7 @@ pub fn LinearFifo(
261 _ = data;261 _ = data;
262 @panic("TODO");262 @panic("TODO");
263 }263 }
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 {
265 const fifo: *Self = @alignCast(@ptrCast(ctx));265 const fifo: *Self = @alignCast(@ptrCast(ctx));
266 _ = fifo;266 _ = fifo;
267 _ = limit;267 _ = limit;
lib/std/fs/Dir.zig+3-3
...@@ -1963,7 +1963,7 @@ pub fn readFileAlloc(...@@ -1963,7 +1963,7 @@ pub fn readFileAlloc(
1963 /// * The array list's length is increased by exactly one byte past `limit`.1963 /// * The array list's length is increased by exactly one byte past `limit`.
1964 /// * The file seek position is advanced by exactly one byte past `limit`.1964 /// * The file seek position is advanced by exactly one byte past `limit`.
1965 /// * `error.StreamTooLong` is returned.1965 /// * `error.StreamTooLong` is returned.
1966 limit: std.io.Reader.Limit,1966 limit: std.io.Limit,
1967) ReadFileAllocError![]u8 {1967) ReadFileAllocError![]u8 {
1968 return dir.readFileAllocOptions(file_path, gpa, limit, null, .of(u8), null);1968 return dir.readFileAllocOptions(file_path, gpa, limit, null, .of(u8), null);
1969}1969}
...@@ -1982,7 +1982,7 @@ pub fn readFileAllocOptions(...@@ -1982,7 +1982,7 @@ pub fn readFileAllocOptions(
1982 /// * The array list's length is increased by exactly one byte past `limit`.1982 /// * The array list's length is increased by exactly one byte past `limit`.
1983 /// * The file seek position is advanced by exactly one byte past `limit`.1983 /// * The file seek position is advanced by exactly one byte past `limit`.
1984 /// * `error.StreamTooLong` is returned.1984 /// * `error.StreamTooLong` is returned.
1985 limit: std.io.Reader.Limit,1985 limit: std.io.Limit,
1986 /// If specified, the initial buffer size is calculated using this value,1986 /// If specified, the initial buffer size is calculated using this value,
1987 /// otherwise the effective file size is used instead.1987 /// otherwise the effective file size is used instead.
1988 size_hint: ?usize,1988 size_hint: ?usize,
...@@ -2020,7 +2020,7 @@ pub fn readFileIntoArrayList(...@@ -2020,7 +2020,7 @@ pub fn readFileIntoArrayList(
2020 /// On other platforms, an opaque sequence of bytes with no particular encoding.2020 /// On other platforms, an opaque sequence of bytes with no particular encoding.
2021 file_path: []const u8,2021 file_path: []const u8,
2022 gpa: Allocator,2022 gpa: Allocator,
2023 limit: std.io.Reader.Limit,2023 limit: std.io.Limit,
2024 /// If specified, the initial buffer size is calculated using this value,2024 /// If specified, the initial buffer size is calculated using this value,
2025 /// otherwise the effective file size is used instead.2025 /// otherwise the effective file size is used instead.
2026 size_hint: ?usize,2026 size_hint: ?usize,
lib/std/fs/File.zig+109-198
...@@ -922,12 +922,13 @@ pub const Reader = struct {...@@ -922,12 +922,13 @@ pub const Reader = struct {
922 positional,922 positional,
923 streaming_reading,923 streaming_reading,
924 positional_reading,924 positional_reading,
925 failure,
925926
926 pub fn toStreaming(m: @This()) @This() {927 pub fn toStreaming(m: @This()) @This() {
927 return switch (m) {928 return switch (m) {
928 .positional => .streaming,929 .positional, .streaming => .streaming,
929 .positional_reading => .streaming_reading,930 .positional_reading, .streaming_reading => .streaming_reading,
930 else => unreachable,931 .failure => .failure,
931 };932 };
932 }933 }
933 };934 };
...@@ -936,8 +937,7 @@ pub const Reader = struct {...@@ -936,8 +937,7 @@ pub const Reader = struct {
936 return .{937 return .{
937 .context = r,938 .context = r,
938 .vtable = &.{939 .vtable = &.{
939 .read = Reader.read,940 .read = Reader.stream,
940 .readVec = Reader.readVec,
941 .discard = Reader.discard,941 .discard = Reader.discard,
942 },942 },
943 };943 };
...@@ -963,12 +963,14 @@ pub const Reader = struct {...@@ -963,12 +963,14 @@ pub const Reader = struct {
963 pub fn seekBy(r: *Reader, offset: i64) SeekError!void {963 pub fn seekBy(r: *Reader, offset: i64) SeekError!void {
964 switch (r.mode) {964 switch (r.mode) {
965 .positional, .positional_reading => {965 .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);
967 },968 },
968 .streaming, .streaming_reading => {969 .streaming, .streaming_reading => {
969 const seek_err = r.seek_err orelse e: {970 const seek_err = r.seek_err orelse e: {
970 if (posix.lseek_CUR(r.file.handle, offset)) |_| {971 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);
972 return;974 return;
973 } else |err| {975 } else |err| {
974 r.seek_err = err;976 r.seek_err = err;
...@@ -983,6 +985,7 @@ pub const Reader = struct {...@@ -983,6 +985,7 @@ pub const Reader = struct {
983 remaining -= n;985 remaining -= n;
984 }986 }
985 },987 },
988 .failure => return error.Unseekable,
986 }989 }
987 }990 }
988991
...@@ -1006,188 +1009,42 @@ pub const Reader = struct {...@@ -1006,188 +1009,42 @@ pub const Reader = struct {
1006 /// vectors through the underlying read calls as possible.1009 /// vectors through the underlying read calls as possible.
1007 const max_buffers_len = 16;1010 const max_buffers_len = 16;
10081011
1009 fn read(1012 fn stream(
1010 context: ?*anyopaque,1013 context: ?*anyopaque,
1011 bw: *BufferedWriter,1014 bw: *BufferedWriter,
1012 limit: std.io.Reader.Limit,1015 limit: std.io.Limit,
1013 ) std.io.Reader.RwError!usize {1016 ) std.io.Reader.StreamError!usize {
1014 const r: *Reader = @ptrCast(@alignCast(context));1017 const r: *Reader = @ptrCast(@alignCast(context));
1015 const file = r.file;1018 return bw.writeFile(r, limit, &.{}, 0) catch |write_file_error| switch (write_file_error) {
1016 const pos = r.pos;1019 error.ReadFailed => return error.ReadFailed,
1017 switch (r.mode) {1020 error.WriteFailed => return error.WriteFailed,
1018 .positional => {1021 error.Unimplemented => switch (r.mode) {
1019 const size = r.size orelse {1022 .positional => {
1020 if (file.getEndPos()) |size| {1023 r.mode = .positional_reading;
1021 r.size = size;
1022 } else |err| {
1023 r.size_err = err;
1024 r.mode = .streaming;
1025 }
1026 return 0;1024 return 0;
1027 };1025 },
1028 const new_limit = limit.min(.limited(size - pos));1026 .streaming => {
1029 const n = bw.writeFile(file, .init(pos), new_limit, &.{}, 0) catch |err| switch (err) {1027 r.mode = .streaming_reading;
1030 error.WriteFailed => return error.WriteFailed,1028 return 0;
1031 error.Unseekable => {1029 },
1032 r.mode = .streaming;1030 .positional_reading => {
1033 if (pos != 0) @panic("TODO need to seek here");1031 const dest = limit.slice(try bw.writableSliceGreedy(1));
1034 return 0;1032 const n = try readPositional(r, dest);
1035 },1033 bw.advance(n);
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;
1118 return n;1034 return n;
1119 }1035 },
11201036 .streaming_reading => {
1121 var iovecs: [max_buffers_len]std.posix.iovec = undefined;1037 const dest = limit.slice(try bw.writableSliceGreedy(1));
1122 var iovecs_i: usize = 0;1038 const n = try readStreaming(r, dest);
1123 for (data) |d| {1039 bw.advance(n);
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;
1164 return n;1040 return n;
1165 }1041 },
11661042 .failure => return error.ReadFailed,
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;
1186 },1043 },
1187 }1044 };
1188 }1045 }
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 {
1191 const r: *Reader = @ptrCast(@alignCast(context));1048 const r: *Reader = @ptrCast(@alignCast(context));
1192 const file = r.file;1049 const file = r.file;
1193 const pos = r.pos;1050 const pos = r.pos;
...@@ -1258,6 +1115,44 @@ pub const Reader = struct {...@@ -1258,6 +1115,44 @@ pub const Reader = struct {
1258 },1115 },
1259 }1116 }
1260 }1117 }
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 }
1261};1156};
12621157
1263pub const Writer = struct {1158pub const Writer = struct {
...@@ -1266,7 +1161,6 @@ pub const Writer = struct {...@@ -1266,7 +1161,6 @@ pub const Writer = struct {
1266 mode: Writer.Mode = .positional,1161 mode: Writer.Mode = .positional,
1267 pos: u64 = 0,1162 pos: u64 = 0,
1268 sendfile_err: ?SendfileError = null,1163 sendfile_err: ?SendfileError = null,
1269 read_err: ?ReadError = null,
1270 seek_err: ?SeekError = null,1164 seek_err: ?SeekError = null,
12711165
1272 pub const Mode = Reader.Mode;1166 pub const Mode = Reader.Mode;
...@@ -1359,15 +1253,14 @@ pub const Writer = struct {...@@ -1359,15 +1253,14 @@ pub const Writer = struct {
13591253
1360 pub fn writeFile(1254 pub fn writeFile(
1361 context: ?*anyopaque,1255 context: ?*anyopaque,
1362 in_file: std.fs.File,1256 file_reader: *Reader,
1363 in_offset: std.io.Writer.Offset,1257 limit: std.io.Limit,
1364 in_limit: std.io.Limit,
1365 headers_and_trailers: []const []const u8,1258 headers_and_trailers: []const []const u8,
1366 headers_len: usize,1259 headers_len: usize,
1367 ) std.io.Writer.FileError!usize {1260 ) std.io.Writer.FileError!usize {
1368 const w: *Writer = @ptrCast(@alignCast(context));1261 const w: *Writer = @ptrCast(@alignCast(context));
1369 const out_fd = w.file.handle;1262 const out_fd = w.file.handle;
1370 const in_fd = in_file.handle;1263 const in_fd = file_reader.file.handle;
1371 // TODO try using copy_file_range on Linux1264 // TODO try using copy_file_range on Linux
1372 // TODO try using copy_file_range on FreeBSD1265 // TODO try using copy_file_range on FreeBSD
1373 // TODO try using sendfile on macOS1266 // TODO try using sendfile on macOS
...@@ -1379,23 +1272,41 @@ pub const Writer = struct {...@@ -1379,23 +1272,41 @@ pub const Writer = struct {
1379 // support a streaming read from in_file.1272 // support a streaming read from in_file.
1380 if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1);1273 if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1);
1381 const max_count = 0x7ffff000; // Avoid EINVAL.1274 const max_count = 0x7ffff000; // Avoid EINVAL.
1382 const smaller_len = in_limit.minInt(max_count);
1383 var off: std.os.linux.off_t = undefined;1275 var off: std.os.linux.off_t = undefined;
1384 const off_ptr: ?*std.os.linux.off_t = if (in_offset.toInt()) |offset| b: {1276 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
1385 off = std.math.cast(std.os.linux.off_t, offset) orelse1277 .positional => o: {
1386 return writeSplat(context, headers_and_trailers, 1);1278 const size = file_reader.size orelse {
1387 break :b &off;1279 if (file_reader.file.getEndPos()) |size| {
1388 } else null;1280 file_reader.size = size;
1389 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, smaller_len) catch |err| switch (err) {1281 } else |err| {
1390 // Errors that imply sendfile should be avoided on the next write.1282 file_reader.size_err = err;
1391 error.UnsupportedOperation,1283 file_reader.mode = .streaming;
1392 error.Unexpected,1284 }
1393 => |e| {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| {
1394 w.sendfile_err = e;1305 w.sendfile_err = e;
1395 break :sf;1306 return 0;
1396 },1307 },
1397 else => |e| return e,
1398 };1308 };
1309 file_reader.pos += n;
1399 w.pos += n;1310 w.pos += n;
1400 return n;1311 return n;
1401 }1312 }
lib/std/http/Client.zig+1-1
...@@ -1631,7 +1631,7 @@ pub const FetchOptions = struct {...@@ -1631,7 +1631,7 @@ pub const FetchOptions = struct {
1631 list: *std.ArrayListUnmanaged(u8),1631 list: *std.ArrayListUnmanaged(u8),
1632 /// If null then only the existing capacity will be used.1632 /// If null then only the existing capacity will be used.
1633 allocator: ?Allocator = null,1633 allocator: ?Allocator = null,
1634 append_limit: std.io.Reader.Limit = .unlimited,1634 append_limit: std.io.Limit = .unlimited,
1635 };1635 };
1636};1636};
16371637
lib/std/io/AllocatingWriter.zig+16-28
...@@ -160,13 +160,12 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std....@@ -160,13 +160,12 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.
160160
161fn writeFile(161fn writeFile(
162 context: ?*anyopaque,162 context: ?*anyopaque,
163 file: std.fs.File,163 file_reader: *std.fs.File.Reader,
164 offset: std.io.Writer.Offset,
165 limit: std.io.Writer.Limit,164 limit: std.io.Writer.Limit,
166 headers_and_trailers_full: []const []const u8,165 headers_and_trailers_full: []const []const u8,
167 headers_len_full: usize,166 headers_len_full: usize,
168) std.io.Writer.FileError!usize {167) std.io.Writer.FileError!usize {
169 if (std.fs.File.Handle == void) unreachable;168 if (std.fs.File.Handle == void) return error.Unimplemented;
170 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));169 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
171 const gpa = aw.allocator;170 const gpa = aw.allocator;
172 var list = aw.toArrayList();171 var list = aw.toArrayList();
...@@ -178,35 +177,24 @@ fn writeFile(...@@ -178,35 +177,24 @@ fn writeFile(
178 break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };177 break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };
179 } else .{ headers_and_trailers_full, headers_len_full };178 } else .{ headers_and_trailers_full, headers_len_full };
180 const trailers = headers_and_trailers[headers_len..];179 const trailers = headers_and_trailers[headers_len..];
181 const pos = offset.toInt() orelse @panic("TODO treat file as stream");180 const pos = file_reader.pos;
182 const limit_int = limit.toInt() orelse {181
183 var new_capacity: usize = list.capacity + std.atomic.cache_line;182 const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;
184 for (headers_and_trailers) |bytes| new_capacity += bytes.len;183 var new_capacity: usize = list.capacity + limit.minInt(additional);
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;
200 for (headers_and_trailers) |bytes| new_capacity += bytes.len;184 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
201 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;185 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
202 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);186 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
203 const dest = list.items.ptr[list.items.len..][0..limit_int];187 const dest = limit.slice(list.items.ptr[list.items.len..list.capacity]);
204 const n = try file.pread(dest, pos);188 const n = try file_reader.read(dest);
205 list.items.len += n;189 const is_end = if (file_reader.getSize()) |size| n >= size - pos else n == 0;
206 if (n < dest.len) {190 if (is_end) {
207 return list.items.len - start_len;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;
208 }197 }
209 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
210 return list.items.len - start_len;198 return list.items.len - start_len;
211}199}
212200
lib/std/io/Reader.zig+1-1
...@@ -128,7 +128,7 @@ pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLo...@@ -128,7 +128,7 @@ pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLo
128/// See also:128/// See also:
129/// * `readRemainingArrayList`129/// * `readRemainingArrayList`
130/// * `BufferedReader.readRemainingArrayList`130/// * `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 {
132 var buffer: ArrayList(u8) = .empty;132 var buffer: ArrayList(u8) = .empty;
133 defer buffer.deinit(gpa);133 defer buffer.deinit(gpa);
134 try readRemainingArrayList(r, gpa, null, &buffer, limit, 1);134 try readRemainingArrayList(r, gpa, null, &buffer, limit, 1);
lib/std/io/Reader/Limited.zig+4-3
...@@ -3,9 +3,10 @@ const Limited = @This();...@@ -3,9 +3,10 @@ const Limited = @This();
3const std = @import("../../std.zig");3const std = @import("../../std.zig");
4const Reader = std.io.Reader;4const Reader = std.io.Reader;
5const BufferedWriter = std.io.BufferedWriter;5const BufferedWriter = std.io.BufferedWriter;
6const Limit = std.io.Limit;
67
7unlimited_reader: Reader,8unlimited_reader: Reader,
8remaining: Reader.Limit,9remaining: Limit,
910
10pub fn reader(l: *Limited) Reader {11pub fn reader(l: *Limited) Reader {
11 return .{12 return .{
...@@ -18,7 +19,7 @@ pub fn reader(l: *Limited) Reader {...@@ -18,7 +19,7 @@ pub fn reader(l: *Limited) Reader {
18 };19 };
19}20}
2021
21fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {22fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) Reader.RwError!usize {
22 const l: *Limited = @alignCast(@ptrCast(context));23 const l: *Limited = @alignCast(@ptrCast(context));
23 const combined_limit = limit.min(l.remaining);24 const combined_limit = limit.min(l.remaining);
24 const n = try l.unlimited_reader.read(bw, combined_limit);25 const n = try l.unlimited_reader.read(bw, combined_limit);
...@@ -26,7 +27,7 @@ fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit)...@@ -26,7 +27,7 @@ fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit)
26 return n;27 return n;
27}28}
2829
29fn passthruDiscard(context: ?*anyopaque, limit: Reader.Limit) Reader.Error!usize {30fn passthruDiscard(context: ?*anyopaque, limit: Limit) Reader.Error!usize {
30 const l: *Limited = @alignCast(@ptrCast(context));31 const l: *Limited = @alignCast(@ptrCast(context));
31 const combined_limit = limit.min(l.remaining);32 const combined_limit = limit.min(l.remaining);
32 const n = try l.unlimited_reader.discard(combined_limit);33 const n = try l.unlimited_reader.discard(combined_limit);
lib/std/net.zig+2-2
...@@ -1916,7 +1916,7 @@ pub const Stream = struct {...@@ -1916,7 +1916,7 @@ pub const Stream = struct {
1916 fn read(1916 fn read(
1917 context: ?*anyopaque,1917 context: ?*anyopaque,
1918 bw: *std.io.BufferedWriter,1918 bw: *std.io.BufferedWriter,
1919 limit: std.io.Reader.Limit,1919 limit: std.io.Limit,
1920 ) std.io.Reader.Error!usize {1920 ) std.io.Reader.Error!usize {
1921 const buf = limit.slice(try bw.writableSliceGreedy(1));1921 const buf = limit.slice(try bw.writableSliceGreedy(1));
1922 const n = try readVec(context, &.{buf});1922 const n = try readVec(context, &.{buf});
...@@ -1958,7 +1958,7 @@ pub const Stream = struct {...@@ -1958,7 +1958,7 @@ pub const Stream = struct {
1958 return .{ .len = n, .end = n == 0 };1958 return .{ .len = n, .end = n == 0 };
1959 }1959 }
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 {
1962 _ = context;1962 _ = context;
1963 _ = limit;1963 _ = limit;
1964 @panic("TODO");1964 @panic("TODO");
lib/std/tar.zig+2-2
...@@ -358,7 +358,7 @@ pub const Iterator = struct {...@@ -358,7 +358,7 @@ pub const Iterator = struct {
358 };358 };
359 }359 }
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 {
362 const file: *File = @ptrCast(@alignCast(context));362 const file: *File = @ptrCast(@alignCast(context));
363 if (file.unread_bytes.* == 0) return error.EndOfStream;363 if (file.unread_bytes.* == 0) return error.EndOfStream;
364 const n = try file.parent_reader.read(bw, limit.min(.limited(file.unread_bytes.*)));364 const n = try file.parent_reader.read(bw, limit.min(.limited(file.unread_bytes.*)));
...@@ -374,7 +374,7 @@ pub const Iterator = struct {...@@ -374,7 +374,7 @@ pub const Iterator = struct {
374 return n;374 return n;
375 }375 }
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 {
378 const file: *File = @ptrCast(@alignCast(context));378 const file: *File = @ptrCast(@alignCast(context));
379 const n = limit.minInt(file.unread_bytes.*);379 const n = limit.minInt(file.unread_bytes.*);
380 file.unread_bytes.* -= n;380 file.unread_bytes.* -= n;
lib/std/zip.zig+2-22
...@@ -175,11 +175,7 @@ pub const Decompress = union {...@@ -175,11 +175,7 @@ pub const Decompress = union {
175 return .{175 return .{
176 .unbuffered_reader = .{176 .unbuffered_reader = .{
177 .context = d,177 .context = d,
178 .vtable = &.{178 .vtable = &.{ .read = readStore },
179 .read = readStore,
180 .readVec = readVecUnimplemented,
181 .discard = discardUnimplemented,
182 },
183 },179 },
184 .buffer = buffer,180 .buffer = buffer,
185 .end = 0,181 .end = 0,
...@@ -191,11 +187,7 @@ pub const Decompress = union {...@@ -191,11 +187,7 @@ pub const Decompress = union {
191 return .{187 return .{
192 .unbuffered_reader = .{188 .unbuffered_reader = .{
193 .context = d,189 .context = d,
194 .vtable = &.{190 .vtable = &.{ .read = readDeflate },
195 .read = readDeflate,
196 .readVec = readVecUnimplemented,
197 .discard = discardUnimplemented,
198 },
199 },191 },
200 .buffer = buffer,192 .buffer = buffer,
201 .end = 0,193 .end = 0,
...@@ -223,18 +215,6 @@ pub const Decompress = union {...@@ -223,18 +215,6 @@ pub const Decompress = union {
223 const d: *Decompress = @ptrCast(@alignCast(context));215 const d: *Decompress = @ptrCast(@alignCast(context));
224 return std.compress.flate.Decompress.read(&d.inflate, writer, limit);216 return std.compress.flate.Decompress.read(&d.inflate, writer, limit);
225 }217 }
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 }
238};218};
239219
240fn isBadFilename(filename: []const u8) bool {220fn isBadFilename(filename: []const u8) bool {