use core::{
hash::{Hash, Hasher},
mem::{align_of, offset_of, size_of},
};
pub use std::collections::hash_map::Entry;
pub use gxhash::{GxBuildHasher, GxHasher, HashMap, HashMapExt, HashSet, HashSetExt};
pub use papaya;
const STRIPE: usize = 64;
const LANES: usize = 4;
const _: () = assert!(LANES.is_power_of_two(), "LANES 必须为 2 的幂");
const LANE_SALT: [u64; LANES] = [
0,
0x9E37_79B9_7F4A_7C15,
0xC2B2_AE3D_27D4_EB4F,
0x1656_67C9_1973_60D5,
];
#[inline]
const fn smear(x: u64) -> u64 {
let z = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
let z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
const DEFAULT_LANES: [i64; LANES] = [
smear(LANE_SALT[0]) as i64,
smear(LANE_SALT[1]) as i64,
smear(LANE_SALT[2]) as i64,
smear(LANE_SALT[3]) as i64,
];
#[repr(C, align(64))]
#[derive(Clone, Debug)]
pub struct StreamHasher {
lanes: [i64; LANES],
seed: u64,
stripes: u64,
total: u64,
buf_len: usize,
buf: [u8; STRIPE],
}
const _: () = assert!(size_of::<StreamHasher>() == 128);
const _: () = assert!(align_of::<StreamHasher>() == 64);
const _: () = assert!(offset_of!(StreamHasher, buf) == 64);
impl Default for StreamHasher {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl StreamHasher {
#[inline]
pub const fn new() -> Self {
Self {
lanes: DEFAULT_LANES,
seed: 0,
stripes: 0,
total: 0,
buf_len: 0,
buf: [0; STRIPE],
}
}
#[inline]
pub const fn with_seed(seed: u64) -> Self {
let lanes = if seed == 0 {
DEFAULT_LANES
} else {
[
smear(seed ^ LANE_SALT[0]) as i64,
smear(seed ^ LANE_SALT[1]) as i64,
smear(seed ^ LANE_SALT[2]) as i64,
smear(seed ^ LANE_SALT[3]) as i64,
]
};
Self {
lanes,
seed,
stripes: 0,
total: 0,
buf_len: 0,
buf: [0; STRIPE],
}
}
#[inline(always)]
fn fold(lanes: &mut [i64; LANES], stripes: &mut u64, stripe: &[u8; STRIPE]) {
let lane = *stripes as usize & (LANES - 1);
let lane_ref = unsafe { lanes.get_unchecked_mut(lane) };
*lane_ref = gxhash::gxhash64(stripe, *lane_ref) as i64;
*stripes += 1;
}
#[inline]
pub fn write(&mut self, mut bytes: &[u8]) {
if bytes.is_empty() {
return;
}
self.total = self.total.wrapping_add(bytes.len() as u64);
if self.buf_len > 0 {
let needed = STRIPE - self.buf_len;
if bytes.len() < needed {
let next_len = self.buf_len + bytes.len();
self.buf[self.buf_len..next_len].copy_from_slice(bytes);
self.buf_len = next_len;
return;
}
self.buf[self.buf_len..].copy_from_slice(&bytes[..needed]);
Self::fold(&mut self.lanes, &mut self.stripes, &self.buf);
self.buf_len = 0;
bytes = &bytes[needed..];
}
let (mut stripes, tail) = bytes.as_chunks::<STRIPE>();
while (self.stripes as usize & (LANES - 1)) != 0 && !stripes.is_empty() {
Self::fold(&mut self.lanes, &mut self.stripes, &stripes[0]);
stripes = &stripes[1..];
}
let (quads, rem_stripes) = stripes.as_chunks::<LANES>();
for quad in quads {
self.lanes[0] = gxhash::gxhash64(&quad[0], self.lanes[0]) as i64;
self.lanes[1] = gxhash::gxhash64(&quad[1], self.lanes[1]) as i64;
self.lanes[2] = gxhash::gxhash64(&quad[2], self.lanes[2]) as i64;
self.lanes[3] = gxhash::gxhash64(&quad[3], self.lanes[3]) as i64;
}
self.stripes += (quads.len() * LANES) as u64;
for stripe in rem_stripes {
Self::fold(&mut self.lanes, &mut self.stripes, stripe);
}
self.buf[..tail.len()].copy_from_slice(tail);
self.buf_len = tail.len();
}
#[inline]
pub fn finish(&self) -> u64 {
debug_assert!(self.buf_len < STRIPE);
let mut state = self.seed as i64;
state = gxhash::gxhash64(&self.lanes[0].to_le_bytes(), state) as i64;
state = gxhash::gxhash64(&self.lanes[1].to_le_bytes(), state) as i64;
state = gxhash::gxhash64(&self.lanes[2].to_le_bytes(), state) as i64;
state = gxhash::gxhash64(&self.lanes[3].to_le_bytes(), state) as i64;
if self.buf_len == 0 {
gxhash::gxhash64(&self.total.to_le_bytes(), state)
} else {
let mut tail = [0u8; STRIPE + 8];
tail[..self.buf_len].copy_from_slice(&self.buf[..self.buf_len]);
let end = self.buf_len + 8;
tail[self.buf_len..end].copy_from_slice(&self.total.to_le_bytes());
gxhash::gxhash64(&tail[..end], state)
}
}
#[inline]
pub fn reset(&mut self) {
self.lanes = if self.seed == 0 {
DEFAULT_LANES
} else {
[
smear(self.seed ^ LANE_SALT[0]) as i64,
smear(self.seed ^ LANE_SALT[1]) as i64,
smear(self.seed ^ LANE_SALT[2]) as i64,
smear(self.seed ^ LANE_SALT[3]) as i64,
]
};
self.stripes = 0;
self.total = 0;
self.buf_len = 0;
}
#[inline]
pub const fn total_bytes_written(&self) -> u64 {
self.total
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.total == 0
}
}
impl Hasher for StreamHasher {
#[inline]
fn finish(&self) -> u64 {
self.finish()
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
self.write(bytes);
}
}
#[inline]
pub fn compute_checksum(data: &[u8]) -> u64 {
let mut hasher = StreamHasher::new();
hasher.write(data);
hasher.finish()
}
#[inline]
pub fn compute_checksum_with_seed(data: &[u8], seed: u64) -> u64 {
let mut hasher = StreamHasher::with_seed(seed);
hasher.write(data);
hasher.finish()
}
pub type DefaultBuildHasher = GxBuildHasher;
#[inline]
pub fn new_hash_map<K, V>() -> HashMap<K, V> {
HashMap::with_hasher(DefaultBuildHasher::default())
}
#[inline]
pub fn hash_map_with_capacity<K, V>(capacity: usize) -> HashMap<K, V> {
HashMap::with_capacity_and_hasher(capacity, DefaultBuildHasher::default())
}
#[inline]
pub fn new_hash_set<T>() -> HashSet<T> {
HashSet::with_hasher(DefaultBuildHasher::default())
}
#[inline]
pub fn hash_set_with_capacity<T>(capacity: usize) -> HashSet<T> {
HashSet::with_capacity_and_hasher(capacity, DefaultBuildHasher::default())
}
pub type GxPapayaMap<K, V> = papaya::HashMap<K, V, GxBuildHasher>;
#[inline]
pub fn new_papaya_map<K, V>() -> GxPapayaMap<K, V> {
papaya::HashMap::builder()
.hasher(GxBuildHasher::default())
.build()
}
#[inline]
pub fn papaya_map_with_capacity<K, V>(capacity: usize) -> GxPapayaMap<K, V> {
papaya::HashMap::builder()
.hasher(GxBuildHasher::default())
.capacity(capacity)
.build()
}
pub type GxPapayaSet<T> = papaya::HashSet<T, GxBuildHasher>;
#[inline]
pub fn new_papaya_set<T>() -> GxPapayaSet<T> {
papaya::HashSet::builder()
.hasher(GxBuildHasher::default())
.build()
}
#[inline]
pub fn papaya_set_with_capacity<T>(capacity: usize) -> GxPapayaSet<T> {
papaya::HashSet::builder()
.hasher(GxBuildHasher::default())
.capacity(capacity)
.build()
}
#[inline(always)]
pub fn fast_hash(bytes: &[u8]) -> u64 {
gxhash::gxhash64(bytes, 0)
}
#[inline(always)]
pub fn fast_hash_u64(val: u64) -> u64 {
fast_hash(&val.to_le_bytes())
}
#[inline(always)]
pub fn fast_hash_with_seed(bytes: &[u8], seed: u64) -> u64 {
gxhash::gxhash64(bytes, seed as i64)
}
#[inline(always)]
pub fn fast_hash128(bytes: &[u8]) -> u128 {
gxhash::gxhash128(bytes, 0)
}
#[inline(always)]
pub fn hash128(bytes: &[u8], seed_a: u64, seed_b: u64) -> u128 {
let combined_seed = (seed_a ^ seed_b.rotate_left(32)) as i64;
gxhash::gxhash128(bytes, combined_seed)
}
#[inline(always)]
pub fn hash128_with_seed(bytes: &[u8], seed: u64) -> u128 {
gxhash::gxhash128(bytes, seed as i64)
}
#[inline]
pub fn hash_value<T: Hash + ?Sized>(value: &T) -> u64 {
let mut hasher = gxhash::GxHasher::with_seed(0);
value.hash(&mut hasher);
hasher.finish()
}
#[inline]
pub fn hash_value_with_seed<T: Hash + ?Sized>(value: &T, seed: u64) -> u64 {
let mut hasher = gxhash::GxHasher::with_seed(seed as i64);
value.hash(&mut hasher);
hasher.finish()
}