Padding struct to fixed size with usable bytes

You can use a function instead of a field to get the remaining padding.

const std = @import("std");

const Location = struct {
    _: void align(std.atomic.cache_line) = {},
    state: std.atomic.Value(u32),
    descriptor: Descriptor,

    const last_used_byte = blk: {
        var max: usize = 0;
        for (std.meta.fieldNames(Location)) |field_name| {
            const end = @offsetOf(Location, field_name) + @sizeOf(@FieldType(Location, field_name));
            max = @max(max, end);
        }
        break :blk max;
    };
    const trailing_bytes_size = @sizeOf(Location) - last_used_byte;

    pub fn trailingBytes(loc: *Location) *[trailing_bytes_size]u8 {
        const bytes: [*]u8 = @ptrCast(loc);
        return bytes[last_used_byte..@sizeOf(Location)];
    }
};

Hacky, unreliable version (but we’re trusting Andrew for placement of _: void align(std.atomic.cache_line) = {},, too (-: ) to get it as a field, thus hoping the auto layout doesn’t do something too surprising:

const Location2 = struct {
    _: void align(std.atomic.cache_line) = {},
    state: std.atomic.Value(u32),
    descriptor: Descriptor,
    bytes: [trailing_bytes_size]u8,

    const last_used_byte = blk: {
        const Layout = struct {
            state: std.atomic.Value(u32),
            descriptor: Descriptor,
        };

        var max: usize = 0;
        for (std.meta.fieldNames(Layout)) |field_name| {
            const end = @offsetOf(Layout, field_name) + @sizeOf(@FieldType(Layout, field_name));
            max = @max(max, end);
        }
        break :blk max;
    };
    const trailing_bytes_size = @sizeOf(Location) - last_used_byte;
};

But I’d stick with the first one.

2 Likes