1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
//@ 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<u32>), // 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<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, 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<u8> = 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<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)
}
|