From 822f41242438faeaaf9846e3ebed454b59525ba7 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 21 Nov 2025 22:26:58 -0800 Subject: [PATCH] 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. --- lib/std/fs/path.zig | 12 ++++-------- lib/std/os/windows.zig | 27 ++++++++++++++------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 6270c47ab5..62324fe5b5 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -57,11 +57,12 @@ pub const PathType = enum { posix, /// Returns true if `c` is a valid path separator for the `path_type`. + /// If `T` is `u16`, `c` is assumed to be little-endian. pub inline fn isSep(comptime path_type: PathType, comptime T: type, c: T) bool { return switch (path_type) { - .windows => c == '/' or c == '\\', - .posix => c == '/', - .uefi => c == '\\', + .windows => c == mem.nativeToLittle(T, '/') or c == mem.nativeToLittle(T, '\\'), + .posix => c == mem.nativeToLittle(T, '/'), + .uefi => c == mem.nativeToLittle(T, '\\'), }; } }; @@ -2439,11 +2440,6 @@ test "ComponentIterator windows" { } test "ComponentIterator windows WTF-16" { - // TODO: Fix on big endian architectures - if (builtin.cpu.arch.endian() != .little) { - return error.SkipZigTest; - } - const WindowsComponentIterator = ComponentIterator(.windows, u16); const L = std.unicode.utf8ToUtf16LeStringLiteral; diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index e7642ad9aa..f9181be89f 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -2491,15 +2491,15 @@ pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType { if (path.len < 1) return .relative; const windows_path = std.fs.path.PathType.windows; - if (windows_path.isSep(T, mem.littleToNative(T, path[0]))) { + if (windows_path.isSep(T, path[0])) { // \x - if (path.len < 2 or !windows_path.isSep(T, mem.littleToNative(T, path[1]))) return .rooted; + if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted; // \\. or \\? - if (path.len > 2 and (mem.littleToNative(T, path[2]) == '.' or mem.littleToNative(T, path[2]) == '?')) { + if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) { // exactly \\. or \\? with nothing trailing if (path.len == 3) return .root_local_device; // \\.\x or \\?\x - if (windows_path.isSep(T, mem.littleToNative(T, path[3]))) return .local_device; + if (windows_path.isSep(T, path[3])) return .local_device; } // \\x return .unc_absolute; @@ -2538,9 +2538,9 @@ pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType { else => @compileError("unsupported type: " ++ @typeName(T)), }; // x - if (path.len < colon_i + 1 or mem.littleToNative(T, path[colon_i]) != ':') return .relative; + if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative; // x:\ - if (path.len > colon_i + 1 and windows_path.isSep(T, mem.littleToNative(T, path[colon_i + 1]))) return .drive_absolute; + if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute; // x: return .drive_relative; } @@ -2632,12 +2632,13 @@ fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType std.debug.assert(getWin32PathType(T, path) == .local_device); } - const all_backslash = mem.littleToNative(T, path[0]) == '\\' and - mem.littleToNative(T, path[1]) == '\\' and - mem.littleToNative(T, path[3]) == '\\'; - return switch (mem.littleToNative(T, path[2])) { - '?' => if (all_backslash) .verbatim else .fake_verbatim, - '.' => .local_device, + const backslash = mem.nativeToLittle(T, '\\'); + const all_backslash = path[0] == backslash and + path[1] == backslash and + path[3] == backslash; + return switch (path[2]) { + mem.nativeToLittle(T, '?') => if (all_backslash) .verbatim else .fake_verbatim, + mem.nativeToLittle(T, '.') => .local_device, else => unreachable, }; } @@ -2664,7 +2665,7 @@ pub fn ntToWin32Namespace(path: []const u16, out: []u16) error{ NameTooLong, Not // `\??\UNC\` should be replaced by `\\` (two backslashes) const is_unc = after_prefix.len >= 4 and eqlIgnoreCaseWtf16(after_prefix[0..3], std.unicode.utf8ToUtf16LeStringLiteral("UNC")) and - std.fs.path.PathType.windows.isSep(u16, std.mem.littleToNative(u16, after_prefix[3])); + std.fs.path.PathType.windows.isSep(u16, after_prefix[3]); const win32_len = path.len - @as(usize, if (is_unc) 6 else 4); if (out.len < win32_len) return error.NameTooLong; if (is_unc) {