#[allow(unused_imports)]
use std::sync::atomic;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
use std::ptr::NonNull;
pub struct ArcInner<T: ?Sized> {
count: atomic::AtomicUsize,
data: T
}
pub struct UniqueArc<T: ?Sized>(Arc<T>);
unsafe impl<T: ?Sized + Sync + Send> Send for ArcInner<T> {}
unsafe impl<T: ?Sized + Sync + Send> Sync for ArcInner<T> {}
impl<T> UniqueArc<T> {
#[inline]
pub fn new(data: T) -> Self {
UniqueArc(Arc::new(data))
}
#[inline]
pub fn shareable(self) -> Arc<T> {
self.0
}
}
impl<T> Deref for UniqueArc<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.0
}
}
impl<T> DerefMut for UniqueArc<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut (*self.0.ptr()).data }
}
}
pub struct Arc<T: ?Sized> {
p: NonNull<ArcInner<T>>
}
unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
impl<T> Arc<T> {
#[inline]
pub fn new(data: T) -> Self {
let x = Box::new(ArcInner {
count: atomic::AtomicUsize::new(1),
data,
});
unsafe {
Arc { p: NonNull::new_unchecked(Box::into_raw(x)) }
}
}
}
impl<T: ?Sized> Arc<T> {
#[inline]
fn ptr(&self) -> *mut ArcInner<T> {
self.p.as_ptr()
}
#[inline]
fn inner(&self) -> &ArcInner<T> {
unsafe { &*self.ptr() }
}
#[inline]
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
if this.is_unique() {
unsafe {
Some(&mut (*this.ptr()).data)
}
} else {
None
}
}
#[inline]
pub fn is_unique(&self) -> bool {
self.inner().count.load(Relaxed) == 1
}
#[inline(never)]
unsafe fn drop_slow(&mut self) {
let _ = Box::from_raw(self.ptr());
}
}
impl<T: ?Sized> Deref for Arc<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.inner().data
}
}
impl<T: ?Sized> Clone for Arc<T> {
#[inline]
fn clone(&self) -> Self {
self.inner().count.fetch_add(1, Relaxed);
unsafe {
Arc { p: NonNull::new_unchecked(self.ptr()) }
}
}
}
impl<T: ?Sized> Drop for Arc<T> {
#[inline]
fn drop(&mut self) {
if self.inner().count.fetch_sub(1, Release) != 1 {
return;
}
self.inner().count.load(Acquire);
unsafe {
self.drop_slow();
}
}
}