use crate::{
traits::{Layout, RawConvert, StableLayout},
BrinyError,
};
use core::{
mem::{self, ManuallyDrop},
ptr, slice,
};
#[inline(always)]
pub const fn reinterpret<T: Layout<U>, U: StableLayout>(input: T) -> U {
union Reinterpret<T, U> {
t: ManuallyDrop<T>,
u: ManuallyDrop<U>,
}
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
assert!(
size_of::<T>() == size_of::<U>(),
"cannot cast between types of different sizes"
);
}
let u = Reinterpret {
t: ManuallyDrop::new(input),
};
unsafe { ManuallyDrop::into_inner(u.u) }
}
#[inline(always)]
pub const fn slice_to_bytes<T: RawConvert>(slice: &[T]) -> &[u8] {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
}
let ptr = slice.as_ptr().cast::<u8>();
let len = size_of_val(slice);
unsafe { slice::from_raw_parts(ptr, len) }
}
#[inline(always)]
pub const fn slice_to_bytes_mut<T: RawConvert>(slice: &mut [T]) -> &mut [u8] {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
}
let ptr = slice.as_mut_ptr().cast::<u8>();
let len = size_of_val(slice);
unsafe { slice::from_raw_parts_mut(ptr, len) }
}
#[inline(always)]
pub const fn to_bytes<T: RawConvert>(input: &T) -> &[u8] {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
}
unsafe { slice::from_raw_parts(ptr::from_ref::<T>(input).cast::<u8>(), size_of::<T>()) }
}
#[inline(always)]
pub const fn to_bytes_mut<T: RawConvert>(input: &mut T) -> &mut [u8] {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
}
unsafe { slice::from_raw_parts_mut(ptr::from_mut::<T>(input).cast::<u8>(), size_of::<T>()) }
}
#[inline(always)]
pub fn slice_from_bytes<T: RawConvert>(bytes: &[u8]) -> Result<&[T], BrinyError> {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
}
let mut err = BrinyError::RESERVED;
let elem_size = size_of::<T>();
if !bytes.len().is_multiple_of(elem_size) {
err = err.add(BrinyError::UNALIGNED_ACCESS);
}
let ptr = bytes.as_ptr();
if !(ptr as usize).is_multiple_of(align_of::<T>()) {
err = err.add(BrinyError::UNALIGNED_ACCESS);
}
if err.is_err() {
return Err(err);
}
let len = bytes.len() / elem_size;
let t_ptr = ptr.cast();
Ok(unsafe { slice::from_raw_parts(t_ptr, len) })
}
#[inline(always)]
pub fn from_bytes<T: RawConvert>(bytes: &[u8]) -> Result<T, BrinyError> {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
}
let mut err = BrinyError::RESERVED;
if bytes.len() != size_of::<T>() {
err = err.add(BrinyError::SIZE_BOUND_FAILURE);
}
if !(bytes.as_ptr() as usize).is_multiple_of(align_of::<T>()) {
err = err.add(BrinyError::UNALIGNED_ACCESS);
}
if err.is_err() {
return Err(err);
}
let mut tmp = mem::MaybeUninit::<T>::uninit();
unsafe {
ptr::copy_nonoverlapping(
bytes.as_ptr(),
tmp.as_mut_ptr().cast::<u8>(),
size_of::<T>(),
);
Ok(tmp.assume_init())
}
}
#[inline(always)]
pub const fn from_bytes_unaligned<T: RawConvert>(bytes: &[u8]) -> Result<T, BrinyError> {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
}
if bytes.len() != size_of::<T>() {
return Err(BrinyError::SIZE_BOUND_FAILURE);
}
let mut tmp = mem::MaybeUninit::<T>::uninit();
unsafe {
ptr::copy_nonoverlapping(
bytes.as_ptr(),
tmp.as_mut_ptr().cast::<u8>(),
size_of::<T>(),
);
Ok(tmp.assume_init())
}
}
#[inline(always)]
pub const fn cast<T: Layout<U>, U: StableLayout>(input: &T) -> U {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
assert!(
size_of::<T>() == size_of::<U>(),
"cannot cast between types of different sizes"
);
assert!(
align_of::<T>() >= align_of::<U>(),
"cannot cast unaligned types"
);
}
let src_as_u = ptr::from_ref(input).cast::<U>();
unsafe { ptr::read_unaligned(src_as_u) }
}
#[inline(always)]
pub const fn cast_mut<T: Layout<U>, U: StableLayout>(input: &mut T) -> U {
const {
assert!(size_of::<T>() > 0, "cannot cast between ZSTs");
assert!(
size_of::<T>() == size_of::<U>(),
"cannot cast between types of different sizes"
);
assert!(
align_of::<T>() >= align_of::<U>(),
"cannot cast unaligned types"
);
}
let src_as_u = ptr::from_ref(input).cast::<U>();
unsafe { ptr::read_unaligned(src_as_u) }
}
#[inline(always)]
pub const fn cast_slice<T: Layout<U>, U: StableLayout>(input: &[T]) -> &[U] {
const {
assert!(
size_of::<T>() > 0 && size_of::<U>() > 0,
"cannot cast between ZSTs"
);
}
let len = size_of_val(input) / size_of::<U>();
let src_as_u = input.as_ptr().cast::<U>();
let val = unsafe { slice::from_raw_parts(src_as_u, len) };
val
}
#[inline(always)]
pub const fn cast_slice_mut<T: Layout<U>, U: StableLayout>(input: &mut [T]) -> &mut [U] {
const {
assert!(
size_of::<T>() > 0 && size_of::<U>() > 0,
"cannot cast between ZSTs"
);
}
let len = size_of_val(input) / size_of::<U>();
let src_as_u = input.as_mut_ptr().cast::<U>();
let val = unsafe { slice::from_raw_parts_mut(src_as_u, len) };
val
}
#[cfg(test)]
mod tests {
use super::*;
#[repr(C, align(8))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct ThePod {
a: u32,
b: u32,
}
unsafe impl crate::traits::RawConvert for ThePod {}
unsafe impl crate::traits::StableLayout for ThePod {}
unsafe impl crate::traits::Pod for ThePod {}
#[test]
fn stack_misaligned_slice_from_bytes() {
let mut buf = [0u8; 12];
let misaligned_ptr = buf.as_mut_ptr().wrapping_add(1);
let slice = unsafe { slice::from_raw_parts(misaligned_ptr, 8) };
let result = slice_from_bytes::<u32>(slice);
assert!(result.is_err());
}
#[test]
fn to_bytes_roundtrip() {
let val = 0x12345678u32;
let bytes = slice_to_bytes(slice::from_ref(&val));
let restored = from_bytes::<u32>(bytes).unwrap();
assert_eq!(val, restored);
}
#[test]
fn slice_from_bytes_valid() {
let arr = [1u32, 2, 3];
let bytes = to_bytes(&arr);
let restored = slice_from_bytes::<u32>(bytes).unwrap();
assert_eq!(restored, &arr);
}
#[test]
fn cast_between_same_size_types() {
let original: u32 = 0xDEADBEEF;
let casted = cast::<u32, f32>(&original);
let restored = cast::<f32, u32>(&casted);
assert_eq!(restored, original);
}
#[test]
fn reinterpret_same_size_types() {
let original: u32 = 0xDEADBEEF;
let casted = reinterpret::<u32, f32>(original);
let restored = reinterpret::<f32, u32>(casted);
assert_eq!(restored, original);
}
#[test]
fn custom_struct_bytes_roundtrip() {
let pod = ThePod {
a: 0xABCD,
b: 0x12345678,
};
let bytes = slice_to_bytes(slice::from_ref(&pod));
let restored: ThePod = from_bytes(bytes).unwrap();
assert_eq!(pod, restored);
}
#[test]
fn cast_struct_to_u64_and_back() {
assert_eq!(align_of::<ThePod>(), 8);
let pod = ThePod {
a: 0x1122,
b: 0x33445566,
};
let raw: u64 = cast(&pod);
let back: ThePod = cast(&raw);
assert_eq!(pod, back);
}
#[test]
fn from_bytes_unaligned_safety() {
let val = 42u32;
let bytes = val.to_le_bytes();
let result = from_bytes_unaligned::<u32>(&bytes).unwrap();
assert_eq!(result, val);
let mut buffer = [0u8; 8];
buffer[1..5].copy_from_slice(&val.to_le_bytes());
let slice = &buffer[1..5];
let result = from_bytes_unaligned::<u32>(slice).unwrap();
assert_eq!(result, val);
}
}