use std::{
cell::{Cell, UnsafeCell},
mem::MaybeUninit,
ops::{Deref, DerefMut},
};
#[repr(C)]
pub(crate) struct CellHeader {
borrow: Cell<isize>,
initialized: Cell<bool>,
pub(crate) info: &'static CellInfo,
}
pub(crate) type ParentStep = (fn() -> &'static CellInfo, usize);
pub struct CellInfo {
pub(crate) path: &'static str,
pub(crate) value: unsafe fn(*mut CellHeader) -> *mut u8,
pub(crate) parent: Option<ParentStep>,
}
#[repr(C)]
pub(crate) struct RubyCell<T> {
header: CellHeader,
value: UnsafeCell<MaybeUninit<T>>,
}
impl<T> RubyCell<T> {
pub(crate) fn new(info: &'static CellInfo, value: Option<T>) -> Self {
let initialized = value.is_some();
RubyCell {
header: CellHeader {
borrow: Cell::new(0),
initialized: Cell::new(initialized),
info,
},
value: UnsafeCell::new(match value {
Some(value) => MaybeUninit::new(value),
None => MaybeUninit::uninit(),
}),
}
}
pub(crate) unsafe fn value_ptr(header: *mut CellHeader) -> *mut u8 {
let cell = header as *mut RubyCell<T>;
(*cell).value.get() as *mut u8
}
pub(crate) unsafe fn gc_view(cell: *mut RubyCell<T>) -> Option<(*mut T, bool)> {
let header = &(*cell).header;
if header.initialized.get() {
Some(((*cell).value.get() as *mut T, header.borrow.get() != 0))
} else {
None
}
}
}
impl<T> Drop for RubyCell<T> {
fn drop(&mut self) {
if self.header.initialized.get() {
unsafe { self.value.get_mut().assume_init_drop() };
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BorrowError {
Uninitialized,
MutablyBorrowed,
Borrowed,
}
impl CellHeader {
pub(crate) fn initialized(&self) -> bool {
self.initialized.get()
}
pub(crate) fn set_initialized(&self) {
self.initialized.set(true);
}
pub(crate) fn is_borrowed(&self) -> bool {
self.borrow.get() != 0
}
fn acquire(&self, mutable: bool) -> Result<(), BorrowError> {
let borrow = self.borrow.get();
if !self.initialized.get() {
Err(BorrowError::Uninitialized)
} else if borrow < 0 {
Err(BorrowError::MutablyBorrowed)
} else if mutable && borrow > 0 {
Err(BorrowError::Borrowed)
} else {
self.borrow.set(if mutable { -1 } else { borrow + 1 });
Ok(())
}
}
}
pub struct Borrowed<T> {
header: *const CellHeader,
value: *const T,
}
pub struct BorrowedMut<T> {
header: *const CellHeader,
value: *mut T,
}
impl<T> Borrowed<T> {
pub(crate) unsafe fn acquire(
header: *const CellHeader,
value: *const T,
) -> Result<Self, BorrowError> {
(*header).acquire(false)?;
Ok(Borrowed { header, value })
}
}
impl<T> BorrowedMut<T> {
pub(crate) unsafe fn acquire(
header: *const CellHeader,
value: *mut T,
) -> Result<Self, BorrowError> {
(*header).acquire(true)?;
Ok(BorrowedMut { header, value })
}
}
impl<T> Deref for Borrowed<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.value }
}
}
impl<T> Deref for BorrowedMut<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.value }
}
}
impl<T> DerefMut for BorrowedMut<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.value }
}
}
impl<T> Drop for Borrowed<T> {
fn drop(&mut self) {
let borrow = unsafe { &(*self.header).borrow };
borrow.set(borrow.get() - 1);
}
}
impl<T> Drop for BorrowedMut<T> {
fn drop(&mut self) {
unsafe { (*self.header).borrow.set(0) };
}
}