use crate::{
Tracer,
trace::{Finalize, Trace},
};
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::{
cell::{Cell, UnsafeCell},
cmp::Ordering,
fmt::{self, Debug, Display},
hash::Hash,
ops::{Deref, DerefMut},
};
#[derive(Copy, Clone)]
struct BorrowFlag(usize);
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum BorrowState {
Reading,
Writing,
Unused,
}
const WRITING: usize = !0;
const UNUSED: usize = 0;
const BORROWFLAG_INIT: BorrowFlag = BorrowFlag(UNUSED);
impl BorrowFlag {
const fn borrowed(self) -> BorrowState {
match self.0 {
UNUSED => BorrowState::Unused,
WRITING => BorrowState::Writing,
_ => BorrowState::Reading,
}
}
const fn set_writing(self) -> Self {
Self(self.0 | WRITING)
}
#[inline]
fn add_reading(self) -> Self {
assert!(self.borrowed() != BorrowState::Writing);
let flags = Self(self.0 + 1);
{
assert!(flags.borrowed() == BorrowState::Reading);
}
flags
}
fn sub_reading(self) -> Self {
assert!(self.borrowed() == BorrowState::Reading);
Self(self.0 - 1)
}
}
impl Debug for BorrowFlag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowFlag")
.field("State", &self.borrowed())
.finish()
}
}
pub struct GcRefCell<T: ?Sized + 'static> {
borrow: Cell<BorrowFlag>,
cell: UnsafeCell<T>,
}
impl<T> GcRefCell<T> {
pub const fn new(value: T) -> Self {
Self {
borrow: Cell::new(BORROWFLAG_INIT),
cell: UnsafeCell::new(value),
}
}
pub fn into_inner(self) -> T {
self.cell.into_inner()
}
}
impl<T: ?Sized> GcRefCell<T> {
pub fn borrow(&self) -> GcRef<'_, T> {
match self.try_borrow() {
Ok(value) => value,
Err(e) => panic!("{}", e),
}
}
#[track_caller]
pub fn borrow_mut(&self) -> GcRefMut<'_, T> {
match self.try_borrow_mut() {
Ok(value) => value,
Err(e) => panic!("{}", e),
}
}
pub fn try_borrow(&self) -> Result<GcRef<'_, T>, BorrowError> {
if self.borrow.get().borrowed() == BorrowState::Writing {
return Err(BorrowError);
}
self.borrow.set(self.borrow.get().add_reading());
unsafe {
Ok(GcRef {
borrow: BorrowGcRef {
borrow: &self.borrow,
},
value: NonNull::new_unchecked(self.cell.get()),
})
}
}
pub fn try_borrow_mut(&self) -> Result<GcRefMut<'_, T>, BorrowMutError> {
if self.borrow.get().borrowed() != BorrowState::Unused {
return Err(BorrowMutError);
}
self.borrow.set(self.borrow.get().set_writing());
unsafe {
Ok(GcRefMut {
borrow: BorrowGcRefMut {
borrow: &self.borrow,
},
value: NonNull::new_unchecked(self.cell.get()),
marker: PhantomData,
})
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
pub struct BorrowError;
impl Display for BorrowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt("GcCell<T> already mutably borrowed", f)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
pub struct BorrowMutError;
impl Display for BorrowMutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt("GcCell<T> already borrowed", f)
}
}
impl<T: Trace + ?Sized> Finalize for GcRefCell<T> {}
unsafe impl<T: Trace + ?Sized> Trace for GcRefCell<T> {
unsafe fn trace(&self, tracer: &mut Tracer) {
match self.borrow.get().borrowed() {
BorrowState::Writing => (),
_ => unsafe { (*self.cell.get()).trace(tracer) },
}
}
unsafe fn trace_non_roots(&self) {
match self.borrow.get().borrowed() {
BorrowState::Writing => (),
_ => unsafe { (*self.cell.get()).trace_non_roots() },
}
}
fn run_finalizer(&self) {
Finalize::finalize(self);
match self.borrow.get().borrowed() {
BorrowState::Writing => (),
_ => unsafe { (*self.cell.get()).run_finalizer() },
}
}
}
struct BorrowGcRef<'a> {
borrow: &'a Cell<BorrowFlag>,
}
impl Drop for BorrowGcRef<'_> {
fn drop(&mut self) {
debug_assert!(self.borrow.get().borrowed() == BorrowState::Reading);
self.borrow.set(self.borrow.get().sub_reading());
}
}
impl Clone for BorrowGcRef<'_> {
#[inline]
fn clone(&self) -> Self {
self.borrow.set(self.borrow.get().add_reading());
BorrowGcRef {
borrow: self.borrow,
}
}
}
pub struct GcRef<'a, T: ?Sized + 'static> {
value: NonNull<T>,
borrow: BorrowGcRef<'a>,
}
impl<'a, T: ?Sized> GcRef<'a, T> {
#[must_use]
pub unsafe fn cast<U>(orig: Self) -> GcRef<'a, U> {
let value = orig.value.cast::<U>();
GcRef {
borrow: orig.borrow,
value,
}
}
#[allow(clippy::should_implement_trait)]
#[must_use]
pub fn clone(orig: &GcRef<'a, T>) -> GcRef<'a, T> {
GcRef {
borrow: orig.borrow.clone(),
value: orig.value,
}
}
pub fn try_map<U, F>(orig: Self, f: F) -> Option<GcRef<'a, U>>
where
U: ?Sized,
F: FnOnce(&T) -> Option<&U>,
{
let value = NonNull::from(f(&*orig)?);
let ret = GcRef {
borrow: orig.borrow,
value,
};
Some(ret)
}
pub fn map<U, F>(orig: Self, f: F) -> GcRef<'a, U>
where
U: ?Sized,
F: FnOnce(&T) -> &U,
{
let value = NonNull::from(f(&*orig));
GcRef {
borrow: orig.borrow,
value,
}
}
pub fn map_split<U, V, F>(orig: Self, f: F) -> (GcRef<'a, U>, GcRef<'a, V>)
where
U: ?Sized,
V: ?Sized,
F: FnOnce(&T) -> (&U, &V),
{
let (a, b) = f(&*orig);
let borrow = orig.borrow.clone();
(
GcRef {
borrow,
value: NonNull::from(a),
},
GcRef {
value: NonNull::from(b),
borrow: orig.borrow,
},
)
}
}
impl<T: ?Sized> Deref for GcRef<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.value.as_ref() }
}
}
impl<T: ?Sized + Debug> Debug for GcRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<T: ?Sized + Display> Display for GcRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
struct BorrowGcRefMut<'a> {
borrow: &'a Cell<BorrowFlag>,
}
impl Drop for BorrowGcRefMut<'_> {
fn drop(&mut self) {
debug_assert!(self.borrow.get().borrowed() == BorrowState::Writing);
self.borrow.set(BorrowFlag(UNUSED));
}
}
pub struct GcRefMut<'a, T: ?Sized> {
value: NonNull<T>,
borrow: BorrowGcRefMut<'a>,
marker: PhantomData<&'a mut T>,
}
impl<'a, T: ?Sized> GcRefMut<'a, T> {
#[must_use]
pub unsafe fn cast<V>(orig: Self) -> GcRefMut<'a, V> {
let value = orig.value.cast::<V>();
GcRefMut {
borrow: orig.borrow,
value,
marker: PhantomData,
}
}
pub fn try_map<V, F>(mut orig: GcRefMut<'a, T>, f: F) -> Option<GcRefMut<'a, V>>
where
V: ?Sized,
F: FnOnce(&mut T) -> Option<&mut V>,
{
let value = NonNull::from(f(&mut *orig)?);
let ret = GcRefMut {
borrow: orig.borrow,
value,
marker: PhantomData,
};
Some(ret)
}
pub fn map<V, F>(mut orig: Self, f: F) -> GcRefMut<'a, V>
where
V: ?Sized,
F: FnOnce(&mut T) -> &mut V,
{
let value = NonNull::from(f(&mut *orig));
GcRefMut {
borrow: orig.borrow,
value,
marker: PhantomData,
}
}
}
impl<T: ?Sized> Deref for GcRefMut<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.value.as_ref() }
}
}
impl<T: ?Sized> DerefMut for GcRefMut<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { self.value.as_mut() }
}
}
impl<T: Debug + ?Sized> Debug for GcRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<T: Display + ?Sized> Display for GcRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
unsafe impl<T: ?Sized + Send> Send for GcRefCell<T> {}
impl<T: Trace + Clone> Clone for GcRefCell<T> {
fn clone(&self) -> Self {
Self::new(self.borrow().clone())
}
}
impl<T: Default> Default for GcRefCell<T> {
fn default() -> Self {
Self::new(Default::default())
}
}
#[allow(clippy::inline_always)]
impl<T: ?Sized + PartialEq> PartialEq for GcRefCell<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
*self.borrow() == *other.borrow()
}
}
impl<T: ?Sized + Eq> Eq for GcRefCell<T> {}
#[allow(clippy::inline_always)]
impl<T: ?Sized + PartialOrd> PartialOrd for GcRefCell<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(*self.borrow()).partial_cmp(&*other.borrow())
}
#[inline(always)]
fn lt(&self, other: &Self) -> bool {
*self.borrow() < *other.borrow()
}
#[inline(always)]
fn le(&self, other: &Self) -> bool {
*self.borrow() <= *other.borrow()
}
#[inline(always)]
fn gt(&self, other: &Self) -> bool {
*self.borrow() > *other.borrow()
}
#[inline(always)]
fn ge(&self, other: &Self) -> bool {
*self.borrow() >= *other.borrow()
}
}
impl<T: ?Sized + Ord> Ord for GcRefCell<T> {
fn cmp(&self, other: &Self) -> Ordering {
(*self.borrow()).cmp(&*other.borrow())
}
}
impl<T: ?Sized + Debug> Debug for GcRefCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.borrow.get().borrowed() {
BorrowState::Unused | BorrowState::Reading => f
.debug_struct("GcCell")
.field("flags", &self.borrow.get())
.field("value", &self.borrow())
.finish_non_exhaustive(),
BorrowState::Writing => f
.debug_struct("GcCell")
.field("flags", &self.borrow.get())
.field("value", &"<borrowed>")
.finish_non_exhaustive(),
}
}
}