use std::{
hash::{Hash, Hasher},
ops::Deref,
};
use gazebo::{coerce::Coerce, prelude::*};
use indexmap::Equivalent;
use crate as starlark;
use crate::collections::{idhasher::mix_u32, StarlarkHasher};
#[derive(Hash, Eq, PartialEq, Clone, Copy, Dupe, Debug, Default, Trace)]
pub struct StarlarkHashValue(u32);
#[derive(PartialEq, Eq, Debug, Clone, Copy, Trace, Coerce)]
#[repr(C)]
pub struct Hashed<K> {
hash: StarlarkHashValue,
key: K,
}
#[derive(Copy_, Clone_, Dupe_, Eq, PartialEq)]
pub struct BorrowHashed<'a, Q: ?Sized> {
hash: StarlarkHashValue,
key: &'a Q,
}
impl StarlarkHashValue {
pub fn new<K: Hash + ?Sized>(key: &K) -> Self {
let mut hasher = StarlarkHasher::new();
key.hash(&mut hasher);
hasher.finish_small()
}
pub fn new_unchecked(hash: u32) -> Self {
Self(hash)
}
pub(crate) const fn hash_64(h: u64) -> Self {
let h = h ^ (h >> 33);
let h = h.wrapping_mul(0xff51afd7ed558ccd);
let h = h ^ (h >> 33);
let h = h.wrapping_mul(0xc4ceb9fe1a85ec53);
let h = h ^ (h >> 33);
StarlarkHashValue(h as u32)
}
pub fn get(self) -> u32 {
self.0
}
#[inline(always)]
pub fn promote(self) -> u64 {
mix_u32(self.0)
}
}
impl<'a, Q: ?Sized> BorrowHashed<'a, Q> {
pub fn new(key: &'a Q) -> Self
where
Q: Hash,
{
Self::new_unchecked(StarlarkHashValue::new(key), key)
}
pub fn new_unchecked(hash: StarlarkHashValue, key: &'a Q) -> Self {
Self { hash, key }
}
pub fn hash(&self) -> StarlarkHashValue {
self.hash
}
pub fn key(&self) -> &'a Q {
self.key
}
}
impl<'a, Q: Clone> BorrowHashed<'a, Q> {
pub fn unborrow_clone(&self) -> Hashed<Q> {
Hashed::new_unchecked(self.hash, self.key.clone())
}
}
impl<'a, Q: Copy> BorrowHashed<'a, Q> {
pub fn unborrow_copy(&self) -> Hashed<Q> {
Hashed::new_unchecked(self.hash, *self.key)
}
}
impl<'a, Q, K> Equivalent<Hashed<K>> for BorrowHashed<'a, Q>
where
Q: Equivalent<K> + ?Sized,
{
fn equivalent(&self, key: &Hashed<K>) -> bool {
self.hash == key.hash && self.key.equivalent(&key.key)
}
}
impl<'a, Q: ?Sized> Hash for BorrowHashed<'a, Q> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash.hash(state)
}
}
impl<K> Deref for Hashed<K> {
type Target = K;
fn deref(&self) -> &Self::Target {
&self.key
}
}
#[allow(clippy::derive_hash_xor_eq)]
impl<K> Hash for Hashed<K> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash.hash(state)
}
}
impl<K> Hashed<K> {
pub fn new(key: K) -> Self
where
K: Hash,
{
Self::new_unchecked(StarlarkHashValue::new(&key), key)
}
pub fn new_unchecked(hash: StarlarkHashValue, key: K) -> Self {
Self { hash, key }
}
pub fn key(&self) -> &K {
&self.key
}
pub fn key_mut(&mut self) -> &mut K {
&mut self.key
}
pub fn into_key(self) -> K {
self.key
}
pub fn hash(&self) -> StarlarkHashValue {
self.hash
}
pub fn borrow(&self) -> BorrowHashed<K> {
BorrowHashed::new_unchecked(self.hash, &self.key)
}
}
#[cfg(test)]
mod tests {
use indexmap::map::IndexMap;
use crate::collections::{BorrowHashed, Hashed};
#[test]
fn borrow_and_hashed_equivalent() {
let mut m = IndexMap::new();
m.insert(Hashed::new(1), 'b');
assert_eq!(m.get(&BorrowHashed::new(&1)), Some(&'b'));
}
}