Commit e6334fe46d

Andrew Kelley <superjoe30@gmail.com>
2017-10-09 21:59:10
implement std.io.InStream for windows
See #302
1 parent 055b856
Changed files (3)
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;
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));
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;
         }
     }