pui_core/
dynamic.rs

1//! A dynamically created type that is guarnteed to be
2//! unique on the given thread or process
3//!
4//! [`Dynamic`] uses [`ScalarAllocator`] and [`PoolMut`]
5//! to ensure that it holds a unique value. This value
6//! is cloned into the [`DynamicToken`] and used to
7//! verify identity.
8//!
9//! [`Dynamic`] initially tries to pull a value from [`PoolMut`]
10//! if that fails, it then allocates a new value from the
11//! [`ScalarAllocator`]. Unless the backing `Scalar` is `()`,
12//! then this checks it's identity at runtime. Hence the name,
13//! `Dynamic`.
14//!
15//! If the provided pool is `()`, then no values will be reused,
16//! and this allows `Dynamic` to implement [`OneShotIdentifier`].
17
18use core::{
19    cmp::Ordering,
20    hash::{Hash, Hasher},
21    marker::PhantomData,
22    num::NonZeroU64,
23};
24
25use crate::{
26    pool::PoolMut,
27    scalar::{OpaqueScalar, ScalarAllocator},
28    Identifier, OneShotIdentifier, Token,
29};
30
31crate::scalar_allocator! {
32    /// A global scalar allocator that's backed by a [`NonZeroU64`].
33    /// This allows `Option<DynamicToken<Global>>` to be the same size
34    /// as [`DynamicToken<Global>`](DynamicToken)
35    pub struct Global(NonZeroU64);
36}
37
38#[cfg(feature = "std")]
39#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
40crate::scalar_allocator! {
41    /// A thread-local scalar allocator that's backed by a [`NonZeroU64`]
42    /// This allows `Option<DynamicToken<ThreadLocal>>` to be the same size
43    /// as [`DynamicToken<ThreadLocal>`](DynamicToken)
44    pub thread_local struct ThreadLocal(NonZeroU64);
45}
46
47/// A dynamically created type that is guarnteed to be unique on the given thread
48/// and if `A::AutoTraits: Send + Sync` on the given process.
49///
50/// `Dynamic` implements [`OneShotIdentifier`] if `P == ()`, i.e. there is no pool
51#[derive(Debug)]
52pub struct Dynamic<A: ScalarAllocator = Global, P: PoolMut<A> = ()> {
53    scalar: A::Scalar,
54    pool: P,
55    auto: PhantomData<A::AutoTraits>,
56}
57
58/// A token that is recognized by [`Dynamic`]
59#[derive(Debug)]
60#[repr(transparent)]
61pub struct DynamicToken<A: ScalarAllocator = Global> {
62    scalar: A::Scalar,
63    auto: PhantomData<A::AutoTraits>,
64}
65
66impl<A: ScalarAllocator<Scalar = ()>> Default for DynamicToken<A> {
67    #[inline]
68    fn default() -> Self { Self::NEW }
69}
70
71impl<A: ScalarAllocator<Scalar = ()>> DynamicToken<A> {
72    /// Create a new token
73    pub const NEW: Self = Self {
74        scalar: (),
75        auto: PhantomData,
76    };
77}
78
79impl<A: ScalarAllocator> DynamicToken<A> {
80    #[inline]
81    /// Create a new token
82    pub fn new(scalar: A::Scalar) -> Self {
83        Self {
84            scalar,
85            auto: PhantomData,
86        }
87    }
88}
89
90impl Dynamic {
91    /// Create a new `Dynamic` using the `Global` `ScalarAllocator`
92    #[inline]
93    pub fn create() -> Self { Self::with_pool(()) }
94}
95
96impl<P: PoolMut<Global>> Dynamic<Global, P> {
97    #[inline]
98    /// Create a new `Dynamic` using the `Global` `ScalarAllocator` and the given pool
99    pub fn with_pool(pool: P) -> Self { Self::with_alloc_and_pool(pool) }
100}
101
102impl<A: ScalarAllocator> Dynamic<A> {
103    #[inline]
104    /// Create a new `Dynamic` using the given `ScalarAllocator`
105    pub fn with_alloc() -> Self { Self::with_alloc_and_pool(()) }
106}
107
108impl<A: ScalarAllocator, P: PoolMut<A>> Dynamic<A, P> {
109    #[inline]
110    /// Create a new `Dynamic` using the given `ScalarAllocator` and pool
111    pub fn with_alloc_and_pool(mut pool: P) -> Self {
112        Self {
113            scalar: pool.remove_mut().map(OpaqueScalar::into_inner).unwrap_or_else(A::alloc),
114            pool,
115            auto: PhantomData,
116        }
117    }
118}
119
120impl<A: ScalarAllocator, P: PoolMut<A>> Drop for Dynamic<A, P> {
121    #[inline]
122    fn drop(&mut self) { let _ = self.pool.insert_mut(unsafe { OpaqueScalar::new(self.scalar.clone()) }); }
123}
124
125impl<A: ScalarAllocator, P: PoolMut<A>> Dynamic<A, P> {
126    /// Checks if self created the given [`DynamicToken`]
127    #[inline]
128    pub fn owns_token(&self, token: &DynamicToken<A>) -> bool { self.scalar == token.scalar }
129
130    /// Creates a [`DynamicToken`]
131    #[inline]
132    pub fn token(&self) -> DynamicToken<A> {
133        DynamicToken {
134            scalar: self.scalar.clone(),
135            auto: PhantomData,
136        }
137    }
138}
139
140unsafe impl<A: ScalarAllocator> Token for DynamicToken<A> {}
141
142unsafe impl<A: ScalarAllocator> OneShotIdentifier for Dynamic<A> {}
143unsafe impl<A: ScalarAllocator, P: PoolMut<A>> Identifier for Dynamic<A, P> {
144    type Token = DynamicToken<A>;
145
146    #[inline]
147    fn owns_token(&self, token: &Self::Token) -> bool { self.owns_token(token) }
148
149    #[inline]
150    fn token(&self) -> Self::Token { self.token() }
151}
152
153impl<A: ScalarAllocator<Scalar = ()>> crate::Init for DynamicToken<A> {
154    const INIT: Self = Self {
155        auto: PhantomData,
156        scalar: (),
157    };
158}
159
160impl<A: ScalarAllocator<Scalar = ()>> crate::Trivial for DynamicToken<A> {}
161impl<A: ScalarAllocator> Copy for DynamicToken<A> where A::Scalar: Copy {}
162impl<A: ScalarAllocator> Clone for DynamicToken<A> {
163    #[inline]
164    fn clone(&self) -> Self {
165        Self {
166            scalar: self.scalar.clone(),
167            auto: PhantomData,
168        }
169    }
170}
171
172impl<A: ScalarAllocator> Eq for DynamicToken<A> {}
173impl<A: ScalarAllocator> PartialEq for DynamicToken<A> {
174    #[inline]
175    fn eq(&self, other: &Self) -> bool { self.scalar == other.scalar }
176}
177
178impl<A: ScalarAllocator> Hash for DynamicToken<A>
179where
180    A::Scalar: Hash,
181{
182    #[inline]
183    fn hash<H: Hasher>(&self, state: &mut H) { self.scalar.hash(state) }
184}
185
186impl<A: ScalarAllocator> PartialOrd for DynamicToken<A>
187where
188    A::Scalar: PartialOrd,
189{
190    #[inline]
191    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.scalar.partial_cmp(&other.scalar) }
192}
193
194impl<A: ScalarAllocator> Ord for DynamicToken<A>
195where
196    A::Scalar: Ord,
197{
198    #[inline]
199    fn cmp(&self, other: &Self) -> Ordering { self.scalar.cmp(&other.scalar) }
200}