use std::{
fmt::Debug,
ops::{
Deref,
DerefMut
},
cell::{
Cell,
UnsafeCell
},
ptr::NonNull,
num::NonZeroUsize
};
use crate::errors::HedelError;
#[derive(Debug, Clone, Copy)]
pub enum BorrowFlag {
Exclusive,
Shared(NonZeroUsize),
None
}
#[derive(Debug)]
pub struct HedelCell<T: Debug> {
flag: Cell<BorrowFlag>,
cell: UnsafeCell<T>
}
impl<T: Debug> HedelCell<T> {
pub fn new(value: T) -> Self {
Self {
flag: Cell::new(BorrowFlag::None),
cell: UnsafeCell::<T>::new(value)
}
}
pub fn try_get(&self) -> Result<RefHedel<T>, HedelError> {
match self.flag.get() {
BorrowFlag::None => {
self.flag.replace(BorrowFlag::Shared(NonZeroUsize::new(1).unwrap()));
},
BorrowFlag::Shared(n) => {
self.flag.replace(BorrowFlag::Shared(n.saturating_add(1)));
},
_ => {
return Err(HedelError::SharedBorrow);
}
}
Ok(RefHedel {
value: unsafe { &*self.cell.get() },
flag: &self.flag
})
}
pub fn get(&self) -> RefHedel<T> {
self.try_get().unwrap()
}
pub fn try_get_mut<'a>(&'a self) -> Result<RefMutHedel<'a, T>, HedelError> {
if let BorrowFlag::None = self.flag.get() {
self.flag.replace(BorrowFlag::Exclusive);
let value = match NonNull::<T>::new(UnsafeCell::raw_get(&self.cell as *const UnsafeCell::<T>)) {
Some(value) => value,
None => return Err(HedelError::InvalidNonNull)
};
return Ok(RefMutHedel::<T> {
flag: &self.flag,
value
});
} Err(HedelError::MutBorrow_)
}
pub fn get_mut(&self) -> RefMutHedel<T> {
self.try_get_mut().unwrap()
}
pub fn into_inner(self) -> T {
self.cell.into_inner()
}
}
pub struct RefHedel<'a, T: Debug> {
value: &'a T,
flag: &'a Cell<BorrowFlag>
}
impl<'a, T: Debug> Deref for RefHedel<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.value
}
}
impl<'a, T: Debug> Drop for RefHedel<'a, T> {
fn drop(&mut self) {
match self.flag.get() {
BorrowFlag::Shared(n) => {
if n.get() > 1 {
self.flag.replace(BorrowFlag::Shared(NonZeroUsize::new(n.get() - 1).unwrap()));
} else {
self.flag.replace(BorrowFlag::None);
}
},
_ => {
unreachable!("Before a `RefHedel` gets dropped, there should be a `BorrowFlag::Shared(_)`");
}
}
}
}
pub struct RefMutHedel<'a, T: Debug> {
value: NonNull<T>,
flag: &'a Cell<BorrowFlag>
}
impl<'a, T: Debug> Deref for RefMutHedel<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.value.as_ref() }
}
}
impl<'a, T: Debug> DerefMut for RefMutHedel<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { self.value.as_mut() }
}
}
impl<'a, T: Debug> Drop for RefMutHedel<'a, T> {
fn drop(&mut self) {
self.flag.replace(BorrowFlag::None);
}
}