use crate::source_map::SourceMap;
use core::{
borrow::Borrow,
cmp::PartialEq,
fmt,
hash::{Hash, Hasher},
num::NonZeroU32,
ops::Deref,
str,
};
use fxhash::FxBuildHasher;
use indexmap::IndexSet;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::cell::RefCell;
include!(concat!(env!("OUT_DIR"), "/symbols_generated.rs"));
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Symbol(
#[serde(deserialize_with = "Symbol::serde_to_symbol")]
#[serde(serialize_with = "Symbol::serde_from_symbol")]
NonZeroU32,
);
impl Default for Symbol {
fn default() -> Self {
Symbol(NonZeroU32::MIN)
}
}
impl Symbol {
pub const fn new(index: u32) -> Self {
let index = index.saturating_add(1);
Self(match NonZeroU32::new(index) {
None => unreachable!(),
Some(x) => x,
})
}
pub fn intern(string: &str) -> Self {
with_session_globals(|session_globals| session_globals.symbol_interner.intern(string))
}
pub fn as_str<R>(self, s: &SessionGlobals, with: impl FnOnce(&str) -> R) -> R {
s.symbol_interner.get(self, with)
}
pub const fn as_u32(self) -> u32 {
self.0.get() - 1
}
fn serde_to_symbol<'de, D: Deserializer<'de>>(de: D) -> Result<NonZeroU32, D::Error> {
Ok(Symbol::intern(<&str>::deserialize(de)?).0)
}
fn serde_from_symbol<S: Serializer>(index: &NonZeroU32, ser: S) -> Result<S::Ok, S::Error> {
with_session_globals(|sg| Self(*index).as_str(sg, |s| ser.serialize_str(s)))
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with_session_globals(|s| self.as_str(s, |s| fmt::Debug::fmt(s, f)))
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with_session_globals(|s| self.as_str(s, |s| fmt::Display::fmt(s, f)))
}
}
pub struct SessionGlobals {
symbol_interner: Interner,
pub source_map: SourceMap,
}
impl Default for SessionGlobals {
fn default() -> Self {
Self { symbol_interner: Interner::prefilled(), source_map: SourceMap::default() }
}
}
scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
#[inline]
pub fn create_session_if_not_set_then<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
if !SESSION_GLOBALS.is_set() {
let sg = SessionGlobals::default();
SESSION_GLOBALS.set(&sg, || SESSION_GLOBALS.with(f))
} else {
SESSION_GLOBALS.with(f)
}
}
#[inline]
pub fn with_session_globals<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
SESSION_GLOBALS.with(f)
}
#[derive(Eq)]
enum InternedStr {
Static(&'static str),
Owned(Box<str>),
}
impl Borrow<str> for InternedStr {
fn borrow(&self) -> &str {
self.deref()
}
}
impl Deref for InternedStr {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
Self::Static(s) => s,
Self::Owned(s) => s,
}
}
}
impl PartialEq for InternedStr {
fn eq(&self, other: &InternedStr) -> bool {
self.deref() == other.deref()
}
}
impl Hash for InternedStr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().hash(state);
}
}
struct InnerInterner {
set: IndexSet<InternedStr, FxBuildHasher>,
}
struct Interner {
inner: RefCell<InnerInterner>,
}
impl Interner {
fn prefilled() -> Self {
Self::prefill(PRE_DEFINED)
}
fn prefill(init: &[&'static str]) -> Self {
let inner = InnerInterner {
set: init.iter().copied().map(InternedStr::Static).collect(),
};
Self { inner: RefCell::new(inner) }
}
fn intern(&self, string: &str) -> Symbol {
let InnerInterner { set } = &mut *self.inner.borrow_mut();
if let Some(sym) = set.get_index_of(string) {
return Symbol::new(sym as u32);
}
Symbol::new(set.insert_full(InternedStr::Owned(string.into())).0 as u32)
}
fn get<R>(&self, symbol: Symbol, with: impl FnOnce(&str) -> R) -> R {
let set = &self.inner.borrow().set;
with(set.get_index(symbol.as_u32() as usize).unwrap())
}
}