#[allow(unused_imports)]
use core::{
cell::UnsafeCell,
sync::atomic::{AtomicBool, AtomicPtr, Ordering},
};
use crate::Symbol;
#[repr(C)]
pub struct Site {
inner: UnsafeCell<&'static &'static str>,
#[cfg(any(miri, target_arch = "wasm32", feature = "debug-assertions"))]
initialized: AtomicBool,
}
unsafe impl Sync for Site {}
impl Site {
#[inline(always)]
#[must_use]
#[doc(hidden)]
pub const fn new(string: &'static &'static str) -> Self {
Self {
inner: UnsafeCell::new(string),
#[cfg(any(miri, target_arch = "wasm32", feature = "debug-assertions"))]
initialized: AtomicBool::new(false),
}
}
#[inline(always)]
pub unsafe fn get_string(&self) -> &'static &'static str {
unsafe {
*self.inner.get()
}
}
#[doc(hidden)]
#[inline(always)]
pub unsafe fn initialize(&self, interned: Symbol) {
#[cfg(any(miri, target_arch = "wasm32", feature = "debug-assertions"))]
{
self.initialized
.store(true, core::sync::atomic::Ordering::SeqCst);
}
unsafe {
*self.inner.get() = interned.inner();
}
}
#[inline(always)]
#[must_use]
pub unsafe fn get_ref_after_ctor(&'static self) -> &'static Symbol {
#[cfg(any(miri, target_arch = "wasm32"))]
unsafe {
return get_without_ctor_support(self);
}
#[cfg(not(any(miri, target_arch = "wasm32")))]
unsafe {
get_with_ctor_support(self)
}
}
#[inline(always)]
#[must_use]
pub unsafe fn get_after_ctor(&'static self) -> Symbol {
unsafe { *self.get_ref_after_ctor() }
}
}
#[inline(always)]
#[allow(unused)] unsafe fn get_with_ctor_support(site: &'static Site) -> &'static Symbol {
#[cfg(feature = "debug-assertions")]
{
assert!(
site.initialized.load(core::sync::atomic::Ordering::Relaxed),
"This `sym!()` call site has not been initialized by a static constructor. This can happen for the following reasons: \n
a) The current platform does not support static constructors (e.g., Miri)\n
b) The current crate is a dynamic library, but it reuses the registration from another crate, i.e., stringleton!(foreign_crate) is being used across a dynamic linking boundary\n
c) The call site is somehow reached without its containing binary having its static ctor functions called"
);
}
unsafe {
let ptr: *const &'static &'static str = site.inner.get();
let ptr: *const Symbol = ptr.cast();
&*ptr
}
}
#[inline(always)]
#[cfg(any(miri, target_arch = "wasm32"))]
unsafe fn get_without_ctor_support(site: &'static Site) -> &'static Symbol {
let inner_ptr: *mut *mut &'static str = {
let ptr: *mut &'static &'static str = site.inner.get();
ptr.cast()
};
if site.initialized.load(Ordering::SeqCst) {
unsafe {
return &*(inner_ptr as *const Symbol);
}
}
unsafe {
initialize_atomic(inner_ptr, &site.initialized);
}
unsafe {
&*(inner_ptr as *const Symbol)
}
}
#[cfg(any(miri, target_arch = "wasm32"))]
unsafe fn initialize_atomic(inner_ptr: *mut *mut &'static str, initialized: &'static AtomicBool) {
let atomic_inner: &AtomicPtr<&'static str> = unsafe {
AtomicPtr::from_ptr(inner_ptr)
};
let stored_value: &'static &'static str = unsafe {
&*(atomic_inner.load(Ordering::Relaxed))
};
let interned = crate::Registry::global().get_or_insert_static(stored_value);
let ptr = core::ptr::from_ref(interned.inner());
atomic_inner.store(ptr as *mut &'static str, Ordering::SeqCst);
initialized.store(true, Ordering::SeqCst);
}