authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-07-10 11:17:07-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-07-10 11:17:07-07:00
logcc56ab8c685a82e69e8dc8771398af023c7c171d
tree7a5b178d61a052669adaf0a0beaada4c8e39f215
parentd7c6cfa7fdf51f0b52aa3666aaa3022bd8e4876f
parent2eae013378882910257be38917cd0f9e70b80c31
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16100 from squeek502/windows-path-compare

fs.path: Fix Windows path component comparison being ASCII-only

3 files changed, 257 insertions(+), 8 deletions(-)

lib/std/fs/path.zig+8-7
...@@ -423,8 +423,7 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool {...@@ -423,8 +423,7 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool {
423 var it1 = mem.tokenizeScalar(u8, ns1, sep1);423 var it1 = mem.tokenizeScalar(u8, ns1, sep1);
424 var it2 = mem.tokenizeScalar(u8, ns2, sep2);424 var it2 = mem.tokenizeScalar(u8, ns2, sep2);
425425
426 // TODO ASCII is wrong, we actually need full unicode support to compare paths.426 return windows.eqlIgnoreCaseUtf8(it1.next().?, it2.next().?);
427 return ascii.eqlIgnoreCase(it1.next().?, it2.next().?);
428}427}
429428
430fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool {429fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool {
...@@ -444,8 +443,7 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8...@@ -444,8 +443,7 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8
444 var it1 = mem.tokenizeScalar(u8, p1, sep1);443 var it1 = mem.tokenizeScalar(u8, p1, sep1);
445 var it2 = mem.tokenizeScalar(u8, p2, sep2);444 var it2 = mem.tokenizeScalar(u8, p2, sep2);
446445
447 // TODO ASCII is wrong, we actually need full unicode support to compare paths.446 return windows.eqlIgnoreCaseUtf8(it1.next().?, it2.next().?) and windows.eqlIgnoreCaseUtf8(it1.next().?, it2.next().?);
448 return ascii.eqlIgnoreCase(it1.next().?, it2.next().?) and ascii.eqlIgnoreCase(it1.next().?, it2.next().?);
449 },447 },
450 }448 }
451}449}
...@@ -1084,8 +1082,7 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !...@@ -1084,8 +1082,7 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !
1084 const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest());1082 const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest());
1085 const to_rest = to_it.rest();1083 const to_rest = to_it.rest();
1086 if (to_it.next()) |to_component| {1084 if (to_it.next()) |to_component| {
1087 // TODO ASCII is wrong, we actually need full unicode support to compare paths.1085 if (windows.eqlIgnoreCaseUtf8(from_component, to_component))
1088 if (ascii.eqlIgnoreCase(from_component, to_component))
1089 continue;1086 continue;
1090 }1087 }
1091 var up_index_end = "..".len;1088 var up_index_end = "..".len;
...@@ -1162,7 +1159,7 @@ test "relative" {...@@ -1162,7 +1159,7 @@ test "relative" {
1162 try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");1159 try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
1163 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");1160 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
1164 try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");1161 try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");
1165 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/bbbb", "");1162 try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", "");
1166 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc");1163 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc");
1167 try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc");1164 try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc");
1168 try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb");1165 try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb");
...@@ -1188,6 +1185,10 @@ test "relative" {...@@ -1188,6 +1185,10 @@ test "relative" {
1188 try testRelativeWindows("a/b/c", "a", "..\\..");1185 try testRelativeWindows("a/b/c", "a", "..\\..");
1189 try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d");1186 try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d");
11901187
1188 try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", "");
1189 // Unicode-aware case-insensitive path comparison
1190 try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", "");
1191
1191 try testRelativePosix("/var/lib", "/var", "..");1192 try testRelativePosix("/var/lib", "/var", "..");
1192 try testRelativePosix("/var/lib", "/bin", "../../bin");1193 try testRelativePosix("/var/lib", "/bin", "../../bin");
1193 try testRelativePosix("/var/lib", "/var/lib", "");1194 try testRelativePosix("/var/lib", "/var/lib", "");
lib/std/os/windows.zig+83-1
...@@ -29,6 +29,7 @@ pub const ws2_32 = @import("windows/ws2_32.zig");...@@ -29,6 +29,7 @@ pub const ws2_32 = @import("windows/ws2_32.zig");
29pub const gdi32 = @import("windows/gdi32.zig");29pub const gdi32 = @import("windows/gdi32.zig");
30pub const winmm = @import("windows/winmm.zig");30pub const winmm = @import("windows/winmm.zig");
31pub const crypt32 = @import("windows/crypt32.zig");31pub const crypt32 = @import("windows/crypt32.zig");
32pub const nls = @import("windows/nls.zig");
3233
33pub const self_process_handle = @as(HANDLE, @ptrFromInt(maxInt(usize)));34pub const self_process_handle = @as(HANDLE, @ptrFromInt(maxInt(usize)));
3435
...@@ -1911,8 +1912,31 @@ pub fn nanoSecondsToFileTime(ns: i128) FILETIME {...@@ -1911,8 +1912,31 @@ pub fn nanoSecondsToFileTime(ns: i128) FILETIME {
1911 };1912 };
1912}1913}
19131914
1914/// Compares two WTF16 strings using RtlEqualUnicodeString1915/// Compares two WTF16 strings using the equivalent functionality of
1916/// `RtlEqualUnicodeString` (with case insensitive comparison enabled).
1917/// This function can be called on any target.
1915pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {1918pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
1919 if (@inComptime() or builtin.os.tag != .windows) {
1920 // This function compares the strings code unit by code unit (aka u16-to-u16),
1921 // so any length difference implies inequality. In other words, there's no possible
1922 // conversion that changes the number of UTF-16 code units needed for the uppercase/lowercase
1923 // version in the conversion table since only codepoints <= max(u16) are eligible
1924 // for conversion at all.
1925 if (a.len != b.len) return false;
1926
1927 for (a, b) |a_c, b_c| {
1928 // The slices are always UTF-16 LE, so need to convert the elements to native
1929 // endianness for the uppercasing
1930 const a_c_native = std.mem.littleToNative(u16, a_c);
1931 const b_c_native = std.mem.littleToNative(u16, b_c);
1932 if (a_c != b_c and nls.upcaseW(a_c_native) != nls.upcaseW(b_c_native)) {
1933 return false;
1934 }
1935 }
1936 return true;
1937 }
1938 // Use RtlEqualUnicodeString on Windows when not in comptime to avoid including a
1939 // redundant copy of the uppercase data.
1916 const a_bytes = @as(u16, @intCast(a.len * 2));1940 const a_bytes = @as(u16, @intCast(a.len * 2));
1917 const a_string = UNICODE_STRING{1941 const a_string = UNICODE_STRING{
1918 .Length = a_bytes,1942 .Length = a_bytes,
...@@ -1928,6 +1952,64 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {...@@ -1928,6 +1952,64 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
1928 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;1952 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
1929}1953}
19301954
1955/// Compares two UTF-8 strings using the equivalent functionality of
1956/// `RtlEqualUnicodeString` (with case insensitive comparison enabled).
1957/// This function can be called on any target.
1958/// Assumes `a` and `b` are valid UTF-8.
1959pub fn eqlIgnoreCaseUtf8(a: []const u8, b: []const u8) bool {
1960 // A length equality check is not possible here because there are
1961 // some codepoints that have a different length uppercase UTF-8 representations
1962 // than their lowercase counterparts, e.g. U+0250 (2 bytes) <-> U+2C6F (3 bytes).
1963 // There are 7 such codepoints in the uppercase data used by Windows.
1964
1965 var a_utf8_it = std.unicode.Utf8View.initUnchecked(a).iterator();
1966 var b_utf8_it = std.unicode.Utf8View.initUnchecked(b).iterator();
1967
1968 // Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a
1969 // redundant copy of the uppercase data.
1970 const upcaseImpl = switch (builtin.os.tag) {
1971 .windows => if (@inComptime()) nls.upcaseW else ntdll.RtlUpcaseUnicodeChar,
1972 else => nls.upcaseW,
1973 };
1974
1975 while (true) {
1976 var a_cp = a_utf8_it.nextCodepoint() orelse break;
1977 var b_cp = b_utf8_it.nextCodepoint() orelse return false;
1978
1979 if (a_cp <= std.math.maxInt(u16) and b_cp <= std.math.maxInt(u16)) {
1980 if (a_cp != b_cp and upcaseImpl(@intCast(a_cp)) != upcaseImpl(@intCast(b_cp))) {
1981 return false;
1982 }
1983 } else if (a_cp != b_cp) {
1984 return false;
1985 }
1986 }
1987 // Make sure there are no leftover codepoints in b
1988 if (b_utf8_it.nextCodepoint() != null) return false;
1989
1990 return true;
1991}
1992
1993fn testEqlIgnoreCase(comptime expect_eql: bool, comptime a: []const u8, comptime b: []const u8) !void {
1994 try std.testing.expectEqual(expect_eql, eqlIgnoreCaseUtf8(a, b));
1995 try std.testing.expectEqual(expect_eql, eqlIgnoreCaseWTF16(
1996 std.unicode.utf8ToUtf16LeStringLiteral(a),
1997 std.unicode.utf8ToUtf16LeStringLiteral(b),
1998 ));
1999
2000 try comptime std.testing.expect(expect_eql == eqlIgnoreCaseUtf8(a, b));
2001 try comptime std.testing.expect(expect_eql == eqlIgnoreCaseWTF16(
2002 std.unicode.utf8ToUtf16LeStringLiteral(a),
2003 std.unicode.utf8ToUtf16LeStringLiteral(b),
2004 ));
2005}
2006
2007test "eqlIgnoreCaseWTF16/Utf8" {
2008 try testEqlIgnoreCase(true, "\x01 a B Λ ɐ", "\x01 A b λ Ɐ");
2009 // does not do case-insensitive comparison for codepoints >= U+10000
2010 try testEqlIgnoreCase(false, "𐓏", "𐓷");
2011}
2012
1931pub const PathSpace = struct {2013pub const PathSpace = struct {
1932 data: [PATH_MAX_WIDE:0]u16,2014 data: [PATH_MAX_WIDE:0]u16,
1933 len: usize,2015 len: usize,
lib/std/os/windows/nls.zig created+166
...@@ -0,0 +1,166 @@
1//! Implementations of functionality related to National Language Support
2//! on Windows.
3
4const builtin = @import("builtin");
5const std = @import("../../std.zig");
6
7/// This corresponds to the uppercase table within the locale-independent
8/// l_intl.nls data (found at system32\l_intl.nls).
9/// - In l_intl.nls, this data starts at offset 0x04.
10/// - In the PEB, this data starts at index [2] of peb.UnicodeCaseTableData when
11/// it is casted to `[*]u16`.
12///
13/// Note: This data has not changed since Windows 8.1, and has become out-of-sync with
14/// the Unicode standard.
15const uppercase_table = [2544]u16{
16 272, 288, 304, 320, 336, 352, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
17 256, 256, 256, 256, 256, 368, 384, 400, 256, 416, 256, 256, 432, 256, 256, 256, 256, 256, 256, 256, 448, 464, 256, 256,
18 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
19 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
20 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
21 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
22 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 480, 496,
23 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
24 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
25 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
26 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 512, 528, 528, 528, 528, 528, 528, 528, 528,
27 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 544, 560, 528, 528, 528, 576, 528, 528, 592, 608,
28 624, 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816, 832, 848, 864, 880, 896, 912, 928, 944, 960, 976, 992,
29 1008, 1024, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 1040, 528, 528, 1056, 528, 528, 1072, 1088, 1104, 1120, 1136, 1152,
30 528, 528, 528, 1168, 1184, 1200, 1216, 1232, 1248, 1264, 1280, 1296, 1312, 1328, 1344, 1360, 1376, 1392, 1408, 528, 528, 528, 1424, 1440,
31 1456, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 1472, 528, 528, 528, 528, 528, 528, 528, 528,
32 1488, 1504, 1520, 1536, 1552, 1568, 1584, 1600, 1616, 1632, 1648, 1664, 1680, 1696, 1712, 1728, 1744, 1760, 1776, 1792, 1808, 1824, 1840, 1856,
33 1872, 1888, 1904, 1920, 1936, 1952, 1968, 1984, 528, 528, 528, 528, 2000, 528, 528, 2016, 2032, 528, 528, 528, 528, 528, 528, 528,
34 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 2048, 2064, 528, 528, 528, 528, 2080, 2096, 2112, 2128, 2144,
35 2160, 2176, 2192, 2208, 2224, 2240, 2256, 528, 2272, 2288, 2304, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528,
36 528, 528, 528, 528, 2320, 2336, 2352, 528, 2368, 2384, 528, 528, 528, 528, 528, 528, 528, 528, 2400, 2416, 2432, 2448, 2464, 2480,
37 2496, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 2512, 2528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528,
38 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65504, 65504, 65504, 65504, 65504, 65504, 65504,
39 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 0, 0, 0, 0, 0,
40 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504,
41 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 0, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 121,
42 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
43 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
44 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0,
45 65535, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
46 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
47 0, 0, 65535, 0, 65535, 0, 65535, 0, 195, 0, 0, 65535, 0, 65535, 0, 0, 65535, 0, 0, 0, 65535, 0, 0, 0,
48 0, 0, 65535, 0, 0, 97, 0, 0, 0, 65535, 163, 0, 0, 0, 130, 0, 0, 65535, 0, 65535, 0, 65535, 0, 0,
49 65535, 0, 0, 0, 0, 65535, 0, 0, 65535, 0, 0, 0, 65535, 0, 65535, 0, 0, 65535, 0, 0, 0, 65535, 0, 56,
50 0, 0, 0, 0, 0, 0, 65534, 0, 0, 65534, 0, 0, 65534, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0,
51 65535, 0, 65535, 0, 65535, 65457, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
52 0, 0, 0, 65534, 0, 65535, 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
53 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
54 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 0, 0, 0,
55 0, 0, 0, 0, 65535, 0, 0, 0, 0, 0, 65535, 0, 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
56 10783, 10780, 0, 65326, 65330, 0, 65331, 65331, 0, 65334, 0, 65333, 0, 0, 0, 0, 65331, 0, 0, 65329, 0, 0, 0, 0,
57 65327, 65325, 0, 10743, 0, 0, 0, 65325, 0, 10749, 65323, 0, 0, 65322, 0, 0, 0, 0, 0, 0, 0, 10727, 0, 0,
58 65318, 0, 0, 65318, 0, 0, 0, 0, 65318, 65467, 65319, 65319, 65465, 0, 0, 0, 0, 0, 65317, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 65535, 0, 65535, 0, 0, 0, 65535, 0, 0, 0, 130, 130, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 65498, 65499, 65499, 65499, 0, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504,
62 65504, 65504, 0, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65472, 65473, 65473, 0, 0, 0, 0, 0, 0, 0, 0, 65528,
63 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
64 0, 0, 7, 0, 0, 0, 0, 0, 65535, 0, 0, 65535, 0, 0, 0, 0, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504,
65 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504,
66 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 65456, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
67 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
68 0, 65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
69 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
70 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 0, 65535, 0, 65535, 0, 65535, 0,
71 65535, 0, 65535, 0, 65535, 0, 65535, 65521, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
72 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
73 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
74 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488,
76 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 0,
77 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35332, 0, 0, 0, 3814, 0, 0,
78 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
79 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
80 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
81 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
82 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
83 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
84 0, 65535, 0, 65535, 0, 65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
85 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
86 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
87 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
88 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 8, 8, 8, 8, 8, 8, 8, 8,
89 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
90 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8,
91 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
92 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8,
93 0, 0, 0, 0, 0, 0, 0, 0, 74, 74, 86, 86, 86, 86, 100, 100, 128, 128, 112, 112, 126, 126, 0, 0,
94 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8,
95 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0,
96 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0,
97 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
98 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0,
99 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65508, 0,
100 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 65520, 0, 0, 0, 0, 65535, 0, 0, 0,
101 0, 0, 0, 0, 0, 0, 0, 0, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510,
102 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 65510, 0, 0, 0, 0, 0, 0, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488,
103 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488,
104 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 65488, 0, 0, 65535, 0, 0, 0, 54741, 54744, 0,
105 65535, 0, 65535, 0, 65535, 0, 0, 0, 0, 0, 0, 65535, 0, 0, 65535, 0, 0, 0, 0, 0, 0, 0, 0, 0,
106 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
107 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
108 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
109 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
110 0, 65535, 0, 65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272,
111 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272, 58272,
112 58272, 58272, 58272, 58272, 58272, 58272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
113 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
114 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
115 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 0, 0, 0, 0, 0, 0, 0,
116 0, 0, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 0, 0, 65535, 0, 65535, 0, 65535,
117 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
118 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535,
119 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65535, 0, 65535, 0, 0, 65535,
120 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 0, 0, 0, 65535, 0, 0, 0, 0, 65504, 65504, 65504, 65504, 65504, 65504, 65504,
121 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 65504, 0, 0, 0, 0, 0,
122};
123
124/// Cross-platform implementation of `ntdll.RtlUpcaseUnicodeChar`.
125/// Transforms the UTF-16 code unit in `c` to its uppercased version
126/// if there is one. Otherwise, returns `c` unmodified.
127///
128/// Note: When this function is referenced, it will need to include
129/// `uppercase_table.len * 2` bytes of data in the resulting binary
130/// since it depends on the `uppercase_table` data. When
131/// targeting Windows, `ntdll.RtlUpcaseUnicodeChar` can be
132/// used instead to avoid having to include a copy of this data.
133pub fn upcaseW(c: u16) u16 {
134 if (c < 'a') {
135 return c;
136 }
137 if (c <= 'z') {
138 return c - ('a' - 'A');
139 }
140 if (c >= 0xC0) {
141 var offset: u16 = 0;
142
143 offset += @as(u8, @truncate(c >> 8));
144 offset = uppercase_table[offset];
145 offset += @as(u4, @truncate(c >> 4));
146 offset = uppercase_table[offset];
147 offset += @as(u4, @truncate(c));
148 offset = uppercase_table[offset];
149
150 return c +% offset;
151 }
152 return c;
153}
154
155test "upcaseW matches RtlUpcaseUnicodeChar" {
156 if (builtin.os.tag != .windows) return error.SkipZigTest;
157
158 var c: u16 = 0;
159 while (true) : (c += 1) {
160 std.testing.expectEqual(std.os.windows.ntdll.RtlUpcaseUnicodeChar(c), upcaseW(c)) catch |err| {
161 std.debug.print("mismatch for codepoint U+{X}\n", .{c});
162 return err;
163 };
164 if (c == 0xFFFF) break;
165 }
166}