Skip to content

Commit

Permalink
[wip] UnalignUnsized
Browse files Browse the repository at this point in the history
  • Loading branch information
jswrenn committed Oct 8, 2024
1 parent d5c2b36 commit 7d7b050
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 1 deletion.
61 changes: 61 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,16 @@ pub unsafe trait KnownLayout {
// resulting size would not fit in a `usize`.
meta.size_for_metadata(Self::LAYOUT)
}

/// Run `slf`'s destructor (if any).
///
/// # Safety
///
/// `slf`'s destructor must not already have been run. After invoking this
/// function, re-using `slf` may be undefined behavior.
#[cfg(zerocopy_unstable)]
#[doc(hidden)]
unsafe fn drop(slf: &mut UnalignUnsized<Self>);
}

/// The metadata associated with a [`KnownLayout`] type.
Expand Down Expand Up @@ -920,6 +930,57 @@ unsafe impl<T> KnownLayout for [T] {
// struct `Foo(i32, [u8])` or `(u64, Foo)`.
slc.len()
}

#[cfg(feature = "alloc")]
unsafe fn drop(slf: &mut UnalignUnsized<Self>) {
let meta = KnownLayout::pointer_to_metadata(slf);
let size = meta.size_for_metadata(Self::LAYOUT).unwrap();
let aligned = MaybeUninit::<Self>::new_boxed_uninit(meta).unwrap();
let aligned = Box::into_raw(aligned);
// SAFETY: This invocation satisfies the safety contract of
// copy_nonoverlapping [1]:
// - `slf as *mut _ as *mut u8` is valid for reads of `size` bytes
// - `aligned as *mut u8` is valid for writes of `size` bytes, because
// `aligned`'s referent is greater-than-or-equal in size to that of
// `slf`, because `aligned` might include trailing padding.
// - `src` and `dst` are, trivially, properly aligned
// - the region of memory beginning at `src` with a size of `size` bytes
// does not overlap with the region of memory beginning at `aligned`
// with the same size, because `aligned` is derived from a fresh
// allocation.
//
// [1] https://doc.rust-lang.org/1.81.0/core/ptr/fn.copy_nonoverlapping.html#safety
unsafe {
core::ptr::copy_nonoverlapping(slf as *mut _ as *mut u8, aligned as *mut u8, size);
}
// LEMMA 1: `aligned`'s referent is a bit-valid and aligned instance of
// `Self`. In its declaration, `aligned` was initialized from a
// `Box<MaybeUninit<Self>>`, which has the same alignment as `Self`.
// Then, via the preceeding `copy_nonoverlapping`, `aligned` was
// initialized with a valid instance of `Self.`
let aligned = aligned as *mut Self;
// SAFETY: This invocation satisfies the safety contract of
// `Box::from_raw` [1], because `aligned` is directly derived from
// `Box::into_raw`. By LEMMA 1, `aligned`'s referent is additionally a
// valid instance of `Self`. The `Layout`s of `Self` and
// `MaybeUninit<Self>` are the same, by contract on `MaybeUninit<Self>`.
//
// [1] Per https://doc.rust-lang.org/1.81.0/alloc/boxed/struct.Box.html#method.from_raw:
//
// It is valid to convert both ways between a `Box`` and a raw pointer
// allocated with the `Global`` allocator, given that the `Layout`
// used with the allocator is correct for the type.
unsafe {
// drop `aligned`
let _ = Box::from_raw(aligned);
}
}

#[cfg(not(feature = "alloc"))]
unsafe fn drop(_: &mut UnalignUnsized<Self>) {
// PME if T needs drop.
static_assert!(T=> !core::mem::needs_drop::<T>());
}
}

