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(...@@ -718,7 +718,7 @@ pub inline fn readSliceEndian(
718 endian: std.builtin.Endian,718 endian: std.builtin.Endian,
719) Error!void {719) Error!void {
720 try readSliceAll(r, @ptrCast(buffer));720 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);
722}722}
723723
724pub const ReadAllocError = Error || Allocator.Error;724pub const ReadAllocError = Error || Allocator.Error;
...@@ -734,8 +734,7 @@ pub inline fn readSliceEndianAlloc(...@@ -734,8 +734,7 @@ pub inline fn readSliceEndianAlloc(
734) ReadAllocError![]Elem {734) ReadAllocError![]Elem {
735 const dest = try allocator.alloc(Elem, len);735 const dest = try allocator.alloc(Elem, len);
736 errdefer allocator.free(dest);736 errdefer allocator.free(dest);
737 try readSliceAll(r, @ptrCast(dest));737 try r.readSliceEndian(Elem, dest, endian);
738 if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem);
739 return dest;738 return dest;
740}739}
741740
...@@ -1227,8 +1226,7 @@ pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endia...@@ -1227,8 +1226,7 @@ pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endia
1227 .auto => @compileError("ill-defined memory layout"),1226 .auto => @compileError("ill-defined memory layout"),
1228 .@"extern" => {1227 .@"extern" => {
1229 var res: T = undefined;1228 var res: T = undefined;
1230 try r.readSliceAll(std.mem.asBytes(&res));1229 try r.readSliceEndian(T, (&res)[0..1], endian);
1231 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
1232 return res;1230 return res;
1233 },1231 },
1234 .@"packed" => {1232 .@"packed" => {
lib/std/mem.zig+66-43
...@@ -2215,33 +2215,54 @@ test writeVarPackedInt {...@@ -2215,33 +2215,54 @@ test writeVarPackedInt {
2215 try testing.expectEqual(T{ .a = 1, .b = value, .c = 4 }, st);2215 try testing.expectEqual(T{ .a = 1, .b = value, .c = 4 }, st);
2216}2216}
22172217
2218/// Swap the byte order of all the members of the fields of a struct2218/// Deprecated: use `byteSwap` instead.
2219/// (Changing their endianness)2219pub const byteSwapAllFields = byteSwap;
2220pub fn byteSwapAllFields(comptime S: type, ptr: *S) void {2220
2221 byteSwapAllFieldsAligned(S, .of(S), ptr);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);
2222}2231}
22232232
2224/// Swap the byte order of all the members of the fields of a struct2233/// Reverses the byte order.
2225/// (Changing their endianness)2234/// Handles structs, unions, arrays, enums, floats, and integers recursively.
2226pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *align(a.toByteUnits()) S) void {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 {
2227 switch (@typeInfo(S)) {2243 switch (@typeInfo(S)) {
2228 .@"struct" => |@"struct"| {2244 .@"struct" => |@"struct"| {
2229 if (@"struct".backing_integer) |Int| {2245 if (@"struct".backing_integer) |Int| {
2230 ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*))));2246 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| {2247 } else {
2232 switch (@typeInfo(f_type)) {2248 if (@"struct".layout != .@"extern") {
2233 .@"struct" => byteSwapAllFieldsAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)),2249 @compileError("byteSwapAligned expects a packed or extern struct");
2234 .@"union", .array => byteSwapAllFieldsAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)),2250 }
2235 .@"enum" => {2251 inline for (@"struct".field_types, @"struct".field_names, @"struct".field_attrs) |f_type, f_name, f_attr| {
2236 @field(ptr, f_name) = @fromBackingInt(@intCast(@byteSwap(@backingInt(@field(ptr, f_name)))));2252 switch (@typeInfo(f_type)) {
2237 },2253 .@"struct" => byteSwapAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)),
2238 .bool => {},2254 .@"union", .array => byteSwapAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)),
2239 .float => |float| {2255 .@"enum" => {
2240 @field(ptr, f_name) = @bitCast(@byteSwap(@as(@Int(.unsigned, float.bits), @bitCast(@field(ptr, f_name)))));2256 @field(ptr, f_name) = @fromBackingInt(@byteSwap(@backingInt(@field(ptr, f_name))));
2241 },2257 },
2242 else => {2258 .bool => {},
2243 @field(ptr, f_name) = @byteSwap(@field(ptr, f_name));2259 .float => |float| {
2244 },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 }
2245 }2266 }
2246 }2267 }
2247 },2268 },
...@@ -2249,7 +2270,7 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a...@@ -2249,7 +2270,7 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a
2249 ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*))));2270 ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*))));
2250 } else {2271 } else {
2251 if (@"union".layout != .@"extern") {2272 if (@"union".layout != .@"extern") {
2252 @compileError("byteSwapAllFields expects a packed or extern union");2273 @compileError("byteSwapAligned expects a packed or extern union");
2253 }2274 }
22542275
2255 const first_size = @bitSizeOf(@"union".field_types[0]);2276 const first_size = @bitSizeOf(@"union".field_types[0]);
...@@ -2266,13 +2287,21 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a...@@ -2266,13 +2287,21 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a
2266 .array => |array| {2287 .array => |array| {
2267 byteSwapAllElements(array.child, ptr);2288 byteSwapAllElements(array.child, ptr);
2268 },2289 },
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 },
2269 else => {2298 else => {
2270 ptr.* = @byteSwap(ptr.*);2299 ptr.* = @byteSwap(ptr.*);
2271 },2300 },
2272 }2301 }
2273}2302}
22742303
2275test byteSwapAllFields {2304test byteSwap {
2276 const T = extern struct {2305 const T = extern struct {
2277 f0: u8,2306 f0: u8,
2278 f1: u16,2307 f1: u16,
...@@ -2304,6 +2333,9 @@ test byteSwapAllFields {...@@ -2304,6 +2333,9 @@ test byteSwapAllFields {
2304 } align(4),2333 } align(4),
2305 f2: u32,2334 f2: u32,
2306 };2335 };
2336 const E = enum(u32) {
2337 _,
2338 };
2307 var s = T{2339 var s = T{
2308 .f0 = 0x12,2340 .f0 = 0x12,
2309 .f1 = 0x1234,2341 .f1 = 0x1234,
...@@ -2327,10 +2359,14 @@ test byteSwapAllFields {...@@ -2327,10 +2359,14 @@ test byteSwapAllFields {
2327 .f1 = .{ .f0 = 0x123456789ABCDEF0 },2359 .f1 = .{ .f0 = 0x123456789ABCDEF0 },
2328 .f2 = 0x87654321,2360 .f2 = 0x87654321,
2329 };2361 };
2330 byteSwapAllFields(T, &s);2362 var e: E = @fromBackingInt(0x12345678);
2331 byteSwapAllFields(K, &k);2363 var f: f32 = @bitCast(@as(u32, 0x4640e400));
2332 byteSwapAllFields(P, &p);2364 byteSwap(T, &s);
2333 byteSwapAllFields(A, &a);2365 byteSwap(K, &k);
2366 byteSwap(P, &p);
2367 byteSwap(A, &a);
2368 byteSwap(E, &e);
2369 byteSwap(f32, &f);
2334 try std.testing.expectEqual(T{2370 try std.testing.expectEqual(T{
2335 .f0 = 0x12,2371 .f0 = 0x12,
2336 .f1 = 0x3412,2372 .f1 = 0x3412,
...@@ -2354,28 +2390,15 @@ test byteSwapAllFields {...@@ -2354,28 +2390,15 @@ test byteSwapAllFields {
2354 .f1 = .{ .f0 = 0xF0DEBC9A78563412 },2390 .f1 = .{ .f0 = 0xF0DEBC9A78563412 },
2355 .f2 = 0x21436587,2391 .f2 = 0x21436587,
2356 }, a);2392 }, a);
2393 try std.testing.expectEqual(@as(E, @fromBackingInt(0x78563412)), e);
2394 try std.testing.expectEqual(@as(f32, @bitCast(@as(u32, 0x00e44046))), f);
2357}2395}
23582396
2359/// Reverses the byte order of all elements in a slice.2397/// Reverses the byte order of all elements in a slice.
2360/// Handles structs, unions, arrays, enums, floats, and integers recursively.2398/// Handles structs, unions, arrays, enums, floats, and integers recursively.
2361/// Useful for converting between little-endian and big-endian representations.2399/// Useful for converting between little-endian and big-endian representations.
2362pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void {2400pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void {
2363 for (slice) |*elem| {2401 for (slice) |*elem| byteSwap(Elem, 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 }
2379}2402}
23802403
2381/// Returns an iterator that iterates over the slices of `buffer` that are not2404/// Returns an iterator that iterates over the slices of `buffer` that are not