use std::{
fmt::{self, Debug},
intrinsics::copy_nonoverlapping,
mem, slice, str,
};
use gazebo::coerce::Coerce;
use hashbrown::raw::RawTable;
use crate as starlark;
use crate::{
collections::{BorrowHashed, Hashed, StarlarkHashValue},
values::{StringValue, Trace},
};
#[derive(Clone, Trace)]
pub(crate) struct SymbolMap<T>(RawTable<(Symbol, T)>);
impl<T: Debug> Debug for SymbolMap<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map()
.entries(self.iter().map(|x| (&x.0, &x.1)))
.finish()
}
}
#[derive(Clone, Trace)]
pub(crate) struct Symbol {
hash: u64,
len: usize,
payload: Box<[u64]>,
small_hash: StarlarkHashValue,
}
unsafe impl Coerce<Symbol> for Symbol {}
impl Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
impl PartialEq for Symbol {
fn eq(&self, other: &Self) -> bool {
if self.len != other.len {
return false;
}
let p1 = &*self.payload;
let p2 = &*other.payload;
for i in 0..self.payload.len() {
if unsafe { p1.get_unchecked(i) != p2.get_unchecked(i) } {
return false;
}
}
true
}
}
impl Eq for Symbol {}
impl Symbol {
pub fn new(x: &str) -> Self {
Self::new_hashed(BorrowHashed::new(x))
}
pub fn new_hashed(x: BorrowHashed<str>) -> Self {
let small_hash = x.hash();
let hash = small_hash.promote();
let len = x.key().len();
let len8 = (len + 7) / 8;
let mut payload = vec![0; len8]; unsafe {
copy_nonoverlapping(x.key().as_ptr(), payload.as_mut_ptr() as *mut u8, len);
}
Self {
hash,
len,
payload: payload.into_boxed_slice(),
small_hash,
}
}
pub fn as_str(&self) -> &str {
unsafe {
let s =
slice::from_raw_parts(self.payload.as_ptr() as *const u64 as *const u8, self.len);
str::from_utf8_unchecked(s)
}
}
pub fn small_hash(&self) -> StarlarkHashValue {
self.small_hash
}
}
impl<T> SymbolMap<T> {
pub fn new() -> Self {
Self(RawTable::new())
}
pub fn with_capacity(capacity: usize) -> Self {
Self(RawTable::with_capacity(capacity))
}
pub fn insert(&mut self, key: &str, value: T) -> Option<T> {
let s = Symbol::new(key);
if let Some((_, item)) = self.0.get_mut(s.hash, |x| s == x.0) {
Some(mem::replace(item, value))
} else {
self.0.insert(s.hash, (s, value), |x| x.0.hash);
None
}
}
pub fn get(&self, key: &Symbol) -> Option<&T> {
self.0.get(key.hash, |x| key == &x.0).map(|x| &x.1)
}
pub fn get_str(&self, key: &str) -> Option<&T> {
self.get_hashed_str(BorrowHashed::new(key))
}
pub fn get_hashed_str(&self, key: BorrowHashed<str>) -> Option<&T> {
self.0
.get(key.hash().promote(), |x| x.0.as_str() == key.key())
.map(|x| &x.1)
}
pub(crate) fn get_hashed_string_value(&self, key: Hashed<StringValue>) -> Option<&T> {
self.0
.get(key.hash().promote(), |x| x.0.as_str() == key.key().as_str())
.map(|x| &x.1)
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter<'a>(&'a self) -> impl ExactSizeIterator<Item = &'a (Symbol, T)> + 'a {
unsafe { self.0.iter().map(|x| x.as_ref()) }
}
pub fn keys<'a>(&'a self) -> impl ExactSizeIterator<Item = &'a Symbol> + 'a {
self.iter().map(|x| &x.0)
}
pub fn values<'a>(&'a self) -> impl ExactSizeIterator<Item = &'a T> + 'a {
self.iter().map(|x| &x.1)
}
}