use core::{
any,
fmt::{
self,
Debug,
Display,
Formatter,
Pointer,
},
mem,
};
use tap::{
Pipe,
TryConv,
};
pub use wyz::comu::{
Address,
Const,
Mut,
Mutability,
NullPtrError,
};
use wyz::FmtForward;
#[inline]
pub fn check_alignment<M, T>(
addr: Address<M, T>,
) -> Result<Address<M, T>, MisalignError<T>>
where M: Mutability {
let ptr = addr.to_const();
let mask = mem::align_of::<T>() - 1;
if ptr as usize & mask != 0 {
Err(MisalignError { ptr })
}
else {
Ok(addr)
}
}
pub(crate) trait AddressExt<T> {
type Permission: Mutability;
unsafe fn force_wrap(self) -> Address<Self::Permission, T>;
}
impl<T> AddressExt<T> for *const T {
type Permission = Const;
unsafe fn force_wrap(self) -> Address<Const, T> {
self.try_conv::<Address<_, _>>()
.unwrap_or_else(|err| unreachable!("{}", err))
.pipe(check_alignment)
.unwrap_or_else(|err| unreachable!("{}", err))
}
}
impl<T> AddressExt<T> for *mut T {
type Permission = Mut;
unsafe fn force_wrap(self) -> Address<Mut, T> {
self.try_conv::<Address<_, _>>()
.unwrap_or_else(|err| unreachable!("{}", err))
.pipe(check_alignment)
.unwrap_or_else(|err| unreachable!("{}", err))
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct MisalignError<T> {
pub ptr: *const T,
}
impl<T> MisalignError<T> {
const ALIGN: usize = mem::align_of::<T>();
const CTTZ: usize = Self::ALIGN.trailing_zeros() as usize;
}
impl<T> Debug for MisalignError<T> {
#[inline]
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
fmt.debug_tuple("Misalign")
.field(&self.ptr.fmt_pointer())
.field(&Self::ALIGN)
.finish()
}
}
impl<T> Display for MisalignError<T> {
#[inline]
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(
fmt,
"Type {} requires {}-byte alignment: address ",
any::type_name::<T>(),
Self::ALIGN,
)?;
Pointer::fmt(&self.ptr, fmt)?;
write!(fmt, " must clear its least {} bits", Self::CTTZ)
}
}
unsafe impl<T> Send for MisalignError<T> {
}
unsafe impl<T> Sync for MisalignError<T> {
}
#[cfg(feature = "std")]
impl<T> std::error::Error for MisalignError<T> {
}
#[test]
#[cfg(feature = "alloc")]
fn render() {
#[cfg(not(feature = "std"))]
use alloc::format;
use core::ptr::NonNull;
assert_eq!(
format!(
"{}",
check_alignment(Address::<Const, u16>::new(
NonNull::new(0x13579 as *mut _).unwrap()
))
.unwrap_err()
),
"Type u16 requires 2-byte alignment: address 0x13579 must clear its \
least 1 bits"
);
assert_eq!(
format!(
"{}",
check_alignment(Address::<Const, u32>::new(
NonNull::new(0x13579 as *mut _).unwrap()
))
.unwrap_err()
),
"Type u32 requires 4-byte alignment: address 0x13579 must clear its \
least 2 bits"
);
}