Skip to content

Commit

Permalink
Use AlignmentError in Unalign's failure conditions
Browse files Browse the repository at this point in the history
This make's `Unalign`'s methods consistent with zerocopy's other
methods, and, in the case of `Unalign::try_deref_mut`, allows the
original `&mut Unalign<T>` to be reused in the event of failure.

Makes progress towards #1139
  • Loading branch information
jswrenn committed May 6, 2024
1 parent f4a6c55 commit fa30a6b
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 10 deletions.
37 changes: 33 additions & 4 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,12 +613,41 @@ mod _conversions {
c
}
}

impl<'a, T, I> Ptr<'a, T, I>
where
I: Invariants,
{
/// Converts a `Ptr` to a `Unalign<T>` into an `Ptr` to an unaligned `T`.
pub(crate) fn into_unaligned(
self,
) -> Ptr<'a, crate::Unalign<T>, (I::Aliasing, Aligned, I::Validity)> {
// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` type
// wrapping a single `T` field. Thus, `Unalign<T>` has the same size
// as `T` and contains `UnsafeCell`s at the same locations as `T`.
// The cast is implemented in the form `|p: *mut T| p as *mut U`,
// where `U` is `Unalign<T>`.
let ptr = unsafe {
#[allow(clippy::as_conversions)]
self.cast_unsized(|p: *mut T| p as *mut crate::Unalign<T>)
};
// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` type
// wrapping a single `T` field, thus `Unalign<T>` has exactly the
// same validity as `T`.
let ptr = unsafe { ptr.assume_validity::<I::Validity>() };
// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` type
// wrapping a single `T` field, thus `Unalign<T>` is always
// trivially aligned.
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
ptr
}
}
}

/// State transitions between invariants.
mod _transitions {
use super::*;
use crate::{TryFromBytes, ValidityError};
use crate::{AlignmentError, TryFromBytes, ValidityError};

impl<'a, T, I> Ptr<'a, T, I>
where
Expand Down Expand Up @@ -745,16 +774,16 @@ mod _transitions {
/// on success.
pub(crate) fn bikeshed_try_into_aligned(
self,
) -> Option<Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>>
) -> Result<Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>, AlignmentError<Self, T>>
where
T: Sized,
{
if !crate::util::aligned_to::<_, T>(self.as_non_null()) {
return None;
return Err(AlignmentError::new(self));
}

// SAFETY: We just checked the alignment.
Some(unsafe { self.assume_alignment::<Aligned>() })
Ok(unsafe { self.assume_alignment::<Aligned>() })
}

/// Recalls that `self`'s referent is validly-aligned for `T`.
Expand Down
18 changes: 12 additions & 6 deletions src/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,29 +136,35 @@ impl<T> Unalign<T> {
/// not properly aligned.
///
/// If `self` does not satisfy `mem::align_of::<T>()`, then it is unsound to
/// return a reference to the wrapped `T`, and `try_deref` returns `None`.
/// return a reference to the wrapped `T`, and `try_deref` returns `Err`.
///
/// If `T: Unaligned`, then `Unalign<T>` implements [`Deref`], and callers
/// may prefer [`Deref::deref`], which is infallible.
#[inline(always)]
pub fn try_deref(&self) -> Option<&T> {
pub fn try_deref(&self) -> Result<&T, AlignmentError<&Self, T>> {
let inner = Ptr::from_ref(self).transparent_wrapper_into_inner();
inner.bikeshed_try_into_aligned().map(Ptr::as_ref)
match inner.bikeshed_try_into_aligned() {
Ok(aligned) => Ok(aligned.as_ref()),
Err(err) => Err(err.map_src(|src| src.into_unaligned().as_ref())),
}
}

/// Attempts to return a mutable reference to the wrapped `T`, failing if
/// `self` is not properly aligned.
///
/// If `self` does not satisfy `mem::align_of::<T>()`, then it is unsound to
/// return a reference to the wrapped `T`, and `try_deref_mut` returns
/// `None`.
/// `Err`.
///
/// If `T: Unaligned`, then `Unalign<T>` implements [`DerefMut`], and
/// callers may prefer [`DerefMut::deref_mut`], which is infallible.
#[inline(always)]
pub fn try_deref_mut(&mut self) -> Option<&mut T> {
pub fn try_deref_mut(&mut self) -> Result<&mut T, AlignmentError<&mut Self, T>> {
let inner = Ptr::from_mut(self).transparent_wrapper_into_inner();
inner.bikeshed_try_into_aligned().map(Ptr::as_mut)
match inner.bikeshed_try_into_aligned() {
Ok(aligned) => Ok(aligned.as_mut()),
Err(err) => Err(err.map_src(|src| src.into_unaligned().as_mut())),
}
}

/// Returns a reference to the wrapped `T` without checking alignment.
Expand Down

0 comments on commit fa30a6b

Please sign in to comment.