From a888e5f2c009dd5ef048185fb2a766ae173f3c4d Mon Sep 17 00:00:00 2001 From: zachs18 <8355914+zachs18@users.noreply.github.com> Date: Wed, 13 Mar 2024 00:00:32 -0500 Subject: [PATCH] Convert `BoxBytes` to/from boxed slices (#228) * Allow converting BoxBytes to/from boxed slices of NoUninit, and from Box. * Implement From> for BoxBytes generically * Minor cleanup --- src/allocation.rs | 117 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 19 deletions(-) diff --git a/src/allocation.rs b/src/allocation.rs index 2000674..fe39e81 100644 --- a/src/allocation.rs +++ b/src/allocation.rs @@ -727,53 +727,132 @@ impl Drop for BoxBytes { } } -impl From> for BoxBytes { +impl From> for BoxBytes { fn from(value: Box) -> Self { + value.box_bytes_of() + } +} + +mod sealed { + use crate::{BoxBytes, PodCastError}; + use alloc::boxed::Box; + + pub trait BoxBytesOf { + fn box_bytes_of(self: Box) -> BoxBytes; + } + + pub trait FromBoxBytes { + fn try_from_box_bytes( + bytes: BoxBytes, + ) -> Result, (PodCastError, BoxBytes)>; + } +} + +impl sealed::BoxBytesOf for T { + fn box_bytes_of(self: Box) -> BoxBytes { let layout = Layout::new::(); - let ptr = Box::into_raw(value) as *mut u8; + let ptr = Box::into_raw(self) as *mut u8; // SAFETY: Box::into_raw() returns a non-null pointer. let ptr = unsafe { NonNull::new_unchecked(ptr) }; BoxBytes { ptr, layout } } } +impl sealed::BoxBytesOf for [T] { + fn box_bytes_of(self: Box) -> BoxBytes { + let layout = Layout::for_value::<[T]>(&self); + let ptr = Box::into_raw(self) as *mut u8; + // SAFETY: Box::into_raw() returns a non-null pointer. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + BoxBytes { ptr, layout } + } +} + +impl sealed::BoxBytesOf for str { + fn box_bytes_of(self: Box) -> BoxBytes { + self.into_boxed_bytes().box_bytes_of() + } +} + +impl sealed::FromBoxBytes for T { + fn try_from_box_bytes( + bytes: BoxBytes, + ) -> Result, (PodCastError, BoxBytes)> { + let layout = Layout::new::(); + if bytes.layout.align() != layout.align() { + Err((PodCastError::AlignmentMismatch, bytes)) + } else if bytes.layout.size() != layout.size() { + Err((PodCastError::SizeMismatch, bytes)) + } else { + let (ptr, _) = bytes.into_raw_parts(); + // SAFETY: See BoxBytes type invariant. + Ok(unsafe { Box::from_raw(ptr.as_ptr() as *mut T) }) + } + } +} + +impl sealed::FromBoxBytes for [T] { + fn try_from_box_bytes( + bytes: BoxBytes, + ) -> Result, (PodCastError, BoxBytes)> { + let single_layout = Layout::new::(); + if bytes.layout.align() != single_layout.align() { + Err((PodCastError::AlignmentMismatch, bytes)) + } else if single_layout.size() == 0 { + Err((PodCastError::SizeMismatch, bytes)) + } else if bytes.layout.size() % single_layout.size() != 0 { + Err((PodCastError::OutputSliceWouldHaveSlop, bytes)) + } else { + let (ptr, layout) = bytes.into_raw_parts(); + let length = layout.size() / single_layout.size(); + let ptr = + core::ptr::slice_from_raw_parts_mut(ptr.as_ptr() as *mut T, length); + // SAFETY: See BoxBytes type invariant. + Ok(unsafe { Box::from_raw(ptr) }) + } + } +} + /// Re-interprets `Box` as `BoxBytes`. +/// +/// `T` must be either [`Sized`] and [`NoUninit`], +/// [`[U]`](slice) where `U: NoUninit`, or [`str`]. #[inline] -pub fn box_bytes_of(input: Box) -> BoxBytes { - input.into() +pub fn box_bytes_of(input: Box) -> BoxBytes { + input.box_bytes_of() } /// Re-interprets `BoxBytes` as `Box`. /// +/// `T` must be either [`Sized`] + [`AnyBitPattern`], or +/// [`[U]`](slice) where `U: AnyBitPattern`. +/// /// ## Panics /// /// This is [`try_from_box_bytes`] but will panic on error and the input will be /// dropped. #[inline] -pub fn from_box_bytes(input: BoxBytes) -> Box { +pub fn from_box_bytes( + input: BoxBytes, +) -> Box { try_from_box_bytes(input).map_err(|(error, _)| error).unwrap() } /// Re-interprets `BoxBytes` as `Box`. /// -/// ## Panics +/// `T` must be either [`Sized`] + [`AnyBitPattern`], or +/// [`[U]`](slice) where `U: AnyBitPattern`. /// -/// * If the input isn't aligned for the new type -/// * If the input's length isn’t exactly the size of the new type +/// Returns `Err`: +/// * If the input isn't aligned for `T`. +/// * If `T: Sized` and the input's length isn't exactly the size of `T`. +/// * If `T = [U]` and the input's length isn't exactly a multiple of the size +/// of `U`. #[inline] -pub fn try_from_box_bytes( +pub fn try_from_box_bytes( input: BoxBytes, ) -> Result, (PodCastError, BoxBytes)> { - let layout = Layout::new::(); - if input.layout.align() != layout.align() { - return Err((PodCastError::AlignmentMismatch, input)); - } else if input.layout.size() != layout.size() { - return Err((PodCastError::SizeMismatch, input)); - } else { - let (ptr, _) = input.into_raw_parts(); - // SAFETY: See type invariant. - Ok(unsafe { Box::from_raw(ptr.as_ptr() as *mut T) }) - } + T::try_from_box_bytes(input) } impl BoxBytes {