use core::{
cmp::Ordering,
fmt::{self, Debug, Display, Formatter},
hash::Hash,
mem::{self, ManuallyDrop},
ops::{Deref, DerefMut},
ptr,
};
use super::*;
#[allow(missing_debug_implementations)]
#[derive(Default, Copy)]
#[cfg_attr(
any(feature = "derive", test),
derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned)
)]
#[repr(C, packed)]
pub struct Unalign<T>(T);
#[cfg(not(any(feature = "derive", test)))]
impl_known_layout!(T => Unalign<T>);
safety_comment! {
impl_or_verify!(T => Unaligned for Unalign<T>);
impl_or_verify!(T: FromZeroes => FromZeroes for Unalign<T>);
impl_or_verify!(T: FromBytes => FromBytes for Unalign<T>);
impl_or_verify!(T: AsBytes => AsBytes for Unalign<T>);
}
impl<T: Copy> Clone for Unalign<T> {
#[inline(always)]
fn clone(&self) -> Unalign<T> {
*self
}
}
impl<T> Unalign<T> {
#[inline(always)]
pub const fn new(val: T) -> Unalign<T> {
Unalign(val)
}
#[inline(always)]
pub const fn into_inner(self) -> T {
#[repr(C)]
union Transmute<T> {
u: ManuallyDrop<Unalign<T>>,
t: ManuallyDrop<T>,
}
unsafe { ManuallyDrop::into_inner(Transmute { u: ManuallyDrop::new(self) }.t) }
}
#[inline(always)]
pub fn try_deref(&self) -> Option<&T> {
if !crate::util::aligned_to::<_, T>(self) {
return None;
}
unsafe { Some(self.deref_unchecked()) }
}
#[inline(always)]
pub fn try_deref_mut(&mut self) -> Option<&mut T> {
if !crate::util::aligned_to::<_, T>(&*self) {
return None;
}
unsafe { Some(self.deref_mut_unchecked()) }
}
#[inline(always)]
pub const unsafe fn deref_unchecked(&self) -> &T {
unsafe { mem::transmute(self) }
}
#[inline(always)]
pub unsafe fn deref_mut_unchecked(&mut self) -> &mut T {
unsafe { &mut *self.get_mut_ptr() }
}
#[inline(always)]
pub const fn get_ptr(&self) -> *const T {
ptr::addr_of!(self.0)
}
#[inline(always)]
pub fn get_mut_ptr(&mut self) -> *mut T {
ptr::addr_of_mut!(self.0)
}
#[inline(always)]
pub fn set(&mut self, t: T) {
*self = Unalign::new(t);
}
#[inline]
pub fn update<O, F: FnOnce(&mut T) -> O>(&mut self, f: F) -> O {
struct WriteBackOnDrop<T> {
copy: ManuallyDrop<T>,
slf: *mut Unalign<T>,
}
impl<T> Drop for WriteBackOnDrop<T> {
fn drop(&mut self) {
let copy = unsafe { ManuallyDrop::take(&mut self.copy) };
unsafe { ptr::write(self.slf, Unalign::new(copy)) };
}
}
let copy = unsafe { ptr::read(self) }.into_inner();
let mut write_back = WriteBackOnDrop { copy: ManuallyDrop::new(copy), slf: self };
let ret = f(&mut write_back.copy);
drop(write_back);
ret
}
}
impl<T: Copy> Unalign<T> {
#[inline(always)]
pub fn get(&self) -> T {
let Unalign(val) = *self;
val
}
}
impl<T: Unaligned> Deref for Unalign<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
unsafe { self.deref_unchecked() }
}
}
impl<T: Unaligned> DerefMut for Unalign<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
unsafe { self.deref_mut_unchecked() }
}
}
impl<T: Unaligned + PartialOrd> PartialOrd<Unalign<T>> for Unalign<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Unalign<T>) -> Option<Ordering> {
PartialOrd::partial_cmp(self.deref(), other.deref())
}
}
impl<T: Unaligned + Ord> Ord for Unalign<T> {
#[inline(always)]
fn cmp(&self, other: &Unalign<T>) -> Ordering {
Ord::cmp(self.deref(), other.deref())
}
}
impl<T: Unaligned + PartialEq> PartialEq<Unalign<T>> for Unalign<T> {
#[inline(always)]
fn eq(&self, other: &Unalign<T>) -> bool {
PartialEq::eq(self.deref(), other.deref())
}
}
impl<T: Unaligned + Eq> Eq for Unalign<T> {}
impl<T: Unaligned + Hash> Hash for Unalign<T> {
#[inline(always)]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.deref().hash(state);
}
}
impl<T: Unaligned + Debug> Debug for Unalign<T> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(self.deref(), f)
}
}
impl<T: Unaligned + Display> Display for Unalign<T> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self.deref(), f)
}
}
#[cfg(test)]
mod tests {
use core::panic::AssertUnwindSafe;
use super::*;
use crate::util::testutil::*;
#[repr(C)]
struct ForceUnalign<T, A> {
_u: u8,
t: T,
_a: [A; 0],
}
impl<T, A> ForceUnalign<T, A> {
const fn new(t: T) -> ForceUnalign<T, A> {
ForceUnalign { _u: 0, t, _a: [] }
}
}
#[test]
fn test_unalign() {
let mut u = Unalign::new(AU64(123));
assert_eq!(u.get(), AU64(123));
assert_eq!(u.into_inner(), AU64(123));
assert_eq!(u.get_ptr(), <*const _>::cast::<AU64>(&u));
assert_eq!(u.get_mut_ptr(), <*mut _>::cast::<AU64>(&mut u));
u.set(AU64(321));
assert_eq!(u.get(), AU64(321));
let mut u: Align<_, AU64> = Align::new(Unalign::new(AU64(123)));
assert_eq!(u.t.try_deref(), Some(&AU64(123)));
assert_eq!(u.t.try_deref_mut(), Some(&mut AU64(123)));
assert_eq!(unsafe { u.t.deref_unchecked() }, &AU64(123));
assert_eq!(unsafe { u.t.deref_mut_unchecked() }, &mut AU64(123));
*u.t.try_deref_mut().unwrap() = AU64(321);
assert_eq!(u.t.get(), AU64(321));
let mut u: ForceUnalign<_, AU64> = ForceUnalign::new(Unalign::new(AU64(123)));
assert_eq!(u.t.try_deref(), None);
assert_eq!(u.t.try_deref_mut(), None);
let mut u = Unalign::new(123u8);
assert_eq!(u.try_deref(), Some(&123));
assert_eq!(u.try_deref_mut(), Some(&mut 123));
assert_eq!(u.deref(), &123);
assert_eq!(u.deref_mut(), &mut 123);
*u = 21;
assert_eq!(u.get(), 21);
const _UNALIGN: Unalign<u64> = Unalign::new(0);
const _UNALIGN_PTR: *const u64 = _UNALIGN.get_ptr();
const _U64: u64 = _UNALIGN.into_inner();
#[allow(dead_code)]
const _: () = {
let x: Align<_, AU64> = Align::new(Unalign::new(AU64(123)));
let au64 = unsafe { x.t.deref_unchecked() };
match au64 {
AU64(123) => {}
_ => unreachable!(),
}
};
}
#[test]
fn test_unalign_update() {
let mut u = Unalign::new(AU64(123));
u.update(|a| a.0 += 1);
assert_eq!(u.get(), AU64(124));
let mut u = Unalign::new(Box::new(AU64(123)));
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
u.update(|a| {
a.0 += 1;
panic!();
})
}));
assert!(res.is_err());
assert_eq!(u.into_inner(), Box::new(AU64(124)));
}
}