my final implementation, which supports packed structs, with tests
// TODO: check recursively for struct fields that are enums?
pub fn isECatPackable(comptime T: type) bool {
if (@bitSizeOf(T) % 8 != 0) return false;
return switch (@typeInfo(T)) {
.@"struct" => |_struct| blk: {
// must be a packed struct
break :blk (_struct.layout == .@"packed");
},
.int, .float => true,
.@"union" => |_union| blk: {
// must be a packed union
break :blk (_union.layout == .@"packed");
},
.@"enum" => |_enum| blk: {
// TODO: check enum cannot contain invalid values after bitcast
// i.e. must be non-exhaustive or represent all values of
// backing integer.
break :blk !_enum.is_exhaustive;
},
else => false,
};
}
/// Convert little endian packed bytes from EtherCAT to host representation.
///
/// Supports enums, packed structs, and most primitive types. All must have
/// bitlength divisible by 8.
pub fn packFromECat(comptime T: type, ecat_bytes: *const [@divExact(@bitSizeOf(T), 8)]u8) T {
comptime assert(isECatPackable(T));
return switch (@typeInfo(T)) {
.@"enum" => |info| @enumFromInt(std.mem.readInt(
info.tag_type,
ecat_bytes,
.little,
)),
else => @bitCast(std.mem.readInt(
@Int(.unsigned, @bitSizeOf(T)),
ecat_bytes,
.little,
)),
};
}
test packFromECat {
const Command = packed struct(u8) {
flag: bool = true,
reserved: u7 = 0,
};
try std.testing.expectEqual(
Command{},
packFromECat(Command, &[_]u8{1}),
);
const Command2 = packed struct(u16) {
flag: bool = true,
reserved: u7 = 0,
num: u8 = 7,
};
try std.testing.expectEqual(
Command2{},
packFromECat(Command2, &[_]u8{ 1, 7 }),
);
const Command3 = packed struct(u24) {
flag: bool = true,
reserved: u7 = 0,
num: u16 = 0x1122,
};
try std.testing.expectEqual(
Command3{},
packFromECat(Command3, &[_]u8{ 1, 0x22, 0x11 }),
);
const Command4 = packed struct(u32) {
flag: bool = true,
reserved: u7 = 0,
num: u16 = 0x1122,
num2: u5 = 0x03,
num3: u3 = 0,
};
try std.testing.expectEqual(
Command4{},
packFromECat(Command4, &[_]u8{ 1, 0x22, 0x11, 0x03 }),
);
const Command5 = packed struct(u40) {
flag: bool = true,
reserved: u7 = 0,
num: u16 = 0x1122,
num2: u5 = 0x03,
num3: u3 = 0,
num4: u8 = 0xAB,
};
try std.testing.expectEqual(
Command5{},
packFromECat(Command5, &[_]u8{ 1, 0x22, 0x11, 0x03, 0xAB }),
);
const EnumUnsigned = enum(u24) {
one = 1,
_,
};
try std.testing.expectEqual(
EnumUnsigned.one,
packFromECat(EnumUnsigned, &[_]u8{ 1, 0, 0 }),
);
const EnumSigned = enum(i8) {
minus_one = -1,
_,
};
try std.testing.expectEqual(
EnumSigned.minus_one,
packFromECat(EnumSigned, &[_]u8{0xFF}),
);
}