#![forbid(unsafe_code)] use crate::deflate::{State, HASH_SIZE, STD_MIN_MATCH}; #[derive(Debug, Clone, Copy)] pub enum HashCalcVariant { Standard, Roll, } impl HashCalcVariant { /// Use rolling hash for deflate_slow algorithm with level 9. It allows us to /// properly lookup different hash chains to speed up longest_match search. pub fn for_max_chain_length(max_chain_length: u16) -> Self { if max_chain_length > 1024 { HashCalcVariant::Roll } else { HashCalcVariant::Standard } } } pub struct StandardHashCalc; impl StandardHashCalc { const HASH_CALC_OFFSET: usize = 0; const HASH_CALC_MASK: u32 = (HASH_SIZE - 1) as u32; fn hash_calc(_: u32, val: u32) -> u32 { const HASH_SLIDE: u32 = 16; val.wrapping_mul(2654435761) >> HASH_SLIDE } pub fn update_hash(h: u32, val: u32) -> u32 { Self::hash_calc(h, val) & Self::HASH_CALC_MASK } #[inline] pub fn quick_insert_string(state: &mut State, string: usize) -> u16 { let slice = &state.window.filled()[string + Self::HASH_CALC_OFFSET..]; let val = u32::from_le_bytes(slice[..4].try_into().unwrap()); Self::quick_insert_value(state, string, val) } #[inline] pub fn quick_insert_value(state: &mut State, string: usize, val: u32) -> u16 { let hm = Self::update_hash(0, val) as usize; let head = state.head.as_slice()[hm]; if head != string as u16 { state.prev.as_mut_slice()[string & state.w_mask()] = head; state.head.as_mut_slice()[hm] = string as u16; } head } pub fn insert_string(state: &mut State, string: usize, count: usize) { let slice = &state.window.filled()[string + Self::HASH_CALC_OFFSET..]; // it can happen that insufficient bytes are initialized // .take(count) generates worse assembly let slice = &slice[..Ord::min(slice.len(), count + 3)]; let w_mask = state.w_mask(); for (i, w) in slice.windows(4).enumerate() { let idx = string as u16 + i as u16; let val = u32::from_le_bytes(w.try_into().unwrap()); let hm = Self::update_hash(0, val) as usize; let head = state.head.as_slice()[hm]; if head != idx { state.prev.as_mut_slice()[idx as usize & w_mask] = head; state.head.as_mut_slice()[hm] = idx; } } } } pub struct RollHashCalc; impl RollHashCalc { const HASH_CALC_OFFSET: usize = STD_MIN_MATCH - 1; const HASH_CALC_MASK: u32 = (1 << 15) - 1; fn hash_calc(h: u32, val: u32) -> u32 { const HASH_SLIDE: u32 = 5; (h << HASH_SLIDE) ^ val } pub fn update_hash(h: u32, val: u32) -> u32 { Self::hash_calc(h, val) & Self::HASH_CALC_MASK } pub fn quick_insert_string(state: &mut State, string: usize) -> u16 { let val = state.window.filled()[string + Self::HASH_CALC_OFFSET] as u32; state.ins_h = Self::hash_calc(state.ins_h, val); state.ins_h &= Self::HASH_CALC_MASK; let hm = state.ins_h as usize; let head = state.head.as_slice()[hm]; if head != string as u16 { state.prev.as_mut_slice()[string & state.w_mask()] = head; state.head.as_mut_slice()[hm] = string as u16; } head } pub fn insert_string(state: &mut State, string: usize, count: usize) { let slice = &state.window.filled()[string + Self::HASH_CALC_OFFSET..][..count]; let w_mask = state.w_mask(); for (i, val) in slice.iter().copied().enumerate() { let idx = string as u16 + i as u16; state.ins_h = Self::hash_calc(state.ins_h, val as u32); state.ins_h &= Self::HASH_CALC_MASK; let hm = state.ins_h as usize; let head = state.head.as_slice()[hm]; if head != idx { state.prev.as_mut_slice()[idx as usize & w_mask] = head; state.head.as_mut_slice()[hm] = idx; } } } } #[cfg(test)] mod tests { use super::*; #[test] fn roll_hash_calc() { assert_eq!(RollHashCalc::hash_calc(2565, 93), 82173); assert_eq!(RollHashCalc::hash_calc(16637, 10), 532394); assert_eq!(RollHashCalc::hash_calc(8106, 100), 259364); assert_eq!(RollHashCalc::hash_calc(29988, 101), 959717); assert_eq!(RollHashCalc::hash_calc(9445, 98), 302274); assert_eq!(RollHashCalc::hash_calc(7362, 117), 235573); assert_eq!(RollHashCalc::hash_calc(6197, 103), 198343); assert_eq!(RollHashCalc::hash_calc(1735, 32), 55488); assert_eq!(RollHashCalc::hash_calc(22720, 61), 727101); assert_eq!(RollHashCalc::hash_calc(6205, 32), 198528); assert_eq!(RollHashCalc::hash_calc(3826, 117), 122421); assert_eq!(RollHashCalc::hash_calc(24117, 101), 771781); } #[test] fn standard_hash_calc() { assert_eq!(StandardHashCalc::hash_calc(0, 807411760), 65468); assert_eq!(StandardHashCalc::hash_calc(0, 540024864), 42837); assert_eq!(StandardHashCalc::hash_calc(0, 538980384), 33760); assert_eq!(StandardHashCalc::hash_calc(0, 775430176), 8925); assert_eq!(StandardHashCalc::hash_calc(0, 941629472), 42053); } }