use std::collections::BTreeMap;
use std::collections::HashMap;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker::PhantomData;
use std::sync::Arc;
use ref_cast::RefCast;
pub use blueprint_strong_hash_derive::StrongHash;
use crate as strong_hash;
mod impls;
pub trait StrongHash {
fn strong_hash<H: Hasher>(&self, state: &mut H);
}
#[macro_export]
macro_rules! impl_strong_hash_for_impl_hash {
($($t:ty)*) => {
$(
impl strong_hash::StrongHash for $t {
fn strong_hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::hash::Hash::hash(self, state);
}
}
)*
};
}
impl_strong_hash_for_impl_hash!(bool u8 i8 u16 i16 u32 i32 u64 i64 usize str &str String);
impl<T: StrongHash> StrongHash for [T] {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.len().strong_hash(state);
for item in self.iter() {
item.strong_hash(state);
}
}
}
impl<T: StrongHash> StrongHash for &[T] {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
<[T] as StrongHash>::strong_hash(*self, state);
}
}
impl<T: StrongHash> StrongHash for Vec<T> {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.len().strong_hash(state);
(&self[..]).strong_hash(state);
}
}
impl<T: StrongHash> StrongHash for Option<T> {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.is_some().strong_hash(state);
if let Some(t) = self.as_ref() {
t.strong_hash(state);
}
}
}
impl StrongHash for () {
fn strong_hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<A: StrongHash> StrongHash for (A,) {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
1.strong_hash(state);
self.0.strong_hash(state);
}
}
impl<A: StrongHash, B: StrongHash> StrongHash for (A, B) {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
2.strong_hash(state);
self.0.strong_hash(state);
self.1.strong_hash(state);
}
}
impl<A: StrongHash, B: StrongHash, C: StrongHash> StrongHash for (A, B, C) {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
3.strong_hash(state);
self.0.strong_hash(state);
self.1.strong_hash(state);
self.2.strong_hash(state);
}
}
impl<T: StrongHash + ?Sized> StrongHash for Box<T> {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().strong_hash(state);
}
}
impl<T: StrongHash + ?Sized> StrongHash for Arc<T> {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().strong_hash(state);
}
}
impl<K: StrongHash, V: StrongHash> StrongHash for BTreeMap<K, V> {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.len().strong_hash(state);
for (k, v) in self.iter() {
k.strong_hash(state);
v.strong_hash(state);
}
}
}
impl<K: StrongHash, V: StrongHash> StrongHash for HashMap<K, V> {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.len().strong_hash(state);
for (k, v) in self.iter() {
k.strong_hash(state);
v.strong_hash(state);
}
}
}
impl StrongHash for *const () {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
(*self as usize).strong_hash(state);
}
}
impl<T: ?Sized> StrongHash for PhantomData<T> {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state);
}
}
#[derive(RefCast)]
#[repr(transparent)]
pub struct UseStrongHashing<T: ?Sized>(pub T);
impl<T> Hash for UseStrongHashing<T>
where
T: ?Sized + StrongHash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.strong_hash(state);
}
}
#[cfg(test)]
mod test {
use super::*;
struct TestHashable {
hash: u64,
strong_hash: u64,
}
impl Hash for TestHashable {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash.hash(state);
}
}
impl StrongHash for TestHashable {
fn strong_hash<H: Hasher>(&self, state: &mut H) {
self.strong_hash.strong_hash(state);
}
}
fn hash<T: Hash>(t: &T) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
t.hash(&mut hasher);
hasher.finish()
}
fn strong_hash<T: StrongHash>(t: &T) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
t.strong_hash(&mut hasher);
hasher.finish()
}
#[test]
fn test_use_strong_hashing() {
let x = TestHashable {
hash: 1,
strong_hash: 2,
};
let y = TestHashable {
hash: 1,
strong_hash: 3,
};
assert_eq!(hash(&x), hash(&y));
assert_eq!(strong_hash(&x), hash(UseStrongHashing::ref_cast(&x)));
assert_ne!(
hash(UseStrongHashing::ref_cast(&x)),
hash(UseStrongHashing::ref_cast(&y))
);
}
}