1 #![no_std]
2 #![warn(missing_docs)]
3 
4 //! This crate gives small utilities for casting between plain data types.
5 //!
6 //! ## Basics
7 //!
8 //! Data comes in five basic forms in Rust, so we have five basic casting
9 //! functions:
10 //!
11 //! * `T` uses [`cast`]
12 //! * `&T` uses [`cast_ref`]
13 //! * `&mut T` uses [`cast_mut`]
14 //! * `&[T]` uses [`cast_slice`]
15 //! * `&mut [T]` uses [`cast_slice_mut`]
16 //!
17 //! Some casts will never fail (eg: `cast::<u32, f32>` always works), other
18 //! casts might fail (eg: `cast_ref::<[u8; 4], u32>` will fail if the reference
19 //! isn't already aligned to 4). Each casting function has a "try" version which
20 //! will return a `Result`, and the "normal" version which will simply panic on
21 //! invalid input.
22 //!
23 //! ## Using Your Own Types
24 //!
25 //! All the functions here are guarded by the [`Pod`] trait, which is a
26 //! sub-trait of the [`Zeroable`] trait.
27 //!
28 //! If you're very sure that your type is eligible, you can implement those
29 //! traits for your type and then they'll have full casting support. However,
30 //! these traits are `unsafe`, and you should carefully read the requirements
31 //! before adding the them to your own types.
32 //!
33 //! ## Features
34 //!
35 //! * This crate is core only by default, but if you're using Rust 1.36 or later
36 //!   you can enable the `extern_crate_alloc` cargo feature for some additional
37 //!   methods related to `Box` and `Vec`. Note that the `docs.rs` documentation
38 //!   is always built with `extern_crate_alloc` cargo feature enabled.
39 
40 #[cfg(target_arch = "x86")]
41 use core::arch::x86;
42 #[cfg(target_arch = "x86_64")]
43 use core::arch::x86_64;
44 //
45 use core::{marker::*, mem::*, num::*, ptr::*};
46 
47 // Used from macros to ensure we aren't using some locally defined name and
48 // actually are referencing libcore. This also would allow pre-2018 edition
49 // crates to use our macros, but I'm not sure how important that is.
50 #[doc(hidden)]
51 pub use ::core as __core;
52 
53 macro_rules! impl_unsafe_marker_for_array {
54   ( $marker:ident , $( $n:expr ),* ) => {
55     $(unsafe impl<T> $marker for [T; $n] where T: $marker {})*
56   }
57 }
58 
59 #[cfg(feature = "extern_crate_alloc")]
60 extern crate alloc;
61 #[cfg(feature = "extern_crate_alloc")]
62 pub mod allocation;
63 #[cfg(feature = "extern_crate_alloc")]
64 pub use allocation::*;
65 
66 mod zeroable;
67 pub use zeroable::*;
68 
69 mod pod;
70 pub use pod::*;
71 
72 mod contiguous;
73 pub use contiguous::*;
74 
75 mod offset_of;
76 pub use offset_of::*;
77 
78 mod transparent;
79 pub use transparent::*;
80 
81 /*
82 
83 Note(Lokathor): We've switched all of the `unwrap` to `match` because there is
84 apparently a bug: https://github.com/rust-lang/rust/issues/68667
85 and it doesn't seem to show up in simple godbolt examples but has been reported
86 as having an impact when there's a cast mixed in with other more complicated
87 code around it. Rustc/LLVM ends up missing that the `Err` can't ever happen for
88 particular type combinations, and then it doesn't fully eliminated the panic
89 possibility code branch.
90 
91 */
92 
93 /// Immediately panics.
94 #[cold]
95 #[inline(never)]
something_went_wrong(src: &str, err: PodCastError) -> !96 fn something_went_wrong(src: &str, err: PodCastError) -> ! {
97   // Note(Lokathor): Keeping the panic here makes the panic _formatting_ go
98   // here too, which helps assembly readability and also helps keep down
99   // the inline pressure.
100   panic!("{src}>{err:?}", src = src, err = err)
101 }
102 
103 /// Re-interprets `&T` as `&[u8]`.
104 ///
105 /// Any ZST becomes an empty slice, and in that case the pointer value of that
106 /// empty slice might not match the pointer value of the input reference.
107 #[inline]
bytes_of<T: Pod>(t: &T) -> &[u8]108 pub fn bytes_of<T: Pod>(t: &T) -> &[u8] {
109   match try_cast_slice::<T, u8>(core::slice::from_ref(t)) {
110     Ok(s) => s,
111     Err(_) => unreachable!(),
112   }
113 }
114 
115 /// Re-interprets `&mut T` as `&mut [u8]`.
116 ///
117 /// Any ZST becomes an empty slice, and in that case the pointer value of that
118 /// empty slice might not match the pointer value of the input reference.
119 #[inline]
bytes_of_mut<T: Pod>(t: &mut T) -> &mut [u8]120 pub fn bytes_of_mut<T: Pod>(t: &mut T) -> &mut [u8] {
121   match try_cast_slice_mut::<T, u8>(core::slice::from_mut(t)) {
122     Ok(s) => s,
123     Err(_) => unreachable!(),
124   }
125 }
126 
127 /// Re-interprets `&[u8]` as `&T`.
128 ///
129 /// ## Panics
130 ///
131 /// This is [`try_from_bytes`] but will panic on error.
132 #[inline]
from_bytes<T: Pod>(s: &[u8]) -> &T133 pub fn from_bytes<T: Pod>(s: &[u8]) -> &T {
134   match try_from_bytes(s) {
135     Ok(t) => t,
136     Err(e) => something_went_wrong("from_bytes", e),
137   }
138 }
139 
140 /// Re-interprets `&mut [u8]` as `&mut T`.
141 ///
142 /// ## Panics
143 ///
144 /// This is [`try_from_bytes_mut`] but will panic on error.
145 #[inline]
from_bytes_mut<T: Pod>(s: &mut [u8]) -> &mut T146 pub fn from_bytes_mut<T: Pod>(s: &mut [u8]) -> &mut T {
147   match try_from_bytes_mut(s) {
148     Ok(t) => t,
149     Err(e) => something_went_wrong("from_bytes_mut", e),
150   }
151 }
152 
153 /// Re-interprets `&[u8]` as `&T`.
154 ///
155 /// ## Failure
156 ///
157 /// * If the slice isn't aligned for the new type
158 /// * If the slice's length isn’t exactly the size of the new type
159 #[inline]
try_from_bytes<T: Pod>(s: &[u8]) -> Result<&T, PodCastError>160 pub fn try_from_bytes<T: Pod>(s: &[u8]) -> Result<&T, PodCastError> {
161   if s.len() != size_of::<T>() {
162     Err(PodCastError::SizeMismatch)
163   } else if (s.as_ptr() as usize) % align_of::<T>() != 0 {
164     Err(PodCastError::AlignmentMismatch)
165   } else {
166     Ok(unsafe { &*(s.as_ptr() as *const T) })
167   }
168 }
169 
170 /// Re-interprets `&mut [u8]` as `&mut T`.
171 ///
172 /// ## Failure
173 ///
174 /// * If the slice isn't aligned for the new type
175 /// * If the slice's length isn’t exactly the size of the new type
176 #[inline]
try_from_bytes_mut<T: Pod>( s: &mut [u8], ) -> Result<&mut T, PodCastError>177 pub fn try_from_bytes_mut<T: Pod>(
178   s: &mut [u8],
179 ) -> Result<&mut T, PodCastError> {
180   if s.len() != size_of::<T>() {
181     Err(PodCastError::SizeMismatch)
182   } else if (s.as_ptr() as usize) % align_of::<T>() != 0 {
183     Err(PodCastError::AlignmentMismatch)
184   } else {
185     Ok(unsafe { &mut *(s.as_mut_ptr() as *mut T) })
186   }
187 }
188 
189 /// The things that can go wrong when casting between [`Pod`] data forms.
190 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
191 pub enum PodCastError {
192   /// You tried to cast a slice to an element type with a higher alignment
193   /// requirement but the slice wasn't aligned.
194   TargetAlignmentGreaterAndInputNotAligned,
195   /// If the element size changes then the output slice changes length
196   /// accordingly. If the output slice wouldn't be a whole number of elements
197   /// then the conversion fails.
198   OutputSliceWouldHaveSlop,
199   /// When casting a slice you can't convert between ZST elements and non-ZST
200   /// elements. When casting an individual `T`, `&T`, or `&mut T` value the
201   /// source size and destination size must be an exact match.
202   SizeMismatch,
203   /// For this type of cast the alignments must be exactly the same and they
204   /// were not so now you're sad.
205   AlignmentMismatch,
206 }
207 
208 /// Cast `T` into `U`
209 ///
210 /// ## Panics
211 ///
212 /// This is [`try_cast`] but will panic on error.
213 #[inline]
cast<A: Pod, B: Pod>(a: A) -> B214 pub fn cast<A: Pod, B: Pod>(a: A) -> B {
215   if size_of::<A>() == size_of::<B>() {
216     // Plz mr compiler, just notice that we can't ever hit Err in this case.
217     match try_cast(a) {
218       Ok(b) => b,
219       Err(_) => unreachable!(),
220     }
221   } else {
222     match try_cast(a) {
223       Ok(b) => b,
224       Err(e) => something_went_wrong("cast", e),
225     }
226   }
227 }
228 
229 /// Cast `&mut T` into `&mut U`.
230 ///
231 /// ## Panics
232 ///
233 /// This is [`try_cast_mut`] but will panic on error.
234 #[inline]
cast_mut<A: Pod, B: Pod>(a: &mut A) -> &mut B235 pub fn cast_mut<A: Pod, B: Pod>(a: &mut A) -> &mut B {
236   if size_of::<A>() == size_of::<B>() && align_of::<A>() >= align_of::<B>() {
237     // Plz mr compiler, just notice that we can't ever hit Err in this case.
238     match try_cast_mut(a) {
239       Ok(b) => b,
240       Err(_) => unreachable!(),
241     }
242   } else {
243     match try_cast_mut(a) {
244       Ok(b) => b,
245       Err(e) => something_went_wrong("cast_mut", e),
246     }
247   }
248 }
249 
250 /// Cast `&T` into `&U`.
251 ///
252 /// ## Panics
253 ///
254 /// This is [`try_cast_ref`] but will panic on error.
255 #[inline]
cast_ref<A: Pod, B: Pod>(a: &A) -> &B256 pub fn cast_ref<A: Pod, B: Pod>(a: &A) -> &B {
257   if size_of::<A>() == size_of::<B>() && align_of::<A>() >= align_of::<B>() {
258     // Plz mr compiler, just notice that we can't ever hit Err in this case.
259     match try_cast_ref(a) {
260       Ok(b) => b,
261       Err(_) => unreachable!(),
262     }
263   } else {
264     match try_cast_ref(a) {
265       Ok(b) => b,
266       Err(e) => something_went_wrong("cast_ref", e),
267     }
268   }
269 }
270 
271 /// Cast `&[T]` into `&[U]`.
272 ///
273 /// ## Panics
274 ///
275 /// This is [`try_cast_slice`] but will panic on error.
276 #[inline]
cast_slice<A: Pod, B: Pod>(a: &[A]) -> &[B]277 pub fn cast_slice<A: Pod, B: Pod>(a: &[A]) -> &[B] {
278   match try_cast_slice(a) {
279     Ok(b) => b,
280     Err(e) => something_went_wrong("cast_slice", e),
281   }
282 }
283 
284 /// Cast `&mut [T]` into `&mut [U]`.
285 ///
286 /// ## Panics
287 ///
288 /// This is [`try_cast_slice_mut`] but will panic on error.
289 #[inline]
cast_slice_mut<A: Pod, B: Pod>(a: &mut [A]) -> &mut [B]290 pub fn cast_slice_mut<A: Pod, B: Pod>(a: &mut [A]) -> &mut [B] {
291   match try_cast_slice_mut(a) {
292     Ok(b) => b,
293     Err(e) => something_went_wrong("cast_slice_mut", e),
294   }
295 }
296 
297 /// As `align_to`, but safe because of the [`Pod`] bound.
298 #[inline]
pod_align_to<T: Pod, U: Pod>(vals: &[T]) -> (&[T], &[U], &[T])299 pub fn pod_align_to<T: Pod, U: Pod>(vals: &[T]) -> (&[T], &[U], &[T]) {
300   unsafe { vals.align_to::<U>() }
301 }
302 
303 /// As `align_to_mut`, but safe because of the [`Pod`] bound.
304 #[inline]
pod_align_to_mut<T: Pod, U: Pod>( vals: &mut [T], ) -> (&mut [T], &mut [U], &mut [T])305 pub fn pod_align_to_mut<T: Pod, U: Pod>(
306   vals: &mut [T],
307 ) -> (&mut [T], &mut [U], &mut [T]) {
308   unsafe { vals.align_to_mut::<U>() }
309 }
310 
311 /// Try to cast `T` into `U`.
312 ///
313 /// ## Failure
314 ///
315 /// * If the types don't have the same size this fails.
316 #[inline]
try_cast<A: Pod, B: Pod>(a: A) -> Result<B, PodCastError>317 pub fn try_cast<A: Pod, B: Pod>(a: A) -> Result<B, PodCastError> {
318   if size_of::<A>() == size_of::<B>() {
319     let mut b = B::zeroed();
320     // Note(Lokathor): We copy in terms of `u8` because that allows us to bypass
321     // any potential alignment difficulties.
322     let ap = &a as *const A as *const u8;
323     let bp = &mut b as *mut B as *mut u8;
324     unsafe { ap.copy_to_nonoverlapping(bp, size_of::<A>()) };
325     Ok(b)
326   } else {
327     Err(PodCastError::SizeMismatch)
328   }
329 }
330 
331 /// Try to convert a `&T` into `&U`.
332 ///
333 /// ## Failure
334 ///
335 /// * If the reference isn't aligned in the new type
336 /// * If the source type and target type aren't the same size.
337 #[inline]
try_cast_ref<A: Pod, B: Pod>(a: &A) -> Result<&B, PodCastError>338 pub fn try_cast_ref<A: Pod, B: Pod>(a: &A) -> Result<&B, PodCastError> {
339   // Note(Lokathor): everything with `align_of` and `size_of` will optimize away
340   // after monomorphization.
341   if align_of::<B>() > align_of::<A>()
342     && (a as *const A as usize) % align_of::<B>() != 0
343   {
344     Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
345   } else if size_of::<B>() == size_of::<A>() {
346     Ok(unsafe { &*(a as *const A as *const B) })
347   } else {
348     Err(PodCastError::SizeMismatch)
349   }
350 }
351 
352 /// Try to convert a `&mut T` into `&mut U`.
353 ///
354 /// As [`try_cast_ref`], but `mut`.
355 #[inline]
try_cast_mut<A: Pod, B: Pod>(a: &mut A) -> Result<&mut B, PodCastError>356 pub fn try_cast_mut<A: Pod, B: Pod>(a: &mut A) -> Result<&mut B, PodCastError> {
357   // Note(Lokathor): everything with `align_of` and `size_of` will optimize away
358   // after monomorphization.
359   if align_of::<B>() > align_of::<A>()
360     && (a as *mut A as usize) % align_of::<B>() != 0
361   {
362     Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
363   } else if size_of::<B>() == size_of::<A>() {
364     Ok(unsafe { &mut *(a as *mut A as *mut B) })
365   } else {
366     Err(PodCastError::SizeMismatch)
367   }
368 }
369 
370 /// Try to convert `&[T]` into `&[U]` (possibly with a change in length).
371 ///
372 /// * `input.as_ptr() as usize == output.as_ptr() as usize`
373 /// * `input.len() * size_of::<A>() == output.len() * size_of::<B>()`
374 ///
375 /// ## Failure
376 ///
377 /// * If the target type has a greater alignment requirement and the input slice
378 ///   isn't aligned.
379 /// * If the target element type is a different size from the current element
380 ///   type, and the output slice wouldn't be a whole number of elements when
381 ///   accounting for the size change (eg: 3 `u16` values is 1.5 `u32` values, so
382 ///   that's a failure).
383 /// * Similarly, you can't convert between a
384 ///   [ZST](https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts)
385 ///   and a non-ZST.
386 #[inline]
try_cast_slice<A: Pod, B: Pod>(a: &[A]) -> Result<&[B], PodCastError>387 pub fn try_cast_slice<A: Pod, B: Pod>(a: &[A]) -> Result<&[B], PodCastError> {
388   // Note(Lokathor): everything with `align_of` and `size_of` will optimize away
389   // after monomorphization.
390   if align_of::<B>() > align_of::<A>()
391     && (a.as_ptr() as usize) % align_of::<B>() != 0
392   {
393     Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
394   } else if size_of::<B>() == size_of::<A>() {
395     Ok(unsafe { core::slice::from_raw_parts(a.as_ptr() as *const B, a.len()) })
396   } else if size_of::<A>() == 0 || size_of::<B>() == 0 {
397     Err(PodCastError::SizeMismatch)
398   } else if core::mem::size_of_val(a) % size_of::<B>() == 0 {
399     let new_len = core::mem::size_of_val(a) / size_of::<B>();
400     Ok(unsafe { core::slice::from_raw_parts(a.as_ptr() as *const B, new_len) })
401   } else {
402     Err(PodCastError::OutputSliceWouldHaveSlop)
403   }
404 }
405 
406 /// Try to convert `&mut [T]` into `&mut [U]` (possibly with a change in length).
407 ///
408 /// As [`try_cast_slice`], but `&mut`.
409 #[inline]
try_cast_slice_mut<A: Pod, B: Pod>( a: &mut [A], ) -> Result<&mut [B], PodCastError>410 pub fn try_cast_slice_mut<A: Pod, B: Pod>(
411   a: &mut [A],
412 ) -> Result<&mut [B], PodCastError> {
413   // Note(Lokathor): everything with `align_of` and `size_of` will optimize away
414   // after monomorphization.
415   if align_of::<B>() > align_of::<A>()
416     && (a.as_mut_ptr() as usize) % align_of::<B>() != 0
417   {
418     Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
419   } else if size_of::<B>() == size_of::<A>() {
420     Ok(unsafe {
421       core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, a.len())
422     })
423   } else if size_of::<A>() == 0 || size_of::<B>() == 0 {
424     Err(PodCastError::SizeMismatch)
425   } else if core::mem::size_of_val(a) % size_of::<B>() == 0 {
426     let new_len = core::mem::size_of_val(a) / size_of::<B>();
427     Ok(unsafe {
428       core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, new_len)
429     })
430   } else {
431     Err(PodCastError::OutputSliceWouldHaveSlop)
432   }
433 }
434