next_gen_transmute/lib.rs
1//! Stable backport of the unstable
2//! [Next-Gen Transmute](https://github.com/rust-lang/rust/issues/155079) functions
3//! [`transmute_prefix`] and [`transmute_neo`] to Rust 1.56
4
5#![no_std]
6
7use core::mem::{size_of, ManuallyDrop};
8
9/// Like [`transmute`](`core::mem::transmute`), but only initializes the "common prefix" of the
10/// first `min(size_of::<Src>(), size_of::<Dst>())` bytes of the destination from the
11/// corresponding bytes of the source.
12///
13/// This is equivalent to a "union cast" through a `union` with `#[repr(C)]`.
14///
15/// That means some size mismatches are not UB, like `[T; 2]` to `[T; 1]`.
16/// Increasing size is usually UB from being insufficiently initialized -- like
17/// `u8` to `u32` -- but isn't always. For example, going from `u8` to
18/// `#[repr(C, align(4))] AlignedU8(u8);` is sound.
19///
20/// Prefer normal `transmute` where possible, for the extra checking, since
21/// both do exactly the same thing at runtime, if they both compile.
22///
23/// # Safety
24///
25/// If `size_of::<Src>() >= size_of::<Dst>()`, the first `size_of::<Dst>()` bytes
26/// of `src` must be be *valid* when interpreted as a `Dst`. (In this case, the
27/// preconditions are the same as for `transmute_copy(&ManuallyDrop::new(src))`.)
28///
29/// If `size_of::<Src>() <= size_of::<Dst>()`, the bytes of `src` padded with
30/// uninitialized bytes afterwards up to a total size of `size_of::<Dst>()`
31/// must be *valid* when interpreted as a `Dst`.
32///
33/// In both cases, any safety preconditions of the `Dst` type must also be upheld.
34///
35/// # Examples
36///
37/// ```
38/// use next_gen_transmute::transmute_prefix;
39///
40/// assert_eq!(unsafe { transmute_prefix::<[i32; 4], [i32; 2]>([1, 2, 3, 4]) }, [1, 2]);
41///
42/// let expected = if cfg!(target_endian = "little") { 0x34 } else { 0x12 };
43/// assert_eq!(unsafe { transmute_prefix::<u16, u8>(0x1234) }, expected);
44///
45/// // Would be UB because the destination is incompletely initialized.
46/// // transmute_prefix::<u8, u16>(123)
47///
48/// // OK because the destination is allowed to be partially initialized.
49/// let _: std::mem::MaybeUninit<u16> = unsafe { transmute_prefix(123_u8) };
50/// ```
51pub const unsafe fn transmute_prefix<Src, Dst>(src: Src) -> Dst {
52 #[repr(C)]
53 union Transmute<A, B> {
54 a: ManuallyDrop<A>,
55 b: ManuallyDrop<B>,
56 }
57
58 // SAFETY: any safety invariants need to be upheld by the caller.
59 #[allow(unused_unsafe)]
60 unsafe {
61 ManuallyDrop::into_inner(
62 Transmute {
63 a: ManuallyDrop::new(src),
64 }
65 .b,
66 )
67 }
68}
69
70/// New version of `transmute`, exposed under this name so it can be iterated upon
71/// without risking breakage to uses of "real" transmute.
72///
73/// It will not be stabilized under this name.
74///
75/// # Examples
76///
77/// ```
78/// use next_gen_transmute::transmute_neo;
79///
80/// assert_eq!(unsafe { transmute_neo::<f32, u32>(0.0) }, 0);
81/// ```
82///
83/// ```compile_fail,E0080
84/// use next_gen_transmute::transmute_neo;
85///
86/// unsafe { transmute_neo::<u32, u16>(123) };
87/// ```
88pub const unsafe fn transmute_neo<Src, Dst>(src: Src) -> Dst {
89 struct AssertSameSize<A, B>(A, B);
90 impl<A, B> AssertSameSize<A, B> {
91 const ASSERT_SAME_SIZE: () = [()][!(size_of::<A>() == size_of::<B>()) as usize];
92 }
93 let () = AssertSameSize::<Src, Dst>::ASSERT_SAME_SIZE;
94
95 // SAFETY: the const-assert just checked that they're the same size,
96 // and any other safety invariants need to be upheld by the caller.
97 #[allow(unused_unsafe)]
98 unsafe {
99 transmute_prefix(src)
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use core::mem::MaybeUninit;
107
108 const U32_4: [u32; 4] = [0x23456789, 0x456789AB, 0x6789ABCD, 0x89ABCDEF];
109 const U32_2: [u32; 2] = [U32_4[0], U32_4[1]];
110 const U32: u32 = U32_4[0];
111 const I32_4: [i32; 4] = [U32_4[0] as _, U32_4[1] as _, U32_4[2] as _, U32_4[3] as _];
112 const U8_4_2: [[u8; 4]; 2] = [U32_2[0].to_ne_bytes(), U32_2[1].to_ne_bytes()];
113 const U8_8: [u8; 8] = [
114 U8_4_2[0][0],
115 U8_4_2[0][1],
116 U8_4_2[0][2],
117 U8_4_2[0][3],
118 U8_4_2[1][0],
119 U8_4_2[1][1],
120 U8_4_2[1][2],
121 U8_4_2[1][3],
122 ];
123
124 #[derive(Debug, PartialEq)]
125 #[repr(C, align(8))]
126 struct Align8U32(u32);
127
128 #[test]
129 fn bigger_to_smaller() {
130 assert!(U32_4.len() >= U32_2.len());
131 // SAFETY: every `[u32; M]` is a valid prefix of `[u32; N]` where `N >= M`.
132 let x: [u32; U32_2.len()] = unsafe { transmute_prefix(U32_4) };
133 assert_eq!(x, U32_2);
134 }
135
136 #[test]
137 fn smaller_to_bigger() {
138 // SAFETY: the destination does not have to be fully initialized.
139 let mut x: [MaybeUninit<u32>; U32_4.len()] = unsafe { transmute_prefix(U32_2) };
140 for i in U32_2.len()..U32_4.len() {
141 x[i].write(U32_4[i]);
142 }
143 // SAFETY: the source is now fully initialized.
144 let y: [u32; U32_4.len()] = unsafe { transmute_neo(x) };
145 assert_eq!(y, U32_4);
146 }
147
148 #[test]
149 fn array_to_first_element() {
150 assert!(U32_4.len() >= 1);
151 // SAFETY: every `u32` is a valid prefix of `[u32; N]` where `N >= 1`.
152 let x: u32 = unsafe { transmute_prefix(U32_4) };
153 assert_eq!(x, U32);
154 }
155
156 #[test]
157 fn first_element_to_array() {
158 // SAFETY: the destination does not have to be fully initialized.
159 let mut x: [MaybeUninit<u32>; U32_4.len()] = unsafe { transmute_prefix(U32) };
160 for i in 1..U32_4.len() {
161 x[i].write(U32_4[i]);
162 }
163 // SAFETY: the source is now fully initialized.
164 let y: [u32; U32_4.len()] = unsafe { transmute_neo(x) };
165 assert_eq!(y, U32_4);
166 }
167
168 #[test]
169 fn decrease_alignment() {
170 // SAFETY: every `u32` is a valid prefix of `Align8U32`.
171 let x: u32 = unsafe { transmute_prefix(Align8U32(U32)) };
172 assert_eq!(x, U32);
173 }
174
175 #[test]
176 fn increase_alignment() {
177 // SAFETY: every `u32` is a valid prefix of `Align8U32`,
178 // and the rest does not have to be initialized.
179 let x: Align8U32 = unsafe { transmute_prefix(U32) };
180 assert_eq!(x, Align8U32(U32));
181 }
182
183 #[test]
184 fn cast_signed() {
185 // SAFETY: every `[u32; 4]` is a valid `[i32; 4]`.
186 let x: [i32; U32_4.len()] = unsafe { transmute_neo(U32_4) };
187 assert_eq!(x, I32_4);
188 }
189
190 #[test]
191 fn cast_unsigned() {
192 // SAFETY: every `[i32; 4]` is a valid `[u32; 4]`.
193 let x: [u32; I32_4.len()] = unsafe { transmute_neo(I32_4) };
194 assert_eq!(x, U32_4);
195 }
196
197 #[test]
198 fn from_ne_bytes() {
199 // SAFETY: every `[u8; 4]` is a valid `u32`.
200 let x: [u32; U8_8.len() / 4] = unsafe { transmute_neo(U8_8) };
201 assert_eq!(x, U32_2);
202 }
203
204 #[test]
205 fn to_ne_bytes() {
206 // SAFETY: every `u32` is a valid `[u8; 4]`.
207 let y: [u8; U32_2.len() * 4] = unsafe { transmute_neo(U32_2) };
208 assert_eq!(y, U8_8);
209 }
210}