| author | |
| committer | |
| log | a8a1b5af07519b49b5ba31ad6c802a7ccc8a5e23 |
| tree | 4da94fe3e2b0fc4692eb368244e90696f2cbcec1 |
| parent | b7be082bd9aaba4cbf7c7c7449e481f9caa1dc24 |
* move getAppDataDir and utf16leToUtf8 from self-hosted to std lib
* fix std.event.Loop on windows6 files changed, 156 insertions(+), 141 deletions(-)
CMakeLists.txt+1| ... | @@ -557,6 +557,7 @@ set(ZIG_STD_FILES | ... | @@ -557,6 +557,7 @@ set(ZIG_STD_FILES |
| 557 | "os/darwin_errno.zig" | 557 | "os/darwin_errno.zig" |
| 558 | "os/epoch.zig" | 558 | "os/epoch.zig" |
| 559 | "os/file.zig" | 559 | "os/file.zig" |
| 560 | "os/get_app_data_dir.zig" | ||
| 560 | "os/get_user_id.zig" | 561 | "os/get_user_id.zig" |
| 561 | "os/index.zig" | 562 | "os/index.zig" |
| 562 | "os/linux/errno.zig" | 563 | "os/linux/errno.zig" |
src-self-hosted/compilation.zig+1-138| ... | @@ -4,7 +4,6 @@ const io = std.io; | ... | @@ -4,7 +4,6 @@ const io = std.io; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const Allocator = mem.Allocator; | 5 | const Allocator = mem.Allocator; |
| 6 | const Buffer = std.Buffer; | 6 | const Buffer = std.Buffer; |
| 7 | const unicode = std.unicode; | ||
| 8 | const llvm = @import("llvm.zig"); | 7 | const llvm = @import("llvm.zig"); |
| 9 | const c = @import("c.zig"); | 8 | const c = @import("c.zig"); |
| 10 | const builtin = @import("builtin"); | 9 | const builtin = @import("builtin"); |
| ... | @@ -952,141 +951,5 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { | ... | @@ -952,141 +951,5 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { |
| 952 | } | 951 | } |
| 953 | 952 | ||
| 954 | fn getZigDir(allocator: *mem.Allocator) ![]u8 { | 953 | fn getZigDir(allocator: *mem.Allocator) ![]u8 { |
| 955 | return getAppDataDir(allocator, "zig"); | 954 | return os.getAppDataDir(allocator, "zig"); |
| 956 | } | ||
| 957 | |||
| 958 | const GetAppDataDirError = error{ | ||
| 959 | OutOfMemory, | ||
| 960 | AppDataDirUnavailable, | ||
| 961 | }; | ||
| 962 | |||
| 963 | /// Caller owns returned memory. | ||
| 964 | /// TODO move to zig std lib | ||
| 965 | fn 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 | |||
| 997 | test "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 | ||
| 1003 | fn 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 | |||
| 1038 | test "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 | |||
| 1088 | fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { | ||
| 1089 | var index: usize = 0; | ||
| 1090 | while (ptr[index] != 0) : (index += 1) {} | ||
| 1091 | return ptr[0..index]; | ||
| 1092 | } | 955 | } |
std/event/loop.zig+1-3| ... | @@ -233,8 +233,6 @@ pub const Loop = struct { | ... | @@ -233,8 +233,6 @@ pub const Loop = struct { |
| 233 | } | 233 | } |
| 234 | }, | 234 | }, |
| 235 | builtin.Os.windows => { | 235 | builtin.Os.windows => { |
| 236 | self.os_data.extra_thread_count = extra_thread_count; | ||
| 237 | |||
| 238 | self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( | 236 | self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( |
| 239 | windows.INVALID_HANDLE_VALUE, | 237 | windows.INVALID_HANDLE_VALUE, |
| 240 | null, | 238 | null, |
| ... | @@ -468,7 +466,7 @@ pub const Loop = struct { | ... | @@ -468,7 +466,7 @@ pub const Loop = struct { |
| 468 | }, | 466 | }, |
| 469 | builtin.Os.windows => { | 467 | builtin.Os.windows => { |
| 470 | var i: usize = 0; | 468 | 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) { |
| 472 | while (true) { | 470 | while (true) { |
| 473 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | 471 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| 474 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; | 472 | 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 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const unicode = std.unicode; | ||
| 4 | const mem = std.mem; | ||
| 5 | const os = std.os; | ||
| 6 | |||
| 7 | pub const GetAppDataDirError = error{ | ||
| 8 | OutOfMemory, | ||
| 9 | AppDataDirUnavailable, | ||
| 10 | }; | ||
| 11 | |||
| 12 | /// Caller owns returned memory. | ||
| 13 | pub 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 | |||
| 50 | fn 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 | |||
| 56 | test "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" { | ... | @@ -18,6 +18,7 @@ test "std.os" { |
| 18 | _ = @import("test.zig"); | 18 | _ = @import("test.zig"); |
| 19 | _ = @import("time.zig"); | 19 | _ = @import("time.zig"); |
| 20 | _ = @import("windows/index.zig"); | 20 | _ = @import("windows/index.zig"); |
| 21 | _ = @import("get_app_data_dir.zig"); | ||
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | pub const windows = @import("windows/index.zig"); | 24 | pub const windows = @import("windows/index.zig"); |
| ... | @@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError; | ... | @@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError; |
| 76 | 77 | ||
| 77 | pub const FileHandle = if (is_windows) windows.HANDLE else i32; | 78 | pub const FileHandle = if (is_windows) windows.HANDLE else i32; |
| 78 | 79 | ||
| 80 | pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir; | ||
| 81 | pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError; | ||
| 82 | |||
| 79 | const debug = std.debug; | 83 | const debug = std.debug; |
| 80 | const assert = debug.assert; | 84 | const assert = debug.assert; |
| 81 | 85 |
std/unicode.zig+89| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | const std = @import("./index.zig"); | 1 | const std = @import("./index.zig"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const debug = std.debug; | 3 | const debug = std.debug; |
| 4 | const assert = std.debug.assert; | ||
| 5 | const mem = std.mem; | ||
| 3 | 6 | ||
| 4 | /// Returns how many bytes the UTF-8 representation would require | 7 | /// Returns how many bytes the UTF-8 representation would require |
| 5 | /// for the given codepoint. | 8 | /// for the given codepoint. |
| ... | @@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 { | ... | @@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 { |
| 441 | debug.assert(bytes.len == length); | 444 | debug.assert(bytes.len == length); |
| 442 | return utf8Decode(bytes); | 445 | return utf8Decode(bytes); |
| 443 | } | 446 | } |
| 447 | |||
| 448 | // TODO: make this API on top of a non-allocating Utf16LeView | ||
| 449 | pub 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 | |||
| 484 | test "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 | } |