cl-structures: Cleanup for GlobalSym

This commit is contained in:
John 2024-04-24 18:11:21 -05:00
parent be604b7b45
commit ede00c3c86
2 changed files with 46 additions and 38 deletions

View File

@ -7,42 +7,48 @@ use std::{
sync::{OnceLock, RwLock}, sync::{OnceLock, RwLock},
}; };
/// Holds a globally accessible [Interner] which uses [GlobalSym] as its [Symbol]
static GLOBAL_INTERNER: OnceLock<RwLock<Interner<GlobalSym>>> = OnceLock::new(); static GLOBAL_INTERNER: OnceLock<RwLock<Interner<GlobalSym>>> = 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 /// Copies of that string can be obtained with [GlobalSym::get] or [String::try_from].
/// 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<GlobalSym> {
GLOBAL_INTERNER.get()?.read().ok()?.get(s)
}
/// Gets the [String] associated with this [GlobalSym], if there is one
/// ///
/// Returns none if the global symbol table is poisoned. /// New strings can be interned with [GlobalSym::new] or [GlobalSym::from]
pub fn get_string(sym: GlobalSym) -> Option<String> {
sym.try_into().ok()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GlobalSym(NonZeroU32); pub struct GlobalSym(NonZeroU32);
impl GlobalSym { 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<Self> { pub fn try_from_str(value: &str) -> Option<Self> {
GLOBAL_INTERNER.get()?.read().ok()?.get(value) 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> {
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<T>(&self, f: impl Fn(&str) -> T) -> Option<T> {
Some(f(GLOBAL_INTERNER.get()?.read().ok()?.get_str(*self)?))
}
} }
impl Display for GlobalSym { impl Display for GlobalSym {
@ -70,7 +76,7 @@ impl Symbol for GlobalSym {
} }
} }
impl From<&str> for GlobalSym { impl<T: AsRef<str>> From<T> for GlobalSym {
/// Converts to this type from the input type. /// Converts to this type from the input type.
/// ///
/// # Blocks /// # Blocks
@ -78,12 +84,8 @@ impl From<&str> for GlobalSym {
/// ///
/// # May Panic /// # May Panic
/// Panics if the Global Interner's lock has been poisoned by a panic in another thread /// Panics if the Global Interner's lock has been poisoned by a panic in another thread
fn from(value: &str) -> Self { fn from(value: T) -> Self {
GLOBAL_INTERNER Self::new(value.as_ref())
.get_or_init(Default::default)
.write()
.expect("global interner should not be poisoned in another thread")
.get_or_insert(value)
} }
} }

View File

@ -10,19 +10,25 @@ fn globalsym_from_returns_unique_value_for_unique_keys() {
assert_eq!(foo_baz, GlobalSym::from("foo_baz")); assert_eq!(foo_baz, GlobalSym::from("foo_baz"));
} }
#[test] #[test]
fn get_returns_none_before_init() { fn try_from_str_returns_none_before_init() {
if let Some(value) = get("") { if let Some(value) = GlobalSym::try_from_str("") {
panic!("{value}") panic!("{value}")
} }
} }
#[test] #[test]
fn get_returns_some_when_key_exists() { fn try_from_str_returns_some_when_key_exists() {
let _ = GlobalSym::from("foo_bar"); let _ = GlobalSym::from("foo_bar");
assert!(dbg!(get("foo_bar")).is_some()); assert!(dbg!(GlobalSym::try_from_str("foo_bar")).is_some());
} }
#[test] #[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"); 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());
} }