Convert BoxBytes to/from boxed slices (#228)

* Allow converting BoxBytes to/from boxed slices of NoUninit, and from Box<str>.

* Implement From<Box<T>> for BoxBytes generically

* Minor cleanup
This commit is contained in:
zachs18 2024-03-13 00:00:32 -05:00 committed by GitHub
parent 91bbbdf9a5
commit a888e5f2c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -727,53 +727,132 @@ impl Drop for BoxBytes {
} }
} }
impl<T: NoUninit> From<Box<T>> for BoxBytes { impl<T: ?Sized + sealed::BoxBytesOf> From<Box<T>> for BoxBytes {
fn from(value: Box<T>) -> Self { fn from(value: Box<T>) -> Self {
value.box_bytes_of()
}
}
mod sealed {
use crate::{BoxBytes, PodCastError};
use alloc::boxed::Box;
pub trait BoxBytesOf {
fn box_bytes_of(self: Box<Self>) -> BoxBytes;
}
pub trait FromBoxBytes {
fn try_from_box_bytes(
bytes: BoxBytes,
) -> Result<Box<Self>, (PodCastError, BoxBytes)>;
}
}
impl<T: NoUninit> sealed::BoxBytesOf for T {
fn box_bytes_of(self: Box<Self>) -> BoxBytes {
let layout = Layout::new::<T>(); let layout = Layout::new::<T>();
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. // SAFETY: Box::into_raw() returns a non-null pointer.
let ptr = unsafe { NonNull::new_unchecked(ptr) }; let ptr = unsafe { NonNull::new_unchecked(ptr) };
BoxBytes { ptr, layout } BoxBytes { ptr, layout }
} }
} }
impl<T: NoUninit> sealed::BoxBytesOf for [T] {
fn box_bytes_of(self: Box<Self>) -> 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<Self>) -> BoxBytes {
self.into_boxed_bytes().box_bytes_of()
}
}
impl<T: AnyBitPattern> sealed::FromBoxBytes for T {
fn try_from_box_bytes(
bytes: BoxBytes,
) -> Result<Box<Self>, (PodCastError, BoxBytes)> {
let layout = Layout::new::<T>();
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<T: AnyBitPattern> sealed::FromBoxBytes for [T] {
fn try_from_box_bytes(
bytes: BoxBytes,
) -> Result<Box<Self>, (PodCastError, BoxBytes)> {
let single_layout = Layout::new::<T>();
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<T>` as `BoxBytes`. /// Re-interprets `Box<T>` as `BoxBytes`.
///
/// `T` must be either [`Sized`] and [`NoUninit`],
/// [`[U]`](slice) where `U: NoUninit`, or [`str`].
#[inline] #[inline]
pub fn box_bytes_of<T: NoUninit>(input: Box<T>) -> BoxBytes { pub fn box_bytes_of<T: sealed::BoxBytesOf + ?Sized>(input: Box<T>) -> BoxBytes {
input.into() input.box_bytes_of()
} }
/// Re-interprets `BoxBytes` as `Box<T>`. /// Re-interprets `BoxBytes` as `Box<T>`.
/// ///
/// `T` must be either [`Sized`] + [`AnyBitPattern`], or
/// [`[U]`](slice) where `U: AnyBitPattern`.
///
/// ## Panics /// ## Panics
/// ///
/// This is [`try_from_box_bytes`] but will panic on error and the input will be /// This is [`try_from_box_bytes`] but will panic on error and the input will be
/// dropped. /// dropped.
#[inline] #[inline]
pub fn from_box_bytes<T: AnyBitPattern>(input: BoxBytes) -> Box<T> { pub fn from_box_bytes<T: sealed::FromBoxBytes + ?Sized>(
input: BoxBytes,
) -> Box<T> {
try_from_box_bytes(input).map_err(|(error, _)| error).unwrap() try_from_box_bytes(input).map_err(|(error, _)| error).unwrap()
} }
/// Re-interprets `BoxBytes` as `Box<T>`. /// Re-interprets `BoxBytes` as `Box<T>`.
/// ///
/// ## Panics /// `T` must be either [`Sized`] + [`AnyBitPattern`], or
/// [`[U]`](slice) where `U: AnyBitPattern`.
/// ///
/// * If the input isn't aligned for the new type /// Returns `Err`:
/// * If the input's length isnt exactly the size of the new type /// * 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] #[inline]
pub fn try_from_box_bytes<T: AnyBitPattern>( pub fn try_from_box_bytes<T: sealed::FromBoxBytes + ?Sized>(
input: BoxBytes, input: BoxBytes,
) -> Result<Box<T>, (PodCastError, BoxBytes)> { ) -> Result<Box<T>, (PodCastError, BoxBytes)> {
let layout = Layout::new::<T>(); T::try_from_box_bytes(input)
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) })
}
} }
impl BoxBytes { impl BoxBytes {