authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-09 15:59:10-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-09 15:59:10-04:00
loge6334fe46d29f3f9d488b80453cc5bd12ffb97ea
tree58dd5a7bd39dffaaa80a21379054d7354ffb6066
parent055b856f13ce527a92b2b28760daabce41edd440

implement std.io.InStream for windows

See #302

3 files changed, 82 insertions(+), 10 deletions(-)

std/io.zig+32-8
...@@ -242,9 +242,15 @@ pub const InStream = struct {...@@ -242,9 +242,15 @@ pub const InStream = struct {
242 .handle = {},242 .handle = {},
243 };243 };
244 } else if (is_windows) {244 } else if (is_windows) {
245 @compileError("TODO windows InStream.open");245 const handle = %return os.windowsOpen(path, system.GENERIC_READ, system.FILE_SHARE_READ,
246 system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL, allocator);
247 return InStream {
248 .fd = {},
249 .handle_id = undefined,
250 .handle = handle,
251 };
246 } else {252 } else {
247 @compileError("Unsupported OS");253 unreachable;
248 }254 }
249 }255 }
250256
...@@ -253,18 +259,20 @@ pub const InStream = struct {...@@ -253,18 +259,20 @@ pub const InStream = struct {
253 pub fn close(self: &InStream) {259 pub fn close(self: &InStream) {
254 if (is_posix) {260 if (is_posix) {
255 os.posixClose(self.fd);261 os.posixClose(self.fd);
262 } else if (is_windows) {
263 os.windowsClose(%%self.getHandle());
256 } else {264 } else {
257 @compileError("Unsupported OS");265 unreachable;
258 }266 }
259 }267 }
260268
261 /// Returns the number of bytes read. If the number read is smaller than buf.len, then269 /// Returns the number of bytes read. If the number read is smaller than buf.len, then
262 /// the stream reached End Of File.270 /// the stream reached End Of File.
263 pub fn read(is: &InStream, buf: []u8) -> %usize {271 pub fn read(self: &InStream, buf: []u8) -> %usize {
264 if (is_posix) {272 if (is_posix) {
265 var index: usize = 0;273 var index: usize = 0;
266 while (index < buf.len) {274 while (index < buf.len) {
267 const amt_read = system.read(is.fd, &buf[index], buf.len - index);275 const amt_read = system.read(self.fd, &buf[index], buf.len - index);
268 const read_err = system.getErrno(amt_read);276 const read_err = system.getErrno(amt_read);
269 if (read_err > 0) {277 if (read_err > 0) {
270 switch (read_err) {278 switch (read_err) {
...@@ -273,7 +281,7 @@ pub const InStream = struct {...@@ -273,7 +281,7 @@ pub const InStream = struct {
273 system.EFAULT => unreachable,281 system.EFAULT => unreachable,
274 system.EBADF => return error.BadFd,282 system.EBADF => return error.BadFd,
275 system.EIO => return error.Io,283 system.EIO => return error.Io,
276 else => return error.Unexpected,284 else => return error.Unexpected,
277 }285 }
278 }286 }
279 if (amt_read == 0) return index;287 if (amt_read == 0) return index;
...@@ -281,9 +289,25 @@ pub const InStream = struct {...@@ -281,9 +289,25 @@ pub const InStream = struct {
281 }289 }
282 return index;290 return index;
283 } else if (is_windows) {291 } else if (is_windows) {
284 @compileError("TODO windows read impl");292 const handle = %return self.getHandle();
293 var index: usize = 0;
294 while (index < buf.len) {
295 const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buf.len - index));
296 var amt_read: system.DWORD = undefined;
297 if (!system.ReadFile(handle, @ptrCast(&c_void, &buf[index]), want_read_count, &amt_read, null)) {
298 const err = system.GetLastError();
299 return switch (err) {
300 system.ERROR.OPERATION_ABORTED => continue,
301 system.ERROR.BROKEN_PIPE => error.PipeFail,
302 else => error.Unexpected,
303 };
304 }
305 if (amt_read == 0) return index;
306 index += amt_read;
307 }
308 return index;
285 } else {309 } else {
286 @compileError("Unsupported OS");310 unreachable;
287 }311 }
288 }312 }
289313
std/os/index.zig+50
...@@ -151,6 +151,10 @@ pub fn posixClose(fd: i32) {...@@ -151,6 +151,10 @@ pub fn posixClose(fd: i32) {
151 }151 }
152}152}
153153
154pub fn windowsClose(handle: windows.HANDLE) {
155 assert(windows.CloseHandle(handle));
156}
157
154/// Calls POSIX read, and keeps trying if it gets interrupted.158/// Calls POSIX read, and keeps trying if it gets interrupted.
155pub fn posixRead(fd: i32, buf: []u8) -> %void {159pub fn posixRead(fd: i32, buf: []u8) -> %void {
156 var index: usize = 0;160 var index: usize = 0;
...@@ -299,6 +303,7 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al...@@ -299,6 +303,7 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al
299 posix.ENOSPC => error.NoSpaceLeft,303 posix.ENOSPC => error.NoSpaceLeft,
300 posix.ENOTDIR => error.NotDir,304 posix.ENOTDIR => error.NotDir,
301 posix.EPERM => error.AccessDenied,305 posix.EPERM => error.AccessDenied,
306 posix.EEXIST => error.PathAlreadyExists,
302 else => error.Unexpected,307 else => error.Unexpected,
303 }308 }
304 }309 }
...@@ -306,6 +311,51 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al...@@ -306,6 +311,51 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al
306 }311 }
307}312}
308313
314
315error SharingViolation;
316error PipeBusy;
317
318/// `file_path` may need to be copied in memory to add a null terminating byte. In this case
319/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed
320/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.
321/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
322pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD,
323 creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&Allocator) -> %windows.HANDLE
324{
325 var stack_buf: [max_noalloc_path_len]u8 = undefined;
326 var path0: []u8 = undefined;
327 var need_free = false;
328 defer if (need_free) (??allocator).free(path0);
329
330 if (file_path.len < stack_buf.len) {
331 path0 = stack_buf[0..file_path.len + 1];
332 } else if (allocator) |a| {
333 path0 = %return a.alloc(u8, file_path.len + 1);
334 need_free = true;
335 } else {
336 return error.NameTooLong;
337 }
338 mem.copy(u8, path0, file_path);
339 path0[file_path.len] = 0;
340
341 const result = windows.CreateFileA(path0.ptr, desired_access, share_mode, null, creation_disposition,
342 flags_and_attrs, null);
343
344 if (result == windows.INVALID_HANDLE_VALUE) {
345 const err = windows.GetLastError();
346 return switch (err) {
347 windows.ERROR.SHARING_VIOLATION => error.SharingViolation,
348 windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => error.PathAlreadyExists,
349 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
350 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
351 windows.ERROR.PIPE_BUSY => error.PipeBusy,
352 else => error.Unexpected,
353 };
354 }
355
356 return result;
357}
358
309pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {359pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
310 while (true) {360 while (true) {
311 const err = posix.getErrno(posix.dup2(old_fd, new_fd));361 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
std/os/windows/index.zig-2
...@@ -38,8 +38,6 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz...@@ -38,8 +38,6 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz
38/// Retrieves a handle to the specified standard device (standard input, standard output, or standard error).38/// Retrieves a handle to the specified standard device (standard input, standard output, or standard error).
39pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE;39pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE;
4040
41/// 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.
42/// This function is designed for both synchronous and asynchronous operations. For a similar function designed solely for asynchronous operation, see ReadFileEx.
43pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID,41pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID,
44 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,42 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
45 in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;43 in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;