use core::mem;
use core::ptr::{self, NonNull};
use core::sync::atomic::AtomicU32;
use allocator_api2::alloc::Allocator;
use ptr_meta::Pointee;
#[inline]
const fn meta_bytes<T: ?Sized + Pointee>() -> usize {
mem::size_of::<<T as Pointee>::Metadata>()
}
const STRONG_BYTES: usize = mem::size_of::<AtomicU32>();
const STRONG_ALIGN: usize = mem::align_of::<AtomicU32>();
#[inline]
pub(crate) const fn strong_prefix_bytes_for(value_align: usize, meta: usize) -> usize {
(STRONG_BYTES + meta).next_multiple_of(value_align)
}
#[inline]
pub(crate) const fn arc_block_align(value_align: usize) -> usize {
if value_align >= STRONG_ALIGN { value_align } else { STRONG_ALIGN }
}
pub(crate) trait Strong {
type Ptr<T: ?Sized + Pointee, A: Allocator + Clone>;
fn block_align(value_align: usize) -> usize;
unsafe fn write_one(base: *mut u8);
unsafe fn adopt<T: ?Sized + Pointee, A: Allocator + Clone>(thin: NonNull<u8>) -> Self::Ptr<T, A>;
}
pub(crate) enum AtomicStrong {}
pub(crate) enum LocalStrong {}
impl Strong for AtomicStrong {
type Ptr<T: ?Sized + Pointee, A: Allocator + Clone> = crate::Arc<T, A>;
#[inline]
fn block_align(value_align: usize) -> usize {
arc_block_align(value_align)
}
#[inline]
#[allow(
clippy::cast_ptr_alignment,
reason = "block_align floors at STRONG_ALIGN, so `base` is aligned for AtomicU32"
)]
unsafe fn write_one(base: *mut u8) {
unsafe { base.cast::<AtomicU32>().write(AtomicU32::new(1)) };
}
#[inline]
unsafe fn adopt<T: ?Sized + Pointee, A: Allocator + Clone>(thin: NonNull<u8>) -> crate::Arc<T, A> {
unsafe { crate::Arc::from_raw(thin) }
}
}
impl Strong for LocalStrong {
type Ptr<T: ?Sized + Pointee, A: Allocator + Clone> = crate::Rc<T, A>;
#[inline]
fn block_align(value_align: usize) -> usize {
value_align
}
#[inline]
unsafe fn write_one(base: *mut u8) {
unsafe { ptr::write_unaligned(base.cast::<u32>(), 1) };
}
#[inline]
unsafe fn adopt<T: ?Sized + Pointee, A: Allocator + Clone>(thin: NonNull<u8>) -> crate::Rc<T, A> {
unsafe { crate::Rc::from_raw(thin) }
}
}
#[inline]
pub(crate) unsafe fn local_strong_ptr<T: ?Sized + Pointee>(value_ptr: NonNull<u8>, value_align: usize) -> *mut u32 {
let prefix = strong_prefix_bytes_for(value_align, meta_bytes::<T>());
unsafe { value_ptr.byte_sub(prefix).cast::<u32>().as_ptr() }
}
#[inline]
pub(crate) unsafe fn strong_ref<'a, T: ?Sized + Pointee>(value_ptr: NonNull<u8>, value_align: usize) -> &'a AtomicU32 {
let prefix = strong_prefix_bytes_for(value_align, meta_bytes::<T>());
unsafe { value_ptr.byte_sub(prefix).cast::<AtomicU32>().as_ref() }
}
#[inline]
unsafe fn read_metadata<T: ?Sized + Pointee>(value_ptr: NonNull<u8>) -> <T as Pointee>::Metadata {
unsafe {
let meta_ptr = value_ptr.as_ptr().sub(meta_bytes::<T>()).cast::<<T as Pointee>::Metadata>();
ptr::read_unaligned(meta_ptr)
}
}
#[inline]
pub(crate) unsafe fn as_fat<T: ?Sized + Pointee>(value_ptr: NonNull<u8>) -> NonNull<T> {
unsafe {
let meta = read_metadata::<T>(value_ptr);
let fat = ptr_meta::from_raw_parts_mut::<T>(value_ptr.as_ptr().cast::<()>(), meta);
NonNull::new_unchecked(fat)
}
}