use core::marker::PhantomData;
use core::mem::transmute;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicUsize, Ordering};
pub struct Global<T> {
address: AtomicUsize,
init: fn() -> usize,
_pd: PhantomData<T>,
}
impl<T> Global<T> {
#[doc(hidden)]
pub const fn new(init: fn() -> usize) -> Self {
Self {
address: AtomicUsize::new(0),
init,
_pd: PhantomData,
}
}
pub fn address(&self) -> usize {
let mut value = self.address.load(Ordering::Acquire);
if value == 0 {
value = (self.init)();
self.address.store(value, Ordering::Release);
}
value
}
pub fn force(&self) {
_ = self.address();
}
}
impl<T> Deref for Global<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
let value = self.address();
unsafe { transmute(value) }
}
}
impl<T> DerefMut for Global<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
let value = self.address();
unsafe { transmute(value) }
}
}
#[macro_export]
macro_rules! global {
{
$(
$vs:vis extern $gname:ident: $gtype:ty = $( ($resolver:path) )? $module:literal $sep:tt $offset:expr;
)*
} => {
$(
$vs static $gname: $crate::Global<$gtype> = $crate::Global::new(
|| unsafe { ($crate::__resolver!( $($resolver)? ))( $crate::__resolve_by!($sep $module, $offset) ) }
);
)*
};
}