Skip to main content

oxihuman_core/
string_hash.rs

1#![allow(dead_code)]
2// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
3// SPDX-License-Identifier: Apache-2.0
4
5/// Computes a deterministic hash for a string.
6#[allow(dead_code)]
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct StringHash(pub u64);
9
10#[allow(dead_code)]
11pub fn compute_string_hash(s: &str) -> StringHash {
12    StringHash(string_hash_u64(s))
13}
14
15#[allow(dead_code)]
16pub fn string_hash_u32(s: &str) -> u32 {
17    let mut h: u32 = 5381;
18    for b in s.bytes() {
19        h = h.wrapping_mul(33).wrapping_add(u32::from(b));
20    }
21    h
22}
23
24#[allow(dead_code)]
25pub fn string_hash_u64(s: &str) -> u64 {
26    let mut h: u64 = 14_695_981_039_346_656_037;
27    for b in s.bytes() {
28        h ^= u64::from(b);
29        h = h.wrapping_mul(1_099_511_628_211);
30    }
31    h
32}
33
34#[allow(dead_code)]
35pub fn string_hashes_equal(a: &str, b: &str) -> bool {
36    string_hash_u64(a) == string_hash_u64(b)
37}
38
39#[allow(dead_code)]
40pub fn hash_combine_strings(a: &str, b: &str) -> u64 {
41    let ha = string_hash_u64(a);
42    let hb = string_hash_u64(b);
43    ha ^ (hb
44        .wrapping_add(0x9e37_79b9_7f4a_7c15)
45        .wrapping_add(ha << 6)
46        .wrapping_add(ha >> 2))
47}
48
49#[allow(dead_code)]
50pub fn hash_to_hex_sh(hash: u64) -> String {
51    format!("{:016x}", hash)
52}
53
54#[allow(dead_code)]
55pub fn string_hash_seed(s: &str, seed: u64) -> u64 {
56    let mut h = seed;
57    for b in s.bytes() {
58        h ^= u64::from(b);
59        h = h.wrapping_mul(1_099_511_628_211);
60    }
61    h
62}
63
64#[allow(dead_code)]
65pub fn hash_empty_string() -> u64 {
66    string_hash_u64("")
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_compute_string_hash() {
75        let h = compute_string_hash("hello");
76        assert_ne!(h.0, 0);
77    }
78
79    #[test]
80    fn test_string_hash_u32() {
81        let h = string_hash_u32("test");
82        assert_ne!(h, 0);
83    }
84
85    #[test]
86    fn test_string_hash_u64() {
87        let h = string_hash_u64("test");
88        assert_ne!(h, 0);
89    }
90
91    #[test]
92    fn test_string_hashes_equal() {
93        assert!(string_hashes_equal("abc", "abc"));
94        assert!(!string_hashes_equal("abc", "def"));
95    }
96
97    #[test]
98    fn test_hash_combine_strings() {
99        let h = hash_combine_strings("hello", "world");
100        assert_ne!(h, string_hash_u64("hello"));
101    }
102
103    #[test]
104    fn test_hash_to_hex_sh() {
105        let hex = hash_to_hex_sh(255);
106        assert_eq!(hex, "00000000000000ff");
107    }
108
109    #[test]
110    fn test_string_hash_seed() {
111        let h1 = string_hash_seed("test", 42);
112        let h2 = string_hash_seed("test", 99);
113        assert_ne!(h1, h2);
114    }
115
116    #[test]
117    fn test_hash_empty_string() {
118        let h = hash_empty_string();
119        assert_eq!(h, string_hash_u64(""));
120    }
121
122    #[test]
123    fn test_deterministic() {
124        assert_eq!(string_hash_u64("abc"), string_hash_u64("abc"));
125    }
126
127    #[test]
128    fn test_different_strings() {
129        assert_ne!(string_hash_u64("abc"), string_hash_u64("xyz"));
130    }
131}