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 {
5757 posix,
5858
5959 /// 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.
6061 pub inline fn isSep(comptime path_type: PathType, comptime T: type, c: T) bool {
6162 return switch (path_type) {
62 .windows => c == '/' or c == '\\',
63 .posix => c == '/',
64 .uefi => c == '\\',
63 .windows => c == mem.nativeToLittle(T, '/') or c == mem.nativeToLittle(T, '\\'),
64 .posix => c == mem.nativeToLittle(T, '/'),
65 .uefi => c == mem.nativeToLittle(T, '\\'),
6566 };
6667 }
6768};
......@@ -2439,11 +2440,6 @@ test "ComponentIterator windows" {
24392440}
24402441
24412442test "ComponentIterator windows WTF-16" {
2442 // TODO: Fix on big endian architectures
2443 if (builtin.cpu.arch.endian() != .little) {
2444 return error.SkipZigTest;
2445 }
2446
24472443 const WindowsComponentIterator = ComponentIterator(.windows, u16);
24482444 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 {
24912491 if (path.len < 1) return .relative;
24922492
24932493 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])) {
24952495 // \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;
24972497 // \\. 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, '?'))) {
24992499 // exactly \\. or \\? with nothing trailing
25002500 if (path.len == 3) return .root_local_device;
25012501 // \\.\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;
25032503 }
25042504 // \\x
25052505 return .unc_absolute;
......@@ -2538,9 +2538,9 @@ pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {
25382538 else => @compileError("unsupported type: " ++ @typeName(T)),
25392539 };
25402540 // 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;
25422542 // 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;
25442544 // x:
25452545 return .drive_relative;
25462546 }
......@@ -2632,12 +2632,13 @@ fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType
26322632 std.debug.assert(getWin32PathType(T, path) == .local_device);
26332633 }
26342634
2635 const all_backslash = mem.littleToNative(T, path[0]) == '\\' and
2636 mem.littleToNative(T, path[1]) == '\\' and
2637 mem.littleToNative(T, path[3]) == '\\';
2638 return switch (mem.littleToNative(T, path[2])) {
2639 '?' => if (all_backslash) .verbatim else .fake_verbatim,
2640 '.' => .local_device,
2635 const backslash = mem.nativeToLittle(T, '\\');
2636 const all_backslash = path[0] == backslash and
2637 path[1] == backslash and
2638 path[3] == backslash;
2639 return switch (path[2]) {
2640 mem.nativeToLittle(T, '?') => if (all_backslash) .verbatim else .fake_verbatim,
2641 mem.nativeToLittle(T, '.') => .local_device,
26412642 else => unreachable,
26422643 };
26432644}
......@@ -2664,7 +2665,7 @@ pub fn ntToWin32Namespace(path: []const u16, out: []u16) error{ NameTooLong, Not
26642665 // `\??\UNC\` should be replaced by `\\` (two backslashes)
26652666 const is_unc = after_prefix.len >= 4 and
26662667 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]);
26682669 const win32_len = path.len - @as(usize, if (is_unc) 6 else 4);
26692670 if (out.len < win32_len) return error.NameTooLong;
26702671 if (is_unc) {