From 10c97c26d964ea70230341468e05fdea533a77a3 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Sun, 14 Jun 2026 11:20:16 +0100 Subject: [PATCH] std: update for new `@bitCast` semantics --- lib/std/Build/Configuration.zig | 6 +- lib/std/Io/Threaded.zig | 8 +- lib/std/Io/net.zig | 6 +- lib/std/Random/RomuTrio.zig | 4 +- lib/std/Random/Xoshiro256.zig | 4 +- lib/std/crypto/tls/Client.zig | 10 +-- lib/std/hash/xxhash.zig | 121 ++++++++++++++------------- lib/std/hash_map.zig | 4 +- lib/std/http/HeadParser.zig | 13 ++- lib/std/http/Server.zig | 15 ++-- lib/std/mem.zig | 134 +++++++++++++++++------------- lib/std/os/linux/IoUring/test.zig | 32 +++++-- lib/std/os/uefi.zig | 2 +- lib/std/os/windows.zig | 73 ++++++++-------- lib/std/testing.zig | 54 ++++++------ lib/std/zig/llvm/Builder.zig | 4 +- 16 files changed, 274 insertions(+), 216 deletions(-) diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 31ab7a7f4e6af90526bba195f94c077927cb38d4..f852ba12b99805f8e99f5c8f57f0538d45b59909 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -3215,7 +3215,8 @@ pub const Storage = enum { .@"extern" => { const n = @divExact(@sizeOf(Field), @sizeOf(u32)); defer i.* += n; - return @bitCast(buffer[i.*..][0..n].*); + const ptr: *align(@alignOf(u32)) const Field = @ptrCast(buffer[i.*..][0..n]); + return ptr.*; }, }, else => comptime unreachable, @@ -3381,7 +3382,8 @@ pub const Storage = enum { }, .@"extern" => { const n = @divExact(@sizeOf(Field), @sizeOf(u32)); - buffer[i..][0..n].* = @bitCast(value); + const ptr: *align(@alignOf(Field)) const [n]u32 = @ptrCast(&value); + buffer[i..][0..n].* = ptr.*; return n; }, }, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 3d84e8db1ae81e4656326f8a8610000c7856a761..65110de165e6341af93952e940705620718e01b7 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -14188,9 +14188,11 @@ fn addressUnixToPosix(a: *const net.UnixAddress, storage: *UnixAddress) posix.so } fn address4FromPosix(in: *const posix.sockaddr.in) net.Ip4Address { + // The network byte order address in `in.addr` is already the byte order we want. + const addr_bytes: *const [4]u8 = @ptrCast(&in.addr); return .{ .port = std.mem.bigToNative(u16, in.port), - .bytes = @bitCast(in.addr), + .bytes = addr_bytes.*, }; } @@ -14204,9 +14206,11 @@ fn address6FromPosix(in6: *const posix.sockaddr.in6) net.Ip6Address { } fn address4ToPosix(a: net.Ip4Address) posix.sockaddr.in { + // The byte order of `a.bytes` is already equivalent to a network byte order address. + const addr_raw: *align(1) const u32 = @ptrCast(&a.bytes); return .{ .port = std.mem.nativeToBig(u16, a.port), - .addr = @bitCast(a.bytes), + .addr = addr_raw.*, }; } diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig index c32d7dfa5016d16ca6c79ba890c9372fec056c27..6611502cc157238167445e516cb5ea5b6a0af832 100644 --- a/lib/std/Io/net.zig +++ b/lib/std/Io/net.zig @@ -592,11 +592,7 @@ pub const Ip6Address = struct { if (remaining != 0) return .incomplete; } - // Workaround that can be removed when this proposal is - // implemented https://github.com/ziglang/zig/issues/19755 - if ((comptime @import("builtin").cpu.arch.endian()) != .big) { - for (&parts) |*part| part.* = @byteSwap(part.*); - } + for (&parts) |*part| part.* = @byteSwap(part.*); return .{ .success = .{ .bytes = @bitCast(parts), diff --git a/lib/std/Random/RomuTrio.zig b/lib/std/Random/RomuTrio.zig index 5b7664d7f2b6cd7844020c1cd045a77955a0bc1f..12885d49415cedc85ccc7e066bac25ef9e5e5346 100644 --- a/lib/std/Random/RomuTrio.zig +++ b/lib/std/Random/RomuTrio.zig @@ -33,7 +33,7 @@ fn next(self: *RomuTrio) u64 { } pub fn seedWithBuf(self: *RomuTrio, buf: [24]u8) void { - const seed_buf = @as([3]u64, @bitCast(buf)); + const seed_buf: [3]u64 = @bitCast(buf); self.x_state = seed_buf[0]; self.y_state = seed_buf[1]; self.z_state = seed_buf[2]; @@ -121,7 +121,7 @@ test fill { } test "buf seeding test" { - const buf0 = @as([24]u8, @bitCast([3]u64{ 16294208416658607535, 13964609475759908645, 4703697494102998476 })); + const buf0: [24]u8 = @bitCast([3]u64{ 16294208416658607535, 13964609475759908645, 4703697494102998476 }); const resulting_state = .{ .x = 16294208416658607535, .y = 13964609475759908645, .z = 4703697494102998476 }; var r = RomuTrio.init(0); r.seedWithBuf(buf0); diff --git a/lib/std/Random/Xoshiro256.zig b/lib/std/Random/Xoshiro256.zig index fb65c2ba58e89b0bb50b22d1937ee8d359324e1c..6cb0583d982622b3f7df21b94368b03ddeb80c17 100644 --- a/lib/std/Random/Xoshiro256.zig +++ b/lib/std/Random/Xoshiro256.zig @@ -46,12 +46,12 @@ pub fn jump(self: *Xoshiro256) void { while (table != 0) : (table >>= 1) { if (@as(u1, @truncate(table)) != 0) { - s ^= @as(u256, @bitCast(self.s)); + s ^= @bitCast(self.s); } _ = self.next(); } - self.s = @as([4]u64, @bitCast(s)); + self.s = @bitCast(s); } pub fn seed(self: *Xoshiro256, init_s: u64) void { diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig index 4a0a8c6ddaf1c9231a89dce253c01c4eef8bf2fa..11eb30f226f217f9f28771f01aa53b8ccb21ccc0 100644 --- a/lib/std/crypto/tls/Client.zig +++ b/lib/std/crypto/tls/Client.zig @@ -376,7 +376,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client const nonce = nonce: { const V = @Vector(P.AEAD.nonce_length, u8); const pad: [P.AEAD.nonce_length - 8]u8 = @splat(0); - const operand: V = pad ++ @as([8]u8, @bitCast(big(read_seq))); + const operand: V = pad ++ @as([8]u8, @bitCast(@byteSwap(read_seq))); break :nonce @as(V, pv.server_handshake_iv) ^ operand; }; P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch @@ -416,7 +416,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client const nonce: [P.AEAD.nonce_length]u8 = nonce: { const V = @Vector(P.AEAD.nonce_length, u8); const pad: [P.AEAD.nonce_length - 8]u8 = @splat(0); - const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq))); + const operand: V = pad ++ @as([8]u8, @bitCast(@byteSwap(masked_read_seq))); break :nonce @as(V, pv.app_cipher.server_write_IV ++ record_iv) ^ operand; }; const ciphertext = record_decoder.slice(message_len); @@ -792,7 +792,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client const nonce: [P.AEAD.nonce_length]u8 = nonce: { const V = @Vector(P.AEAD.nonce_length, u8); const pad: [P.AEAD.nonce_length - 8]u8 = @splat(0); - const operand: V = pad ++ @as([8]u8, @bitCast(big(write_seq))); + const operand: V = pad ++ @as([8]u8, @bitCast(@byteSwap(write_seq))); break :nonce @as(V, pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt) ^ operand; }; var client_verify_msg = .{@intFromEnum(tls.ContentType.handshake)} ++ @@ -1105,7 +1105,7 @@ fn prepareCiphertextRecord( const nonce: [P.AEAD.nonce_length]u8 = nonce: { const V = @Vector(P.AEAD.nonce_length, u8); const pad: [P.AEAD.nonce_length - 8]u8 = @splat(0); - const operand: V = pad ++ @as([8]u8, @bitCast(big(c.write_seq))); + const operand: V = pad ++ @as([8]u8, @bitCast(@byteSwap(c.write_seq))); break :nonce @as(V, pv.client_write_IV ++ pv.client_salt) ^ operand; }; record_iv.* = nonce[P.fixed_iv_length..].*; @@ -1214,7 +1214,7 @@ fn readIndirect(c: *Client) Reader.Error!usize { const nonce: [P.AEAD.nonce_length]u8 = nonce: { const V = @Vector(P.AEAD.nonce_length, u8); const pad: [P.AEAD.nonce_length - 8]u8 = @splat(0); - const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq))); + const operand: V = pad ++ @as([8]u8, @bitCast(@byteSwap(masked_read_seq))); break :nonce @as(V, pv.server_write_IV ++ record_iv) ^ operand; }; const ciphertext = input.take(message_len) catch unreachable; // already peeked diff --git a/lib/std/hash/xxhash.zig b/lib/std/hash/xxhash.zig index 107b608006c2f8c23c6ba85077b24304138429b4..11794234a334067b4618d923ba23650b5439121b 100644 --- a/lib/std/hash/xxhash.zig +++ b/lib/std/hash/xxhash.zig @@ -421,7 +421,18 @@ pub const XxHash32 = struct { }; pub const XxHash3 = struct { + const block_bytes = 64; const Block = @Vector(8, u64); + const InputBlock = extern struct { + raw: [block_bytes]u8, + inline fn load(ptr: *const InputBlock) Block { + return @bitCast(ptr.raw); + } + inline fn store(ptr: *InputBlock, val: Block) void { + ptr.raw = @bitCast(val); + } + }; + const default_secret: [192]u8 = .{ 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, @@ -464,10 +475,6 @@ pub const XxHash3 = struct { return wide[0] ^ wide[1]; } - inline fn swap(x: anytype) @TypeOf(x) { - return if (native_endian == .big) @byteSwap(x) else x; - } - inline fn disableAutoVectorization(x: anytype) void { if (!@inComptime()) asm volatile ("" : @@ -476,20 +483,20 @@ pub const XxHash3 = struct { } inline fn mix16(seed: u64, input: []const u8, secret: []const u8) u64 { - const blk: [4]u64 = @bitCast([_][16]u8{ input[0..16].*, secret[0..16].* }); + const blk: [4]u64 = @bitCast([2][16]u8{ input[0..16].*, secret[0..16].* }); disableAutoVectorization(seed); return fold( - swap(blk[0]) ^ (swap(blk[2]) +% seed), - swap(blk[1]) ^ (swap(blk[3]) -% seed), + blk[0] ^ (blk[2] +% seed), + blk[1] ^ (blk[3] -% seed), ); } - const Accumulator = extern struct { + const Accumulator = struct { consumed: usize = 0, seed: u64, - secret: [192]u8 = undefined, - state: Block = Block{ + secret: [192]u8, + state: Block = .{ XxHash32.prime_3, XxHash64.prime_1, XxHash64.prime_2, @@ -501,33 +508,35 @@ pub const XxHash3 = struct { }, inline fn init(seed: u64) Accumulator { - var self = Accumulator{ .seed = seed }; - for ( - std.mem.bytesAsSlice(Block, &self.secret), - std.mem.bytesAsSlice(Block, &default_secret), - ) |*dst, src| { - dst.* = swap(swap(src) +% Block{ - seed, @as(u64, 0) -% seed, - seed, @as(u64, 0) -% seed, - seed, @as(u64, 0) -% seed, - seed, @as(u64, 0) -% seed, - }); + const seed_block: Block = .{ + seed, @as(u64, 0) -% seed, + seed, @as(u64, 0) -% seed, + seed, @as(u64, 0) -% seed, + seed, @as(u64, 0) -% seed, + }; + + var secret: [192]u8 = undefined; + const secret_blocks: []InputBlock = @ptrCast(&secret); + const default_secret_blocks: []const InputBlock = @ptrCast(&default_secret); + for (secret_blocks, default_secret_blocks) |*dst, *src| { + dst.store(src.load() +% seed_block); } - return self; + + return .{ .seed = seed, .secret = secret }; } inline fn round( noalias state: *Block, - noalias input_block: *align(1) const Block, - noalias secret_block: *align(1) const Block, + noalias input_block: *const InputBlock, + noalias secret_block: *const InputBlock, ) void { - const data = swap(input_block.*); - const mixed = data ^ swap(secret_block.*); + const data = input_block.load(); + const mixed = data ^ secret_block.load(); state.* +%= (mixed & @as(Block, @splat(0xffffffff))) *% (mixed >> @splat(32)); state.* +%= @shuffle(u64, data, undefined, [_]i32{ 1, 0, 3, 2, 5, 4, 7, 6 }); } - fn accumulate(noalias self: *Accumulator, blocks: []align(1) const Block) void { + fn accumulate(noalias self: *Accumulator, blocks: []const InputBlock) void { const secret = std.mem.bytesAsSlice(u64, self.secret[self.consumed * 8 ..]); for (blocks, secret[0..blocks.len]) |*input_block, *secret_block| { @prefetch(@as([*]const u8, @ptrCast(input_block)) + 320, .{}); @@ -536,14 +545,14 @@ pub const XxHash3 = struct { } fn scramble(self: *Accumulator) void { - const secret_block: Block = @bitCast(self.secret[192 - @sizeOf(Block) .. 192].*); + const secret_block: Block = @bitCast(self.secret[192 - block_bytes .. 192].*); self.state ^= self.state >> @splat(47); - self.state ^= swap(secret_block); + self.state ^= secret_block; self.state *%= @as(Block, @splat(XxHash32.prime_1)); } - fn consume(noalias self: *Accumulator, input_blocks: []align(1) const Block) void { - const blocks_per_scramble = 1024 / @sizeOf(Block); + fn consume(noalias self: *Accumulator, input_blocks: []const InputBlock) void { + const blocks_per_scramble = 1024 / block_bytes; std.debug.assert(self.consumed <= blocks_per_scramble); var blocks = input_blocks; @@ -561,12 +570,12 @@ pub const XxHash3 = struct { self.consumed += blocks.len; } - fn digest(noalias self: *Accumulator, total_len: u64, noalias last_block: *align(1) const Block) u64 { - const secret_block = self.secret[192 - @sizeOf(Block) - 7 ..][0..@sizeOf(Block)]; + fn digest(noalias self: *Accumulator, total_len: u64, noalias last_block: *const InputBlock) u64 { + const secret_block = self.secret[192 - block_bytes - 7 ..][0..block_bytes]; round(&self.state, last_block, @ptrCast(secret_block)); - const merge_block: Block = @bitCast(self.secret[11 .. 11 + @sizeOf(Block)].*); - self.state ^= swap(merge_block); + const merge_block: Block = @bitCast(self.secret[11 .. 11 + block_bytes].*); + self.state ^= merge_block; var result = XxHash64.prime_1 *% total_len; inline for (0..4) |i| { @@ -588,7 +597,7 @@ pub const XxHash3 = struct { if (input.len > 0) return hash3(seed, input, secret); const flip: [2]u64 = @bitCast(secret[56..72].*); - const key = swap(flip[0]) ^ swap(flip[1]); + const key = flip[0] ^ flip[1]; return avalanche(.h64, seed ^ key); } @@ -604,8 +613,8 @@ pub const XxHash3 = struct { input[input.len / 2], }); - const key = @as(u64, swap(flip[0]) ^ swap(flip[1])) +% seed; - return avalanche(.h64, key ^ swap(blk)); + const key = @as(u64, flip[0] ^ flip[1]) +% seed; + return avalanche(.h64, key ^ blk); } fn hash8(seed: u64, input: anytype, noalias secret: *const [192]u8) u64 { @@ -619,8 +628,8 @@ pub const XxHash3 = struct { }); const mixed = seed ^ (@as(u64, @byteSwap(@as(u32, @truncate(seed)))) << 32); - const key = (swap(flip[0]) ^ swap(flip[1])) -% mixed; - const combined = (@as(u64, swap(blk[0])) << 32) +% swap(blk[1]); + const key = (flip[0] ^ flip[1]) -% mixed; + const combined = (@as(u64, blk[0]) << 32) +% blk[1]; return avalanche(.{ .rrmxmx = input.len }, key ^ combined); } @@ -634,8 +643,8 @@ pub const XxHash3 = struct { input[input.len - 8 ..][0..8].*, }); - const lo = swap(blk[0]) ^ ((swap(flip[0]) ^ swap(flip[1])) +% seed); - const hi = swap(blk[1]) ^ ((swap(flip[2]) ^ swap(flip[3])) -% seed); + const lo = blk[0] ^ ((flip[0] ^ flip[1]) +% seed); + const hi = blk[1] ^ ((flip[2] ^ flip[3]) -% seed); const combined = @as(u64, input.len) +% @byteSwap(lo) +% hi +% fold(lo, hi); return avalanche(.h3, combined); } @@ -679,11 +688,11 @@ pub const XxHash3 = struct { @branchHint(.unlikely); std.debug.assert(input.len >= 240); - const block_count = ((input.len - 1) / @sizeOf(Block)) * @sizeOf(Block); - const last_block = input[input.len - @sizeOf(Block) ..][0..@sizeOf(Block)]; + const block_count = ((input.len - 1) / block_bytes) * block_bytes; + const last_block = input[input.len - block_bytes ..][0..block_bytes]; var acc = Accumulator.init(seed); - acc.consume(std.mem.bytesAsSlice(Block, input[0..block_count])); + acc.consume(std.mem.bytesAsSlice(InputBlock, input[0..block_count])); return acc.digest(input.len, @ptrCast(last_block)); } @@ -716,21 +725,21 @@ pub const XxHash3 = struct { @memcpy(self.buffer[self.buffered..], consumable[0..remaining]); consumable = consumable[remaining..]; - self.accumulator.consume(std.mem.bytesAsSlice(Block, &self.buffer)); + self.accumulator.consume(std.mem.bytesAsSlice(InputBlock, &self.buffer)); self.buffered = 0; } // The input isn't small enough to fit in the buffer. Consume it directly. if (consumable.len > self.buffer.len) { - const block_count = ((consumable.len - 1) / @sizeOf(Block)) * @sizeOf(Block); - self.accumulator.consume(std.mem.bytesAsSlice(Block, consumable[0..block_count])); + const block_count = ((consumable.len - 1) / block_bytes) * block_bytes; + self.accumulator.consume(std.mem.bytesAsSlice(InputBlock, consumable[0..block_count])); consumable = consumable[block_count..]; // In case we consume all remaining input, write the last block to end of the buffer // to populate the last_block_copy in final() similar to hashLong()'s last_block. @memcpy( - self.buffer[self.buffer.len - @sizeOf(Block) .. self.buffer.len], - (consumable.ptr - @sizeOf(Block))[0..@sizeOf(Block)], + self.buffer[self.buffer.len - block_bytes .. self.buffer.len], + (consumable.ptr - block_bytes)[0..block_bytes], ); } @@ -751,16 +760,16 @@ pub const XxHash3 = struct { // Make a copy of the Accumulator state in case `self` needs to update() / be used later. var accumulator_copy = self.accumulator; - var last_block_copy: [@sizeOf(Block)]u8 = undefined; + var last_block_copy: [block_bytes]u8 = undefined; // Digest the last block onthe Accumulator copy. return accumulator_copy.digest(self.total_len, last_block: { - if (self.buffered >= @sizeOf(Block)) { - const block_count = ((self.buffered - 1) / @sizeOf(Block)) * @sizeOf(Block); - accumulator_copy.consume(std.mem.bytesAsSlice(Block, self.buffer[0..block_count])); - break :last_block @ptrCast(self.buffer[self.buffered - @sizeOf(Block) ..][0..@sizeOf(Block)]); + if (self.buffered >= block_bytes) { + const block_count = ((self.buffered - 1) / block_bytes) * block_bytes; + accumulator_copy.consume(std.mem.bytesAsSlice(InputBlock, self.buffer[0..block_count])); + break :last_block @ptrCast(self.buffer[self.buffered - block_bytes ..][0..block_bytes]); } else { - const remaining = @sizeOf(Block) - self.buffered; + const remaining = block_bytes - self.buffered; @memcpy(last_block_copy[0..remaining], self.buffer[self.buffer.len - remaining ..][0..remaining]); @memcpy(last_block_copy[remaining..][0..self.buffered], self.buffer[0..self.buffered]); break :last_block @ptrCast(&last_block_copy); diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 5ba6aadc9ed418611482075e7daf0c6edaf775ea..a6562bc5ea1ab479963b308beae24fb303d98c6d 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -593,8 +593,8 @@ fn Custom( fingerprint: FingerPrint = free, used: u1 = 0, - const slot_free = @as(u8, @bitCast(Metadata{ .fingerprint = free })); - const slot_tombstone = @as(u8, @bitCast(Metadata{ .fingerprint = tombstone })); + const slot_free: u8 = @bitCast(Metadata{ .fingerprint = free }); + const slot_tombstone: u8 = @bitCast(Metadata{ .fingerprint = tombstone }); pub fn isUsed(self: Metadata) bool { return self.used == 1; diff --git a/lib/std/http/HeadParser.zig b/lib/std/http/HeadParser.zig index 7b9ca6d2c58f4dce7be24cd4db7e225a1d8bc13a..4c7f1779043b0b0f809cef4f39553fe049e1b53d 100644 --- a/lib/std/http/HeadParser.zig +++ b/lib/std/http/HeadParser.zig @@ -116,11 +116,8 @@ pub fn feed(p: *HeadParser, bytes: []const u8) usize { const chunk = bytes[index..][0..vector_len]; const v: Vector = chunk.*; - // depends on https://github.com/ziglang/zig/issues/19755 - // const matches_r: BitVector = @bitCast(v == @as(Vector, @splat('\r'))); - // const matches_n: BitVector = @bitCast(v == @as(Vector, @splat('\n'))); - const matches_r: BitVector = @select(u1, v == @as(Vector, @splat('\r')), @as(Vector, @splat(1)), @as(Vector, @splat(0))); - const matches_n: BitVector = @select(u1, v == @as(Vector, @splat('\n')), @as(Vector, @splat(1)), @as(Vector, @splat(0))); + const matches_r: BitVector = @bitCast(v == @as(Vector, @splat('\r'))); + const matches_n: BitVector = @bitCast(v == @as(Vector, @splat('\n'))); const matches_or: SizeVector = matches_r | matches_n; const matches = @reduce(.Add, matches_or); @@ -331,15 +328,15 @@ pub fn feed(p: *HeadParser, bytes: []const u8) usize { } inline fn int16(array: *const [2]u8) u16 { - return @bitCast(array.*); + return std.mem.toNative(u16, @bitCast(array.*), .little); } inline fn int24(array: *const [3]u8) u24 { - return @bitCast(array.*); + return std.mem.toNative(u24, @bitCast(array.*), .little); } inline fn int32(array: *const [4]u8) u32 { - return @bitCast(array.*); + return std.mem.toNative(u32, @bitCast(array.*), .little); } inline fn intShift(comptime T: type, x: anytype) T { diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig index 7820ad6d0dc2db532f2c1f7bebc58cb50bce0a04..3d56f59da31ce69377e8886a62245cc8f6612c7c 100644 --- a/lib/std/http/Server.zig +++ b/lib/std/http/Server.zig @@ -726,7 +726,7 @@ pub const WebSocket = struct { else => @intFromEnum(h1.payload_len), }; if (len > in.buffer.len) return error.MessageOversize; - const mask: u32 = @bitCast((try in.takeArray(4)).*); + const mask: [4]u8 = (try in.takeArray(4)).*; const payload = try in.take(len); // Skip pongs. @@ -734,11 +734,16 @@ pub const WebSocket = struct { // The last item may contain a partial word of unused data. const floored_len = (payload.len / 4) * 4; - const u32_payload: []align(1) u32 = @ptrCast(payload[0..floored_len]); - for (u32_payload) |*elem| elem.* ^= mask; - const mask_bytes: []const u8 = @ptrCast(&mask); - for (payload[floored_len..], mask_bytes[0 .. payload.len - floored_len]) |*leftover, m| + + const payload_chunks: [][4]u8 = @ptrCast(payload[0..floored_len]); + for (payload_chunks) |*chunk| { + const mask_i: u32 = @bitCast(mask); + const chunk_i: u32 = @bitCast(chunk.*); + chunk.* = @bitCast(chunk_i ^ mask_i); + } + for (payload[floored_len..], mask[0 .. payload.len - floored_len]) |*leftover, m| { leftover.* ^= m; + } return .{ .opcode = h0.opcode, diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 4d29a35380daacea074e5f32d441ce23e9c2a0be..31566b2e5df9e581834f30d6f6179df5bd0d6f05 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1848,18 +1848,18 @@ pub fn readVarPackedInt( if (@bitSizeOf(T) <= 8) { // These are the same shifts/masks we perform below, but adds `@truncate`/`@intCast` // where needed since int is smaller than a byte. - const value = if (read_size == 1) b: { - break :b @as(uN, @truncate(read_bytes[0] >> bit_shift)); + const value: uN = if (read_size == 1) b: { + break :b @truncate(read_bytes[0] >> bit_shift); } else b: { const i: u1 = @intFromBool(endian == .big); - const head = @as(uN, @truncate(read_bytes[i] >> bit_shift)); - const tail_shift = @as(Log2N, @intCast(@as(u4, 8) - bit_shift)); - const tail = @as(uN, @truncate(read_bytes[1 - i])); + const head: uN = @truncate(read_bytes[i] >> bit_shift); + const tail_shift: Log2N = @intCast(@as(u4, 8) - bit_shift); + const tail: uN = @truncate(read_bytes[1 - i]); break :b (tail << tail_shift) | head; }; switch (signedness) { - .signed => return @as(T, @intCast((@as(iN, @bitCast(value)) << pad) >> pad)), - .unsigned => return @as(T, @intCast((@as(uN, @bitCast(value)) << pad) >> pad)), + .signed => return @intCast((@as(iN, @bitCast(value)) << pad) >> pad), + .unsigned => return @intCast((value << pad) >> pad), } } @@ -1880,8 +1880,8 @@ pub fn readVarPackedInt( }, } switch (signedness) { - .signed => return @as(T, @intCast((@as(iN, @bitCast(int)) << pad) >> pad)), - .unsigned => return @as(T, @intCast((@as(uN, @bitCast(int)) << pad) >> pad)), + .signed => return @intCast((@as(iN, @bitCast(int)) << pad) >> pad), + .unsigned => return @intCast((int << pad) >> pad), } } @@ -1896,8 +1896,13 @@ test readVarPackedInt { /// The bit count of T must be evenly divisible by 8. /// This function cannot fail and cannot cause undefined behavior. pub inline fn readInt(comptime T: type, buffer: *const [@divExact(@typeInfo(T).int.bits, 8)]u8, endian: Endian) T { - const value: T = @bitCast(buffer.*); - return if (endian == native_endian) value else @byteSwap(value); + // Zig's logical bit order aligns with a little-endian byte array, so when reading in big-endian + // we must `@byteSwap` the int after we `@bitCast` to it. + const little_val: T = @bitCast(buffer.*); + return switch (endian) { + .little => little_val, + .big => @byteSwap(little_val), + }; } test readInt { @@ -1940,13 +1945,15 @@ fn readPackedIntLittle(comptime T: type, bytes: []const u8, bit_offset: usize) T // Read by loading a LoadInt, and then follow it up with a 1-byte read // of the tail if bit_offset pushed us over a byte boundary. const read_bytes = bytes[bit_offset / 8 ..]; - const val = @as(uN, @truncate(readInt(LoadInt, read_bytes[0..load_size], .little) >> bit_shift)); + const val: uN = @truncate(readInt(LoadInt, read_bytes[0..load_size], .little) >> bit_shift); if (bit_shift > load_tail_bits) { const tail_bits = @as(Log2N, @intCast(bit_shift - load_tail_bits)); const tail_byte = read_bytes[load_size]; const tail_truncated = if (bit_count < 8) @as(uN, @truncate(tail_byte)) else @as(uN, tail_byte); - return @as(T, @bitCast(val | (tail_truncated << (@as(Log2N, @truncate(bit_count)) -% tail_bits)))); - } else return @as(T, @bitCast(val)); + return @bitCast(val | (tail_truncated << (@as(Log2N, @truncate(bit_count)) -% tail_bits))); + } else { + return @bitCast(val); + } } fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T { @@ -1972,8 +1979,10 @@ fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T { if (bit_shift > load_tail_bits) { const tail_bits = @as(Log2N, @intCast(bit_shift - load_tail_bits)); const tail_byte = if (bit_count < 8) @as(uN, @truncate(read_bytes[0])) else @as(uN, read_bytes[0]); - return @as(T, @bitCast(val | (tail_byte << (@as(Log2N, @truncate(bit_count)) -% tail_bits)))); - } else return @as(T, @bitCast(val)); + return @bitCast(val | (tail_byte << (@as(Log2N, @truncate(bit_count)) -% tail_bits))); + } else { + return @bitCast(val); + } } /// Loads an integer from packed memory. @@ -2011,7 +2020,12 @@ test "comptime read/write int" { /// This function always succeeds, has defined behavior for all inputs, but /// the integer bit width must be divisible by 8. pub inline fn writeInt(comptime T: type, buffer: *[@divExact(@typeInfo(T).int.bits, 8)]u8, value: T, endian: Endian) void { - buffer.* = @bitCast(if (endian == native_endian) value else @byteSwap(value)); + // Zig's logical bit order aligns with a little-endian byte array, so when writing in big-endian + // we must `@byteSwap` the int before we `@bitCast` to an array. + buffer.* = switch (endian) { + .little => @bitCast(value), + .big => @bitCast(@byteSwap(value)), + }; } test writeInt { @@ -2209,10 +2223,10 @@ pub fn byteSwapAllFields(comptime S: type, ptr: *S) void { /// (Changing their endianness) pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *align(a.toByteUnits()) S) void { switch (@typeInfo(S)) { - .@"struct" => |struct_info| { - if (struct_info.backing_integer) |Int| { + .@"struct" => |@"struct"| { + if (@"struct".backing_integer) |Int| { ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*)))); - } else inline for (struct_info.field_types, struct_info.field_names, struct_info.field_attrs) |f_type, f_name, f_attr| { + } else inline for (@"struct".field_types, @"struct".field_names, @"struct".field_attrs) |f_type, f_name, f_attr| { switch (@typeInfo(f_type)) { .@"struct" => byteSwapAllFieldsAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)), .@"union", .array => byteSwapAllFieldsAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)), @@ -2220,8 +2234,8 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a @field(ptr, f_name) = @enumFromInt(@byteSwap(@intFromEnum(@field(ptr, f_name)))); }, .bool => {}, - .float => |float_info| { - @field(ptr, f_name) = @bitCast(@byteSwap(@as(@Int(.unsigned, float_info.bits), @bitCast(@field(ptr, f_name))))); + .float => |float| { + @field(ptr, f_name) = @bitCast(@byteSwap(@as(@Int(.unsigned, float.bits), @bitCast(@field(ptr, f_name))))); }, else => { @field(ptr, f_name) = @byteSwap(@field(ptr, f_name)); @@ -2229,23 +2243,26 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a } } }, - .@"union" => |union_info| { - if (union_info.tag_type != null) { - @compileError("byteSwapAllFields expects an untagged union"); + .@"union" => |@"union"| if (@"union".backing_integer) |Int| { + ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*)))); + } else { + if (@"union".layout != .@"extern") { + @compileError("byteSwapAllFields expects a packed or extern union"); } - const first_size = @bitSizeOf(union_info.field_types[0]); - inline for (union_info.field_types) |field_type| { + const first_size = @bitSizeOf(@"union".field_types[0]); + inline for (@"union".field_types) |field_type| { if (@bitSizeOf(field_type) != first_size) { @compileError("Unable to byte-swap unions with varying field sizes"); } } - const BackingInt = @Int(.unsigned, @bitSizeOf(S)); - ptr.* = @bitCast(@byteSwap(@as(BackingInt, @bitCast(ptr.*)))); + const FieldInt = @Int(.unsigned, first_size); + const field_ptr = &@field(ptr, @"union".field_names[0]); + field_ptr.* = @bitCast(@byteSwap(@as(FieldInt, @bitCast(field_ptr.*)))); }, - .array => |info| { - byteSwapAllElements(info.child, ptr); + .array => |array| { + byteSwapAllElements(array.child, ptr); }, else => { ptr.* = @byteSwap(ptr.*); @@ -2291,7 +2308,7 @@ test byteSwapAllFields { .f2 = 0x12345678, .f3 = .{0x12}, .f4 = true, - .f5 = @as(f32, @bitCast(@as(u32, 0x4640e400))), + .f5 = @bitCast(@as(u32, 0x4640e400)), .f6 = .{ .f0 = 0x1234 }, }; var k = K{ @@ -2300,7 +2317,7 @@ test byteSwapAllFields { .f2 = 0x1234, .f3 = .{0x12}, .f4 = false, - .f5 = @as(f32, @bitCast(@as(u32, 0x45d42800))), + .f5 = @bitCast(@as(u32, 0x45d42800)), }; var p: P = @bitCast(@as(u32, 0x01234567)); var a: A = A{ @@ -2318,7 +2335,7 @@ test byteSwapAllFields { .f2 = 0x78563412, .f3 = .{0x12}, .f4 = true, - .f5 = @as(f32, @bitCast(@as(u32, 0x00e44046))), + .f5 = @bitCast(@as(u32, 0x00e44046)), .f6 = .{ .f0 = 0x3412 }, }, s); try std.testing.expectEqual(K{ @@ -2327,7 +2344,7 @@ test byteSwapAllFields { .f2 = 0x3412, .f3 = .{0x12}, .f4 = false, - .f5 = @as(f32, @bitCast(@as(u32, 0x0028d445))), + .f5 = @bitCast(@as(u32, 0x0028d445)), }, k); try std.testing.expectEqual(@as(P, @bitCast(@as(u32, 0x67452301))), p); try std.testing.expectEqual(A{ @@ -2348,8 +2365,9 @@ pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void { elem.* = @enumFromInt(@byteSwap(@intFromEnum(elem.*))); }, .bool => {}, - .float => |float_info| { - elem.* = @bitCast(@byteSwap(@as(@Int(.unsigned, float_info.bits), @bitCast(elem.*)))); + .float => |float| { + const int_repr: @Int(.unsigned, float.bits) = @bitCast(elem.*); + elem.* = @bitCast(@byteSwap(int_repr)); }, else => { elem.* = @byteSwap(elem.*); @@ -3870,25 +3888,29 @@ inline fn reverseVector(comptime N: usize, comptime T: type, a: []T) [N]T { pub fn reverse(comptime T: type, items: []T) void { var i: usize = 0; const end = items.len / 2; - if (use_vectors and - !@inComptime() and - @bitSizeOf(T) > 0 and - std.math.isPowerOfTwo(@bitSizeOf(T))) - { - if (std.simd.suggestVectorLength(T)) |simd_size| { - if (simd_size <= end) { - const simd_end = end - (simd_size - 1); - while (i < simd_end) : (i += simd_size) { - const left_slice = items[i .. i + simd_size]; - const right_slice = items[items.len - i - simd_size .. items.len - i]; - const left_shuffled: [simd_size]T = reverseVector(simd_size, T, left_slice); - const right_shuffled: [simd_size]T = reverseVector(simd_size, T, right_slice); + vec: { + if (!use_vectors) break :vec; + if (@inComptime()) break :vec; + switch (@typeInfo(T)) { + .int, .float => {}, + .pointer => |pointer| if (pointer.size == .slice) break :vec, + else => break :vec, + } + if (@bitSizeOf(T) == 0 or !comptime std.math.isPowerOfTwo(@bitSizeOf(T))) break :vec; + const simd_size = std.simd.suggestVectorLength(T) orelse break :vec; + if (simd_size > end) break :vec; - @memcpy(right_slice, &left_shuffled); - @memcpy(left_slice, &right_shuffled); - } - } + const simd_end = end - (simd_size - 1); + while (i < simd_end) : (i += simd_size) { + const left_slice = items[i .. i + simd_size]; + const right_slice = items[items.len - i - simd_size .. items.len - i]; + + const left_shuffled: [simd_size]T = reverseVector(simd_size, T, left_slice); + const right_shuffled: [simd_size]T = reverseVector(simd_size, T, right_slice); + + @memcpy(right_slice, &left_shuffled); + @memcpy(left_slice, &right_shuffled); } } @@ -5009,8 +5031,8 @@ test "read/write(Var)PackedInt" { for ([_]PackedType{ ~@as(PackedType, 0), // all ones: -1 iN / maxInt uN @as(PackedType, 0), // all zeros: 0 iN / 0 uN - @as(PackedType, @bitCast(@as(iPackedType, math.maxInt(iPackedType)))), // maxInt iN - @as(PackedType, @bitCast(@as(iPackedType, math.minInt(iPackedType)))), // maxInt iN + @bitCast(@as(iPackedType, math.maxInt(iPackedType))), // maxInt iN + @bitCast(@as(iPackedType, math.minInt(iPackedType))), // maxInt iN random.int(PackedType), // random random.int(PackedType), // random }) |write_value| { diff --git a/lib/std/os/linux/IoUring/test.zig b/lib/std/os/linux/IoUring/test.zig index add4f30f8512e1c0c470cba0f253a7ff80159cf1..27925c81fb138ec20efbb6bc05acfff5a4a09879 100644 --- a/lib/std/os/linux/IoUring/test.zig +++ b/lib/std/os/linux/IoUring/test.zig @@ -526,7 +526,9 @@ test "sendmsg/recvmsg" { var address_server: linux.sockaddr.in = .{ .port = 0, - .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), + .addr = @as(*align(1) const u32, @ptrCast( + &@as([4]u8, .{ 127, 0, 0, 1 }), + )).*, }; const server = try socket(address_server.family, posix.SOCK.DGRAM, 0); @@ -1028,7 +1030,9 @@ test "shutdown" { var address: linux.sockaddr.in = .{ .port = 0, - .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), + .addr = @as(*align(1) const u32, @ptrCast( + &@as([4]u8, .{ 127, 0, 0, 1 }), + )).*, }; // Socket bound, expect shutdown to work @@ -1740,7 +1744,9 @@ test "accept multishot" { var address: linux.sockaddr.in = .{ .port = 0, - .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), + .addr = @as(*align(1) const u32, @ptrCast( + &@as([4]u8, .{ 127, 0, 0, 1 }), + )).*, }; const listener_socket = try createListenerSocket(&address); defer _ = linux.close(listener_socket); @@ -1842,7 +1848,9 @@ test "accept_direct" { defer ring.deinit(); var address: linux.sockaddr.in = .{ .port = 0, - .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), + .addr = @as(*align(1) const u32, @ptrCast( + &@as([4]u8, .{ 127, 0, 0, 1 }), + )).*, }; // register direct file descriptors @@ -1931,7 +1939,9 @@ test "accept_multishot_direct" { var address: linux.sockaddr.in = .{ .port = 0, - .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), + .addr = @as(*align(1) const u32, @ptrCast( + &@as([4]u8, .{ 127, 0, 0, 1 }), + )).*, }; var registered_fds: [2]linux.fd_t = @splat(-1); @@ -2041,7 +2051,9 @@ test "socket_direct/socket_direct_alloc/close_direct" { // use sockets from registered_fds in connect operation var address: linux.sockaddr.in = .{ .port = 0, - .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), + .addr = @as(*align(1) const u32, @ptrCast( + &@as([4]u8, .{ 127, 0, 0, 1 }), + )).*, }; const listener_socket = try createListenerSocket(&address); defer _ = linux.close(listener_socket); @@ -2426,7 +2438,9 @@ test "bind/listen/connect" { var addr: linux.sockaddr.in = .{ .port = 0, - .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), + .addr = @as(*align(1) const u32, @ptrCast( + &@as([4]u8, .{ 127, 0, 0, 1 }), + )).*, }; const proto: u32 = if (addr.family == linux.AF.UNIX) 0 else linux.IPPROTO.TCP; @@ -2614,7 +2628,9 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { // Create a TCP server socket var address: linux.sockaddr.in = .{ .port = 0, - .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), + .addr = @as(*align(1) const u32, @ptrCast( + &@as([4]u8, .{ 127, 0, 0, 1 }), + )).*, }; const listener_socket = try createListenerSocket(&address); errdefer _ = linux.close(listener_socket); diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index cc2dac949e31d6c5a0bda855079f234f0ae7e181..e48ec85fe32bc7af9c7dce101ebf5d2d6a9fbd28 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -218,7 +218,7 @@ pub const TimeCapabilities = extern struct { pub const FileHandle = *opaque {}; test "GUID formatting" { - const bytes = [_]u8{ 137, 60, 203, 50, 128, 128, 124, 66, 186, 19, 80, 73, 135, 59, 194, 135 }; + const bytes: [16]u8 = .{ 137, 60, 203, 50, 128, 128, 124, 66, 186, 19, 80, 73, 135, 59, 194, 135 }; const guid: Guid = @bitCast(bytes); const str = try std.fmt.allocPrint(std.testing.allocator, "{f}", .{guid}); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 89105b673602a7c5d13439100a4426082b7c3b0f..7a3c134e2a3416f35aed6cbff592105f0cd95109 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -4189,19 +4189,11 @@ pub const GUID = extern struct { Data3: u16, Data4: [8]u8, - const hex_offsets = switch (builtin.target.cpu.arch.endian()) { - .big => [16]u6{ - 0, 2, 4, 6, - 9, 11, 14, 16, - 19, 21, 24, 26, - 28, 30, 32, 34, - }, - .little => [16]u6{ - 6, 4, 2, 0, - 11, 9, 16, 14, - 19, 21, 24, 26, - 28, 30, 32, 34, - }, + const hex_offsets: [16]u6 = .{ + 6, 4, 2, 0, + 11, 9, 16, 14, + 19, 21, 24, 26, + 28, 30, 32, 34, }; pub fn parse(s: []const u8) GUID { @@ -4216,12 +4208,21 @@ pub const GUID = extern struct { assert(s[13] == '-'); assert(s[18] == '-'); assert(s[23] == '-'); - var bytes: [16]u8 = undefined; - for (hex_offsets, 0..) |hex_offset, i| { - bytes[i] = (try std.fmt.charToDigit(s[hex_offset], 16)) << 4 | - try std.fmt.charToDigit(s[hex_offset + 1], 16); - } - return @as(GUID, @bitCast(bytes)); + var raw1: [4]u8 = undefined; + var raw2: [2]u8 = undefined; + var raw3: [2]u8 = undefined; + var raw4: [8]u8 = undefined; + assert((try std.fmt.hexToBytes(&raw1, s[0..8])).len == raw1.len); + assert((try std.fmt.hexToBytes(&raw2, s[9..13])).len == raw2.len); + assert((try std.fmt.hexToBytes(&raw3, s[14..18])).len == raw3.len); + assert((try std.fmt.hexToBytes(raw4[0..2], s[19..23])).len == 2); + assert((try std.fmt.hexToBytes(raw4[2..8], s[24..36])).len == 6); + return .{ + .Data1 = @byteSwap(@as(u32, @bitCast(raw1))), + .Data2 = @byteSwap(@as(u16, @bitCast(raw2))), + .Data3 = @byteSwap(@as(u16, @bitCast(raw3))), + .Data4 = raw4, + }; } pub fn format(self: GUID, w: *std.Io.Writer) std.Io.Writer.Error!void { @@ -4233,28 +4234,28 @@ pub const GUID = extern struct { self.Data4[2..8], }); } -}; -test GUID { - try std.testing.expectEqual( - GUID{ + test parse { + const expected: GUID = .{ .Data1 = 0x01234567, .Data2 = 0x89ab, .Data3 = 0xef10, .Data4 = "\x32\x54\x76\x98\xba\xdc\xfe\x91".*, - }, - GUID.parse("{01234567-89AB-EF10-3254-7698badcfe91}"), - ); - try std.testing.expectFmt( - "{01234567-89ab-ef10-3254-7698badcfe91}", - "{f}", - .{GUID.parse("{01234567-89AB-EF10-3254-7698badcfe91}")}, - ); - try std.testing.expectFmt( - "{00000001-0001-0001-0001-000000000001}", - "{f}", - .{GUID{ .Data1 = 1, .Data2 = 1, .Data3 = 1, .Data4 = [_]u8{ 0, 1, 0, 0, 0, 0, 0, 1 } }}, - ); + }; + try std.testing.expectEqual(expected, GUID.parse("{01234567-89AB-EF10-3254-7698badcfe91}")); + } + + test format { + const guid0: GUID = .{ .Data1 = 1, .Data2 = 1, .Data3 = 1, .Data4 = .{ 0, 1, 0, 0, 0, 0, 0, 1 } }; + try std.testing.expectFmt("{00000001-0001-0001-0001-000000000001}", "{f}", .{guid0}); + + const guid1: GUID = .parse("{01234567-89AB-EF10-3254-7698badcfe91}"); + try std.testing.expectFmt("{01234567-89ab-ef10-3254-7698badcfe91}", "{f}", .{guid1}); + } +}; + +test { + _ = GUID; } pub const COORD = extern struct { diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 971cd865b24232360c06e1caa5c872aa13da01bb..46c7fe938b22300c23dcedc3570cb329090d3d3a 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -141,39 +141,45 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void { try expectEqualSlices(info.child, &expect_array, &actual_array); }, - .@"struct" => |structType| { - inline for (structType.field_names) |field_name| { + .@"struct" => |@"struct"| { + inline for (@"struct".field_names) |field_name| { try expectEqual(@field(expected, field_name), @field(actual, field_name)); } }, - .@"union" => |union_info| { - if (union_info.tag_type == null) { - const first_size = @bitSizeOf(union_info.field_types[0]); - inline for (union_info.field_types) |field_type| { + .@"union" => |@"union"| if (@"union".backing_integer) |Int| { + try expectEqual(@as(Int, @bitCast(expected)), @as(Int, @bitCast(actual))); + } else switch (@"union".layout) { + .@"packed" => { + const Int = @Int(.unsigned, @bitSizeOf(T)); + try expectEqual(@as(Int, @bitCast(expected)), @as(Int, @bitCast(actual))); + }, + .@"extern" => { + const first_size = @bitSizeOf(@"union".field_types[0]); + inline for (@"union".field_types) |field_type| { if (@bitSizeOf(field_type) != first_size) { - @compileError("Unable to compare untagged unions with varying field sizes for type " ++ @typeName(@TypeOf(actual))); + @compileError("Unable to compare extern unions with varying field sizes for type " ++ @typeName(T)); } } - - const BackingInt = @Int(.unsigned, @bitSizeOf(T)); + const FieldInt = @Int(.unsigned, first_size); + const expected_field = @field(expected, @"union".field_names[0]); + const actual_field = @field(actual, @"union".field_names[0]); return expectEqual( - @as(BackingInt, @bitCast(expected)), - @as(BackingInt, @bitCast(actual)), + @as(FieldInt, @bitCast(expected_field)), + @as(FieldInt, @bitCast(actual_field)), ); - } - - const Tag = std.meta.Tag(@TypeOf(expected)); - - const expectedTag = @as(Tag, expected); - const actualTag = @as(Tag, actual); - - try expectEqual(expectedTag, actualTag); - - // we only reach this switch if the tags are equal - switch (expected) { - inline else => |val, tag| try expectEqual(val, @field(actual, @tagName(tag))), - } + }, + .auto => { + const Tag = @"union".tag_type orelse @compileError("byteSwapAllFields expects packed, extern, or tagged union"); + + try expectEqual(@as(Tag, expected), @as(Tag, actual)); + switch (expected) { + inline else => |expected_payload, tag| { + const actual_payload = @field(actual, @tagName(tag)); + try expectEqual(expected_payload, actual_payload); + }, + } + }, }, .optional => { diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 6546fc5a53f1b707cfd4e8fd41041a4ad42f9bbf..f780e28f5c4d897d8a920c890c38933ea8f0ee85 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -1816,7 +1816,7 @@ pub const Linkage = enum(u4) { } }; -pub const Preemption = enum { +pub const Preemption = enum(u2) { dso_preemptable, dso_local, implicit_dso_local, @@ -2011,7 +2011,7 @@ pub const AddrSpace = enum(u24) { } }; -pub const ExternallyInitialized = enum { +pub const ExternallyInitialized = enum(u1) { default, externally_initialized, -- 2.54.0