authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-05 19:01:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:29-07:00
log084e45fd865e73f862d85da2a634f8f2cb74580d
tree0f53a4bdc7a0ccb7db6437170c3cc6189b683a33
parentb393112674d0b948832c3223d5b300f080c71151

std.io.Reader: implement readVec

and specify that data.len == 0 means "flush"

6 files changed, 205 insertions(+), 161 deletions(-)

lib/compiler/resinator/errors.zig+13-10
...@@ -1081,11 +1081,17 @@ const CorrespondingLines = struct {...@@ -1081,11 +1081,17 @@ const CorrespondingLines = struct {
1081 last_byte: u8 = 0,1081 last_byte: u8 = 0,
1082 at_eof: bool = false,1082 at_eof: bool = false,
1083 span: SourceMappings.CorrespondingSpan,1083 span: SourceMappings.CorrespondingSpan,
1084 file: std.fs.File,1084 file_reader: *std.fs.File.Reader,
1085 buffered_reader: *std.io.Reader,
1086 code_page: SupportedCodePage,1085 code_page: SupportedCodePage,
10871086
1088 pub fn init(cwd: std.fs.Dir, err_details: ErrorDetails, line_for_comparison: []const u8, corresponding_span: SourceMappings.CorrespondingSpan, corresponding_file: []const u8) !CorrespondingLines {1087 pub fn init(
1088 cwd: std.fs.Dir,
1089 err_details: ErrorDetails,
1090 line_for_comparison: []const u8,
1091 corresponding_span: SourceMappings.CorrespondingSpan,
1092 corresponding_file: []const u8,
1093 file_read_buffer: []u8,
1094 ) !CorrespondingLines {
1089 // We don't do line comparison for this error, so don't print the note if the line1095 // We don't do line comparison for this error, so don't print the note if the line
1090 // number is different1096 // number is different
1091 if (err_details.err == .string_literal_too_long and err_details.token.line_number != corresponding_span.start_line) {1097 if (err_details.err == .string_literal_too_long and err_details.token.line_number != corresponding_span.start_line) {
...@@ -1097,15 +1103,12 @@ const CorrespondingLines = struct {...@@ -1097,15 +1103,12 @@ const CorrespondingLines = struct {
1097 return error.NotWorthPrintingLines;1103 return error.NotWorthPrintingLines;
1098 }1104 }
10991105
1100 var corresponding_lines = CorrespondingLines{1106 var file = try utils.openFileNotDir(cwd, corresponding_file, .{});
1107 var corresponding_lines: CorrespondingLines = .{
1101 .span = corresponding_span,1108 .span = corresponding_span,
1102 .file = try utils.openFileNotDir(cwd, corresponding_file, .{}),1109 .file_reader = file.reader(&file_read_buffer),
1103 .buffered_reader = undefined,
1104 .code_page = err_details.code_page,1110 .code_page = err_details.code_page,
1105 };1111 };
1106 corresponding_lines.buffered_reader = .{
1107 .unbuffered_reader = corresponding_lines.file.reader(),
1108 };
1109 errdefer corresponding_lines.deinit();1112 errdefer corresponding_lines.deinit();
11101113
1111 var fbs = std.io.fixedBufferStream(&corresponding_lines.line_buf);1114 var fbs = std.io.fixedBufferStream(&corresponding_lines.line_buf);
...@@ -1215,7 +1218,7 @@ const CorrespondingLines = struct {...@@ -1215,7 +1218,7 @@ const CorrespondingLines = struct {
1215 }1218 }
12161219
1217 pub fn deinit(self: *CorrespondingLines) void {1220 pub fn deinit(self: *CorrespondingLines) void {
1218 self.file.close();1221 self.file_reader.file.close();
1219 }1222 }
1220};1223};
12211224
lib/std/fs/File.zig+54-14
...@@ -1027,14 +1027,10 @@ pub const Reader = struct {...@@ -1027,14 +1027,10 @@ pub const Reader = struct {
1027 /// vectors through the underlying read calls as possible.1027 /// vectors through the underlying read calls as possible.
1028 const max_buffers_len = 16;1028 const max_buffers_len = 16;
10291029
1030 fn stream(1030 fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {
1031 io_reader: *std.io.Reader,
1032 bw: *std.io.Writer,
1033 limit: std.io.Limit,
1034 ) std.io.Reader.StreamError!usize {
1035 const r: *Reader = @fieldParentPtr("interface", io_reader);1031 const r: *Reader = @fieldParentPtr("interface", io_reader);
1036 switch (r.mode) {1032 switch (r.mode) {
1037 .positional, .streaming => return bw.writeFile(r, limit, &.{}, 0) catch |write_err| switch (write_err) {1033 .positional, .streaming => return w.writeFile(r, limit, &.{}, 0) catch |write_err| switch (write_err) {
1038 error.ReadFailed => return error.ReadFailed,1034 error.ReadFailed => return error.ReadFailed,
1039 error.WriteFailed => return error.WriteFailed,1035 error.WriteFailed => return error.WriteFailed,
1040 error.Unimplemented => {1036 error.Unimplemented => {
...@@ -1043,16 +1039,60 @@ pub const Reader = struct {...@@ -1043,16 +1039,60 @@ pub const Reader = struct {
1043 },1039 },
1044 },1040 },
1045 .positional_reading => {1041 .positional_reading => {
1046 const dest = limit.slice(try bw.writableSliceGreedy(1));1042 if (is_windows) {
1047 const n = try readPositional(r, dest);1043 // Unfortunately, `ReadFileScatter` cannot be used since it
1048 bw.advance(n);1044 // requires page alignment.
1049 return n;1045 const dest = limit.slice(try w.writableSliceGreedy(1));
1046 const n = try readPositional(r, dest);
1047 w.advance(n);
1048 return n;
1049 }
1050 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1051 const dest = w.writableVectorPosix(&iovecs_buffer, limit);
1052 assert(dest[0].len > 0);
1053 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
1054 error.Unseekable => {
1055 r.mode = r.mode.toStreaming();
1056 if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch {
1057 r.mode = .failure;
1058 return error.ReadFailed;
1059 };
1060 return 0;
1061 },
1062 else => |e| {
1063 r.err = e;
1064 return error.ReadFailed;
1065 },
1066 };
1067 if (n == 0) {
1068 r.size = r.pos;
1069 return error.EndOfStream;
1070 }
1071 r.pos += n;
1072 return w.advanceVector(n);
1050 },1073 },
1051 .streaming_reading => {1074 .streaming_reading => {
1052 const dest = limit.slice(try bw.writableSliceGreedy(1));1075 if (is_windows) {
1053 const n = try readStreaming(r, dest);1076 // Unfortunately, `ReadFileScatter` cannot be used since it
1054 bw.advance(n);1077 // requires page alignment.
1055 return n;1078 const dest = limit.slice(try w.writableSliceGreedy(1));
1079 const n = try readStreaming(r, dest);
1080 w.advance(n);
1081 return n;
1082 }
1083 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1084 const dest = w.writableVectorPosix(&iovecs_buffer, limit);
1085 assert(dest[0].len > 0);
1086 const n = posix.pread(r.file.handle, dest) catch |err| {
1087 r.err = err;
1088 return error.ReadFailed;
1089 };
1090 if (n == 0) {
1091 r.size = r.pos;
1092 return error.EndOfStream;
1093 }
1094 r.pos += n;
1095 return w.advanceVector(n);
1056 },1096 },
1057 .failure => return error.ReadFailed,1097 .failure => return error.ReadFailed,
1058 }1098 }
lib/std/io/Reader.zig+77-54
...@@ -22,12 +22,12 @@ seek: usize,...@@ -22,12 +22,12 @@ seek: usize,
22end: usize,22end: usize,
2323
24pub const VTable = struct {24pub const VTable = struct {
25 /// Writes bytes from the internally tracked logical position to `bw`.25 /// Writes bytes from the internally tracked logical position to `w`.
26 ///26 ///
27 /// Returns the number of bytes written, which will be at minimum `0` and27 /// Returns the number of bytes written, which will be at minimum `0` and
28 /// at most `limit`. The number returned, including zero, does not indicate28 /// at most `limit`. The number returned, including zero, does not indicate
29 /// end of stream. `limit` is guaranteed to be at least as large as the29 /// end of stream. `limit` is guaranteed to be at least as large as the
30 /// buffer capacity of `bw`.30 /// buffer capacity of `w`.
31 ///31 ///
32 /// The reader's internal logical seek position moves forward in accordance32 /// The reader's internal logical seek position moves forward in accordance
33 /// with the number of bytes returned from this function.33 /// with the number of bytes returned from this function.
...@@ -35,6 +35,8 @@ pub const VTable = struct {...@@ -35,6 +35,8 @@ pub const VTable = struct {
35 /// Implementations are encouraged to utilize mandatory minimum buffer35 /// Implementations are encouraged to utilize mandatory minimum buffer
36 /// sizes combined with short reads (returning a value less than `limit`)36 /// sizes combined with short reads (returning a value less than `limit`)
37 /// in order to minimize complexity.37 /// in order to minimize complexity.
38 ///
39 /// This function is always called when `buffer` is empty.
38 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,40 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
3941
40 /// Consumes bytes from the internally tracked stream position without42 /// Consumes bytes from the internally tracked stream position without
...@@ -51,7 +53,7 @@ pub const VTable = struct {...@@ -51,7 +53,7 @@ pub const VTable = struct {
51 /// sizes combined with short reads (returning a value less than `limit`)53 /// sizes combined with short reads (returning a value less than `limit`)
52 /// in order to minimize complexity.54 /// in order to minimize complexity.
53 ///55 ///
54 /// The default implementation is is based on calling `read`, borrowing56 /// The default implementation is is based on calling `stream`, borrowing
55 /// `buffer` to construct a temporary `Writer` and ignoring the written57 /// `buffer` to construct a temporary `Writer` and ignoring the written
56 /// data.58 /// data.
57 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,59 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
...@@ -88,7 +90,7 @@ pub const ShortError = error{...@@ -88,7 +90,7 @@ pub const ShortError = error{
88pub const failing: Reader = .{90pub const failing: Reader = .{
89 .context = undefined,91 .context = undefined,
90 .vtable = &.{92 .vtable = &.{
91 .read = failingRead,93 .read = failingStream,
92 .discard = failingDiscard,94 .discard = failingDiscard,
93 },95 },
94 .buffer = &.{},96 .buffer = &.{},
...@@ -107,7 +109,7 @@ pub fn fixed(buffer: []const u8) Reader {...@@ -107,7 +109,7 @@ pub fn fixed(buffer: []const u8) Reader {
107 return .{109 return .{
108 .context = undefined,110 .context = undefined,
109 .vtable = &.{111 .vtable = &.{
110 .read = endingRead,112 .stream = endingStream,
111 .discard = endingDiscard,113 .discard = endingDiscard,
112 },114 },
113 // This cast is safe because all potential writes to it will instead115 // This cast is safe because all potential writes to it will instead
...@@ -274,9 +276,17 @@ pub fn appendRemaining(...@@ -274,9 +276,17 @@ pub fn appendRemaining(
274/// The reader's internal logical seek position moves forward in accordance276/// The reader's internal logical seek position moves forward in accordance
275/// with the number of bytes returned from this function.277/// with the number of bytes returned from this function.
276pub fn readVec(r: *Reader, data: []const []u8) Error!usize {278pub fn readVec(r: *Reader, data: []const []u8) Error!usize {
277 return readVecLimit(r, data, .unlimited);279 return readVec(r, data, .unlimited);
278}280}
279281
282const VectorWrapped = struct {
283 writer: Writer,
284 first: []u8,
285 middle: []const []u8,
286 last: []u8,
287 var unique_address: u8 = undefined;
288};
289
280/// Equivalent to `readVec` but reads at most `limit` bytes.290/// Equivalent to `readVec` but reads at most `limit` bytes.
281///291///
282/// This ultimately will lower to a call to `stream`, but it must ensure292/// This ultimately will lower to a call to `stream`, but it must ensure
...@@ -296,42 +306,55 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {...@@ -296,42 +306,55 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
296 if (remaining == 0) break;306 if (remaining == 0) break;
297 if (buf.len - copy_len == 0) continue;307 if (buf.len - copy_len == 0) continue;
298308
309 // All of `buffer` has been copied to `data`. We now set up a structure
310 // that enables the `Writer.writableVector` API, while also ensuring
311 // API that directly operates on the `Writable.buffer` has its minimum
312 // buffer capacity requirements met.
299 r.seek = 0;313 r.seek = 0;
300 r.end = 0;314 r.end = 0;
301 var vecs: [8][]u8 = undefined; // Arbitrarily chosen value.315 const first = buf[copy_len..];
302 const available_remaining_buf = buf[copy_len..];316 var wrapped: VectorWrapped = .{
303 vecs[0] = available_remaining_buf[0..@min(available_remaining_buf.len, remaining)];317 .first = first,
304 const vec_start_remaining = remaining;318 .middle = data[i + 1 ..],
305 remaining -= vecs[0].len;319 .last = r.buffer,
306 var vecs_i: usize = 1;320 .writer = .{
307 var data_i: usize = i + 1;321 .context = &VectorWrapped.unique_address,
308 while (true) {322 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
309 if (vecs.len - vecs_i == 0) {323 .vtable = &.{ .drain = Writer.fixedDrain },
310 const n = try r.unbuffered_reader.readVec(&vecs);324 },
311 return @intFromEnum(limit) - vec_start_remaining + n;325 };
312 }326 var n = r.vtable.stream(r, &wrapped.writer, .limited(remaining)) catch |err| switch (err) {
313 if (remaining == 0 or data.len - data_i == 0) {327 error.WriteFailed => {
314 vecs[vecs_i] = r.buffer;328 if (wrapped.writer.buffer.ptr == first.ptr) {
315 vecs_i += 1;329 remaining -= wrapped.writer.end;
316 const n = try r.unbuffered_reader.readVec(vecs[0..vecs_i]);
317 const cutoff = vec_start_remaining - remaining;
318 if (n > cutoff) {
319 r.end = n - cutoff;
320 return @intFromEnum(limit) - remaining;
321 } else {330 } else {
322 return @intFromEnum(limit) - vec_start_remaining + n;331 r.end = wrapped.writer.end;
323 }332 }
333 break;
334 },
335 else => |e| return e,
336 };
337 assert(n == wrapped.writer.end);
338 if (wrapped.writer.buffer.ptr != first.ptr) {
339 r.end = n;
340 break;
341 }
342 if (n < first.len) {
343 remaining -= n;
344 break;
345 }
346 remaining -= first.len;
347 n -= first.len;
348 for (wrapped.middle) |middle| {
349 if (n < middle.len) {
350 remaining -= n;
351 break;
324 }352 }
325 if (data[data_i].len == 0) {353 remaining -= middle.len;
326 data_i += 1;354 n -= middle.len;
327 continue;
328 }
329 const data_elem = data[data_i];
330 vecs[vecs_i] = data_elem[0..@min(data_elem.len, remaining)];
331 remaining -= vecs[vecs_i].len;
332 vecs_i += 1;
333 data_i += 1;
334 }355 }
356 r.end = n;
357 break;
335 }358 }
336 return @intFromEnum(limit) - remaining;359 return @intFromEnum(limit) - remaining;
337}360}
...@@ -366,10 +389,10 @@ pub fn readVecAll(r: *Reader, data: [][]u8) Error!void {...@@ -366,10 +389,10 @@ pub fn readVecAll(r: *Reader, data: [][]u8) Error!void {
366}389}
367390
368/// "Pump" data from the reader to the writer.391/// "Pump" data from the reader to the writer.
369pub fn readAll(r: *Reader, bw: *Writer, limit: Limit) StreamError!void {392pub fn readAll(r: *Reader, w: *Writer, limit: Limit) StreamError!void {
370 var remaining = limit;393 var remaining = limit;
371 while (remaining.nonzero()) {394 while (remaining.nonzero()) {
372 const n = try r.read(bw, remaining);395 const n = try r.read(w, remaining);
373 remaining = remaining.subtract(n).?;396 remaining = remaining.subtract(n).?;
374 }397 }
375}398}
...@@ -774,12 +797,12 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {...@@ -774,12 +797,12 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
774 return result[0 .. result.len - 1];797 return result[0 .. result.len - 1];
775}798}
776799
777/// Appends to `bw` contents by reading from the stream until `delimiter` is800/// Appends to `w` contents by reading from the stream until `delimiter` is
778/// found. Does not write the delimiter itself.801/// found. Does not write the delimiter itself.
779///802///
780/// Returns number of bytes streamed.803/// Returns number of bytes streamed.
781pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize {804pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize {
782 const amount, const to = try r.readAny(bw, delimiter, .unlimited);805 const amount, const to = try r.readAny(w, delimiter, .unlimited);
783 return switch (to) {806 return switch (to) {
784 .delimiter => amount,807 .delimiter => amount,
785 .limit => unreachable,808 .limit => unreachable,
...@@ -787,7 +810,7 @@ pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize {...@@ -787,7 +810,7 @@ pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize {
787 };810 };
788}811}
789812
790/// Appends to `bw` contents by reading from the stream until `delimiter` is found.813/// Appends to `w` contents by reading from the stream until `delimiter` is found.
791/// Does not write the delimiter itself.814/// Does not write the delimiter itself.
792///815///
793/// Succeeds if stream ends before delimiter found.816/// Succeeds if stream ends before delimiter found.
...@@ -795,10 +818,10 @@ pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize {...@@ -795,10 +818,10 @@ pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize {
795/// Returns number of bytes streamed. The end is not signaled to the writer.818/// Returns number of bytes streamed. The end is not signaled to the writer.
796pub fn readDelimiterEnding(819pub fn readDelimiterEnding(
797 r: *Reader,820 r: *Reader,
798 bw: *Writer,821 w: *Writer,
799 delimiter: u8,822 delimiter: u8,
800) StreamRemainingError!usize {823) StreamRemainingError!usize {
801 const amount, const to = try r.readAny(bw, delimiter, .unlimited);824 const amount, const to = try r.readAny(w, delimiter, .unlimited);
802 return switch (to) {825 return switch (to) {
803 .delimiter, .end => amount,826 .delimiter, .end => amount,
804 .limit => unreachable,827 .limit => unreachable,
...@@ -812,17 +835,17 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{...@@ -812,17 +835,17 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{
812 StreamTooLong,835 StreamTooLong,
813};836};
814837
815/// Appends to `bw` contents by reading from the stream until `delimiter` is found.838/// Appends to `w` contents by reading from the stream until `delimiter` is found.
816/// Does not write the delimiter itself.839/// Does not write the delimiter itself.
817///840///
818/// Returns number of bytes streamed.841/// Returns number of bytes streamed.
819pub fn readDelimiterLimit(842pub fn readDelimiterLimit(
820 r: *Reader,843 r: *Reader,
821 bw: *Writer,844 w: *Writer,
822 delimiter: u8,845 delimiter: u8,
823 limit: Limit,846 limit: Limit,
824) StreamDelimiterLimitedError!usize {847) StreamDelimiterLimitedError!usize {
825 const amount, const to = try r.readAny(bw, delimiter, limit);848 const amount, const to = try r.readAny(w, delimiter, limit);
826 return switch (to) {849 return switch (to) {
827 .delimiter => amount,850 .delimiter => amount,
828 .limit => error.StreamTooLong,851 .limit => error.StreamTooLong,
...@@ -832,7 +855,7 @@ pub fn readDelimiterLimit(...@@ -832,7 +855,7 @@ pub fn readDelimiterLimit(
832855
833fn readAny(856fn readAny(
834 r: *Reader,857 r: *Reader,
835 bw: *Writer,858 w: *Writer,
836 delimiter: ?u8,859 delimiter: ?u8,
837 limit: Limit,860 limit: Limit,
838) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } {861) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } {
...@@ -844,11 +867,11 @@ fn readAny(...@@ -844,11 +867,11 @@ fn readAny(
844 error.EndOfStream => return .{ amount, .end },867 error.EndOfStream => return .{ amount, .end },
845 });868 });
846 if (delimiter) |d| if (std.mem.indexOfScalar(u8, available, d)) |delimiter_index| {869 if (delimiter) |d| if (std.mem.indexOfScalar(u8, available, d)) |delimiter_index| {
847 try bw.writeAll(available[0..delimiter_index]);870 try w.writeAll(available[0..delimiter_index]);
848 r.toss(delimiter_index + 1);871 r.toss(delimiter_index + 1);
849 return .{ amount + delimiter_index, .delimiter };872 return .{ amount + delimiter_index, .delimiter };
850 };873 };
851 try bw.writeAll(available);874 try w.writeAll(available);
852 r.toss(available.len);875 r.toss(available.len);
853 amount += available.len;876 amount += available.len;
854 remaining = remaining.subtract(available.len).?;877 remaining = remaining.subtract(available.len).?;
...@@ -1303,7 +1326,7 @@ test "expected error.EndOfStream" {...@@ -1303,7 +1326,7 @@ test "expected error.EndOfStream" {
1303 try std.testing.expectError(error.EndOfStream, r.isBytes("foo"));1326 try std.testing.expectError(error.EndOfStream, r.isBytes("foo"));
1304}1327}
13051328
1306fn endingRead(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {1329fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1307 _ = r;1330 _ = r;
1308 _ = w;1331 _ = w;
1309 _ = limit;1332 _ = limit;
...@@ -1316,7 +1339,7 @@ fn endingDiscard(r: *Reader, limit: Limit) Error!usize {...@@ -1316,7 +1339,7 @@ fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
1316 return error.EndOfStream;1339 return error.EndOfStream;
1317}1340}
13181341
1319fn failingRead(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {1342fn failingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1320 _ = r;1343 _ = r;
1321 _ = w;1344 _ = w;
1322 _ = limit;1345 _ = limit;
...@@ -1409,8 +1432,8 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -1409,8 +1432,8 @@ pub fn Hashed(comptime Hasher: type) type {
14091432
1410 fn discard(r: *Reader, limit: Limit) Error!usize {1433 fn discard(r: *Reader, limit: Limit) Error!usize {
1411 const this: *@This() = @alignCast(@fieldParentPtr("interface", r));1434 const this: *@This() = @alignCast(@fieldParentPtr("interface", r));
1412 var bw = this.hasher.writable(&.{});1435 var w = this.hasher.writable(&.{});
1413 const n = this.in.read(&bw, limit) catch |err| switch (err) {1436 const n = this.in.read(&w, limit) catch |err| switch (err) {
1414 error.WriteFailed => unreachable,1437 error.WriteFailed => unreachable,
1415 else => |e| return e,1438 else => |e| return e,
1416 };1439 };
lib/std/io/Writer.zig+29-18
...@@ -9,7 +9,12 @@ const File = std.fs.File;...@@ -9,7 +9,12 @@ const File = std.fs.File;
9const testing = std.testing;9const testing = std.testing;
10const Allocator = std.mem.Allocator;10const Allocator = std.mem.Allocator;
1111
12context: ?*anyopaque,12/// There are two strategies for obtaining context; one can use this field, or
13/// embed the `Writer` and use `@fieldParentPtr`. This field must be either set
14/// to a valid pointer or left as `null` because the interface will sometimes
15/// check if this pointer value is a known special value, for example to make
16/// `writableVector` work.
17context: ?*anyopaque = null,
13vtable: *const VTable,18vtable: *const VTable,
14/// If this has length zero, the writer is unbuffered, and `flush` is a no-op.19/// If this has length zero, the writer is unbuffered, and `flush` is a no-op.
15buffer: []u8,20buffer: []u8,
...@@ -30,11 +35,16 @@ pub const VTable = struct {...@@ -30,11 +35,16 @@ pub const VTable = struct {
30 /// order. Elements of `data` may alias each other but may not alias35 /// order. Elements of `data` may alias each other but may not alias
31 /// `buffer`.36 /// `buffer`.
32 ///37 ///
33 /// This function modifies `Writer` state.38 /// This function modifies `Writer.end` and `Writer.buffer`.
34 ///39 ///
35 /// `data.len` must be greater than zero, and the last element of `data` is40 /// If `data.len` is zero, it indicates this is a "flush" operation; all
36 /// special. It is repeated as necessary so that it is written `splat`41 /// remaining buffered data must be logically consumed. Generally, this
37 /// number of times, which may be zero.42 /// means that `end` will be set to zero before returning, however, it is
43 /// legal for implementations to manage that data differently. There may be
44 /// subsequent calls to `drain` and `sendFile` after a flush operation.
45 ///
46 /// The last element of `data` is special. It is repeated as necessary so
47 /// that it is written `splat` number of times, which may be zero.
38 ///48 ///
39 /// Number of bytes actually written is returned, excluding bytes from49 /// Number of bytes actually written is returned, excluding bytes from
40 /// `buffer`. Bytes from `buffer` are tracked by modifying `end`.50 /// `buffer`. Bytes from `buffer` are tracked by modifying `end`.
...@@ -94,7 +104,6 @@ pub const FileError = error{...@@ -94,7 +104,6 @@ pub const FileError = error{
94/// modified externally, `count` will always equal `end`.104/// modified externally, `count` will always equal `end`.
95pub fn fixed(buffer: []u8) Writer {105pub fn fixed(buffer: []u8) Writer {
96 return .{106 return .{
97 .context = undefined,
98 .vtable = &.{ .drain = fixedDrain },107 .vtable = &.{ .drain = fixedDrain },
99 .buffer = buffer,108 .buffer = buffer,
100 };109 };
...@@ -105,7 +114,6 @@ pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) {...@@ -105,7 +114,6 @@ pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) {
105}114}
106115
107pub const failing: Writer = .{116pub const failing: Writer = .{
108 .context = undefined,
109 .vtable = &.{117 .vtable = &.{
110 .drain = failingDrain,118 .drain = failingDrain,
111 .sendFile = failingSendFile,119 .sendFile = failingSendFile,
...@@ -114,7 +122,6 @@ pub const failing: Writer = .{...@@ -114,7 +122,6 @@ pub const failing: Writer = .{
114122
115pub fn discarding(buffer: []u8) Writer {123pub fn discarding(buffer: []u8) Writer {
116 return .{124 return .{
117 .context = undefined,
118 .vtable = &.{125 .vtable = &.{
119 .drain = discardingDrain,126 .drain = discardingDrain,
120 .sendFile = discardingSendFile,127 .sendFile = discardingSendFile,
...@@ -199,12 +206,11 @@ pub fn writeSplatLimit(...@@ -199,12 +206,11 @@ pub fn writeSplatLimit(
199}206}
200207
201/// Drains all remaining buffered data.208/// Drains all remaining buffered data.
209///
210/// It is legal for `VTable.drain` implementations to refrain from modifying
211/// `end`.
202pub fn flush(w: *Writer) Error!void {212pub fn flush(w: *Writer) Error!void {
203 const drainFn = w.vtable.drain;213 assert(0 == try w.vtable.drain(w, &.{}, 0));
204 // This implementation allows for drain functions that do not modify `end`,
205 // such as `fixedDrain`.
206 var remaining = w.end;
207 while (remaining != 0) remaining -= try drainFn(w, &.{""}, 1);
208}214}
209215
210pub fn unusedCapacitySlice(w: *const Writer) []u8 {216pub fn unusedCapacitySlice(w: *const Writer) []u8 {
...@@ -321,14 +327,13 @@ pub fn writeSplatAll(w: *Writer, data: [][]const u8, splat: usize) Error!void {...@@ -321,14 +327,13 @@ pub fn writeSplatAll(w: *Writer, data: [][]const u8, splat: usize) Error!void {
321327
322pub fn write(w: *Writer, bytes: []const u8) Error!usize {328pub fn write(w: *Writer, bytes: []const u8) Error!usize {
323 if (w.end + bytes.len <= w.buffer.len) {329 if (w.end + bytes.len <= w.buffer.len) {
330 @branchHint(.likely);
324 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);331 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
325 w.end += bytes.len;332 w.end += bytes.len;
326 w.count += bytes.len;333 w.count += bytes.len;
327 return bytes.len;334 return bytes.len;
328 }335 }
329 const end = w.end;336 return w.vtable.drain(w, &.{bytes}, 1);
330 const n = try w.vtable.drain(w, &.{bytes}, 1);
331 return n -| end;
332}337}
333338
334/// Calls `write` as many times as necessary such that all of `bytes` are339/// Calls `write` as many times as necessary such that all of `bytes` are
...@@ -1675,6 +1680,7 @@ pub fn unimplementedSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit...@@ -1675,6 +1680,7 @@ pub fn unimplementedSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit
1675/// available buffer has been filled. Also, it may be called from `flush` in1680/// available buffer has been filled. Also, it may be called from `flush` in
1676/// which case it should return successfully.1681/// which case it should return successfully.
1677fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {1682fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1683 if (data.len == 0) return 0;
1678 for (data[0 .. data.len - 1]) |bytes| {1684 for (data[0 .. data.len - 1]) |bytes| {
1679 const dest = w.buffer[w.end..];1685 const dest = w.buffer[w.end..];
1680 const len = @min(bytes.len, dest.len);1686 const len = @min(bytes.len, dest.len);
...@@ -1726,7 +1732,6 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -1726,7 +1732,6 @@ pub fn Hashed(comptime Hasher: type) type {
1726 .out = out,1732 .out = out,
1727 .hasher = .{},1733 .hasher = .{},
1728 .interface = .{1734 .interface = .{
1729 .context = undefined,
1730 .vtable = &.{@This().drain},1735 .vtable = &.{@This().drain},
1731 },1736 },
1732 };1737 };
...@@ -1734,6 +1739,13 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -1734,6 +1739,13 @@ pub fn Hashed(comptime Hasher: type) type {
17341739
1735 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {1740 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1736 const this: *@This() = @alignCast(@fieldParentPtr("interface", w));1741 const this: *@This() = @alignCast(@fieldParentPtr("interface", w));
1742 if (data.len == 0) {
1743 const buf = w.buffered();
1744 try this.out.writeAll(buf);
1745 this.hasher.update(buf);
1746 w.end = 0;
1747 return buf.len;
1748 }
1737 const aux_n = try this.out.writeSplatAux(w.buffered(), data, splat);1749 const aux_n = try this.out.writeSplatAux(w.buffered(), data, splat);
1738 if (aux_n < w.end) {1750 if (aux_n < w.end) {
1739 this.hasher.update(w.buffer[0..aux_n]);1751 this.hasher.update(w.buffer[0..aux_n]);
...@@ -1839,7 +1851,6 @@ pub const Allocating = struct {...@@ -1839,7 +1851,6 @@ pub const Allocating = struct {
18391851
1840 const init_interface: Writer = .{1852 const init_interface: Writer = .{
1841 .interface = .{1853 .interface = .{
1842 .context = undefined,
1843 .vtable = &.{1854 .vtable = &.{
1844 .drain = Allocating.drain,1855 .drain = Allocating.drain,
1845 .sendFile = Allocating.sendFile,1856 .sendFile = Allocating.sendFile,
lib/std/net.zig+3-23
...@@ -1912,10 +1912,7 @@ pub const Stream = struct {...@@ -1912,10 +1912,7 @@ pub const Stream = struct {
1912 return .{1912 return .{
1913 .interface_state = .{1913 .interface_state = .{
1914 .context = undefined,1914 .context = undefined,
1915 .vtable = &.{1915 .vtable = &.{ .stream = stream },
1916 .stream = stream,
1917 .discard = discard,
1918 },
1919 .buffer = buffer,1916 .buffer = buffer,
1920 },1917 },
1921 .net_stream = net_stream,1918 .net_stream = net_stream,
...@@ -1924,19 +1921,9 @@ pub const Stream = struct {...@@ -1924,19 +1921,9 @@ pub const Stream = struct {
19241921
1925 fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize {1922 fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize {
1926 const r: *Reader = @fieldParentPtr("interface", io_r);1923 const r: *Reader = @fieldParentPtr("interface", io_r);
1927 const data = io_w.writableVector(limit);
1928 var iovecs: [max_buffers_len]windows.WSABUF = undefined;1924 var iovecs: [max_buffers_len]windows.WSABUF = undefined;
1929 var iovecs_i: usize = 0;1925 const bufs = io_w.writableVectorWsa(&iovecs, limit);
1930 for (data) |d| {1926 assert(bufs[0].len > 0);
1931 // In case Windows checks pointer address before length, we must omit
1932 // length-zero vectors.
1933 if (d.len == 0) continue;
1934 iovecs[iovecs_i] = .{ .buf = d.ptr, .len = d.len };
1935 iovecs_i += 1;
1936 if (iovecs_i >= iovecs.len) break;
1937 }
1938 const bufs = iovecs[0..iovecs_i];
1939 if (bufs.len == 0) return .{}; // Prevent false positive end detection on empty `data`.
1940 var n: u32 = undefined;1927 var n: u32 = undefined;
1941 var flags: u32 = 0;1928 var flags: u32 = 0;
1942 const rc = windows.ws2_32.WSARecvFrom(r.net_stream.handle, bufs.ptr, bufs.len, &n, &flags, null, null, null, null);1929 const rc = windows.ws2_32.WSARecvFrom(r.net_stream.handle, bufs.ptr, bufs.len, &n, &flags, null, null, null, null);
...@@ -1958,13 +1945,6 @@ pub const Stream = struct {...@@ -1958,13 +1945,6 @@ pub const Stream = struct {
1958 if (n == 0) return error.EndOfStream;1945 if (n == 0) return error.EndOfStream;
1959 return io_w.advanceVector(n);1946 return io_w.advanceVector(n);
1960 }1947 }
1961
1962 fn discard(io_r: *io.Reader, limit: io.Limit) io.Reader.Error!usize {
1963 const r: *Reader = @fieldParentPtr("interface", io_r);
1964 _ = r;
1965 _ = limit;
1966 @panic("TODO");
1967 }
1968 },1948 },
1969 else => struct {1949 else => struct {
1970 file_reader: File.Reader,1950 file_reader: File.Reader,
lib/std/zip.zig+29-42
...@@ -8,6 +8,7 @@ const std = @import("std");...@@ -8,6 +8,7 @@ const std = @import("std");
8const File = std.fs.File;8const File = std.fs.File;
9const is_le = builtin.target.cpu.arch.endian() == .little;9const is_le = builtin.target.cpu.arch.endian() == .little;
10const Writer = std.io.Writer;10const Writer = std.io.Writer;
11const Reader = std.io.Reader;
1112
12pub const CompressionMethod = enum(u16) {13pub const CompressionMethod = enum(u16) {
13 store = 0,14 store = 0,
...@@ -160,61 +161,47 @@ pub const EndRecord = extern struct {...@@ -160,61 +161,47 @@ pub const EndRecord = extern struct {
160 }161 }
161};162};
162163
163pub const Decompress = union {164pub const Decompress = struct {
164 inflate: std.compress.flate.Decompress,165 interface: Reader,
165 store: *std.io.Reader,166 state: union {
166167 inflate: std.compress.flate.Decompress,
167 fn readable(168 store: *Reader,
168 d: *Decompress,169 },
169 reader: *std.io.Reader,170
170 method: CompressionMethod,171 pub fn init(reader: *Reader, method: CompressionMethod, buffer: []u8) Reader {
171 buffer: []u8,172 return switch (method) {
172 ) std.io.Reader {173 .store => .{
173 switch (method) {174 .state = .{ .store = reader },
174 .store => {175 .interface = .{
175 d.* = .{ .store = reader };176 .context = undefined,
176 return .{177 .vtable = &.{ .stream = streamStore },
177 .unbuffered_reader = .{
178 .context = d,
179 .vtable = &.{ .read = readStore },
180 },
181 .buffer = buffer,178 .buffer = buffer,
182 .end = 0,179 .end = 0,
183 .seek = 0,180 .seek = 0,
184 };181 },
185 },182 },
186 .deflate => {183 .deflate => .{
187 d.* = .{ .inflate = .init(reader, .raw) };184 .state = .{ .inflate = .init(reader, .raw) },
188 return .{185 .interface = .{
189 .unbuffered_reader = .{186 .context = undefined,
190 .context = d,187 .vtable = &.{ .stream = streamDeflate },
191 .vtable = &.{ .read = readDeflate },
192 },
193 .buffer = buffer,188 .buffer = buffer,
194 .end = 0,189 .end = 0,
195 .seek = 0,190 .seek = 0,
196 };191 },
197 },192 },
198 else => unreachable,193 else => unreachable,
199 }194 };
200 }195 }
201196
202 fn readStore(197 fn streamStore(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
203 context: ?*anyopaque,198 const d: *Decompress = @fieldParentPtr("interface", r);
204 writer: *Writer,199 return d.store.read(w, limit);
205 limit: std.io.Limit,
206 ) std.io.Reader.StreamError!usize {
207 const d: *Decompress = @ptrCast(@alignCast(context));
208 return d.store.read(writer, limit);
209 }200 }
210201
211 fn readDeflate(202 fn streamDeflate(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
212 context: ?*anyopaque,203 const d: *Decompress = @fieldParentPtr("interface", r);
213 writer: *Writer,204 return std.compress.flate.Decompress.read(&d.inflate, w, limit);
214 limit: std.io.Limit,
215 ) std.io.Reader.StreamError!usize {
216 const d: *Decompress = @ptrCast(@alignCast(context));
217 return std.compress.flate.Decompress.read(&d.inflate, writer, limit);
218 }205 }
219};206};
220207