pin_arc/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#![no_std]
#![deny(unsafe_code)]

//! This crate provides reference counting pointers similar to `Rc` and `Arc`, but without heap allocation.
//! You are responsible for creating a `Pin{Arc|Rc}Storage`, which you can obtain `Pin{Arc|Rc}` pointers from.
//! The storage needs to be pinned, for example using [`pin`](core::pin::pin).
//!
//! ```rust
//! # use std::pin::pin;
//! # use pin_arc::{PinArc, PinArcStorage};
//! let storage = pin!(PinArcStorage::new(4));
//! let arc = storage.as_ref().create_handle();
//! println!("{arc:?}");
//! ```
//!
//! If the storage is dropped before all references to it are released, the program is aborted (even if you have set panics to unwind):
//! ```should_panic
//! # use std::pin::pin;
//! # use pin_arc::{PinArc,PinArcStorage};
//! fn escaping_handle() -> PinArc<u32> {
//!     let storage = pin!(PinArcStorage::new(4));
//!     storage.as_ref().create_handle()
//! }
//! escaping_handle();
//! ```

#[cfg(all(feature = "unsafe_disable_abort", not(debug_assertions)))]
const _: () = const {
    panic!("the feature unsafe_disable_abort should only be used for testing this crate. Enabling it makes the api unsound.")
};

use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt::{Debug, Formatter};
use core::hash::{Hash, Hasher};
use core::ops::Deref;
use core::pin::Pin;
use radium::Radium;

#[allow(unsafe_code)]
mod generic_rc;

use crate::generic_rc::Inner;
pub use generic_rc::*;

impl<T, C: Radium<Item = usize>> Deref for PinRcGenericStorage<T, C> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.inner().value().get_ref()
    }
}

impl<T, C: Radium<Item = usize>> PinRcGenericStorage<T, C> {
    /// Get the number of handles currently referring to `self`.
    /// Beware of race conditions:
    /// Concurrent operations may change the count between
    /// the time you observe it and the time you act on the observation.
    pub fn ref_count(&self) -> usize {
        self.inner().count()
    }

    /// Get a pinned reference to the contained value.
    pub fn get_pinned(&self) -> Pin<&T> {
        self.inner().value()
    }

    /// Create a handle referring to `self`.
    /// Note that this takes `Pin<&Self>`.
    /// If you have a `Pin<&mut Self>`, call `as_ref`:
    /// ```rust
    /// # use std::pin::pin;
    /// # use pin_arc::{PinArc, PinArcStorage};
    /// # let storage=pin!(PinArcStorage::new(4));
    /// let arc = storage.as_ref().create_handle();
    /// ```
    pub fn create_handle(self: Pin<&Self>) -> PinRcGeneric<T, C> {
        self.inner().create_handle()
    }
}

impl<T, C: Radium<Item = usize>> PinRcGeneric<T, C> {
    /// Get the number of handles currently referring to the same storage (including `self`).
    /// Beware of race conditions:
    /// Concurrent operations may change the count between
    /// the time you observe it and the time you act on the observation.
    pub fn ref_count(&self) -> usize {
        self.inner().count()
    }

    /// Get a pinned reference to the contained value.
    pub fn get_pinned(&self) -> Pin<&T> {
        self.inner().value()
    }
}

impl<T, C: Radium<Item = usize>> Deref for PinRcGeneric<T, C> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.inner().value().get_ref()
    }
}

impl<T, C: Radium<Item = usize>> Clone for PinRcGeneric<T, C> {
    fn clone(&self) -> Self {
        self.inner().create_handle()
    }
}

macro_rules! impl_cmp_trait {
    ($Trait:ident{$($name:ident->$Ret:ty),*} for $For:ident) => {
        impl<T:$Trait,C:Radium<Item=usize>>  $Trait for $For<T,C>{
            $(
                #[inline]
                fn $name(&self, other: &Self)->$Ret{
                    <T as $Trait>::$name(&**self,&**other)
                }
            )*
        }
    };
}

impl_cmp_trait!(PartialEq{eq->bool} for PinRcGeneric);
impl_cmp_trait!(Eq{} for PinRcGeneric);
impl_cmp_trait!(PartialOrd{partial_cmp->Option<Ordering>,lt->bool,le->bool,gt->bool,ge->bool} for PinRcGeneric);
impl_cmp_trait!(Ord{cmp->Ordering} for PinRcGeneric);

impl_cmp_trait!(PartialEq{eq->bool} for PinRcGenericStorage);
impl_cmp_trait!(Eq{} for PinRcGenericStorage);
impl_cmp_trait!(PartialOrd{partial_cmp->Option<Ordering>,lt->bool,le->bool,gt->bool,ge->bool} for PinRcGenericStorage);
impl_cmp_trait!(Ord{cmp->Ordering} for PinRcGenericStorage);

macro_rules! impl_others {
    ($For:ident) => {
        impl<T: Hash, C: Radium<Item = usize>> Hash for $For<T, C> {
            fn hash<H: Hasher>(&self, state: &mut H) {
                <T as Hash>::hash(&**self, state)
            }
        }

        impl<T, C: Radium<Item = usize>> Borrow<T> for $For<T, C> {
            fn borrow(&self) -> &T {
                self
            }
        }

        impl<T: Debug, C: Radium<Item = usize>> Debug for $For<T, C> {
            fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
                Debug::fmt(self.inner(), f)
            }
        }
    };
}

impl_others!(PinRcGeneric);
impl_others!(PinRcGenericStorage);

impl<T: Debug, C: Radium<Item = usize>> Debug for Inner<T, C> {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        let mut s = f.debug_struct("PinRcGeneric");
        s.field("ref_count", &self.count());
        s.field("value", &*self.value());
        s.finish()
    }
}