use get_size::GetSize;
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
use crate::utils::murmur;
const HASH_SEED: u64 = 12345678;
pub fn symbol_hash(s: &str) -> u64 {
murmur::murmur_hash64a(s.as_bytes(), HASH_SEED)
}
pub trait Sym {
fn find(&mut self, name: &str) -> u64;
fn get(&self, id: u64) -> Option<&str>;
}
#[derive(Serialize, Deserialize)]
struct SymbolsSerde(Vec<String>);
impl From<SymbolsSerde> for Symbols {
fn from(serde: SymbolsSerde) -> Symbols {
let map = serde
.0
.iter()
.cloned()
.map(|name| {
let id = symbol_hash(&name);
(id, name)
})
.collect();
Symbols(map)
}
}
impl From<Symbols> for SymbolsSerde {
fn from(value: Symbols) -> Self {
SymbolsSerde(value.0.into_values().collect())
}
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, GetSize)]
#[serde(from = "SymbolsSerde")]
#[serde(into = "SymbolsSerde")]
pub struct Symbols(BTreeMap<u64, String>);
impl Sym for Symbols {
fn find(&mut self, name: &str) -> u64 {
let h = symbol_hash(name);
self.0.insert(h, name.to_owned());
h
}
fn get(&self, id: u64) -> Option<&str> {
self.0.get(&id).map(String::as_str)
}
}
impl Symbols {
pub fn clear(&mut self) {
self.0.clear()
}
pub fn push(&mut self, symbol: String) -> u64 {
let h = symbol_hash(&symbol);
self.0.insert(h, symbol);
h
}
pub fn view(&self) -> SymbolsView {
SymbolsView::new(self)
}
pub fn as_vec(&self) -> Vec<String> {
self.0.values().cloned().collect()
}
}
pub struct SymbolsView<'a> {
top: &'a Symbols,
extra: Option<Symbols>,
}
impl SymbolsView<'_> {
pub(crate) fn new(s: &Symbols) -> SymbolsView {
SymbolsView {
top: s,
extra: None,
}
}
pub fn into_extra(self) -> Symbols {
self.extra.unwrap_or_default()
}
}
impl Sym for SymbolsView<'_> {
fn find(&mut self, name: &str) -> u64 {
let h = symbol_hash(name);
if self.top.0.contains_key(&h) {
h
} else if let Some(extra) = self.extra.as_mut() {
extra.0.insert(h, name.to_string());
h
} else {
let mut extra = Symbols::default();
extra.0.insert(h, name.to_string());
self.extra = Some(extra);
h
}
}
fn get(&self, id: u64) -> Option<&str> {
if let Some(name) = self.top.get(id) {
Some(name)
} else if let Some(extra) = self.extra.as_ref() {
extra.get(id)
} else {
None
}
}
}