Closed
Description
It turns out repr(u32, i8, etc...)
on non-C-like enums has had an unofficially specified and C(++)-compatible layout for a good while. I have filed an RFC to make it officially specified, but in the mean time we can set up cbindgen to generate this layout, which will let us use native Rust tagged unions in our bindings, and with today's stable Rust!
You can check out the RFC for details (it's not quite the obvious layout), but here's a test Rust program to prove that it works:
works_for_me.rs
use std::time::Duration;
use std::mem;
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum MyEnum {
A(u32),
B { x: u8, y: i16 },
C,
D(Option<u32>),
E(Duration),
}
#[allow(non_snake_case)]
#[repr(C)]
union MyEnumRepr {
A: MyEnumVariantA,
B: MyEnumVariantB,
C: MyEnumVariantC,
D: MyEnumVariantD,
E: MyEnumVariantE,
}
#[repr(u8)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(MyEnumTag, u32);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB { tag: MyEnumTag, x: u8, y: i16 }
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantC(MyEnumTag);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(MyEnumTag, Option<u32>);
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(MyEnumTag, Duration);
fn main() {
let result: Vec<Result<MyEnum, ()>> = vec![
Ok(MyEnum::A(17)),
Ok(MyEnum::B { x: 206, y: 1145 }),
Ok(MyEnum::C),
Err(()),
Ok(MyEnum::D(Some(407))),
Ok(MyEnum::D(None)),
Ok(MyEnum::E(Duration::from_secs(100))),
Err(()),
];
let input: Vec<u8> = vec![
0, 17, 0, 0, 0,
1, 206, 121, 4,
2,
8,
3, 0, 151, 1, 0, 0,
3, 1,
4, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
];
let mut output = vec![];
let mut buf = &input[..];
unsafe {
let mut dest: MyEnum = mem::uninitialized();
while buf.len() > 0 {
match parse_my_enum(&mut dest, &mut buf) {
Ok(()) => output.push(Ok(dest)),
Err(()) => output.push(Err(())),
}
}
}
assert_eq!(output, result);
}
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum MyEnum {
A(u32),
B { x: u8, y: i16 },
C,
D(Option<u32>),
E(Duration),
}
fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> {
unsafe {
let dest: &'a mut MyEnumRepr = mem::transmute(dest);
let tag = read_u8(buf)?;
dest.A.0 = match tag {
0 => MyEnumTag::A,
1 => MyEnumTag::B,
2 => MyEnumTag::C,
3 => MyEnumTag::D,
4 => MyEnumTag::E,
_ => return Err(()),
};
match dest.B.tag {
MyEnumTag::A => {
dest.A.1 = read_u32_le(buf)?;
}
MyEnumTag::B => {
dest.B.x = read_u8(buf)?;
dest.B.y = read_u16_le(buf)? as i16;
}
MyEnumTag::C => {
/* do nothing */
}
MyEnumTag::D => {
let is_some = read_u8(buf)? == 0;
if is_some {
dest.D.1 = Some(read_u32_le(buf)?);
} else {
dest.D.1 = None;
}
}
MyEnumTag::E => {
let secs = read_u64_le(buf)?;
let nanos = read_u32_le(buf)?;
dest.E.1 = Duration::new(secs, nanos);
}
}
Ok(())
}
}
fn read_u64_le(buf: &mut &[u8]) -> Result<u64, ()> {
if buf.len() < 8 { return Err(()) }
let val = (buf[0] as u64) << 0
| (buf[1] as u64) << 8
| (buf[2] as u64) << 16
| (buf[3] as u64) << 24
| (buf[4] as u64) << 32
| (buf[5] as u64) << 40
| (buf[6] as u64) << 48
| (buf[7] as u64) << 56;
*buf = &buf[8..];
Ok(val)
}
fn read_u32_le(buf: &mut &[u8]) -> Result<u32, ()> {
if buf.len() < 4 { return Err(()) }
let val = (buf[0] as u32) << 0
| (buf[1] as u32) << 8
| (buf[2] as u32) << 16
| (buf[3] as u32) << 24;
*buf = &buf[4..];
Ok(val)
}
fn read_u16_le(buf: &mut &[u8]) -> Result<u16, ()> {
if buf.len() < 2 { return Err(()) }
let val = (buf[0] as u16) << 0
| (buf[1] as u16) << 8;
*buf = &buf[2..];
Ok(val)
}
fn read_u8(buf: &mut &[u8]) -> Result<u8, ()> {
if buf.len() < 1 { return Err(()) }
let val = buf[0];
*buf = &buf[1..];
Ok(val)
}
Metadata
Metadata
Assignees
Labels
No labels