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 {
423423 var it1 = mem.tokenizeScalar(u8, ns1, sep1);
424424 var it2 = mem.tokenizeScalar(u8, ns2, sep2);
425425
426 // TODO ASCII is wrong, we actually need full unicode support to compare paths.
427 return ascii.eqlIgnoreCase(it1.next().?, it2.next().?);
426 return windows.eqlIgnoreCaseUtf8(it1.next().?, it2.next().?);
428427}
429428
430429fn 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
444443 var it1 = mem.tokenizeScalar(u8, p1, sep1);
445444 var it2 = mem.tokenizeScalar(u8, p2, sep2);
446445
447 // TODO ASCII is wrong, we actually need full unicode support to compare paths.
448 return ascii.eqlIgnoreCase(it1.next().?, it2.next().?) and ascii.eqlIgnoreCase(it1.next().?, it2.next().?);
446 return windows.eqlIgnoreCaseUtf8(it1.next().?, it2.next().?) and windows.eqlIgnoreCaseUtf8(it1.next().?, it2.next().?);
449447 },
450448 }
451449}
......@@ -1084,8 +1082,7 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !
10841082 const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest());
10851083 const to_rest = to_it.rest();
10861084 if (to_it.next()) |to_component| {
1087 // TODO ASCII is wrong, we actually need full unicode support to compare paths.
1088 if (ascii.eqlIgnoreCase(from_component, to_component))
1085 if (windows.eqlIgnoreCaseUtf8(from_component, to_component))
10891086 continue;
10901087 }
10911088 var up_index_end = "..".len;
......@@ -1162,7 +1159,7 @@ test "relative" {
11621159 try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
11631160 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
11641161 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", "");
11661163 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc");
11671164 try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc");
11681165 try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb");
......@@ -1188,6 +1185,10 @@ test "relative" {
11881185 try testRelativeWindows("a/b/c", "a", "..\\..");
11891186 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
11911192 try testRelativePosix("/var/lib", "/var", "..");
11921193 try testRelativePosix("/var/lib", "/bin", "../../bin");
11931194 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");
2929pub const gdi32 = @import("windows/gdi32.zig");
3030pub const winmm = @import("windows/winmm.zig");
3131pub const crypt32 = @import("windows/crypt32.zig");
32pub const nls = @import("windows/nls.zig");
3233
3334pub const self_process_handle = @as(HANDLE, @ptrFromInt(maxInt(usize)));
3435
......@@ -1911,8 +1912,31 @@ pub fn nanoSecondsToFileTime(ns: i128) FILETIME {
19111912 };
19121913}
19131914
1914/// Compares two WTF16 strings using RtlEqualUnicodeString
1915/// 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.
19151918pub 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.
19161940 const a_bytes = @as(u16, @intCast(a.len * 2));
19171941 const a_string = UNICODE_STRING{
19181942 .Length = a_bytes,
......@@ -1928,6 +1952,64 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
19281952 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
19291953}
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
19312013pub const PathSpace = struct {
19322014 data: [PATH_MAX_WIDE:0]u16,
19332015 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}