diff --git a/src/allocation.rs b/src/allocation.rs index 1763ffd..fe30668 100644 --- a/src/allocation.rs +++ b/src/allocation.rs @@ -24,6 +24,67 @@ use core::{ mem::ManuallyDrop, ops::{Deref, DerefMut}, }; +use internal::{CastablePointer, OwnedCastablePointer}; + +unsafe impl CastablePointer for Box { + #[inline] + fn into_raw(self) -> *mut T { + Box::into_raw(self) + } + + #[inline] + unsafe fn from_raw(ptr: *mut T) -> Self { + Box::from_raw(ptr) + } +} + +unsafe impl CastablePointer for Rc { + #[inline] + fn into_raw(self) -> *mut T { + Rc::into_raw(self) as *mut T + } + + #[inline] + unsafe fn from_raw(ptr: *mut T) -> Self { + Rc::from_raw(ptr) + } +} + +#[cfg(target_has_atomic = "ptr")] +unsafe impl CastablePointer for Arc { + #[inline] + fn into_raw(self) -> *mut T { + Arc::into_raw(self) as *mut T + } + + #[inline] + unsafe fn from_raw(ptr: *mut T) -> Self { + Arc::from_raw(ptr) + } +} + +// Safety: This is a valid implementation because according to the docs of +// `std::alloc::GlobalAlloc::dealloc()`, the `Layout` that was used to alloc the +// block must be the same `Layout` that is used to dealloc the block. Luckily, +// `Layout` only stores two things, the alignment, and the size in bytes. So as +// long as both of those stay the same, the Layout will remain a valid input to +// dealloc. This matches the requirements of `OwnedCastablePointer`. +unsafe impl OwnedCastablePointer for Box {} +// Safety: This is a valid implementation because according to the docs of +// std::rc::Rc::from_raw(), the type U that was in the original Rc acquired +// from Rc::into_raw() must have the same size and alignment of the type T in +// the new Rc. So as long as both the size and alignment stay the same, the +// Arc will remain a valid Arc. This matches the requirements of +// `OwnedCastablePointer`. +unsafe impl OwnedCastablePointer for Rc {} +// Safety: This is a valid implementation because according to the docs of +// std::sync::Arc::from_raw(), the type U that was in the original Arc +// acquired from Arc::into_raw() must have the same size and alignment of the +// type T in the new Arc. So as long as both the size and alignment stay the +// same, the Arc will remain a valid Arc. This matches the requirements of +// `OwnedCastablePointer`. +#[cfg(target_has_atomic = "ptr")] +unsafe impl OwnedCastablePointer for Arc {} /// As [`try_cast_box`], but unwraps for you. #[inline] @@ -44,15 +105,9 @@ pub fn cast_box(input: Box) -> Box { pub fn try_cast_box( input: Box, ) -> Result, (PodCastError, Box)> { - if align_of::() != align_of::() { - Err((PodCastError::AlignmentMismatch, input)) - } else if size_of::() != size_of::() { - Err((PodCastError::SizeMismatch, input)) - } else { - // Note(Lokathor): This is much simpler than with the Vec casting! - let ptr: *mut B = Box::into_raw(input) as *mut B; - Ok(unsafe { Box::from_raw(ptr) }) - } + // Safety: We uphold that A: NoUninit and B: AnyBitPattern, so this cast is + // safe. + unsafe { internal::try_cast_owned_ptr(input) } } /// Allocates a `Box` with all of the contents being zeroed out. @@ -169,33 +224,9 @@ pub fn cast_slice_box( pub fn try_cast_slice_box( input: Box<[A]>, ) -> Result, (PodCastError, Box<[A]>)> { - if align_of::() != align_of::() { - Err((PodCastError::AlignmentMismatch, input)) - } else if size_of::() != size_of::() { - if size_of::() * input.len() % size_of::() != 0 { - // If the size in bytes of the underlying buffer does not match an exact - // multiple of the size of B, we cannot cast between them. - Err((PodCastError::SizeMismatch, input)) - } else { - // Because the size is an exact multiple, we can now change the length - // of the slice and recreate the Box - // NOTE: This is a valid operation because according to the docs of - // std::alloc::GlobalAlloc::dealloc(), the Layout that was used to alloc - // the block must be the same Layout that is used to dealloc the block. - // Luckily, Layout only stores two things, the alignment, and the size in - // bytes. So as long as both of those stay the same, the Layout will - // remain a valid input to dealloc. - let length = size_of::() * input.len() / size_of::(); - let box_ptr: *mut A = Box::into_raw(input) as *mut A; - let ptr: *mut [B] = - unsafe { core::slice::from_raw_parts_mut(box_ptr as *mut B, length) }; - Ok(unsafe { Box::<[B]>::from_raw(ptr) }) - } - } else { - let box_ptr: *mut [A] = Box::into_raw(input); - let ptr: *mut [B] = box_ptr as *mut [B]; - Ok(unsafe { Box::<[B]>::from_raw(ptr) }) - } + // Safety: We uphold that A: NoUninit and B: AnyBitPattern, so this cast is + // safe. + unsafe { internal::try_cast_owned_slice_ptr(input) } } /// As [`try_cast_vec`], but unwraps for you. @@ -324,19 +355,16 @@ pub fn cast_rc( /// * The start and end content type of the `Rc` must have the exact same /// alignment. /// * The start and end size of the `Rc` must have the exact same size. +// FIXME(zachs18): Depending on the exact stabilized requirements of +// Rc::get_mut_unchecked, the bounds on this function could be relaxed to A: +// NoUninit, B: AnyBitPattern. #[inline] pub fn try_cast_rc( input: Rc, ) -> Result, (PodCastError, Rc)> { - if align_of::() != align_of::() { - Err((PodCastError::AlignmentMismatch, input)) - } else if size_of::() != size_of::() { - Err((PodCastError::SizeMismatch, input)) - } else { - // Safety: Rc::from_raw requires size and alignment match, which is met. - let ptr: *const B = Rc::into_raw(input) as *const B; - Ok(unsafe { Rc::from_raw(ptr) }) - } + // Safety: We uphold that A: NoUninit and B: AnyBitPattern, so this cast is + // safe. + unsafe { internal::try_cast_owned_ptr(input) } } /// As [`try_cast_arc`], but unwraps for you. @@ -361,6 +389,9 @@ pub fn cast_arc( /// * The start and end content type of the `Arc` must have the exact same /// alignment. /// * The start and end size of the `Arc` must have the exact same size. +// FIXME(zachs18): Depending on the exact stabilized requirements of +// Arc::get_mut_unchecked, the bounds on this function could be relaxed to A: +// NoUninit, B: AnyBitPattern. #[inline] #[cfg(target_has_atomic = "ptr")] pub fn try_cast_arc< @@ -369,15 +400,9 @@ pub fn try_cast_arc< >( input: Arc, ) -> Result, (PodCastError, Arc)> { - if align_of::() != align_of::() { - Err((PodCastError::AlignmentMismatch, input)) - } else if size_of::() != size_of::() { - Err((PodCastError::SizeMismatch, input)) - } else { - // Safety: Arc::from_raw requires size and alignment match, which is met. - let ptr: *const B = Arc::into_raw(input) as *const B; - Ok(unsafe { Arc::from_raw(ptr) }) - } + // Safety: We uphold that A: NoUninit and B: AnyBitPattern, so this cast is + // safe. + unsafe { internal::try_cast_owned_ptr(input) } } /// As [`try_cast_slice_rc`], but unwraps for you. @@ -405,6 +430,9 @@ pub fn cast_slice_rc< /// alignment. /// * The start and end content size in bytes of the `Rc<[T]>` must be the exact /// same. +// FIXME(zachs18): Depending on the exact stabilized requirements of +// Rc::get_mut_unchecked, the bounds on this function could be relaxed to A: +// NoUninit, B: AnyBitPattern. #[inline] pub fn try_cast_slice_rc< A: NoUninit + AnyBitPattern, @@ -412,34 +440,9 @@ pub fn try_cast_slice_rc< >( input: Rc<[A]>, ) -> Result, (PodCastError, Rc<[A]>)> { - if align_of::() != align_of::() { - Err((PodCastError::AlignmentMismatch, input)) - } else if size_of::() != size_of::() { - if size_of::() * input.len() % size_of::() != 0 { - // If the size in bytes of the underlying buffer does not match an exact - // multiple of the size of B, we cannot cast between them. - Err((PodCastError::SizeMismatch, input)) - } else { - // Because the size is an exact multiple, we can now change the length - // of the slice and recreate the Rc - // NOTE: This is a valid operation because according to the docs of - // std::rc::Rc::from_raw(), the type U that was in the original Rc - // acquired from Rc::into_raw() must have the same size alignment and - // size of the type T in the new Rc. So as long as both the size - // and alignment stay the same, the Rc will remain a valid Rc. - let length = size_of::() * input.len() / size_of::(); - let rc_ptr: *const A = Rc::into_raw(input) as *const A; - // Must use ptr::slice_from_raw_parts, because we cannot make an - // intermediate const reference, because it has mutable provenance, - // nor an intermediate mutable reference, because it could be aliased. - let ptr = core::ptr::slice_from_raw_parts(rc_ptr as *const B, length); - Ok(unsafe { Rc::<[B]>::from_raw(ptr) }) - } - } else { - let rc_ptr: *const [A] = Rc::into_raw(input); - let ptr: *const [B] = rc_ptr as *const [B]; - Ok(unsafe { Rc::<[B]>::from_raw(ptr) }) - } + // Safety: We uphold that A: NoUninit and B: AnyBitPattern, so this cast is + // safe. + unsafe { internal::try_cast_owned_slice_ptr(input) } } /// As [`try_cast_slice_arc`], but unwraps for you. @@ -468,6 +471,9 @@ pub fn cast_slice_arc< /// alignment. /// * The start and end content size in bytes of the `Arc<[T]>` must be the /// exact same. +// FIXME(zachs18): Depending on the exact stabilized requirements of +// Arc::get_mut_unchecked, the bounds on this function could be relaxed to A: +// NoUninit, B: AnyBitPattern. #[inline] #[cfg(target_has_atomic = "ptr")] pub fn try_cast_slice_arc< @@ -476,34 +482,9 @@ pub fn try_cast_slice_arc< >( input: Arc<[A]>, ) -> Result, (PodCastError, Arc<[A]>)> { - if align_of::() != align_of::() { - Err((PodCastError::AlignmentMismatch, input)) - } else if size_of::() != size_of::() { - if size_of::() * input.len() % size_of::() != 0 { - // If the size in bytes of the underlying buffer does not match an exact - // multiple of the size of B, we cannot cast between them. - Err((PodCastError::SizeMismatch, input)) - } else { - // Because the size is an exact multiple, we can now change the length - // of the slice and recreate the Arc - // NOTE: This is a valid operation because according to the docs of - // std::sync::Arc::from_raw(), the type U that was in the original Arc - // acquired from Arc::into_raw() must have the same size alignment and - // size of the type T in the new Arc. So as long as both the size - // and alignment stay the same, the Arc will remain a valid Arc. - let length = size_of::() * input.len() / size_of::(); - let arc_ptr: *const A = Arc::into_raw(input) as *const A; - // Must use ptr::slice_from_raw_parts, because we cannot make an - // intermediate const reference, because it has mutable provenance, - // nor an intermediate mutable reference, because it could be aliased. - let ptr = core::ptr::slice_from_raw_parts(arc_ptr as *const B, length); - Ok(unsafe { Arc::<[B]>::from_raw(ptr) }) - } - } else { - let arc_ptr: *const [A] = Arc::into_raw(input); - let ptr: *const [B] = arc_ptr as *const [B]; - Ok(unsafe { Arc::<[B]>::from_raw(ptr) }) - } + // Safety: We uphold that A: NoUninit and B: AnyBitPattern, so this cast is + // safe. + unsafe { internal::try_cast_owned_slice_ptr(input) } } /// An extension trait for `TransparentWrapper` and alloc types. diff --git a/src/internal.rs b/src/internal.rs index 3ede50f..b4e8c9b 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -45,6 +45,337 @@ pub(crate) fn something_went_wrong(_src: &str, _err: D) -> ! { panic!("Called a panicing helper from bytemuck which paniced"); } +/// # Safety +/// +/// Must only be implemented on types where it is valid to pass a +/// pointer to from_raw that came from into_raw on a different instantiation of +/// the same ADT, e.g. +/// ```rs +/// let ptr: *mut U = as CastablePointer>::into_raw(my_pointer); +/// let new_ptr: *mut T = / cast ptr to *mut T upholding the invariants below */; +/// let my_new_pointer = as CastablePointer>::from_raw(new_ptr); +/// ``` +/// +/// For all implementors, it must be valid to pass the same pointer returned +/// from `Self::into_raw(self)` (uncasted) back to `Self::from_raw(ptr)` to get +/// `self` back. +/// +/// Looser requirements are given by subtraits. In particular, changing the +/// pointee type is generally allowed as long as layout invariants and +/// bytemuck's castability trait invariants are upheld. +pub(crate) unsafe trait CastablePointer { + /// Return a pointer that can be given to `Self::from_raw``, if all other + /// invariants hold. This pointer should not dereferenced, as it may be + /// invalid for anything other than passing to from_raw. (e.g. it may actually + /// be a `*const T``, or it may point to a dropped T that has not been + /// deallocated.) + fn into_raw(self) -> *mut T; + /// # Safety + /// + /// `ptr` must have come from CastablePointer::into_raw on the same ADT + /// (e.g. `Box` -> `Box`, `&'a T` -> `&'a U`), and any additional + /// invariants of the ADT must be upheld. See the ADT's particular + /// `AdjectiveCastablePointer` impls' documentation for those invariants. + unsafe fn from_raw(ptr: *mut T) -> Self; +} + +/// This marker trait indicates that a [`CastablePointer`] is a borrowed +/// pointer. +/// +/// # Safety +/// +/// * `CastablePointer::from_raw` must accept pointers where the pointee has the +/// same size as the `into_raw`'d pointee, and the pointer is validly aligned +/// for `Self`'s pointee. +/// * Additional castability invariants on the pointee (e.g. NoUninit) must also +/// be upheld. +pub(crate) unsafe trait BorrowedCastablePointer {} + +/// This marker trait indicates that a [`CastablePointer`] is an owned +/// pointer. +/// +/// # Safety +/// +/// * `from_raw` must accept pointers where the pointee has the same layout +/// (size and alignment) as the `into_raw`'d pointee. +/// * Additional castability invariants on the pointee (e.g. NoUninit) must also +/// be upheld. +#[cfg(feature = "extern_crate_alloc")] +pub(crate) unsafe trait OwnedCastablePointer {} + +unsafe impl<'a, T: ?Sized> CastablePointer for &'a T { + #[inline] + fn into_raw(self) -> *mut T { + self as *const T as *mut T + } + + #[inline] + unsafe fn from_raw(ptr: *mut T) -> Self { + &*(ptr as *const T) + } +} + +unsafe impl<'a, T: ?Sized> CastablePointer for &'a mut T { + #[inline] + fn into_raw(self) -> *mut T { + self as *mut T + } + + #[inline] + unsafe fn from_raw(ptr: *mut T) -> Self { + &mut *ptr + } +} + +unsafe impl<'a, T: ?Sized> BorrowedCastablePointer for &'a T {} +unsafe impl<'a, T: ?Sized> BorrowedCastablePointer for &'a mut T {} + +/// Attempts to cast the content type of an owned `CastablePointer`. +/// +/// On failure you get back an error along with the starting `CastablePointer`. +/// +/// ## Failure +/// +/// * The start and end pointee type must have the exact same size and +/// alignment. +/// +/// ## Safety +/// +/// * `AP` and `BP` must be instantiations of the same ADT with `A` and `B`, +/// respectively (e.g. `Box` and `Box`). +/// * The invariants on A and B (e.g. NoUninit) must be upheld by the caller. +#[cfg(feature = "extern_crate_alloc")] +#[inline] +pub(crate) unsafe fn try_cast_owned_ptr< + A: Copy, + B: Copy, + AP: CastablePointer + OwnedCastablePointer, + BP: CastablePointer + OwnedCastablePointer, +>( + input: AP, +) -> Result { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + Err((PodCastError::SizeMismatch, input)) + } else { + // Safety: we uphold the requirements of OwnedCastablePointer because `A` + // and `B` have the same layout. Castability invariants on A + // and B are upheld by our caller. + unsafe { + let ptr: *mut A = CastablePointer::into_raw(input); + Ok(CastablePointer::from_raw(ptr as *mut B)) + } + } +} + +/// Try to convert an owned `CastablePointer<[A]>` into `CastablePointer<[B]>` +/// (possibly with a change in length). +/// +/// * `input.as_ptr() as usize == output.as_ptr() as usize` +/// * `input.len() * size_of::() == output.len() * size_of::()` +/// +/// ## Failure +/// +/// * The start and end content type of the `CastablePointer<[T]>` must have the +/// exact same alignment. +/// * The start and end content size in bytes of the `CastablePointer<[T]>` must +/// be the exact same. +/// +/// ## Safety +/// +/// * `AP` and `BP` must be instantiations of the same ADT (e.g. `Box<[A]>` and +/// `Box<[B]>`). +/// * The castabilty invariants on A and B, if any (e.g. NoUninit) must be +/// upheld by the caller. +/// +/// TODO: if we want to support Weak, we'd have to remove the Deref bound and +/// get the length from the pointer directly, but that is not possible in +/// bytemuck's MSRV, so would have to be under a feature flag. +#[cfg(feature = "extern_crate_alloc")] +#[inline] +pub(crate) unsafe fn try_cast_owned_slice_ptr< + A: Copy, + B: Copy, + AP: CastablePointer<[A]> + OwnedCastablePointer + core::ops::Deref, + BP: CastablePointer<[B]> + OwnedCastablePointer, +>( + input: AP, +) -> Result { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + let input_bytes = size_of_val::<[A]>(&*input); + if (size_of::() == 0 && input_bytes != 0) + || (size_of::() != 0 && input_bytes % size_of::() != 0) + { + // If the size in bytes of the underlying buffer does not match an exact + // multiple of the size of B, we cannot cast between them. + Err((PodCastError::OutputSliceWouldHaveSlop, input)) + } else { + // Because the size is an exact multiple, we can now change the length + // of the slice and recreate the pointer. + let length = + if size_of::() != 0 { input_bytes / size_of::() } else { 0 }; + let ptr: *mut [A] = CastablePointer::into_raw(input); + let ptr: *mut [B] = + core::ptr::slice_from_raw_parts_mut(ptr as *mut B, length); + // Safety: We uphold the requirements of OwnedCastablePointer because *ptr + // has the same size as before, and `A` and `B` have the same alignemnt. + unsafe { Ok(CastablePointer::from_raw(ptr)) } + } + } else { + // Safety: we uphold the requirements of OwnedCastablePointer because `A` + // and `B` have the same layout and we did not change the slice length. + // Castability invariants on A and B are upheld by our caller. + unsafe { + let ptr: *mut [A] = CastablePointer::into_raw(input); + Ok(CastablePointer::from_raw(ptr as *mut [B])) + } + } +} + +/// Attempts to cast the content type of an `CastablePointer`. The alignment +/// of `A` and `B` need not be the same as long as `input` is validly aligned +/// for `B`. +/// +/// On failure you get back an error along with the starting `CastablePointer`. +/// +/// ## Failure +/// +/// * The start and end pointee type must have the exact same size. +/// +/// ## Safety +/// +/// * `AP` and `BP` must be instantiations of the same ADT with `A` and `B`, +/// respectively (e.g. `Box` and `Box`) such that `from_raw` is valid to +/// pass same-size types where the pointer is validly aligned for the +/// destination type. +/// * The pointer returned from `AP::into_raw` must be aligned for `A`. +/// * The invariants on A and B (e.g. NoUninit) must be upheld by the caller. +#[inline] +pub(crate) unsafe fn try_cast_borrowed_ptr< + A: Copy, + B: Copy, + AP: CastablePointer + BorrowedCastablePointer, + BP: CastablePointer + BorrowedCastablePointer, +>( + input: AP, +) -> Result { + if size_of::() != size_of::() { + Err((PodCastError::SizeMismatch, input)) + } else { + let ptr: *mut A = CastablePointer::into_raw(input); + // only do the alignment check if it could fail + if align_of::() >= align_of::() + || is_aligned_to(ptr as *const (), align_of::()) + { + // Safety: same size, and the pointer is aligned. Other invariants are + // upheld by our caller. + unsafe { Ok(CastablePointer::from_raw(ptr as *mut B)) } + } else { + // Cannot cast unaligned pointer to more-aligned type. + // Safety: AP::from_raw(AP::into_raw(ptr)) is always safe. + Err((PodCastError::TargetAlignmentGreaterAndInputNotAligned, unsafe { + CastablePointer::from_raw(ptr) + })) + } + } +} + +/// Try to convert `CastablePointer<[A]>` into `CastablePointer<[B]>` (possibly +/// with a change in length). The alignment of `A` and `B` need not be the same +/// as long as `input` is validly aligned for `B`. +/// +/// * `input.as_ptr() as usize == output.as_ptr() as usize` +/// * `input.len() * size_of::() == output.len() * size_of::()` +/// +/// ## Failure +/// +/// * The start and end content size in bytes of the `CastablePointer<[T]>` must +/// be the exact same. +/// +/// ## Safety +/// +/// * `AP` and `BP` must be instantiations of the same ADT with `[A]` and `[B]` +/// (e.g. `Box<[A]>` and `Box<[B]>`) such that `from_raw` is valid to pass +/// same-size types where the pointer is validly aligned for the destination +/// type. +/// * The pointer returned from `AP::into_raw` must be aligned for `A`. +/// * The invariants on A and B, if any (e.g. NoUninit) must be upheld by the +/// caller. +/// +/// TODO: if we want to support Weak, we'd have to remove the Deref bound and +/// get the length from the pointer directly, but that is not possible in +/// bytemuck's MSRV, so would have to be under a feature flag. +#[inline] +unsafe fn try_cast_borrowed_slice_ptr< + A: Copy, + B: Copy, + AP: CastablePointer<[A]> + + BorrowedCastablePointer + + core::ops::Deref, + BP: CastablePointer<[B]> + BorrowedCastablePointer, +>( + input: AP, +) -> Result { + if size_of::() != size_of::() { + let input_bytes = size_of::() * input.len(); + if (size_of::() == 0 && input_bytes != 0) + || (size_of::() != 0 && input_bytes % size_of::() != 0) + { + // If the size in bytes of the underlying buffer does not match an exact + // multiple of the size of B, we cannot cast between them. + Err((PodCastError::OutputSliceWouldHaveSlop, input)) + } else { + let length = if size_of::() != 0 { + size_of::() * input.len() / size_of::() + } else { + 0 + }; + let ptr: *mut [A] = CastablePointer::into_raw(input); + // only do the alignment check if it could fail + if align_of::() >= align_of::() + || is_aligned_to(ptr as *const (), align_of::()) + { + // Because the size is an exact multiple, we can now change the length + // of the slice and recreate the pointer. + // NOTE: This is a valid operation because according to the contract of + // CastablePointer, the size in bytes and type alignment must be the + // same. The invariants on A and B must be upheld by our caller. + let ptr: *mut [B] = + core::ptr::slice_from_raw_parts_mut(ptr as *mut B, length); + unsafe { Ok(CastablePointer::from_raw(ptr)) } + } else { + // Cannot cast unaligned pointer to more-aligned type. + // Safety: AP::from_raw(AP::into_raw(ptr)) is always safe. + Err((PodCastError::TargetAlignmentGreaterAndInputNotAligned, unsafe { + CastablePointer::from_raw(ptr) + })) + } + } + } else { + let ptr: *mut [A] = CastablePointer::into_raw(input); + // only do the alignment check if it could fail + if align_of::() >= align_of::() + || is_aligned_to(ptr as *const (), align_of::()) + { + // Because the size is the same, we can recreate the pointer without + // changing the length. NOTE: This is a valid operation because + // according to the contract of CastablePointer, the size in bytes + // and type alignment must be the same. The invariants on A and B + // must be upheld by our caller. + unsafe { Ok(CastablePointer::from_raw(ptr as *mut [B])) } + } else { + // Cannot cast unaligned pointer to more-aligned type. + // Safety: AP::from_raw(AP::into_raw(ptr)) is always safe. + Err((PodCastError::TargetAlignmentGreaterAndInputNotAligned, unsafe { + CastablePointer::from_raw(ptr) + })) + } + } +} + /// Re-interprets `&T` as `&[u8]`. /// /// Any ZST becomes an empty slice, and in that case the pointer value of that @@ -301,17 +632,10 @@ pub(crate) unsafe fn try_cast( pub(crate) unsafe fn try_cast_ref( a: &A, ) -> Result<&B, PodCastError> { - // Note(Lokathor): everything with `align_of` and `size_of` will optimize away - // after monomorphization. - if align_of::() > align_of::() - && !is_aligned_to(a as *const A as *const (), align_of::()) - { - Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) - } else if size_of::() == size_of::() { - Ok(unsafe { &*(a as *const A as *const B) }) - } else { - Err(PodCastError::SizeMismatch) - } + // Safety: `&A` and `&B` are valid to cast between under the requirements of + // try_cast_ptr. Additional invariants on `A` and `B` are upheld by our + // caller. + unsafe { try_cast_borrowed_ptr(a) }.map_err(|(err, _)| err) } /// Try to convert a `&mut T` into `&mut U`. @@ -321,17 +645,10 @@ pub(crate) unsafe fn try_cast_ref( pub(crate) unsafe fn try_cast_mut( a: &mut A, ) -> Result<&mut B, PodCastError> { - // Note(Lokathor): everything with `align_of` and `size_of` will optimize away - // after monomorphization. - if align_of::() > align_of::() - && !is_aligned_to(a as *const A as *const (), align_of::()) - { - Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) - } else if size_of::() == size_of::() { - Ok(unsafe { &mut *(a as *mut A as *mut B) }) - } else { - Err(PodCastError::SizeMismatch) - } + // Safety: `&mut A` and `&mut B` are valid to cast between under the + // requirements of try_cast_ptr. Additional invariants on `A` and `B` are + // upheld by our caller. + unsafe { try_cast_borrowed_ptr(a) }.map_err(|(err, _)| err) } /// Try to convert `&[A]` into `&[B]` (possibly with a change in length). @@ -353,22 +670,10 @@ pub(crate) unsafe fn try_cast_mut( pub(crate) unsafe fn try_cast_slice( a: &[A], ) -> Result<&[B], PodCastError> { - // Note(Lokathor): everything with `align_of` and `size_of` will optimize away - // after monomorphization. - if align_of::() > align_of::() - && !is_aligned_to(a.as_ptr() as *const (), align_of::()) - { - Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) - } else if size_of::() == size_of::() { - Ok(unsafe { core::slice::from_raw_parts(a.as_ptr() as *const B, a.len()) }) - } else if size_of::() == 0 || size_of::() == 0 { - Err(PodCastError::SizeMismatch) - } else if core::mem::size_of_val(a) % size_of::() == 0 { - let new_len = core::mem::size_of_val(a) / size_of::(); - Ok(unsafe { core::slice::from_raw_parts(a.as_ptr() as *const B, new_len) }) - } else { - Err(PodCastError::OutputSliceWouldHaveSlop) - } + // Safety: `&[A]` and `&[B]` are valid to cast between under the requirements + // of try_cast_ptr. Additional invariants on `A` and `B` are upheld by our + // caller. + unsafe { try_cast_borrowed_slice_ptr(a) }.map_err(|(err, _)| err) } /// Try to convert `&mut [A]` into `&mut [B]` (possibly with a change in @@ -379,24 +684,8 @@ pub(crate) unsafe fn try_cast_slice( pub(crate) unsafe fn try_cast_slice_mut( a: &mut [A], ) -> Result<&mut [B], PodCastError> { - // Note(Lokathor): everything with `align_of` and `size_of` will optimize away - // after monomorphization. - if align_of::() > align_of::() - && !is_aligned_to(a.as_ptr() as *const (), align_of::()) - { - Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) - } else if size_of::() == size_of::() { - Ok(unsafe { - core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, a.len()) - }) - } else if size_of::() == 0 || size_of::() == 0 { - Err(PodCastError::SizeMismatch) - } else if core::mem::size_of_val(a) % size_of::() == 0 { - let new_len = core::mem::size_of_val(a) / size_of::(); - Ok(unsafe { - core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, new_len) - }) - } else { - Err(PodCastError::OutputSliceWouldHaveSlop) - } + // Safety: `&mut [A]` and `&mut [B]` are valid to cast between under the + // requirements of try_cast_ptr. Additional invariants on `A` and `B` are + // upheld by our caller. + unsafe { try_cast_borrowed_slice_ptr(a) }.map_err(|(err, _)| err) }