use crate::{Any, InstFnNameHash, IntoComponent, Item, Protocol};
use serde::{Deserialize, Serialize};
use std::any;
use std::fmt;
use std::hash;
use std::hash::{BuildHasher as _, BuildHasherDefault, Hash as _, Hasher as _};
use std::mem;
use twox_hash::XxHash64;
const SEP: usize = 0x7f;
const TYPE: usize = 1;
const INSTANCE_FUNCTION_HASH: u64 = 0x5ea77ffbcdf5f302;
const FIELD_FUNCTION_HASH: u64 = 0xab53b6a7a53c757e;
const OBJECT_KEYS: usize = 4;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[repr(transparent)]
pub struct Hash(u64);
impl Hash {
pub(crate) const fn new(hash: u64) -> Self {
Self(hash)
}
pub(crate) fn of<T: hash::Hash>(thing: T) -> Self {
let mut hasher = Self::new_hasher();
thing.hash(&mut hasher);
Self(hasher.finish())
}
pub fn type_hash<I>(path: I) -> Self
where
I: IntoTypeHash,
{
path.into_type_hash()
}
pub fn from_any<T>() -> Self
where
T: Any,
{
Self::from_type_id(any::TypeId::of::<T>())
}
pub fn from_type_id(type_id: any::TypeId) -> Self {
unsafe { mem::transmute(type_id) }
}
#[inline]
pub fn instance_function<N>(type_hash: Hash, name: N) -> Self
where
N: InstFnNameHash,
{
let name = name.inst_fn_name_hash();
Self(INSTANCE_FUNCTION_HASH ^ (type_hash.0 ^ name.0))
}
#[inline]
pub fn field_fn<N>(protocol: Protocol, type_hash: Hash, name: N) -> Self
where
N: InstFnNameHash,
{
let name = name.inst_fn_name_hash();
Self(FIELD_FUNCTION_HASH ^ ((type_hash.0 ^ protocol.hash.0) ^ name.0))
}
pub fn static_bytes(bytes: &[u8]) -> Hash {
Self::of(bytes)
}
pub fn instance_fn_name(name: &str) -> Hash {
Self::of(name)
}
pub fn object_keys<I>(keys: I) -> Self
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let mut hasher = Self::new_hasher();
OBJECT_KEYS.hash(&mut hasher);
for key in keys {
SEP.hash(&mut hasher);
key.as_ref().hash(&mut hasher);
}
Self(hasher.finish())
}
fn new_hasher() -> impl hash::Hasher {
BuildHasherDefault::<XxHash64>::default().build_hasher()
}
fn path_hash<I>(kind: usize, path: I) -> Self
where
I: IntoIterator,
I::Item: IntoComponent,
{
let mut hasher = Self::new_hasher();
kind.hash(&mut hasher);
for c in path {
c.hash_component(&mut hasher);
}
Self(hasher.finish())
}
}
impl fmt::Display for Hash {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "0x{:x}", self.0)
}
}
impl fmt::Debug for Hash {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "Hash(0x{:x})", self.0)
}
}
pub trait IntoTypeHash: Copy {
fn into_type_hash(self) -> Hash;
fn into_item(self) -> Item;
}
impl IntoTypeHash for Hash {
fn into_type_hash(self) -> Hash {
self
}
fn into_item(self) -> Item {
Item::new()
}
}
impl<I> IntoTypeHash for I
where
I: Copy + IntoIterator,
I::Item: IntoComponent,
{
fn into_type_hash(self) -> Hash {
Hash::path_hash(TYPE, self)
}
fn into_item(self) -> Item {
Item::with_item(self)
}
}