use core::{
cell::UnsafeCell,
mem::{self, ManuallyDrop, MaybeUninit},
num::{NonZeroUsize, Wrapping},
ptr::NonNull,
sync::atomic::{
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32,
AtomicU8, AtomicUsize,
},
};
use crate::{
pointer::invariant::{self, Invariants},
Unalign,
};
#[doc(hidden)]
pub unsafe trait TransparentWrapper<I: Invariants> {
type Inner: ?Sized;
type UnsafeCellVariance;
type AlignmentVariance: AlignmentVariance<I::Alignment>;
type ValidityVariance: ValidityVariance<I::Validity>;
fn cast_into_inner(ptr: *mut Self) -> *mut Self::Inner;
fn cast_from_inner(ptr: *mut Self::Inner) -> *mut Self;
}
#[allow(unreachable_pub)]
#[doc(hidden)]
pub trait AlignmentVariance<I: invariant::Alignment> {
type Applied: invariant::Alignment;
}
#[allow(unreachable_pub)]
#[doc(hidden)]
pub trait ValidityVariance<I: invariant::Validity> {
type Applied: invariant::Validity;
}
#[doc(hidden)]
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub enum Covariant {}
impl<I: invariant::Alignment> AlignmentVariance<I> for Covariant {
type Applied = I;
}
impl<I: invariant::Validity> ValidityVariance<I> for Covariant {
type Applied = I;
}
#[doc(hidden)]
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub enum Invariant {}
impl<I: invariant::Alignment> AlignmentVariance<I> for Invariant {
type Applied = invariant::Any;
}
impl<I: invariant::Validity> ValidityVariance<I> for Invariant {
type Applied = invariant::Any;
}
unsafe impl<T, I: Invariants> TransparentWrapper<I> for MaybeUninit<T> {
type Inner = T;
type UnsafeCellVariance = Covariant;
type AlignmentVariance = Covariant;
type ValidityVariance = Invariant;
fn cast_into_inner(ptr: *mut MaybeUninit<T>) -> *mut T {
ptr.cast::<T>()
}
fn cast_from_inner(ptr: *mut T) -> *mut MaybeUninit<T> {
ptr.cast::<MaybeUninit<T>>()
}
}
unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for ManuallyDrop<T> {
type Inner = T;
type UnsafeCellVariance = Covariant;
type AlignmentVariance = Covariant;
type ValidityVariance = Covariant;
fn cast_into_inner(ptr: *mut ManuallyDrop<T>) -> *mut T {
#[allow(clippy::as_conversions)]
return ptr as *mut T;
}
fn cast_from_inner(ptr: *mut T) -> *mut ManuallyDrop<T> {
#[allow(clippy::as_conversions)]
return ptr as *mut ManuallyDrop<T>;
}
}
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Wrapping<T> {
type Inner = T;
type UnsafeCellVariance = Covariant;
type AlignmentVariance = Covariant;
type ValidityVariance = Covariant;
fn cast_into_inner(ptr: *mut Wrapping<T>) -> *mut T {
ptr.cast::<T>()
}
fn cast_from_inner(ptr: *mut T) -> *mut Wrapping<T> {
ptr.cast::<Wrapping<T>>()
}
}
unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for UnsafeCell<T> {
type Inner = T;
type UnsafeCellVariance = Invariant;
type AlignmentVariance = Covariant;
type ValidityVariance = Covariant;
fn cast_into_inner(ptr: *mut UnsafeCell<T>) -> *mut T {
#[allow(clippy::as_conversions)]
return ptr as *mut T;
}
fn cast_from_inner(ptr: *mut T) -> *mut UnsafeCell<T> {
#[allow(clippy::as_conversions)]
return ptr as *mut UnsafeCell<T>;
}
}
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Unalign<T> {
type Inner = T;
type UnsafeCellVariance = Covariant;
type AlignmentVariance = Invariant;
type ValidityVariance = Covariant;
fn cast_into_inner(ptr: *mut Unalign<T>) -> *mut T {
ptr.cast::<T>()
}
fn cast_from_inner(ptr: *mut T) -> *mut Unalign<T> {
ptr.cast::<Unalign<T>>()
}
}
macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
($(#[$attr:meta])* $(,)?) => {};
($(#[$attr:meta])* $atomic:ty [$native:ty], $($atomics:ty [$natives:ty]),* $(,)?) => {
$(#[$attr])*
unsafe impl<I: Invariants> TransparentWrapper<I> for $atomic {
unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]);
}
unsafe_impl_transparent_wrapper_for_atomic!($(#[$attr])* $($atomics [$natives],)*);
};
($(#[$attr:meta])* $tyvar:ident => $atomic:ty [$native:ty]) => {
$(#[$attr])*
unsafe impl<$tyvar, I: Invariants> TransparentWrapper<I> for $atomic {
unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]);
}
};
(@inner $atomic:ty [$native:ty]) => {
type Inner = UnsafeCell<$native>;
type UnsafeCellVariance = Covariant;
type AlignmentVariance = Invariant;
type ValidityVariance = Covariant;
fn cast_into_inner(ptr: *mut $atomic) -> *mut UnsafeCell<$native> {
ptr.cast::<UnsafeCell<$native>>()
}
fn cast_from_inner(ptr: *mut UnsafeCell<$native>) -> *mut $atomic {
ptr.cast::<$atomic>()
}
};
}
safety_comment! {
unsafe_impl_transparent_wrapper_for_atomic!(T => AtomicPtr<T> [*mut T]);
unsafe_impl_transparent_wrapper_for_atomic!(
AtomicBool [bool],
AtomicI16 [i16], AtomicI32 [i32], AtomicI8 [i8], AtomicIsize [isize],
AtomicU16 [u16], AtomicU32 [u32], AtomicU8 [u8], AtomicUsize [usize],
);
#[cfg(not(target_arch = "powerpc"))]
unsafe_impl_transparent_wrapper_for_atomic!(
#[cfg_attr(doc_cfg, doc(cfg(not(target_arch = "powerpc"))))]
core::sync::atomic::AtomicI64 [i64],
core::sync::atomic::AtomicU64 [u64],
);
}
pub(crate) trait AsAddress {
fn addr(self) -> usize;
}
impl<'a, T: ?Sized> AsAddress for &'a T {
#[inline(always)]
fn addr(self) -> usize {
let ptr: *const T = self;
AsAddress::addr(ptr)
}
}
impl<'a, T: ?Sized> AsAddress for &'a mut T {
#[inline(always)]
fn addr(self) -> usize {
let ptr: *const T = self;
AsAddress::addr(ptr)
}
}
impl<T: ?Sized> AsAddress for NonNull<T> {
#[inline(always)]
fn addr(self) -> usize {
AsAddress::addr(self.as_ptr())
}
}
impl<T: ?Sized> AsAddress for *const T {
#[inline(always)]
fn addr(self) -> usize {
#[allow(clippy::as_conversions)]
#[cfg_attr(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, allow(lossy_provenance_casts))]
return self.cast::<()>() as usize;
}
}
impl<T: ?Sized> AsAddress for *mut T {
#[inline(always)]
fn addr(self) -> usize {
let ptr: *const T = self;
AsAddress::addr(ptr)
}
}
#[inline(always)]
pub(crate) fn aligned_to<T: AsAddress, U>(t: T) -> bool {
#[allow(clippy::arithmetic_side_effects)]
let remainder = t.addr() % mem::align_of::<U>();
remainder == 0
}
pub(crate) const fn padding_needed_for(len: usize, align: NonZeroUsize) -> usize {
#[allow(clippy::arithmetic_side_effects)]
let mask = align.get() - 1;
!(len.wrapping_sub(1)) & mask
}
#[inline(always)]
pub(crate) const fn round_down_to_next_multiple_of_alignment(
n: usize,
align: NonZeroUsize,
) -> usize {
let align = align.get();
#[cfg(zerocopy_panic_in_const)]
debug_assert!(align.is_power_of_two());
#[allow(clippy::arithmetic_side_effects)]
let mask = !(align - 1);
n & mask
}
pub(crate) const fn max(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize {
if a.get() < b.get() {
b
} else {
a
}
}
pub(crate) const fn min(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize {
if a.get() > b.get() {
b
} else {
a
}
}
pub(crate) fn assert_dst_is_not_zst<T>()
where
T: crate::KnownLayout + ?Sized,
{
trait ConstAssert: crate::KnownLayout {
const DST_IS_NOT_ZST: bool = {
let dst_is_zst = match Self::LAYOUT.size_info {
crate::SizeInfo::Sized { .. } => false,
crate::SizeInfo::SliceDst(crate::TrailingSliceLayout { elem_size, .. }) => {
elem_size == 0
}
};
const_assert!(!dst_is_zst);
!dst_is_zst
};
}
impl<T: crate::KnownLayout + ?Sized> ConstAssert for T {}
const_assert!(<T as ConstAssert>::DST_IS_NOT_ZST);
}
pub(crate) mod polyfills {
use core::ptr::{self, NonNull};
#[allow(unused)]
pub(crate) trait NonNullExt<T> {
fn slice_from_raw_parts(data: Self, len: usize) -> NonNull<[T]>;
}
impl<T> NonNullExt<T> for NonNull<T> {
#[inline(always)]
fn slice_from_raw_parts(data: Self, len: usize) -> NonNull<[T]> {
let ptr = ptr::slice_from_raw_parts_mut(data.as_ptr(), len);
unsafe { NonNull::new_unchecked(ptr) }
}
}
#[allow(unused)]
pub(crate) trait NumExt {
unsafe fn unchecked_sub(self, rhs: Self) -> Self;
}
impl NumExt for usize {
unsafe fn unchecked_sub(self, rhs: usize) -> usize {
match self.checked_sub(rhs) {
Some(x) => x,
None => {
unsafe { core::hint::unreachable_unchecked() }
}
}
}
}
}
#[cfg(test)]
pub(crate) mod testutil {
use crate::*;
#[derive(Default)]
pub(crate) struct Align<T, A> {
pub(crate) t: T,
_a: [A; 0],
}
impl<T: Default, A> Align<T, A> {
pub(crate) fn set_default(&mut self) {
self.t = T::default();
}
}
impl<T, A> Align<T, A> {
pub(crate) const fn new(t: T) -> Align<T, A> {
Align { t, _a: [] }
}
}
#[derive(
KnownLayout,
Immutable,
FromBytes,
IntoBytes,
Eq,
PartialEq,
Ord,
PartialOrd,
Default,
Debug,
Copy,
Clone,
)]
#[repr(C, align(8))]
pub(crate) struct AU64(pub(crate) u64);
impl AU64 {
pub(crate) fn to_bytes(self) -> [u8; 8] {
crate::transmute!(self)
}
}
impl Display for AU64 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
#[derive(Immutable, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone)]
#[repr(C)]
pub(crate) struct Nested<T, U: ?Sized> {
_t: T,
_u: U,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_round_down_to_next_multiple_of_alignment() {
fn alt_impl(n: usize, align: NonZeroUsize) -> usize {
let mul = n / align.get();
mul * align.get()
}
for align in [1, 2, 4, 8, 16] {
for n in 0..256 {
let align = NonZeroUsize::new(align).unwrap();
let want = alt_impl(n, align);
let got = round_down_to_next_multiple_of_alignment(n, align);
assert_eq!(got, want, "round_down_to_next_multiple_of_alignment({}, {})", n, align);
}
}
}
#[rustversion::since(1.57.0)]
#[test]
#[should_panic]
fn test_round_down_to_next_multiple_of_alignment_panic_in_const() {
round_down_to_next_multiple_of_alignment(0, NonZeroUsize::new(3).unwrap());
}
}
#[cfg(kani)]
mod proofs {
use super::*;
#[kani::proof]
fn prove_round_down_to_next_multiple_of_alignment() {
fn model_impl(n: usize, align: NonZeroUsize) -> usize {
assert!(align.get().is_power_of_two());
let mul = n / align.get();
mul * align.get()
}
let align: NonZeroUsize = kani::any();
kani::assume(align.get().is_power_of_two());
let n: usize = kani::any();
let expected = model_impl(n, align);
let actual = round_down_to_next_multiple_of_alignment(n, align);
assert_eq!(expected, actual, "round_down_to_next_multiple_of_alignment({}, {})", n, align);
}
#[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)]
#[kani::proof]
fn prove_padding_needed_for() {
fn model_impl(len: usize, align: NonZeroUsize) -> usize {
let padded = len.next_multiple_of(align.get());
let padding = padded - len;
padding
}
let align: NonZeroUsize = kani::any();
kani::assume(align.get().is_power_of_two());
let len: usize = kani::any();
kani::assume(len <= isize::MAX as usize);
kani::assume(align.get() < 1 << 29);
let expected = model_impl(len, align);
let actual = padding_needed_for(len, align);
assert_eq!(expected, actual, "padding_needed_for({}, {})", len, align);
let padded_len = actual + len;
assert_eq!(padded_len % align, 0);
assert!(padded_len / align >= len / align);
}
}