const GEAR_SEED: u64 = 0x1234_5678_9abc_def0;
const GEAR_GUARD_FIRST: u64 = 0x3b5c_9f8e_2d71_a046;
const GEAR_GUARD_LAST: u64 = 0x9e1d_4a7c_60b3_82f5;
const fn build_gear_table() -> [u64; 256] {
let mut table = [0u64; 256];
let mut state = GEAR_SEED;
let mut i = 0usize;
while i < 256 {
state = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^= z >> 31;
table[i] = z;
i += 1;
}
table[0] = GEAR_GUARD_FIRST;
table[255] = GEAR_GUARD_LAST;
table
}
pub const GEAR_TABLE: [u64; 256] = build_gear_table();
#[inline]
pub(crate) fn gear_roll(hash: u64, byte: u8) -> u64 {
(hash << 1).wrapping_add(GEAR_TABLE[byte as usize])
}
#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use super::*;
#[test]
fn gear_roll_from_zero_is_the_table_entry() {
assert_eq!(gear_roll(0, 0xAB), GEAR_TABLE[0xAB]);
}
#[test]
fn gear_roll_shifts_then_adds() {
let expected = 2u64.wrapping_add(GEAR_TABLE[0]);
assert_eq!(gear_roll(1, 0), expected);
}
#[test]
fn gear_roll_wraps_on_overflow() {
let h = u64::MAX;
let expected = (h << 1).wrapping_add(GEAR_TABLE[0xFF]);
assert_eq!(gear_roll(h, 0xFF), expected);
}
}