mirror of
https://github.com/Lokathor/bytemuck.git
synced 2024-11-21 22:32:23 +00:00
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:
parent
91bbbdf9a5
commit
a888e5f2c0
@ -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 {
|
||||
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 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<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`.
|
||||
///
|
||||
/// `T` must be either [`Sized`] and [`NoUninit`],
|
||||
/// [`[U]`](slice) where `U: NoUninit`, or [`str`].
|
||||
#[inline]
|
||||
pub fn box_bytes_of<T: NoUninit>(input: Box<T>) -> BoxBytes {
|
||||
input.into()
|
||||
pub fn box_bytes_of<T: sealed::BoxBytesOf + ?Sized>(input: Box<T>) -> BoxBytes {
|
||||
input.box_bytes_of()
|
||||
}
|
||||
|
||||
/// Re-interprets `BoxBytes` as `Box<T>`.
|
||||
///
|
||||
/// `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<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()
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// * 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<T: AnyBitPattern>(
|
||||
pub fn try_from_box_bytes<T: sealed::FromBoxBytes + ?Sized>(
|
||||
input: BoxBytes,
|
||||
) -> Result<Box<T>, (PodCastError, BoxBytes)> {
|
||||
let layout = Layout::new::<T>();
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user