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 {
326326 /// returned bytes means end of stream reached. With limit=0 returns as
327327 /// much data it can. It newer will be more than 65536 bytes, which is
328328 /// size of internal buffer.
329 /// TODO merge this logic into reader_streamRead and reader_streamReadVec
329330 pub fn get(self: *Self, limit: usize) Error![]const u8 {
330331 while (true) {
331332 const out = self.hist.readAtMost(limit);
......@@ -342,31 +343,27 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
342343 ctx: ?*anyopaque,
343344 bw: *std.io.BufferedWriter,
344345 limit: std.io.Reader.Limit,
345 ) std.io.Reader.RwResult {
346 ) anyerror!std.io.Reader.Status {
346347 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));
350350 @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 };
352353 }
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 {
355356 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 };
365362 }
366 return .{ .len = total, .end = total == 0 };
363 return .{};
367364 }
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 {
370367 return reader_streamReadVec(self, data);
371368 }
372369
lib/std/debug/Dwarf.zig+5-6
......@@ -2247,16 +2247,15 @@ pub const ElfModule = struct {
22472247 errdefer gpa.free(decompressed_section);
22482248
22492249 {
2250 var read_index: usize = 0;
2250 var i: usize = 0;
22512251 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 {
22542253 gpa.free(decompressed_section);
22552254 continue :shdrs;
22562255 };
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) {
22602259 gpa.free(decompressed_section);
22612260 continue :shdrs;
22622261 }
lib/std/debug/SelfInfo.zig+1-1
......@@ -2034,7 +2034,7 @@ pub const VirtualMachine = struct {
20342034 const streams: [2]*std.io.BufferedReader = .{ &cie_stream, &fde_stream };
20352035
20362036 for (&streams, 0..) |stream, i| {
2037 while (stream.seek < stream.buffer.len) {
2037 while (stream.seek < stream.storageBuffer().len) {
20382038 const instruction = try std.debug.Dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
20392039 prev_row = try self.step(allocator, cie, i == 0, instruction);
20402040 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
14991499 error.FileDescriptorNotASocket,
15001500 error.NetworkUnreachable,
15011501 error.NetworkSubsystemFailed,
1502 => return self.writeFileAllUnseekable(in_file, args),
1502 => return self.writeFileUnseekableAll(in_file, args),
15031503
15041504 else => |e| return e,
15051505 };
......@@ -1507,53 +1507,11 @@ pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFile
15071507
15081508/// Does not try seeking in either of the File parameters.
15091509/// See `writeFileAll` as an alternative to calling this.
1510pub 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
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);
1510pub 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");
15571515}
15581516
15591517/// Low level function which can fail for OS-specific reasons.
......@@ -1635,6 +1593,30 @@ pub fn reader(file: File) std.io.Reader {
16351593 };
16361594}
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
16381620pub fn writer(file: File) std.io.Writer {
16391621 return .{
16401622 .context = handleToOpaque(file.handle),
......@@ -1692,12 +1674,12 @@ pub fn reader_streamReadVec(context: ?*anyopaque, data: []const []u8) anyerror!s
16921674 };
16931675}
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 {
16961678 const file = opaqueToHandle(context);
16971679 var splat_buffer: [256]u8 = undefined;
16981680 if (is_windows) {
16991681 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);
17011683 }
17021684 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
17031685 var len: usize = @min(iovecs.len, data.len);
......@@ -1706,8 +1688,8 @@ pub fn writer_writeSplat(context: ?*anyopaque, data: []const []const u8, splat:
17061688 .len = d.len,
17071689 };
17081690 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]),
17111693 else => {
17121694 const pattern = data[data.len - 1];
17131695 if (pattern.len == 1) {
......@@ -1725,21 +1707,21 @@ pub fn writer_writeSplat(context: ?*anyopaque, data: []const []const u8, splat:
17251707 iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat };
17261708 len += 1;
17271709 }
1728 return .{ .len = std.posix.writev(file, iovecs[0..len]) catch |err| return .{ .err = err } };
1710 return std.posix.writev(file, iovecs[0..len]);
17291711 }
17301712 },
17311713 }
1732 return .{ .len = std.posix.writev(file, iovecs[0..len]) catch |err| return .{ .err = err } };
1714 return std.posix.writev(file, iovecs[0..len]);
17331715}
17341716
17351717pub fn writer_writeFile(
17361718 context: ?*anyopaque,
17371719 in_file: std.fs.File,
1738 in_offset: u64,
1720 in_offset: std.io.Writer.Offset,
17391721 in_len: std.io.Writer.FileLen,
17401722 headers_and_trailers: []const []const u8,
17411723 headers_len: usize,
1742) std.io.Writer.Result {
1724) anyerror!usize {
17431725 const out_fd = opaqueToHandle(context);
17441726 const in_fd = in_file.handle;
17451727 const len_int = switch (in_len) {
......@@ -1747,6 +1729,26 @@ pub fn writer_writeFile(
17471729 .entire_file => 0,
17481730 else => in_len.int(),
17491731 };
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 }
17501752 var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined;
17511753 const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, headers_and_trailers.len)];
17521754 for (iovecs, headers_and_trailers[0..iovecs.len]) |*v, d| v.* = .{ .base = d.ptr, .len = d.len };
......@@ -1783,7 +1785,7 @@ fn writeFileUnseekable(
17831785 @panic("TODO writeFileUnseekable");
17841786}
17851787
1786fn handleToOpaque(handle: Handle) *anyopaque {
1788fn handleToOpaque(handle: Handle) ?*anyopaque {
17871789 return switch (@typeInfo(Handle)) {
17881790 .pointer => @ptrCast(handle),
17891791 .int => @ptrFromInt(@as(u32, @bitCast(handle))),
......@@ -1791,7 +1793,7 @@ fn handleToOpaque(handle: Handle) *anyopaque {
17911793 };
17921794}
17931795
1794fn opaqueToHandle(userdata: *anyopaque) Handle {
1796fn opaqueToHandle(userdata: ?*anyopaque) Handle {
17951797 return switch (@typeInfo(Handle)) {
17961798 .pointer => @ptrCast(userdata),
17971799 .int => @intCast(@intFromPtr(userdata)),
......@@ -1976,9 +1978,13 @@ pub fn downgradeLock(file: File) LockError!void {
19761978 }
19771979}
19781980
1981const builtin = @import("builtin");
1982const Os = std.builtin.Os;
1983const native_os = builtin.os.tag;
1984const is_windows = native_os == .windows;
1985
19791986const File = @This();
19801987const std = @import("../std.zig");
1981const builtin = @import("builtin");
19821988const Allocator = std.mem.Allocator;
19831989const posix = std.posix;
19841990const io = std.io;
......@@ -1986,7 +1992,5 @@ const math = std.math;
19861992const assert = std.debug.assert;
19871993const linux = std.os.linux;
19881994const windows = std.os.windows;
1989const Os = std.builtin.Os;
19901995const maxInt = std.math.maxInt;
1991const is_windows = builtin.os.tag == .windows;
19921996const 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 {
2323 br.storage.initFixed(buffer);
2424}
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
2689/// Constructs `br` such that it will read from `buffer` and then end.
2790pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
2891 br.* = .{
......@@ -31,19 +94,16 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
3194 .buffer = .initBuffer(@constCast(buffer)),
3295 .unbuffered_writer = .{
3396 .context = undefined,
34 .vtable = &std.io.Writer.VTable.eof,
97 .vtable = &eof_writer,
3598 },
3699 },
37 .unbuffered_reader = &.{
38 .context = undefined,
39 .vtable = &std.io.Reader.VTable.eof,
40 },
100 .unbuffered_reader = &.{ .context = undefined, .vtable = &eof_reader },
41101 };
42102}
43103
44104pub 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);
47107 return br.storage.buffer.allocatedSlice();
48108}
49109
lib/std/io/BufferedWriter.zig+51-130
......@@ -43,7 +43,7 @@ const fixed_vtable: Writer.VTable = .{
4343};
4444
4545/// 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.
4747pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {
4848 bw.* = .{
4949 .unbuffered_writer = .{
......@@ -86,25 +86,25 @@ pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 {
8686 return cap_slice;
8787 }
8888 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) {
9191 @branchHint(.likely);
9292 list.items.len = 0;
93 try result.err;
9493 return list.unusedCapacitySlice();
9594 }
96 if (result.len > 0) {
97 const remainder = buffer[result.len..];
95 if (n > 0) {
96 const remainder = buffer[n..];
9897 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
9998 list.items.len = remainder.len;
10099 }
101 try result.err;
102100 return list.unusedCapacitySlice();
103101}
104102
105103/// After calling `writableSlice`, this function tracks how many bytes were written to it.
106104pub 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);
108108}
109109
110110/// 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 {
122122 }
123123}
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 {
126126 return passthru_writeSplat(bw, data, splat);
127127}
128128
129pub fn writev(bw: *BufferedWriter, data: []const []const u8) Writer.Result {
129pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {
130130 return passthru_writeSplat(bw, data, 1);
131131}
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 {
134134 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
135135 const list = &bw.buffer;
136136 const buffer = list.allocatedSlice();
......@@ -156,45 +156,27 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
156156 if (len >= remaining_data.len) {
157157 @branchHint(.likely);
158158 // 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);
161160 if (n < end) {
162161 @branchHint(.unlikely);
163162 const remainder = buffer[n..end];
164163 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
165164 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;
171166 }
172167 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;
178169 }
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);
181171 if (n < end) {
182172 @branchHint(.unlikely);
183173 const remainder = buffer[n..end];
184174 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
185175 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;
191177 }
192178 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;
198180 }
199181
200182 const pattern = data[data.len - 1];
......@@ -204,7 +186,7 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
204186 // It was added in the loop above; undo it here.
205187 end -= pattern.len;
206188 list.items.len = end;
207 return .{ .len = end - start_end };
189 return end - start_end;
208190 }
209191
210192 const remaining_splat = splat - 1;
......@@ -212,7 +194,7 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
212194 switch (pattern.len) {
213195 0 => {
214196 list.items.len = end;
215 return .{ .len = end - start_end };
197 return end - start_end;
216198 },
217199 1 => {
218200 const new_end = end + remaining_splat;
......@@ -220,29 +202,20 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
220202 @branchHint(.likely);
221203 @memset(buffer[end..new_end], pattern[0]);
222204 list.items.len = new_end;
223 return .{ .len = new_end - start_end };
205 return new_end - start_end;
224206 }
225207 buffers[0] = buffer[0..end];
226208 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);
229210 if (n < end) {
230211 @branchHint(.unlikely);
231212 const remainder = buffer[n..end];
232213 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
233214 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;
239216 }
240217 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;
246219 },
247220 else => {
248221 const new_end = end + pattern.len * remaining_splat;
......@@ -252,29 +225,20 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
252225 @memcpy(buffer[end..][0..pattern.len], pattern);
253226 }
254227 list.items.len = new_end;
255 return .{ .len = new_end - start_end };
228 return new_end - start_end;
256229 }
257230 buffers[0] = buffer[0..end];
258231 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);
261233 if (n < end) {
262234 @branchHint(.unlikely);
263235 const remainder = buffer[n..end];
264236 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
265237 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;
271239 }
272240 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;
278242 },
279243 }
280244}
......@@ -282,13 +246,12 @@ fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: us
282246/// When this function is called it means the buffer got full, so it's time
283247/// to return an error. However, we still need to make sure all of the
284248/// 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 {
286250 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
287251 const list = &bw.buffer;
288 const start_len = list.items.len;
289252 for (data) |bytes| {
290253 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;
292255 const len = @min(bytes.len, dest.len);
293256 @memcpy(dest[0..len], bytes[0..len]);
294257 list.items.len += len;
......@@ -301,60 +264,43 @@ fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize
301264 else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern),
302265 }
303266 list.items.len = list.capacity;
304 return .{ .len = list.items.len - start_len, .end = true };
267 return error.NoSpaceLeft;
305268}
306269
307pub fn write(bw: *BufferedWriter, bytes: []const u8) Writer.Result {
270pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
308271 const list = &bw.buffer;
309272 const buffer = list.allocatedSlice();
310273 const end = list.items.len;
311274 const new_end = end + bytes.len;
312275 if (new_end > buffer.len) {
313276 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);
316278 if (n < end) {
317279 @branchHint(.unlikely);
318280 const remainder = buffer[n..end];
319281 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
320282 list.items.len = remainder.len;
321 return .{
322 .err = result.err,
323 .len = 0,
324 .end = result.end,
325 };
283 return 0;
326284 }
327285 list.items.len = 0;
328 return .{
329 .err = result.err,
330 .len = n - end,
331 .end = result.end,
332 };
286 return n - end;
333287 }
334288 @memcpy(buffer[end..new_end], bytes);
335289 list.items.len = new_end;
336290 return bytes.len;
337291}
338292
339pub 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`.
343294pub fn writeAllCount(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
344295 try writeAll(bw, bytes);
345296 return bytes.len;
346297}
347298
348/// If the number returned is less than `bytes.len` it indicates end of stream.
349pub 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.
301pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) anyerror!void {
350302 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..]);
358304}
359305
360306pub 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
365311 return std.fmt.format(bw, format, args);
366312}
367313
368pub 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.
372315pub fn writeByteCount(bw: *BufferedWriter, byte: u8) anyerror!usize {
373316 try writeByte(bw, byte);
374317 return 1;
375318}
376319
377/// Returns 0 or 1 indicating how many bytes were written.
378/// `0` means end of stream encountered.
379pub fn writeByteUntilEnd(bw: *BufferedWriter, byte: u8) anyerror!usize {
320pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
380321 const list = &bw.buffer;
381322 const buffer = list.items;
382323 if (buffer.len < list.capacity) {
383324 @branchHint(.likely);
384325 buffer.ptr[buffer.len] = byte;
385326 list.items.len = buffer.len + 1;
386 return 1;
327 return;
387328 }
388329 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
389330 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);
393332 if (n == 0) {
394333 @branchHint(.unlikely);
395 if (result.end) return 0;
396334 continue;
397335 } else if (n >= buffer.len) {
398336 @branchHint(.likely);
399337 if (n > buffer.len) {
400338 @branchHint(.likely);
401339 list.items.len = 0;
402 return 1;
340 return;
403341 } else {
404342 buffer[0] = byte;
405343 list.items.len = 1;
406 return 1;
344 return;
407345 }
408346 }
409347 const remainder = buffer[n..];
410348 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
411349 buffer[remainder.len] = byte;
412350 list.items.len = remainder.len + 1;
413 return 1;
351 return;
414352 }
415353}
416354
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.
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.
355/// Convenience function that calls `splatByteAll` and then returns `n`.
427356pub fn splatByteAllCount(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {
428357 try splatByteAll(bw, byte, n);
429358 return n;
......@@ -431,23 +360,15 @@ pub fn splatByteAllCount(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize
431360
432361/// Writes the same byte many times, performing the underlying write call as
433362/// many times as necessary.
434///
435/// If the number returned is less than `n` it indicates end of stream.
436pub 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 }
363pub 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);
445366}
446367
447368/// Writes the same byte many times, allowing short writes.
448369///
449370/// 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 {
451372 return passthru_writeSplat(bw, &.{&.{byte}}, n);
452373}
453374
lib/std/io/Reader.zig+11-46
......@@ -19,8 +19,8 @@ pub const VTable = struct {
1919 ///
2020 /// If this is `null` it is equivalent to always returning
2121 /// `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,
2424
2525 /// Writes bytes from the internally tracked stream position to `bw`, or
2626 /// returns `error.Unstreamable`, indicating `posRead` should be used
......@@ -37,25 +37,18 @@ pub const VTable = struct {
3737 ///
3838 /// If this is `null` it is equivalent to always returning
3939 /// `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,
4942};
5043
51pub const Result = std.io.Writer.Result;
44pub const Len = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 } });
5245
53pub 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,
46pub 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,
5952};
6053
6154pub const Limit = enum(usize) {
......@@ -171,31 +164,3 @@ test "when the backing reader provides one byte at a time" {
171164 defer std.testing.allocator.free(res);
172165 try std.testing.expectEqualStrings(str, res);
173166}
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 {
1717 /// Number of bytes returned may be zero, which does not mean
1818 /// end-of-stream. A subsequent call may return nonzero, or may signal end
1919 /// 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
2222 /// Writes contents from an open file. `headers` are written first, then `len`
2323 /// bytes of `file` starting from `offset`, then `trailers`.
......@@ -38,23 +38,7 @@ pub const VTable = struct {
3838 /// zero, they can be forwarded directly to `VTable.writev`.
3939 headers_and_trailers: []const []const u8,
4040 headers_len: usize,
41 ) Result,
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,
41 ) anyerror!usize,
5842};
5943
6044pub const Offset = enum(u64) {
......@@ -90,11 +74,11 @@ pub const FileLen = enum(u64) {
9074 }
9175};
9276
93pub fn writev(w: Writer, data: []const []const u8) Result {
77pub fn writev(w: Writer, data: []const []const u8) anyerror!usize {
9478 return w.vtable.writeSplat(w.context, data, 1);
9579}
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 {
9882 return w.vtable.writeSplat(w.context, data, splat);
9983}
10084
......@@ -105,7 +89,7 @@ pub fn writeFile(
10589 len: FileLen,
10690 headers_and_trailers: []const []const u8,
10791 headers_len: usize,
108) Result {
92) anyerror!usize {
10993 return w.vtable.writeFile(w.context, file, offset, len, headers_and_trailers, headers_len);
11094}
11195
......@@ -116,14 +100,14 @@ pub fn unimplemented_writeFile(
116100 len: FileLen,
117101 headers_and_trailers: []const []const u8,
118102 headers_len: usize,
119) Result {
103) anyerror!usize {
120104 _ = context;
121105 _ = file;
122106 _ = offset;
123107 _ = len;
124108 _ = headers_and_trailers;
125109 _ = headers_len;
126 return .{ .err = error.Unimplemented };
110 return error.Unimplemented;
127111}
128112
129113pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
......@@ -146,7 +130,7 @@ pub const @"null": Writer = .{
146130 },
147131};
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 {
150134 _ = context;
151135 const headers = data[0 .. data.len - 1];
152136 const pattern = data[headers.len..];
......@@ -162,14 +146,14 @@ fn null_writeFile(
162146 len: FileLen,
163147 headers_and_trailers: []const []const u8,
164148 headers_len: usize,
165) Result {
149) anyerror!usize {
166150 _ = context;
167151 var n: usize = 0;
168152 if (len == .entire_file) {
169153 const headers = headers_and_trailers[0..headers_len];
170154 for (headers) |bytes| n += bytes.len;
171155 if (offset.toInt()) |off| {
172 const stat = file.stat() catch |err| return .{ .err = err, .len = n };
156 const stat = try file.stat();
173157 n += stat.size - off;
174158 for (headers_and_trailers[headers_len..]) |bytes| n += bytes.len;
175159 return .{ .len = n };
......@@ -183,27 +167,3 @@ fn null_writeFile(
183167test @"null" {
184168 try @"null".writeAll("yay");
185169}
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;
63596359pub fn sendfile(
63606360 out_fd: fd_t,
63616361 in_fd: fd_t,
6362 in_offset: u64,
6362 in_offset: ?u64,
63636363 in_len: u64,
63646364 headers: []const iovec_const,
63656365 trailers: []const iovec_const,
......@@ -6390,8 +6390,9 @@ pub fn sendfile(
63906390
63916391 const sendfile_sym = if (lfs64_abi) system.sendfile64 else system.sendfile;
63926392 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);
63956396 switch (errno(rc)) {
63966397 .SUCCESS => {
63976398 const amt: usize = @bitCast(rc);