use std::collections::HashMap;
use std::rc::Weak;
use std::rc::Rc;
use std::cell::RefCell;
use std::hash::{Hash, Hasher};
#[allow(dead_code)]
struct StringInterner {
strmap: HashMap<String, Weak<String>>,
fixed: std::vec::Vec<Symbol>,
allocated_since_gc: usize,
}
impl StringInterner {
fn new() -> Self {
let mut s = Self { strmap: HashMap::new(), fixed: vec![], allocated_since_gc: 0 };
s.fixed = vec![
s.s2sym("x"),
s.s2sym("y"),
s.s2sym("z"),
s.s2sym("w"),
s.s2sym("_data"),
s.s2sym("_proto"),
s.s2sym("r"),
s.s2sym("g"),
s.s2sym("b"),
s.s2sym("a"),
s.s2sym("h"),
s.s2sym("s"),
s.s2sym("v"),
];
s
}
fn collect(&mut self) -> i64 {
let mut count = 0;
let mut free = vec![];
for (k, v) in self.strmap.iter() {
if v.upgrade().is_none() {
free.push(k.to_string());
}
}
for k in free.iter() {
count += 1;
self.strmap.remove(k);
}
self.allocated_since_gc = 0;
count
}
fn new_alloc_check_gc(&mut self) {
self.allocated_since_gc += 1;
let max_alloc_before_gc = 100;
if self.allocated_since_gc > max_alloc_before_gc {
self.collect();
}
}
#[inline]
fn s2sym(&mut self, s: &str) -> Symbol {
if let Some(wc) = self.strmap.get(s) {
if let Some(rc) = wc.upgrade() {
return Symbol(rc);
}
}
let rc = Rc::new(s.to_string());
self.strmap.insert(s.to_string(), Rc::downgrade(&rc));
self.new_alloc_check_gc();
Symbol(rc)
}
#[inline]
fn new_sym_mv(&mut self, s: String) -> Symbol {
if let Some(wc) = self.strmap.get(&s) {
if let Some(rc) = wc.upgrade() {
return Symbol(rc);
}
}
let rc = Rc::new(s.clone());
self.strmap.insert(s, Rc::downgrade(&rc));
self.new_alloc_check_gc();
Symbol(rc)
}
}
thread_local! {
static STR_INTERN: RefCell<StringInterner> = RefCell::new(StringInterner::new());
}
pub struct Symbol(Rc<String>);
impl Symbol {
#[inline]
pub fn ref_id(&self) -> i64 {
&*(self.0) as *const String as i64
}
}
impl PartialEq for Symbol {
#[inline]
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for Symbol {}
impl Hash for Symbol {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
let addr = &*(self.0) as *const String as u64;
state.write_u64(addr);
}
}
impl std::fmt::Debug for Symbol {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let addr = &*(self.0) as *const String as u64;
write!(f, "Symbol({}:{})", addr, *self.0)
}
}
impl std::clone::Clone for Symbol {
#[inline]
fn clone(&self) -> Self { Self(self.0.clone()) }
}
impl std::convert::AsRef<str> for Symbol {
#[inline]
fn as_ref(&self) -> &str { &*self.0 }
}
impl std::cmp::PartialOrd for Symbol {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some((&*self.0).cmp(&*other.0))
}
}
impl std::cmp::Ord for Symbol {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(&*self.0).cmp(&*other.0)
}
}
impl std::fmt::Display for Symbol {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", &*self.0)
}
}
impl std::ops::Deref for Symbol {
type Target = str;
fn deref(&self) -> &str {
&*self.0
}
}
pub fn s2sym(s: &str) -> Symbol {
STR_INTERN.with(|si| {
si.borrow_mut().s2sym(s)
})
}
pub fn new_sym_mv(s: String) -> Symbol {
STR_INTERN.with(|si| {
si.borrow_mut().new_sym_mv(s)
})
}
pub fn string_interner_collect() -> i64 {
STR_INTERN.with(|si| {
si.borrow_mut().collect()
})
}