diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-26 16:12:09 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-26 19:58:56 -0800 |
| commit | f160d5f979c9f2bcc98ef0169a879a76d0234ed2 (patch) | |
| tree | 0d0ffdb2d1399ed0001c792d81d11491743bab58 /lib/std | |
| parent | feeed922e1a7d89b99572d5577c814f9e6293b9b (diff) | |
| download | zig-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.zig | 90 | ||||
| -rw-r--r-- | lib/std/os/windows.zig | 109 | ||||
| -rw-r--r-- | lib/std/posix.zig | 60 |
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, |
