use core::{fmt, hash::Hash};
use super::*;
#[allow(missing_debug_implementations)]
#[derive(Default, Copy)]
#[cfg_attr(any(feature = "derive", test), derive(Immutable, FromBytes, IntoBytes, Unaligned))]
#[repr(C, packed)]
pub struct Unalign<T>(T);
impl_known_layout!(T => Unalign<T>);
#[allow(unused_unsafe)] const _: () = unsafe {
impl_or_verify!(T => Unaligned for Unalign<T>);
impl_or_verify!(T: Immutable => Immutable for Unalign<T>);
impl_or_verify!(
T: TryFromBytes => TryFromBytes for Unalign<T>;
|c| T::is_bit_valid(c.transmute())
);
impl_or_verify!(T: FromZeros => FromZeros for Unalign<T>);
impl_or_verify!(T: FromBytes => FromBytes for Unalign<T>);
impl_or_verify!(T: IntoBytes => IntoBytes 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 {
unsafe { crate::util::transmute_unchecked(self) }
}
#[inline(always)]
pub fn try_deref(&self) -> Result<&T, AlignmentError<&Self, T>> {
let inner = Ptr::from_ref(self).transmute();
match inner.try_into_aligned() {
Ok(aligned) => Ok(aligned.as_ref()),
Err(err) => Err(err.map_src(|src| src.into_unalign().as_ref())),
}
}
#[inline(always)]
pub fn try_deref_mut(&mut self) -> Result<&mut T, AlignmentError<&mut Self, T>> {
let inner = Ptr::from_mut(self).transmute::<_, _, (_, (_, _))>();
match inner.try_into_aligned() {
Ok(aligned) => Ok(aligned.as_mut()),
Err(err) => Err(err.map_src(|src| src.into_unalign().as_mut())),
}
}
#[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 {
if mem::align_of::<T>() == 1 {
let t = unsafe { self.deref_mut_unchecked() };
return f(t);
}
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 {
Ptr::from_ref(self).transmute().bikeshed_recall_aligned().as_ref()
}
}
impl<T: Unaligned> DerefMut for Unalign<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
Ptr::from_mut(self).transmute::<_, _, (_, (_, _))>().bikeshed_recall_aligned().as_mut()
}
}
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)
}
}
#[repr(transparent)]
#[doc(hidden)]
pub struct MaybeUninit<T: ?Sized + KnownLayout>(
) `T::MaybeUninit` has `T::LAYOUT` identical to `T`,
T::MaybeUninit,
);
#[doc(hidden)]
impl<T: ?Sized + KnownLayout> MaybeUninit<T> {
#[inline(always)]
pub fn new(val: T) -> Self
where
T: Sized,
Self: Sized,
{
unsafe { crate::util::transmute_unchecked(val) }
}
#[must_use]
#[inline(always)]
pub fn uninit() -> Self
where
T: Sized,
Self: Sized,
{
let uninit = CoreMaybeUninit::<T>::uninit();
unsafe { crate::util::transmute_unchecked(uninit) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn new_boxed_uninit(meta: T::PointerMetadata) -> Result<Box<Self>, AllocError> {
unsafe { crate::util::new_box(meta, alloc::alloc::alloc) }
}
#[inline(always)]
pub unsafe fn assume_init(self) -> T
where
T: Sized,
Self: Sized,
{
unsafe { crate::util::transmute_unchecked(self) }
}
}
impl<T: ?Sized + KnownLayout> fmt::Debug for MaybeUninit<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(core::any::type_name::<Self>())
}
}
#[cfg(test)]
mod tests {
use core::panic::AssertUnwindSafe;
use super::*;
use crate::util::testutil::*;
#[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().unwrap(), &AU64(123));
assert_eq!(u.t.try_deref_mut().unwrap(), &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!(matches!(u.t.try_deref(), Err(AlignmentError { .. })));
assert!(matches!(u.t.try_deref_mut(), Err(AlignmentError { .. })));
let mut u = Unalign::new(123u8);
assert_eq!(u.try_deref(), Ok(&123));
assert_eq!(u.try_deref_mut(), Ok(&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) => {}
_ => const_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)));
let mut u = Unalign::new([0u8, 1]);
u.update(|a| a[0] += 1);
assert_eq!(u.get(), [1u8, 1]);
}
#[test]
fn test_unalign_copy_clone() {
let u = ForceUnalign::<_, AU64>::new(Unalign::new(AU64(123)));
#[allow(clippy::clone_on_copy)]
let v = u.t.clone();
let w = u.t;
assert_eq!(u.t.get(), v.get());
assert_eq!(u.t.get(), w.get());
assert_eq!(v.get(), w.get());
}
#[test]
fn test_unalign_trait_impls() {
let zero = Unalign::new(0u8);
let one = Unalign::new(1u8);
assert!(zero < one);
assert_eq!(PartialOrd::partial_cmp(&zero, &one), Some(Ordering::Less));
assert_eq!(Ord::cmp(&zero, &one), Ordering::Less);
assert_ne!(zero, one);
assert_eq!(zero, zero);
assert!(!PartialEq::eq(&zero, &one));
assert!(PartialEq::eq(&zero, &zero));
fn hash<T: Hash>(t: &T) -> u64 {
let mut h = std::collections::hash_map::DefaultHasher::new();
t.hash(&mut h);
h.finish()
}
assert_eq!(hash(&zero), hash(&0u8));
assert_eq!(hash(&one), hash(&1u8));
assert_eq!(format!("{:?}", zero), format!("{:?}", 0u8));
assert_eq!(format!("{:?}", one), format!("{:?}", 1u8));
assert_eq!(format!("{}", zero), format!("{}", 0u8));
assert_eq!(format!("{}", one), format!("{}", 1u8));
}
#[test]
#[allow(clippy::as_conversions)]
fn test_maybe_uninit() {
{
let input = 42;
let uninit = MaybeUninit::new(input);
let output = unsafe { uninit.assume_init() };
assert_eq!(input, output);
}
{
let input = 42;
let uninit = MaybeUninit::new(&input);
let output = unsafe { uninit.assume_init() };
assert_eq!(&input as *const _, output as *const _);
assert_eq!(input, *output);
}
{
let input = [1, 2, 3, 4];
let uninit = MaybeUninit::new(&input[..]);
let output = unsafe { uninit.assume_init() };
assert_eq!(&input[..] as *const _, output as *const _);
assert_eq!(input, *output);
}
}
}