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