authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-09 14:21:35-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-09 14:21:35-04:00
logc4262da8de025ca0236df9ebb823b727ca3d43fd
tree855c75970e6f82889f71a43e27d79216058b439b
parenta4310cf8b4f104802360cb854e2dcc48802301a6

implement os.path.real for windows and update allocator interface


7 files changed, 191 insertions(+), 36 deletions(-)

std/debug.zig+8-4
......@@ -974,9 +974,13 @@ fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
974974}
975975
976976fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
977 const result = %return globalAlloc(self, new_size, alignment);
978 @memcpy(result.ptr, old_mem.ptr, old_mem.len);
979 return result;
977 if (new_size <= old_mem.len) {
978 return old_mem[0..new_size];
979 } else {
980 const result = %return globalAlloc(self, new_size, alignment);
981 @memcpy(result.ptr, old_mem.ptr, old_mem.len);
982 return result;
983 }
980984}
981985
982fn globalFree(self: &mem.Allocator, ptr: &u8) { }
986fn globalFree(self: &mem.Allocator, memory: []u8) { }
std/io.zig+2-2
......@@ -65,7 +65,7 @@ error SystemFdQuotaExceeded;
6565error NameTooLong;
6666error NoDevice;
6767error PathNotFound;
68error NoMem;
68error OutOfMemory;
6969error Unseekable;
7070error EndOfFile;
7171error NoStdHandles;
......@@ -394,7 +394,7 @@ pub const InStream = struct {
394394 if (err > 0) {
395395 return switch (err) {
396396 system.EBADF => error.BadFd,
397 system.ENOMEM => error.NoMem,
397 system.ENOMEM => error.OutOfMemory,
398398 else => error.Unexpected,
399399 }
400400 }
std/mem.zig+48-22
......@@ -8,17 +8,22 @@ const Os = builtin.Os;
88
99pub const Cmp = math.Cmp;
1010
11error NoMem;
11error OutOfMemory;
1212
1313pub const Allocator = struct {
1414 /// Allocate byte_count bytes and return them in a slice, with the
1515 /// slicer's pointer aligned at least to alignment bytes.
1616 allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8,
1717
18 /// Guaranteed: old_mem.len > 0 and alignment >= alignment of old_mem.ptr
18 /// Guaranteed: `old_mem.len` is the same as what was returned from allocFn or reallocFn.
19 /// Guaranteed: alignment >= alignment of old_mem.ptr
20 ///
21 /// If `new_byte_count` is less than or equal to `old_mem.len` this function must
22 /// return successfully.
1923 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8,
2024
21 freeFn: fn (self: &Allocator, ptr: &u8),
25 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
26 freeFn: fn (self: &Allocator, old_mem: []u8),
2227
2328 fn create(self: &Allocator, comptime T: type) -> %&T {
2429 const slice = %return self.alloc(T, 1);
......@@ -41,24 +46,41 @@ pub const Allocator = struct {
4146 }
4247
4348 // Assert that old_mem.ptr is properly aligned.
44 _ = @alignCast(@alignOf(T), old_mem.ptr);
49 const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
4550
4651 const byte_count = %return math.mul(usize, @sizeOf(T), n);
47 const byte_slice = %return self.reallocFn(self, ([]u8)(old_mem), byte_count, @alignOf(T));
48 ([]T)(@alignCast(@alignOf(T), byte_slice))
52 const byte_slice = %return self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
53 return ([]T)(@alignCast(@alignOf(T), byte_slice));
54 }
55
56 /// Reallocate, but `n` must be less than or equal to `old_mem.len`.
57 /// Unlike `realloc`, this function cannot fail.
58 /// Shrinking to 0 is the same as calling `free`.
59 fn shrink(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> []T {
60 if (n == 0) {
61 self.free(old_mem);
62 return old_mem[0..0];
63 }
64
65 assert(n <= old_mem.len);
66
67 // Assert that old_mem.ptr is properly aligned.
68 const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
69
70 // Here we skip the overflow checking on the multiplication because
71 // n <= old_mem.len and the multiplication didn't overflow for that operation.
72 const byte_count = @sizeOf(T) * n;
73
74 const byte_slice = %%self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
75 return ([]T)(@alignCast(@alignOf(T), byte_slice));
4976 }
5077
5178 fn free(self: &Allocator, memory: var) {
52 const ptr = if (@typeId(@typeOf(memory)) == builtin.TypeId.Pointer) {
53 memory
54 } else {
55 const const_slice = ([]const u8)(memory);
56 if (memory.len == 0)
57 return;
58 const_slice.ptr
59 };
60 const non_const_ptr = @intToPtr(&u8, @ptrToInt(ptr));
61 self.freeFn(self, non_const_ptr);
79 const bytes = ([]const u8)(memory);
80 if (bytes.len == 0)
81 return;
82 const non_const_ptr = @intToPtr(&u8, @ptrToInt(bytes.ptr));
83 self.freeFn(self, non_const_ptr[0..bytes.len]);
6284 }
6385};
6486
......@@ -74,7 +96,7 @@ pub const IncrementingAllocator = struct {
7496 const addr = p.mmap(null, capacity, p.PROT_READ|p.PROT_WRITE,
7597 p.MAP_PRIVATE|p.MAP_ANONYMOUS|p.MAP_NORESERVE, -1, 0);
7698 if (addr == p.MAP_FAILED) {
77 return error.NoMem;
99 return error.OutOfMemory;
78100 }
79101 return IncrementingAllocator {
80102 .allocator = Allocator {
......@@ -132,7 +154,7 @@ pub const IncrementingAllocator = struct {
132154 const adjusted_index = self.end_index + march_forward_bytes;
133155 const new_end_index = adjusted_index + n;
134156 if (new_end_index > self.bytes.len) {
135 return error.NoMem;
157 return error.OutOfMemory;
136158 }
137159 const result = self.bytes[adjusted_index .. new_end_index];
138160 self.end_index = new_end_index;
......@@ -140,12 +162,16 @@ pub const IncrementingAllocator = struct {
140162 }
141163
142164 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
143 const result = %return alloc(allocator, new_size, alignment);
144 copy(u8, result, old_mem);
145 return result;
165 if (new_size <= old_mem.len) {
166 return old_mem[0..new_size];
167 } else {
168 const result = %return alloc(allocator, new_size, alignment);
169 copy(u8, result, old_mem);
170 return result;
171 }
146172 }
147173
148 fn free(allocator: &Allocator, bytes: &u8) {
174 fn free(allocator: &Allocator, bytes: []u8) {
149175 // Do nothing. That's the point of an incrementing allocator.
150176 }
151177};
std/net.zig+2-2
......@@ -8,7 +8,7 @@ error Io;
88error TimedOut;
99error ConnectionReset;
1010error ConnectionRefused;
11error NoMem;
11error OutOfMemory;
1212error NotSocket;
1313error BadFd;
1414
......@@ -38,7 +38,7 @@ const Connection = struct {
3838 linux.EFAULT => unreachable,
3939 linux.ENOTSOCK => return error.NotSocket,
4040 linux.EINTR => return error.SigInterrupt,
41 linux.ENOMEM => return error.NoMem,
41 linux.ENOMEM => return error.OutOfMemory,
4242 linux.ECONNREFUSED => return error.ConnectionRefused,
4343 linux.EBADF => return error.BadFd,
4444 // TODO more error values
std/os/index.zig+8-3
......@@ -491,7 +491,7 @@ pub fn getCwd(allocator: &Allocator) -> %[]u8 {
491491 continue;
492492 }
493493
494 return buf[0..result];
494 return allocator.shrink(u8, buf, result);
495495 }
496496 },
497497 else => {
......@@ -506,12 +506,17 @@ pub fn getCwd(allocator: &Allocator) -> %[]u8 {
506506 return error.Unexpected;
507507 }
508508
509 return cstr.toSlice(buf.ptr);
509 return allocator.shrink(u8, buf, cstr.len(buf.ptr));
510510 }
511511 },
512512 }
513513}
514514
515test "os.getCwd" {
516 // at least call it so it gets compiled
517 _ = getCwd(&debug.global_allocator);
518}
519
515520pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
516521 const full_buf = %return allocator.alloc(u8, existing_path.len + new_path.len + 2);
517522 defer allocator.free(full_buf);
......@@ -988,7 +993,7 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
988993 result_buf = %return allocator.realloc(u8, result_buf, result_buf.len * 2);
989994 continue;
990995 }
991 return result_buf[0..ret_val];
996 return allocator.shrink(u8, result_buf, ret_val);
992997 }
993998}
994999
std/os/path.zig+64-3
......@@ -6,8 +6,9 @@ const mem = @import("../mem.zig");
66const fmt = @import("../fmt/index.zig");
77const Allocator = mem.Allocator;
88const os = @import("index.zig");
9const math = @import("../math.zig");
9const math = @import("../math/index.zig");
1010const posix = os.posix;
11const windows = os.windows;
1112const c = @import("../c/index.zig");
1213const cstr = @import("../cstr.zig");
1314
......@@ -921,7 +922,62 @@ error Unexpected;
921922/// Caller must deallocate result.
922923pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
923924 switch (builtin.os) {
924 Os.windows => @compileError("TODO implement os.path.real for windows"),
925 Os.windows => {
926 const pathname_buf = %return allocator.alloc(u8, pathname.len + 1);
927 defer allocator.free(pathname_buf);
928
929 mem.copy(u8, pathname_buf, pathname);
930 pathname_buf[pathname.len] = 0;
931
932 const h_file = windows.CreateFileA(pathname_buf.ptr,
933 windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING,
934 windows.FILE_ATTRIBUTE_NORMAL, null);
935 if (h_file == windows.INVALID_HANDLE_VALUE) {
936 const err = windows.GetLastError();
937 return switch (err) {
938 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
939 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
940 windows.ERROR.FILENAME_EXCED_RANGE => error.NameTooLong,
941 else => error.Unexpected,
942 };
943 }
944 defer assert(windows.CloseHandle(h_file));
945 var buf = %return allocator.alloc(u8, 256);
946 %defer allocator.free(buf);
947 while (true) {
948 const buf_len = math.cast(windows.DWORD, buf.len) %% return error.NameTooLong;
949 const result = windows.GetFinalPathNameByHandleA(h_file, buf.ptr, buf_len, windows.VOLUME_NAME_DOS);
950
951 if (result == 0) {
952 const err = windows.GetLastError();
953 return switch (err) {
954 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
955 windows.ERROR.NOT_ENOUGH_MEMORY => error.OutOfMemory,
956 windows.ERROR.INVALID_PARAMETER => unreachable,
957 else => error.Unexpected,
958 };
959 }
960
961 if (result > buf.len) {
962 buf = %return allocator.realloc(u8, buf, result);
963 continue;
964 }
965
966 // windows returns \\?\ prepended to the path
967 // we strip it because nobody wants \\?\ prepended to their path
968 const final_len = if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {
969 var i: usize = 4;
970 while (i < result) : (i += 1) {
971 buf[i - 4] = buf[i];
972 }
973 result - 4
974 } else {
975 result
976 };
977
978 return allocator.shrink(u8, buf, final_len);
979 }
980 },
925981 Os.darwin, Os.macosx, Os.ios => {
926982 // TODO instead of calling the libc function here, port the implementation
927983 // to Zig, and then remove the NameTooLong error possibility.
......@@ -950,7 +1006,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
9501006 else => error.Unexpected,
9511007 };
9521008 }
953 return cstr.toSlice(result_buf.ptr);
1009 return allocator.realloc(u8, result_buf, cstr.len(result_buf.ptr));
9541010 },
9551011 Os.linux => {
9561012 const fd = %return os.posixOpen(pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0, allocator);
......@@ -964,3 +1020,8 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
9641020 else => @compileError("TODO implement os.path.real for " ++ @enumTagName(builtin.os)),
9651021 }
9661022}
1023
1024test "os.path.real" {
1025 // at least call it so it gets compiled
1026 _ = real(&debug.global_allocator, "some_path");
1027}
std/os/windows/index.zig+59
......@@ -1,5 +1,11 @@
11pub const ERROR = @import("error.zig");
22
3pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) -> BOOL;
4
5pub extern "kernel32" stdcallcc fn CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: DWORD,
6 dwShareMode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD,
7 dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE) -> HANDLE;
8
39pub extern "kernel32" stdcallcc fn CryptAcquireContext(phProv: &HCRYPTPROV, pszContainer: LPCTSTR,
410 pszProvider: LPCTSTR, dwProvType: DWORD, dwFlags: DWORD) -> bool;
511
......@@ -26,6 +32,9 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE
2632 in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: &c_void,
2733 in_dwBufferSize: DWORD) -> bool;
2834
35pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR,
36 cchFilePath: DWORD, dwFlags: DWORD) -> DWORD;
37
2938/// Retrieves a handle to the specified standard device (standard input, standard output, or standard error).
3039pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE;
3140
......@@ -131,3 +140,53 @@ pub const FILE_NAME_INFO = extern struct {
131140 FileNameLength: DWORD,
132141 FileName: [1]WCHAR,
133142};
143
144
145/// Return the normalized drive name. This is the default.
146pub const FILE_NAME_NORMALIZED = 0x0;
147/// Return the opened file name (not normalized).
148pub const FILE_NAME_OPENED = 0x8;
149
150/// Return the path with the drive letter. This is the default.
151pub const VOLUME_NAME_DOS = 0x0;
152/// Return the path with a volume GUID path instead of the drive name.
153pub const VOLUME_NAME_GUID = 0x1;
154/// Return the path with no drive information.
155pub const VOLUME_NAME_NONE = 0x4;
156/// Return the path with the volume device path.
157pub const VOLUME_NAME_NT = 0x2;
158
159
160pub const SECURITY_ATTRIBUTES = extern struct {
161 nLength: DWORD,
162 lpSecurityDescriptor: LPVOID,
163 bInheritHandle: BOOL,
164};
165pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES;
166pub const LPSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES;
167
168
169pub const GENERIC_READ = 0x80000000;
170pub const GENERIC_WRITE = 0x40000000;
171pub const GENERIC_EXECUTE = 0x20000000;
172pub const GENERIC_ALL = 0x10000000;
173
174pub const FILE_SHARE_DELETE = 0x00000004;
175pub const FILE_SHARE_READ = 0x00000001;
176pub const FILE_SHARE_WRITE = 0x00000002;
177
178pub const CREATE_ALWAYS = 2;
179pub const CREATE_NEW = 1;
180pub const OPEN_ALWAYS = 4;
181pub const OPEN_EXISTING = 3;
182pub const TRUNCATE_EXISTING = 5;
183
184
185pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
186pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
187pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
188pub const FILE_ATTRIBUTE_NORMAL = 0x80;
189pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
190pub const FILE_ATTRIBUTE_READONLY = 0x1;
191pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
192pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;