use crate::RefBox;
use core::cell::{Cell, UnsafeCell};
use core::marker::PhantomData;
use core::ptr::{self, NonNull};
use std::alloc;
#[cfg(feature = "cyclic_stable")]
use std::alloc::Layout;
pub(crate) type WeakCount = u32;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Status {
Available,
Borrowed,
Dropped,
DroppedWhileBorrowed,
}
#[derive(Debug)]
pub(crate) struct RefBoxHeapInner {
status: Cell<Status>,
weak_count: Cell<WeakCount>,
#[cfg(feature = "cyclic_stable")]
layout: Layout,
}
#[repr(C)]
#[derive(Debug)]
pub struct RefBoxHeap<T: ?Sized> {
pub(crate) inner: RefBoxHeapInner,
pub(crate) data: UnsafeCell<T>,
}
impl RefBoxHeapInner {
#[inline]
pub(crate) fn status(&self) -> Status {
self.status.get()
}
#[inline]
pub(crate) fn weak_count(&self) -> WeakCount {
self.weak_count.get()
}
#[cfg(feature = "cyclic_stable")]
#[inline]
fn layout(&self) -> Layout {
self.layout
}
#[inline]
#[cfg(test)]
pub(crate) fn set_weak_count(&self, count: WeakCount) {
self.weak_count.set(count);
}
#[inline]
pub(crate) fn increase_weak_count(&self) {
let refcount = self.weak_count();
if refcount == WeakCount::MAX {
cold_panic();
} else {
self.weak_count.set(refcount + 1);
}
}
#[inline]
pub(crate) fn try_increase_weak_count(&self) -> bool {
let refcount = self.weak_count();
if refcount == WeakCount::MAX {
cold_false()
} else {
self.weak_count.set(refcount + 1);
true
}
}
#[inline]
fn decrease_refcount(&self) -> WeakCount {
let refcount = self.weak_count() - 1;
self.weak_count.set(refcount);
refcount
}
#[inline]
pub(crate) fn is_alive(&self) -> bool {
matches!(self.status(), Status::Available | Status::Borrowed)
}
#[inline]
pub(crate) fn is_borrowed(&self) -> bool {
matches!(self.status(), Status::Borrowed)
}
#[inline]
pub(crate) fn start_borrow(&self) {
self.status.set(Status::Borrowed);
}
}
impl<T: ?Sized> RefBoxHeap<T> {
#[inline]
pub(crate) unsafe fn data_ref(&self) -> &T {
unsafe { &*self.data.get() }
}
#[inline]
pub(crate) unsafe fn data_mut(&self) -> &mut T {
unsafe { &mut *self.data.get() }
}
pub(crate) unsafe fn drop_data(&self) {
unsafe { ptr::drop_in_place(self.data.get()) };
self.inner.status.set(Status::Dropped);
}
}
#[cold]
#[inline(never)]
fn cold_panic() {
panic!()
}
#[cold]
#[inline(never)]
fn cold_false() -> bool {
false
}
#[inline]
pub(crate) fn new_ref_box<T>(value: T) -> RefBox<T> {
let heap = Box::into_raw(Box::new(RefBoxHeap {
inner: RefBoxHeapInner {
status: Cell::new(Status::Available),
weak_count: Cell::new(0),
#[cfg(feature = "cyclic_stable")]
layout: Layout::new::<RefBoxHeap<T>>(),
},
data: UnsafeCell::new(value),
}));
let ptr = unsafe { NonNull::new_unchecked(heap) };
RefBox {
ptr,
_p: PhantomData,
}
}
#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
#[inline]
pub(crate) fn new_cyclic_ref_box<T, F: FnOnce(&crate::Weak<T>) -> T>(op: F) -> RefBox<T> {
let heap = Box::into_raw(Box::new(RefBoxHeap {
inner: RefBoxHeapInner {
status: Cell::new(Status::Dropped),
weak_count: Cell::new(1),
#[cfg(feature = "cyclic_stable")]
layout: Layout::new::<RefBoxHeap<T>>(),
},
data: UnsafeCell::new(core::mem::MaybeUninit::<T>::uninit()),
}));
let weak = crate::Weak {
ptr: unsafe { NonNull::new_unchecked(heap.cast()) },
};
let data = op(&weak);
unsafe { (*heap).data.get_mut().write(data) };
unsafe { (*heap).inner.status.set(Status::Available) };
drop(weak);
RefBox {
ptr: unsafe { NonNull::new_unchecked(heap.cast()) },
_p: PhantomData,
}
}
unsafe fn dealloc_heap<T: ?Sized>(heap: NonNull<RefBoxHeap<T>>) {
#[cfg(feature = "cyclic_stable")]
let layout = unsafe { &(*heap.as_ptr()).inner }.layout();
#[cfg(all(feature = "cyclic", not(feature = "cyclic_stable")))]
let layout = unsafe { alloc::Layout::for_value_raw(heap.as_ptr()) };
#[cfg(all(not(feature = "cyclic"), not(feature = "cyclic_stable")))]
let layout = alloc::Layout::for_value(unsafe { heap.as_ref() });
unsafe {
alloc::dealloc(heap.as_ptr().cast(), layout);
}
}
#[inline]
pub(crate) unsafe fn drop_ref_box<T: ?Sized>(heap: NonNull<RefBoxHeap<T>>) {
match unsafe { heap.as_ref() }.inner.status() {
Status::Available => {
unsafe { heap.as_ref().drop_data() };
if unsafe { heap.as_ref() }.inner.weak_count() == 0 {
unsafe { dealloc_heap(heap) };
}
}
Status::Borrowed => {
unsafe { heap.as_ref() }
.inner
.status
.set(Status::DroppedWhileBorrowed);
}
Status::DroppedWhileBorrowed | Status::Dropped => {
unsafe { std::hint::unreachable_unchecked() };
}
}
}
#[inline]
pub(crate) unsafe fn drop_weak<T: ?Sized>(heap: NonNull<RefBoxHeap<T>>) {
let refcount = unsafe { &(*heap.as_ptr()).inner }.decrease_refcount();
if refcount == 0 {
if unsafe { &(*heap.as_ptr()).inner }.status() == Status::Dropped {
unsafe { dealloc_heap(heap) };
}
}
}
#[inline]
pub(crate) unsafe fn drop_borrow<T: ?Sized>(heap: &RefBoxHeap<T>) {
match heap.inner.status() {
Status::Borrowed => {
heap.inner.status.set(Status::Available);
}
Status::DroppedWhileBorrowed => {
unsafe { heap.drop_data() };
}
Status::Available | Status::Dropped => {
unsafe { std::hint::unreachable_unchecked() };
}
}
}