Skip to content

Commit 95a7f29

Browse files
committed
Add reftest that checks the layout of repr(int) on non-c-like enums
This verifies the layout specified in rfc #2195
1 parent 2f581cf commit 95a7f29

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// This test deserializes an enum in-place by transmuting to a union that
12+
// should have the same layout, and manipulating the tag and payloads
13+
// independently. This verifies that `repr(some_int)` has a stable representation,
14+
// and that we don't miscompile these kinds of manipulations.
15+
16+
use std::time::Duration;
17+
use std::mem;
18+
19+
#[repr(u8)]
20+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
21+
enum MyEnum {
22+
A(u32), // Single primitive value
23+
B { x: u8, y: i16 }, // Composite, and the offset of `y` depends on tag being internal
24+
C, // Empty
25+
D(Option<u32>), // Contains an enum
26+
E(Duration), // Contains a struct
27+
}
28+
29+
#[allow(non_snake_case)]
30+
#[repr(C)]
31+
union MyEnumRepr {
32+
A: MyEnumVariantA,
33+
B: MyEnumVariantB,
34+
C: MyEnumVariantC,
35+
D: MyEnumVariantD,
36+
E: MyEnumVariantE,
37+
}
38+
39+
#[repr(u8)] #[derive(Copy, Clone)] enum MyEnumTag { A, B, C, D, E }
40+
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantA(MyEnumTag, u32);
41+
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantB { tag: MyEnumTag, x: u8, y: i16 }
42+
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantC(MyEnumTag);
43+
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantD(MyEnumTag, Option<u32>);
44+
#[repr(C)] #[derive(Copy, Clone)] struct MyEnumVariantE(MyEnumTag, Duration);
45+
46+
fn main() {
47+
let result: Vec<Result<MyEnum, ()>> = vec![
48+
Ok(MyEnum::A(17)),
49+
Ok(MyEnum::B { x: 206, y: 1145 }),
50+
Ok(MyEnum::C),
51+
Err(()),
52+
Ok(MyEnum::D(Some(407))),
53+
Ok(MyEnum::D(None)),
54+
Ok(MyEnum::E(Duration::from_secs(100))),
55+
Err(()),
56+
];
57+
58+
// Binary serialized version of the above (little-endian)
59+
let input: Vec<u8> = vec![
60+
0, 17, 0, 0, 0,
61+
1, 206, 121, 4,
62+
2,
63+
8, // Invalid tag value
64+
3, 0, 151, 1, 0, 0,
65+
3, 1,
66+
4, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67+
0, // Incomplete value
68+
];
69+
70+
let mut output = vec![];
71+
let mut buf = &input[..];
72+
73+
unsafe {
74+
// This should be safe, because we don't match on it unless it's fully formed,
75+
// and it doesn't have a destructor.
76+
let mut dest: MyEnum = mem::uninitialized();
77+
while buf.len() > 0 {
78+
match parse_my_enum(&mut dest, &mut buf) {
79+
Ok(()) => output.push(Ok(dest)),
80+
Err(()) => output.push(Err(())),
81+
}
82+
}
83+
}
84+
85+
assert_eq!(output, result);
86+
}
87+
88+
fn parse_my_enum<'a>(dest: &'a mut MyEnum, buf: &mut &[u8]) -> Result<(), ()> {
89+
unsafe {
90+
// Should be correct to do this transmute.
91+
let dest: &'a mut MyEnumRepr = mem::transmute(dest);
92+
let tag = read_u8(buf)?;
93+
94+
dest.A.0 = match tag {
95+
0 => MyEnumTag::A,
96+
1 => MyEnumTag::B,
97+
2 => MyEnumTag::C,
98+
3 => MyEnumTag::D,
99+
4 => MyEnumTag::E,
100+
_ => return Err(()),
101+
};
102+
103+
match dest.B.tag {
104+
MyEnumTag::A => {
105+
dest.A.1 = read_u32_le(buf)?;
106+
}
107+
MyEnumTag::B => {
108+
dest.B.x = read_u8(buf)?;
109+
dest.B.y = read_u16_le(buf)? as i16;
110+
}
111+
MyEnumTag::C => {
112+
/* do nothing */
113+
}
114+
MyEnumTag::D => {
115+
let is_some = read_u8(buf)? == 0;
116+
if is_some {
117+
dest.D.1 = Some(read_u32_le(buf)?);
118+
} else {
119+
dest.D.1 = None;
120+
}
121+
}
122+
MyEnumTag::E => {
123+
let secs = read_u64_le(buf)?;
124+
let nanos = read_u32_le(buf)?;
125+
dest.E.1 = Duration::new(secs, nanos);
126+
}
127+
}
128+
Ok(())
129+
}
130+
}
131+
132+
133+
134+
// reader helpers
135+
136+
fn read_u64_le(buf: &mut &[u8]) -> Result<u64, ()> {
137+
if buf.len() < 8 { return Err(()) }
138+
let val = (buf[0] as u64) << 0
139+
| (buf[1] as u64) << 8
140+
| (buf[2] as u64) << 16
141+
| (buf[3] as u64) << 24
142+
| (buf[4] as u64) << 32
143+
| (buf[5] as u64) << 40
144+
| (buf[6] as u64) << 48
145+
| (buf[7] as u64) << 56;
146+
*buf = &buf[8..];
147+
Ok(val)
148+
}
149+
150+
fn read_u32_le(buf: &mut &[u8]) -> Result<u32, ()> {
151+
if buf.len() < 4 { return Err(()) }
152+
let val = (buf[0] as u32) << 0
153+
| (buf[1] as u32) << 8
154+
| (buf[2] as u32) << 16
155+
| (buf[3] as u32) << 24;
156+
*buf = &buf[4..];
157+
Ok(val)
158+
}
159+
160+
fn read_u16_le(buf: &mut &[u8]) -> Result<u16, ()> {
161+
if buf.len() < 2 { return Err(()) }
162+
let val = (buf[0] as u16) << 0
163+
| (buf[1] as u16) << 8;
164+
*buf = &buf[2..];
165+
Ok(val)
166+
}
167+
168+
fn read_u8(buf: &mut &[u8]) -> Result<u8, ()> {
169+
if buf.len() < 1 { return Err(()) }
170+
let val = buf[0];
171+
*buf = &buf[1..];
172+
Ok(val)
173+
}

0 commit comments

Comments
 (0)