#[allow(unused_imports, reason = "used for docs")]
use crate::Brc;
use crate::allocator_api::alloc::Allocator;
use crate::runtime::RawBrcHeader;
use core::alloc::Layout;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use core::sync::atomic::{AtomicU32, Ordering};
pub const WEAK_LOCKED_COUNT: u32 = u32::MAX;
pub const WEAK_OVERFLOW_THRESHOLD: u32 = u32::MAX / 2;
#[repr(C)]
pub struct BrcHeader<A: Allocator> {
pub strong: RawBrcHeader,
pub weak_count: AtomicU32,
pub alloc: ManuallyDrop<A>,
}
impl<A: Allocator> BrcHeader<A> {
#[inline]
pub unsafe fn drop_weak(header_ptr: *mut Self, layout_info: LayoutInfo<A>) {
let weak_count = unsafe { &(*header_ptr).weak_count };
if weak_count.fetch_sub(1, Ordering::Release) == 1 {
atomic::fence(Ordering::Acquire);
unsafe { Self::drop_weak_slow(header_ptr, layout_info.full_layout) }
}
}
#[cold]
unsafe fn drop_weak_slow(header_ptr: *mut Self, full_layout: Layout) {
let alloc = unsafe { Self::take_allocator(header_ptr) };
unsafe {
alloc.deallocate(NonNull::new_unchecked(header_ptr.cast::<u8>()), full_layout);
}
}
#[inline]
pub unsafe fn take_allocator(header_ptr: *const Self) -> A {
unsafe {
header_ptr
.byte_add(core::mem::offset_of!(Self, alloc))
.cast::<A>()
.read()
}
}
}
pub struct LayoutInfo<A: Allocator> {
pub value_offset: isize,
pub full_layout: Layout,
alloc_marker: PhantomData<fn(A)>,
}
impl<A: Allocator> Copy for LayoutInfo<A> {}
impl<A: Allocator> Clone for LayoutInfo<A> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<A: Allocator> LayoutInfo<A> {
pub const MIN_VALUE_ALIGNMENT: usize = 2;
#[inline]
pub fn new_or_panic(layout: Layout) -> Self {
#[cold]
#[inline(never)]
fn layout_overflow() -> ! {
panic!("Layout of Brc would overflow an isize")
}
match Self::new(layout) {
Ok(res) => res,
Err(_) => layout_overflow(),
}
}
#[inline]
pub fn new(layout: Layout) -> Result<Self, core::alloc::LayoutError> {
let (full_layout, value_offset) =
Layout::new::<BrcHeader<A>>().extend(layout.align_to(Self::MIN_VALUE_ALIGNMENT)?)?;
Ok(LayoutInfo {
full_layout,
#[expect(clippy::cast_possible_wrap, reason = "offset fits in isize")]
value_offset: value_offset as isize,
alloc_marker: PhantomData,
})
}
#[inline]
pub fn header_offset(&self) -> isize {
unsafe { 0isize.unchecked_sub(self.value_offset) }
}
#[inline]
pub unsafe fn for_value<T: ?Sized>(value: &T) -> Self {
let value_layout = Layout::for_value(value);
unsafe { Self::new(value_layout).unwrap_unchecked() }
}
}