1#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
20pub struct Key(pub u64);
21
22pub const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
25pub const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
26
27#[inline]
35pub fn fnv(mut h: u64, bytes: &[u8]) -> u64 {
36 for &b in bytes {
37 h ^= b as u64;
38 h = h.wrapping_mul(FNV_PRIME);
39 }
40 h
41}
42
43const BULK_MIN: usize = 32;
46
47#[inline]
53pub fn hash_bulk(bytes: &[u8]) -> u64 {
54 const K: u64 = 0x517c_c1b7_2722_0a95;
55 let mut h = FNV_OFFSET ^ (bytes.len() as u64).wrapping_mul(K);
56 let (words, tail) = bytes.as_chunks::<8>();
57 for w in words {
58 h = (h.rotate_left(5) ^ u64::from_le_bytes(*w)).wrapping_mul(K);
59 }
60 if !tail.is_empty() {
61 let mut t = [0u8; 8];
62 t[..tail.len()].copy_from_slice(tail);
63 h = (h.rotate_left(5) ^ u64::from_le_bytes(t)).wrapping_mul(K);
64 }
65 h ^= h >> 33;
66 h = h.wrapping_mul(0xff51_afd7_ed55_8ccd);
67 h ^= h >> 33;
68 h = h.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
69 h ^ (h >> 33)
70}
71
72#[inline]
75pub fn mix_content(h: u64, bytes: &[u8]) -> u64 {
76 if bytes.len() < BULK_MIN {
77 fnv(h, bytes)
78 } else {
79 fnv(h, &hash_bulk(bytes).to_le_bytes())
80 }
81}
82
83impl Key {
84 pub const ROOT: Key = Key(FNV_OFFSET);
85
86 fn mix_bytes(h: u64, bytes: &[u8]) -> u64 {
87 fnv(h, bytes)
88 }
89
90 pub fn str(self, label: &str) -> Key {
92 let h = Self::mix_bytes(self.0 ^ 0x53, label.as_bytes());
94 Key(h)
95 }
96
97 pub fn index(self, i: u64) -> Key {
99 let h = Self::mix_bytes(self.0 ^ 0x49, &i.to_le_bytes());
100 Key(h)
101 }
102}
103
104#[derive(Default)]
112pub(crate) struct LabelIndex {
113 entries: Vec<(Key, u32, u32, crate::tree::OriginId)>,
117 text: String,
118}
119
120impl LabelIndex {
121 pub(crate) fn clear(&mut self) {
122 self.entries.clear();
123 self.text.clear();
124 }
125
126 pub(crate) fn push(&mut self, key: Key, label: &str, origin: crate::tree::OriginId) {
127 let start = self.text.len() as u32;
128 self.text.push_str(label);
129 self.entries.push((key, start, label.len() as u32, origin));
130 }
131
132 pub(crate) fn label_of(&self, key: Key) -> Option<&str> {
134 self.entries
135 .iter()
136 .find(|(k, ..)| *k == key)
137 .map(|(_, start, len, _)| &self.text[*start as usize..(*start + *len) as usize])
138 }
139
140 pub(crate) fn iter(&self) -> impl Iterator<Item = (Key, &str)> + '_ {
143 self.entries
144 .iter()
145 .map(|(k, start, len, _)| (*k, &self.text[*start as usize..(*start + *len) as usize]))
146 }
147
148 pub(crate) fn find<'a>(
151 &'a self,
152 label: &'a str,
153 ) -> impl Iterator<Item = (Key, crate::tree::OriginId)> + 'a {
154 self.entries
155 .iter()
156 .filter(move |(_, start, len, _)| {
157 *len as usize == label.len()
158 && &self.text[*start as usize..(*start + *len) as usize] == label
159 })
160 .map(|(k, _, _, o)| (*k, *o))
161 }
162
163 pub(crate) fn find_for(
174 &self,
175 label: &str,
176 origin: crate::tree::OriginId,
177 ) -> (Option<Key>, usize) {
178 let mut mine = self.find(label).filter(|(_, o)| *o == origin);
179 let first = mine.next();
180 if let Some((k, _)) = first {
181 return (Some(k), 1 + mine.count());
182 }
183 if origin != crate::tree::OriginId::HOST {
184 return (None, 0);
185 }
186 let mut all = self.find(label);
187 match all.next() {
188 Some((k, _)) => (Some(k), 1 + all.count()),
189 None => (None, 0),
190 }
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
203 fn the_bulk_hash_separates_tails_and_lengths() {
204 let a = b"0123456789abcdef0123456789abcdef0123456789abcdef";
205 let mut with_zero = a.to_vec();
206 with_zero.push(0);
207 assert_ne!(hash_bulk(a), hash_bulk(&with_zero));
208 let mut tail = a.to_vec();
209 tail[47] ^= 1;
210 assert_ne!(hash_bulk(a), hash_bulk(&tail));
211 let mut word = a.to_vec();
212 word[8] ^= 1;
213 assert_ne!(hash_bulk(a), hash_bulk(&word));
214 assert_eq!(hash_bulk(a), hash_bulk(a.as_ref()));
215 assert_ne!(hash_bulk(b""), hash_bulk(b"\0"));
216 assert_ne!(hash_bulk(b"abcdefg"), hash_bulk(b"abcdefg\0"));
217 let short = b"0123456789abcdef0123456789abcde";
219 let long = b"0123456789abcdef0123456789abcdef";
220 assert_eq!(mix_content(FNV_OFFSET, short), fnv(FNV_OFFSET, short));
221 assert_eq!(
222 mix_content(FNV_OFFSET, long),
223 fnv(FNV_OFFSET, &hash_bulk(long).to_le_bytes())
224 );
225 assert_ne!(
226 mix_content(FNV_OFFSET, short),
227 mix_content(FNV_OFFSET, long)
228 );
229 }
230
231 #[test]
232 fn label_index_finds_in_tree_order_and_clears() {
233 use crate::tree::OriginId;
234 let mut idx = LabelIndex::default();
235 idx.push(Key::ROOT.str("a"), "a", OriginId::HOST);
236 idx.push(Key::ROOT.str("ab"), "ab", OriginId::HOST);
237 idx.push(Key::ROOT.index(0).str("a"), "a", OriginId(1));
238 let a: Vec<Key> = idx.find("a").map(|(k, _)| k).collect();
239 assert_eq!(a, [Key::ROOT.str("a"), Key::ROOT.index(0).str("a")]);
240 assert_eq!(
243 idx.find_for("a", OriginId::HOST),
244 (Some(Key::ROOT.str("a")), 1)
245 );
246 assert_eq!(
247 idx.find_for("a", OriginId(1)),
248 (Some(Key::ROOT.index(0).str("a")), 1)
249 );
250 assert_eq!(idx.find_for("a", OriginId(2)), (None, 0));
251 assert_eq!(idx.find_for("ab", OriginId(1)), (None, 0));
252 assert_eq!(
253 idx.find_for("ab", OriginId::HOST),
254 (Some(Key::ROOT.str("ab")), 1)
255 );
256 idx.push(Key::ROOT.str("g"), "g", OriginId(1));
257 idx.push(Key::ROOT.index(1).str("g"), "g", OriginId(2));
258 assert_eq!(
259 idx.find_for("g", OriginId::HOST),
260 (Some(Key::ROOT.str("g")), 2),
261 "the host sees both guests' and hears of the clash"
262 );
263 assert_eq!(idx.find("ab").count(), 1, "a prefix is not a match");
264 assert_eq!(idx.find("b").count(), 0);
265 assert_eq!(idx.label_of(Key::ROOT.str("ab")), Some("ab"));
266 assert_eq!(idx.label_of(Key::ROOT.str("zz")), None);
267 idx.clear();
268 assert_eq!(idx.find("a").count(), 0);
269 assert_eq!(idx.label_of(Key::ROOT.str("ab")), None);
270 }
271
272 #[test]
273 fn keys_are_stable() {
274 let a = Key::ROOT.str("panel").index(3);
275 let b = Key::ROOT.str("panel").index(3);
276 assert_eq!(a, b);
277 }
278
279 #[test]
280 fn keys_distinguish_paths() {
281 assert_ne!(Key::ROOT.str("a"), Key::ROOT.str("b"));
282 assert_ne!(Key::ROOT.index(0), Key::ROOT.index(1));
283 assert_ne!(Key::ROOT.str("a").str("b"), Key::ROOT.str("b").str("a"));
284 assert_ne!(Key::ROOT.str("1"), Key::ROOT.index(1));
286 assert_ne!(Key::ROOT.str("ab"), Key::ROOT.str("a").str("b"));
288 }
289
290 #[test]
291 fn no_collisions_over_many_indices() {
292 use std::collections::HashSet;
293 let mut seen = HashSet::new();
294 for scope in 0..100u64 {
295 let s = Key::ROOT.index(scope);
296 for i in 0..100u64 {
297 assert!(seen.insert(s.index(i)), "collision at {scope}/{i}");
298 }
299 }
300 }
301}