implement os.path.real for windows and update allocator interface
parent
a4310cf8b4
commit
c4262da8de
|
@ -974,9 +974,13 @@ fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
|
|||
}
|
||||
|
||||
fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
const result = %return globalAlloc(self, new_size, alignment);
|
||||
@memcpy(result.ptr, old_mem.ptr, old_mem.len);
|
||||
return result;
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const result = %return globalAlloc(self, new_size, alignment);
|
||||
@memcpy(result.ptr, old_mem.ptr, old_mem.len);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn globalFree(self: &mem.Allocator, ptr: &u8) { }
|
||||
fn globalFree(self: &mem.Allocator, memory: []u8) { }
|
||||
|
|
|
@ -65,7 +65,7 @@ error SystemFdQuotaExceeded;
|
|||
error NameTooLong;
|
||||
error NoDevice;
|
||||
error PathNotFound;
|
||||
error NoMem;
|
||||
error OutOfMemory;
|
||||
error Unseekable;
|
||||
error EndOfFile;
|
||||
error NoStdHandles;
|
||||
|
@ -394,7 +394,7 @@ pub const InStream = struct {
|
|||
if (err > 0) {
|
||||
return switch (err) {
|
||||
system.EBADF => error.BadFd,
|
||||
system.ENOMEM => error.NoMem,
|
||||
system.ENOMEM => error.OutOfMemory,
|
||||
else => error.Unexpected,
|
||||
}
|
||||
}
|
||||
|
|
70
std/mem.zig
70
std/mem.zig
|
@ -8,17 +8,22 @@ const Os = builtin.Os;
|
|||
|
||||
pub const Cmp = math.Cmp;
|
||||
|
||||
error NoMem;
|
||||
error OutOfMemory;
|
||||
|
||||
pub const Allocator = struct {
|
||||
/// Allocate byte_count bytes and return them in a slice, with the
|
||||
/// slicer's pointer aligned at least to alignment bytes.
|
||||
allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8,
|
||||
|
||||
/// Guaranteed: old_mem.len > 0 and alignment >= alignment of old_mem.ptr
|
||||
/// Guaranteed: `old_mem.len` is the same as what was returned from allocFn or reallocFn.
|
||||
/// Guaranteed: alignment >= alignment of old_mem.ptr
|
||||
///
|
||||
/// If `new_byte_count` is less than or equal to `old_mem.len` this function must
|
||||
/// return successfully.
|
||||
reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8,
|
||||
|
||||
freeFn: fn (self: &Allocator, ptr: &u8),
|
||||
/// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
|
||||
freeFn: fn (self: &Allocator, old_mem: []u8),
|
||||
|
||||
fn create(self: &Allocator, comptime T: type) -> %&T {
|
||||
const slice = %return self.alloc(T, 1);
|
||||
|
@ -41,24 +46,41 @@ pub const Allocator = struct {
|
|||
}
|
||||
|
||||
// Assert that old_mem.ptr is properly aligned.
|
||||
_ = @alignCast(@alignOf(T), old_mem.ptr);
|
||||
const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
|
||||
|
||||
const byte_count = %return math.mul(usize, @sizeOf(T), n);
|
||||
const byte_slice = %return self.reallocFn(self, ([]u8)(old_mem), byte_count, @alignOf(T));
|
||||
([]T)(@alignCast(@alignOf(T), byte_slice))
|
||||
const byte_slice = %return self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
|
||||
return ([]T)(@alignCast(@alignOf(T), byte_slice));
|
||||
}
|
||||
|
||||
/// Reallocate, but `n` must be less than or equal to `old_mem.len`.
|
||||
/// Unlike `realloc`, this function cannot fail.
|
||||
/// Shrinking to 0 is the same as calling `free`.
|
||||
fn shrink(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> []T {
|
||||
if (n == 0) {
|
||||
self.free(old_mem);
|
||||
return old_mem[0..0];
|
||||
}
|
||||
|
||||
assert(n <= old_mem.len);
|
||||
|
||||
// Assert that old_mem.ptr is properly aligned.
|
||||
const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
|
||||
|
||||
// Here we skip the overflow checking on the multiplication because
|
||||
// n <= old_mem.len and the multiplication didn't overflow for that operation.
|
||||
const byte_count = @sizeOf(T) * n;
|
||||
|
||||
const byte_slice = %%self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
|
||||
return ([]T)(@alignCast(@alignOf(T), byte_slice));
|
||||
}
|
||||
|
||||
fn free(self: &Allocator, memory: var) {
|
||||
const ptr = if (@typeId(@typeOf(memory)) == builtin.TypeId.Pointer) {
|
||||
memory
|
||||
} else {
|
||||
const const_slice = ([]const u8)(memory);
|
||||
if (memory.len == 0)
|
||||
return;
|
||||
const_slice.ptr
|
||||
};
|
||||
const non_const_ptr = @intToPtr(&u8, @ptrToInt(ptr));
|
||||
self.freeFn(self, non_const_ptr);
|
||||
const bytes = ([]const u8)(memory);
|
||||
if (bytes.len == 0)
|
||||
return;
|
||||
const non_const_ptr = @intToPtr(&u8, @ptrToInt(bytes.ptr));
|
||||
self.freeFn(self, non_const_ptr[0..bytes.len]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -74,7 +96,7 @@ pub const IncrementingAllocator = struct {
|
|||
const addr = p.mmap(null, capacity, p.PROT_READ|p.PROT_WRITE,
|
||||
p.MAP_PRIVATE|p.MAP_ANONYMOUS|p.MAP_NORESERVE, -1, 0);
|
||||
if (addr == p.MAP_FAILED) {
|
||||
return error.NoMem;
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
return IncrementingAllocator {
|
||||
.allocator = Allocator {
|
||||
|
@ -132,7 +154,7 @@ pub const IncrementingAllocator = struct {
|
|||
const adjusted_index = self.end_index + march_forward_bytes;
|
||||
const new_end_index = adjusted_index + n;
|
||||
if (new_end_index > self.bytes.len) {
|
||||
return error.NoMem;
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
const result = self.bytes[adjusted_index .. new_end_index];
|
||||
self.end_index = new_end_index;
|
||||
|
@ -140,12 +162,16 @@ pub const IncrementingAllocator = struct {
|
|||
}
|
||||
|
||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
const result = %return alloc(allocator, new_size, alignment);
|
||||
copy(u8, result, old_mem);
|
||||
return result;
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const result = %return alloc(allocator, new_size, alignment);
|
||||
copy(u8, result, old_mem);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn free(allocator: &Allocator, bytes: &u8) {
|
||||
fn free(allocator: &Allocator, bytes: []u8) {
|
||||
// Do nothing. That's the point of an incrementing allocator.
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ error Io;
|
|||
error TimedOut;
|
||||
error ConnectionReset;
|
||||
error ConnectionRefused;
|
||||
error NoMem;
|
||||
error OutOfMemory;
|
||||
error NotSocket;
|
||||
error BadFd;
|
||||
|
||||
|
@ -38,7 +38,7 @@ const Connection = struct {
|
|||
linux.EFAULT => unreachable,
|
||||
linux.ENOTSOCK => return error.NotSocket,
|
||||
linux.EINTR => return error.SigInterrupt,
|
||||
linux.ENOMEM => return error.NoMem,
|
||||
linux.ENOMEM => return error.OutOfMemory,
|
||||
linux.ECONNREFUSED => return error.ConnectionRefused,
|
||||
linux.EBADF => return error.BadFd,
|
||||
// TODO more error values
|
||||
|
|
|
@ -491,7 +491,7 @@ pub fn getCwd(allocator: &Allocator) -> %[]u8 {
|
|||
continue;
|
||||
}
|
||||
|
||||
return buf[0..result];
|
||||
return allocator.shrink(u8, buf, result);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
|
@ -506,12 +506,17 @@ pub fn getCwd(allocator: &Allocator) -> %[]u8 {
|
|||
return error.Unexpected;
|
||||
}
|
||||
|
||||
return cstr.toSlice(buf.ptr);
|
||||
return allocator.shrink(u8, buf, cstr.len(buf.ptr));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test "os.getCwd" {
|
||||
// at least call it so it gets compiled
|
||||
_ = getCwd(&debug.global_allocator);
|
||||
}
|
||||
|
||||
pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
|
||||
const full_buf = %return allocator.alloc(u8, existing_path.len + new_path.len + 2);
|
||||
defer allocator.free(full_buf);
|
||||
|
@ -988,7 +993,7 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
|
|||
result_buf = %return allocator.realloc(u8, result_buf, result_buf.len * 2);
|
||||
continue;
|
||||
}
|
||||
return result_buf[0..ret_val];
|
||||
return allocator.shrink(u8, result_buf, ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@ const mem = @import("../mem.zig");
|
|||
const fmt = @import("../fmt/index.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
const os = @import("index.zig");
|
||||
const math = @import("../math.zig");
|
||||
const math = @import("../math/index.zig");
|
||||
const posix = os.posix;
|
||||
const windows = os.windows;
|
||||
const c = @import("../c/index.zig");
|
||||
const cstr = @import("../cstr.zig");
|
||||
|
||||
|
@ -921,7 +922,62 @@ error Unexpected;
|
|||
/// Caller must deallocate result.
|
||||
pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
|
||||
switch (builtin.os) {
|
||||
Os.windows => @compileError("TODO implement os.path.real for windows"),
|
||||
Os.windows => {
|
||||
const pathname_buf = %return allocator.alloc(u8, pathname.len + 1);
|
||||
defer allocator.free(pathname_buf);
|
||||
|
||||
mem.copy(u8, pathname_buf, pathname);
|
||||
pathname_buf[pathname.len] = 0;
|
||||
|
||||
const h_file = windows.CreateFileA(pathname_buf.ptr,
|
||||
windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING,
|
||||
windows.FILE_ATTRIBUTE_NORMAL, null);
|
||||
if (h_file == windows.INVALID_HANDLE_VALUE) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
|
||||
windows.ERROR.ACCESS_DENIED => error.AccessDenied,
|
||||
windows.ERROR.FILENAME_EXCED_RANGE => error.NameTooLong,
|
||||
else => error.Unexpected,
|
||||
};
|
||||
}
|
||||
defer assert(windows.CloseHandle(h_file));
|
||||
var buf = %return allocator.alloc(u8, 256);
|
||||
%defer allocator.free(buf);
|
||||
while (true) {
|
||||
const buf_len = math.cast(windows.DWORD, buf.len) %% return error.NameTooLong;
|
||||
const result = windows.GetFinalPathNameByHandleA(h_file, buf.ptr, buf_len, windows.VOLUME_NAME_DOS);
|
||||
|
||||
if (result == 0) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
|
||||
windows.ERROR.NOT_ENOUGH_MEMORY => error.OutOfMemory,
|
||||
windows.ERROR.INVALID_PARAMETER => unreachable,
|
||||
else => error.Unexpected,
|
||||
};
|
||||
}
|
||||
|
||||
if (result > buf.len) {
|
||||
buf = %return allocator.realloc(u8, buf, result);
|
||||
continue;
|
||||
}
|
||||
|
||||
// windows returns \\?\ prepended to the path
|
||||
// we strip it because nobody wants \\?\ prepended to their path
|
||||
const final_len = if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {
|
||||
var i: usize = 4;
|
||||
while (i < result) : (i += 1) {
|
||||
buf[i - 4] = buf[i];
|
||||
}
|
||||
result - 4
|
||||
} else {
|
||||
result
|
||||
};
|
||||
|
||||
return allocator.shrink(u8, buf, final_len);
|
||||
}
|
||||
},
|
||||
Os.darwin, Os.macosx, Os.ios => {
|
||||
// TODO instead of calling the libc function here, port the implementation
|
||||
// to Zig, and then remove the NameTooLong error possibility.
|
||||
|
@ -950,7 +1006,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
|
|||
else => error.Unexpected,
|
||||
};
|
||||
}
|
||||
return cstr.toSlice(result_buf.ptr);
|
||||
return allocator.realloc(u8, result_buf, cstr.len(result_buf.ptr));
|
||||
},
|
||||
Os.linux => {
|
||||
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 {
|
|||
else => @compileError("TODO implement os.path.real for " ++ @enumTagName(builtin.os)),
|
||||
}
|
||||
}
|
||||
|
||||
test "os.path.real" {
|
||||
// at least call it so it gets compiled
|
||||
_ = real(&debug.global_allocator, "some_path");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
pub const ERROR = @import("error.zig");
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) -> BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: DWORD,
|
||||
dwShareMode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD,
|
||||
dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE) -> HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CryptAcquireContext(phProv: &HCRYPTPROV, pszContainer: LPCTSTR,
|
||||
pszProvider: LPCTSTR, dwProvType: DWORD, dwFlags: DWORD) -> bool;
|
||||
|
||||
|
@ -26,6 +32,9 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE
|
|||
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: &c_void,
|
||||
in_dwBufferSize: DWORD) -> bool;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR,
|
||||
cchFilePath: DWORD, dwFlags: DWORD) -> DWORD;
|
||||
|
||||
/// 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;
|
||||
|
||||
|
@ -131,3 +140,53 @@ pub const FILE_NAME_INFO = extern struct {
|
|||
FileNameLength: DWORD,
|
||||
FileName: [1]WCHAR,
|
||||
};
|
||||
|
||||
|
||||
/// Return the normalized drive name. This is the default.
|
||||
pub const FILE_NAME_NORMALIZED = 0x0;
|
||||
/// Return the opened file name (not normalized).
|
||||
pub const FILE_NAME_OPENED = 0x8;
|
||||
|
||||
/// Return the path with the drive letter. This is the default.
|
||||
pub const VOLUME_NAME_DOS = 0x0;
|
||||
/// Return the path with a volume GUID path instead of the drive name.
|
||||
pub const VOLUME_NAME_GUID = 0x1;
|
||||
/// Return the path with no drive information.
|
||||
pub const VOLUME_NAME_NONE = 0x4;
|
||||
/// Return the path with the volume device path.
|
||||
pub const VOLUME_NAME_NT = 0x2;
|
||||
|
||||
|
||||
pub const SECURITY_ATTRIBUTES = extern struct {
|
||||
nLength: DWORD,
|
||||
lpSecurityDescriptor: LPVOID,
|
||||
bInheritHandle: BOOL,
|
||||
};
|
||||
pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES;
|
||||
pub const LPSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES;
|
||||
|
||||
|
||||
pub const GENERIC_READ = 0x80000000;
|
||||
pub const GENERIC_WRITE = 0x40000000;
|
||||
pub const GENERIC_EXECUTE = 0x20000000;
|
||||
pub const GENERIC_ALL = 0x10000000;
|
||||
|
||||
pub const FILE_SHARE_DELETE = 0x00000004;
|
||||
pub const FILE_SHARE_READ = 0x00000001;
|
||||
pub const FILE_SHARE_WRITE = 0x00000002;
|
||||
|
||||
pub const CREATE_ALWAYS = 2;
|
||||
pub const CREATE_NEW = 1;
|
||||
pub const OPEN_ALWAYS = 4;
|
||||
pub const OPEN_EXISTING = 3;
|
||||
pub const TRUNCATE_EXISTING = 5;
|
||||
|
||||
|
||||
pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
|
||||
pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
|
||||
pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
|
||||
pub const FILE_ATTRIBUTE_NORMAL = 0x80;
|
||||
pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
|
||||
pub const FILE_ATTRIBUTE_READONLY = 0x1;
|
||||
pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
|
||||
pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
|
||||
|
|
Loading…
Reference in New Issue