authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-11-21 22:26:58-08:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-11-21 22:26:58-08:00
log822f41242438faeaaf9846e3ebed454b59525ba7
treec9362b426eddea0b650c0c649be3fc2524f41890
parent59b8bed222137061d74002a40544b5ea30eab666

fs.path: Fix on big-endian architectures, make PathType.isSep assume WTF-16 is LE

This commit flips usage of PathType.isSep from requiring the caller to convert to native to assuming the input is LE encoded, which is a breaking change. This makes usage a bit nicer, though, and moves the endian conversion work from runtime to comptime.

2 files changed, 18 insertions(+), 21 deletions(-)

lib/std/fs/path.zig+4-8
...@@ -57,11 +57,12 @@ pub const PathType = enum {...@@ -57,11 +57,12 @@ pub const PathType = enum {
57 posix,57 posix,
5858
59 /// Returns true if `c` is a valid path separator for the `path_type`.59 /// Returns true if `c` is a valid path separator for the `path_type`.
60 /// If `T` is `u16`, `c` is assumed to be little-endian.
60 pub inline fn isSep(comptime path_type: PathType, comptime T: type, c: T) bool {61 pub inline fn isSep(comptime path_type: PathType, comptime T: type, c: T) bool {
61 return switch (path_type) {62 return switch (path_type) {
62 .windows => c == '/' or c == '\\',63 .windows => c == mem.nativeToLittle(T, '/') or c == mem.nativeToLittle(T, '\\'),
63 .posix => c == '/',64 .posix => c == mem.nativeToLittle(T, '/'),
64 .uefi => c == '\\',65 .uefi => c == mem.nativeToLittle(T, '\\'),
65 };66 };
66 }67 }
67};68};
...@@ -2439,11 +2440,6 @@ test "ComponentIterator windows" {...@@ -2439,11 +2440,6 @@ test "ComponentIterator windows" {
2439}2440}
24402441
2441test "ComponentIterator windows WTF-16" {2442test "ComponentIterator windows WTF-16" {
2442 // TODO: Fix on big endian architectures
2443 if (builtin.cpu.arch.endian() != .little) {
2444 return error.SkipZigTest;
2445 }
2446
2447 const WindowsComponentIterator = ComponentIterator(.windows, u16);2443 const WindowsComponentIterator = ComponentIterator(.windows, u16);
2448 const L = std.unicode.utf8ToUtf16LeStringLiteral;2444 const L = std.unicode.utf8ToUtf16LeStringLiteral;
24492445
lib/std/os/windows.zig+14-13
...@@ -2491,15 +2491,15 @@ pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {...@@ -2491,15 +2491,15 @@ pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {
2491 if (path.len < 1) return .relative;2491 if (path.len < 1) return .relative;
24922492
2493 const windows_path = std.fs.path.PathType.windows;2493 const windows_path = std.fs.path.PathType.windows;
2494 if (windows_path.isSep(T, mem.littleToNative(T, path[0]))) {2494 if (windows_path.isSep(T, path[0])) {
2495 // \x2495 // \x
2496 if (path.len < 2 or !windows_path.isSep(T, mem.littleToNative(T, path[1]))) return .rooted;2496 if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted;
2497 // \\. or \\?2497 // \\. or \\?
2498 if (path.len > 2 and (mem.littleToNative(T, path[2]) == '.' or mem.littleToNative(T, path[2]) == '?')) {2498 if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) {
2499 // exactly \\. or \\? with nothing trailing2499 // exactly \\. or \\? with nothing trailing
2500 if (path.len == 3) return .root_local_device;2500 if (path.len == 3) return .root_local_device;
2501 // \\.\x or \\?\x2501 // \\.\x or \\?\x
2502 if (windows_path.isSep(T, mem.littleToNative(T, path[3]))) return .local_device;2502 if (windows_path.isSep(T, path[3])) return .local_device;
2503 }2503 }
2504 // \\x2504 // \\x
2505 return .unc_absolute;2505 return .unc_absolute;
...@@ -2538,9 +2538,9 @@ pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {...@@ -2538,9 +2538,9 @@ pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {
2538 else => @compileError("unsupported type: " ++ @typeName(T)),2538 else => @compileError("unsupported type: " ++ @typeName(T)),
2539 };2539 };
2540 // x2540 // x
2541 if (path.len < colon_i + 1 or mem.littleToNative(T, path[colon_i]) != ':') return .relative;2541 if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative;
2542 // x:\2542 // x:\
2543 if (path.len > colon_i + 1 and windows_path.isSep(T, mem.littleToNative(T, path[colon_i + 1]))) return .drive_absolute;2543 if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute;
2544 // x:2544 // x:
2545 return .drive_relative;2545 return .drive_relative;
2546 }2546 }
...@@ -2632,12 +2632,13 @@ fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType...@@ -2632,12 +2632,13 @@ fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType
2632 std.debug.assert(getWin32PathType(T, path) == .local_device);2632 std.debug.assert(getWin32PathType(T, path) == .local_device);
2633 }2633 }
26342634
2635 const all_backslash = mem.littleToNative(T, path[0]) == '\\' and2635 const backslash = mem.nativeToLittle(T, '\\');
2636 mem.littleToNative(T, path[1]) == '\\' and2636 const all_backslash = path[0] == backslash and
2637 mem.littleToNative(T, path[3]) == '\\';2637 path[1] == backslash and
2638 return switch (mem.littleToNative(T, path[2])) {2638 path[3] == backslash;
2639 '?' => if (all_backslash) .verbatim else .fake_verbatim,2639 return switch (path[2]) {
2640 '.' => .local_device,2640 mem.nativeToLittle(T, '?') => if (all_backslash) .verbatim else .fake_verbatim,
2641 mem.nativeToLittle(T, '.') => .local_device,
2641 else => unreachable,2642 else => unreachable,
2642 };2643 };
2643}2644}
...@@ -2664,7 +2665,7 @@ pub fn ntToWin32Namespace(path: []const u16, out: []u16) error{ NameTooLong, Not...@@ -2664,7 +2665,7 @@ pub fn ntToWin32Namespace(path: []const u16, out: []u16) error{ NameTooLong, Not
2664 // `\??\UNC\` should be replaced by `\\` (two backslashes)2665 // `\??\UNC\` should be replaced by `\\` (two backslashes)
2665 const is_unc = after_prefix.len >= 4 and2666 const is_unc = after_prefix.len >= 4 and
2666 eqlIgnoreCaseWtf16(after_prefix[0..3], std.unicode.utf8ToUtf16LeStringLiteral("UNC")) and2667 eqlIgnoreCaseWtf16(after_prefix[0..3], std.unicode.utf8ToUtf16LeStringLiteral("UNC")) and
2667 std.fs.path.PathType.windows.isSep(u16, std.mem.littleToNative(u16, after_prefix[3]));2668 std.fs.path.PathType.windows.isSep(u16, after_prefix[3]);
2668 const win32_len = path.len - @as(usize, if (is_unc) 6 else 4);2669 const win32_len = path.len - @as(usize, if (is_unc) 6 else 4);
2669 if (out.len < win32_len) return error.NameTooLong;2670 if (out.len < win32_len) return error.NameTooLong;
2670 if (is_unc) {2671 if (is_unc) {