A puzzle from TigerBeetle that I don’t quite know how to solve.
We have a setup roughly like this:
const Command = enum(u8) {
reserved = 0,
...
request_blocks = 19,
// Note: no `_` here.
}
const MessageHeader = extern struct {
checksum: u128,
command: Command,
// A bunch of other stuff
}
When we receive a message over the network, we verify the checksum, then cast raw bytes to MessageHeader, and verify semantic validity of each field. This works for almost all fields, which are integers or enums with _ (so any bitpattern goes).
The only problem is this command: Command exhaustive enum, as, if we cast it first and then validate, we might create a value with illegal bitpatern, and that would be IB (illegal behavior).
One way I can handle this is by making the enum non-exhaustive, but that means that all the code would have to deal with non-exhaustiveness, which feels wrong — it should be enough to check this once at the edge of the system.
Another approach I can think about is to comptime-compute the offset of the field, and poke the memory directly, while it is still []const u8, to verify that the byte is OK. This feels somewhat indirect to me though, and error prone.
Yet another approach is to write a type-level function which would take my MessageHeader type and return a different type with exhaustive enums replaced with their reprs (MessageHeaderRaw). I can then cast bytes to that MessageHeaderRaw (where every bitpattern is valid), check that enums are all-right, and then cast Raw into non-Raw. Conceptually, it feels like the right way to do it, but it needs a fair amount of comptime machinery (a whole new function to clone the struct basically).
Is there some simpler, more direct way to do what I want here?