use super::Pod;
use core::mem::{align_of, size_of};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PodCastError {
TargetAlignmentMismatch,
SizeMismatch,
}
impl core::fmt::Display for PodCastError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let msg = match self {
Self::TargetAlignmentMismatch => "source is misaligned for the target type",
Self::SizeMismatch => "source byte length is not a multiple of the target size",
};
f.write_str(msg)
}
}
impl core::error::Error for PodCastError {}
#[inline]
#[must_use]
pub fn bytes_of<T: Pod>(value: &T) -> &[u8] {
unsafe { core::slice::from_raw_parts((value as *const T).cast::<u8>(), size_of::<T>()) }
}
#[inline]
pub fn bytes_of_mut<T: Pod>(value: &mut T) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut((value as *mut T).cast::<u8>(), size_of::<T>()) }
}
#[inline]
fn cast_len<A, B>(src_len: usize) -> Result<usize, PodCastError> {
let src_bytes = size_of::<A>() * src_len;
if size_of::<B>() == 0 {
return Ok(0);
}
if !src_bytes.is_multiple_of(size_of::<B>()) {
return Err(PodCastError::SizeMismatch);
}
Ok(src_bytes / size_of::<B>())
}
pub fn try_cast_slice<A: Pod, B: Pod>(a: &[A]) -> Result<&[B], PodCastError> {
let new_len = cast_len::<A, B>(a.len())?;
if !(a.as_ptr() as usize).is_multiple_of(align_of::<B>()) {
return Err(PodCastError::TargetAlignmentMismatch);
}
Ok(unsafe { core::slice::from_raw_parts(a.as_ptr().cast::<B>(), new_len) })
}
pub fn try_cast_slice_mut<A: Pod, B: Pod>(a: &mut [A]) -> Result<&mut [B], PodCastError> {
let new_len = cast_len::<A, B>(a.len())?;
if !(a.as_ptr() as usize).is_multiple_of(align_of::<B>()) {
return Err(PodCastError::TargetAlignmentMismatch);
}
Ok(unsafe { core::slice::from_raw_parts_mut(a.as_mut_ptr().cast::<B>(), new_len) })
}
#[inline]
#[must_use]
pub fn cast_slice<A: Pod, B: Pod>(a: &[A]) -> &[B] {
match try_cast_slice(a) {
Ok(b) => b,
Err(e) => panic!("cast_slice: {e}"),
}
}
#[inline]
pub fn cast_slice_mut<A: Pod, B: Pod>(a: &mut [A]) -> &mut [B] {
match try_cast_slice_mut(a) {
Ok(b) => b,
Err(e) => panic!("cast_slice_mut: {e}"),
}
}
pub fn try_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&T, PodCastError> {
if bytes.len() != size_of::<T>() {
return Err(PodCastError::SizeMismatch);
}
if !(bytes.as_ptr() as usize).is_multiple_of(align_of::<T>()) {
return Err(PodCastError::TargetAlignmentMismatch);
}
Ok(unsafe { &*bytes.as_ptr().cast::<T>() })
}
#[inline]
#[must_use]
pub fn from_bytes<T: Pod>(bytes: &[u8]) -> &T {
match try_from_bytes(bytes) {
Ok(t) => t,
Err(e) => panic!("from_bytes: {e}"),
}
}
#[inline]
#[must_use]
pub fn pod_read_unaligned<T: Pod>(bytes: &[u8]) -> T {
assert!(
bytes.len() >= size_of::<T>(),
"pod_read_unaligned: buffer shorter than the target type",
);
unsafe { bytes.as_ptr().cast::<T>().read_unaligned() }
}