#![allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss
)]
mod crc16;
mod crc32;
mod fnv;
mod hsieh;
mod jenkins;
mod md5;
mod murmur;
mod murmur3;
mod one_at_a_time;
mod random;
pub mod ketama;
pub mod modula;
pub mod random_slicing;
pub mod token;
pub use crate::hashkit::murmur3::murmur3_x64_64;
pub use crate::hashkit::random::PseudoRng;
pub use crate::hashkit::random_slicing::{RandomSlices, RandomSlicesError};
pub use crate::hashkit::token::DynToken;
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum HashType {
OneAtATime,
Md5,
Crc16,
Crc32,
Crc32a,
Fnv1_64,
Fnv1a_64,
Fnv1_32,
Fnv1a_32,
Hsieh,
Murmur,
Jenkins,
Murmur3,
#[allow(non_camel_case_types)]
Murmur3X64_64,
}
impl HashType {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
HashType::OneAtATime => "one_at_a_time",
HashType::Md5 => "md5",
HashType::Crc16 => "crc16",
HashType::Crc32 => "crc32",
HashType::Crc32a => "crc32a",
HashType::Fnv1_64 => "fnv1_64",
HashType::Fnv1a_64 => "fnv1a_64",
HashType::Fnv1_32 => "fnv1_32",
HashType::Fnv1a_32 => "fnv1a_32",
HashType::Hsieh => "hsieh",
HashType::Murmur => "murmur",
HashType::Jenkins => "jenkins",
HashType::Murmur3 => "murmur3",
HashType::Murmur3X64_64 => "murmur3_x64_64",
}
}
#[must_use]
pub const fn all() -> &'static [HashType] {
&[
HashType::OneAtATime,
HashType::Md5,
HashType::Crc16,
HashType::Crc32,
HashType::Crc32a,
HashType::Fnv1_64,
HashType::Fnv1a_64,
HashType::Fnv1_32,
HashType::Fnv1a_32,
HashType::Hsieh,
HashType::Murmur,
HashType::Jenkins,
HashType::Murmur3,
HashType::Murmur3X64_64,
]
}
#[must_use]
pub fn from_name(name: &str) -> Option<HashType> {
Self::all().iter().copied().find(|h| h.as_str() == name)
}
}
#[must_use]
pub fn hash(ty: HashType, key: &[u8]) -> DynToken {
match ty {
HashType::OneAtATime => one_at_a_time::hash(key),
HashType::Md5 => md5::hash(key),
HashType::Crc16 => crc16::hash(key),
HashType::Crc32 => crc32::hash_libmemcached(key),
HashType::Crc32a => crc32::hash_standard(key),
HashType::Fnv1_64 => fnv::hash_fnv1_64(key),
HashType::Fnv1a_64 => fnv::hash_fnv1a_64(key),
HashType::Fnv1_32 => fnv::hash_fnv1_32(key),
HashType::Fnv1a_32 => fnv::hash_fnv1a_32(key),
HashType::Hsieh => hsieh::hash(key),
HashType::Murmur => murmur::hash(key),
HashType::Jenkins => jenkins::hash(key),
HashType::Murmur3 => murmur3::hash(key),
HashType::Murmur3X64_64 => {
let h = murmur3::murmur3_x64_64(0xc0a1_e5ce, key);
let mut token = DynToken::default();
token.size(2).expect("len 2 fits");
let mag = token.mag_mut();
mag[0] = h as u32;
mag[1] = (h >> 32) as u32;
token
}
}
}
#[must_use]
pub fn hash64(ty: HashType, key: &[u8]) -> u64 {
if matches!(ty, HashType::Murmur3X64_64) {
return murmur3::murmur3_x64_64(0xc0a1_e5ce, key);
}
let token = hash(ty, key);
let mag = token.mag();
let lo = u64::from(mag[0]);
let hi = if mag.len() > 1 { u64::from(mag[1]) } else { 0 };
(hi << 32) | lo
}
#[must_use]
pub fn md5_signature(key: &[u8]) -> [u8; 16] {
md5::digest(key)
}
#[must_use]
pub fn crc32_sz(buf: &[u8], in_crc32: u32) -> u32 {
crc32::crc32_sz(buf, in_crc32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn names_round_trip() {
for ty in HashType::all().iter().copied() {
assert_eq!(HashType::from_name(ty.as_str()), Some(ty));
}
}
#[test]
fn dispatch_is_deterministic() {
let key = b"the quick brown fox";
for ty in HashType::all().iter().copied() {
let a = hash(ty, key);
let b = hash(ty, key);
assert_eq!(a, b, "algorithm {ty:?} not deterministic");
}
}
#[test]
fn dispatch_lengths_match_c_codec() {
for ty in HashType::all().iter().copied() {
let token = hash(ty, b"k");
let expected = match ty {
HashType::Murmur3 => 4,
HashType::Murmur3X64_64 => 2,
_ => 1,
};
assert_eq!(
token.len(),
expected,
"wrong token length for {ty:?}: got {}",
token.len()
);
}
}
}