authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-09 22:08:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:26-07:00
log9dc0b4a98f8efe2d249db74ebe52c6880570b7cc
tree1986b2db5e513ccd7d995c94d202e7690b9c7049
parent6ac7931bec29f1cd4c889d6913a21f28e17c13a8

back to the anyerror!usize vtable


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