Skip to content

Commit

Permalink
implement std.io.InStream for windows
Browse files Browse the repository at this point in the history
See #302
  • Loading branch information
andrewrk committed Oct 9, 2017
1 parent 055b856 commit e6334fe
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 10 deletions.
40 changes: 32 additions & 8 deletions std/io.zig
Expand Up @@ -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;
}
}

Expand All @@ -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) {
Expand All @@ -273,17 +281,33 @@ 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;
index += amt_read;
}
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;
}
}

Expand Down
50 changes: 50 additions & 0 deletions std/os/index.zig
Expand Up @@ -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;
Expand Down Expand Up @@ -299,13 +303,59 @@ 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,
}
}
return i32(result);
}
}


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));
Expand Down
2 changes: 0 additions & 2 deletions std/os/windows/index.zig
Expand Up @@ -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;
Expand Down

0 comments on commit e6334fe

Please sign in to comment.