From e6334fe46d29f3f9d488b80453cc5bd12ffb97ea Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Oct 2017 15:59:10 -0400 Subject: implement std.io.InStream for windows See #302 --- std/io.zig | 40 ++++++++++++++++++++++++++++++-------- std/os/index.zig | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ std/os/windows/index.zig | 2 -- 3 files changed, 82 insertions(+), 10 deletions(-) (limited to 'std') diff --git a/std/io.zig b/std/io.zig index e1f4a818fb..72cc85e437 100644 --- a/std/io.zig +++ b/std/io.zig @@ -242,9 +242,15 @@ pub const InStream = struct { .handle = {}, }; } else if (is_windows) { - @compileError("TODO windows InStream.open"); + const handle = %return os.windowsOpen(path, system.GENERIC_READ, system.FILE_SHARE_READ, + system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL, allocator); + return InStream { + .fd = {}, + .handle_id = undefined, + .handle = handle, + }; } else { - @compileError("Unsupported OS"); + unreachable; } } @@ -253,18 +259,20 @@ pub const InStream = struct { pub fn close(self: &InStream) { if (is_posix) { os.posixClose(self.fd); + } else if (is_windows) { + os.windowsClose(%%self.getHandle()); } else { - @compileError("Unsupported OS"); + unreachable; } } /// Returns the number of bytes read. If the number read is smaller than buf.len, then /// the stream reached End Of File. - pub fn read(is: &InStream, buf: []u8) -> %usize { + pub fn read(self: &InStream, buf: []u8) -> %usize { if (is_posix) { var index: usize = 0; while (index < buf.len) { - const amt_read = system.read(is.fd, &buf[index], buf.len - index); + const amt_read = system.read(self.fd, &buf[index], buf.len - index); const read_err = system.getErrno(amt_read); if (read_err > 0) { switch (read_err) { @@ -273,7 +281,7 @@ pub const InStream = struct { system.EFAULT => unreachable, system.EBADF => return error.BadFd, system.EIO => return error.Io, - else => return error.Unexpected, + else => return error.Unexpected, } } if (amt_read == 0) return index; @@ -281,9 +289,25 @@ pub const InStream = struct { } return index; } else if (is_windows) { - @compileError("TODO windows read impl"); + const handle = %return self.getHandle(); + var index: usize = 0; + while (index < buf.len) { + const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buf.len - index)); + var amt_read: system.DWORD = undefined; + if (!system.ReadFile(handle, @ptrCast(&c_void, &buf[index]), want_read_count, &amt_read, null)) { + const err = system.GetLastError(); + return switch (err) { + system.ERROR.OPERATION_ABORTED => continue, + system.ERROR.BROKEN_PIPE => error.PipeFail, + else => error.Unexpected, + }; + } + if (amt_read == 0) return index; + index += amt_read; + } + return index; } else { - @compileError("Unsupported OS"); + unreachable; } } diff --git a/std/os/index.zig b/std/os/index.zig index 0a1b39d66d..be96f7f776 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -151,6 +151,10 @@ pub fn posixClose(fd: i32) { } } +pub fn windowsClose(handle: windows.HANDLE) { + assert(windows.CloseHandle(handle)); +} + /// Calls POSIX read, and keeps trying if it gets interrupted. pub fn posixRead(fd: i32, buf: []u8) -> %void { var index: usize = 0; @@ -299,6 +303,7 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al posix.ENOSPC => error.NoSpaceLeft, posix.ENOTDIR => error.NotDir, posix.EPERM => error.AccessDenied, + posix.EEXIST => error.PathAlreadyExists, else => error.Unexpected, } } @@ -306,6 +311,51 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al } } + +error SharingViolation; +error PipeBusy; + +/// `file_path` may need to be copied in memory to add a null terminating byte. In this case +/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed +/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned. +/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. +pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD, + creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&Allocator) -> %windows.HANDLE +{ + var stack_buf: [max_noalloc_path_len]u8 = undefined; + var path0: []u8 = undefined; + var need_free = false; + defer if (need_free) (??allocator).free(path0); + + if (file_path.len < stack_buf.len) { + path0 = stack_buf[0..file_path.len + 1]; + } else if (allocator) |a| { + path0 = %return a.alloc(u8, file_path.len + 1); + need_free = true; + } else { + return error.NameTooLong; + } + mem.copy(u8, path0, file_path); + path0[file_path.len] = 0; + + const result = windows.CreateFileA(path0.ptr, desired_access, share_mode, null, creation_disposition, + flags_and_attrs, null); + + if (result == windows.INVALID_HANDLE_VALUE) { + const err = windows.GetLastError(); + return switch (err) { + windows.ERROR.SHARING_VIOLATION => error.SharingViolation, + windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => error.PathAlreadyExists, + windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, + windows.ERROR.ACCESS_DENIED => error.AccessDenied, + windows.ERROR.PIPE_BUSY => error.PipeBusy, + else => error.Unexpected, + }; + } + + return result; +} + pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void { while (true) { const err = posix.getErrno(posix.dup2(old_fd, new_fd)); diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index bf230a2a0d..a7ba338bf7 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -38,8 +38,6 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz /// Retrieves a handle to the specified standard device (standard input, standard output, or standard error). pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE; -/// Reads data from the specified file or input/output (I/O) device. Reads occur at the position specified by the file pointer if supported by the device. -/// This function is designed for both synchronous and asynchronous operations. For a similar function designed solely for asynchronous operation, see ReadFileEx. pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID, in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD, in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; -- cgit v1.2.3