2 // This test deserializes an enum in-place by transmuting to a union that
3 // should have the same layout, and manipulating the tag and payloads
4 // independently. This verifies that `repr(some_int)` has a stable representation,
5 // and that we don't miscompile these kinds of manipulations.
7 use std::time::Duration;
11 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
13 A(u32), // Single primitive value
14 B { x: u8, y: i16, z: u8 }, // Composite, and the offset of `y` and `z`
15 // depend on tag being internal
17 D(Option<u32>), // Contains an enum
18 E(Duration), // Contains a struct
21 #[allow(non_snake_case)]
31 #[repr(u8)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E }
32 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(MyEnumTag, u32);
33 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB { tag: MyEnumTag, x: u8, y: i16, z: u8 }
34 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantC(MyEnumTag);
35 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(MyEnumTag, Option<u32>);
36 #[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(MyEnumTag, Duration);
39 let result: Vec<Result<MyEnum, ()>> = vec![
41 Ok(MyEnum::B { x: 206, y: 1145, z: 78 }),
44 Ok(MyEnum::D(Some(407))),
46 Ok(MyEnum::E(Duration::from_secs(100))),
50 // Binary serialized version of the above (little-endian)
51 let input: Vec<u8> = vec![
55 8, /* invalid tag value */
58 4, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 0, /* incomplete value */
62 let mut output = vec![];
63 let mut buf = &input[..];
66 // This should be safe, because we don't match on it unless it's fully formed,
67 // and it doesn't have a destructor.
69 // MyEnum is repr(u8) so it is guaranteed to have a separate discriminant and each variant
70 // can be zero initialized.
71 let mut dest: MyEnum = mem::zeroed();
73 match parse_my_enum(&mut dest, &mut buf) {
74 Ok(()) => output.push(Ok(dest)),
75 Err(()) => output.push(Err(())),
80 assert_eq!(output, result);
83 fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> {
85 // Should be correct to do this transmute.
86 let dest: &'a mut MyEnumRepr = mem::transmute(dest);
87 let tag = read_u8(buf)?;
89 dest.A.0 = match tag {
100 dest.A.1 = read_u32_le(buf)?;
103 dest.B.x = read_u8(buf)?;
104 dest.B.y = read_u16_le(buf)? as i16;
105 dest.B.z = read_u8(buf)?;
111 let is_some = read_u8(buf)? == 0;
113 dest.D.1 = Some(read_u32_le(buf)?);
119 let secs = read_u64_le(buf)?;
120 let nanos = read_u32_le(buf)?;
121 dest.E.1 = Duration::new(secs, nanos);
132 fn read_u64_le(buf: &mut &[u8]) -> Result<u64, ()> {
133 if buf.len() < 8 { return Err(()) }
134 let val = (buf[0] as u64) << 0
135 | (buf[1] as u64) << 8
136 | (buf[2] as u64) << 16
137 | (buf[3] as u64) << 24
138 | (buf[4] as u64) << 32
139 | (buf[5] as u64) << 40
140 | (buf[6] as u64) << 48
141 | (buf[7] as u64) << 56;
146 fn read_u32_le(buf: &mut &[u8]) -> Result<u32, ()> {
147 if buf.len() < 4 { return Err(()) }
148 let val = (buf[0] as u32) << 0
149 | (buf[1] as u32) << 8
150 | (buf[2] as u32) << 16
151 | (buf[3] as u32) << 24;
156 fn read_u16_le(buf: &mut &[u8]) -> Result<u16, ()> {
157 if buf.len() < 2 { return Err(()) }
158 let val = (buf[0] as u16) << 0
159 | (buf[1] as u16) << 8;
164 fn read_u8(buf: &mut &[u8]) -> Result<u8, ()> {
165 if buf.len() < 1 { return Err(()) }