use core::{
borrow::Borrow,
sync::atomic::{Ordering, fence},
};
use osom_lib_alloc::traits::Allocator;
use osom_lib_reprc::traits::ReprC;
use osom_lib_try_clone::TryClone;
use super::{
consts::MAX_REFERENCES,
errors::{CArcError, MaxReferencesExceededError},
internal::InternalArc,
weak::CWeak,
};
#[repr(transparent)]
#[must_use]
#[derive(Debug)]
pub struct CArc<T, TAllocator: Allocator> {
internal: InternalArc<T, TAllocator>,
}
unsafe impl<T: ReprC, TAllocator: Allocator> ReprC for CArc<T, TAllocator> {
const CHECK: () = {
let () = <InternalArc<T, TAllocator> as ReprC>::CHECK;
let () = T::CHECK;
let () = TAllocator::CHECK;
};
}
#[must_use]
#[repr(C)]
#[derive(Debug)]
pub struct AbandonResult<T, TAllocator: Allocator> {
pub data: T,
pub weak: CWeak<T, TAllocator>,
}
unsafe impl<T: ReprC, TAllocator: Allocator> ReprC for AbandonResult<T, TAllocator> {
const CHECK: () = {
let () = <CWeak<T, TAllocator> as ReprC>::CHECK;
let () = T::CHECK;
let () = TAllocator::CHECK;
};
}
impl<T, TAllocator: Allocator> CArc<T, TAllocator> {
#[inline]
pub fn new(value: T) -> Result<Self, CArcError> {
let internal = InternalArc::new(value)?;
Ok(Self::from_internal(internal))
}
#[inline]
pub fn with_allocator(value: T, allocator: TAllocator) -> Result<Self, CArcError> {
let internal = InternalArc::with_allocator(value, allocator)?;
Ok(Self::from_internal(internal))
}
#[inline(always)]
#[must_use]
pub fn strong_count(&self) -> u32 {
self.internal.strong().load(Ordering::Relaxed)
}
#[inline(always)]
#[must_use]
pub fn weak_count(&self) -> u32 {
self.internal.weak().load(Ordering::Relaxed)
}
#[inline]
#[must_use]
pub const fn data(&self) -> &T {
self.internal.data()
}
pub fn downgrade(&self) -> Result<CWeak<T, TAllocator>, MaxReferencesExceededError> {
let internal_clone = self.internal.clone();
let prev_value = internal_clone.weak().fetch_add(1, Ordering::Relaxed);
if prev_value >= MAX_REFERENCES {
internal_clone.weak().fetch_sub(1, Ordering::Relaxed);
return Err(MaxReferencesExceededError);
}
Ok(CWeak::from_internal(internal_clone))
}
#[inline]
#[must_use]
pub fn abandon(mut self) -> Option<AbandonResult<T, TAllocator>> {
let result = unsafe { self.internal_abandon() };
core::mem::forget(self);
result
}
#[inline(always)]
pub(super) fn from_internal(internal: InternalArc<T, TAllocator>) -> Self {
Self { internal }
}
unsafe fn internal_abandon(&mut self) -> Option<AbandonResult<T, TAllocator>> {
let internal = unsafe { core::ptr::read(&raw const self.internal) };
let prev = internal.strong().fetch_sub(1, Ordering::Release);
if prev > 1 {
return None;
}
let data = unsafe { internal.read_data() };
fence(Ordering::Acquire);
Some(AbandonResult {
data,
weak: CWeak::from_internal(internal),
})
}
}
impl<T, TAllocator: Allocator> Drop for CArc<T, TAllocator> {
fn drop(&mut self) {
let _ = unsafe { self.internal_abandon() };
}
}
impl<T, TAllocator: Allocator> AsRef<T> for CArc<T, TAllocator> {
fn as_ref(&self) -> &T {
self.data()
}
}
impl<T, TAllocator: Allocator> Borrow<T> for CArc<T, TAllocator> {
fn borrow(&self) -> &T {
self.data()
}
}
impl<T, TAllocator: Allocator> Clone for CArc<T, TAllocator> {
fn clone(&self) -> Self {
self.try_clone().expect("CArc strong reference count is too high.")
}
}
impl<T, TAllocator: Allocator> TryClone for CArc<T, TAllocator> {
type Error = MaxReferencesExceededError;
fn try_clone(&self) -> Result<Self, Self::Error> {
let internal_clone = self.internal.clone();
let prev_value = internal_clone.strong().fetch_add(1, Ordering::Relaxed);
if prev_value >= MAX_REFERENCES {
internal_clone.strong().fetch_sub(1, Ordering::Relaxed);
return Err(MaxReferencesExceededError);
}
Ok(Self {
internal: internal_clone,
})
}
}