use std::hash::{BuildHasher, Hasher};
pub fn jenkins_hash(data: &[u8]) -> u32 {
let mut h: u32 = 0;
for &c in data {
h = h.wrapping_add(c as u32);
h = h.wrapping_add(h << 10);
h ^= h >> 6;
}
h = h.wrapping_add(h << 3);
h ^= h >> 11;
h = h.wrapping_add(h << 15);
h
}
#[derive(Debug, Clone, Default)]
pub struct JenkinsBuilder {
pub seed: u32,
}
impl JenkinsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_seed(mut self, seed: u32) -> Self {
self.seed = seed;
self
}
pub fn build(self) -> JenkinsBuildHasher {
JenkinsBuildHasher { seed: self.seed }
}
}
#[derive(Debug, Clone)]
pub struct JenkinsBuildHasher {
seed: u32,
}
impl BuildHasher for JenkinsBuildHasher {
type Hasher = JenkinsHasher;
fn build_hasher(&self) -> Self::Hasher {
JenkinsHasher::with_seed(self.seed)
}
}
#[derive(Debug, Clone)]
pub struct JenkinsHasher {
state: u32,
}
impl JenkinsHasher {
pub fn new() -> Self {
JenkinsHasher { state: 0 }
}
pub fn with_seed(seed: u32) -> Self {
JenkinsHasher { state: seed }
}
pub fn current_jenkins(&self) -> u32 {
self.state
}
}
impl Hasher for JenkinsHasher {
fn finish(&self) -> u64 {
let mut h = self.state;
h = h.wrapping_add(h << 3);
h ^= h >> 11;
h = h.wrapping_add(h << 15);
h as u64
}
fn write(&mut self, bytes: &[u8]) {
for &b in bytes {
self.state = self.state.wrapping_add(b as u32);
self.state = self.state.wrapping_add(self.state << 10);
self.state ^= self.state >> 6;
}
}
}
impl Default for JenkinsHasher {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_jenkins_hash_function() {
let h1 = jenkins_hash(b"hello");
let h2 = jenkins_hash(b"hello");
assert_eq!(h1, h2);
assert_ne!(h1, 0);
let h3 = jenkins_hash(b"Hello");
assert_ne!(h1, h3);
}
#[test]
fn test_hasher() {
let mut hasher = JenkinsHasher::new();
hasher.write(b"abc");
let val1 = hasher.finish();
let h2 = jenkins_hash(b"abc");
assert_eq!(val1, h2 as u64);
}
#[test]
fn test_with_seed() {
let mut hasher = JenkinsHasher::with_seed(12345);
hasher.write(b"abc");
let val_seeded = hasher.finish();
let val_no_seed = jenkins_hash(b"abc") as u64;
assert_ne!(val_seeded, val_no_seed);
}
#[test]
fn test_builder_in_hashmap() {
use std::collections::HashMap;
let buildh = JenkinsBuilder::new().with_seed(999).build();
let mut map = HashMap::with_hasher(buildh);
map.insert("foo", 1);
map.insert("bar", 2);
assert_eq!(map["foo"], 1);
}
}