aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-12-26 16:12:09 -0800
committerAndrew Kelley <andrew@ziglang.org>2025-12-26 19:58:56 -0800
commitf160d5f979c9f2bcc98ef0169a879a76d0234ed2 (patch)
tree0d0ffdb2d1399ed0001c792d81d11491743bab58 /lib/std
parentfeeed922e1a7d89b99572d5577c814f9e6293b9b (diff)
downloadzig-f160d5f979c9f2bcc98ef0169a879a76d0234ed2.tar.gz
zig-f160d5f979c9f2bcc98ef0169a879a76d0234ed2.zip
std.Io.Threaded: implement file writing for Windows
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/Io/Threaded.zig90
-rw-r--r--lib/std/os/windows.zig109
-rw-r--r--lib/std/posix.zig60
3 files changed, 92 insertions, 167 deletions
diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig
index 3241c45d5c..1e37f56365 100644
--- a/lib/std/Io/Threaded.zig
+++ b/lib/std/Io/Threaded.zig
@@ -7420,7 +7420,18 @@ fn fileWritePositional(
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
- if (is_windows) @panic("TODO implement fileWritePositional windows");
+ if (is_windows) {
+ if (header.len != 0) {
+ return writeFilePositionalWindows(current_thread, file.handle, header, offset);
+ }
+ for (data[0 .. data.len - 1]) |buf| {
+ if (buf.len == 0) continue;
+ return writeFilePositionalWindows(current_thread, file.handle, buf, offset);
+ }
+ const pattern = data[data.len - 1];
+ if (pattern.len == 0 or splat == 0) return 0;
+ return writeFilePositionalWindows(current_thread, file.handle, pattern, offset);
+ }
var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
var iovlen: iovlen_t = 0;
@@ -7532,6 +7543,44 @@ fn fileWritePositional(
}
}
+fn writeFilePositionalWindows(
+ current_thread: *Thread,
+ handle: windows.HANDLE,
+ bytes: []const u8,
+ offset: u64,
+) File.WritePositionalError!usize {
+ try current_thread.checkCancel();
+
+ var bytes_written: windows.DWORD = undefined;
+ var overlapped: windows.OVERLAPPED = .{
+ .Internal = 0,
+ .InternalHigh = 0,
+ .DUMMYUNIONNAME = .{
+ .DUMMYSTRUCTNAME = .{
+ .Offset = @truncate(offset),
+ .OffsetHigh = @truncate(offset >> 32),
+ },
+ },
+ .hEvent = null,
+ };
+ const adjusted_len = std.math.lossyCast(u32, bytes.len);
+ if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, &overlapped) == 0) {
+ switch (windows.GetLastError()) {
+ .INVALID_USER_BUFFER => return error.SystemResources,
+ .NOT_ENOUGH_MEMORY => return error.SystemResources,
+ .OPERATION_ABORTED => return error.Canceled,
+ .NOT_ENOUGH_QUOTA => return error.SystemResources,
+ .NO_DATA => return error.BrokenPipe,
+ .INVALID_HANDLE => return error.NotOpenForWriting,
+ .LOCK_VIOLATION => return error.LockViolation,
+ .ACCESS_DENIED => return error.AccessDenied,
+ .WORKING_SET_QUOTA => return error.SystemResources,
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+ return bytes_written;
+}
+
fn fileWriteStreaming(
userdata: ?*anyopaque,
file: File,
@@ -7542,7 +7591,18 @@ fn fileWriteStreaming(
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
- if (is_windows) @panic("TODO implement fileWriteStreaming windows");
+ if (is_windows) {
+ if (header.len != 0) {
+ return writeFileStreamingWindows(current_thread, file.handle, header);
+ }
+ for (data[0 .. data.len - 1]) |buf| {
+ if (buf.len == 0) continue;
+ return writeFileStreamingWindows(current_thread, file.handle, buf);
+ }
+ const pattern = data[data.len - 1];
+ if (pattern.len == 0 or splat == 0) return 0;
+ return writeFileStreamingWindows(current_thread, file.handle, pattern);
+ }
var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
var iovlen: iovlen_t = 0;
@@ -7647,6 +7707,32 @@ fn fileWriteStreaming(
}
}
+fn writeFileStreamingWindows(
+ current_thread: *Thread,
+ handle: windows.HANDLE,
+ bytes: []const u8,
+) File.Writer.Error!usize {
+ try current_thread.checkCancel();
+
+ var bytes_written: windows.DWORD = undefined;
+ const adjusted_len = std.math.lossyCast(u32, bytes.len);
+ if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, null) == 0) {
+ switch (windows.GetLastError()) {
+ .INVALID_USER_BUFFER => return error.SystemResources,
+ .NOT_ENOUGH_MEMORY => return error.SystemResources,
+ .OPERATION_ABORTED => return error.Canceled,
+ .NOT_ENOUGH_QUOTA => return error.SystemResources,
+ .NO_DATA => return error.BrokenPipe,
+ .INVALID_HANDLE => return error.NotOpenForWriting,
+ .LOCK_VIOLATION => return error.LockViolation,
+ .ACCESS_DENIED => return error.AccessDenied,
+ .WORKING_SET_QUOTA => return error.SystemResources,
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+ return bytes_written;
+}
+
fn fileWriteFileStreaming(
userdata: ?*anyopaque,
file: File,
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
index 12ec0427a9..371883e9a4 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -2830,115 +2830,6 @@ pub fn CloseHandle(hObject: HANDLE) void {
assert(ntdll.NtClose(hObject) == .SUCCESS);
}
-pub const ReadFileError = error{
- BrokenPipe,
- /// The specified network name is no longer available.
- ConnectionResetByPeer,
- Canceled,
- /// Unable to read file due to lock.
- LockViolation,
- /// Known to be possible when:
- /// - Unable to read from disconnected virtual com port (Windows)
- AccessDenied,
- NotOpenForReading,
- Unexpected,
-};
-
-/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
-/// multiple non-atomic reads.
-pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usize {
- while (true) {
- const want_read_count: DWORD = @min(@as(DWORD, maxInt(DWORD)), buffer.len);
- var amt_read: DWORD = undefined;
- var overlapped_data: OVERLAPPED = undefined;
- const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
- overlapped_data = .{
- .Internal = 0,
- .InternalHigh = 0,
- .DUMMYUNIONNAME = .{
- .DUMMYSTRUCTNAME = .{
- .Offset = @as(u32, @truncate(off)),
- .OffsetHigh = @as(u32, @truncate(off >> 32)),
- },
- },
- .hEvent = null,
- };
- break :blk &overlapped_data;
- } else null;
- if (kernel32.ReadFile(in_hFile, buffer.ptr, want_read_count, &amt_read, overlapped) == 0) {
- switch (GetLastError()) {
- .IO_PENDING => unreachable,
- .OPERATION_ABORTED => continue,
- .BROKEN_PIPE => return 0,
- .HANDLE_EOF => return 0,
- .NETNAME_DELETED => return error.ConnectionResetByPeer,
- .LOCK_VIOLATION => return error.LockViolation,
- .ACCESS_DENIED => return error.AccessDenied,
- .INVALID_HANDLE => return error.NotOpenForReading,
- else => |err| return unexpectedError(err),
- }
- }
- return amt_read;
- }
-}
-
-pub const WriteFileError = error{
- SystemResources,
- Canceled,
- BrokenPipe,
- NotOpenForWriting,
- /// The process cannot access the file because another process has locked
- /// a portion of the file.
- LockViolation,
- /// The specified network name is no longer available.
- ConnectionResetByPeer,
- /// Known to be possible when:
- /// - Unable to write to disconnected virtual com port (Windows)
- AccessDenied,
- Unexpected,
-};
-
-pub fn WriteFile(
- handle: HANDLE,
- bytes: []const u8,
- offset: ?u64,
-) WriteFileError!usize {
- var bytes_written: DWORD = undefined;
- var overlapped_data: OVERLAPPED = undefined;
- const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
- overlapped_data = .{
- .Internal = 0,
- .InternalHigh = 0,
- .DUMMYUNIONNAME = .{
- .DUMMYSTRUCTNAME = .{
- .Offset = @truncate(off),
- .OffsetHigh = @truncate(off >> 32),
- },
- },
- .hEvent = null,
- };
- break :blk &overlapped_data;
- } else null;
- const adjusted_len = math.cast(u32, bytes.len) orelse maxInt(u32);
- if (kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, overlapped) == 0) {
- switch (GetLastError()) {
- .INVALID_USER_BUFFER => return error.SystemResources,
- .NOT_ENOUGH_MEMORY => return error.SystemResources,
- .OPERATION_ABORTED => return error.Canceled,
- .NOT_ENOUGH_QUOTA => return error.SystemResources,
- .IO_PENDING => unreachable,
- .NO_DATA => return error.BrokenPipe,
- .INVALID_HANDLE => return error.NotOpenForWriting,
- .LOCK_VIOLATION => return error.LockViolation,
- .NETNAME_DELETED => return error.ConnectionResetByPeer,
- .ACCESS_DENIED => return error.AccessDenied,
- .WORKING_SET_QUOTA => return error.SystemResources,
- else => |err| return unexpectedError(err),
- }
- }
- return bytes_written;
-}
-
pub const GetCurrentDirectoryError = error{
NameTooLong,
Unexpected,
diff --git a/lib/std/posix.zig b/lib/std/posix.zig
index 4bd19acb1a..52bc5f83e8 100644
--- a/lib/std/posix.zig
+++ b/lib/std/posix.zig
@@ -486,34 +486,8 @@ pub const ReadError = std.Io.File.Reader.Error;
/// The corresponding POSIX limit is `maxInt(isize)`.
pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
if (buf.len == 0) return 0;
- if (native_os == .windows) {
- return windows.ReadFile(fd, buf, null);
- }
- if (native_os == .wasi and !builtin.link_libc) {
- const iovs = [1]iovec{iovec{
- .base = buf.ptr,
- .len = buf.len,
- }};
-
- var nread: usize = undefined;
- switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
- .SUCCESS => return nread,
- .INTR => unreachable,
- .INVAL => unreachable,
- .FAULT => unreachable,
- .AGAIN => unreachable,
- .BADF => return error.NotOpenForReading, // Can be a race condition.
- .IO => return error.InputOutput,
- .ISDIR => return error.IsDir,
- .NOBUFS => return error.SystemResources,
- .NOMEM => return error.SystemResources,
- .NOTCONN => return error.SocketUnconnected,
- .CONNRESET => return error.ConnectionResetByPeer,
- .TIMEDOUT => return error.Timeout,
- .NOTCAPABLE => return error.AccessDenied,
- else => |err| return unexpectedErrno(err),
- }
- }
+ if (native_os == .windows) @compileError("unsupported OS");
+ if (native_os == .wasi) @compileError("unsupported OS");
// Prevents EINVAL.
const max_count = switch (native_os) {
@@ -606,34 +580,8 @@ pub const WriteError = error{
/// The corresponding POSIX limit is `maxInt(isize)`.
pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
if (bytes.len == 0) return 0;
- if (native_os == .windows) {
- return windows.WriteFile(fd, bytes, null);
- }
-
- if (native_os == .wasi and !builtin.link_libc) {
- const ciovs = [_]iovec_const{iovec_const{
- .base = bytes.ptr,
- .len = bytes.len,
- }};
- var nwritten: usize = undefined;
- switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
- .SUCCESS => return nwritten,
- .INTR => unreachable,
- .INVAL => unreachable,
- .FAULT => unreachable,
- .AGAIN => unreachable,
- .BADF => return error.NotOpenForWriting, // can be a race condition.
- .DESTADDRREQ => unreachable, // `connect` was never called.
- .DQUOT => return error.DiskQuota,
- .FBIG => return error.FileTooBig,
- .IO => return error.InputOutput,
- .NOSPC => return error.NoSpaceLeft,
- .PERM => return error.PermissionDenied,
- .PIPE => return error.BrokenPipe,
- .NOTCAPABLE => return error.AccessDenied,
- else => |err| return unexpectedErrno(err),
- }
- }
+ if (native_os == .windows) @compileError("unsupported OS");
+ if (native_os == .wasi) @compileError("unsupported OS");
const max_count = switch (native_os) {
.linux => 0x7ffff000,