mod test;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ptr;
use core::slice;
use oct::{
FromOctets,
Immutable,
IntoOctets,
Outlay,
Zeroable,
};
#[cfg(feature = "alloc")]
use alloc::alloc::{alloc, handle_alloc_error};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
pub unsafe fn boxed_from_fn_with_metadata<T: ?Sized + Outlay, F: FnOnce(&mut [MaybeUninit<u8>])>(f: F, metadata: T::Metadata) -> Box<T> {
let Ok(layout) = T::layout_for_metadata(metadata) else {
panic!("cannot compute layout for dst with metadata `{metadata:?}`");
};
let raw = if layout.size() > 0 {
let data = unsafe { alloc(layout) };
if data.is_null() {
handle_alloc_error(layout);
};
T::ptr_from_raw_parts_mut(data, metadata)
} else {
T::ptr_dangling_with_metadata_mut(metadata)
};
{
let len = layout.size();
let data = raw.cast::<MaybeUninit<u8>>();
let buf = unsafe { slice::from_raw_parts_mut(data, len) };
f(buf)
}
unsafe { Box::from_raw(raw) }
}
#[inline]
#[must_use]
pub const fn octets_of<T: ?Sized + IntoOctets + Immutable>(value: &T) -> &[u8] {
let len = size_of_val(value);
let data = ptr::from_ref(value).cast::<u8>();
unsafe { slice::from_raw_parts(data, len) }
}
#[inline]
#[must_use]
pub const fn octets_of_mut<T: ?Sized + IntoOctets + FromOctets>(value: &mut T) -> &mut [u8] {
let len = size_of_val(value);
let data = ptr::from_mut(value).cast::<u8>();
unsafe { slice::from_raw_parts_mut(data, len) }
}
#[inline(always)]
pub const fn fill<T: ?Sized + FromOctets>(dst: &mut T, value: u8) {
unsafe { fill_unchecked(dst, value) };
}
#[inline]
#[cfg_attr(miri, track_caller)]
pub const unsafe fn fill_unchecked<T: ?Sized>(dst: &mut T, value: u8) {
let len = size_of_val(dst);
let dst = ptr::from_mut(dst).cast::<u8>();
unsafe { dst.write_bytes(value, len) };
}
#[inline(always)]
pub const fn fill_zeros<T: ?Sized + Zeroable>(dst: &mut T) {
unsafe { fill_unchecked(dst, 0); };
}
#[inline(always)]
pub const fn filled<T: FromOctets>(value: u8) -> T {
let mut buf = MaybeUninit::<T>::uninit();
fill(&mut buf, value);
unsafe { buf.assume_init() }
}
#[inline(always)]
#[must_use]
pub const fn zeroed<T: Zeroable>() -> T {
unsafe { core::mem::zeroed() }
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn zeroed_boxed_with_metadata<T: ?Sized + Zeroable + Outlay>(metadata: T::Metadata) -> Box<T> {
let f = |buf: &mut [MaybeUninit<u8>]| {
buf.fill(MaybeUninit::new(0));
};
unsafe { boxed_from_fn_with_metadata(f, metadata) }
}
#[inline]
#[must_use]
pub const fn transmute<T: IntoOctets, U: FromOctets>(value: T) -> U {
assert!(
size_of::<U>() == size_of::<T>(),
"cannot transmute types of different sizes",
);
unsafe { transmute_unchecked(value) }
}
#[inline]
#[must_use]
pub const fn transmute_copy<T: ?Sized + IntoOctets, U: FromOctets>(value: &T) -> U {
assert!(
size_of_val(value) >= size_of::<U>(),
"cannot copy transmutation of smaller type",
);
let p = ptr::from_ref(value).cast::<U>();
unsafe { p.read_unaligned() }
}
#[inline(always)]
#[must_use]
#[cfg_attr(miri, track_caller)]
pub const unsafe fn transmute_unchecked<T, U>(value: T) -> U {
#[repr(C)]
union Transmute<Src, Dst> {
src: ManuallyDrop<Src>,
dst: ManuallyDrop<Dst>,
}
let transmute = Transmute { src: ManuallyDrop::new(value) };
unsafe { ManuallyDrop::into_inner(transmute.dst) }
}
#[inline]
#[must_use]
#[track_caller]
pub fn transmute_ref<T, U>(r: &T) -> &U
where
T: ?Sized + IntoOctets + Immutable,
U: ?Sized + FromOctets + Immutable + Outlay,
{
let size = size_of_val(r);
let metadata = U::classify_size(size)
.expect("cannot transmute reference with incompatible object size");
transmute_ref_with_metadata(r, metadata)
}
#[inline]
#[must_use]
#[track_caller]
pub fn transmute_mut<T, U>(r: &mut T) -> &mut U
where
T: ?Sized + IntoOctets + FromOctets,
U: ?Sized + IntoOctets + FromOctets + Outlay,
{
let size = size_of_val(r);
let metadata = U::classify_size(size)
.expect("cannot transmute reference with incompatible object size");
transmute_mut_with_metadata(r, metadata)
}
#[inline]
#[must_use]
pub fn transmute_ref_with_metadata<T, U>(r: &T, metadata: U::Metadata) -> &U
where
T: ?Sized + IntoOctets + Immutable,
U: ?Sized + FromOctets + Immutable + Outlay,
{
let Ok(layout) = U::layout_for_metadata(metadata) else {
panic!("cannot construct layout for dst with metadata `{metadata:?}`");
};
assert!(
size_of_val(r) >= layout.size(),
"cannot construct reference from undersized octets",
);
let data = ptr::from_ref(r).cast::<u8>();
let p = U::ptr_from_raw_parts(data, metadata);
assert_eq!(
p.addr() % layout.align(),
0,
"cannot construct reference from unaligned octets",
);
unsafe { &*p }
}
#[inline]
#[must_use]
#[track_caller]
pub fn transmute_mut_with_metadata<T, U>(r: &mut T, metadata: U::Metadata) -> &mut U
where
T: ?Sized + IntoOctets + FromOctets,
U: ?Sized + IntoOctets + FromOctets + Outlay,
{
let Ok(layout) = U::layout_for_metadata(metadata) else {
panic!("cannot construct layout for dst with metadata `{metadata:?}`");
};
assert!(
size_of_val(r) >= layout.size(),
"cannot transmute reference from undersized object",
);
let data = ptr::from_mut(r).cast::<u8>();
let p = U::ptr_from_raw_parts_mut(data, metadata);
assert_eq!(
p.addr() % layout.align(),
0,
"cannot transmute reference with increasing alignment",
);
unsafe { &mut *p }
}