#![deny(
clippy::all,
clippy::cargo, //
clippy::pedantic, //
clippy::as_conversions,
clippy::float_arithmetic,
clippy::arithmetic_side_effects,
clippy::must_use_candidate,
clippy::missing_inline_in_public_items,
clippy::missing_const_for_fn
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(
feature = "unstable",
feature(unsize, dispatch_from_dyn, coerce_unsized, ptr_metadata)
)]
#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use core::alloc::Layout;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::mem;
use core::mem::ManuallyDrop;
use core::mem::MaybeUninit;
use core::ops::Deref;
use core::pin::Pin;
use core::ptr;
use core::ptr::NonNull;
use core::sync::atomic::fence;
use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release};
use alloc::boxed::Box;
#[cfg(doc)]
use alloc::sync::Arc;
#[cfg(feature = "unstable")]
use core::marker::Unsize;
#[cfg(feature = "unstable")]
use core::ops::DispatchFromDyn;
#[cfg(feature = "std")]
use std::panic::{RefUnwindSafe, UnwindSafe};
pub struct Asc<T: ?Sized> {
inner: NonNull<Inner<T>>,
_marker: PhantomData<T>,
}
unsafe impl<T: Send + Sync> Send for Asc<T> {}
unsafe impl<T: Send + Sync> Sync for Asc<T> {}
#[cfg(feature = "std")]
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Asc<T> {}
#[cfg(feature = "std")]
impl<T: RefUnwindSafe + ?Sized> RefUnwindSafe for Asc<T> {}
#[cfg(feature = "unstable")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> core::ops::CoerceUnsized<Asc<U>> for Asc<T> {}
#[cfg(feature = "unstable")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Asc<U>> for Asc<T> {}
#[repr(C)]
struct Inner<T: ?Sized> {
strong: AtomicUsize,
data: T,
}
unsafe fn box_from_nonnull<T: ?Sized>(p: NonNull<T>) -> Box<T> {
Box::from_raw(p.as_ptr())
}
fn box_into_nonnull<T>(b: Box<T>) -> NonNull<T> {
unsafe { NonNull::new_unchecked(Box::into_raw(b)) }
}
#[cfg(not(target_pointer_width = "64"))]
#[cold]
fn critical() -> ! {
struct Bomb {}
impl Drop for Bomb {
fn drop(&mut self) {
panic!("bomb")
}
}
let _bomb = Bomb {};
panic!("critical failure")
}
#[cfg(not(target_pointer_width = "64"))]
#[inline(always)]
fn check_overflow(old: usize) {
if old >= isize::MAX as usize {
critical()
}
}
#[cfg(target_pointer_width = "64")]
#[inline(always)]
const fn check_overflow(_old: usize) {}
impl<T> Asc<T> {
#[inline]
#[must_use]
pub fn pin(data: T) -> Pin<Self> {
unsafe { Pin::new_unchecked(Self::new(data)) }
}
#[inline]
#[must_use]
pub fn new(data: T) -> Self {
let inner = Box::new(Inner {
strong: AtomicUsize::new(1),
data,
});
Self {
inner: box_into_nonnull(inner),
_marker: PhantomData,
}
}
#[inline]
pub fn try_unwrap(this: Self) -> Result<T, Self> {
let s = this.strong();
if s.compare_exchange(1, 0, Acquire, Relaxed).is_err() {
return Err(this);
}
unsafe {
let this = ManuallyDrop::new(this);
let data = ptr::read(&raw const this.inner.as_ref().data);
let layout = Layout::new::<Inner<T>>();
alloc::alloc::dealloc(this.inner.as_ptr().cast::<u8>(), layout);
Ok(data)
}
}
#[inline]
#[must_use]
pub fn into_inner(this: Self) -> Option<T> {
Self::try_unwrap(this).ok()
}
#[inline]
#[must_use]
pub const unsafe fn from_raw(ptr: *const T) -> Self {
let offset = mem::offset_of!(Inner<T>, data);
let inner = ptr.cast::<u8>().sub(offset).cast_mut().cast::<Inner<T>>();
Self {
inner: NonNull::new_unchecked(inner),
_marker: PhantomData,
}
}
#[inline]
pub unsafe fn increment_strong_count(ptr: *const T) {
let offset = mem::offset_of!(Inner<T>, data);
let inner = ptr.cast::<u8>().sub(offset).cast::<Inner<T>>();
let strong = unsafe { &(*inner).strong };
let old = strong.fetch_add(1, Relaxed);
check_overflow(old);
}
#[inline]
pub unsafe fn decrement_strong_count(ptr: *const T) {
let offset = mem::offset_of!(Inner<T>, data);
let inner = ptr.cast::<u8>().sub(offset).cast_mut().cast::<Inner<T>>();
let strong = unsafe { &(*inner).strong };
if strong.fetch_sub(1, Release) != 1 {
return;
}
fence(Acquire);
unsafe {
drop(box_from_nonnull(NonNull::new_unchecked(inner)));
}
}
#[inline]
#[must_use]
pub fn new_uninit() -> Asc<MaybeUninit<T>> {
let layout = Layout::new::<Inner<MaybeUninit<T>>>();
let ptr = unsafe { alloc::alloc::alloc(layout) };
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
let ptr = ptr.cast::<Inner<MaybeUninit<T>>>();
unsafe {
ptr::addr_of_mut!((*ptr).strong).write(AtomicUsize::new(1));
}
Asc {
inner: unsafe { NonNull::new_unchecked(ptr) },
_marker: PhantomData,
}
}
#[inline]
#[must_use]
pub fn new_zeroed() -> Asc<MaybeUninit<T>> {
let layout = Layout::new::<Inner<MaybeUninit<T>>>();
let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
let ptr = ptr.cast::<Inner<MaybeUninit<T>>>();
unsafe {
ptr::addr_of_mut!((*ptr).strong).write(AtomicUsize::new(1));
}
Asc {
inner: unsafe { NonNull::new_unchecked(ptr) },
_marker: PhantomData,
}
}
}
impl<T> Asc<MaybeUninit<T>> {
#[inline]
#[must_use]
pub unsafe fn assume_init(self) -> Asc<T> {
let this = ManuallyDrop::new(self);
Asc {
inner: this.inner.cast::<Inner<T>>(),
_marker: PhantomData,
}
}
}
#[cfg(feature = "unstable")]
impl<T> Asc<[MaybeUninit<T>]> {
#[inline]
#[must_use]
pub unsafe fn assume_init(self) -> Asc<[T]> {
let this = ManuallyDrop::new(self);
let ptr = this.inner.as_ptr();
let len = ptr::metadata(ptr);
let inner = ptr::from_raw_parts_mut::<Inner<[T]>>(ptr.cast::<u8>().cast::<()>(), len);
Asc {
inner: unsafe { NonNull::new_unchecked(inner) },
_marker: PhantomData,
}
}
}
#[cfg(feature = "unstable")]
impl<T> Asc<[T]> {
#[inline]
#[must_use]
pub fn new_uninit_slice(len: usize) -> Asc<[MaybeUninit<T>]> {
let (layout, data_offset) = inner_slice_layout::<T>(len);
let ptr = unsafe { alloc::alloc::alloc(layout) };
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
let inner = ptr_to_inner_slice::<MaybeUninit<T>>(ptr, data_offset, len);
unsafe {
ptr::addr_of_mut!((*inner).strong).write(AtomicUsize::new(1));
}
Asc {
inner: unsafe { NonNull::new_unchecked(inner) },
_marker: PhantomData,
}
}
#[inline]
#[must_use]
pub fn new_zeroed_slice(len: usize) -> Asc<[MaybeUninit<T>]> {
let (layout, data_offset) = inner_slice_layout::<T>(len);
let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
let inner = ptr_to_inner_slice::<MaybeUninit<T>>(ptr, data_offset, len);
unsafe {
ptr::addr_of_mut!((*inner).strong).write(AtomicUsize::new(1));
}
Asc {
inner: unsafe { NonNull::new_unchecked(inner) },
_marker: PhantomData,
}
}
}
#[cfg(feature = "unstable")]
fn inner_slice_layout<T>(len: usize) -> (Layout, usize) {
let strong = Layout::new::<AtomicUsize>();
let data = Layout::array::<T>(len).expect("capacity overflow");
strong.extend(data).unwrap()
}
#[cfg(feature = "unstable")]
const fn ptr_to_inner_slice<T>(base: *mut u8, data_offset: usize, len: usize) -> *mut Inner<[T]> {
let _ = data_offset; ptr::from_raw_parts_mut::<Inner<[T]>>(base.cast::<()>(), len)
}
impl<T: ?Sized> Asc<T> {
const fn strong(&self) -> &AtomicUsize {
unsafe { &self.inner.as_ref().strong }
}
#[inline]
#[must_use]
fn shallow_clone(&self) -> Self {
let s = self.strong();
let old = s.fetch_add(1, Relaxed);
check_overflow(old);
Self {
inner: self.inner,
_marker: PhantomData,
}
}
#[inline(never)]
unsafe fn destroy(&mut self) {
drop(box_from_nonnull(self.inner));
}
#[inline]
#[must_use]
pub fn strong_count(this: &Self) -> usize {
this.strong().load(Relaxed)
}
#[inline]
#[must_use]
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
ptr::eq(this.inner.as_ptr(), other.inner.as_ptr())
}
#[inline]
#[must_use]
pub fn is_unique(this: &Self) -> bool {
this.strong().load(Relaxed) == 1
}
#[inline]
#[must_use]
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
if this.strong().load(Acquire) == 1 {
unsafe { Some(Self::get_mut_unchecked(this)) }
} else {
None
}
}
#[inline]
#[must_use]
pub const unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {
&mut this.inner.as_mut().data
}
#[inline]
#[must_use]
pub const fn as_ptr(this: &Self) -> *const T {
unsafe { ptr::addr_of!(this.inner.as_ref().data) }
}
#[inline]
#[must_use]
pub const fn into_raw(this: Self) -> *const T {
let ptr = Self::as_ptr(&this);
mem::forget(this);
ptr
}
}
impl<T: ?Sized> Deref for Asc<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &self.inner.as_ref().data }
}
}
impl<T: ?Sized> Clone for Asc<T> {
#[inline]
fn clone(&self) -> Self {
self.shallow_clone()
}
}
impl<T: ?Sized> Drop for Asc<T> {
#[inline]
fn drop(&mut self) {
let s = self.strong();
if s.fetch_sub(1, Release) != 1 {
return;
}
fence(Acquire);
unsafe { self.destroy() };
}
}
impl<T: Clone> Asc<T> {
#[inline]
#[must_use]
pub fn unwrap_or_clone(this: Self) -> T {
Self::try_unwrap(this).unwrap_or_else(|a| T::clone(&a))
}
#[inline]
#[must_use]
pub fn make_mut(this: &mut Self) -> &mut T {
let s = this.strong();
let count = s.load(Acquire);
if count > 1 {
*this = Self::new(T::clone(&**this));
}
unsafe { &mut this.inner.as_mut().data }
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for Asc<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T as fmt::Debug>::fmt(&**self, f)
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for Asc<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T as fmt::Display>::fmt(&**self, f)
}
}
impl<T: ?Sized> fmt::Pointer for Asc<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&Self::as_ptr(self), f)
}
}
impl<T: ?Sized + PartialEq> PartialEq for Asc<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T: ?Sized + Eq> Eq for Asc<T> {}
impl<T: ?Sized + Hash> Hash for Asc<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: ?Sized + PartialOrd> PartialOrd for Asc<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T: ?Sized + Ord> Ord for Asc<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T> From<T> for Asc<T> {
#[inline]
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: Default> Default for Asc<T> {
#[inline]
fn default() -> Self {
Self::new(T::default())
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use super::Asc;
use serde::{Deserialize, Serialize};
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de, T: Deserialize<'de>> Deserialize<'de> for Asc<T> {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::de::Deserializer<'de>,
{
T::deserialize(deserializer).map(Self::new)
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<T: Serialize> Serialize for Asc<T> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::ser::Serializer,
{
T::serialize(&**self, serializer)
}
}
}
#[cfg(test)]
mod tests;