#[rustfmt::skip]
Expand Down
13 changes: 12 additions & 1 deletion src/util/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,10 @@ macro_rules! impl_known_layout {
#[inline(always)]
fn pointer_to_metadata(_ptr: *mut Self) -> () {
}

unsafe fn drop(slf: &mut crate::UnalignUnsized<Self>) {
let _ = unsafe { slf.take() };
}
}
};
};
Expand All @@ -563,7 +567,7 @@ macro_rules! impl_known_layout {
/// - It must be valid to perform an `as` cast from `*mut $repr` to `*mut $ty`,
/// and this operation must preserve referent size (ie, `size_of_val_raw`).
macro_rules! unsafe_impl_known_layout {
($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($repr:ty)] $ty:ty) => {
($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($(packed,)? $repr:ty)] $ty:ty) => {
const _: () = {
use core::ptr::NonNull;

Expand Down Expand Up @@ -597,6 +601,13 @@ macro_rules! unsafe_impl_known_layout {
let ptr = ptr as *mut $repr;
<$repr>::pointer_to_metadata(ptr)
}

unsafe fn drop(slf: &mut UnalignUnsized<Self>) {
// SAFETY: TODO
let slf = unsafe { &mut *(slf as *mut _ as *mut UnalignUnsized<$repr>) };
// SAFETY: TODO
unsafe { KnownLayout::drop(slf) }
}
}
};
};
Expand Down
2 changes: 2 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,8 @@ pub(crate) unsafe fn copy_unchecked(src: &[u8], dst: &mut [u8]) {
// bytes does not overlap with the region of memory beginning at `dst`
// with the same size, because `dst` is derived from an exclusive
// reference.
//
// [1] https://doc.rust-lang.org/1.81.0/core/ptr/fn.copy_nonoverlapping.html#safety
unsafe {
core::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len());
};
Expand Down
86 changes: 86 additions & 0 deletions src/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,92 @@ impl<T: Unaligned + Display> Display for Unalign<T> {
}
}

/// A type with no alignment requirement.
#[derive(Debug)]
#[repr(C, packed)]
pub struct UnalignUnsized<T: ?Sized>(ManuallyDrop<T>)
where
T: KnownLayout;

// SAFETY: TODO
unsafe impl<T: ?Sized + KnownLayout> KnownLayout for UnalignUnsized<T> {
#[allow(clippy::missing_inline_in_public_items)]
#[cfg_attr(coverage_nightly, coverage(off))]
fn only_derive_is_allowed_to_implement_this_trait() {}

type PointerMetadata = <T as KnownLayout>::PointerMetadata;

type MaybeUninit = UnalignUnsized<<T as KnownLayout>::MaybeUninit>;

const LAYOUT: DstLayout = DstLayout {
// The alignment is `1`, since `Self` is `repr(packed)`.
align: unsafe { NonZeroUsize::new_unchecked(1) },
// Otherwise, we retain the size of the inner `T`.
size_info: <T as KnownLayout>::LAYOUT.size_info,
};

#[inline(always)]
fn raw_from_ptr_len(
bytes: NonNull<u8>,
meta: <T as KnownLayout>::PointerMetadata,
) -> NonNull<Self> {
#[allow(clippy::as_conversions)]
let ptr = <T>::raw_from_ptr_len(bytes, meta).as_ptr() as *mut Self;
unsafe { NonNull::new_unchecked(ptr) }
}

#[inline(always)]
fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
#[allow(clippy::as_conversions)]
let ptr = ptr as *mut T;
<T>::pointer_to_metadata(ptr)
}

#[inline(always)]
unsafe fn drop(slf: &mut UnalignUnsized<Self>) {
let slf = slf.peel();
// SAFETY: By contract on the caller, this method — and, by extension,
// the below invocation — is executed at most once.
unsafe {
KnownLayout::drop(slf);
}
}
}

impl<T> UnalignUnsized<T>
where
T: KnownLayout,
{
pub(crate) unsafe fn take(&mut self) -> T {
let inner = core::ptr::addr_of!(self.0) as *const _;
// SAFETY: TODO
unsafe { core::ptr::read_unaligned(inner) }
}
}

impl<T: ?Sized> UnalignUnsized<UnalignUnsized<T>>
where
T: KnownLayout,
{
pub(crate) fn peel(&mut self) -> &mut UnalignUnsized<T> {
// SAFETY: TODO
unsafe { &mut *(self as *mut _ as *mut UnalignUnsized<T>) }
}
}

impl<T: ?Sized> Drop for UnalignUnsized<T>
where
T: KnownLayout,
{
fn drop(&mut self) {
if core::mem::needs_drop::<T>() {
// SAFETY: By contract on the caller, this method — and, by
// extension, the below invocation — is executed at most once.
unsafe { T::drop(self) }
}
}
}

/// A wrapper type to construct uninitialized instances of `T`.
///
/// `MaybeUninit` is identical to the [standard library
Expand Down

0 comments on commit 7d7b050

Please sign in to comment.