From 0e8cc40233ca0d020ba68357fef3c852196c309b Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 14 May 2024 12:49:11 -0700 Subject: [PATCH] [WIP] try_transmute_{ref,mut}! --- src/macro_util.rs | 102 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 13 deletions(-) diff --git a/src/macro_util.rs b/src/macro_util.rs index 4cf9585716..6d105238d8 100644 --- a/src/macro_util.rs +++ b/src/macro_util.rs @@ -27,9 +27,9 @@ use core::ptr::{self, NonNull}; use crate::{ pointer::{ invariant::{self, AtLeast, Invariants}, - AliasingSafe, AliasingSafeReason, BecauseExclusive, + AliasingSafe, AliasingSafeReason, BecauseExclusive, BecauseImmutable, }, - IntoBytes, Ptr, TryFromBytes, ValidityError, + Immutable, IntoBytes, Ptr, TryFromBytes, ValidityError, }; /// A compile-time check that should be one particular value. @@ -417,24 +417,26 @@ pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( unsafe { &mut *dst } } -/// Is a given source a valid instance of `Self`? +/// Attempts to transmute a pointer from `Src` to `Dst`. /// /// # Safety /// -/// Unsafe code may assume that, if `is_mut_src_valid(src)` returns true, `*src` -/// is a bit-valid instance of `Dst`, and that the size of `Src` is greater than -/// or equal to the size of `Dst`. +/// Unsafe code may assume that, if `try_transmute_ptr(src)` returns true, +/// `*src` is a bit-valid instance of `Dst`, and that the size of `Src` is +/// greater than or equal to the size of `Dst`. /// /// # Panics /// -/// `is_src_valid` may either produce a post-monomorphization error or a panic -/// if `Dst` is bigger than `Src`. Otherwise, `is_src_valid` panics under the -/// same circumstances as [`is_bit_valid`]. +/// `try_transmute_ptr` may either produce a post-monomorphization error or a +/// panic if `Dst` is bigger than `Src`. Otherwise, `try_transmute_ptr` panics +/// under the same circumstances as [`is_bit_valid`]. /// /// [`is_bit_valid`]: TryFromBytes::is_bit_valid #[doc(hidden)] #[inline] -fn is_src_valid(src: Ptr<'_, Src, I>) -> bool +fn try_transmute_ptr( + src: Ptr<'_, Src, I>, +) -> Result, ()> where Src: IntoBytes, Dst: TryFromBytes + AliasingSafe, @@ -459,9 +461,16 @@ where // SAFETY: `c_ptr` is derived from `src` which is `IntoBytes`. By // invariant on `IntoByte`s, `c_ptr`'s referent consists entirely of // initialized bytes. - let c_ptr = unsafe { c_ptr.assume_initialized() }; + let mut c_ptr = unsafe { c_ptr.assume_initialized() }; + + if Dst::is_bit_valid(c_ptr.reborrow()) { + // SAFETY: TODO + let ptr = unsafe { c_ptr.assume_valid() }; - Dst::is_bit_valid(c_ptr) + Ok(ptr) + } else { + Err(()) + } } /// Attempts to transmute `Src` into `Dst`. @@ -481,7 +490,7 @@ where Src: IntoBytes, Dst: TryFromBytes, { - if !is_src_valid::(Ptr::from_mut(&mut src)) { + if try_transmute_ptr::(Ptr::from_mut(&mut src)).is_err() { return Err(ValidityError::new(src)); } @@ -497,6 +506,73 @@ where Ok(unsafe { core::mem::transmute_copy(&*src) }) } +/// Attempts to transmute `&Src` into `&Dst`. +/// +/// A helper for `try_transmute_ref!`. +/// +/// # Safety +/// +/// The caller promises that `size_of::() == size_of::()` and that +/// `align_of::() >= align_of::()`. +/// +/// # Panics +/// +/// `try_transmute_ref` may either produce a post-monomorphization error or a +/// panic if `Dst` is bigger than `Src`. Otherwise, `try_transmute_ref` panics +/// under the same circumstances as [`is_bit_valid`]. +/// +/// [`is_bit_valid`]: TryFromBytes::is_bit_valid +#[inline(always)] +pub unsafe fn try_transmute_ref(src: &Src) -> Result<&Dst, ValidityError<&Src, Dst>> +where + Src: IntoBytes + Immutable, + Dst: TryFromBytes + Immutable, +{ + let ptr = Ptr::from_ref(src); + try_transmute_ptr::(ptr) + .map(|ptr| { + // SAFETY: TODO + let ptr = unsafe { ptr.assume_alignment::() }; + ptr.as_ref() + }) + .map_err(|()| ValidityError::new(src)) +} + +/// Attempts to transmute `&mut Src` into `&mut Dst`. +/// +/// A helper for `try_transmute_mut!`. +/// +/// # Safety +/// +/// The caller promises that `size_of::() == size_of::()` and that +/// `align_of::() >= align_of::()`. +/// +/// # Panics +/// +/// `try_transmute_mut` may either produce a post-monomorphization error or a +/// panic if `Dst` is bigger than `Src`. Otherwise, `try_transmute_mut` panics +/// under the same circumstances as [`is_bit_valid`]. +/// +/// [`is_bit_valid`]: TryFromBytes::is_bit_valid +#[inline(always)] +pub unsafe fn try_transmute_mut( + src: &mut Src, +) -> Result<&mut Dst, ValidityError<&mut Src, Dst>> +where + Src: IntoBytes, + Dst: TryFromBytes, +{ + let ptr = Ptr::from_mut(src); + match try_transmute_ptr::(ptr) { + Ok(ptr) => { + // SAFETY: TODO + let ptr = unsafe { ptr.assume_alignment::() }; + Ok(ptr.as_mut()) + } + Err(()) => Err(ValidityError::new(src)), + } +} + /// A function which emits a warning if its return value is not used. #[must_use] #[inline(always)]