use core::{fmt, hash::Hash};
use super::*;
use crate::pointer::{invariant::Valid, SizeEq, TransmuteFrom};
#[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>);
#[cfg_attr(
all(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, any(feature = "derive", test)),
expect(unused_unsafe)
)]
#[cfg_attr(
all(
not(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS),
any(feature = "derive", test)
),
allow(unused_unsafe)
)]
#[allow(clippy::multiple_unsafe_ops_per_block)]
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::<_, _, BecauseImmutable>())
);
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(
#[inline(always)]
|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>())
}
}
#[allow(unreachable_pub)] #[doc(hidden)]
pub use read_only_def::*;
mod read_only_def {
#[repr(transparent)]
pub struct ReadOnly<T: ?Sized> {
inner: T,
}
impl<T> ReadOnly<T> {
#[must_use]
#[inline(always)]
pub const fn new(t: T) -> ReadOnly<T> {
ReadOnly { inner: t }
}
#[must_use]
#[inline(always)]
pub fn into_inner(r: ReadOnly<T>) -> T {
r.inner
}
}
impl<T: ?Sized> ReadOnly<T> {
#[inline(always)]
pub(crate) fn as_mut(r: &mut ReadOnly<T>) -> &mut T {
&mut r.inner
}
pub(crate) const unsafe fn as_ref_unchecked(r: &ReadOnly<T>) -> &T {
&r.inner
}
}
}
const _: () = unsafe {
unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ReadOnly<T>);
};
#[allow(clippy::multiple_unsafe_ops_per_block)]
const _: () = unsafe {
unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ReadOnly<T>);
unsafe_impl!(
T: ?Sized + TryFromBytes => TryFromBytes for ReadOnly<T>;
|c| T::is_bit_valid(c.cast::<_, <ReadOnly<T> as SizeEq<ReadOnly<ReadOnly<T>>>>::CastFrom, _>())
);
unsafe_impl!(T: ?Sized + FromZeros => FromZeros for ReadOnly<T>);
unsafe_impl!(T: ?Sized + FromBytes => FromBytes for ReadOnly<T>);
unsafe_impl!(T: ?Sized + IntoBytes => IntoBytes for ReadOnly<T>);
};
const _: () = unsafe {
unsafe_impl!(T: ?Sized => Immutable for ReadOnly<T>);
};
const _: () = {
use crate::pointer::cast::CastExact;
define_cast!(unsafe { pub CastFromReadOnly<T: ?Sized> = ReadOnly<T> => T});
unsafe impl<T: ?Sized> CastExact<ReadOnly<T>, T> for CastFromReadOnly {}
define_cast!(unsafe { pub CastToReadOnly<T: ?Sized> = T => ReadOnly<T>});
unsafe impl<T: ?Sized> CastExact<T, ReadOnly<T>> for CastToReadOnly {}
impl<T: ?Sized> SizeEq<ReadOnly<T>> for T {
type CastFrom = CastFromReadOnly;
}
impl<T: ?Sized> SizeEq<T> for ReadOnly<T> {
type CastFrom = CastToReadOnly;
}
};
unsafe impl<T: ?Sized> TransmuteFrom<T, Valid, Valid> for ReadOnly<T> {}
unsafe impl<T: ?Sized> TransmuteFrom<ReadOnly<T>, Valid, Valid> for T {}
impl<'a, T: ?Sized + Immutable> From<&'a T> for &'a ReadOnly<T> {
#[inline(always)]
fn from(t: &'a T) -> &'a ReadOnly<T> {
let ro = Ptr::from_ref(t).transmute::<_, _, (_, _)>();
let ro = unsafe { ro.assume_alignment() };
ro.as_ref()
}
}
impl<T: ?Sized + Immutable> Deref for ReadOnly<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe { ReadOnly::as_ref_unchecked(self) }
}
}
impl<T: ?Sized + Immutable> DerefMut for ReadOnly<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
ReadOnly::as_mut(self)
}
}
impl<T: ?Sized + Immutable + Debug> Debug for ReadOnly<T> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.deref().fmt(f)
}
}
unsafe impl<T: HasTag + ?Sized> HasTag for ReadOnly<T> {
#[allow(clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized,
{
}
type Tag = T::Tag;
type ProjectToTag = crate::pointer::cast::TransitiveProject<
T,
<T as SizeEq<ReadOnly<T>>>::CastFrom,
T::ProjectToTag,
>;
}
unsafe impl<T, Field, const VARIANT_ID: i128, const FIELD_ID: i128>
HasField<Field, VARIANT_ID, FIELD_ID> for ReadOnly<T>
where
T: HasField<Field, VARIANT_ID, FIELD_ID> + ?Sized,
{
#[allow(clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized,
{
}
type Type = ReadOnly<T::Type>;
#[inline(always)]
fn project(slf: PtrInner<'_, Self>) -> *mut ReadOnly<T::Type> {
slf.project::<_, <T as SizeEq<ReadOnly<T>>>::CastFrom>()
.project::<_, crate::pointer::cast::Projection<Field, VARIANT_ID, FIELD_ID>>()
.project::<_, <ReadOnly<T::Type> as SizeEq<T::Type>>::CastFrom>()
.as_non_null()
.as_ptr()
}
}
unsafe impl<T, Field, I, const VARIANT_ID: i128, const FIELD_ID: i128>
ProjectField<Field, I, VARIANT_ID, FIELD_ID> for ReadOnly<T>
where
T: ProjectField<Field, I, VARIANT_ID, FIELD_ID> + ?Sized,
I: invariant::Invariants,
{
#[allow(clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized,
{
}
type Invariants = T::Invariants;
type Error = T::Error;
#[inline(always)]
fn is_projectable<'a>(ptr: Ptr<'a, Self::Tag, I>) -> Result<(), Self::Error> {
T::is_projectable(ptr)
}
}
#[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);
}
}
#[test]
fn test_maybe_uninit_uninit() {
let _uninit = MaybeUninit::<u8>::uninit();
}
#[test]
#[cfg(feature = "alloc")]
fn test_maybe_uninit_new_boxed_uninit() {
let _boxed = MaybeUninit::<u8>::new_boxed_uninit(()).unwrap();
}
#[test]
fn test_maybe_uninit_debug() {
let uninit = MaybeUninit::<u8>::uninit();
assert!(format!("{:?}", uninit).contains("MaybeUninit"));
}
}