hecatia-elegua / bilge

Use bitsized types as if they were a feature of rust.
Apache License 2.0
171 stars 17 forks source link

Tagged Union/Associated Data in enum support #80

Open stevefan1999-personal opened 1 year ago

stevefan1999-personal commented 1 year ago

This is like a normal Rust enum except we have a known limited choice, and this means we have to add a discriminator field and the subsequent bits. This is very useful to represent bit fields inside a C union.

Final size of the enum is (discriminator bits + max(content of each enum)). Examples coming soon.

#[tagged(discriminator_bits = 2)]
enum Entity {
  Human { foo: u1 } = 0b00,
  Dog { foo: u2 } = 0b10,
  Cat { bar: u4 } = 0b01,
  Squirrel { foo_bar: u6 } = 0b11,
}

This means we have a u8 field because we have a u2 header/discriminator + max(all enum trailing content) = u6 so u8. This is indicated by the dominant size of Squirrel. All others will have their unused bits zero-extended.

pickx commented 1 year ago

thanks for the proposal. let's clarify this a little. I've summarized what I understand from your proposal (please feel free to correct me) and added additional questions in parenthesis.

stevefan1999-personal commented 1 year ago

thanks for the proposal. let's clarify this a little. I've summarized what I understand from your proposal (please feel free to correct me) and added additional questions in parenthesis.

  • the enum has only "named fields" variants (do you want to additionally support tuple variants?), but not unit variants
  • all of the variants' fields are arbitrary_ints (or would you like to support any Bitsized type?)
  • variants have only one field (or would you like to allow Hamster { foo: u1, bar: u4, baz: u1 }?)
  • the enum is not annotated with #[bitsize(n)]. instead it is annotated with #[tagged(discriminator_bits = n)]
  • the enum is filled (I assume fallback would not be allowed?)
  • the discriminator is the MSB of the bitfield (or is it in the LSB position?)
  • let k = n + max_bitsize_of_any_variant. the generated bitfield for a variant would be a concat of: the discriminator, the field(s) of the variant, then zero-padding to length k

I just think of it like this:

| discriminator bits | largest variant bits |

So using the example above:

| 2 bits | largest variant = Squirrel = 6 bits |

This is nice because the current Rust enum implementation won't be able to optimize the discriminator size (it almost always takes one byte at the least), so we can pack the whole thing into one byte rather than having align_up(8 + 6) = 16 bits = 2 bytes. One byte savings could be significant!

stevefan1999-personal commented 1 year ago

https://alic.dev/blog/dense-enums

Just saw this on HN today. Could be useful.