pui_core/
scalar.rs

1//! [`Scalar`] defined types that can be used to back the [`ScalarAllocator`]s
2//!
3//! `ScalarAllocator`s are types that produce a sequence of `Scalar`s with no
4//! duplicates in the sequence
5
6use core::{
7    cell::Cell,
8    cmp,
9    hash::{Hash, Hasher},
10    num,
11    sync::atomic::{Ordering::*, *},
12};
13
14use radium::Radium;
15
16macro_rules! inc {
17    (fn inc($self:ident) -> Option<Self> { $($block:tt)* }) => {
18        #[doc(hidden)]
19        const LOCAL_INIT: Self::Local = Self::Local::new(0);
20        #[doc(hidden)]
21        const ATOMIC_INIT: Self::Atomic = Self::Atomic::new(0);
22
23        #[inline]
24        #[doc(hidden)]
25        fn inc_local($self: &Self::Local) -> Option<Self> { $($block)* }
26        #[doc(hidden)]
27        fn inc_atomic($self: &Self::Atomic) -> Option<Self> { $($block)* }
28    };
29}
30
31/// Simple types that can be produced from [`ScalarAllocator`], these types
32/// are known to have stable [`Clone`] and [`Eq`] implementations that allow
33/// them to be used in the implementation of [`Token`](crate::Token)
34///
35/// This trait is used by the [`scalar_allocator`](crate::scalar_allocator)
36/// macro's implementation to verify safety, and provide pieces of implementation.
37///
38/// All contents of `Scalar` that are hidden from documentation, have
39/// ***NO STABILITY GUARANTEES WHATSOEVER***
40pub trait Scalar: crate::Seal + Copy + Ord + Hash {
41    #[doc(hidden)]
42    type Local;
43    #[doc(hidden)]
44    type Atomic;
45
46    #[doc(hidden)]
47    const LOCAL_INIT: Self::Local;
48    #[doc(hidden)]
49    const ATOMIC_INIT: Self::Atomic;
50
51    #[doc(hidden)]
52    fn inc_local(local: &Self::Local) -> Option<Self>;
53    #[doc(hidden)]
54    fn inc_atomic(local: &Self::Atomic) -> Option<Self>;
55}
56
57/// A opaque type that allows the implementation of [`Pool`](crate::pool::Pool)/
58/// [`PoolMut`](crate::pool::PoolMut) to be safe
59pub struct OpaqueScalar<S: ScalarAllocator>(S::Scalar);
60
61impl<S: ScalarAllocator> OpaqueScalar<S> {
62    /// Create a new [`OpaqueScalar`]
63    ///
64    /// # Safety
65    ///
66    /// * You must have goteen this scalar from `OpaqueScalar::into_inner`
67    /// * You must create at most 1 `OpaqueScalar` from this `scalar`
68    pub unsafe fn new(scalar: S::Scalar) -> Self { Self(scalar) }
69
70    /// Get the underlying scalar from `OpaqueScalar`
71    pub fn into_inner(self) -> S::Scalar { self.0 }
72}
73
74/// A type that produces a sequence of unique [`Scalar`]s
75///
76/// # Safety
77///
78/// * `AutoTraits` should not be `Send` or `Sync` if the sequence
79///    is only guaranteed to be unique wihtin a given thread.
80/// * `alloc` should never produces two `Scalar`s that compare equal
81///    to each other
82pub unsafe trait ScalarAllocator {
83    /// The types in the sequence
84    type Scalar: Clone + Eq;
85    /// This type's autotraits restrictions that will
86    /// be applied to both `Dynamic` and `DynamicToken`
87    type AutoTraits;
88
89    /// The next item in the sequence
90    ///
91    /// # Panic
92    ///
93    /// If the sequence is exhausted, `alloc` may panic
94    fn alloc() -> Self::Scalar;
95}
96
97impl<A: ScalarAllocator> Eq for OpaqueScalar<A> {}
98impl<A: ScalarAllocator> PartialEq for OpaqueScalar<A> {
99    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
100}
101
102impl<A: ScalarAllocator> Hash for OpaqueScalar<A>
103where
104    A::Scalar: Hash,
105{
106    fn hash<H: Hasher>(&self, state: &mut H) { self.0.hash(state) }
107}
108
109impl<A: ScalarAllocator> PartialOrd for OpaqueScalar<A>
110where
111    A::Scalar: PartialOrd,
112{
113    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { self.0.partial_cmp(&other.0) }
114}
115
116impl<A: ScalarAllocator> Ord for OpaqueScalar<A>
117where
118    A::Scalar: Ord,
119{
120    fn cmp(&self, other: &Self) -> cmp::Ordering { self.0.cmp(&other.0) }
121}
122
123impl crate::Seal for () {}
124impl Scalar for () {
125    #[doc(hidden)]
126    type Local = Cell<bool>;
127    #[doc(hidden)]
128    type Atomic = AtomicBool;
129
130    #[doc(hidden)]
131    const LOCAL_INIT: Self::Local = Cell::new(false);
132    #[doc(hidden)]
133    const ATOMIC_INIT: Self::Atomic = AtomicBool::new(false);
134
135    #[doc(hidden)]
136    #[inline]
137    fn inc_local(local: &Self::Local) -> Option<Self> {
138        match local.replace(true) {
139            false => Some(()),
140            true => None,
141        }
142    }
143
144    #[doc(hidden)]
145    #[inline]
146    fn inc_atomic(local: &Self::Atomic) -> Option<Self> {
147        match local.swap(true, Relaxed) {
148            false => Some(()),
149            true => None,
150        }
151    }
152}
153
154#[doc(hidden)]
155#[macro_export]
156macro_rules! __scalar_allocator {
157    (@create $name:ident) => {
158        impl $name {
159            /// Create a new `Dynamic<Self>` that implements `OneShotIdentifier`
160            pub fn oneshot() -> $crate::dynamic::Dynamic<Self> { $crate::dynamic::Dynamic::with_alloc() }
161
162            /// Create a new `Dynamic<Self>` with the given pool
163            pub fn with_pool<P: $crate::pool::PoolMut<Self>>(pool: P) -> $crate::dynamic::Dynamic<Self, P> {
164                $crate::dynamic::Dynamic::with_alloc_and_pool(pool)
165            }
166        }
167    };
168    (@create type $name:ident) => {
169        impl $name {
170            /// Create a `Dynamic<Self>` that implements `OneShotIdentifier`
171            pub fn oneshot() -> $crate::dynamic::Dynamic<Self> { $crate::dynamic::Dynamic::with_alloc() }
172
173            /// Create a `Dynamic<Self, Self>` that implements `Identifier`
174            pub fn reuse() -> $crate::dynamic::Dynamic<Self, Self> { Self::with_pool(Self) }
175
176            /// Create a `Dynamic<Self, P>` with the given pool
177            pub fn with_pool<P: $crate::pool::PoolMut<Self>>(pool: P) -> $crate::dynamic::Dynamic<Self, P> {
178                $crate::dynamic::Dynamic::with_alloc_and_pool(pool)
179            }
180        }
181    };
182    (
183        $(#[$meta:meta])*
184        $v:vis struct $name:ident($scalar:ty);
185    ) => {
186        $(#[$meta])*
187        $v struct $name;
188
189        unsafe impl $crate::scalar::ScalarAllocator for $name {
190            type Scalar = $scalar;
191            type AutoTraits = ();
192
193            fn alloc() -> Self::Scalar {
194                static __SCALAR_ALLOCATOR: <$scalar as $crate::scalar::Scalar>::Atomic = <$scalar as $crate::scalar::Scalar>::ATOMIC_INIT;
195
196                $crate::scalar::Scalar::inc_atomic(&__SCALAR_ALLOCATOR)
197                    .expect(concat!(
198                        "Could not allocate more scalars from ",
199                        stringify!($name),
200                    ))
201            }
202        }
203    };
204    (
205        $(#[$meta:meta])*
206        $v:vis thread_local struct $name:ident($scalar:ty);
207    ) => {
208        $(#[$meta])*
209        $v struct $name;
210
211        unsafe impl $crate::scalar::ScalarAllocator for $name {
212            type Scalar = $scalar;
213            type AutoTraits = $crate::export::NoSendSync;
214
215            fn alloc() -> Self::Scalar {
216                $crate::export::thread_local! {
217                    static __SCALAR_ALLOCATOR: <$scalar as $crate::scalar::Scalar>::Local = <$scalar as $crate::scalar::Scalar>::LOCAL_INIT;
218                }
219
220                __SCALAR_ALLOCATOR.with(|scalar| {
221                    $crate::scalar::Scalar::inc_local(scalar)
222                }).expect(concat!(
223                    "Could not allocate more scalars from ",
224                    stringify!($name),
225                ))
226            }
227        }
228    };
229}
230
231/// Construct a new [`ScalarAllocator`]
232///
233/// For example:
234/// ```rust
235/// pui_core::scalar_allocator! {
236///     /// Your scalar allocator
237///     struct Foo;
238/// }
239///
240/// pui_core::scalar_allocator! {
241///     #[derive(Debug)]
242///     pub struct Bar(u8);
243/// }
244///
245/// let foo = Foo;
246/// let bar = Bar;
247/// ```
248///
249/// `Foo: ScalarAllocator`, and because it is guaratneed to
250/// produce one scalar, it also implements [`Pool`](crate::pool::Pool)
251/// with a [`Flag`](crate::pool::Flag). `Bar: ScalarAllocator`, as you
252/// can see it also has a paramter: `u8`. You can use any type that
253/// implements [`Scalar`] in it's place. This type defines what sort
254/// the inner type of [`Dynamic`](crate::dynamic::Dynamic)
255/// and [`DynamicToken`](crate::dynamic::DynamicToken).
256///
257/// For example you could use `u64`:
258///
259/// ```
260/// pui_core::scalar_allocator! {
261///     #[derive(Debug)]
262///     pub struct Bar(u64);
263/// }
264///```
265///
266/// You can also prefix `struct` with `thread_local` to get a [`ScalarAllocator`]
267/// that is only produces unique scalars on within a given thread
268///
269/// For example:
270/// ```rust
271/// pui_core::scalar_allocator! {
272///     thread_local struct Foo;
273/// }
274///
275/// pui_core::scalar_allocator! {
276///     thread_local struct Bar(u8);
277/// }
278///
279/// let foo = Foo;
280/// let bar = Bar;
281/// ```
282#[macro_export]
283macro_rules! scalar_allocator {
284    (
285        $(#[$meta:meta])*
286        $v:vis struct $name:ident;
287    ) => {
288        $crate::__scalar_allocator! {
289            $(#[$meta])*
290            $v struct $name(());
291        }
292
293        $crate::__scalar_allocator! {
294            @create type $name
295        }
296
297        $crate::__global_pool! {
298            $name($crate::pool::Flag<$name>)
299        }
300    };
301    (
302        $(#[$meta:meta])*
303        $v:vis thread_local struct $name:ident;
304    ) => {
305        $crate::__scalar_allocator! {
306            $(#[$meta])*
307            $v thread_local struct $name(());
308        }
309
310        $crate::__scalar_allocator! {
311            @create type $name
312        }
313
314        $crate::__global_pool! {
315            thread_local $name($crate::export::LocalFlag<$name>)
316        }
317    };
318    (
319        $(#[$meta:meta])*
320        $v:vis struct $name:ident($scalar:ty);
321    ) => {
322        $crate::__scalar_allocator! {
323            $(#[$meta])*
324            $v struct $name($scalar);
325        }
326
327        $crate::__scalar_allocator! {
328            @create $name
329        }
330    };
331    (
332        $(#[$meta:meta])*
333        $v:vis thread_local struct $name:ident($scalar:ty);
334    ) => {
335        $crate::__scalar_allocator! {
336            $(#[$meta])*
337            $v thread_local struct $name($scalar);
338        }
339
340        $crate::__scalar_allocator! {
341            @create $name
342        }
343    };
344}
345
346macro_rules! norm_prim {
347    ($($prim:ty => $atomic:ty, $nonzero:ty,)*) => {$(
348        impl crate::Seal for $prim {}
349        impl Scalar for $prim {
350            #[doc(hidden)]
351            type Local = Cell<$prim>;
352            #[doc(hidden)]
353            type Atomic = $atomic;
354
355            inc! {
356                fn inc(this) -> Option<Self> {
357                    let mut value = this.load(Relaxed);
358
359                    loop {
360                        let next = value.checked_add(1)?;
361
362                        if let Err(current) = this.compare_exchange_weak(value, next, Acquire, Relaxed) {
363                            value = current
364                        } else {
365                            return Some(value)
366                        }
367                    }
368                }
369            }
370        }
371
372        impl crate::Seal for $nonzero {}
373        impl Scalar for $nonzero {
374            #[doc(hidden)]
375            type Local = Cell<$prim>;
376            #[doc(hidden)]
377            type Atomic = $atomic;
378
379            inc! {
380                fn inc(this) -> Option<Self> {
381                    let mut value = this.load(Relaxed);
382
383                    loop {
384                        let next = value.checked_add(1)?;
385
386                        if let Err(current) = this.compare_exchange_weak(value, next, Acquire, Relaxed) {
387                            value = current
388                        } else {
389                            return <$nonzero>::new(value.wrapping_add(1))
390                        }
391                    }
392                }
393            }
394        }
395    )*};
396}
397
398norm_prim! {
399    u8 => AtomicU8, num::NonZeroU8,
400    u16 => AtomicU16, num::NonZeroU16,
401    u32 => AtomicU32, num::NonZeroU32,
402    u64 => AtomicU64, num::NonZeroU64,
403    usize => AtomicUsize, num::NonZeroUsize,
404}