1use std::cell::RefCell;
12use std::collections::{HashMap, HashSet};
13use std::fmt::{Display, Formatter};
14use std::hash::{Hash, Hasher};
15use std::sync::{OnceLock, RwLock};
16use std::sync::atomic::{AtomicU32, Ordering};
17use crate::error::Error;
18use crate::symbols;
19
20pub mod iterator;
21
22type CodeStoreType = RwLock<HashMap<String, u32>>;
23type NameStoreType = RwLock<HashMap<u32, String>>;
24
25static CODE_REGISTER: OnceLock<CodeStoreType> = OnceLock::new();
26static NAME_REGISTER: OnceLock<NameStoreType> = OnceLock::new();
27static SYMBOL_ID: AtomicU32 = AtomicU32::new(100);
28
29pub fn get_code(name: &str) -> Result<u32, Error> {
51 let mut register = get_code_register().write()?;
52 let name = name.trim().to_string();
53
54 if name.is_empty() {
55 return Err(Error::general("name should not be an empty string"))
56 }
57
58 if let Some(code) = register.get(&name) {
59 return Ok(*code);
60 }
61
62 let code = SYMBOL_ID.fetch_add(1, Ordering::SeqCst);
63 register.insert(name.clone(), code);
64 drop(register);
65
66 let mut register = get_name_register().write()?;
67 register.insert(code, name);
68 drop(register);
69
70 Ok(code)
71}
72
73pub fn get_name(code: u32) -> Option<String> {
76 let register = get_name_register().read().ok()?;
77 register.get(&code).cloned()
78}
79
80fn get_code_register() -> &'static CodeStoreType {
81 CODE_REGISTER.get_or_init(|| {
82 RwLock::new(HashMap::new())
83 })
84}
85
86fn get_name_register() -> &'static NameStoreType {
87 NAME_REGISTER.get_or_init(|| {
88 RwLock::new(HashMap::new())
89 })
90}
91
92
93#[derive(Debug, Clone, Copy)]
94pub struct Symbol {
95 code: u32
96}
97
98impl PartialEq for Symbol {
99 fn eq(&self, other: &Self) -> bool {
100 self.code == other.code
101 }
102}
103
104impl Eq for Symbol {}
105
106impl Hash for Symbol {
107 fn hash<H: Hasher>(&self, state: &mut H) {
108 self.code.hash(state);
109 }
110}
111
112impl Symbol {
113 #[inline]
115 pub fn from_code(code: u32) -> Self {
116 Symbol {
117 code
118 }
119 }
120
121 pub fn build<S: AsRef<str>>(name: S) -> Result<Symbol, Error>{
126 let name = name.as_ref();
127 Ok(Self::from_code(get_code(name)?))
128 }
129
130 pub fn code(&self) -> u32 {
136 self.code
137 }
138
139 pub fn name(&self) -> Option<String> {
141 get_name(self.code)
142 }
143}
144
145impl Display for Symbol {
146 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
147 if let Some(name) = get_name(self.code) {
148 return f.write_str(name.as_str());
149 }
150
151 write!(f, "code:{}", self.code)
152 }
153}
154
155pub trait SymbolStore {
157 fn add_symbol(&self, name: &str) -> crate::Result<Symbol>;
158 fn get_symbol(&self, name: &str) -> Option<Symbol>;
159}
160
161impl SymbolStore for RefCell<HashSet<u32>> {
162 fn add_symbol(&self, name: &str) -> crate::Result<Symbol> {
163 let code = symbols::get_code(name)?;
164
165 let mut map = self.borrow_mut();
166 map.insert(code);
167
168 Ok(Symbol::from_code(code))
169 }
170
171 fn get_symbol(&self, name: &str) -> Option<Symbol> {
172 let code = get_code(name).ok()?;
173 let symbols = self.borrow();
174
175 symbols.get(&code)
176 .cloned()
177 .map(Symbol::from_code)
178 }
179}
180
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn register_is_idempotent() {
188 let hello = get_code("Hello").unwrap();
189 let bye = get_code("bye").unwrap();
190
191 assert_eq!(get_code("Hello").unwrap(), hello);
192 assert_ne!(get_code("Hello").unwrap(), bye);
193 }
194
195 #[test]
196 fn register_records_names() {
197 let hello = "hello";
198 let hcode = get_code(hello).unwrap();
199
200 assert_eq!(hello, get_name(hcode).unwrap());
201 }
202
203 #[test]
204 fn no_name() {
205 assert!(get_name(43_432_444).is_none());
206 }
207
208 #[test]
209 fn empty_string_is_error() {
210 assert!(get_code("").is_err());
211 assert!(get_code(" ").is_err());
212
213 assert!(get_code(" d").is_ok());
214 }
215
216 #[test]
217 fn names_are_trimmed() {
218 let code = get_code(" d ").unwrap();
219 let code2 = get_code("d").unwrap();
220 assert_eq!(code, code2);
221
222 assert_eq!(get_name(code).unwrap(), "d");
223 assert_eq!(get_name(code2).unwrap(), "d");
224 }
225}