From dcf9cae2568a7422ab7885404da63fafa31b00fc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 May 2024 12:06:29 -0700 Subject: [PATCH] std.Progress: handle big-endian targets We cannot rely on host endianness because the parent or child process may be executing inside QEMU. --- lib/std/Progress.zig | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 90dfbfba98..bfdf0f7436 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -7,6 +7,7 @@ const testing = std.testing; const assert = std.debug.assert; const Progress = @This(); const posix = std.posix; +const is_big_endian = builtin.cpu.arch.endian() == .big; /// `null` if the current node (and its children) should /// not print on update() @@ -101,6 +102,11 @@ pub const Node = struct { }; } + fn byteSwap(s: *Storage) void { + s.completed_count = @byteSwap(s.completed_count); + s.estimated_total_count = @byteSwap(s.estimated_total_count); + } + comptime { assert((@sizeOf(Storage) % 4) == 0); } @@ -719,9 +725,14 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff // Mount the root here. copyRoot(main_storage, &storage[0]); + if (is_big_endian) main_storage.byteSwap(); // Copy the rest of the tree to the end. - @memcpy(serialized_buffer.storage[serialized_len..][0..nodes_len], storage[1..][0..nodes_len]); + const storage_dest = serialized_buffer.storage[serialized_len..][0..nodes_len]; + @memcpy(storage_dest, storage[1..][0..nodes_len]); + + // Always little-endian over the pipe. + if (is_big_endian) for (storage_dest) |*s| s.byteSwap(); // Patch up parent pointers taking into account how the subtree is mounted. for (serialized_buffer.parents[serialized_len..][0..nodes_len], parents[1..][0..nodes_len]) |*dest, p| { @@ -983,6 +994,10 @@ fn write(buf: []const u8) anyerror!void { } fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { + // Byteswap if necessary to ensure little endian over the pipe. This is + // needed because the parent or child process might be running in qemu. + if (is_big_endian) for (serialized.storage) |*s| s.byteSwap(); + assert(serialized.parents.len == serialized.storage.len); const serialized_len: u8 = @intCast(serialized.parents.len); const header = std.mem.asBytes(&serialized_len); @@ -995,9 +1010,6 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { .{ .base = parents.ptr, .len = parents.len }, }; - // TODO: if big endian, byteswap - // this is needed because the parent or child process might be running in qemu - // If this write would block we do not want to keep trying, but we need to // know if a partial message was written. if (posix.writev(fd, &vecs)) |written| {