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?