//@ run-pass // This test deserializes an enum in-place by transmuting to a union that // should have the same layout, and manipulating the tag and payloads // independently. This verifies that `repr(some_int)` has a stable representation, // and that we don't miscompile these kinds of manipulations. use std::time::Duration; use std::mem; #[repr(u8)] #[derive(Copy, Clone, Eq, PartialEq, Debug)] enum MyEnum { A(u32), // Single primitive value B { x: u8, y: i16, z: u8 }, // Composite, and the offset of `y` and `z` // depend on tag being internal C, // Empty D(Option), // Contains an enum E(Duration), // Contains a struct } #[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, z: u8 } #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantC(MyEnumTag); #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(MyEnumTag, Option); #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(MyEnumTag, Duration); fn main() { let result: Vec> = vec![ Ok(MyEnum::A(17)), Ok(MyEnum::B { x: 206, y: 1145, z: 78 }), Ok(MyEnum::C), Err(()), Ok(MyEnum::D(Some(407))), Ok(MyEnum::D(None)), Ok(MyEnum::E(Duration::from_secs(100))), Err(()), ]; // Binary serialized version of the above (little-endian) let input: Vec = vec![ 0, 17, 0, 0, 0, 1, 206, 121, 4, 78, 2, 8, /* invalid tag value */ 3, 0, 151, 1, 0, 0, 3, 1, 4, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* incomplete value */ ]; let mut output = vec![]; let mut buf = &input[..]; unsafe { // This should be safe, because we don't match on it unless it's fully formed, // and it doesn't have a destructor. // // MyEnum is repr(u8) so it is guaranteed to have a separate discriminant and each variant // can be zero initialized. let mut dest: MyEnum = mem::zeroed(); 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); } fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> { unsafe { // Should be correct to do this transmute. 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; dest.B.z = read_u8(buf)?; } 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(()) } } // reader helpers fn read_u64_le(buf: &mut &[u8]) -> Result { 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 { 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 { 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 { if buf.len() < 1 { return Err(()) } let val = buf[0]; *buf = &buf[1..]; Ok(val) }