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, Key)>,
119 text: String,
120}
121
122impl LabelIndex {
123 pub(crate) fn clear(&mut self) {
124 self.entries.clear();
125 self.text.clear();
126 }
127
128 pub(crate) fn push(&mut self, key: Key, label: &str, origin: crate::tree::OriginId, fill: Key) {
129 let start = self.text.len() as u32;
130 self.text.push_str(label);
131 self.entries
132 .push((key, start, label.len() as u32, origin, fill));
133 }
134
135 pub(crate) fn label_of(&self, key: Key) -> Option<&str> {
137 self.entries
138 .iter()
139 .find(|(k, ..)| *k == key)
140 .map(|(_, start, len, ..)| &self.text[*start as usize..(*start + *len) as usize])
141 }
142
143 pub(crate) fn iter(&self) -> impl Iterator<Item = (Key, &str)> + '_ {
146 self.entries
147 .iter()
148 .map(|(k, start, len, ..)| (*k, &self.text[*start as usize..(*start + *len) as usize]))
149 }
150
151 pub(crate) fn find<'a>(
154 &'a self,
155 label: &'a str,
156 ) -> impl Iterator<Item = (Key, crate::tree::OriginId)> + 'a {
157 self.find_in(label).map(|(k, o, _)| (k, o))
158 }
159
160 pub(crate) fn find_in_fill(
167 &self,
168 label: &str,
169 origin: crate::tree::OriginId,
170 fill: Key,
171 ) -> (Option<Key>, usize) {
172 let mut own = self
173 .find_in(label)
174 .filter(|(_, o, f)| *o == origin && *f == fill);
175 match own.next() {
176 Some((k, ..)) => (Some(k), 1 + own.count()),
177 None => (None, 0),
178 }
179 }
180
181 fn find_in<'a>(
183 &'a self,
184 label: &'a str,
185 ) -> impl Iterator<Item = (Key, crate::tree::OriginId, Key)> + 'a {
186 self.entries
187 .iter()
188 .filter(move |(_, start, len, ..)| {
189 *len as usize == label.len()
190 && &self.text[*start as usize..(*start + *len) as usize] == label
191 })
192 .map(|(k, _, _, o, f)| (*k, *o, *f))
193 }
194
195 pub(crate) fn find_for(
208 &self,
209 label: &str,
210 origin: crate::tree::OriginId,
211 ) -> (Option<Key>, usize) {
212 let mut mine = self.find(label).filter(|(_, o)| *o == origin);
213 let first = mine.next();
214 if let Some((k, _)) = first {
215 return (Some(k), 1 + mine.count());
216 }
217 if origin != crate::tree::OriginId::HOST {
218 return (None, 0);
219 }
220 let mut all = self.find(label);
221 match all.next() {
222 Some((k, _)) => (Some(k), 1 + all.count()),
223 None => (None, 0),
224 }
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
237 fn the_bulk_hash_separates_tails_and_lengths() {
238 let a = b"0123456789abcdef0123456789abcdef0123456789abcdef";
239 let mut with_zero = a.to_vec();
240 with_zero.push(0);
241 assert_ne!(hash_bulk(a), hash_bulk(&with_zero));
242 let mut tail = a.to_vec();
243 tail[47] ^= 1;
244 assert_ne!(hash_bulk(a), hash_bulk(&tail));
245 let mut word = a.to_vec();
246 word[8] ^= 1;
247 assert_ne!(hash_bulk(a), hash_bulk(&word));
248 assert_eq!(hash_bulk(a), hash_bulk(a.as_ref()));
249 assert_ne!(hash_bulk(b""), hash_bulk(b"\0"));
250 assert_ne!(hash_bulk(b"abcdefg"), hash_bulk(b"abcdefg\0"));
251 let short = b"0123456789abcdef0123456789abcde";
253 let long = b"0123456789abcdef0123456789abcdef";
254 assert_eq!(mix_content(FNV_OFFSET, short), fnv(FNV_OFFSET, short));
255 assert_eq!(
256 mix_content(FNV_OFFSET, long),
257 fnv(FNV_OFFSET, &hash_bulk(long).to_le_bytes())
258 );
259 assert_ne!(
260 mix_content(FNV_OFFSET, short),
261 mix_content(FNV_OFFSET, long)
262 );
263 }
264
265 #[test]
266 fn label_index_finds_in_tree_order_and_clears() {
267 use crate::tree::OriginId;
268 let mut idx = LabelIndex::default();
269 idx.push(Key::ROOT.str("a"), "a", OriginId::HOST, Key::ROOT);
270 idx.push(Key::ROOT.str("ab"), "ab", OriginId::HOST, Key::ROOT);
271 idx.push(Key::ROOT.index(0).str("a"), "a", OriginId(1), Key::ROOT);
272 let a: Vec<Key> = idx.find("a").map(|(k, _)| k).collect();
273 assert_eq!(a, [Key::ROOT.str("a"), Key::ROOT.index(0).str("a")]);
274 assert_eq!(
277 idx.find_for("a", OriginId::HOST),
278 (Some(Key::ROOT.str("a")), 1)
279 );
280 assert_eq!(
281 idx.find_for("a", OriginId(1)),
282 (Some(Key::ROOT.index(0).str("a")), 1)
283 );
284 assert_eq!(idx.find_for("a", OriginId(2)), (None, 0));
285 assert_eq!(idx.find_for("ab", OriginId(1)), (None, 0));
286 assert_eq!(
287 idx.find_for("ab", OriginId::HOST),
288 (Some(Key::ROOT.str("ab")), 1)
289 );
290 idx.push(Key::ROOT.str("g"), "g", OriginId(1), Key::ROOT);
291 idx.push(Key::ROOT.index(1).str("g"), "g", OriginId(2), Key::ROOT);
292 assert_eq!(
293 idx.find_for("g", OriginId::HOST),
294 (Some(Key::ROOT.str("g")), 2),
295 "the host sees both guests' and hears of the clash"
296 );
297 let (fa, fb) = (Key::ROOT.str("pane/a"), Key::ROOT.str("pane/b"));
300 idx.push(fa.str("list"), "list", OriginId(3), fa);
301 idx.push(fb.str("list"), "list", OriginId(3), fb);
302 assert_eq!(
303 idx.find_in_fill("list", OriginId(3), fb),
304 (Some(fb.str("list")), 1)
305 );
306 assert_eq!(idx.find_in_fill("list", OriginId(3), Key::ROOT), (None, 0));
307 assert_eq!(idx.find_in_fill("list", OriginId(4), fa), (None, 0));
308 assert_eq!(
309 idx.find_for("list", OriginId(3)),
310 (Some(fa.str("list")), 2),
311 "across the origin's fills, both, and the clash"
312 );
313 assert_eq!(idx.find("ab").count(), 1, "a prefix is not a match");
314 assert_eq!(idx.find("b").count(), 0);
315 assert_eq!(idx.label_of(Key::ROOT.str("ab")), Some("ab"));
316 assert_eq!(idx.label_of(Key::ROOT.str("zz")), None);
317 idx.clear();
318 assert_eq!(idx.find("a").count(), 0);
319 assert_eq!(idx.label_of(Key::ROOT.str("ab")), None);
320 }
321
322 #[test]
323 fn keys_are_stable() {
324 let a = Key::ROOT.str("panel").index(3);
325 let b = Key::ROOT.str("panel").index(3);
326 assert_eq!(a, b);
327 }
328
329 #[test]
330 fn keys_distinguish_paths() {
331 assert_ne!(Key::ROOT.str("a"), Key::ROOT.str("b"));
332 assert_ne!(Key::ROOT.index(0), Key::ROOT.index(1));
333 assert_ne!(Key::ROOT.str("a").str("b"), Key::ROOT.str("b").str("a"));
334 assert_ne!(Key::ROOT.str("1"), Key::ROOT.index(1));
336 assert_ne!(Key::ROOT.str("ab"), Key::ROOT.str("a").str("b"));
338 }
339
340 #[test]
341 fn no_collisions_over_many_indices() {
342 use std::collections::HashSet;
343 let mut seen = HashSet::new();
344 for scope in 0..100u64 {
345 let s = Key::ROOT.index(scope);
346 for i in 0..100u64 {
347 assert!(seen.insert(s.index(i)), "collision at {scope}/{i}");
348 }
349 }
350 }
351}