use std::{
mem,
ops::Deref,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::RefOverflow;
#[derive(Debug)]
pub struct CellRef<'a, T>
where
T: ?Sized + 'a,
{
pub(crate) flag: &'a AtomicUsize,
pub(crate) value: &'a T,
}
pub(crate) const REF_LIMIT_MAX: usize = isize::MAX as usize;
impl<'a, T> CellRef<'a, T>
where
T: ?Sized,
{
pub fn try_clone(&self) -> Result<Self, RefOverflow> {
let previous_value = self.flag.fetch_add(1, Ordering::Relaxed);
let overflow = previous_value >= REF_LIMIT_MAX;
if unlikely(overflow) {
self.flag.fetch_sub(1, Ordering::Relaxed);
Err(RefOverflow)
} else {
Ok(CellRef {
flag: self.flag,
value: self.value,
})
}
}
pub fn map<U, F>(self, f: F) -> CellRef<'a, U>
where
F: FnOnce(&T) -> &U,
U: ?Sized,
{
let flag = unsafe { &*(self.flag as *const _) };
let value = unsafe { &*(self.value as *const _) };
mem::forget(self);
CellRef {
flag,
value: f(value),
}
}
}
impl<'a, T> Deref for CellRef<'a, T>
where
T: ?Sized,
{
type Target = T;
fn deref(&self) -> &T {
self.value
}
}
impl<'a, T> Drop for CellRef<'a, T>
where
T: ?Sized,
{
fn drop(&mut self) {
self.flag.fetch_sub(1, Ordering::Release);
}
}
impl<'a, T> Clone for CellRef<'a, T>
where
T: ?Sized,
{
fn clone(&self) -> Self {
self.try_clone()
.unwrap_or_else(|e| panic!("Failed to clone `CellRef`: {e}"))
}
}
#[cold]
#[inline(always)]
fn cold() {}
#[inline(always)]
fn unlikely(cond: bool) -> bool {
if cond {
cold();
}
cond
}
#[cfg(test)]
mod tests {
use std::{
error::Error,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::RefOverflow;
use super::{CellRef, REF_LIMIT_MAX};
#[test]
fn try_clone_returns_ok_when_ref_count_less_than_isize_max() {
let flag = &AtomicUsize::new(1);
let value = &1u32;
let cell_ref = CellRef { flag, value };
assert_eq!(1, cell_ref.flag.load(Ordering::SeqCst));
let try_clone_result = cell_ref.try_clone();
let cloned = try_clone_result.expect("try_clone_result to be ok");
assert_eq!(2, cloned.flag.load(Ordering::SeqCst));
}
#[test]
fn try_clone_returns_err_when_ref_count_equals_isize_max() {
let flag = &AtomicUsize::new(REF_LIMIT_MAX);
let value = &1u32;
let cell_ref = CellRef { flag, value };
assert_eq!(REF_LIMIT_MAX, cell_ref.flag.load(Ordering::SeqCst));
let try_clone_result = cell_ref.try_clone();
let e = try_clone_result.expect_err("try_clone_result to be err");
assert_eq!(RefOverflow, e);
assert!(e.source().is_none());
assert_eq!(REF_LIMIT_MAX, cell_ref.flag.load(Ordering::SeqCst));
}
#[test]
fn clone_returns_cell_ref_when_ref_count_less_than_isize_max() {
let flag = &AtomicUsize::new(1);
let value = &1u32;
let cell_ref = CellRef { flag, value };
assert_eq!(1, cell_ref.flag.load(Ordering::SeqCst));
let cloned = cell_ref.clone();
assert_eq!(2, cell_ref.flag.load(Ordering::SeqCst));
assert_eq!(2, cloned.flag.load(Ordering::SeqCst));
}
#[test]
#[should_panic(expected = "Failed to clone `CellRef`: Ref count exceeded `isize::MAX`")]
fn clone_panics_when_ref_count_equals_isize_max() {
let flag = &AtomicUsize::new(REF_LIMIT_MAX);
let value = &1u32;
let cell_ref = CellRef { flag, value };
assert_eq!(REF_LIMIT_MAX, cell_ref.flag.load(Ordering::SeqCst));
let _clone = cell_ref.clone();
}
}