use crate::CharUnit;
use rustc_hash::FxHasher;
use smallvec::SmallVec;
use std::hash::{Hash, Hasher};
#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
pub struct NodeSignature {
pub hash: u64,
}
impl NodeSignature {
pub fn new(hash: u64) -> Self {
NodeSignature { hash }
}
pub fn zero() -> Self {
NodeSignature { hash: 0 }
}
pub fn compute<U, I>(is_final: bool, edges: I) -> Self
where
U: CharUnit,
I: IntoIterator<Item = (U, NodeSignature)>,
{
let mut hasher = FxHasher::default();
is_final.hash(&mut hasher);
let mut edge_hashes: SmallVec<[(U, u64); 4]> = edges
.into_iter()
.map(|(label, child_sig)| (label, child_sig.hash))
.collect();
edge_hashes.sort_unstable_by_key(|(label, _)| *label);
for (label, child_hash) in &edge_hashes {
label.hash(&mut hasher);
child_hash.hash(&mut hasher);
}
NodeSignature {
hash: hasher.finish(),
}
}
pub fn compute_with_value<U, I>(is_final: bool, value_hash: Option<u64>, edges: I) -> Self
where
U: CharUnit,
I: IntoIterator<Item = (U, NodeSignature)>,
{
let mut hasher = FxHasher::default();
is_final.hash(&mut hasher);
if let Some(vh) = value_hash {
true.hash(&mut hasher); vh.hash(&mut hasher);
} else {
false.hash(&mut hasher); }
let mut edge_hashes: SmallVec<[(U, u64); 4]> = edges
.into_iter()
.map(|(label, child_sig)| (label, child_sig.hash))
.collect();
edge_hashes.sort_unstable_by_key(|(label, _)| *label);
for (label, child_hash) in &edge_hashes {
label.hash(&mut hasher);
child_hash.hash(&mut hasher);
}
NodeSignature {
hash: hasher.finish(),
}
}
}
impl Default for NodeSignature {
fn default() -> Self {
Self::zero()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signature_basic() {
let sig1 = NodeSignature::compute::<u8, _>(true, std::iter::empty());
let sig2 = NodeSignature::compute::<u8, _>(true, std::iter::empty());
assert_eq!(sig1, sig2);
let sig3 = NodeSignature::compute::<u8, _>(false, std::iter::empty());
assert_ne!(sig1, sig3);
}
#[test]
fn test_signature_with_edges() {
let child1 = NodeSignature::compute::<u8, _>(true, std::iter::empty());
let child2 = NodeSignature::compute::<u8, _>(false, std::iter::empty());
let sig1 = NodeSignature::compute::<u8, _>(false, vec![(b'a', child1), (b'b', child2)]);
let sig2 = NodeSignature::compute::<u8, _>(false, vec![(b'a', child1), (b'b', child2)]);
assert_eq!(sig1, sig2);
let sig3 = NodeSignature::compute::<u8, _>(false, vec![(b'b', child2), (b'a', child1)]);
assert_eq!(sig1, sig3);
let sig4 = NodeSignature::compute::<u8, _>(false, vec![(b'a', child1)]);
assert_ne!(sig1, sig4);
}
#[test]
fn test_signature_with_value() {
let sig1 = NodeSignature::compute_with_value::<u8, _>(true, Some(42), std::iter::empty());
let sig2 = NodeSignature::compute_with_value::<u8, _>(true, Some(99), std::iter::empty());
assert_ne!(sig1, sig2);
let sig3 = NodeSignature::compute_with_value::<u8, _>(true, Some(42), std::iter::empty());
assert_eq!(sig1, sig3);
let sig4 = NodeSignature::compute_with_value::<u8, _>(true, None, std::iter::empty());
assert_ne!(sig1, sig4);
}
#[test]
fn test_signature_char_unit() {
let child = NodeSignature::compute::<char, _>(true, std::iter::empty());
let sig = NodeSignature::compute::<char, _>(false, vec![('a', child), ('é', child)]);
assert_ne!(sig.hash, 0);
}
#[test]
fn test_signature_u64_unit() {
let child = NodeSignature::compute::<u64, _>(true, std::iter::empty());
let sig = NodeSignature::compute::<u64, _>(false, vec![(1u64, child), (1000u64, child)]);
assert_ne!(sig.hash, 0);
}
}