authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-18 10:07:22-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-18 10:07:22-04:00
loga8a1b5af07519b49b5ba31ad6c802a7ccc8a5e23
tree4da94fe3e2b0fc4692eb368244e90696f2cbcec1
parentb7be082bd9aaba4cbf7c7c7449e481f9caa1dc24

fix build on windows

* move getAppDataDir and utf16leToUtf8 from self-hosted to std lib * fix std.event.Loop on windows

6 files changed, 156 insertions(+), 141 deletions(-)

CMakeLists.txt+1
......@@ -557,6 +557,7 @@ set(ZIG_STD_FILES
557557 "os/darwin_errno.zig"
558558 "os/epoch.zig"
559559 "os/file.zig"
560 "os/get_app_data_dir.zig"
560561 "os/get_user_id.zig"
561562 "os/index.zig"
562563 "os/linux/errno.zig"
src-self-hosted/compilation.zig+1-138
......@@ -4,7 +4,6 @@ const io = std.io;
44const mem = std.mem;
55const Allocator = mem.Allocator;
66const Buffer = std.Buffer;
7const unicode = std.unicode;
87const llvm = @import("llvm.zig");
98const c = @import("c.zig");
109const builtin = @import("builtin");
......@@ -952,141 +951,5 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
952951}
953952
954953fn getZigDir(allocator: *mem.Allocator) ![]u8 {
955 return getAppDataDir(allocator, "zig");
956}
957
958const GetAppDataDirError = error{
959 OutOfMemory,
960 AppDataDirUnavailable,
961};
962
963/// Caller owns returned memory.
964/// TODO move to zig std lib
965fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
966 switch (builtin.os) {
967 builtin.Os.windows => {
968 var dir_path_ptr: [*]u16 = undefined;
969 switch (os.windows.SHGetKnownFolderPath(
970 &os.windows.FOLDERID_LocalAppData,
971 os.windows.KF_FLAG_CREATE,
972 null,
973 &dir_path_ptr,
974 )) {
975 os.windows.S_OK => {
976 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
977 const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr));
978 defer allocator.free(global_dir);
979 return os.path.join(allocator, global_dir, appname);
980 },
981 os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
982 else => return error.AppDataDirUnavailable,
983 }
984 },
985 // TODO for macos it should be "~/Library/Application Support/<APPNAME>"
986 else => {
987 const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) {
988 error.OutOfMemory => return error.OutOfMemory,
989 error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd
990 };
991 defer allocator.free(home_dir);
992 return os.path.join(allocator, home_dir, ".local", "share", appname);
993 },
994 }
995}
996
997test "getAppDataDir" {
998 const result = try getAppDataDir(std.debug.global_allocator, "zig");
999 std.debug.warn("{}...", result);
1000}
1001
1002// TODO: put general purpose stuff in std.unicode
1003fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
1004 var result = ArrayList(u8).init(allocator);
1005 // optimistically guess that it will all be ascii.
1006 try result.ensureCapacity(utf16le.len);
1007
1008 const utf16le_as_bytes = @sliceToBytes(utf16le);
1009 var i: usize = 0;
1010 var out_index: usize = 0;
1011 while (i < utf16le_as_bytes.len) : (i += 2) {
1012 // decode
1013 const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
1014 var codepoint: u32 = undefined;
1015 if (c0 & ~u32(0x03ff) == 0xd800) {
1016 // surrogate pair
1017 i += 2;
1018 if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf;
1019 const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
1020 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
1021 codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
1022 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
1023 return error.UnexpectedSecondSurrogateHalf;
1024 } else {
1025 codepoint = c0;
1026 }
1027
1028 // encode
1029 const utf8_len = unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
1030 try result.resize(result.len + utf8_len);
1031 _ = unicode.utf8Encode(codepoint, result.items[out_index..]) catch unreachable;
1032 out_index += utf8_len;
1033 }
1034
1035 return result.toOwnedSlice();
1036}
1037
1038test "utf16leToUtf8" {
1039 var utf16le: [2]u16 = undefined;
1040 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
1041
1042 {
1043 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);
1044 mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little);
1045 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
1046 assert(mem.eql(u8, utf8, "Aa"));
1047 }
1048
1049 {
1050 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);
1051 mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little);
1052 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
1053 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));
1054 }
1055
1056 {
1057 // the values just outside the surrogate half range
1058 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);
1059 mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little);
1060 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
1061 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));
1062 }
1063
1064 {
1065 // smallest surrogate pair
1066 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);
1067 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
1068 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
1069 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));
1070 }
1071
1072 {
1073 // largest surrogate pair
1074 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
1075 mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little);
1076 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
1077 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));
1078 }
1079
1080 {
1081 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
1082 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
1083 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
1084 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
1085 }
1086}
1087
1088fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
1089 var index: usize = 0;
1090 while (ptr[index] != 0) : (index += 1) {}
1091 return ptr[0..index];
954 return os.getAppDataDir(allocator, "zig");
1092955}
std/event/loop.zig+1-3
......@@ -233,8 +233,6 @@ pub const Loop = struct {
233233 }
234234 },
235235 builtin.Os.windows => {
236 self.os_data.extra_thread_count = extra_thread_count;
237
238236 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(
239237 windows.INVALID_HANDLE_VALUE,
240238 null,
......@@ -468,7 +466,7 @@ pub const Loop = struct {
468466 },
469467 builtin.Os.windows => {
470468 var i: usize = 0;
471 while (i < self.os_data.extra_thread_count) : (i += 1) {
469 while (i < self.extra_threads.len + 1) : (i += 1) {
472470 while (true) {
473471 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
474472 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
std/os/get_app_data_dir.zig created+60
......@@ -0,0 +1,60 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const unicode = std.unicode;
4const mem = std.mem;
5const os = std.os;
6
7pub const GetAppDataDirError = error{
8 OutOfMemory,
9 AppDataDirUnavailable,
10};
11
12/// Caller owns returned memory.
13pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
14 switch (builtin.os) {
15 builtin.Os.windows => {
16 var dir_path_ptr: [*]u16 = undefined;
17 switch (os.windows.SHGetKnownFolderPath(
18 &os.windows.FOLDERID_LocalAppData,
19 os.windows.KF_FLAG_CREATE,
20 null,
21 &dir_path_ptr,
22 )) {
23 os.windows.S_OK => {
24 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
25 const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) {
26 error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
27 error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
28 error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,
29 error.OutOfMemory => return error.OutOfMemory,
30 };
31 defer allocator.free(global_dir);
32 return os.path.join(allocator, global_dir, appname);
33 },
34 os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
35 else => return error.AppDataDirUnavailable,
36 }
37 },
38 // TODO for macos it should be "~/Library/Application Support/<APPNAME>"
39 else => {
40 const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) {
41 error.OutOfMemory => return error.OutOfMemory,
42 error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd
43 };
44 defer allocator.free(home_dir);
45 return os.path.join(allocator, home_dir, ".local", "share", appname);
46 },
47 }
48}
49
50fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
51 var index: usize = 0;
52 while (ptr[index] != 0) : (index += 1) {}
53 return ptr[0..index];
54}
55
56test "getAppDataDir" {
57 const result = try getAppDataDir(std.debug.global_allocator, "zig");
58 std.debug.warn("{}...", result);
59}
60
std/os/index.zig+4
......@@ -18,6 +18,7 @@ test "std.os" {
1818 _ = @import("test.zig");
1919 _ = @import("time.zig");
2020 _ = @import("windows/index.zig");
21 _ = @import("get_app_data_dir.zig");
2122}
2223
2324pub const windows = @import("windows/index.zig");
......@@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError;
7677
7778pub const FileHandle = if (is_windows) windows.HANDLE else i32;
7879
80pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir;
81pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError;
82
7983const debug = std.debug;
8084const assert = debug.assert;
8185
std/unicode.zig+89
......@@ -1,5 +1,8 @@
11const std = @import("./index.zig");
2const builtin = @import("builtin");
23const debug = std.debug;
4const assert = std.debug.assert;
5const mem = std.mem;
36
47/// Returns how many bytes the UTF-8 representation would require
58/// for the given codepoint.
......@@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 {
441444 debug.assert(bytes.len == length);
442445 return utf8Decode(bytes);
443446}
447
448// TODO: make this API on top of a non-allocating Utf16LeView
449pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
450 var result = std.ArrayList(u8).init(allocator);
451 // optimistically guess that it will all be ascii.
452 try result.ensureCapacity(utf16le.len);
453
454 const utf16le_as_bytes = @sliceToBytes(utf16le);
455 var i: usize = 0;
456 var out_index: usize = 0;
457 while (i < utf16le_as_bytes.len) : (i += 2) {
458 // decode
459 const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
460 var codepoint: u32 = undefined;
461 if (c0 & ~u32(0x03ff) == 0xd800) {
462 // surrogate pair
463 i += 2;
464 if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf;
465 const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
466 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
467 codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
468 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
469 return error.UnexpectedSecondSurrogateHalf;
470 } else {
471 codepoint = c0;
472 }
473
474 // encode
475 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
476 try result.resize(result.len + utf8_len);
477 _ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable;
478 out_index += utf8_len;
479 }
480
481 return result.toOwnedSlice();
482}
483
484test "utf16leToUtf8" {
485 var utf16le: [2]u16 = undefined;
486 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
487
488 {
489 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);
490 mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little);
491 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
492 assert(mem.eql(u8, utf8, "Aa"));
493 }
494
495 {
496 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);
497 mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little);
498 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
499 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));
500 }
501
502 {
503 // the values just outside the surrogate half range
504 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);
505 mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little);
506 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
507 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));
508 }
509
510 {
511 // smallest surrogate pair
512 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);
513 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
514 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
515 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));
516 }
517
518 {
519 // largest surrogate pair
520 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
521 mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little);
522 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
523 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));
524 }
525
526 {
527 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
528 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
529 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
530 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
531 }
532}