WIP land the std.Io interface

fix std lib compilation errors caused by introducing std.Io
This commit is contained in:
Andrew Kelley 2025-10-02 21:39:32 -07:00
parent 85a6fea3be
commit 00f26cb0a4
10 changed files with 111 additions and 3098 deletions

View file

@ -2,12 +2,12 @@ gpa: Allocator,
thread_pool: *std.Thread.Pool, thread_pool: *std.Thread.Pool,
graph: *const Build.Graph, graph: *const Build.Graph,
all_steps: []const *Build.Step, all_steps: []const *Build.Step,
listen_address: std.net.Address, listen_address: net.IpAddress,
ttyconf: std.Io.tty.Config, ttyconf: std.Io.tty.Config,
root_prog_node: std.Progress.Node, root_prog_node: std.Progress.Node,
watch: bool, watch: bool,
tcp_server: ?std.net.Server, tcp_server: ?net.Server,
serve_thread: ?std.Thread, serve_thread: ?std.Thread,
base_timestamp: i128, base_timestamp: i128,
@ -56,7 +56,7 @@ pub const Options = struct {
ttyconf: std.Io.tty.Config, ttyconf: std.Io.tty.Config,
root_prog_node: std.Progress.Node, root_prog_node: std.Progress.Node,
watch: bool, watch: bool,
listen_address: std.net.Address, listen_address: net.IpAddress,
}; };
pub fn init(opts: Options) WebServer { pub fn init(opts: Options) WebServer {
// The upcoming `std.Io` interface should allow us to use `Io.async` and `Io.concurrent` // The upcoming `std.Io` interface should allow us to use `Io.async` and `Io.concurrent`
@ -244,7 +244,7 @@ pub fn now(s: *const WebServer) i64 {
return @intCast(std.time.nanoTimestamp() - s.base_timestamp); return @intCast(std.time.nanoTimestamp() - s.base_timestamp);
} }
fn accept(ws: *WebServer, connection: std.net.Server.Connection) void { fn accept(ws: *WebServer, connection: net.Server.Connection) void {
defer connection.stream.close(); defer connection.stream.close();
var send_buffer: [4096]u8 = undefined; var send_buffer: [4096]u8 = undefined;
@ -851,5 +851,6 @@ const Cache = Build.Cache;
const Fuzz = Build.Fuzz; const Fuzz = Build.Fuzz;
const abi = Build.abi; const abi = Build.abi;
const http = std.http; const http = std.http;
const net = std.Io.net;
const WebServer = @This(); const WebServer = @This();

View file

@ -1,6 +1,8 @@
const File = @This(); const File = @This();
const builtin = @import("builtin"); const builtin = @import("builtin");
const native_os = builtin.os.tag;
const is_windows = native_os == .windows;
const std = @import("../std.zig"); const std = @import("../std.zig");
const Io = std.Io; const Io = std.Io;
@ -131,6 +133,18 @@ pub const Stat = struct {
} }
}; };
pub fn stdout() File {
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdOutput else std.posix.STDOUT_FILENO };
}
pub fn stderr() File {
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdError else std.posix.STDERR_FILENO };
}
pub fn stdin() File {
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdInput else std.posix.STDIN_FILENO };
}
pub const StatError = std.posix.FStatError || Io.Cancelable; pub const StatError = std.posix.FStatError || Io.Cancelable;
/// Returns `Stat` containing basic information about the `File`. /// Returns `Stat` containing basic information about the `File`.
@ -183,6 +197,11 @@ pub fn write(file: File, io: Io, buffer: []const u8) WriteError!usize {
return @errorCast(file.pwrite(io, buffer, -1)); return @errorCast(file.pwrite(io, buffer, -1));
} }
pub fn writeAll(file: File, io: Io, bytes: []const u8) WriteError!void {
var index: usize = 0;
while (index < bytes.len) index += try file.write(io, bytes[index..]);
}
pub const PWriteError = std.fs.File.PWriteError || Io.Cancelable; pub const PWriteError = std.fs.File.PWriteError || Io.Cancelable;
pub fn pwrite(file: File, io: Io, buffer: []const u8, offset: std.posix.off_t) PWriteError!usize { pub fn pwrite(file: File, io: Io, buffer: []const u8, offset: std.posix.off_t) PWriteError!usize {
@ -350,7 +369,7 @@ pub const Reader = struct {
const io = r.io; const io = r.io;
switch (r.mode) { switch (r.mode) {
.positional, .positional_reading => { .positional, .positional_reading => {
setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset)); setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
}, },
.streaming, .streaming_reading => { .streaming, .streaming_reading => {
if (std.posix.SEEK == void) { if (std.posix.SEEK == void) {
@ -359,7 +378,7 @@ pub const Reader = struct {
} }
const seek_err = r.seek_err orelse e: { const seek_err = r.seek_err orelse e: {
if (io.vtable.fileSeekBy(io.userdata, r.file, offset)) |_| { if (io.vtable.fileSeekBy(io.userdata, r.file, offset)) |_| {
setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset)); setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
return; return;
} else |err| { } else |err| {
r.seek_err = err; r.seek_err = err;
@ -384,16 +403,17 @@ pub const Reader = struct {
const io = r.io; const io = r.io;
switch (r.mode) { switch (r.mode) {
.positional, .positional_reading => { .positional, .positional_reading => {
setPosAdjustingBuffer(r, offset); setLogicalPos(r, offset);
}, },
.streaming, .streaming_reading => { .streaming, .streaming_reading => {
if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos)); const logical_pos = logicalPos(r);
if (offset >= logical_pos) return Reader.seekBy(r, @intCast(offset - logical_pos));
if (r.seek_err) |err| return err; if (r.seek_err) |err| return err;
io.vtable.fileSeekTo(io.userdata, r.file, offset) catch |err| { io.vtable.fileSeekTo(io.userdata, r.file, offset) catch |err| {
r.seek_err = err; r.seek_err = err;
return err; return err;
}; };
setPosAdjustingBuffer(r, offset); setLogicalPos(r, offset);
}, },
.failure => return r.seek_err.?, .failure => return r.seek_err.?,
} }
@ -403,7 +423,7 @@ pub const Reader = struct {
return r.pos - r.interface.bufferedLen(); return r.pos - r.interface.bufferedLen();
} }
fn setPosAdjustingBuffer(r: *Reader, offset: u64) void { fn setLogicalPos(r: *Reader, offset: u64) void {
const logical_pos = logicalPos(r); const logical_pos = logicalPos(r);
if (offset < logical_pos or offset >= r.pos) { if (offset < logical_pos or offset >= r.pos) {
r.interface.seek = 0; r.interface.seek = 0;
@ -544,9 +564,10 @@ pub const Reader = struct {
} }
} }
/// Returns whether the stream is at the logical end.
pub fn atEnd(r: *Reader) bool { pub fn atEnd(r: *Reader) bool {
// Even if stat fails, size is set when end is encountered. // Even if stat fails, size is set when end is encountered.
const size = r.size orelse return false; const size = r.size orelse return false;
return size - r.pos == 0; return size - logicalPos(r) == 0;
} }
}; };

View file

@ -43,6 +43,14 @@ pub const Protocol = enum(u32) {
mptcp = 262, mptcp = 262,
}; };
/// Windows 10 added support for unix sockets in build 17063, redstone 4 is the
/// first release to support them.
pub const has_unix_sockets = switch (native_os) {
.windows => builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false,
.wasi => false,
else => true,
};
pub const IpAddress = union(enum) { pub const IpAddress = union(enum) {
ip4: Ip4Address, ip4: Ip4Address,
ip6: Ip6Address, ip6: Ip6Address,
@ -980,7 +988,13 @@ pub const Stream = struct {
stream: Stream, stream: Stream,
err: ?Error, err: ?Error,
pub const Error = std.net.Stream.ReadError || Io.Cancelable || Io.Writer.Error || error{EndOfStream}; pub const Error = std.posix.ReadError || error{
SocketNotBound,
MessageTooBig,
NetworkSubsystemFailed,
ConnectionResetByPeer,
SocketUnconnected,
} || Io.Cancelable || Io.Writer.Error || error{EndOfStream};
pub fn init(stream: Stream, buffer: []u8) Reader { pub fn init(stream: Stream, buffer: []u8) Reader {
return .{ return .{
@ -1019,7 +1033,15 @@ pub const Stream = struct {
stream: Stream, stream: Stream,
err: ?Error = null, err: ?Error = null,
pub const Error = std.net.Stream.WriteError || Io.Cancelable; pub const Error = std.posix.SendMsgError || error{
ConnectionResetByPeer,
SocketNotBound,
MessageTooBig,
NetworkSubsystemFailed,
SystemResources,
SocketUnconnected,
Unexpected,
} || Io.Cancelable;
pub fn init(stream: Stream, buffer: []u8) Writer { pub fn init(stream: Stream, buffer: []u8) Writer {
return .{ return .{
@ -1096,4 +1118,5 @@ fn testIp6ParseTransform(expected: []const u8, input: []const u8) !void {
test { test {
_ = HostName; _ = HostName;
_ = @import("net/test.zig");
} }

View file

@ -1,38 +1,38 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const net = std.net; const net = std.Io.net;
const mem = std.mem; const mem = std.mem;
const testing = std.testing; const testing = std.testing;
test "parse and render IP addresses at comptime" { test "parse and render IP addresses at comptime" {
comptime { comptime {
const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable; const ipv6addr = net.IpAddress.parseIp("::1", 0) catch unreachable;
try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr}); try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr});
const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable; const ipv4addr = net.IpAddress.parseIp("127.0.0.1", 0) catch unreachable;
try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr}); try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr});
try testing.expectError(error.InvalidIpAddressFormat, net.Address.parseIp("::123.123.123.123", 0)); try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.parseIp("::123.123.123.123", 0));
try testing.expectError(error.InvalidIpAddressFormat, net.Address.parseIp("127.01.0.1", 0)); try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.parseIp("127.01.0.1", 0));
try testing.expectError(error.InvalidIpAddressFormat, net.Address.resolveIp("::123.123.123.123", 0)); try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.resolveIp("::123.123.123.123", 0));
try testing.expectError(error.InvalidIpAddressFormat, net.Address.resolveIp("127.01.0.1", 0)); try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.resolveIp("127.01.0.1", 0));
} }
} }
test "format IPv6 address with no zero runs" { test "format IPv6 address with no zero runs" {
const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0); const addr = try std.net.IpAddress.parseIp6("2001:db8:1:2:3:4:5:6", 0);
try std.testing.expectFmt("[2001:db8:1:2:3:4:5:6]:0", "{f}", .{addr}); try std.testing.expectFmt("[2001:db8:1:2:3:4:5:6]:0", "{f}", .{addr});
} }
test "parse IPv6 addresses and check compressed form" { test "parse IPv6 addresses and check compressed form" {
try std.testing.expectFmt("[2001:db8::1:0:0:2]:0", "{f}", .{ try std.testing.expectFmt("[2001:db8::1:0:0:2]:0", "{f}", .{
try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0), try std.net.IpAddress.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0),
}); });
try std.testing.expectFmt("[2001:db8::1:2]:0", "{f}", .{ try std.testing.expectFmt("[2001:db8::1:2]:0", "{f}", .{
try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0), try std.net.IpAddress.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0),
}); });
try std.testing.expectFmt("[2001:db8:1:0:1::2]:0", "{f}", .{ try std.testing.expectFmt("[2001:db8:1:0:1::2]:0", "{f}", .{
try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0), try std.net.IpAddress.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0),
}); });
} }
@ -44,7 +44,7 @@ test "parse IPv6 address, check raw bytes" {
0x00, 0x00, 0x00, 0x02, // :0000:0002 0x00, 0x00, 0x00, 0x02, // :0000:0002
}; };
const addr = try std.net.Address.parseIp6("2001:db8:0000:0000:0001:0000:0000:0002", 0); const addr = try std.net.IpAddress.parseIp6("2001:db8:0000:0000:0001:0000:0000:0002", 0);
const actual_raw = addr.in6.sa.addr[0..]; const actual_raw = addr.in6.sa.addr[0..];
try std.testing.expectEqualSlices(u8, expected_raw[0..], actual_raw); try std.testing.expectEqualSlices(u8, expected_raw[0..], actual_raw);
@ -77,30 +77,30 @@ test "parse and render IPv6 addresses" {
"::ffff:123.5.123.5", "::ffff:123.5.123.5",
}; };
for (ips, 0..) |ip, i| { for (ips, 0..) |ip, i| {
const addr = net.Address.parseIp6(ip, 0) catch unreachable; const addr = net.IpAddress.parseIp6(ip, 0) catch unreachable;
var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable; var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable;
try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));
if (builtin.os.tag == .linux) { if (builtin.os.tag == .linux) {
const addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable; const addr_via_resolve = net.IpAddress.resolveIp6(ip, 0) catch unreachable;
var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr_via_resolve}) catch unreachable; var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr_via_resolve}) catch unreachable;
try std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3])); try std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3]));
} }
} }
try testing.expectError(error.InvalidCharacter, net.Address.parseIp6(":::", 0)); try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6(":::", 0));
try testing.expectError(error.Overflow, net.Address.parseIp6("FF001::FB", 0)); try testing.expectError(error.Overflow, net.IpAddress.parseIp6("FF001::FB", 0));
try testing.expectError(error.InvalidCharacter, net.Address.parseIp6("FF01::Fb:zig", 0)); try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6("FF01::Fb:zig", 0));
try testing.expectError(error.InvalidEnd, net.Address.parseIp6("FF01:0:0:0:0:0:0:FB:", 0)); try testing.expectError(error.InvalidEnd, net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB:", 0));
try testing.expectError(error.Incomplete, net.Address.parseIp6("FF01:", 0)); try testing.expectError(error.Incomplete, net.IpAddress.parseIp6("FF01:", 0));
try testing.expectError(error.InvalidIpv4Mapping, net.Address.parseIp6("::123.123.123.123", 0)); try testing.expectError(error.InvalidIpv4Mapping, net.IpAddress.parseIp6("::123.123.123.123", 0));
try testing.expectError(error.Incomplete, net.Address.parseIp6("1", 0)); try testing.expectError(error.Incomplete, net.IpAddress.parseIp6("1", 0));
// TODO Make this test pass on other operating systems. // TODO Make this test pass on other operating systems.
if (builtin.os.tag == .linux or comptime builtin.os.tag.isDarwin() or builtin.os.tag == .windows) { if (builtin.os.tag == .linux or comptime builtin.os.tag.isDarwin() or builtin.os.tag == .windows) {
try testing.expectError(error.Incomplete, net.Address.resolveIp6("ff01::fb%", 0)); try testing.expectError(error.Incomplete, net.IpAddress.resolveIp6("ff01::fb%", 0));
// Assumes IFNAMESIZE will always be a multiple of 2 // Assumes IFNAMESIZE will always be a multiple of 2
try testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%wlp3" ++ "s0" ** @divExact(std.posix.IFNAMESIZE - 4, 2), 0)); try testing.expectError(error.Overflow, net.IpAddress.resolveIp6("ff01::fb%wlp3" ++ "s0" ** @divExact(std.posix.IFNAMESIZE - 4, 2), 0));
try testing.expectError(error.Overflow, net.Address.resolveIp6("ff01::fb%12345678901234", 0)); try testing.expectError(error.Overflow, net.IpAddress.resolveIp6("ff01::fb%12345678901234", 0));
} }
} }
@ -111,7 +111,7 @@ test "invalid but parseable IPv6 scope ids" {
return error.SkipZigTest; return error.SkipZigTest;
} }
try testing.expectError(error.InterfaceNotFound, net.Address.resolveIp6("ff01::fb%123s45678901234", 0)); try testing.expectError(error.InterfaceNotFound, net.IpAddress.resolveIp6("ff01::fb%123s45678901234", 0));
} }
test "parse and render IPv4 addresses" { test "parse and render IPv4 addresses" {
@ -123,17 +123,17 @@ test "parse and render IPv4 addresses" {
"123.255.0.91", "123.255.0.91",
"127.0.0.1", "127.0.0.1",
}) |ip| { }) |ip| {
const addr = net.Address.parseIp4(ip, 0) catch unreachable; const addr = net.IpAddress.parseIp4(ip, 0) catch unreachable;
var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable; var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable;
try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
} }
try testing.expectError(error.Overflow, net.Address.parseIp4("256.0.0.1", 0)); try testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0));
try testing.expectError(error.InvalidCharacter, net.Address.parseIp4("x.0.0.1", 0)); try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0));
try testing.expectError(error.InvalidEnd, net.Address.parseIp4("127.0.0.1.1", 0)); try testing.expectError(error.InvalidEnd, net.IpAddress.parseIp4("127.0.0.1.1", 0));
try testing.expectError(error.Incomplete, net.Address.parseIp4("127.0.0.", 0)); try testing.expectError(error.Incomplete, net.IpAddress.parseIp4("127.0.0.", 0));
try testing.expectError(error.InvalidCharacter, net.Address.parseIp4("100..0.1", 0)); try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("100..0.1", 0));
try testing.expectError(error.NonCanonical, net.Address.parseIp4("127.01.0.1", 0)); try testing.expectError(error.NonCanonical, net.IpAddress.parseIp4("127.01.0.1", 0));
} }
test "parse and render UNIX addresses" { test "parse and render UNIX addresses" {
@ -161,8 +161,8 @@ test "resolve DNS" {
// Resolve localhost, this should not fail. // Resolve localhost, this should not fail.
{ {
const localhost_v4 = try net.Address.parseIp("127.0.0.1", 80); const localhost_v4 = try net.IpAddress.parseIp("127.0.0.1", 80);
const localhost_v6 = try net.Address.parseIp("::2", 80); const localhost_v6 = try net.IpAddress.parseIp("::2", 80);
const result = try net.getAddressList(testing.allocator, "localhost", 80); const result = try net.getAddressList(testing.allocator, "localhost", 80);
defer result.deinit(); defer result.deinit();
@ -198,13 +198,13 @@ test "listen on a port, send bytes, receive bytes" {
// Try only the IPv4 variant as some CI builders have no IPv6 localhost // Try only the IPv4 variant as some CI builders have no IPv6 localhost
// configured. // configured.
const localhost = try net.Address.parseIp("127.0.0.1", 0); const localhost = try net.IpAddress.parseIp("127.0.0.1", 0);
var server = try localhost.listen(.{}); var server = try localhost.listen(.{});
defer server.deinit(); defer server.deinit();
const S = struct { const S = struct {
fn clientFn(server_address: net.Address) !void { fn clientFn(server_address: net.IpAddress) !void {
const socket = try net.tcpConnectToAddress(server_address); const socket = try net.tcpConnectToAddress(server_address);
defer socket.close(); defer socket.close();
@ -232,7 +232,7 @@ test "listen on an in use port" {
return error.SkipZigTest; return error.SkipZigTest;
} }
const localhost = try net.Address.parseIp("127.0.0.1", 0); const localhost = try net.IpAddress.parseIp("127.0.0.1", 0);
var server1 = try localhost.listen(.{ .reuse_address = true }); var server1 = try localhost.listen(.{ .reuse_address = true });
defer server1.deinit(); defer server1.deinit();
@ -253,7 +253,7 @@ fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyer
try testing.expect(mem.eql(u8, msg, "hello from server\n")); try testing.expect(mem.eql(u8, msg, "hello from server\n"));
} }
fn testClient(addr: net.Address) anyerror!void { fn testClient(addr: net.IpAddress) anyerror!void {
if (builtin.os.tag == .wasi) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest;
const socket_file = try net.tcpConnectToAddress(addr); const socket_file = try net.tcpConnectToAddress(addr);
@ -290,7 +290,7 @@ test "listen on a unix socket, send bytes, receive bytes" {
const socket_path = try generateFileName("socket.unix"); const socket_path = try generateFileName("socket.unix");
defer testing.allocator.free(socket_path); defer testing.allocator.free(socket_path);
const socket_addr = try net.Address.initUnix(socket_path); const socket_addr = try net.IpAddress.initUnix(socket_path);
defer std.fs.cwd().deleteFile(socket_path) catch {}; defer std.fs.cwd().deleteFile(socket_path) catch {};
var server = try socket_addr.listen(.{}); var server = try socket_addr.listen(.{});
@ -351,7 +351,7 @@ test "non-blocking tcp server" {
return error.SkipZigTest; return error.SkipZigTest;
} }
const localhost = try net.Address.parseIp("127.0.0.1", 0); const localhost = try net.IpAddress.parseIp("127.0.0.1", 0);
var server = localhost.listen(.{ .force_nonblocking = true }); var server = localhost.listen(.{ .force_nonblocking = true });
defer server.deinit(); defer server.deinit();

View file

@ -523,9 +523,7 @@ pub fn setStatus(new_status: Status) void {
/// Returns whether a resize is needed to learn the terminal size. /// Returns whether a resize is needed to learn the terminal size.
fn wait(timeout_ns: u64) bool { fn wait(timeout_ns: u64) bool {
const resize_flag = if (global_progress.redraw_event.timedWait(timeout_ns)) |_| const resize_flag = if (global_progress.redraw_event.timedWait(timeout_ns)) |_| true else |err| switch (err) {
true
else |err| switch (err) {
error.Timeout => false, error.Timeout => false,
}; };
global_progress.redraw_event.reset(); global_progress.redraw_event.reset();

View file

@ -71,7 +71,7 @@ pub const ResetEvent = enum(u32) {
/// ///
/// The memory accesses before the set() can be said to happen before /// The memory accesses before the set() can be said to happen before
/// timedWait() returns without error. /// timedWait() returns without error.
pub fn timedWait(re: *ResetEvent, timeout_ns: u64) void { pub fn timedWait(re: *ResetEvent, timeout_ns: u64) error{Timeout}!void {
if (builtin.single_threaded) switch (re.*) { if (builtin.single_threaded) switch (re.*) {
.unset => { .unset => {
sleep(timeout_ns); sleep(timeout_ns);
@ -1774,9 +1774,9 @@ test "setName, getName" {
if (builtin.single_threaded) return error.SkipZigTest; if (builtin.single_threaded) return error.SkipZigTest;
const Context = struct { const Context = struct {
start_wait_event: ResetEvent = .{}, start_wait_event: ResetEvent = .unset,
test_done_event: ResetEvent = .{}, test_done_event: ResetEvent = .unset,
thread_done_event: ResetEvent = .{}, thread_done_event: ResetEvent = .unset,
done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
thread: Thread = undefined, thread: Thread = undefined,
@ -1843,7 +1843,7 @@ test join {
if (builtin.single_threaded) return error.SkipZigTest; if (builtin.single_threaded) return error.SkipZigTest;
var value: usize = 0; var value: usize = 0;
var event = ResetEvent{}; var event: ResetEvent = .unset;
const thread = try Thread.spawn(.{}, testIncrementNotify, .{ &value, &event }); const thread = try Thread.spawn(.{}, testIncrementNotify, .{ &value, &event });
thread.join(); thread.join();
@ -1855,7 +1855,7 @@ test detach {
if (builtin.single_threaded) return error.SkipZigTest; if (builtin.single_threaded) return error.SkipZigTest;
var value: usize = 0; var value: usize = 0;
var event = ResetEvent{}; var event: ResetEvent = .unset;
const thread = try Thread.spawn(.{}, testIncrementNotify, .{ &value, &event }); const thread = try Thread.spawn(.{}, testIncrementNotify, .{ &value, &event });
thread.detach(); thread.detach();
@ -1902,8 +1902,7 @@ fn testTls() !void {
} }
test "ResetEvent smoke test" { test "ResetEvent smoke test" {
// make sure the event is unset var event: ResetEvent = .unset;
var event = ResetEvent{};
try testing.expectEqual(false, event.isSet()); try testing.expectEqual(false, event.isSet());
// make sure the event gets set // make sure the event gets set
@ -1932,8 +1931,8 @@ test "ResetEvent signaling" {
} }
const Context = struct { const Context = struct {
in: ResetEvent = .{}, in: ResetEvent = .unset,
out: ResetEvent = .{}, out: ResetEvent = .unset,
value: usize = 0, value: usize = 0,
fn input(self: *@This()) !void { fn input(self: *@This()) !void {
@ -1994,7 +1993,7 @@ test "ResetEvent broadcast" {
const num_threads = 10; const num_threads = 10;
const Barrier = struct { const Barrier = struct {
event: ResetEvent = .{}, event: ResetEvent = .unset,
counter: std.atomic.Value(usize) = std.atomic.Value(usize).init(num_threads), counter: std.atomic.Value(usize) = std.atomic.Value(usize).init(num_threads),
fn wait(self: *@This()) void { fn wait(self: *@This()) void {

View file

@ -97,25 +97,6 @@ pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, null);
/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. /// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null); pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null);
/// Same as `Dir.updateFile`, except asserts that both `source_path` and `dest_path`
/// are absolute. See `Dir.updateFile` for a function that operates on both
/// absolute and relative paths.
/// On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
/// On WASI, both paths should be encoded as valid UTF-8.
/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn updateFileAbsolute(
source_path: []const u8,
dest_path: []const u8,
args: Dir.CopyFileOptions,
) !std.Io.Dir.PrevStatus {
assert(path.isAbsolute(source_path));
assert(path.isAbsolute(dest_path));
const my_cwd = cwd();
return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args);
}
test updateFileAbsolute {}
/// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path` /// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path`
/// are absolute. See `Dir.copyFile` for a function that operates on both /// are absolute. See `Dir.copyFile` for a function that operates on both
/// absolute and relative paths. /// absolute and relative paths.

View file

@ -698,17 +698,6 @@ pub fn read(self: File, buffer: []u8) ReadError!usize {
return posix.read(self.handle, buffer); return posix.read(self.handle, buffer);
} }
/// Deprecated in favor of `Reader`.
pub fn readAll(self: File, buffer: []u8) ReadError!usize {
var index: usize = 0;
while (index != buffer.len) {
const amt = try self.read(buffer[index..]);
if (amt == 0) break;
index += amt;
}
return index;
}
/// On Windows, this function currently does alter the file pointer. /// On Windows, this function currently does alter the file pointer.
/// https://github.com/ziglang/zig/issues/12783 /// https://github.com/ziglang/zig/issues/12783
pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
@ -719,17 +708,6 @@ pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
return posix.pread(self.handle, buffer, offset); return posix.pread(self.handle, buffer, offset);
} }
/// Deprecated in favor of `Reader`.
pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
var index: usize = 0;
while (index != buffer.len) {
const amt = try self.pread(buffer[index..], offset + index);
if (amt == 0) break;
index += amt;
}
return index;
}
/// See https://github.com/ziglang/zig/issues/7699 /// See https://github.com/ziglang/zig/issues/7699
pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize { pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize {
if (is_windows) { if (is_windows) {
@ -741,36 +719,6 @@ pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize {
return posix.readv(self.handle, iovecs); return posix.readv(self.handle, iovecs);
} }
/// Deprecated in favor of `Reader`.
pub fn readvAll(self: File, iovecs: []posix.iovec) ReadError!usize {
if (iovecs.len == 0) return 0;
// We use the address of this local variable for all zero-length
// vectors so that the OS does not complain that we are giving it
// addresses outside the application's address space.
var garbage: [1]u8 = undefined;
for (iovecs) |*v| {
if (v.len == 0) v.base = &garbage;
}
var i: usize = 0;
var off: usize = 0;
while (true) {
var amt = try self.readv(iovecs[i..]);
var eof = amt == 0;
off += amt;
while (amt >= iovecs[i].len) {
amt -= iovecs[i].len;
i += 1;
if (i >= iovecs.len) return off;
eof = false;
}
if (eof) return off;
iovecs[i].base += amt;
iovecs[i].len -= amt;
}
}
/// See https://github.com/ziglang/zig/issues/7699 /// See https://github.com/ziglang/zig/issues/7699
/// On Windows, this function currently does alter the file pointer. /// On Windows, this function currently does alter the file pointer.
/// https://github.com/ziglang/zig/issues/12783 /// https://github.com/ziglang/zig/issues/12783
@ -784,28 +732,6 @@ pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!u
return posix.preadv(self.handle, iovecs, offset); return posix.preadv(self.handle, iovecs, offset);
} }
/// Deprecated in favor of `Reader`.
pub fn preadvAll(self: File, iovecs: []posix.iovec, offset: u64) PReadError!usize {
if (iovecs.len == 0) return 0;
var i: usize = 0;
var off: usize = 0;
while (true) {
var amt = try self.preadv(iovecs[i..], offset + off);
var eof = amt == 0;
off += amt;
while (amt >= iovecs[i].len) {
amt -= iovecs[i].len;
i += 1;
if (i >= iovecs.len) return off;
eof = false;
}
if (eof) return off;
iovecs[i].base += amt;
iovecs[i].len -= amt;
}
}
pub const WriteError = posix.WriteError; pub const WriteError = posix.WriteError;
pub const PWriteError = posix.PWriteError; pub const PWriteError = posix.PWriteError;
@ -817,7 +743,6 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize {
return posix.write(self.handle, bytes); return posix.write(self.handle, bytes);
} }
/// Deprecated in favor of `Writer`.
pub fn writeAll(self: File, bytes: []const u8) WriteError!void { pub fn writeAll(self: File, bytes: []const u8) WriteError!void {
var index: usize = 0; var index: usize = 0;
while (index < bytes.len) { while (index < bytes.len) {
@ -835,14 +760,6 @@ pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
return posix.pwrite(self.handle, bytes, offset); return posix.pwrite(self.handle, bytes, offset);
} }
/// Deprecated in favor of `Writer`.
pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void {
var index: usize = 0;
while (index < bytes.len) {
index += try self.pwrite(bytes[index..], offset + index);
}
}
/// See https://github.com/ziglang/zig/issues/7699 /// See https://github.com/ziglang/zig/issues/7699
pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize { pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize {
if (is_windows) { if (is_windows) {
@ -855,31 +772,6 @@ pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize {
return posix.writev(self.handle, iovecs); return posix.writev(self.handle, iovecs);
} }
/// Deprecated in favor of `Writer`.
pub fn writevAll(self: File, iovecs: []posix.iovec_const) WriteError!void {
if (iovecs.len == 0) return;
// We use the address of this local variable for all zero-length
// vectors so that the OS does not complain that we are giving it
// addresses outside the application's address space.
var garbage: [1]u8 = undefined;
for (iovecs) |*v| {
if (v.len == 0) v.base = &garbage;
}
var i: usize = 0;
while (true) {
var amt = try self.writev(iovecs[i..]);
while (amt >= iovecs[i].len) {
amt -= iovecs[i].len;
i += 1;
if (i >= iovecs.len) return;
}
iovecs[i].base += amt;
iovecs[i].len -= amt;
}
}
/// See https://github.com/ziglang/zig/issues/7699 /// See https://github.com/ziglang/zig/issues/7699
/// On Windows, this function currently does alter the file pointer. /// On Windows, this function currently does alter the file pointer.
/// https://github.com/ziglang/zig/issues/12783 /// https://github.com/ziglang/zig/issues/12783
@ -893,485 +785,8 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError
return posix.pwritev(self.handle, iovecs, offset); return posix.pwritev(self.handle, iovecs, offset);
} }
/// Deprecated in favor of `Writer`. /// Deprecated in favor of `std.Io.File.Reader`.
pub fn pwritevAll(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError!void { pub const Reader = std.Io.File.Reader;
if (iovecs.len == 0) return;
var i: usize = 0;
var off: u64 = 0;
while (true) {
var amt = try self.pwritev(iovecs[i..], offset + off);
off += amt;
while (amt >= iovecs[i].len) {
amt -= iovecs[i].len;
i += 1;
if (i >= iovecs.len) return;
}
iovecs[i].base += amt;
iovecs[i].len -= amt;
}
}
pub const CopyRangeError = posix.CopyFileRangeError;
/// Deprecated in favor of `Writer`.
pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 {
const adjusted_len = math.cast(usize, len) orelse maxInt(usize);
const result = try posix.copy_file_range(in.handle, in_offset, out.handle, out_offset, adjusted_len, 0);
return result;
}
/// Deprecated in favor of `Writer`.
pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 {
var total_bytes_copied: u64 = 0;
var in_off = in_offset;
var out_off = out_offset;
while (total_bytes_copied < len) {
const amt_copied = try copyRange(in, in_off, out, out_off, len - total_bytes_copied);
if (amt_copied == 0) return total_bytes_copied;
total_bytes_copied += amt_copied;
in_off += amt_copied;
out_off += amt_copied;
}
return total_bytes_copied;
}
/// Memoizes key information about a file handle such as:
/// * The size from calling stat, or the error that occurred therein.
/// * The current seek position.
/// * The error that occurred when trying to seek.
/// * Whether reading should be done positionally or streaming.
/// * Whether reading should be done via fd-to-fd syscalls (e.g. `sendfile`)
/// versus plain variants (e.g. `read`).
///
/// Fulfills the `std.Io.Reader` interface.
pub const Reader = struct {
file: File,
err: ?ReadError = null,
mode: Reader.Mode = .positional,
/// Tracks the true seek position in the file. To obtain the logical
/// position, use `logicalPos`.
pos: u64 = 0,
size: ?u64 = null,
size_err: ?SizeError = null,
seek_err: ?Reader.SeekError = null,
interface: std.Io.Reader,
pub const SizeError = std.os.windows.GetFileSizeError || StatError || error{
/// Occurs if, for example, the file handle is a network socket and therefore does not have a size.
Streaming,
};
pub const SeekError = File.SeekError || error{
/// Seeking fell back to reading, and reached the end before the requested seek position.
/// `pos` remains at the end of the file.
EndOfStream,
/// Seeking fell back to reading, which failed.
ReadFailed,
};
pub const Mode = enum {
streaming,
positional,
/// Avoid syscalls other than `read` and `readv`.
streaming_reading,
/// Avoid syscalls other than `pread` and `preadv`.
positional_reading,
/// Indicates reading cannot continue because of a seek failure.
failure,
pub fn toStreaming(m: @This()) @This() {
return switch (m) {
.positional, .streaming => .streaming,
.positional_reading, .streaming_reading => .streaming_reading,
.failure => .failure,
};
}
pub fn toReading(m: @This()) @This() {
return switch (m) {
.positional, .positional_reading => .positional_reading,
.streaming, .streaming_reading => .streaming_reading,
.failure => .failure,
};
}
};
pub fn initInterface(buffer: []u8) std.Io.Reader {
return .{
.vtable = &.{
.stream = Reader.stream,
.discard = Reader.discard,
.readVec = Reader.readVec,
},
.buffer = buffer,
.seek = 0,
.end = 0,
};
}
pub fn init(file: File, buffer: []u8) Reader {
return .{
.file = file,
.interface = initInterface(buffer),
};
}
pub fn initSize(file: File, buffer: []u8, size: ?u64) Reader {
return .{
.file = file,
.interface = initInterface(buffer),
.size = size,
};
}
/// Positional is more threadsafe, since the global seek position is not
/// affected, but when such syscalls are not available, preemptively
/// initializing in streaming mode skips a failed syscall.
pub fn initStreaming(file: File, buffer: []u8) Reader {
return .{
.file = file,
.interface = Reader.initInterface(buffer),
.mode = .streaming,
.seek_err = error.Unseekable,
.size_err = error.Streaming,
};
}
pub fn getSize(r: *Reader) SizeError!u64 {
return r.size orelse {
if (r.size_err) |err| return err;
if (is_windows) {
if (windows.GetFileSizeEx(r.file.handle)) |size| {
r.size = size;
return size;
} else |err| {
r.size_err = err;
return err;
}
}
if (posix.Stat == void) {
r.size_err = error.Streaming;
return error.Streaming;
}
if (stat(r.file)) |st| {
if (st.kind == .file) {
r.size = st.size;
return st.size;
} else {
r.mode = r.mode.toStreaming();
r.size_err = error.Streaming;
return error.Streaming;
}
} else |err| {
r.size_err = err;
return err;
}
};
}
pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void {
switch (r.mode) {
.positional, .positional_reading => {
setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
},
.streaming, .streaming_reading => {
if (posix.SEEK == void) {
r.seek_err = error.Unseekable;
return error.Unseekable;
}
const seek_err = r.seek_err orelse e: {
if (posix.lseek_CUR(r.file.handle, offset)) |_| {
setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
return;
} else |err| {
r.seek_err = err;
break :e err;
}
};
var remaining = std.math.cast(u64, offset) orelse return seek_err;
while (remaining > 0) {
remaining -= discard(&r.interface, .limited64(remaining)) catch |err| {
r.seek_err = err;
return err;
};
}
r.interface.seek = 0;
r.interface.end = 0;
},
.failure => return r.seek_err.?,
}
}
pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {
switch (r.mode) {
.positional, .positional_reading => {
setLogicalPos(r, offset);
},
.streaming, .streaming_reading => {
const logical_pos = logicalPos(r);
if (offset >= logical_pos) return Reader.seekBy(r, @intCast(offset - logical_pos));
if (r.seek_err) |err| return err;
posix.lseek_SET(r.file.handle, offset) catch |err| {
r.seek_err = err;
return err;
};
setLogicalPos(r, offset);
},
.failure => return r.seek_err.?,
}
}
pub fn logicalPos(r: *const Reader) u64 {
return r.pos - r.interface.bufferedLen();
}
fn setLogicalPos(r: *Reader, offset: u64) void {
const logical_pos = logicalPos(r);
if (offset < logical_pos or offset >= r.pos) {
r.interface.seek = 0;
r.interface.end = 0;
r.pos = offset;
} else {
const logical_delta: usize = @intCast(offset - logical_pos);
r.interface.seek += logical_delta;
}
}
/// Number of slices to store on the stack, when trying to send as many byte
/// vectors through the underlying read calls as possible.
const max_buffers_len = 16;
fn stream(io_reader: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
switch (r.mode) {
.positional, .streaming => @panic("TODO"),
.positional_reading => {
const dest = limit.slice(try w.writableSliceGreedy(1));
var data: [1][]u8 = .{dest};
const n = try readVecPositional(r, &data);
w.advance(n);
return n;
},
.streaming_reading => {
const dest = limit.slice(try w.writableSliceGreedy(1));
var data: [1][]u8 = .{dest};
const n = try readVecStreaming(r, &data);
w.advance(n);
return n;
},
.failure => return error.ReadFailed,
}
}
fn readVec(io_reader: *std.Io.Reader, data: [][]u8) std.Io.Reader.Error!usize {
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
switch (r.mode) {
.positional, .positional_reading => return readVecPositional(r, data),
.streaming, .streaming_reading => return readVecStreaming(r, data),
.failure => return error.ReadFailed,
}
}
fn readVecPositional(r: *Reader, data: [][]u8) std.Io.Reader.Error!usize {
const io_reader = &r.interface;
if (is_windows) {
// Unfortunately, `ReadFileScatter` cannot be used since it
// requires page alignment.
if (io_reader.seek == io_reader.end) {
io_reader.seek = 0;
io_reader.end = 0;
}
const first = data[0];
if (first.len >= io_reader.buffer.len - io_reader.end) {
return readPositional(r, first);
} else {
io_reader.end += try readPositional(r, io_reader.buffer[io_reader.end..]);
return 0;
}
}
var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
const dest = iovecs_buffer[0..dest_n];
assert(dest[0].len > 0);
const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
error.Unseekable => {
r.mode = r.mode.toStreaming();
const pos = r.pos;
if (pos != 0) {
r.pos = 0;
r.seekBy(@intCast(pos)) catch {
r.mode = .failure;
return error.ReadFailed;
};
}
return 0;
},
else => |e| {
r.err = e;
return error.ReadFailed;
},
};
if (n == 0) {
r.size = r.pos;
return error.EndOfStream;
}
r.pos += n;
if (n > data_size) {
io_reader.end += n - data_size;
return data_size;
}
return n;
}
fn readVecStreaming(r: *Reader, data: [][]u8) std.Io.Reader.Error!usize {
const io_reader = &r.interface;
if (is_windows) {
// Unfortunately, `ReadFileScatter` cannot be used since it
// requires page alignment.
if (io_reader.seek == io_reader.end) {
io_reader.seek = 0;
io_reader.end = 0;
}
const first = data[0];
if (first.len >= io_reader.buffer.len - io_reader.end) {
return readStreaming(r, first);
} else {
io_reader.end += try readStreaming(r, io_reader.buffer[io_reader.end..]);
return 0;
}
}
var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
const dest = iovecs_buffer[0..dest_n];
assert(dest[0].len > 0);
const n = posix.readv(r.file.handle, dest) catch |err| {
r.err = err;
return error.ReadFailed;
};
if (n == 0) {
r.size = r.pos;
return error.EndOfStream;
}
r.pos += n;
if (n > data_size) {
io_reader.end += n - data_size;
return data_size;
}
return n;
}
fn discard(io_reader: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize {
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
const file = r.file;
const pos = r.pos;
switch (r.mode) {
.positional, .positional_reading => {
const size = r.getSize() catch {
r.mode = r.mode.toStreaming();
return 0;
};
const delta = @min(@intFromEnum(limit), size - pos);
r.pos = pos + delta;
return delta;
},
.streaming, .streaming_reading => {
// Unfortunately we can't seek forward without knowing the
// size because the seek syscalls provided to us will not
// return the true end position if a seek would exceed the
// end.
fallback: {
if (r.size_err == null and r.seek_err == null) break :fallback;
var trash_buffer: [128]u8 = undefined;
if (is_windows) {
const n = windows.ReadFile(file.handle, limit.slice(&trash_buffer), null) catch |err| {
r.err = err;
return error.ReadFailed;
};
if (n == 0) {
r.size = pos;
return error.EndOfStream;
}
r.pos = pos + n;
return n;
}
var iovecs: [max_buffers_len]std.posix.iovec = undefined;
var iovecs_i: usize = 0;
var remaining = @intFromEnum(limit);
while (remaining > 0 and iovecs_i < iovecs.len) {
iovecs[iovecs_i] = .{ .base = &trash_buffer, .len = @min(trash_buffer.len, remaining) };
remaining -= iovecs[iovecs_i].len;
iovecs_i += 1;
}
const n = posix.readv(file.handle, iovecs[0..iovecs_i]) catch |err| {
r.err = err;
return error.ReadFailed;
};
if (n == 0) {
r.size = pos;
return error.EndOfStream;
}
r.pos = pos + n;
return n;
}
const size = r.getSize() catch return 0;
const n = @min(size - pos, maxInt(i64), @intFromEnum(limit));
file.seekBy(n) catch |err| {
r.seek_err = err;
return 0;
};
r.pos = pos + n;
return n;
},
.failure => return error.ReadFailed,
}
}
fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
error.Unseekable => {
r.mode = r.mode.toStreaming();
const pos = r.pos;
if (pos != 0) {
r.pos = 0;
r.seekBy(@intCast(pos)) catch {
r.mode = .failure;
return error.ReadFailed;
};
}
return 0;
},
else => |e| {
r.err = e;
return error.ReadFailed;
},
};
if (n == 0) {
r.size = r.pos;
return error.EndOfStream;
}
r.pos += n;
return n;
}
fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
const n = r.file.read(dest) catch |err| {
r.err = err;
return error.ReadFailed;
};
if (n == 0) {
r.size = r.pos;
return error.EndOfStream;
}
r.pos += n;
return n;
}
pub fn atEnd(r: *Reader) bool {
// Even if stat fails, size is set when end is encountered.
const size = r.size orelse return false;
return size - r.pos == 0;
}
};
pub const Writer = struct { pub const Writer = struct {
file: File, file: File,

File diff suppressed because it is too large Load diff

View file

@ -85,7 +85,6 @@ pub const macho = @import("macho.zig");
pub const math = @import("math.zig"); pub const math = @import("math.zig");
pub const mem = @import("mem.zig"); pub const mem = @import("mem.zig");
pub const meta = @import("meta.zig"); pub const meta = @import("meta.zig");
pub const net = @import("net.zig");
pub const os = @import("os.zig"); pub const os = @import("os.zig");
pub const once = @import("once.zig").once; pub const once = @import("once.zig").once;
pub const pdb = @import("pdb.zig"); pub const pdb = @import("pdb.zig");