symbol_table/
global.rs

1use crate::*;
2
3use std::mem::MaybeUninit;
4use std::str::FromStr;
5use std::sync::Once;
6
7#[cfg(feature = "global")]
8/// Macro for creating symbols from &'static str. Useful for commonly used symbols known at compile time.
9/// This is faster then GlobalSymbol::from(s) by avoiding mutex contention.
10///
11/// # Examples
12///
13/// ```
14/// use symbol_table::static_symbol;
15/// use symbol_table::GlobalSymbol;
16///
17/// let hello = static_symbol!("hello");
18/// assert_eq!(hello, GlobalSymbol::from("hello"));
19///
20/// // The same symbol is returned on subsequent calls
21/// let hello2 = static_symbol!("hello");
22/// assert_eq!(hello, hello2);
23/// ```
24#[macro_export]
25macro_rules! static_symbol {
26    ($s:literal) => {{
27        use std::sync::OnceLock;
28        static SYMBOL: OnceLock<GlobalSymbol> = OnceLock::new();
29
30        *SYMBOL.get_or_init(|| GlobalSymbol::from($s))
31    }};
32}
33
34/// A interned string in the global symbol table.
35///
36/// This requires the `global` feature on the crate.
37///
38/// [`GlobalSymbol`] is a wrapper around [`Symbol`] that knows to refer to a
39/// built-in, global [`SymbolTable`]. Strings into the global table are never freed.
40///
41/// This enables a lot of convenience methods and trait implementations over
42/// [`GlobalSymbol`] (see below). In particular,
43///   you can convert it to `&'static str`,
44///   convert [`From`] and [`Into`] a `&str`,
45///   and de/serialize using [`serde`](https://serde.rs) if the `serde` feature is enabled.
46#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize))]
48#[cfg_attr(feature = "serde", serde(into = "&'static str"))]
49pub struct GlobalSymbol(Symbol);
50
51impl From<NonZeroU32> for GlobalSymbol {
52    fn from(n: NonZeroU32) -> Self {
53        Self(Symbol::from(n))
54    }
55}
56
57impl From<GlobalSymbol> for NonZeroU32 {
58    fn from(n: GlobalSymbol) -> Self {
59        n.0.into()
60    }
61}
62
63fn singleton() -> &'static SymbolTable {
64    static mut SINGLETON: MaybeUninit<SymbolTable> = MaybeUninit::uninit();
65    static ONCE: Once = Once::new();
66
67    // SAFETY:
68    // - writing to the singleton is OK because we only do it one time
69    // - the ONCE guarantees that SINGLETON is init'ed before assume_init_ref
70    unsafe {
71        ONCE.call_once(|| {
72            SINGLETON.write(SymbolTable::new());
73        });
74        SINGLETON.assume_init_ref()
75    }
76}
77
78impl GlobalSymbol {
79    /// Intern a string into the global symbol table.
80    pub fn new(s: impl AsRef<str>) -> Self {
81        s.as_ref().into()
82    }
83
84    /// Convert this symbol into the string in the static, global symbol table.
85    pub fn as_str(&self) -> &'static str {
86        (*self).into()
87    }
88}
89
90impl From<&str> for GlobalSymbol {
91    fn from(s: &str) -> Self {
92        GlobalSymbol(singleton().intern(s))
93    }
94}
95
96impl From<String> for GlobalSymbol {
97    fn from(s: String) -> Self {
98        GlobalSymbol(singleton().intern(&s))
99    }
100}
101
102impl From<&String> for GlobalSymbol {
103    fn from(s: &String) -> Self {
104        GlobalSymbol(singleton().intern(s))
105    }
106}
107
108impl FromStr for GlobalSymbol {
109    type Err = std::convert::Infallible;
110
111    fn from_str(s: &str) -> Result<Self, Self::Err> {
112        Ok(s.into())
113    }
114}
115
116impl From<GlobalSymbol> for &'static str {
117    fn from(sym: GlobalSymbol) -> Self {
118        singleton().resolve(sym.0)
119    }
120}
121
122impl std::fmt::Debug for GlobalSymbol {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        std::fmt::Debug::fmt(self.as_str(), f)
125    }
126}
127
128impl std::fmt::Display for GlobalSymbol {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        std::fmt::Display::fmt(self.as_str(), f)
131    }
132}
133
134#[cfg(feature = "serde")]
135struct StrVisitor;
136
137#[cfg(feature = "serde")]
138impl<'de> serde::de::Visitor<'de> for StrVisitor {
139    type Value = GlobalSymbol;
140
141    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
142        formatter.write_str("a &str")
143    }
144
145    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
146    where
147        E: serde::de::Error,
148    {
149        Ok(v.into())
150    }
151}
152
153#[cfg(feature = "serde")]
154impl<'de> serde::Deserialize<'de> for GlobalSymbol {
155    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
156    where
157        D: serde::Deserializer<'de>,
158    {
159        let x = deserializer.deserialize_str(StrVisitor)?;
160        Ok(x)
161    }
162}