scope_local/
item.rs

1use core::{
2    alloc::Layout,
3    marker::PhantomData,
4    ops::{Deref, DerefMut},
5    ptr::{NonNull, addr_of},
6};
7
8use crate::scope::{ActiveScope, Scope};
9
10#[doc(hidden)]
11pub struct Item {
12    pub layout: Layout,
13    pub init: fn(NonNull<()>),
14    pub drop: fn(NonNull<()>),
15}
16
17pub(crate) struct Registry;
18
19impl Deref for Registry {
20    type Target = [Item];
21
22    fn deref(&self) -> &Self::Target {
23        unsafe extern "Rust" {
24            static __start_scope_local: Item;
25            static __stop_scope_local: Item;
26        }
27        let start = addr_of!(__start_scope_local) as usize;
28        let len = (addr_of!(__stop_scope_local) as usize - start) / core::mem::size_of::<Item>();
29        unsafe { core::slice::from_raw_parts(start as *const Item, len) }
30    }
31}
32
33impl Item {
34    #[inline]
35    pub(crate) fn index(&'static self) -> usize {
36        unsafe { (self as *const Item).offset_from_unsigned(Registry.as_ptr()) }
37    }
38}
39
40/// A scope-local item.
41pub struct LocalItem<T> {
42    item: &'static Item,
43    _p: PhantomData<T>,
44}
45
46impl<T> LocalItem<T> {
47    #[doc(hidden)]
48    #[inline]
49    pub const fn new(item: &'static Item) -> Self {
50        Self {
51            item,
52            _p: PhantomData,
53        }
54    }
55
56    /// Returns a reference to this item within the given scope.
57    pub fn scope<'scope>(&self, scope: &'scope Scope) -> ScopeItem<'scope, T> {
58        ScopeItem {
59            item: self.item,
60            scope,
61            _p: PhantomData,
62        }
63    }
64
65    /// Returns a mutable reference to this item within the given scope.
66    pub fn scope_mut<'scope>(&self, scope: &'scope mut Scope) -> ScopeItemMut<'scope, T> {
67        ScopeItemMut {
68            item: self.item,
69            scope,
70            _p: PhantomData,
71        }
72    }
73}
74
75impl<T> Deref for LocalItem<T> {
76    type Target = T;
77
78    #[inline]
79    fn deref(&self) -> &Self::Target {
80        ActiveScope::get(self.item).as_ref()
81    }
82}
83
84/// A reference to a scope-local item within a specific scope.
85///
86/// Created by [`LocalItem::scope`].
87pub struct ScopeItem<'scope, T> {
88    item: &'static Item,
89    scope: &'scope Scope,
90    _p: PhantomData<T>,
91}
92
93impl<'scope, T> Deref for ScopeItem<'scope, T> {
94    type Target = T;
95
96    #[inline]
97    fn deref(&self) -> &Self::Target {
98        self.scope.get(self.item).as_ref()
99    }
100}
101
102/// A mutable reference to a scope-local item within a specific scope.
103///
104/// Created by [`LocalItem::scope_mut`].
105pub struct ScopeItemMut<'scope, T> {
106    item: &'static Item,
107    scope: &'scope mut Scope,
108    _p: PhantomData<T>,
109}
110
111impl<'scope, T> Deref for ScopeItemMut<'scope, T> {
112    type Target = T;
113
114    #[inline]
115    fn deref(&self) -> &Self::Target {
116        self.scope.get(self.item).as_ref()
117    }
118}
119
120impl<'scope, T> DerefMut for ScopeItemMut<'scope, T> {
121    #[inline]
122    fn deref_mut(&mut self) -> &mut Self::Target {
123        self.scope.get_mut(self.item).as_mut()
124    }
125}
126
127/// Define a scope-local item.
128///
129/// # Example
130///
131/// ```
132/// # use std::sync::atomic::AtomicUsize;
133/// # use scope_local::scope_local;
134/// scope_local! {
135///     /// An integer.
136///     pub static MY_I32: i32 = 42;
137///     /// An atomic integer.
138///     pub static MY_ATOMIC_USIZE: AtomicUsize = AtomicUsize::new(0);
139/// }
140/// ```
141#[macro_export]
142macro_rules! scope_local {
143    ( $( $(#[$attr:meta])* $vis:vis static $name:ident: $ty:ty = $default:expr; )+ ) => {
144        $(
145            $(#[$attr])*
146            $vis static $name: $crate::LocalItem<$ty> = {
147                #[unsafe(link_section = "scope_local")]
148                static ITEM: $crate::Item = $crate::Item {
149                    layout: core::alloc::Layout::new::<$ty>(),
150                    init: |ptr| {
151                        let val: $ty = $default;
152                        unsafe { ptr.cast().write(val) }
153                    },
154                    drop: |ptr| unsafe {
155                        ptr.cast::<$ty>().drop_in_place();
156                    },
157                };
158
159                $crate::LocalItem::new(&ITEM)
160            };
161        )+
162    }
163}