diff --git a/compiler/cl-structures/src/arena/global_intern.rs b/compiler/cl-structures/src/arena/global_intern.rs index dcb238c..34e3109 100644 --- a/compiler/cl-structures/src/arena/global_intern.rs +++ b/compiler/cl-structures/src/arena/global_intern.rs @@ -7,42 +7,48 @@ use std::{ sync::{OnceLock, RwLock}, }; +/// Holds a globally accessible [Interner] which uses [GlobalSym] as its [Symbol] static GLOBAL_INTERNER: OnceLock>> = OnceLock::new(); -/// Gets the [GlobalSym] associated with this string, if there is one, or creates a new one +/// A unique identifier corresponding to a particular interned [String]. /// -/// # Blocks -/// Locks the Global Interner for writing. If it is already locked, -/// # May Panic -/// T -pub fn get_or_insert(s: &str) -> GlobalSym { - GLOBAL_INTERNER - .get_or_init(Default::default) - .write() - .expect("global interner should not have been held by a panicked thread") - .get_or_insert(s) -} - -/// Gets the [GlobalSym] associated with this string, if there is one -pub fn get(s: &str) -> Option { - GLOBAL_INTERNER.get()?.read().ok()?.get(s) -} - -/// Gets the [String] associated with this [GlobalSym], if there is one +/// Copies of that string can be obtained with [GlobalSym::get] or [String::try_from]. /// -/// Returns none if the global symbol table is poisoned. -pub fn get_string(sym: GlobalSym) -> Option { - sym.try_into().ok() -} - +/// New strings can be interned with [GlobalSym::new] or [GlobalSym::from] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct GlobalSym(NonZeroU32); impl GlobalSym { - /// Gets a [GlobalSym] associated with the given string, if one exists + /// Gets the interned [GlobalSym] for the given value, or interns a new one. + /// + /// # Blocks + /// This conversion blocks if the Global Interner lock is held. + /// + /// # May Panic + /// Panics if the Global Interner's lock has been poisoned by a panic in another thread + pub fn new(value: &str) -> Self { + GLOBAL_INTERNER + .get_or_init(Default::default) + .write() + .expect("global interner should not be poisoned in another thread") + .get_or_insert(value) + } + /// Gets a [GlobalSym] associated with the given string, if one already exists pub fn try_from_str(value: &str) -> Option { GLOBAL_INTERNER.get()?.read().ok()?.get(value) } + + /// Gets a copy of the value of the [GlobalSym] + // TODO: Make this copy-less + pub fn get(self) -> Option { + String::try_from(self).ok() + } + + /// Looks up the string associated with this [GlobalSym], + /// and performs a transformation on it if it exists. + pub fn map(&self, f: impl Fn(&str) -> T) -> Option { + Some(f(GLOBAL_INTERNER.get()?.read().ok()?.get_str(*self)?)) + } } impl Display for GlobalSym { @@ -70,7 +76,7 @@ impl Symbol for GlobalSym { } } -impl From<&str> for GlobalSym { +impl> From for GlobalSym { /// Converts to this type from the input type. /// /// # Blocks @@ -78,12 +84,8 @@ impl From<&str> for GlobalSym { /// /// # May Panic /// Panics if the Global Interner's lock has been poisoned by a panic in another thread - fn from(value: &str) -> Self { - GLOBAL_INTERNER - .get_or_init(Default::default) - .write() - .expect("global interner should not be poisoned in another thread") - .get_or_insert(value) + fn from(value: T) -> Self { + Self::new(value.as_ref()) } } diff --git a/compiler/cl-structures/src/arena/global_intern/tests.rs b/compiler/cl-structures/src/arena/global_intern/tests.rs index 1bcfff0..cedc609 100644 --- a/compiler/cl-structures/src/arena/global_intern/tests.rs +++ b/compiler/cl-structures/src/arena/global_intern/tests.rs @@ -10,19 +10,25 @@ fn globalsym_from_returns_unique_value_for_unique_keys() { assert_eq!(foo_baz, GlobalSym::from("foo_baz")); } #[test] -fn get_returns_none_before_init() { - if let Some(value) = get("") { +fn try_from_str_returns_none_before_init() { + if let Some(value) = GlobalSym::try_from_str("") { panic!("{value}") } } #[test] -fn get_returns_some_when_key_exists() { +fn try_from_str_returns_some_when_key_exists() { let _ = GlobalSym::from("foo_bar"); - assert!(dbg!(get("foo_bar")).is_some()); + assert!(dbg!(GlobalSym::try_from_str("foo_bar")).is_some()); } #[test] -fn get_returns_the_same_thing_as_globalsym_from() { +fn try_from_str_returns_the_same_thing_as_globalsym_from() { let foo_bar = GlobalSym::from("foo_bar"); - assert_eq!(Some(foo_bar), get("foo_bar")); + assert_eq!(Some(foo_bar), GlobalSym::try_from_str("foo_bar")); +} + +#[test] +fn map_works() { + let foo_bar = GlobalSym::from("foo_bar"); + assert!(foo_bar.map(|sym| "foo_bar" == sym).unwrap()); }