diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e7c1df350..096ac50cfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -557,6 +557,7 @@ set(ZIG_STD_FILES "os/darwin_errno.zig" "os/epoch.zig" "os/file.zig" + "os/get_app_data_dir.zig" "os/get_user_id.zig" "os/index.zig" "os/linux/errno.zig" diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 5ffb43667d..741324c871 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -4,7 +4,6 @@ const io = std.io; const mem = std.mem; const Allocator = mem.Allocator; const Buffer = std.Buffer; -const unicode = std.unicode; const llvm = @import("llvm.zig"); const c = @import("c.zig"); const builtin = @import("builtin"); @@ -952,141 +951,5 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { } fn getZigDir(allocator: *mem.Allocator) ![]u8 { - return getAppDataDir(allocator, "zig"); -} - -const GetAppDataDirError = error{ - OutOfMemory, - AppDataDirUnavailable, -}; - -/// Caller owns returned memory. -/// TODO move to zig std lib -fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { - switch (builtin.os) { - builtin.Os.windows => { - var dir_path_ptr: [*]u16 = undefined; - switch (os.windows.SHGetKnownFolderPath( - &os.windows.FOLDERID_LocalAppData, - os.windows.KF_FLAG_CREATE, - null, - &dir_path_ptr, - )) { - os.windows.S_OK => { - defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); - const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)); - defer allocator.free(global_dir); - return os.path.join(allocator, global_dir, appname); - }, - os.windows.E_OUTOFMEMORY => return error.OutOfMemory, - else => return error.AppDataDirUnavailable, - } - }, - // TODO for macos it should be "~/Library/Application Support/" - else => { - const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd - }; - defer allocator.free(home_dir); - return os.path.join(allocator, home_dir, ".local", "share", appname); - }, - } -} - -test "getAppDataDir" { - const result = try getAppDataDir(std.debug.global_allocator, "zig"); - std.debug.warn("{}...", result); -} - -// TODO: put general purpose stuff in std.unicode -fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 { - var result = ArrayList(u8).init(allocator); - // optimistically guess that it will all be ascii. - try result.ensureCapacity(utf16le.len); - - const utf16le_as_bytes = @sliceToBytes(utf16le); - var i: usize = 0; - var out_index: usize = 0; - while (i < utf16le_as_bytes.len) : (i += 2) { - // decode - const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); - var codepoint: u32 = undefined; - if (c0 & ~u32(0x03ff) == 0xd800) { - // surrogate pair - i += 2; - if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf; - const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); - if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; - codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); - } else if (c0 & ~u32(0x03ff) == 0xdc00) { - return error.UnexpectedSecondSurrogateHalf; - } else { - codepoint = c0; - } - - // encode - const utf8_len = unicode.utf8CodepointSequenceLength(codepoint) catch unreachable; - try result.resize(result.len + utf8_len); - _ = unicode.utf8Encode(codepoint, result.items[out_index..]) catch unreachable; - out_index += utf8_len; - } - - return result.toOwnedSlice(); -} - -test "utf16leToUtf8" { - var utf16le: [2]u16 = undefined; - const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); - - { - mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "Aa")); - } - - { - mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); - } - - { - // the values just outside the surrogate half range - mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); - } - - { - // smallest surrogate pair - mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); - } - - { - // largest surrogate pair - mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); - } - - { - mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); - mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); - const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); - assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); - } -} - -fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { - var index: usize = 0; - while (ptr[index] != 0) : (index += 1) {} - return ptr[0..index]; + return os.getAppDataDir(allocator, "zig"); } diff --git a/std/event/loop.zig b/std/event/loop.zig index e2bc9e7eb9..485a5be19c 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -233,8 +233,6 @@ pub const Loop = struct { } }, builtin.Os.windows => { - self.os_data.extra_thread_count = extra_thread_count; - self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( windows.INVALID_HANDLE_VALUE, null, @@ -468,7 +466,7 @@ pub const Loop = struct { }, builtin.Os.windows => { var i: usize = 0; - while (i < self.os_data.extra_thread_count) : (i += 1) { + while (i < self.extra_threads.len + 1) : (i += 1) { while (true) { const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; diff --git a/std/os/get_app_data_dir.zig b/std/os/get_app_data_dir.zig new file mode 100644 index 0000000000..b5efdb826a --- /dev/null +++ b/std/os/get_app_data_dir.zig @@ -0,0 +1,60 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const unicode = std.unicode; +const mem = std.mem; +const os = std.os; + +pub const GetAppDataDirError = error{ + OutOfMemory, + AppDataDirUnavailable, +}; + +/// Caller owns returned memory. +pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { + switch (builtin.os) { + builtin.Os.windows => { + var dir_path_ptr: [*]u16 = undefined; + switch (os.windows.SHGetKnownFolderPath( + &os.windows.FOLDERID_LocalAppData, + os.windows.KF_FLAG_CREATE, + null, + &dir_path_ptr, + )) { + os.windows.S_OK => { + defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); + const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) { + error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, + error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, + error.DanglingSurrogateHalf => return error.AppDataDirUnavailable, + error.OutOfMemory => return error.OutOfMemory, + }; + defer allocator.free(global_dir); + return os.path.join(allocator, global_dir, appname); + }, + os.windows.E_OUTOFMEMORY => return error.OutOfMemory, + else => return error.AppDataDirUnavailable, + } + }, + // TODO for macos it should be "~/Library/Application Support/" + else => { + const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd + }; + defer allocator.free(home_dir); + return os.path.join(allocator, home_dir, ".local", "share", appname); + }, + } +} + +fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { + var index: usize = 0; + while (ptr[index] != 0) : (index += 1) {} + return ptr[0..index]; +} + +test "getAppDataDir" { + const result = try getAppDataDir(std.debug.global_allocator, "zig"); + std.debug.warn("{}...", result); +} + diff --git a/std/os/index.zig b/std/os/index.zig index 79b2d2ff53..cb4358af4d 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -18,6 +18,7 @@ test "std.os" { _ = @import("test.zig"); _ = @import("time.zig"); _ = @import("windows/index.zig"); + _ = @import("get_app_data_dir.zig"); } pub const windows = @import("windows/index.zig"); @@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError; pub const FileHandle = if (is_windows) windows.HANDLE else i32; +pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir; +pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError; + const debug = std.debug; const assert = debug.assert; diff --git a/std/unicode.zig b/std/unicode.zig index 9c329acc68..8a9d4a9214 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -1,5 +1,8 @@ const std = @import("./index.zig"); +const builtin = @import("builtin"); const debug = std.debug; +const assert = std.debug.assert; +const mem = std.mem; /// Returns how many bytes the UTF-8 representation would require /// for the given codepoint. @@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 { debug.assert(bytes.len == length); return utf8Decode(bytes); } + +// TODO: make this API on top of a non-allocating Utf16LeView +pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 { + var result = std.ArrayList(u8).init(allocator); + // optimistically guess that it will all be ascii. + try result.ensureCapacity(utf16le.len); + + const utf16le_as_bytes = @sliceToBytes(utf16le); + var i: usize = 0; + var out_index: usize = 0; + while (i < utf16le_as_bytes.len) : (i += 2) { + // decode + const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); + var codepoint: u32 = undefined; + if (c0 & ~u32(0x03ff) == 0xd800) { + // surrogate pair + i += 2; + if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf; + const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); + if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; + codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); + } else if (c0 & ~u32(0x03ff) == 0xdc00) { + return error.UnexpectedSecondSurrogateHalf; + } else { + codepoint = c0; + } + + // encode + const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable; + try result.resize(result.len + utf8_len); + _ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable; + out_index += utf8_len; + } + + return result.toOwnedSlice(); +} + +test "utf16leToUtf8" { + var utf16le: [2]u16 = undefined; + const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); + + { + mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "Aa")); + } + + { + mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); + } + + { + // the values just outside the surrogate half range + mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); + } + + { + // smallest surrogate pair + mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); + } + + { + // largest surrogate pair + mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); + } + + { + mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); + mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); + const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); + assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); + } +}