use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::{OnceLock, RwLock};
use std::sync::atomic::{AtomicU32, Ordering};
use crate::error::Error;
use crate::symbols;
pub mod iterator;
type CodeStoreType = RwLock<HashMap<String, u32>>;
type NameStoreType = RwLock<HashMap<u32, String>>;
static CODE_REGISTER: OnceLock<CodeStoreType> = OnceLock::new();
static NAME_REGISTER: OnceLock<NameStoreType> = OnceLock::new();
static SYMBOL_ID: AtomicU32 = AtomicU32::new(100);
pub fn get_code(name: &str) -> Result<u32, Error> {
let mut register = get_code_register().write()?;
let name = name.trim().to_string();
if name.is_empty() {
return Err(Error::general("name should not be an empty string"))
}
if let Some(code) = register.get(&name) {
return Ok(*code);
}
let code = SYMBOL_ID.fetch_add(1, Ordering::SeqCst);
register.insert(name.clone(), code);
drop(register);
let mut register = get_name_register().write()?;
register.insert(code, name);
drop(register);
Ok(code)
}
pub fn get_name(code: u32) -> Option<String> {
let register = get_name_register().read().ok()?;
register.get(&code).cloned()
}
fn get_code_register() -> &'static CodeStoreType {
CODE_REGISTER.get_or_init(|| {
RwLock::new(HashMap::new())
})
}
fn get_name_register() -> &'static NameStoreType {
NAME_REGISTER.get_or_init(|| {
RwLock::new(HashMap::new())
})
}
#[derive(Debug, Clone, Copy)]
pub struct Symbol {
code: u32
}
impl PartialEq for Symbol {
fn eq(&self, other: &Self) -> bool {
self.code == other.code
}
}
impl Eq for Symbol {}
impl Hash for Symbol {
fn hash<H: Hasher>(&self, state: &mut H) {
self.code.hash(state);
}
}
impl Symbol {
#[inline]
pub fn from_code(code: u32) -> Self {
Symbol {
code
}
}
pub fn build<S: AsRef<str>>(name: S) -> Result<Symbol, Error>{
let name = name.as_ref();
Ok(Self::from_code(get_code(name)?))
}
pub fn code(&self) -> u32 {
self.code
}
pub fn name(&self) -> Option<String> {
get_name(self.code)
}
}
impl Display for Symbol {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Some(name) = get_name(self.code) {
return f.write_str(name.as_str());
}
write!(f, "code:{}", self.code)
}
}
pub trait SymbolStore {
fn add_symbol(&self, name: &str) -> crate::Result<Symbol>;
fn get_symbol(&self, name: &str) -> Option<Symbol>;
}
impl SymbolStore for RefCell<HashSet<u32>> {
fn add_symbol(&self, name: &str) -> crate::Result<Symbol> {
let code = symbols::get_code(name)?;
let mut map = self.borrow_mut();
map.insert(code);
Ok(Symbol::from_code(code))
}
fn get_symbol(&self, name: &str) -> Option<Symbol> {
let code = get_code(name).ok()?;
let symbols = self.borrow();
symbols.get(&code)
.cloned()
.map(Symbol::from_code)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn register_is_idempotent() {
let hello = get_code("Hello").unwrap();
let bye = get_code("bye").unwrap();
assert_eq!(get_code("Hello").unwrap(), hello);
assert_ne!(get_code("Hello").unwrap(), bye);
}
#[test]
fn register_records_names() {
let hello = "hello";
let hcode = get_code(hello).unwrap();
assert_eq!(hello, get_name(hcode).unwrap());
}
#[test]
fn no_name() {
assert!(get_name(43_432_444).is_none());
}
#[test]
fn empty_string_is_error() {
assert!(get_code("").is_err());
assert!(get_code(" ").is_err());
assert!(get_code(" d").is_ok());
}
#[test]
fn names_are_trimmed() {
let code = get_code(" d ").unwrap();
let code2 = get_code("d").unwrap();
assert_eq!(code, code2);
assert_eq!(get_name(code).unwrap(), "d");
assert_eq!(get_name(code2).unwrap(), "d");
}
}