#![doc(html_root_url = "https://docs.rs/tiptoe/0.0.2")]
#![warn(clippy::pedantic, missing_docs)]
#![allow(clippy::semicolon_if_nothing_returned)]
#![no_std]
#[cfg(doctest)]
pub mod readme {
doc_comment::doctest!("../README.md");
}
extern crate alloc;
#[cfg(not(feature = "sync"))]
use core::cell::Cell;
#[cfg(feature = "sync")]
use core::sync::atomic::AtomicUsize;
use core::{
cmp,
hash::Hash,
marker::PhantomPinned,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
pin::Pin,
};
#[cfg(feature = "sync")]
mod sync;
#[cfg(feature = "sync")]
pub use sync::Arc;
const EXCLUSIVITY_MARKER: usize = usize::MAX - (usize::MAX - isize::MAX as usize) / 2;
#[derive(Debug, Default)]
pub struct TipToe {
#[cfg(feature = "sync")]
refcount: AtomicUsize,
#[cfg(not(feature = "sync"))]
refcount: Cell<usize>,
_pinned: PhantomPinned,
}
impl TipToe {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl Clone for TipToe {
fn clone(&self) -> Self {
Self::default()
}
}
impl PartialEq for TipToe {
fn eq(&self, _: &Self) -> bool {
true
}
}
impl Eq for TipToe {}
impl PartialOrd for TipToe {
fn partial_cmp(&self, _: &Self) -> Option<cmp::Ordering> {
Some(cmp::Ordering::Equal)
}
}
impl Ord for TipToe {
fn cmp(&self, _: &Self) -> cmp::Ordering {
cmp::Ordering::Equal
}
}
impl Hash for TipToe {
fn hash<H: core::hash::Hasher>(&self, _: &mut H) {}
}
pub mod ref_counter_api {
use crate::{RefCounter, EXCLUSIVITY_MARKER};
use abort::abort;
#[cfg(any(feature = "sync", doc))]
use core::sync::atomic::Ordering;
mod private {
#[cfg(not(feature = "sync"))]
use core::cell::Cell;
#[cfg(feature = "sync")]
use core::sync::atomic::AtomicUsize;
use crate::TipToe;
pub trait Sealed: 'static {
#[cfg(feature = "sync")]
fn refcount(&self) -> &AtomicUsize;
#[cfg(not(feature = "sync"))]
fn refcount(&self) -> &Cell<usize>;
fn refcount_ptr(&self) -> *mut usize {
#[cfg(feature = "sync")]
return self.refcount() as *const AtomicUsize as *mut usize;
#[cfg(not(feature = "sync"))]
return self.refcount().as_ptr();
}
}
impl Sealed for TipToe {
#[allow(clippy::inline_always)]
#[cfg(feature = "sync")]
#[inline(always)]
fn refcount(&self) -> &AtomicUsize {
&self.refcount
}
#[allow(clippy::inline_always)]
#[cfg(not(feature = "sync"))]
#[inline(always)]
fn refcount(&self) -> &Cell<usize> {
&self.refcount
}
}
}
pub(super) use private::Sealed;
pub trait RefCounterExt: RefCounter {
fn increment(&self) {
#[cfg(feature = "sync")]
{
let old_count = self.refcount().fetch_add(1, Ordering::Relaxed);
if old_count >= (isize::MAX as usize) {
if old_count >= EXCLUSIVITY_MARKER {
if self.refcount().fetch_sub(1, Ordering::Relaxed) > EXCLUSIVITY_MARKER {
panic!("Tried to clone smart pointer during exclusive value borrow.")
} else {
abort()
}
} else {
abort()
}
}
}
#[cfg(not(feature = "sync"))]
{
let old_count = self.refcount().get();
if old_count >= EXCLUSIVITY_MARKER - 1 {
if old_count < EXCLUSIVITY_MARKER {
abort()
} else {
panic!("Tried to clone smart pointer during exclusive value borrow.")
}
}
self.refcount().set(old_count + 1)
};
}
#[inline]
unsafe fn decrement(&self) -> DecrementFollowup {
match {
#[cfg(feature = "sync")]
{
self.refcount().fetch_sub(1, Ordering::Release)
}
#[cfg(not(feature = "sync"))]
{
let old_count = self.refcount().get();
self.refcount().set(old_count - 1);
old_count
}
} {
1 => {
#[cfg(feature = "sync")]
self.refcount().load(Ordering::Acquire);
DecrementFollowup::DropOrMoveIt
}
_ => DecrementFollowup::LeakIt,
}
}
unsafe fn decrement_relaxed(&self) -> DecrementFollowup {
match {
#[cfg(feature = "sync")]
{
self.refcount().fetch_sub(1, Ordering::Relaxed)
}
#[cfg(not(feature = "sync"))]
{
let old_count = self.refcount().get();
self.refcount().set(old_count - 1);
old_count
}
} {
EXCLUSIVITY_MARKER..=usize::MAX => abort(),
1 => DecrementFollowup::DropOrMoveIt,
_ => DecrementFollowup::LeakIt,
}
}
unsafe fn acquire(&self) -> Option<Exclusivity> {
match {
#[cfg(feature = "sync")]
{
self.refcount().load(Ordering::Acquire)
}
#[cfg(not(feature = "sync"))]
self.refcount().get()
} {
1 => Some(Exclusivity::new(self)),
_ => None,
}
}
#[must_use]
fn acquire_relaxed(&self) -> Option<Exclusivity> {
match {
#[cfg(feature = "sync")]
{
self.refcount().load(Ordering::Relaxed)
}
#[cfg(not(feature = "sync"))]
self.refcount().get()
} {
1 => Some(Exclusivity::new(self)),
_ => None,
}
}
}
impl<T> RefCounterExt for T where T: RefCounter {}
pub enum DecrementFollowup {
LeakIt,
DropOrMoveIt,
}
pub struct Exclusivity {
refcount: *mut usize,
displaced_refcount: usize,
}
impl Exclusivity {
fn new<T: ?Sized + Sealed>(counter: &T) -> Self {
let ptr = counter.refcount_ptr();
Self {
displaced_refcount: unsafe { ptr.read() },
refcount: {
unsafe { ptr.write(EXCLUSIVITY_MARKER) };
ptr
},
}
}
}
impl Drop for Exclusivity {
fn drop(&mut self) {
unsafe { self.refcount.write(self.displaced_refcount) }
}
}
}
use ref_counter_api::{Exclusivity, Sealed};
pub trait RefCounter: Sealed {}
impl RefCounter for TipToe {}
pub unsafe trait IntrusivelyCountable {
type RefCounter: RefCounter;
fn ref_counter(&self) -> &Self::RefCounter;
}
unsafe impl<T> IntrusivelyCountable for ManuallyDrop<T>
where
T: IntrusivelyCountable,
{
type RefCounter = T::RefCounter;
fn ref_counter(&self) -> &Self::RefCounter {
#![allow(clippy::inline_always)]
#![inline(always)]
(**self).ref_counter()
}
}
pub trait ManagedClone: Sized {
unsafe fn managed_clone(&self) -> Self;
unsafe fn managed_clone_from(&mut self, source: &Self) {
*self = source.managed_clone()
}
}
impl<T> ManagedClone for T
where
T: Clone,
{
unsafe fn managed_clone(&self) -> Self {
self.clone()
}
unsafe fn managed_clone_from(&mut self, source: &Self) {
self.clone_from(source)
}
}
#[must_use]
pub struct ExclusivePin<'a, T: ?Sized> {
reference: Pin<&'a mut T>,
_exclusivity: Exclusivity,
}
impl<'a, T: ?Sized> ExclusivePin<'a, T> {
pub fn new(exclusivity: Exclusivity, reference: Pin<&'a mut T>) -> Self {
Self {
reference,
_exclusivity: exclusivity,
}
}
}
impl<'a, T: ?Sized> Deref for ExclusivePin<'a, T> {
type Target = Pin<&'a mut T>;
fn deref(&self) -> &Self::Target {
&self.reference
}
}
impl<'a, T: ?Sized> DerefMut for ExclusivePin<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.reference
}
}