| author | |
| committer | |
| log | 9dc0b4a98f8efe2d249db74ebe52c6880570b7cc |
| tree | 1986b2db5e513ccd7d995c94d202e7690b9c7049 |
| parent | 6ac7931bec29f1cd4c889d6913a21f28e17c13a8 |
9 files changed, 228 insertions(+), 321 deletions(-)
lib/std/compress/flate/inflate.zig+14-17| ... | ... | @@ -326,6 +326,7 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type { |
| 326 | 326 | /// returned bytes means end of stream reached. With limit=0 returns as |
| 327 | 327 | /// much data it can. It newer will be more than 65536 bytes, which is |
| 328 | 328 | /// size of internal buffer. |
| 329 | /// TODO merge this logic into reader_streamRead and reader_streamReadVec | |
| 329 | 330 | pub fn get(self: *Self, limit: usize) Error![]const u8 { |
| 330 | 331 | while (true) { |
| 331 | 332 | const out = self.hist.readAtMost(limit); |
| ... | ... | @@ -342,31 +343,27 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type { |
| 342 | 343 | ctx: ?*anyopaque, |
| 343 | 344 | bw: *std.io.BufferedWriter, |
| 344 | 345 | limit: std.io.Reader.Limit, |
| 345 | ) std.io.Reader.RwResult { | |
| 346 | ) anyerror!std.io.Reader.Status { | |
| 346 | 347 | const self: *Self = @alignCast(@ptrCast(ctx)); |
| 347 | const out = bw.writableSlice(1) catch |err| return .{ .write_err = err }; | |
| 348 | const in = self.get(limit.min(out.len)) catch |err| return .{ .read_err = err }; | |
| 349 | if (in.len == 0) return .{ .read_end = true }; | |
| 348 | const out = try bw.writableSlice(1); | |
| 349 | const in = try self.get(limit.min(out.len)); | |
| 350 | 350 | @memcpy(out[0..in.len], in); |
| 351 | return .{ .len = in.len }; | |
| 351 | bw.advance(in.len); | |
| 352 | return .{ .len = in.len, .end = in.len == 0 }; | |
| 352 | 353 | } |
| 353 | 354 | |
| 354 | fn reader_streamReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Result { | |
| 355 | fn reader_streamReadVec(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status { | |
| 355 | 356 | const self: *Self = @alignCast(@ptrCast(ctx)); |
| 356 | var total: usize = 0; | |
| 357 | for (data) |buffer| { | |
| 358 | if (buffer.len == 0) break; | |
| 359 | const out = self.get(buffer.len) catch |err| { | |
| 360 | return .{ .len = total, .err = err }; | |
| 361 | }; | |
| 362 | if (out.len == 0) break; | |
| 363 | @memcpy(buffer[0..out.len], out); | |
| 364 | total += out.len; | |
| 357 | for (data) |out| { | |
| 358 | if (out.len == 0) continue; | |
| 359 | const in = try self.get(out.len); | |
| 360 | @memcpy(out[0..in.len], in); | |
| 361 | return .{ .len = @intCast(in.len), .end = in.len == 0 }; | |
| 365 | 362 | } |
| 366 | return .{ .len = total, .end = total == 0 }; | |
| 363 | return .{}; | |
| 367 | 364 | } |
| 368 | 365 | |
| 369 | pub fn streamReadVec(self: *Self, data: []const []u8) std.io.Reader.Result { | |
| 366 | pub fn streamReadVec(self: *Self, data: []const []u8) anyerror!std.io.Reader.Status { | |
| 370 | 367 | return reader_streamReadVec(self, data); |
| 371 | 368 | } |
| 372 | 369 |
lib/std/debug/Dwarf.zig+5-6| ... | ... | @@ -2247,16 +2247,15 @@ pub const ElfModule = struct { |
| 2247 | 2247 | errdefer gpa.free(decompressed_section); |
| 2248 | 2248 | |
| 2249 | 2249 | { |
| 2250 | var read_index: usize = 0; | |
| 2250 | var i: usize = 0; | |
| 2251 | 2251 | while (true) { |
| 2252 | const read_result = zlib_stream.streamReadVec(&.{decompressed_section[read_index..]}); | |
| 2253 | read_result.err catch { | |
| 2252 | const status = zlib_stream.streamReadVec(&.{decompressed_section[i..]}) catch { | |
| 2254 | 2253 | gpa.free(decompressed_section); |
| 2255 | 2254 | continue :shdrs; |
| 2256 | 2255 | }; |
| 2257 | read_index += read_result.len; | |
| 2258 | if (read_index == decompressed_section.len) break; | |
| 2259 | if (read_result.end) { | |
| 2256 | i += status.len; | |
| 2257 | if (i == decompressed_section.len) break; | |
| 2258 | if (status.end) { | |
| 2260 | 2259 | gpa.free(decompressed_section); |
| 2261 | 2260 | continue :shdrs; |
| 2262 | 2261 | } |
lib/std/debug/SelfInfo.zig+1-1| ... | ... | @@ -2034,7 +2034,7 @@ pub const VirtualMachine = struct { |
| 2034 | 2034 | const streams: [2]*std.io.BufferedReader = .{ &cie_stream, &fde_stream }; |
| 2035 | 2035 | |
| 2036 | 2036 | for (&streams, 0..) |stream, i| { |
| 2037 | while (stream.seek < stream.buffer.len) { | |
| 2037 | while (stream.seek < stream.storageBuffer().len) { | |
| 2038 | 2038 | const instruction = try std.debug.Dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian); |
| 2039 | 2039 | prev_row = try self.step(allocator, cie, i == 0, instruction); |
| 2040 | 2040 | if (pc < fde.pc_begin + self.current_row.offset) return prev_row; |
lib/std/fs/File.zig+65-61| ... | ... | @@ -1499,7 +1499,7 @@ pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFile |
| 1499 | 1499 | error.FileDescriptorNotASocket, |
| 1500 | 1500 | error.NetworkUnreachable, |
| 1501 | 1501 | error.NetworkSubsystemFailed, |
| 1502 | => return self.writeFileAllUnseekable(in_file, args), | |
| 1502 | => return self.writeFileUnseekableAll(in_file, args), | |
| 1503 | 1503 | |
| 1504 | 1504 | else => |e| return e, |
| 1505 | 1505 | }; |
| ... | ... | @@ -1507,53 +1507,11 @@ pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFile |
| 1507 | 1507 | |
| 1508 | 1508 | /// Does not try seeking in either of the File parameters. |
| 1509 | 1509 | /// See `writeFileAll` as an alternative to calling this. |
| 1510 | pub fn writeFileAllUnseekable(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void { | |
| 1511 | // TODO make `try @errorCast(...)` work | |
| 1512 | return @errorCast(writeFileAllUnseekableInner(self, in_file, args)); | |
| 1513 | } | |
| 1514 | ||
| 1515 | fn writeFileAllUnseekableInner(out_file: File, in_file: File, args: WriteFileOptions) anyerror!void { | |
| 1516 | const headers = args.headers_and_trailers[0..args.header_count]; | |
| 1517 | const trailers = args.headers_and_trailers[args.header_count..]; | |
| 1518 | ||
| 1519 | try out_file.writevAll(headers); | |
| 1520 | ||
| 1521 | // Some possible optimizations here: | |
| 1522 | // * Could writev buffer multiple times if the amount to discard is larger than 4096 | |
| 1523 | // * Could combine discard and read in one readv if amount to discard is small | |
| 1524 | ||
| 1525 | var buffer: [4096]u8 = undefined; | |
| 1526 | var remaining = args.in_offset; | |
| 1527 | while (remaining > 0) { | |
| 1528 | const n = try in_file.read(buffer[0..@min(buffer.len, remaining)]); | |
| 1529 | if (n == 0) return error.EndOfStream; | |
| 1530 | remaining -= n; | |
| 1531 | } | |
| 1532 | if (args.in_len) |len| { | |
| 1533 | remaining = len; | |
| 1534 | var buffer_index: usize = 0; | |
| 1535 | while (remaining > 0) { | |
| 1536 | const n = buffer_index + try in_file.read(buffer[buffer_index..@min(buffer.len, remaining)]); | |
| 1537 | if (n == 0) return error.EndOfStream; | |
| 1538 | const written = try out_file.write(buffer[0..n]); | |
| 1539 | if (written == 0) return error.EndOfStream; | |
| 1540 | remaining -= written; | |
| 1541 | std.mem.copyForwards(u8, &buffer, buffer[written..n]); | |
| 1542 | buffer_index = n - written; | |
| 1543 | } | |
| 1544 | } else { | |
| 1545 | var buffer_index: usize = 0; | |
| 1546 | while (true) { | |
| 1547 | const n = buffer_index + try in_file.read(buffer[buffer_index..]); | |
| 1548 | if (n == 0) break; | |
| 1549 | const written = try out_file.write(buffer[0..n]); | |
| 1550 | if (written == 0) return error.EndOfStream; | |
| 1551 | std.mem.copyForwards(u8, &buffer, buffer[written..n]); | |
| 1552 | buffer_index = n - written; | |
| 1553 | } | |
| 1554 | } | |
| 1555 | ||
| 1556 | try out_file.writevAll(trailers); | |
| 1510 | pub fn writeFileUnseekableAll(out_file: File, in_file: File, args: WriteFileOptions) WriteFileError!void { | |
| 1511 | _ = out_file; | |
| 1512 | _ = in_file; | |
| 1513 | _ = args; | |
| 1514 | @panic("TODO call writeFileUnseekable multiple times"); | |
| 1557 | 1515 | } |
| 1558 | 1516 | |
| 1559 | 1517 | /// Low level function which can fail for OS-specific reasons. |
| ... | ... | @@ -1635,6 +1593,30 @@ pub fn reader(file: File) std.io.Reader { |
| 1635 | 1593 | }; |
| 1636 | 1594 | } |
| 1637 | 1595 | |
| 1596 | pub fn unseekableReader(file: File) std.io.Reader { | |
| 1597 | return .{ | |
| 1598 | .context = handleToOpaque(file.handle), | |
| 1599 | .vtable = .{ | |
| 1600 | .posRead = null, | |
| 1601 | .posReadVec = null, | |
| 1602 | .streamRead = reader_streamRead, | |
| 1603 | .streamReadVec = reader_streamReadVec, | |
| 1604 | }, | |
| 1605 | }; | |
| 1606 | } | |
| 1607 | ||
| 1608 | pub fn unstreamableReader(file: File) std.io.Reader { | |
| 1609 | return .{ | |
| 1610 | .context = handleToOpaque(file.handle), | |
| 1611 | .vtable = .{ | |
| 1612 | .posRead = reader_posRead, | |
| 1613 | .posReadVec = reader_posReadVec, | |
| 1614 | .streamRead = null, | |
| 1615 | .streamReadVec = null, | |
| 1616 | }, | |
| 1617 | }; | |
| 1618 | } | |
| 1619 | ||
| 1638 | 1620 | pub fn writer(file: File) std.io.Writer { |
| 1639 | 1621 | return .{ |
| 1640 | 1622 | .context = handleToOpaque(file.handle), |
| ... | ... | @@ -1692,12 +1674,12 @@ pub fn reader_streamReadVec(context: ?*anyopaque, data: []const []u8) anyerror!s |
| 1692 | 1674 | }; |
| 1693 | 1675 | } |
| 1694 | 1676 | |
| 1695 | pub fn writer_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Result { | |
| 1677 | pub fn writer_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 1696 | 1678 | const file = opaqueToHandle(context); |
| 1697 | 1679 | var splat_buffer: [256]u8 = undefined; |
| 1698 | 1680 | if (is_windows) { |
| 1699 | 1681 | if (data.len == 1 and splat == 0) return 0; |
| 1700 | return .{ .len = windows.WriteFile(file, data[0], null) catch |err| return .{ .err = err } }; | |
| 1682 | return windows.WriteFile(file, data[0], null); | |
| 1701 | 1683 | } |
| 1702 | 1684 | var iovecs: [max_buffers_len]std.posix.iovec_const = undefined; |
| 1703 | 1685 | var len: usize = @min(iovecs.len, data.len); |
| ... | ... | @@ -1706,8 +1688,8 @@ pub fn writer_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: |
| 1706 | 1688 | .len = d.len, |
| 1707 | 1689 | }; |
| 1708 | 1690 | switch (splat) { |
| 1709 | 0 => return .{ .len = std.posix.writev(file, iovecs[0 .. len - 1]) catch |err| return .{ .err = err } }, | |
| 1710 | 1 => return .{ .len = std.posix.writev(file, iovecs[0..len]) catch |err| return .{ .err = err } }, | |
| 1691 | 0 => return std.posix.writev(file, iovecs[0 .. len - 1]), | |
| 1692 | 1 => return std.posix.writev(file, iovecs[0..len]), | |
| 1711 | 1693 | else => { |
| 1712 | 1694 | const pattern = data[data.len - 1]; |
| 1713 | 1695 | if (pattern.len == 1) { |
| ... | ... | @@ -1725,21 +1707,21 @@ pub fn writer_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: |
| 1725 | 1707 | iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat }; |
| 1726 | 1708 | len += 1; |
| 1727 | 1709 | } |
| 1728 | return .{ .len = std.posix.writev(file, iovecs[0..len]) catch |err| return .{ .err = err } }; | |
| 1710 | return std.posix.writev(file, iovecs[0..len]); | |
| 1729 | 1711 | } |
| 1730 | 1712 | }, |
| 1731 | 1713 | } |
| 1732 | return .{ .len = std.posix.writev(file, iovecs[0..len]) catch |err| return .{ .err = err } }; | |
| 1714 | return std.posix.writev(file, iovecs[0..len]); | |
| 1733 | 1715 | } |
| 1734 | 1716 | |
| 1735 | 1717 | pub fn writer_writeFile( |
| 1736 | 1718 | context: ?*anyopaque, |
| 1737 | 1719 | in_file: std.fs.File, |
| 1738 | in_offset: u64, | |
| 1720 | in_offset: std.io.Writer.Offset, | |
| 1739 | 1721 | in_len: std.io.Writer.FileLen, |
| 1740 | 1722 | headers_and_trailers: []const []const u8, |
| 1741 | 1723 | headers_len: usize, |
| 1742 | ) std.io.Writer.Result { | |
| 1724 | ) anyerror!usize { | |
| 1743 | 1725 | const out_fd = opaqueToHandle(context); |
| 1744 | 1726 | const in_fd = in_file.handle; |
| 1745 | 1727 | const len_int = switch (in_len) { |
| ... | ... | @@ -1747,6 +1729,26 @@ pub fn writer_writeFile( |
| 1747 | 1729 | .entire_file => 0, |
| 1748 | 1730 | else => in_len.int(), |
| 1749 | 1731 | }; |
| 1732 | if (native_os == .linux) sf: { | |
| 1733 | // Linux sendfile does not support headers or trailers but it does | |
| 1734 | // support a streaming read from in_file. | |
| 1735 | if (headers_len > 0) return writer_writeSplat(context, headers_and_trailers[0..headers_len], 1); | |
| 1736 | const max_count = 0x7ffff000; // Avoid EINVAL. | |
| 1737 | const smaller_len = if (len_int == 0) max_count else @min(len_int, max_count); | |
| 1738 | var off: std.os.linux.off_t = undefined; | |
| 1739 | const off_ptr: ?*std.os.linux.off_t = if (in_offset.toInt()) |offset| b: { | |
| 1740 | off = try std.math.cast(std.os.linux.off_t, offset); | |
| 1741 | break :b &off; | |
| 1742 | } else null; | |
| 1743 | const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, smaller_len) catch |err| switch (err) { | |
| 1744 | error.UnsupportedOperation => break :sf, | |
| 1745 | error.Unseekable => break :sf, | |
| 1746 | error.Unexpected => break :sf, | |
| 1747 | else => |e| return e, | |
| 1748 | }; | |
| 1749 | if (in_offset.toInt()) |offset| assert(n == off - offset); | |
| 1750 | return n; | |
| 1751 | } | |
| 1750 | 1752 | var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined; |
| 1751 | 1753 | const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, headers_and_trailers.len)]; |
| 1752 | 1754 | for (iovecs, headers_and_trailers[0..iovecs.len]) |*v, d| v.* = .{ .base = d.ptr, .len = d.len }; |
| ... | ... | @@ -1783,7 +1785,7 @@ fn writeFileUnseekable( |
| 1783 | 1785 | @panic("TODO writeFileUnseekable"); |
| 1784 | 1786 | } |
| 1785 | 1787 | |
| 1786 | fn handleToOpaque(handle: Handle) *anyopaque { | |
| 1788 | fn handleToOpaque(handle: Handle) ?*anyopaque { | |
| 1787 | 1789 | return switch (@typeInfo(Handle)) { |
| 1788 | 1790 | .pointer => @ptrCast(handle), |
| 1789 | 1791 | .int => @ptrFromInt(@as(u32, @bitCast(handle))), |
| ... | ... | @@ -1791,7 +1793,7 @@ fn handleToOpaque(handle: Handle) *anyopaque { |
| 1791 | 1793 | }; |
| 1792 | 1794 | } |
| 1793 | 1795 | |
| 1794 | fn opaqueToHandle(userdata: *anyopaque) Handle { | |
| 1796 | fn opaqueToHandle(userdata: ?*anyopaque) Handle { | |
| 1795 | 1797 | return switch (@typeInfo(Handle)) { |
| 1796 | 1798 | .pointer => @ptrCast(userdata), |
| 1797 | 1799 | .int => @intCast(@intFromPtr(userdata)), |
| ... | ... | @@ -1976,9 +1978,13 @@ pub fn downgradeLock(file: File) LockError!void { |
| 1976 | 1978 | } |
| 1977 | 1979 | } |
| 1978 | 1980 | |
| 1981 | const builtin = @import("builtin"); | |
| 1982 | const Os = std.builtin.Os; | |
| 1983 | const native_os = builtin.os.tag; | |
| 1984 | const is_windows = native_os == .windows; | |
| 1985 | ||
| 1979 | 1986 | const File = @This(); |
| 1980 | 1987 | const std = @import("../std.zig"); |
| 1981 | const builtin = @import("builtin"); | |
| 1982 | 1988 | const Allocator = std.mem.Allocator; |
| 1983 | 1989 | const posix = std.posix; |
| 1984 | 1990 | const io = std.io; |
| ... | ... | @@ -1986,7 +1992,5 @@ const math = std.math; |
| 1986 | 1992 | const assert = std.debug.assert; |
| 1987 | 1993 | const linux = std.os.linux; |
| 1988 | 1994 | const windows = std.os.windows; |
| 1989 | const Os = std.builtin.Os; | |
| 1990 | 1995 | const maxInt = std.math.maxInt; |
| 1991 | const is_windows = builtin.os.tag == .windows; | |
| 1992 | 1996 | const Alignment = std.mem.Alignment; |
lib/std/io/BufferedReader.zig+67-7| ... | ... | @@ -23,6 +23,69 @@ pub fn init(br: *BufferedReader, r: Reader, buffer: []u8) void { |
| 23 | 23 | br.storage.initFixed(buffer); |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | const eof_writer: std.io.Writer.VTable = .{ | |
| 27 | .writeSplat = eof_writeSplat, | |
| 28 | .writeFile = eof_writeFile, | |
| 29 | }; | |
| 30 | const eof_reader: std.io.Reader.VTable = .{ | |
| 31 | .posRead = eof_posRead, | |
| 32 | .posReadVec = eof_posReadVec, | |
| 33 | .streamRead = eof_streamRead, | |
| 34 | .streamReadVec = eof_streamReadVec, | |
| 35 | }; | |
| 36 | ||
| 37 | fn eof_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!Reader.Status { | |
| 38 | _ = context; | |
| 39 | _ = data; | |
| 40 | _ = splat; | |
| 41 | return error.NoSpaceLeft; | |
| 42 | } | |
| 43 | ||
| 44 | fn eof_writeFile( | |
| 45 | context: ?*anyopaque, | |
| 46 | file: std.fs.File, | |
| 47 | offset: u64, | |
| 48 | len: Reader.FileLen, | |
| 49 | headers_and_trailers: []const []const u8, | |
| 50 | headers_len: usize, | |
| 51 | ) anyerror!Reader.Status { | |
| 52 | _ = context; | |
| 53 | _ = file; | |
| 54 | _ = offset; | |
| 55 | _ = len; | |
| 56 | _ = headers_and_trailers; | |
| 57 | _ = headers_len; | |
| 58 | return error.NoSpaceLeft; | |
| 59 | } | |
| 60 | ||
| 61 | fn eof_posRead(ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Reader.Limit, offset: u64) anyerror!Reader.Status { | |
| 62 | _ = ctx; | |
| 63 | _ = bw; | |
| 64 | _ = limit; | |
| 65 | _ = offset; | |
| 66 | return error.EndOfStream; | |
| 67 | } | |
| 68 | ||
| 69 | fn eof_posReadVec(ctx: ?*anyopaque, data: []const []u8, offset: u64) anyerror!Reader.Status { | |
| 70 | _ = ctx; | |
| 71 | _ = data; | |
| 72 | _ = offset; | |
| 73 | return error.EndOfStream; | |
| 74 | } | |
| 75 | ||
| 76 | fn eof_streamRead(ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Reader.Limit) Reader.Status { | |
| 77 | _ = ctx; | |
| 78 | _ = bw; | |
| 79 | _ = limit; | |
| 80 | return error.EndOfStream; | |
| 81 | } | |
| 82 | ||
| 83 | fn eof_streamReadVec(ctx: ?*anyopaque, data: []const []u8) Reader.Status { | |
| 84 | _ = ctx; | |
| 85 | _ = data; | |
| 86 | return error.EndOfStream; | |
| 87 | } | |
| 88 | ||
| 26 | 89 | /// Constructs `br` such that it will read from `buffer` and then end. |
| 27 | 90 | pub fn initFixed(br: *BufferedReader, buffer: []const u8) void { |
| 28 | 91 | br.* = .{ |
| ... | ... | @@ -31,19 +94,16 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void { |
| 31 | 94 | .buffer = .initBuffer(@constCast(buffer)), |
| 32 | 95 | .unbuffered_writer = .{ |
| 33 | 96 | .context = undefined, |
| 34 | .vtable = &std.io.Writer.VTable.eof, | |
| 97 | .vtable = &eof_writer, | |
| 35 | 98 | }, |
| 36 | 99 | }, |
| 37 | .unbuffered_reader = &.{ | |
| 38 | .context = undefined, | |
| 39 | .vtable = &std.io.Reader.VTable.eof, | |
| 40 | }, | |
| 100 | .unbuffered_reader = &.{ .context = undefined, .vtable = &eof_reader }, | |
| 41 | 101 | }; |
| 42 | 102 | } |
| 43 | 103 | |
| 44 | 104 | pub fn storageBuffer(br: *BufferedReader) []u8 { |
| 45 | assert(br.storage.unbuffered_writer.vtable == &std.io.Writer.VTable.eof); | |
| 46 | assert(br.unbuffered_reader.vtable == &std.io.Reader.VTable.eof); | |
| 105 | assert(br.storage.unbuffered_writer.vtable == &eof_writer); | |
| 106 | assert(br.unbuffered_reader.vtable == &eof_reader); | |
| 47 | 107 | return br.storage.buffer.allocatedSlice(); |
| 48 | 108 | } |
| 49 | 109 |
lib/std/io/BufferedWriter.zig+51-130| ... | ... | @@ -43,7 +43,7 @@ const fixed_vtable: Writer.VTable = .{ |
| 43 | 43 | }; |
| 44 | 44 | |
| 45 | 45 | /// Replaces the `BufferedWriter` with a new one that writes to `buffer` and |
| 46 | /// then ends when it is full. | |
| 46 | /// returns `error.NoSpaceLeft` when it is full. | |
| 47 | 47 | pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void { |
| 48 | 48 | bw.* = .{ |
| 49 | 49 | .unbuffered_writer = .{ |
| ... | ... | @@ -86,25 +86,25 @@ pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 { |
| 86 | 86 | return cap_slice; |
| 87 | 87 | } |
| 88 | 88 | const buffer = list.items; |
| 89 | const result = bw.unbuffered_writer.write(buffer); | |
| 90 | if (result.len == buffer.len) { | |
| 89 | const n = try bw.unbuffered_writer.write(buffer); | |
| 90 | if (n == buffer.len) { | |
| 91 | 91 | @branchHint(.likely); |
| 92 | 92 | list.items.len = 0; |
| 93 | try result.err; | |
| 94 | 93 | return list.unusedCapacitySlice(); |
| 95 | 94 | } |
| 96 | if (result.len > 0) { | |
| 97 | const remainder = buffer[result.len..]; | |
| 95 | if (n > 0) { | |
| 96 | const remainder = buffer[n..]; | |
| 98 | 97 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 99 | 98 | list.items.len = remainder.len; |
| 100 | 99 | } |
| 101 | try result.err; | |
| 102 | 100 | return list.unusedCapacitySlice(); |
| 103 | 101 | } |
| 104 | 102 | |
| 105 | 103 | /// After calling `writableSlice`, this function tracks how many bytes were written to it. |
| 106 | 104 | pub fn advance(bw: *BufferedWriter, n: usize) void { |
| 107 | bw.items.len += n; | |
| 105 | const list = &bw.buffer; | |
| 106 | list.items.len += n; | |
| 107 | assert(list.items.len <= list.capacity); | |
| 108 | 108 | } |
| 109 | 109 | |
| 110 | 110 | /// The `data` parameter is mutable because this function needs to mutate the |
| ... | ... | @@ -122,15 +122,15 @@ pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void { |
| 122 | 122 | } |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) Writer.Result { | |
| 125 | pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) anyerror!usize { | |
| 126 | 126 | return passthru_writeSplat(bw, data, splat); |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | pub fn writev(bw: *BufferedWriter, data: []const []const u8) Writer.Result { | |
| 129 | pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize { | |
| 130 | 130 | return passthru_writeSplat(bw, data, 1); |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Result { | |
| 133 | fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 134 | 134 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 135 | 135 | const list = &bw.buffer; |
| 136 | 136 | const buffer = list.allocatedSlice(); |
| ... | ... | @@ -156,45 +156,27 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us |
| 156 | 156 | if (len >= remaining_data.len) { |
| 157 | 157 | @branchHint(.likely); |
| 158 | 158 | // Made it past the headers, so we can enable splatting. |
| 159 | const result = bw.unbuffered_writer.writeSplat(send_buffers, splat); | |
| 160 | const n = result.len; | |
| 159 | const n = try bw.unbuffered_writer.writeSplat(send_buffers, splat); | |
| 161 | 160 | if (n < end) { |
| 162 | 161 | @branchHint(.unlikely); |
| 163 | 162 | const remainder = buffer[n..end]; |
| 164 | 163 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 165 | 164 | list.items.len = remainder.len; |
| 166 | return .{ | |
| 167 | .err = result.err, | |
| 168 | .len = end - start_end, | |
| 169 | .end = result.end, | |
| 170 | }; | |
| 165 | return end - start_end; | |
| 171 | 166 | } |
| 172 | 167 | list.items.len = 0; |
| 173 | return .{ | |
| 174 | .err = result.err, | |
| 175 | .len = n - start_end, | |
| 176 | .end = result.end, | |
| 177 | }; | |
| 168 | return n - start_end; | |
| 178 | 169 | } |
| 179 | const result = try bw.unbuffered_writer.writeSplat(send_buffers, 1); | |
| 180 | const n = result.len; | |
| 170 | const n = try bw.unbuffered_writer.writeSplat(send_buffers, 1); | |
| 181 | 171 | if (n < end) { |
| 182 | 172 | @branchHint(.unlikely); |
| 183 | 173 | const remainder = buffer[n..end]; |
| 184 | 174 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 185 | 175 | list.items.len = remainder.len; |
| 186 | return .{ | |
| 187 | .err = result.err, | |
| 188 | .len = end - start_end, | |
| 189 | .end = result.end, | |
| 190 | }; | |
| 176 | return end - start_end; | |
| 191 | 177 | } |
| 192 | 178 | list.items.len = 0; |
| 193 | return .{ | |
| 194 | .err = result.err, | |
| 195 | .len = n - start_end, | |
| 196 | .end = result.end, | |
| 197 | }; | |
| 179 | return n - start_end; | |
| 198 | 180 | } |
| 199 | 181 | |
| 200 | 182 | const pattern = data[data.len - 1]; |
| ... | ... | @@ -204,7 +186,7 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us |
| 204 | 186 | // It was added in the loop above; undo it here. |
| 205 | 187 | end -= pattern.len; |
| 206 | 188 | list.items.len = end; |
| 207 | return .{ .len = end - start_end }; | |
| 189 | return end - start_end; | |
| 208 | 190 | } |
| 209 | 191 | |
| 210 | 192 | const remaining_splat = splat - 1; |
| ... | ... | @@ -212,7 +194,7 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us |
| 212 | 194 | switch (pattern.len) { |
| 213 | 195 | 0 => { |
| 214 | 196 | list.items.len = end; |
| 215 | return .{ .len = end - start_end }; | |
| 197 | return end - start_end; | |
| 216 | 198 | }, |
| 217 | 199 | 1 => { |
| 218 | 200 | const new_end = end + remaining_splat; |
| ... | ... | @@ -220,29 +202,20 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us |
| 220 | 202 | @branchHint(.likely); |
| 221 | 203 | @memset(buffer[end..new_end], pattern[0]); |
| 222 | 204 | list.items.len = new_end; |
| 223 | return .{ .len = new_end - start_end }; | |
| 205 | return new_end - start_end; | |
| 224 | 206 | } |
| 225 | 207 | buffers[0] = buffer[0..end]; |
| 226 | 208 | buffers[1] = pattern; |
| 227 | const result = bw.unbuffered_writer.writeSplat(buffers[0..2], remaining_splat); | |
| 228 | const n = result.len; | |
| 209 | const n = try bw.unbuffered_writer.writeSplat(buffers[0..2], remaining_splat); | |
| 229 | 210 | if (n < end) { |
| 230 | 211 | @branchHint(.unlikely); |
| 231 | 212 | const remainder = buffer[n..end]; |
| 232 | 213 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 233 | 214 | list.items.len = remainder.len; |
| 234 | return .{ | |
| 235 | .err = result.err, | |
| 236 | .len = end - start_end, | |
| 237 | .end = result.end, | |
| 238 | }; | |
| 215 | return end - start_end; | |
| 239 | 216 | } |
| 240 | 217 | list.items.len = 0; |
| 241 | return .{ | |
| 242 | .err = result.err, | |
| 243 | .len = n - start_end, | |
| 244 | .end = result.end, | |
| 245 | }; | |
| 218 | return n - start_end; | |
| 246 | 219 | }, |
| 247 | 220 | else => { |
| 248 | 221 | const new_end = end + pattern.len * remaining_splat; |
| ... | ... | @@ -252,29 +225,20 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us |
| 252 | 225 | @memcpy(buffer[end..][0..pattern.len], pattern); |
| 253 | 226 | } |
| 254 | 227 | list.items.len = new_end; |
| 255 | return .{ .len = new_end - start_end }; | |
| 228 | return new_end - start_end; | |
| 256 | 229 | } |
| 257 | 230 | buffers[0] = buffer[0..end]; |
| 258 | 231 | buffers[1] = pattern; |
| 259 | const result = bw.unbuffered_writer.writeSplat(buffers[0..2], remaining_splat); | |
| 260 | const n = result.len; | |
| 232 | const n = try bw.unbuffered_writer.writeSplat(buffers[0..2], remaining_splat); | |
| 261 | 233 | if (n < end) { |
| 262 | 234 | @branchHint(.unlikely); |
| 263 | 235 | const remainder = buffer[n..end]; |
| 264 | 236 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 265 | 237 | list.items.len = remainder.len; |
| 266 | return .{ | |
| 267 | .err = result.err, | |
| 268 | .len = end - start_end, | |
| 269 | .end = result.end, | |
| 270 | }; | |
| 238 | return end - start_end; | |
| 271 | 239 | } |
| 272 | 240 | list.items.len = 0; |
| 273 | return .{ | |
| 274 | .err = result.err, | |
| 275 | .len = n - start_end, | |
| 276 | .end = result.end, | |
| 277 | }; | |
| 241 | return n - start_end; | |
| 278 | 242 | }, |
| 279 | 243 | } |
| 280 | 244 | } |
| ... | ... | @@ -282,13 +246,12 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us |
| 282 | 246 | /// When this function is called it means the buffer got full, so it's time |
| 283 | 247 | /// to return an error. However, we still need to make sure all of the |
| 284 | 248 | /// available buffer has been filled. |
| 285 | fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Result { | |
| 249 | fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 286 | 250 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 287 | 251 | const list = &bw.buffer; |
| 288 | const start_len = list.items.len; | |
| 289 | 252 | for (data) |bytes| { |
| 290 | 253 | const dest = list.unusedCapacitySlice(); |
| 291 | if (dest.len == 0) return .{ .len = list.items.len - start_len, .end = true }; | |
| 254 | if (dest.len == 0) return error.NoSpaceLeft; | |
| 292 | 255 | const len = @min(bytes.len, dest.len); |
| 293 | 256 | @memcpy(dest[0..len], bytes[0..len]); |
| 294 | 257 | list.items.len += len; |
| ... | ... | @@ -301,60 +264,43 @@ fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize |
| 301 | 264 | else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern), |
| 302 | 265 | } |
| 303 | 266 | list.items.len = list.capacity; |
| 304 | return .{ .len = list.items.len - start_len, .end = true }; | |
| 267 | return error.NoSpaceLeft; | |
| 305 | 268 | } |
| 306 | 269 | |
| 307 | pub fn write(bw: *BufferedWriter, bytes: []const u8) Writer.Result { | |
| 270 | pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize { | |
| 308 | 271 | const list = &bw.buffer; |
| 309 | 272 | const buffer = list.allocatedSlice(); |
| 310 | 273 | const end = list.items.len; |
| 311 | 274 | const new_end = end + bytes.len; |
| 312 | 275 | if (new_end > buffer.len) { |
| 313 | 276 | var data: [2][]const u8 = .{ buffer[0..end], bytes }; |
| 314 | const result = bw.unbuffered_writer.writev(&data); | |
| 315 | const n = result.len; | |
| 277 | const n = try bw.unbuffered_writer.writev(&data); | |
| 316 | 278 | if (n < end) { |
| 317 | 279 | @branchHint(.unlikely); |
| 318 | 280 | const remainder = buffer[n..end]; |
| 319 | 281 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 320 | 282 | list.items.len = remainder.len; |
| 321 | return .{ | |
| 322 | .err = result.err, | |
| 323 | .len = 0, | |
| 324 | .end = result.end, | |
| 325 | }; | |
| 283 | return 0; | |
| 326 | 284 | } |
| 327 | 285 | list.items.len = 0; |
| 328 | return .{ | |
| 329 | .err = result.err, | |
| 330 | .len = n - end, | |
| 331 | .end = result.end, | |
| 332 | }; | |
| 286 | return n - end; | |
| 333 | 287 | } |
| 334 | 288 | @memcpy(buffer[end..new_end], bytes); |
| 335 | 289 | list.items.len = new_end; |
| 336 | 290 | return bytes.len; |
| 337 | 291 | } |
| 338 | 292 | |
| 339 | pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) anyerror!void { | |
| 340 | if ((try writeUntilEnd(bw, bytes)) != bytes.len) return error.WriteStreamEnd; | |
| 341 | } | |
| 342 | ||
| 293 | /// Convenience function that calls `writeAll` and then returns `bytes.len`. | |
| 343 | 294 | pub fn writeAllCount(bw: *BufferedWriter, bytes: []const u8) anyerror!usize { |
| 344 | 295 | try writeAll(bw, bytes); |
| 345 | 296 | return bytes.len; |
| 346 | 297 | } |
| 347 | 298 | |
| 348 | /// If the number returned is less than `bytes.len` it indicates end of stream. | |
| 349 | pub fn writeUntilEnd(bw: *BufferedWriter, bytes: []const u8) anyerror!usize { | |
| 299 | /// Calls `write` as many times as necessary such that all of `bytes` are | |
| 300 | /// transferred. | |
| 301 | pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) anyerror!void { | |
| 350 | 302 | var index: usize = 0; |
| 351 | while (true) { | |
| 352 | const result = write(bw, bytes[index..]); | |
| 353 | try result.err; | |
| 354 | index += result.len; | |
| 355 | assert(index <= bytes.len); | |
| 356 | if (index == bytes.len or result.end) return index; | |
| 357 | } | |
| 303 | while (index < bytes.len) index += try write(bw, bytes[index..]); | |
| 358 | 304 | } |
| 359 | 305 | |
| 360 | 306 | pub fn print(bw: *BufferedWriter, comptime format: []const u8, args: anytype) anyerror!void { |
| ... | ... | @@ -365,65 +311,48 @@ pub fn printCount(bw: *BufferedWriter, comptime format: []const u8, args: anytyp |
| 365 | 311 | return std.fmt.format(bw, format, args); |
| 366 | 312 | } |
| 367 | 313 | |
| 368 | pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void { | |
| 369 | if ((try writeByteUntilEnd(bw, byte)) == 0) return error.WriteStreamEnd; | |
| 370 | } | |
| 371 | ||
| 314 | /// Returns 0 or 1 indicating how many bytes were written. | |
| 372 | 315 | pub fn writeByteCount(bw: *BufferedWriter, byte: u8) anyerror!usize { |
| 373 | 316 | try writeByte(bw, byte); |
| 374 | 317 | return 1; |
| 375 | 318 | } |
| 376 | 319 | |
| 377 | /// Returns 0 or 1 indicating how many bytes were written. | |
| 378 | /// `0` means end of stream encountered. | |
| 379 | pub fn writeByteUntilEnd(bw: *BufferedWriter, byte: u8) anyerror!usize { | |
| 320 | pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void { | |
| 380 | 321 | const list = &bw.buffer; |
| 381 | 322 | const buffer = list.items; |
| 382 | 323 | if (buffer.len < list.capacity) { |
| 383 | 324 | @branchHint(.likely); |
| 384 | 325 | buffer.ptr[buffer.len] = byte; |
| 385 | 326 | list.items.len = buffer.len + 1; |
| 386 | return 1; | |
| 327 | return; | |
| 387 | 328 | } |
| 388 | 329 | var buffers: [2][]const u8 = .{ buffer, &.{byte} }; |
| 389 | 330 | while (true) { |
| 390 | const result = bw.unbuffered_writer.writev(&buffers); | |
| 391 | try result.err; | |
| 392 | const n = result.len; | |
| 331 | const n = try bw.unbuffered_writer.writev(&buffers); | |
| 393 | 332 | if (n == 0) { |
| 394 | 333 | @branchHint(.unlikely); |
| 395 | if (result.end) return 0; | |
| 396 | 334 | continue; |
| 397 | 335 | } else if (n >= buffer.len) { |
| 398 | 336 | @branchHint(.likely); |
| 399 | 337 | if (n > buffer.len) { |
| 400 | 338 | @branchHint(.likely); |
| 401 | 339 | list.items.len = 0; |
| 402 | return 1; | |
| 340 | return; | |
| 403 | 341 | } else { |
| 404 | 342 | buffer[0] = byte; |
| 405 | 343 | list.items.len = 1; |
| 406 | return 1; | |
| 344 | return; | |
| 407 | 345 | } |
| 408 | 346 | } |
| 409 | 347 | const remainder = buffer[n..]; |
| 410 | 348 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 411 | 349 | buffer[remainder.len] = byte; |
| 412 | 350 | list.items.len = remainder.len + 1; |
| 413 | return 1; | |
| 351 | return; | |
| 414 | 352 | } |
| 415 | 353 | } |
| 416 | 354 | |
| 417 | /// Writes the same byte many times, performing the underlying write call as | |
| 418 | /// many times as necessary, returning `error.WriteStreamEnd` if the byte | |
| 419 | /// could not be repeated `n` times. | |
| 420 | pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void { | |
| 421 | if ((try splatByteUntilEnd(bw, byte, n)) != n) return error.WriteStreamEnd; | |
| 422 | } | |
| 423 | ||
| 424 | /// Writes the same byte many times, performing the underlying write call as | |
| 425 | /// many times as necessary, returning `error.WriteStreamEnd` if the byte | |
| 426 | /// could not be repeated `n` times, or returning `n` on success. | |
| 355 | /// Convenience function that calls `splatByteAll` and then returns `n`. | |
| 427 | 356 | pub fn splatByteAllCount(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize { |
| 428 | 357 | try splatByteAll(bw, byte, n); |
| 429 | 358 | return n; |
| ... | ... | @@ -431,23 +360,15 @@ pub fn splatByteAllCount(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize |
| 431 | 360 | |
| 432 | 361 | /// Writes the same byte many times, performing the underlying write call as |
| 433 | 362 | /// many times as necessary. |
| 434 | /// | |
| 435 | /// If the number returned is less than `n` it indicates end of stream. | |
| 436 | pub fn splatByteUntilEnd(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize { | |
| 437 | var index: usize = 0; | |
| 438 | while (true) { | |
| 439 | const result = splatByte(bw, byte, n - index); | |
| 440 | try result.err; | |
| 441 | index += result.len; | |
| 442 | assert(index <= n); | |
| 443 | if (index == n or result.end) return index; | |
| 444 | } | |
| 363 | pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void { | |
| 364 | var remaining: usize = n; | |
| 365 | while (remaining > 0) remaining -= try splatByte(bw, byte, remaining); | |
| 445 | 366 | } |
| 446 | 367 | |
| 447 | 368 | /// Writes the same byte many times, allowing short writes. |
| 448 | 369 | /// |
| 449 | 370 | /// Does maximum of one underlying `Writer.VTable.writeSplat`. |
| 450 | pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) Writer.Result { | |
| 371 | pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize { | |
| 451 | 372 | return passthru_writeSplat(bw, &.{&.{byte}}, n); |
| 452 | 373 | } |
| 453 | 374 |
lib/std/io/Reader.zig+11-46| ... | ... | @@ -19,8 +19,8 @@ pub const VTable = struct { |
| 19 | 19 | /// |
| 20 | 20 | /// If this is `null` it is equivalent to always returning |
| 21 | 21 | /// `error.Unseekable`. |
| 22 | posRead: ?*const fn (ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Limit, offset: u64) RwResult, | |
| 23 | posReadVec: ?*const fn (ctx: ?*anyopaque, data: []const []u8, offset: u64) Result, | |
| 22 | posRead: ?*const fn (ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Limit, offset: u64) anyerror!Status, | |
| 23 | posReadVec: ?*const fn (ctx: ?*anyopaque, data: []const []u8, offset: u64) anyerror!Status, | |
| 24 | 24 | |
| 25 | 25 | /// Writes bytes from the internally tracked stream position to `bw`, or |
| 26 | 26 | /// returns `error.Unstreamable`, indicating `posRead` should be used |
| ... | ... | @@ -37,25 +37,18 @@ pub const VTable = struct { |
| 37 | 37 | /// |
| 38 | 38 | /// If this is `null` it is equivalent to always returning |
| 39 | 39 | /// `error.Unstreamable`. |
| 40 | streamRead: ?*const fn (ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Limit) RwResult, | |
| 41 | streamReadVec: ?*const fn (ctx: ?*anyopaque, data: []const []u8) Result, | |
| 42 | ||
| 43 | pub const eof: VTable = .{ | |
| 44 | .posRead = eof_posRead, | |
| 45 | .posReadVec = eof_posReadVec, | |
| 46 | .streamRead = eof_streamRead, | |
| 47 | .streamReadVec = eof_streamReadVec, | |
| 48 | }; | |
| 40 | streamRead: ?*const fn (ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Limit) anyerror!Status, | |
| 41 | streamReadVec: ?*const fn (ctx: ?*anyopaque, data: []const []u8) anyerror!Status, | |
| 49 | 42 | }; |
| 50 | 43 | |
| 51 | pub const Result = std.io.Writer.Result; | |
| 44 | pub const Len = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 } }); | |
| 52 | 45 | |
| 53 | pub const RwResult = struct { | |
| 54 | len: usize = 0, | |
| 55 | read_err: anyerror!void = {}, | |
| 56 | write_err: anyerror!void = {}, | |
| 57 | read_end: bool = false, | |
| 58 | write_end: bool = false, | |
| 46 | pub const Status = packed struct(usize) { | |
| 47 | /// Number of bytes that were transferred. Zero does not mean end of | |
| 48 | /// stream. | |
| 49 | len: Len = 0, | |
| 50 | /// Indicates end of stream. | |
| 51 | end: bool = false, | |
| 59 | 52 | }; |
| 60 | 53 | |
| 61 | 54 | pub const Limit = enum(usize) { |
| ... | ... | @@ -171,31 +164,3 @@ test "when the backing reader provides one byte at a time" { |
| 171 | 164 | defer std.testing.allocator.free(res); |
| 172 | 165 | try std.testing.expectEqualStrings(str, res); |
| 173 | 166 | } |
| 174 | ||
| 175 | fn eof_posRead(ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Limit, offset: u64) RwResult { | |
| 176 | _ = ctx; | |
| 177 | _ = bw; | |
| 178 | _ = limit; | |
| 179 | _ = offset; | |
| 180 | return .{ .end = true }; | |
| 181 | } | |
| 182 | ||
| 183 | fn eof_posReadVec(ctx: ?*anyopaque, data: []const []u8, offset: u64) Result { | |
| 184 | _ = ctx; | |
| 185 | _ = data; | |
| 186 | _ = offset; | |
| 187 | return .{ .end = true }; | |
| 188 | } | |
| 189 | ||
| 190 | fn eof_streamRead(ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Limit) RwResult { | |
| 191 | _ = ctx; | |
| 192 | _ = bw; | |
| 193 | _ = limit; | |
| 194 | return .{ .end = true }; | |
| 195 | } | |
| 196 | ||
| 197 | fn eof_streamReadVec(ctx: ?*anyopaque, data: []const []u8) Result { | |
| 198 | _ = ctx; | |
| 199 | _ = data; | |
| 200 | return .{ .end = true }; | |
| 201 | } |
lib/std/io/Writer.zig+10-50| ... | ... | @@ -17,7 +17,7 @@ pub const VTable = struct { |
| 17 | 17 | /// Number of bytes returned may be zero, which does not mean |
| 18 | 18 | /// end-of-stream. A subsequent call may return nonzero, or may signal end |
| 19 | 19 | /// of stream via an error. |
| 20 | writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) Result, | |
| 20 | writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize, | |
| 21 | 21 | |
| 22 | 22 | /// Writes contents from an open file. `headers` are written first, then `len` |
| 23 | 23 | /// bytes of `file` starting from `offset`, then `trailers`. |
| ... | ... | @@ -38,23 +38,7 @@ pub const VTable = struct { |
| 38 | 38 | /// zero, they can be forwarded directly to `VTable.writev`. |
| 39 | 39 | headers_and_trailers: []const []const u8, |
| 40 | 40 | headers_len: usize, |
| 41 | ) Result, | |
| 42 | ||
| 43 | pub const eof: VTable = .{ | |
| 44 | .writeSplat = eof_writeSplat, | |
| 45 | .writeFile = eof_writeFile, | |
| 46 | }; | |
| 47 | }; | |
| 48 | ||
| 49 | pub const Result = struct { | |
| 50 | /// Even when a failure occurs, `len` may be nonzero, and `end` may be | |
| 51 | /// true. | |
| 52 | err: anyerror!void = {}, | |
| 53 | /// Number of bytes that were transferred. When an error occurs, ideally | |
| 54 | /// this will be zero, but may not always be the case. | |
| 55 | len: usize = 0, | |
| 56 | /// Indicates end of stream. | |
| 57 | end: bool = false, | |
| 41 | ) anyerror!usize, | |
| 58 | 42 | }; |
| 59 | 43 | |
| 60 | 44 | pub const Offset = enum(u64) { |
| ... | ... | @@ -90,11 +74,11 @@ pub const FileLen = enum(u64) { |
| 90 | 74 | } |
| 91 | 75 | }; |
| 92 | 76 | |
| 93 | pub fn writev(w: Writer, data: []const []const u8) Result { | |
| 77 | pub fn writev(w: Writer, data: []const []const u8) anyerror!usize { | |
| 94 | 78 | return w.vtable.writeSplat(w.context, data, 1); |
| 95 | 79 | } |
| 96 | 80 | |
| 97 | pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) Result { | |
| 81 | pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) anyerror!usize { | |
| 98 | 82 | return w.vtable.writeSplat(w.context, data, splat); |
| 99 | 83 | } |
| 100 | 84 | |
| ... | ... | @@ -105,7 +89,7 @@ pub fn writeFile( |
| 105 | 89 | len: FileLen, |
| 106 | 90 | headers_and_trailers: []const []const u8, |
| 107 | 91 | headers_len: usize, |
| 108 | ) Result { | |
| 92 | ) anyerror!usize { | |
| 109 | 93 | return w.vtable.writeFile(w.context, file, offset, len, headers_and_trailers, headers_len); |
| 110 | 94 | } |
| 111 | 95 | |
| ... | ... | @@ -116,14 +100,14 @@ pub fn unimplemented_writeFile( |
| 116 | 100 | len: FileLen, |
| 117 | 101 | headers_and_trailers: []const []const u8, |
| 118 | 102 | headers_len: usize, |
| 119 | ) Result { | |
| 103 | ) anyerror!usize { | |
| 120 | 104 | _ = context; |
| 121 | 105 | _ = file; |
| 122 | 106 | _ = offset; |
| 123 | 107 | _ = len; |
| 124 | 108 | _ = headers_and_trailers; |
| 125 | 109 | _ = headers_len; |
| 126 | return .{ .err = error.Unimplemented }; | |
| 110 | return error.Unimplemented; | |
| 127 | 111 | } |
| 128 | 112 | |
| 129 | 113 | pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter { |
| ... | ... | @@ -146,7 +130,7 @@ pub const @"null": Writer = .{ |
| 146 | 130 | }, |
| 147 | 131 | }; |
| 148 | 132 | |
| 149 | fn null_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Result { | |
| 133 | fn null_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 150 | 134 | _ = context; |
| 151 | 135 | const headers = data[0 .. data.len - 1]; |
| 152 | 136 | const pattern = data[headers.len..]; |
| ... | ... | @@ -162,14 +146,14 @@ fn null_writeFile( |
| 162 | 146 | len: FileLen, |
| 163 | 147 | headers_and_trailers: []const []const u8, |
| 164 | 148 | headers_len: usize, |
| 165 | ) Result { | |
| 149 | ) anyerror!usize { | |
| 166 | 150 | _ = context; |
| 167 | 151 | var n: usize = 0; |
| 168 | 152 | if (len == .entire_file) { |
| 169 | 153 | const headers = headers_and_trailers[0..headers_len]; |
| 170 | 154 | for (headers) |bytes| n += bytes.len; |
| 171 | 155 | if (offset.toInt()) |off| { |
| 172 | const stat = file.stat() catch |err| return .{ .err = err, .len = n }; | |
| 156 | const stat = try file.stat(); | |
| 173 | 157 | n += stat.size - off; |
| 174 | 158 | for (headers_and_trailers[headers_len..]) |bytes| n += bytes.len; |
| 175 | 159 | return .{ .len = n }; |
| ... | ... | @@ -183,27 +167,3 @@ fn null_writeFile( |
| 183 | 167 | test @"null" { |
| 184 | 168 | try @"null".writeAll("yay"); |
| 185 | 169 | } |
| 186 | ||
| 187 | fn eof_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Result { | |
| 188 | _ = context; | |
| 189 | _ = data; | |
| 190 | _ = splat; | |
| 191 | return .{ .end = true }; | |
| 192 | } | |
| 193 | ||
| 194 | fn eof_writeFile( | |
| 195 | context: ?*anyopaque, | |
| 196 | file: std.fs.File, | |
| 197 | offset: u64, | |
| 198 | len: FileLen, | |
| 199 | headers_and_trailers: []const []const u8, | |
| 200 | headers_len: usize, | |
| 201 | ) Result { | |
| 202 | _ = context; | |
| 203 | _ = file; | |
| 204 | _ = offset; | |
| 205 | _ = len; | |
| 206 | _ = headers_and_trailers; | |
| 207 | _ = headers_len; | |
| 208 | return .{ .end = true }; | |
| 209 | } |
lib/std/posix.zig+4-3| ... | ... | @@ -6359,7 +6359,7 @@ pub const SendFileError = PReadError || WriteError || SendError; |
| 6359 | 6359 | pub fn sendfile( |
| 6360 | 6360 | out_fd: fd_t, |
| 6361 | 6361 | in_fd: fd_t, |
| 6362 | in_offset: u64, | |
| 6362 | in_offset: ?u64, | |
| 6363 | 6363 | in_len: u64, |
| 6364 | 6364 | headers: []const iovec_const, |
| 6365 | 6365 | trailers: []const iovec_const, |
| ... | ... | @@ -6390,8 +6390,9 @@ pub fn sendfile( |
| 6390 | 6390 | |
| 6391 | 6391 | const sendfile_sym = if (lfs64_abi) system.sendfile64 else system.sendfile; |
| 6392 | 6392 | while (true) { |
| 6393 | var offset: off_t = @bitCast(in_offset); | |
| 6394 | const rc = sendfile_sym(out_fd, in_fd, &offset, adjusted_count); | |
| 6393 | var offset: off_t = if (in_offset) |o| o else undefined; | |
| 6394 | const offset_pointer: ?*off_t = if (in_offset) &offset else null; | |
| 6395 | const rc = sendfile_sym(out_fd, in_fd, offset_pointer, adjusted_count); | |
| 6395 | 6396 | switch (errno(rc)) { |
| 6396 | 6397 | .SUCCESS => { |
| 6397 | 6398 | const amt: usize = @bitCast(rc); |