use crate::metadata::{
token::Token,
typesystem::{CilFlavor, TypeSource},
};
use std::hash::{DefaultHasher, Hash, Hasher};
pub struct TypeSignatureHash {
state: u64,
}
impl TypeSignatureHash {
#[must_use]
pub fn new() -> Self {
TypeSignatureHash {
state: 0xcbf2_9ce4_8422_2325_u64, }
}
fn mix(&mut self, value: u64) {
self.state ^= value;
self.state = self.state.wrapping_mul(0x0100_0000_01b3_u64);
self.state ^= self.state >> 33;
self.state = self.state.wrapping_mul(0xff51_afd7_ed55_8ccd_u64);
self.state ^= self.state >> 33;
}
#[must_use]
pub fn add_component<T: Hash + ?Sized>(mut self, component: &T) -> Self {
let mut hasher = DefaultHasher::new();
component.hash(&mut hasher);
self.mix(hasher.finish());
self
}
#[must_use]
pub fn add_flavor(self, flavor: &CilFlavor) -> Self {
self.add_component(flavor)
}
#[must_use]
pub fn add_fullname(self, namespace: &str, name: &str) -> Self {
self.add_component(namespace).add_component(name)
}
#[must_use]
pub fn add_source(self, source: &TypeSource) -> Self {
self.add_component(source)
}
#[must_use]
pub fn add_token(self, token: &Token) -> Self {
self.add_component(&token.value())
}
#[must_use]
pub fn finalize(self) -> u64 {
self.state
}
}
impl Default for TypeSignatureHash {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metadata::typesystem::CilFlavor;
#[test]
fn test_hash_deterministic() {
let hash1 = TypeSignatureHash::new()
.add_flavor(&CilFlavor::Class)
.add_fullname("System", "String")
.add_source(&TypeSource::Unknown)
.finalize();
let hash2 = TypeSignatureHash::new()
.add_flavor(&CilFlavor::Class)
.add_fullname("System", "String")
.add_source(&TypeSource::Unknown)
.finalize();
assert_eq!(hash1, hash2, "Hash should be deterministic");
}
#[test]
fn test_hash_order_sensitive() {
let hash1 = TypeSignatureHash::new()
.add_component(&"first")
.add_component(&"second")
.finalize();
let hash2 = TypeSignatureHash::new()
.add_component(&"second")
.add_component(&"first")
.finalize();
assert_ne!(hash1, hash2, "Hash should be order-sensitive");
}
#[test]
fn test_flavor_differentiation() {
let class_hash = TypeSignatureHash::new()
.add_flavor(&CilFlavor::Class)
.add_fullname("System.Collections.Generic", "List`1")
.finalize();
let generic_hash = TypeSignatureHash::new()
.add_flavor(&CilFlavor::GenericInstance)
.add_fullname("System.Collections.Generic", "List`1")
.finalize();
assert_ne!(
class_hash, generic_hash,
"Different flavors should produce different hashes"
);
}
#[test]
fn test_collision_resistance() {
let test_cases = vec![
("System", "String"),
("System", "Object"),
("System.Collections", "String"),
("System", "StringBuilder"),
];
let mut hashes = Vec::new();
for (namespace, name) in test_cases {
let hash = TypeSignatureHash::new()
.add_flavor(&CilFlavor::Class)
.add_fullname(namespace, name)
.add_source(&TypeSource::Unknown)
.finalize();
hashes.push(hash);
}
for i in 0..hashes.len() {
for j in (i + 1)..hashes.len() {
assert_ne!(
hashes[i], hashes[j],
"Similar types should have different hashes"
);
}
}
}
}