authorgravatar for techatrix@mailbox.orgTechatrix <techatrix@mailbox.org> 2026-07-19 01:10:14+02:00
committergravatar for techatrix@mailbox.orgTechatrix <techatrix@mailbox.org> 2026-07-21 16:31:30+02:00
logd697d97a95688e873d2677367e94010cdaa3ac73
tree20beeb7426b65ffd5a26a415a5408546aedb89bb
parentf0b768988d2c28f854195212d98b4fd3a7f654da
signaturebadge-check Signed by SSH key SHA256:HYC3SjXQcAt6uwv9pu/6OoVQ2rUH8rb5zKiUHSe9uxk

std.mem: replace `byteSwapAllFields` with `byteSwap`

The attached doc comment claims to only support structs even though the implementation also supports unions, arrays and integers. And unlike `byteSwapAllElements` it doesn't support enums and floats. booleans were only supported when they we're nested in a struct. A check to reject auto layout structs was missing as well. This function is used by the endianness aware functions in Reader and Writer which prevented some arbitrary types from being supported.

2 files changed, 69 insertions(+), 48 deletions(-)

lib/std/Io/Reader.zig+3-5
......@@ -718,7 +718,7 @@ pub inline fn readSliceEndian(
718718 endian: std.builtin.Endian,
719719) Error!void {
720720 try readSliceAll(r, @ptrCast(buffer));
721 if (native_endian != endian) for (buffer) |*elem| std.mem.byteSwapAllFields(Elem, elem);
721 if (native_endian != endian) std.mem.byteSwapAllElements(Elem, buffer);
722722}
723723
724724pub const ReadAllocError = Error || Allocator.Error;
......@@ -734,8 +734,7 @@ pub inline fn readSliceEndianAlloc(
734734) ReadAllocError![]Elem {
735735 const dest = try allocator.alloc(Elem, len);
736736 errdefer allocator.free(dest);
737 try readSliceAll(r, @ptrCast(dest));
738 if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem);
737 try r.readSliceEndian(Elem, dest, endian);
739738 return dest;
740739}
741740
......@@ -1227,8 +1226,7 @@ pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endia
12271226 .auto => @compileError("ill-defined memory layout"),
12281227 .@"extern" => {
12291228 var res: T = undefined;
1230 try r.readSliceAll(std.mem.asBytes(&res));
1231 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
1229 try r.readSliceEndian(T, (&res)[0..1], endian);
12321230 return res;
12331231 },
12341232 .@"packed" => {
lib/std/mem.zig+66-43
......@@ -2215,33 +2215,54 @@ test writeVarPackedInt {
22152215 try testing.expectEqual(T{ .a = 1, .b = value, .c = 4 }, st);
22162216}
22172217
2218/// Swap the byte order of all the members of the fields of a struct
2219/// (Changing their endianness)
2220pub fn byteSwapAllFields(comptime S: type, ptr: *S) void {
2221 byteSwapAllFieldsAligned(S, .of(S), ptr);
2218/// Deprecated: use `byteSwap` instead.
2219pub const byteSwapAllFields = byteSwap;
2220
2221/// Deprecated: use `byteSwapAligned` instead.
2222pub const byteSwapAllFieldsAligned = byteSwapAligned;
2223
2224/// Reverses the byte order.
2225/// Handles structs, unions, arrays, enums, floats, and integers recursively.
2226/// The order of extern struct fields and array elements remains unchanged and
2227/// will be byte swapped recursively.
2228/// Useful for converting between little-endian and big-endian representations.
2229pub fn byteSwap(comptime S: type, ptr: *S) void {
2230 byteSwapAligned(S, .of(S), ptr);
22222231}
22232232
2224/// Swap the byte order of all the members of the fields of a struct
2225/// (Changing their endianness)
2226pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *align(a.toByteUnits()) S) void {
2233/// Reverses the byte order.
2234/// Handles structs, unions, arrays, enums, floats, and integers recursively.
2235/// The order of extern struct fields and array elements remains unchanged and
2236/// will be byte swapped recursively.
2237/// Useful for converting between little-endian and big-endian representations.
2238pub fn byteSwapAligned(
2239 comptime S: type,
2240 comptime a: Alignment,
2241 ptr: *align(a.toByteUnits()) S,
2242) void {
22272243 switch (@typeInfo(S)) {
22282244 .@"struct" => |@"struct"| {
22292245 if (@"struct".backing_integer) |Int| {
22302246 ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*))));
2231 } else inline for (@"struct".field_types, @"struct".field_names, @"struct".field_attrs) |f_type, f_name, f_attr| {
2232 switch (@typeInfo(f_type)) {
2233 .@"struct" => byteSwapAllFieldsAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)),
2234 .@"union", .array => byteSwapAllFieldsAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)),
2235 .@"enum" => {
2236 @field(ptr, f_name) = @fromBackingInt(@intCast(@byteSwap(@backingInt(@field(ptr, f_name)))));
2237 },
2238 .bool => {},
2239 .float => |float| {
2240 @field(ptr, f_name) = @bitCast(@byteSwap(@as(@Int(.unsigned, float.bits), @bitCast(@field(ptr, f_name)))));
2241 },
2242 else => {
2243 @field(ptr, f_name) = @byteSwap(@field(ptr, f_name));
2244 },
2247 } else {
2248 if (@"struct".layout != .@"extern") {
2249 @compileError("byteSwapAligned expects a packed or extern struct");
2250 }
2251 inline for (@"struct".field_types, @"struct".field_names, @"struct".field_attrs) |f_type, f_name, f_attr| {
2252 switch (@typeInfo(f_type)) {
2253 .@"struct" => byteSwapAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)),
2254 .@"union", .array => byteSwapAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)),
2255 .@"enum" => {
2256 @field(ptr, f_name) = @fromBackingInt(@byteSwap(@backingInt(@field(ptr, f_name))));
2257 },
2258 .bool => {},
2259 .float => |float| {
2260 @field(ptr, f_name) = @bitCast(@byteSwap(@as(@Int(.unsigned, float.bits), @bitCast(@field(ptr, f_name)))));
2261 },
2262 else => {
2263 @field(ptr, f_name) = @byteSwap(@field(ptr, f_name));
2264 },
2265 }
22452266 }
22462267 }
22472268 },
......@@ -2249,7 +2270,7 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a
22492270 ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*))));
22502271 } else {
22512272 if (@"union".layout != .@"extern") {
2252 @compileError("byteSwapAllFields expects a packed or extern union");
2273 @compileError("byteSwapAligned expects a packed or extern union");
22532274 }
22542275
22552276 const first_size = @bitSizeOf(@"union".field_types[0]);
......@@ -2266,13 +2287,21 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a
22662287 .array => |array| {
22672288 byteSwapAllElements(array.child, ptr);
22682289 },
2290 .@"enum" => {
2291 ptr.* = @fromBackingInt(@byteSwap(@backingInt(ptr.*)));
2292 },
2293 .bool => {},
2294 .float => |float| {
2295 const int_repr: @Int(.unsigned, float.bits) = @bitCast(ptr.*);
2296 ptr.* = @bitCast(@byteSwap(int_repr));
2297 },
22692298 else => {
22702299 ptr.* = @byteSwap(ptr.*);
22712300 },
22722301 }
22732302}
22742303
2275test byteSwapAllFields {
2304test byteSwap {
22762305 const T = extern struct {
22772306 f0: u8,
22782307 f1: u16,
......@@ -2304,6 +2333,9 @@ test byteSwapAllFields {
23042333 } align(4),
23052334 f2: u32,
23062335 };
2336 const E = enum(u32) {
2337 _,
2338 };
23072339 var s = T{
23082340 .f0 = 0x12,
23092341 .f1 = 0x1234,
......@@ -2327,10 +2359,14 @@ test byteSwapAllFields {
23272359 .f1 = .{ .f0 = 0x123456789ABCDEF0 },
23282360 .f2 = 0x87654321,
23292361 };
2330 byteSwapAllFields(T, &s);
2331 byteSwapAllFields(K, &k);
2332 byteSwapAllFields(P, &p);
2333 byteSwapAllFields(A, &a);
2362 var e: E = @fromBackingInt(0x12345678);
2363 var f: f32 = @bitCast(@as(u32, 0x4640e400));
2364 byteSwap(T, &s);
2365 byteSwap(K, &k);
2366 byteSwap(P, &p);
2367 byteSwap(A, &a);
2368 byteSwap(E, &e);
2369 byteSwap(f32, &f);
23342370 try std.testing.expectEqual(T{
23352371 .f0 = 0x12,
23362372 .f1 = 0x3412,
......@@ -2354,28 +2390,15 @@ test byteSwapAllFields {
23542390 .f1 = .{ .f0 = 0xF0DEBC9A78563412 },
23552391 .f2 = 0x21436587,
23562392 }, a);
2393 try std.testing.expectEqual(@as(E, @fromBackingInt(0x78563412)), e);
2394 try std.testing.expectEqual(@as(f32, @bitCast(@as(u32, 0x00e44046))), f);
23572395}
23582396
23592397/// Reverses the byte order of all elements in a slice.
23602398/// Handles structs, unions, arrays, enums, floats, and integers recursively.
23612399/// Useful for converting between little-endian and big-endian representations.
23622400pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void {
2363 for (slice) |*elem| {
2364 switch (@typeInfo(@TypeOf(elem.*))) {
2365 .@"struct", .@"union", .array => byteSwapAllFields(@TypeOf(elem.*), elem),
2366 .@"enum" => {
2367 elem.* = @fromBackingInt(@intCast(@byteSwap(@backingInt(elem.*))));
2368 },
2369 .bool => {},
2370 .float => |float| {
2371 const int_repr: @Int(.unsigned, float.bits) = @bitCast(elem.*);
2372 elem.* = @bitCast(@byteSwap(int_repr));
2373 },
2374 else => {
2375 elem.* = @byteSwap(elem.*);
2376 },
2377 }
2378 }
2401 for (slice) |*elem| byteSwap(Elem, elem);
23792402}
23802403
23812404/// Returns an iterator that iterates over the slices of `buffer` that are not