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 {
10811081 last_byte: u8 = 0,
10821082 at_eof: bool = false,
10831083 span: SourceMappings.CorrespondingSpan,
1084 file: std.fs.File,
1085 buffered_reader: *std.io.Reader,
1084 file_reader: *std.fs.File.Reader,
10861085 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 {
10891095 // We don't do line comparison for this error, so don't print the note if the line
10901096 // number is different
10911097 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 {
10971103 return error.NotWorthPrintingLines;
10981104 }
10991105
1100 var corresponding_lines = CorrespondingLines{
1106 var file = try utils.openFileNotDir(cwd, corresponding_file, .{});
1107 var corresponding_lines: CorrespondingLines = .{
11011108 .span = corresponding_span,
1102 .file = try utils.openFileNotDir(cwd, corresponding_file, .{}),
1103 .buffered_reader = undefined,
1109 .file_reader = file.reader(&file_read_buffer),
11041110 .code_page = err_details.code_page,
11051111 };
1106 corresponding_lines.buffered_reader = .{
1107 .unbuffered_reader = corresponding_lines.file.reader(),
1108 };
11091112 errdefer corresponding_lines.deinit();
11101113
11111114 var fbs = std.io.fixedBufferStream(&corresponding_lines.line_buf);
......@@ -1215,7 +1218,7 @@ const CorrespondingLines = struct {
12151218 }
12161219
12171220 pub fn deinit(self: *CorrespondingLines) void {
1218 self.file.close();
1221 self.file_reader.file.close();
12191222 }
12201223};
12211224
lib/std/fs/File.zig+54-14
......@@ -1027,14 +1027,10 @@ pub const Reader = struct {
10271027 /// vectors through the underlying read calls as possible.
10281028 const max_buffers_len = 16;
10291029
1030 fn stream(
1031 io_reader: *std.io.Reader,
1032 bw: *std.io.Writer,
1033 limit: std.io.Limit,
1034 ) std.io.Reader.StreamError!usize {
1030 fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {
10351031 const r: *Reader = @fieldParentPtr("interface", io_reader);
10361032 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) {
10381034 error.ReadFailed => return error.ReadFailed,
10391035 error.WriteFailed => return error.WriteFailed,
10401036 error.Unimplemented => {
......@@ -1043,16 +1039,60 @@ pub const Reader = struct {
10431039 },
10441040 },
10451041 .positional_reading => {
1046 const dest = limit.slice(try bw.writableSliceGreedy(1));
1047 const n = try readPositional(r, dest);
1048 bw.advance(n);
1049 return n;
1042 if (is_windows) {
1043 // Unfortunately, `ReadFileScatter` cannot be used since it
1044 // requires page alignment.
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);
10501073 },
10511074 .streaming_reading => {
1052 const dest = limit.slice(try bw.writableSliceGreedy(1));
1053 const n = try readStreaming(r, dest);
1054 bw.advance(n);
1055 return n;
1075 if (is_windows) {
1076 // Unfortunately, `ReadFileScatter` cannot be used since it
1077 // requires page alignment.
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);
10561096 },
10571097 .failure => return error.ReadFailed,
10581098 }
lib/std/io/Reader.zig+77-54
......@@ -22,12 +22,12 @@ seek: usize,
2222end: usize,
2323
2424pub 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`.
2626 ///
2727 /// Returns the number of bytes written, which will be at minimum `0` and
2828 /// at most `limit`. The number returned, including zero, does not indicate
2929 /// end of stream. `limit` is guaranteed to be at least as large as the
30 /// buffer capacity of `bw`.
30 /// buffer capacity of `w`.
3131 ///
3232 /// The reader's internal logical seek position moves forward in accordance
3333 /// with the number of bytes returned from this function.
......@@ -35,6 +35,8 @@ pub const VTable = struct {
3535 /// Implementations are encouraged to utilize mandatory minimum buffer
3636 /// sizes combined with short reads (returning a value less than `limit`)
3737 /// in order to minimize complexity.
38 ///
39 /// This function is always called when `buffer` is empty.
3840 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
3941
4042 /// Consumes bytes from the internally tracked stream position without
......@@ -51,7 +53,7 @@ pub const VTable = struct {
5153 /// sizes combined with short reads (returning a value less than `limit`)
5254 /// in order to minimize complexity.
5355 ///
54 /// The default implementation is is based on calling `read`, borrowing
56 /// The default implementation is is based on calling `stream`, borrowing
5557 /// `buffer` to construct a temporary `Writer` and ignoring the written
5658 /// data.
5759 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
......@@ -88,7 +90,7 @@ pub const ShortError = error{
8890pub const failing: Reader = .{
8991 .context = undefined,
9092 .vtable = &.{
91 .read = failingRead,
93 .read = failingStream,
9294 .discard = failingDiscard,
9395 },
9496 .buffer = &.{},
......@@ -107,7 +109,7 @@ pub fn fixed(buffer: []const u8) Reader {
107109 return .{
108110 .context = undefined,
109111 .vtable = &.{
110 .read = endingRead,
112 .stream = endingStream,
111113 .discard = endingDiscard,
112114 },
113115 // This cast is safe because all potential writes to it will instead
......@@ -274,9 +276,17 @@ pub fn appendRemaining(
274276/// The reader's internal logical seek position moves forward in accordance
275277/// with the number of bytes returned from this function.
276278pub fn readVec(r: *Reader, data: []const []u8) Error!usize {
277 return readVecLimit(r, data, .unlimited);
279 return readVec(r, data, .unlimited);
278280}
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
280290/// Equivalent to `readVec` but reads at most `limit` bytes.
281291///
282292/// 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 {
296306 if (remaining == 0) break;
297307 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.
299313 r.seek = 0;
300314 r.end = 0;
301 var vecs: [8][]u8 = undefined; // Arbitrarily chosen value.
302 const available_remaining_buf = buf[copy_len..];
303 vecs[0] = available_remaining_buf[0..@min(available_remaining_buf.len, remaining)];
304 const vec_start_remaining = remaining;
305 remaining -= vecs[0].len;
306 var vecs_i: usize = 1;
307 var data_i: usize = i + 1;
308 while (true) {
309 if (vecs.len - vecs_i == 0) {
310 const n = try r.unbuffered_reader.readVec(&vecs);
311 return @intFromEnum(limit) - vec_start_remaining + n;
312 }
313 if (remaining == 0 or data.len - data_i == 0) {
314 vecs[vecs_i] = r.buffer;
315 vecs_i += 1;
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;
315 const first = buf[copy_len..];
316 var wrapped: VectorWrapped = .{
317 .first = first,
318 .middle = data[i + 1 ..],
319 .last = r.buffer,
320 .writer = .{
321 .context = &VectorWrapped.unique_address,
322 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
323 .vtable = &.{ .drain = Writer.fixedDrain },
324 },
325 };
326 var n = r.vtable.stream(r, &wrapped.writer, .limited(remaining)) catch |err| switch (err) {
327 error.WriteFailed => {
328 if (wrapped.writer.buffer.ptr == first.ptr) {
329 remaining -= wrapped.writer.end;
321330 } else {
322 return @intFromEnum(limit) - vec_start_remaining + n;
331 r.end = wrapped.writer.end;
323332 }
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;
324352 }
325 if (data[data_i].len == 0) {
326 data_i += 1;
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;
353 remaining -= middle.len;
354 n -= middle.len;
334355 }
356 r.end = n;
357 break;
335358 }
336359 return @intFromEnum(limit) - remaining;
337360}
......@@ -366,10 +389,10 @@ pub fn readVecAll(r: *Reader, data: [][]u8) Error!void {
366389}
367390
368391/// "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 {
370393 var remaining = limit;
371394 while (remaining.nonzero()) {
372 const n = try r.read(bw, remaining);
395 const n = try r.read(w, remaining);
373396 remaining = remaining.subtract(n).?;
374397 }
375398}
......@@ -774,12 +797,12 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
774797 return result[0 .. result.len - 1];
775798}
776799
777/// Appends to `bw` contents by reading from the stream until `delimiter` is
800/// Appends to `w` contents by reading from the stream until `delimiter` is
778801/// found. Does not write the delimiter itself.
779802///
780803/// Returns number of bytes streamed.
781pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize {
782 const amount, const to = try r.readAny(bw, delimiter, .unlimited);
804pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize {
805 const amount, const to = try r.readAny(w, delimiter, .unlimited);
783806 return switch (to) {
784807 .delimiter => amount,
785808 .limit => unreachable,
......@@ -787,7 +810,7 @@ pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize {
787810 };
788811}
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.
791814/// Does not write the delimiter itself.
792815///
793816/// Succeeds if stream ends before delimiter found.
......@@ -795,10 +818,10 @@ pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize {
795818/// Returns number of bytes streamed. The end is not signaled to the writer.
796819pub fn readDelimiterEnding(
797820 r: *Reader,
798 bw: *Writer,
821 w: *Writer,
799822 delimiter: u8,
800823) StreamRemainingError!usize {
801 const amount, const to = try r.readAny(bw, delimiter, .unlimited);
824 const amount, const to = try r.readAny(w, delimiter, .unlimited);
802825 return switch (to) {
803826 .delimiter, .end => amount,
804827 .limit => unreachable,
......@@ -812,17 +835,17 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{
812835 StreamTooLong,
813836};
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.
816839/// Does not write the delimiter itself.
817840///
818841/// Returns number of bytes streamed.
819842pub fn readDelimiterLimit(
820843 r: *Reader,
821 bw: *Writer,
844 w: *Writer,
822845 delimiter: u8,
823846 limit: Limit,
824847) StreamDelimiterLimitedError!usize {
825 const amount, const to = try r.readAny(bw, delimiter, limit);
848 const amount, const to = try r.readAny(w, delimiter, limit);
826849 return switch (to) {
827850 .delimiter => amount,
828851 .limit => error.StreamTooLong,
......@@ -832,7 +855,7 @@ pub fn readDelimiterLimit(
832855
833856fn readAny(
834857 r: *Reader,
835 bw: *Writer,
858 w: *Writer,
836859 delimiter: ?u8,
837860 limit: Limit,
838861) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } {
......@@ -844,11 +867,11 @@ fn readAny(
844867 error.EndOfStream => return .{ amount, .end },
845868 });
846869 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]);
848871 r.toss(delimiter_index + 1);
849872 return .{ amount + delimiter_index, .delimiter };
850873 };
851 try bw.writeAll(available);
874 try w.writeAll(available);
852875 r.toss(available.len);
853876 amount += available.len;
854877 remaining = remaining.subtract(available.len).?;
......@@ -1303,7 +1326,7 @@ test "expected error.EndOfStream" {
13031326 try std.testing.expectError(error.EndOfStream, r.isBytes("foo"));
13041327}
13051328
1306fn endingRead(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1329fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
13071330 _ = r;
13081331 _ = w;
13091332 _ = limit;
......@@ -1316,7 +1339,7 @@ fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
13161339 return error.EndOfStream;
13171340}
13181341
1319fn failingRead(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1342fn failingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
13201343 _ = r;
13211344 _ = w;
13221345 _ = limit;
......@@ -1409,8 +1432,8 @@ pub fn Hashed(comptime Hasher: type) type {
14091432
14101433 fn discard(r: *Reader, limit: Limit) Error!usize {
14111434 const this: *@This() = @alignCast(@fieldParentPtr("interface", r));
1412 var bw = this.hasher.writable(&.{});
1413 const n = this.in.read(&bw, limit) catch |err| switch (err) {
1435 var w = this.hasher.writable(&.{});
1436 const n = this.in.read(&w, limit) catch |err| switch (err) {
14141437 error.WriteFailed => unreachable,
14151438 else => |e| return e,
14161439 };
lib/std/io/Writer.zig+29-18
......@@ -9,7 +9,12 @@ const File = std.fs.File;
99const testing = std.testing;
1010const 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,
1318vtable: *const VTable,
1419/// If this has length zero, the writer is unbuffered, and `flush` is a no-op.
1520buffer: []u8,
......@@ -30,11 +35,16 @@ pub const VTable = struct {
3035 /// order. Elements of `data` may alias each other but may not alias
3136 /// `buffer`.
3237 ///
33 /// This function modifies `Writer` state.
38 /// This function modifies `Writer.end` and `Writer.buffer`.
3439 ///
35 /// `data.len` must be greater than zero, and the last element of `data` is
36 /// special. It is repeated as necessary so that it is written `splat`
37 /// number of times, which may be zero.
40 /// If `data.len` is zero, it indicates this is a "flush" operation; all
41 /// remaining buffered data must be logically consumed. Generally, this
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.
3848 ///
3949 /// Number of bytes actually written is returned, excluding bytes from
4050 /// `buffer`. Bytes from `buffer` are tracked by modifying `end`.
......@@ -94,7 +104,6 @@ pub const FileError = error{
94104/// modified externally, `count` will always equal `end`.
95105pub fn fixed(buffer: []u8) Writer {
96106 return .{
97 .context = undefined,
98107 .vtable = &.{ .drain = fixedDrain },
99108 .buffer = buffer,
100109 };
......@@ -105,7 +114,6 @@ pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) {
105114}
106115
107116pub const failing: Writer = .{
108 .context = undefined,
109117 .vtable = &.{
110118 .drain = failingDrain,
111119 .sendFile = failingSendFile,
......@@ -114,7 +122,6 @@ pub const failing: Writer = .{
114122
115123pub fn discarding(buffer: []u8) Writer {
116124 return .{
117 .context = undefined,
118125 .vtable = &.{
119126 .drain = discardingDrain,
120127 .sendFile = discardingSendFile,
......@@ -199,12 +206,11 @@ pub fn writeSplatLimit(
199206}
200207
201208/// Drains all remaining buffered data.
209///
210/// It is legal for `VTable.drain` implementations to refrain from modifying
211/// `end`.
202212pub fn flush(w: *Writer) Error!void {
203 const drainFn = w.vtable.drain;
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);
213 assert(0 == try w.vtable.drain(w, &.{}, 0));
208214}
209215
210216pub fn unusedCapacitySlice(w: *const Writer) []u8 {
......@@ -321,14 +327,13 @@ pub fn writeSplatAll(w: *Writer, data: [][]const u8, splat: usize) Error!void {
321327
322328pub fn write(w: *Writer, bytes: []const u8) Error!usize {
323329 if (w.end + bytes.len <= w.buffer.len) {
330 @branchHint(.likely);
324331 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
325332 w.end += bytes.len;
326333 w.count += bytes.len;
327334 return bytes.len;
328335 }
329 const end = w.end;
330 const n = try w.vtable.drain(w, &.{bytes}, 1);
331 return n -| end;
336 return w.vtable.drain(w, &.{bytes}, 1);
332337}
333338
334339/// 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
16751680/// available buffer has been filled. Also, it may be called from `flush` in
16761681/// which case it should return successfully.
16771682fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1683 if (data.len == 0) return 0;
16781684 for (data[0 .. data.len - 1]) |bytes| {
16791685 const dest = w.buffer[w.end..];
16801686 const len = @min(bytes.len, dest.len);
......@@ -1726,7 +1732,6 @@ pub fn Hashed(comptime Hasher: type) type {
17261732 .out = out,
17271733 .hasher = .{},
17281734 .interface = .{
1729 .context = undefined,
17301735 .vtable = &.{@This().drain},
17311736 },
17321737 };
......@@ -1734,6 +1739,13 @@ pub fn Hashed(comptime Hasher: type) type {
17341739
17351740 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
17361741 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 }
17371749 const aux_n = try this.out.writeSplatAux(w.buffered(), data, splat);
17381750 if (aux_n < w.end) {
17391751 this.hasher.update(w.buffer[0..aux_n]);
......@@ -1839,7 +1851,6 @@ pub const Allocating = struct {
18391851
18401852 const init_interface: Writer = .{
18411853 .interface = .{
1842 .context = undefined,
18431854 .vtable = &.{
18441855 .drain = Allocating.drain,
18451856 .sendFile = Allocating.sendFile,
lib/std/net.zig+3-23
......@@ -1912,10 +1912,7 @@ pub const Stream = struct {
19121912 return .{
19131913 .interface_state = .{
19141914 .context = undefined,
1915 .vtable = &.{
1916 .stream = stream,
1917 .discard = discard,
1918 },
1915 .vtable = &.{ .stream = stream },
19191916 .buffer = buffer,
19201917 },
19211918 .net_stream = net_stream,
......@@ -1924,19 +1921,9 @@ pub const Stream = struct {
19241921
19251922 fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize {
19261923 const r: *Reader = @fieldParentPtr("interface", io_r);
1927 const data = io_w.writableVector(limit);
19281924 var iovecs: [max_buffers_len]windows.WSABUF = undefined;
1929 var iovecs_i: usize = 0;
1930 for (data) |d| {
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`.
1925 const bufs = io_w.writableVectorWsa(&iovecs, limit);
1926 assert(bufs[0].len > 0);
19401927 var n: u32 = undefined;
19411928 var flags: u32 = 0;
19421929 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 {
19581945 if (n == 0) return error.EndOfStream;
19591946 return io_w.advanceVector(n);
19601947 }
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 }
19681948 },
19691949 else => struct {
19701950 file_reader: File.Reader,
lib/std/zip.zig+29-42
......@@ -8,6 +8,7 @@ const std = @import("std");
88const File = std.fs.File;
99const is_le = builtin.target.cpu.arch.endian() == .little;
1010const Writer = std.io.Writer;
11const Reader = std.io.Reader;
1112
1213pub const CompressionMethod = enum(u16) {
1314 store = 0,
......@@ -160,61 +161,47 @@ pub const EndRecord = extern struct {
160161 }
161162};
162163
163pub const Decompress = union {
164 inflate: std.compress.flate.Decompress,
165 store: *std.io.Reader,
166
167 fn readable(
168 d: *Decompress,
169 reader: *std.io.Reader,
170 method: CompressionMethod,
171 buffer: []u8,
172 ) std.io.Reader {
173 switch (method) {
174 .store => {
175 d.* = .{ .store = reader };
176 return .{
177 .unbuffered_reader = .{
178 .context = d,
179 .vtable = &.{ .read = readStore },
180 },
164pub const Decompress = struct {
165 interface: Reader,
166 state: union {
167 inflate: std.compress.flate.Decompress,
168 store: *Reader,
169 },
170
171 pub fn init(reader: *Reader, method: CompressionMethod, buffer: []u8) Reader {
172 return switch (method) {
173 .store => .{
174 .state = .{ .store = reader },
175 .interface = .{
176 .context = undefined,
177 .vtable = &.{ .stream = streamStore },
181178 .buffer = buffer,
182179 .end = 0,
183180 .seek = 0,
184 };
181 },
185182 },
186 .deflate => {
187 d.* = .{ .inflate = .init(reader, .raw) };
188 return .{
189 .unbuffered_reader = .{
190 .context = d,
191 .vtable = &.{ .read = readDeflate },
192 },
183 .deflate => .{
184 .state = .{ .inflate = .init(reader, .raw) },
185 .interface = .{
186 .context = undefined,
187 .vtable = &.{ .stream = streamDeflate },
193188 .buffer = buffer,
194189 .end = 0,
195190 .seek = 0,
196 };
191 },
197192 },
198193 else => unreachable,
199 }
194 };
200195 }
201196
202 fn readStore(
203 context: ?*anyopaque,
204 writer: *Writer,
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);
197 fn streamStore(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
198 const d: *Decompress = @fieldParentPtr("interface", r);
199 return d.store.read(w, limit);
209200 }
210201
211 fn readDeflate(
212 context: ?*anyopaque,
213 writer: *Writer,
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);
202 fn streamDeflate(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
203 const d: *Decompress = @fieldParentPtr("interface", r);
204 return std.compress.flate.Decompress.read(&d.inflate, w, limit);
218205 }
219206};
220207