ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.68k stars 2.47k forks source link

Document Effects of Endianness on Packed Structs #20647

Open kj4tmp opened 1 month ago

kj4tmp commented 1 month ago

Zig Version

0.14.0-dev.66+1fdf13a14

Steps to Reproduce and Observed Behavior

The docs say packed structs have defined in-memory layout but do not fully describe what that in-memory layout is, especially when considering host endianness, non-byte aligned, and not-byte-width fields.

Expected Behavior

I expected the docs to describe the in-memory layout of structs under the effects of host-endianness, non-byte aligned, and not-byte-width fields.

rohlem commented 1 month ago

Not sure how complete documentation is (I assume you mean langref). In status-quo, a packed struct is treated as a single integer that concatenates its fields' bits starting from the lowest-value bit. So packed struct {a: A, b: B} stores a: A in its lowest-value bits and b: B its highest-value bits. Endianness affects the mapping of lower-value-byte vs higher-value-byte to lower-address-byte vs higher-address byte. So differences will be observable in the addresses of the constituent bytes.

If the packed struct fits into one byte, there is no difference. If the packed struct has more than 8 bits, its backing bytes will be exactly flipped in address layout, while the backing integer value will still be the same (in the respective native endianness). (Note: Backing bytes may be ceiled, f.e. @sizeOf(u24) may be 4, meaning the u24 is represented as 4 bytes in memory.)

kj4tmp commented 1 month ago

probably also worth mentioning that padding bytes are added for allignment of packed structs, for example this assertion fails:

const EthernetHeader = packed struct(u112) {
        dest_mac: u48,
        src_mac: u48,
        ether_type: u16,

        comptime {
            std.debug.assert(@sizeOf(@This()) == 112 / 8);
        }
    };