Skip to main content

yapi_ai/
hash.rs

1//! pi's `shortHash`, used to derive stable ids from longer ones.
2
3/// pi's `shortHash`: two 32-bit mixes over UTF-16 units, printed in base 36.
4pub fn short_hash(text: &str) -> String {
5    let (mut h1, mut h2): (u32, u32) = (0xdead_beef, 0x41c6_ce57);
6    for unit in text.encode_utf16() {
7        let unit = u32::from(unit);
8        h1 = (h1 ^ unit).wrapping_mul(2_654_435_761);
9        h2 = (h2 ^ unit).wrapping_mul(1_597_334_677);
10    }
11    h1 = (h1 ^ (h1 >> 16)).wrapping_mul(2_246_822_507)
12        ^ (h2 ^ (h2 >> 13)).wrapping_mul(3_266_489_909);
13    h2 = (h2 ^ (h2 >> 16)).wrapping_mul(2_246_822_507)
14        ^ (h1 ^ (h1 >> 13)).wrapping_mul(3_266_489_909);
15    format!("{}{}", base36(h2), base36(h1))
16}
17
18fn base36(mut value: u32) -> String {
19    if value == 0 {
20        return "0".into();
21    }
22    let mut digits = Vec::new();
23    while value > 0 {
24        digits.push(std::char::from_digit(value % 36, 36).unwrap_or('0'));
25        value /= 36;
26    }
27    digits.iter().rev().collect()
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn hashes_like_pi() {
36        // Values computed with pi's shortHash in Node.
37        assert_eq!(short_hash(""), "k4n83c7h0j2b");
38        assert_eq!(
39            short_hash("call_abc|fc_0123456789abcdefghijklmnopqrstuvwxyz"),
40            "zdt65wxpmvl8"
41        );
42    }
43}