flexible.Struct
Hello. I’m hoping for some feedback on this alternative to the FlexibleStruct/FlexibleArray pull request. Its similar but adds lots of stuff like a user Layout API, size and offset helpers and additions to the Self API (value(), ptr(), slice(), sliceField(), sliceLen()).
I haven’t benchmarked it yet
but its designed to be faster than the orignal with flexible fields after Layout fields and sorted by alignment. WIth this, we can simd prefix sum the aligned sizes in constant time. It does trade some possible padding after Layout for this so beware.
I’ve been using it in my toy roaring bitmap project and it seems pretty decent so far. Lots of things I want to do with including moving the repo to Codeberg. So give it a try. We can make small and fast designs with flexible arrays.
I’m hoping any criticism including how to improve this. And on the decision to add computed methods (which may be slower than fixed field access) and the additional padding tradeoff. I’m considering turning this into an alternative to the zig FlexibleStruct PR above since it sounds like Andrew is interested in it.
@tristanpemble I’m interested to hear from original author and maybe work together toward getting something merged. Let me know if you have a minute. I’ve also commented on that issue as archaistvolts. Thanks for the original! I love the idea.
flexible.Struct
A single buffer layout library for structs with flexible fields.
About
User defined Layout fields are first in memory with order determined by Zig. Flexible fields are second and sorted by descending alignment. With no padding, flexible offsets can be calculated in constant time (see calcOffsets()).
Features
- Support all structs: auto, extern, packed
- Constant time field access.
- APIs: Layout, Self and bytes. Users can ptrCast between them.
- Computed fields.
- LSP cooperation. Avoid created/refified types which break autocomplete.
Docs
Usage
Add a pub const flexible_array_capacities decl to your Layout which maps flexible array fields to their capacities.
const Layout = struct {
capacity: u8,
flexible: [*]u8 = undefined,
computed: [*]u8 = undefined,
pub const flexible_array_capacities = .{
.flexible = .capacity,
.computed = .computedLen,
};
pub fn computedLen(_: *const @This()) usize {
return 68;
}
};
const Model = Struct(Layout, .{});
const initlayout = Layout{ .capacity = 42 };
var buf: Model.Buf(&initlayout) align(Model.ALIGN) = undefined; // buffer must be aligned
const model = try Model.initBuffer(&buf, &initlayout);
try testing.expectEqual(42, model.value(.capacity));
try testing.expectEqual(42, model.slice(.flexible).len);
try testing.expectEqual(42, model.asLayout().capacity); // Self <-> Layout
try testing.expectEqual(42, Model.fromLayout(model.asLayout()).value(.capacity));
try testing.expectEqual(68, model.slice(.computed).len);
try testing.expectEqual(136, model.sizeInBytes()); // 24+42+68(~8)=134(~8)=136
}
Supported Zig versions
0.16. 0.17 coming soon.