| author | |
| committer | |
| log | 084e45fd865e73f862d85da2a634f8f2cb74580d |
| tree | 0f53a4bdc7a0ccb7db6437170c3cc6189b683a33 |
| parent | b393112674d0b948832c3223d5b300f080c71151 |
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 | 1081 | last_byte: u8 = 0, |
| 1082 | 1082 | at_eof: bool = false, |
| 1083 | 1083 | span: SourceMappings.CorrespondingSpan, |
| 1084 | file: std.fs.File, | |
| 1085 | buffered_reader: *std.io.Reader, | |
| 1084 | file_reader: *std.fs.File.Reader, | |
| 1086 | 1085 | code_page: SupportedCodePage, |
| 1087 | 1086 | |
| 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 | 1095 | // We don't do line comparison for this error, so don't print the note if the line |
| 1090 | 1096 | // number is different |
| 1091 | 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 | 1103 | return error.NotWorthPrintingLines; |
| 1098 | 1104 | } |
| 1099 | 1105 | |
| 1100 | var corresponding_lines = CorrespondingLines{ | |
| 1106 | var file = try utils.openFileNotDir(cwd, corresponding_file, .{}); | |
| 1107 | var corresponding_lines: CorrespondingLines = .{ | |
| 1101 | 1108 | .span = corresponding_span, |
| 1102 | .file = try utils.openFileNotDir(cwd, corresponding_file, .{}), | |
| 1103 | .buffered_reader = undefined, | |
| 1109 | .file_reader = file.reader(&file_read_buffer), | |
| 1104 | 1110 | .code_page = err_details.code_page, |
| 1105 | 1111 | }; |
| 1106 | corresponding_lines.buffered_reader = .{ | |
| 1107 | .unbuffered_reader = corresponding_lines.file.reader(), | |
| 1108 | }; | |
| 1109 | 1112 | errdefer corresponding_lines.deinit(); |
| 1110 | 1113 | |
| 1111 | 1114 | var fbs = std.io.fixedBufferStream(&corresponding_lines.line_buf); |
| ... | ... | @@ -1215,7 +1218,7 @@ const CorrespondingLines = struct { |
| 1215 | 1218 | } |
| 1216 | 1219 | |
| 1217 | 1220 | pub fn deinit(self: *CorrespondingLines) void { |
| 1218 | self.file.close(); | |
| 1221 | self.file_reader.file.close(); | |
| 1219 | 1222 | } |
| 1220 | 1223 | }; |
| 1221 | 1224 |
lib/std/fs/File.zig+54-14| ... | ... | @@ -1027,14 +1027,10 @@ pub const Reader = struct { |
| 1027 | 1027 | /// vectors through the underlying read calls as possible. |
| 1028 | 1028 | const max_buffers_len = 16; |
| 1029 | 1029 | |
| 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 { | |
| 1035 | 1031 | const r: *Reader = @fieldParentPtr("interface", io_reader); |
| 1036 | 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 | 1034 | error.ReadFailed => return error.ReadFailed, |
| 1039 | 1035 | error.WriteFailed => return error.WriteFailed, |
| 1040 | 1036 | error.Unimplemented => { |
| ... | ... | @@ -1043,16 +1039,60 @@ pub const Reader = struct { |
| 1043 | 1039 | }, |
| 1044 | 1040 | }, |
| 1045 | 1041 | .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); | |
| 1050 | 1073 | }, |
| 1051 | 1074 | .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); | |
| 1056 | 1096 | }, |
| 1057 | 1097 | .failure => return error.ReadFailed, |
| 1058 | 1098 | } |
lib/std/io/Reader.zig+77-54| ... | ... | @@ -22,12 +22,12 @@ seek: usize, |
| 22 | 22 | end: usize, |
| 23 | 23 | |
| 24 | 24 | pub 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 | 27 | /// Returns the number of bytes written, which will be at minimum `0` and |
| 28 | 28 | /// at most `limit`. The number returned, including zero, does not indicate |
| 29 | 29 | /// 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 | 32 | /// The reader's internal logical seek position moves forward in accordance |
| 33 | 33 | /// with the number of bytes returned from this function. |
| ... | ... | @@ -35,6 +35,8 @@ pub const VTable = struct { |
| 35 | 35 | /// Implementations are encouraged to utilize mandatory minimum buffer |
| 36 | 36 | /// sizes combined with short reads (returning a value less than `limit`) |
| 37 | 37 | /// in order to minimize complexity. |
| 38 | /// | |
| 39 | /// This function is always called when `buffer` is empty. | |
| 38 | 40 | stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize, |
| 39 | 41 | |
| 40 | 42 | /// Consumes bytes from the internally tracked stream position without |
| ... | ... | @@ -51,7 +53,7 @@ pub const VTable = struct { |
| 51 | 53 | /// sizes combined with short reads (returning a value less than `limit`) |
| 52 | 54 | /// in order to minimize complexity. |
| 53 | 55 | /// |
| 54 | /// The default implementation is is based on calling `read`, borrowing | |
| 56 | /// The default implementation is is based on calling `stream`, borrowing | |
| 55 | 57 | /// `buffer` to construct a temporary `Writer` and ignoring the written |
| 56 | 58 | /// data. |
| 57 | 59 | discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard, |
| ... | ... | @@ -88,7 +90,7 @@ pub const ShortError = error{ |
| 88 | 90 | pub const failing: Reader = .{ |
| 89 | 91 | .context = undefined, |
| 90 | 92 | .vtable = &.{ |
| 91 | .read = failingRead, | |
| 93 | .read = failingStream, | |
| 92 | 94 | .discard = failingDiscard, |
| 93 | 95 | }, |
| 94 | 96 | .buffer = &.{}, |
| ... | ... | @@ -107,7 +109,7 @@ pub fn fixed(buffer: []const u8) Reader { |
| 107 | 109 | return .{ |
| 108 | 110 | .context = undefined, |
| 109 | 111 | .vtable = &.{ |
| 110 | .read = endingRead, | |
| 112 | .stream = endingStream, | |
| 111 | 113 | .discard = endingDiscard, |
| 112 | 114 | }, |
| 113 | 115 | // This cast is safe because all potential writes to it will instead |
| ... | ... | @@ -274,9 +276,17 @@ pub fn appendRemaining( |
| 274 | 276 | /// The reader's internal logical seek position moves forward in accordance |
| 275 | 277 | /// with the number of bytes returned from this function. |
| 276 | 278 | pub fn readVec(r: *Reader, data: []const []u8) Error!usize { |
| 277 | return readVecLimit(r, data, .unlimited); | |
| 279 | return readVec(r, data, .unlimited); | |
| 278 | 280 | } |
| 279 | 281 | |
| 282 | const VectorWrapped = struct { | |
| 283 | writer: Writer, | |
| 284 | first: []u8, | |
| 285 | middle: []const []u8, | |
| 286 | last: []u8, | |
| 287 | var unique_address: u8 = undefined; | |
| 288 | }; | |
| 289 | ||
| 280 | 290 | /// Equivalent to `readVec` but reads at most `limit` bytes. |
| 281 | 291 | /// |
| 282 | 292 | /// 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 | 306 | if (remaining == 0) break; |
| 297 | 307 | if (buf.len - copy_len == 0) continue; |
| 298 | 308 | |
| 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 | 313 | r.seek = 0; |
| 300 | 314 | 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; | |
| 321 | 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) { | |
| 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; | |
| 334 | 355 | } |
| 356 | r.end = n; | |
| 357 | break; | |
| 335 | 358 | } |
| 336 | 359 | return @intFromEnum(limit) - remaining; |
| 337 | 360 | } |
| ... | ... | @@ -366,10 +389,10 @@ pub fn readVecAll(r: *Reader, data: [][]u8) Error!void { |
| 366 | 389 | } |
| 367 | 390 | |
| 368 | 391 | /// "Pump" data from the reader to the writer. |
| 369 | pub fn readAll(r: *Reader, bw: *Writer, limit: Limit) StreamError!void { | |
| 392 | pub fn readAll(r: *Reader, w: *Writer, limit: Limit) StreamError!void { | |
| 370 | 393 | var remaining = limit; |
| 371 | 394 | while (remaining.nonzero()) { |
| 372 | const n = try r.read(bw, remaining); | |
| 395 | const n = try r.read(w, remaining); | |
| 373 | 396 | remaining = remaining.subtract(n).?; |
| 374 | 397 | } |
| 375 | 398 | } |
| ... | ... | @@ -774,12 +797,12 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { |
| 774 | 797 | return result[0 .. result.len - 1]; |
| 775 | 798 | } |
| 776 | 799 | |
| 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 | |
| 778 | 801 | /// found. Does not write the delimiter itself. |
| 779 | 802 | /// |
| 780 | 803 | /// Returns number of bytes streamed. |
| 781 | pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize { | |
| 782 | const amount, const to = try r.readAny(bw, delimiter, .unlimited); | |
| 804 | pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize { | |
| 805 | const amount, const to = try r.readAny(w, delimiter, .unlimited); | |
| 783 | 806 | return switch (to) { |
| 784 | 807 | .delimiter => amount, |
| 785 | 808 | .limit => unreachable, |
| ... | ... | @@ -787,7 +810,7 @@ pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize { |
| 787 | 810 | }; |
| 788 | 811 | } |
| 789 | 812 | |
| 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 | 814 | /// Does not write the delimiter itself. |
| 792 | 815 | /// |
| 793 | 816 | /// Succeeds if stream ends before delimiter found. |
| ... | ... | @@ -795,10 +818,10 @@ pub fn readDelimiter(r: *Reader, bw: *Writer, delimiter: u8) StreamError!usize { |
| 795 | 818 | /// Returns number of bytes streamed. The end is not signaled to the writer. |
| 796 | 819 | pub fn readDelimiterEnding( |
| 797 | 820 | r: *Reader, |
| 798 | bw: *Writer, | |
| 821 | w: *Writer, | |
| 799 | 822 | delimiter: u8, |
| 800 | 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 | 825 | return switch (to) { |
| 803 | 826 | .delimiter, .end => amount, |
| 804 | 827 | .limit => unreachable, |
| ... | ... | @@ -812,17 +835,17 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{ |
| 812 | 835 | StreamTooLong, |
| 813 | 836 | }; |
| 814 | 837 | |
| 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 | 839 | /// Does not write the delimiter itself. |
| 817 | 840 | /// |
| 818 | 841 | /// Returns number of bytes streamed. |
| 819 | 842 | pub fn readDelimiterLimit( |
| 820 | 843 | r: *Reader, |
| 821 | bw: *Writer, | |
| 844 | w: *Writer, | |
| 822 | 845 | delimiter: u8, |
| 823 | 846 | limit: Limit, |
| 824 | 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 | 849 | return switch (to) { |
| 827 | 850 | .delimiter => amount, |
| 828 | 851 | .limit => error.StreamTooLong, |
| ... | ... | @@ -832,7 +855,7 @@ pub fn readDelimiterLimit( |
| 832 | 855 | |
| 833 | 856 | fn readAny( |
| 834 | 857 | r: *Reader, |
| 835 | bw: *Writer, | |
| 858 | w: *Writer, | |
| 836 | 859 | delimiter: ?u8, |
| 837 | 860 | limit: Limit, |
| 838 | 861 | ) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } { |
| ... | ... | @@ -844,11 +867,11 @@ fn readAny( |
| 844 | 867 | error.EndOfStream => return .{ amount, .end }, |
| 845 | 868 | }); |
| 846 | 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 | 871 | r.toss(delimiter_index + 1); |
| 849 | 872 | return .{ amount + delimiter_index, .delimiter }; |
| 850 | 873 | }; |
| 851 | try bw.writeAll(available); | |
| 874 | try w.writeAll(available); | |
| 852 | 875 | r.toss(available.len); |
| 853 | 876 | amount += available.len; |
| 854 | 877 | remaining = remaining.subtract(available.len).?; |
| ... | ... | @@ -1303,7 +1326,7 @@ test "expected error.EndOfStream" { |
| 1303 | 1326 | try std.testing.expectError(error.EndOfStream, r.isBytes("foo")); |
| 1304 | 1327 | } |
| 1305 | 1328 | |
| 1306 | fn endingRead(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { | |
| 1329 | fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { | |
| 1307 | 1330 | _ = r; |
| 1308 | 1331 | _ = w; |
| 1309 | 1332 | _ = limit; |
| ... | ... | @@ -1316,7 +1339,7 @@ fn endingDiscard(r: *Reader, limit: Limit) Error!usize { |
| 1316 | 1339 | return error.EndOfStream; |
| 1317 | 1340 | } |
| 1318 | 1341 | |
| 1319 | fn failingRead(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { | |
| 1342 | fn failingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { | |
| 1320 | 1343 | _ = r; |
| 1321 | 1344 | _ = w; |
| 1322 | 1345 | _ = limit; |
| ... | ... | @@ -1409,8 +1432,8 @@ pub fn Hashed(comptime Hasher: type) type { |
| 1409 | 1432 | |
| 1410 | 1433 | fn discard(r: *Reader, limit: Limit) Error!usize { |
| 1411 | 1434 | 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) { | |
| 1414 | 1437 | error.WriteFailed => unreachable, |
| 1415 | 1438 | else => |e| return e, |
| 1416 | 1439 | }; |
lib/std/io/Writer.zig+29-18| ... | ... | @@ -9,7 +9,12 @@ const File = std.fs.File; |
| 9 | 9 | const testing = std.testing; |
| 10 | 10 | const Allocator = std.mem.Allocator; |
| 11 | 11 | |
| 12 | context: ?*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. | |
| 17 | context: ?*anyopaque = null, | |
| 13 | 18 | vtable: *const VTable, |
| 14 | 19 | /// If this has length zero, the writer is unbuffered, and `flush` is a no-op. |
| 15 | 20 | buffer: []u8, |
| ... | ... | @@ -30,11 +35,16 @@ pub const VTable = struct { |
| 30 | 35 | /// order. Elements of `data` may alias each other but may not alias |
| 31 | 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` 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. | |
| 38 | 48 | /// |
| 39 | 49 | /// Number of bytes actually written is returned, excluding bytes from |
| 40 | 50 | /// `buffer`. Bytes from `buffer` are tracked by modifying `end`. |
| ... | ... | @@ -94,7 +104,6 @@ pub const FileError = error{ |
| 94 | 104 | /// modified externally, `count` will always equal `end`. |
| 95 | 105 | pub fn fixed(buffer: []u8) Writer { |
| 96 | 106 | return .{ |
| 97 | .context = undefined, | |
| 98 | 107 | .vtable = &.{ .drain = fixedDrain }, |
| 99 | 108 | .buffer = buffer, |
| 100 | 109 | }; |
| ... | ... | @@ -105,7 +114,6 @@ pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) { |
| 105 | 114 | } |
| 106 | 115 | |
| 107 | 116 | pub const failing: Writer = .{ |
| 108 | .context = undefined, | |
| 109 | 117 | .vtable = &.{ |
| 110 | 118 | .drain = failingDrain, |
| 111 | 119 | .sendFile = failingSendFile, |
| ... | ... | @@ -114,7 +122,6 @@ pub const failing: Writer = .{ |
| 114 | 122 | |
| 115 | 123 | pub fn discarding(buffer: []u8) Writer { |
| 116 | 124 | return .{ |
| 117 | .context = undefined, | |
| 118 | 125 | .vtable = &.{ |
| 119 | 126 | .drain = discardingDrain, |
| 120 | 127 | .sendFile = discardingSendFile, |
| ... | ... | @@ -199,12 +206,11 @@ pub fn writeSplatLimit( |
| 199 | 206 | } |
| 200 | 207 | |
| 201 | 208 | /// Drains all remaining buffered data. |
| 209 | /// | |
| 210 | /// It is legal for `VTable.drain` implementations to refrain from modifying | |
| 211 | /// `end`. | |
| 202 | 212 | pub 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)); | |
| 208 | 214 | } |
| 209 | 215 | |
| 210 | 216 | pub fn unusedCapacitySlice(w: *const Writer) []u8 { |
| ... | ... | @@ -321,14 +327,13 @@ pub fn writeSplatAll(w: *Writer, data: [][]const u8, splat: usize) Error!void { |
| 321 | 327 | |
| 322 | 328 | pub fn write(w: *Writer, bytes: []const u8) Error!usize { |
| 323 | 329 | if (w.end + bytes.len <= w.buffer.len) { |
| 330 | @branchHint(.likely); | |
| 324 | 331 | @memcpy(w.buffer[w.end..][0..bytes.len], bytes); |
| 325 | 332 | w.end += bytes.len; |
| 326 | 333 | w.count += bytes.len; |
| 327 | 334 | return bytes.len; |
| 328 | 335 | } |
| 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); | |
| 332 | 337 | } |
| 333 | 338 | |
| 334 | 339 | /// 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 | 1680 | /// available buffer has been filled. Also, it may be called from `flush` in |
| 1676 | 1681 | /// which case it should return successfully. |
| 1677 | 1682 | fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { |
| 1683 | if (data.len == 0) return 0; | |
| 1678 | 1684 | for (data[0 .. data.len - 1]) |bytes| { |
| 1679 | 1685 | const dest = w.buffer[w.end..]; |
| 1680 | 1686 | const len = @min(bytes.len, dest.len); |
| ... | ... | @@ -1726,7 +1732,6 @@ pub fn Hashed(comptime Hasher: type) type { |
| 1726 | 1732 | .out = out, |
| 1727 | 1733 | .hasher = .{}, |
| 1728 | 1734 | .interface = .{ |
| 1729 | .context = undefined, | |
| 1730 | 1735 | .vtable = &.{@This().drain}, |
| 1731 | 1736 | }, |
| 1732 | 1737 | }; |
| ... | ... | @@ -1734,6 +1739,13 @@ pub fn Hashed(comptime Hasher: type) type { |
| 1734 | 1739 | |
| 1735 | 1740 | fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { |
| 1736 | 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 | 1749 | const aux_n = try this.out.writeSplatAux(w.buffered(), data, splat); |
| 1738 | 1750 | if (aux_n < w.end) { |
| 1739 | 1751 | this.hasher.update(w.buffer[0..aux_n]); |
| ... | ... | @@ -1839,7 +1851,6 @@ pub const Allocating = struct { |
| 1839 | 1851 | |
| 1840 | 1852 | const init_interface: Writer = .{ |
| 1841 | 1853 | .interface = .{ |
| 1842 | .context = undefined, | |
| 1843 | 1854 | .vtable = &.{ |
| 1844 | 1855 | .drain = Allocating.drain, |
| 1845 | 1856 | .sendFile = Allocating.sendFile, |
lib/std/net.zig+3-23| ... | ... | @@ -1912,10 +1912,7 @@ pub const Stream = struct { |
| 1912 | 1912 | return .{ |
| 1913 | 1913 | .interface_state = .{ |
| 1914 | 1914 | .context = undefined, |
| 1915 | .vtable = &.{ | |
| 1916 | .stream = stream, | |
| 1917 | .discard = discard, | |
| 1918 | }, | |
| 1915 | .vtable = &.{ .stream = stream }, | |
| 1919 | 1916 | .buffer = buffer, |
| 1920 | 1917 | }, |
| 1921 | 1918 | .net_stream = net_stream, |
| ... | ... | @@ -1924,19 +1921,9 @@ pub const Stream = struct { |
| 1924 | 1921 | |
| 1925 | 1922 | fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize { |
| 1926 | 1923 | const r: *Reader = @fieldParentPtr("interface", io_r); |
| 1927 | const data = io_w.writableVector(limit); | |
| 1928 | 1924 | 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); | |
| 1940 | 1927 | var n: u32 = undefined; |
| 1941 | 1928 | var flags: u32 = 0; |
| 1942 | 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 | 1945 | if (n == 0) return error.EndOfStream; |
| 1959 | 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 | 1949 | else => struct { |
| 1970 | 1950 | file_reader: File.Reader, |
lib/std/zip.zig+29-42| ... | ... | @@ -8,6 +8,7 @@ const std = @import("std"); |
| 8 | 8 | const File = std.fs.File; |
| 9 | 9 | const is_le = builtin.target.cpu.arch.endian() == .little; |
| 10 | 10 | const Writer = std.io.Writer; |
| 11 | const Reader = std.io.Reader; | |
| 11 | 12 | |
| 12 | 13 | pub const CompressionMethod = enum(u16) { |
| 13 | 14 | store = 0, |
| ... | ... | @@ -160,61 +161,47 @@ pub const EndRecord = extern struct { |
| 160 | 161 | } |
| 161 | 162 | }; |
| 162 | 163 | |
| 163 | pub 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 | }, | |
| 164 | pub 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 }, | |
| 181 | 178 | .buffer = buffer, |
| 182 | 179 | .end = 0, |
| 183 | 180 | .seek = 0, |
| 184 | }; | |
| 181 | }, | |
| 185 | 182 | }, |
| 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 }, | |
| 193 | 188 | .buffer = buffer, |
| 194 | 189 | .end = 0, |
| 195 | 190 | .seek = 0, |
| 196 | }; | |
| 191 | }, | |
| 197 | 192 | }, |
| 198 | 193 | else => unreachable, |
| 199 | } | |
| 194 | }; | |
| 200 | 195 | } |
| 201 | 196 | |
| 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); | |
| 209 | 200 | } |
| 210 | 201 | |
| 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); | |
| 218 | 205 | } |
| 219 | 206 | }; |
| 220 | 207 |