encrust_core/
hashstrings.rs1use rapidhash::rapidhash_seeded;
7use zeroize::Zeroize;
8
9#[cfg_attr(docsrs, doc(cfg(feature = "hashstrings")))]
11pub enum Sensitivity {
12 CaseInsensitive,
14 CaseSensitive,
16}
17
18#[cfg_attr(docsrs, doc(cfg(feature = "hashstrings")))]
35pub struct Hashstring {
36 value: u64,
37 seed: u64,
38 sensitivity: Sensitivity,
39}
40
41impl Hashstring {
42 pub fn new(s: &str, seed: u64, sensitivity: Sensitivity) -> Self {
51 let value = match sensitivity {
52 Sensitivity::CaseInsensitive => {
53 let mut lowercase_string = s.to_lowercase();
54 let hash = rapidhash_seeded(lowercase_string.as_bytes(), seed);
55 Zeroize::zeroize(&mut lowercase_string);
56
57 hash
58 }
59 Sensitivity::CaseSensitive => rapidhash_seeded(s.as_bytes(), seed),
60 };
61
62 Self {
63 value,
64 seed,
65 sensitivity,
66 }
67 }
68
69 #[doc(hidden)]
72 #[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
73 #[cfg(feature = "macros")]
74 pub fn get_raw_value(&self) -> u64 {
75 self.value
76 }
77
78 #[doc(hidden)]
81 #[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
82 #[cfg(feature = "macros")]
83 pub fn new_from_raw_value(value: u64, seed: u64, sensitivity: Sensitivity) -> Self {
84 Self {
85 value,
86 seed,
87 sensitivity,
88 }
89 }
90}
91
92impl PartialEq<&str> for Hashstring {
93 fn eq(&self, other: &&str) -> bool {
94 let other_value = match self.sensitivity {
95 Sensitivity::CaseInsensitive => {
96 rapidhash_seeded(other.to_lowercase().as_bytes(), self.seed)
97 }
98 Sensitivity::CaseSensitive => rapidhash_seeded(other.as_bytes(), self.seed),
99 };
100
101 self.value == other_value
102 }
103}
104
105#[cfg_attr(docsrs, doc(cfg(feature = "hashstrings")))]
117pub struct Hashbytes {
118 value: u64,
119 seed: u64,
120}
121
122impl Hashbytes {
123 pub fn new(bytes: &[u8], seed: u64) -> Self {
128 let value = rapidhash_seeded(bytes, seed);
129
130 Self { value, seed }
131 }
132
133 #[doc(hidden)]
136 #[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
137 #[cfg(feature = "macros")]
138 pub fn get_raw_value(&self) -> u64 {
139 self.value
140 }
141
142 #[doc(hidden)]
145 #[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
146 #[cfg(feature = "macros")]
147 pub fn new_from_raw_value(value: u64, seed: u64) -> Self {
148 Self { value, seed }
149 }
150}
151
152impl PartialEq<&[u8]> for Hashbytes {
153 fn eq(&self, other: &&[u8]) -> bool {
154 let other_value = rapidhash_seeded(other, self.seed);
155
156 self.value == other_value
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use rand::RngCore;
163
164 use super::*;
165
166 const A_STRING: &str = "A stringš¶";
167 const A_LOWERCASE_STRING: &str = "a stringš¶";
168 const A_STRING_BYTES: &[u8] = A_STRING.as_bytes();
169 const A_LOWERCASE_STRING_BYTES: &[u8] = A_LOWERCASE_STRING.as_bytes();
170
171 #[test]
172 fn test_hashstrings() {
173 let case_sensitive_hashstring =
174 Hashstring::new(A_STRING, rand::rng().next_u64(), Sensitivity::CaseSensitive);
175 let case_insensitive_hashstring = Hashstring::new(
176 A_STRING,
177 rand::rng().next_u64(),
178 Sensitivity::CaseInsensitive,
179 );
180
181 assert!(case_sensitive_hashstring.eq(&A_STRING));
182 assert!(case_sensitive_hashstring.ne(&A_LOWERCASE_STRING));
183 assert!(case_insensitive_hashstring.eq(&A_STRING));
184 assert!(case_insensitive_hashstring.eq(&A_LOWERCASE_STRING));
185 }
186
187 #[test]
188 fn test_hashbytes() {
189 let hashbytes = Hashbytes::new(A_STRING_BYTES, rand::rng().next_u64());
190
191 assert!(hashbytes.eq(&A_STRING_BYTES));
192 assert!(hashbytes.ne(&A_LOWERCASE_STRING_BYTES));
193 }
194}