1#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
7pub struct Key(pub u64);
8
9pub const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
12pub const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
13
14#[inline]
22pub fn fnv(mut h: u64, bytes: &[u8]) -> u64 {
23 for &b in bytes {
24 h ^= b as u64;
25 h = h.wrapping_mul(FNV_PRIME);
26 }
27 h
28}
29
30const BULK_MIN: usize = 32;
33
34#[inline]
42pub fn hash_bulk(bytes: &[u8]) -> u64 {
43 const K: u64 = 0x517c_c1b7_2722_0a95;
44 let mut h = FNV_OFFSET ^ (bytes.len() as u64).wrapping_mul(K);
45 let (words, tail) = bytes.as_chunks::<8>();
46 for w in words {
47 h = (h.rotate_left(5) ^ u64::from_le_bytes(*w)).wrapping_mul(K);
48 }
49 if !tail.is_empty() {
50 let mut t = [0u8; 8];
51 t[..tail.len()].copy_from_slice(tail);
52 h = (h.rotate_left(5) ^ u64::from_le_bytes(t)).wrapping_mul(K);
53 }
54 h ^= h >> 33;
55 h = h.wrapping_mul(0xff51_afd7_ed55_8ccd);
56 h ^= h >> 33;
57 h = h.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
58 h ^ (h >> 33)
59}
60
61#[inline]
64pub fn mix_content(h: u64, bytes: &[u8]) -> u64 {
65 if bytes.len() < BULK_MIN {
66 fnv(h, bytes)
67 } else {
68 fnv(h, &hash_bulk(bytes).to_le_bytes())
69 }
70}
71
72impl Key {
73 pub const ROOT: Key = Key(FNV_OFFSET);
74
75 fn mix_bytes(h: u64, bytes: &[u8]) -> u64 {
76 fnv(h, bytes)
77 }
78
79 pub fn str(self, label: &str) -> Key {
81 let h = Self::mix_bytes(self.0 ^ 0x53, label.as_bytes());
83 Key(h)
84 }
85
86 pub fn index(self, i: u64) -> Key {
88 let h = Self::mix_bytes(self.0 ^ 0x49, &i.to_le_bytes());
89 Key(h)
90 }
91}
92
93#[derive(Default)]
101pub(crate) struct LabelIndex {
102 entries: Vec<(Key, u32, u32, crate::tree::OriginId)>,
106 text: String,
107}
108
109impl LabelIndex {
110 pub(crate) fn clear(&mut self) {
111 self.entries.clear();
112 self.text.clear();
113 }
114
115 pub(crate) fn push(&mut self, key: Key, label: &str, origin: crate::tree::OriginId) {
116 let start = self.text.len() as u32;
117 self.text.push_str(label);
118 self.entries.push((key, start, label.len() as u32, origin));
119 }
120
121 pub(crate) fn label_of(&self, key: Key) -> Option<&str> {
123 self.entries
124 .iter()
125 .find(|(k, ..)| *k == key)
126 .map(|(_, start, len, _)| &self.text[*start as usize..(*start + *len) as usize])
127 }
128
129 pub(crate) fn iter(&self) -> impl Iterator<Item = (Key, &str)> + '_ {
132 self.entries
133 .iter()
134 .map(|(k, start, len, _)| (*k, &self.text[*start as usize..(*start + *len) as usize]))
135 }
136
137 pub(crate) fn find<'a>(
140 &'a self,
141 label: &'a str,
142 ) -> impl Iterator<Item = (Key, crate::tree::OriginId)> + 'a {
143 self.entries
144 .iter()
145 .filter(move |(_, start, len, _)| {
146 *len as usize == label.len()
147 && &self.text[*start as usize..(*start + *len) as usize] == label
148 })
149 .map(|(k, _, _, o)| (*k, *o))
150 }
151
152 pub(crate) fn find_for(
163 &self,
164 label: &str,
165 origin: crate::tree::OriginId,
166 ) -> (Option<Key>, usize) {
167 let mut mine = self.find(label).filter(|(_, o)| *o == origin);
168 let first = mine.next();
169 if let Some((k, _)) = first {
170 return (Some(k), 1 + mine.count());
171 }
172 if origin != crate::tree::OriginId::HOST {
173 return (None, 0);
174 }
175 let mut all = self.find(label);
176 match all.next() {
177 Some((k, _)) => (Some(k), 1 + all.count()),
178 None => (None, 0),
179 }
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
192 fn the_bulk_hash_separates_tails_and_lengths() {
193 let a = b"0123456789abcdef0123456789abcdef0123456789abcdef";
194 let mut with_zero = a.to_vec();
195 with_zero.push(0);
196 assert_ne!(hash_bulk(a), hash_bulk(&with_zero));
197 let mut tail = a.to_vec();
198 tail[47] ^= 1;
199 assert_ne!(hash_bulk(a), hash_bulk(&tail));
200 let mut word = a.to_vec();
201 word[8] ^= 1;
202 assert_ne!(hash_bulk(a), hash_bulk(&word));
203 assert_eq!(hash_bulk(a), hash_bulk(a.as_ref()));
204 assert_ne!(hash_bulk(b""), hash_bulk(b"\0"));
205 assert_ne!(hash_bulk(b"abcdefg"), hash_bulk(b"abcdefg\0"));
206 let short = b"0123456789abcdef0123456789abcde";
208 let long = b"0123456789abcdef0123456789abcdef";
209 assert_eq!(mix_content(FNV_OFFSET, short), fnv(FNV_OFFSET, short));
210 assert_eq!(
211 mix_content(FNV_OFFSET, long),
212 fnv(FNV_OFFSET, &hash_bulk(long).to_le_bytes())
213 );
214 assert_ne!(
215 mix_content(FNV_OFFSET, short),
216 mix_content(FNV_OFFSET, long)
217 );
218 }
219
220 #[test]
221 fn label_index_finds_in_tree_order_and_clears() {
222 use crate::tree::OriginId;
223 let mut idx = LabelIndex::default();
224 idx.push(Key::ROOT.str("a"), "a", OriginId::HOST);
225 idx.push(Key::ROOT.str("ab"), "ab", OriginId::HOST);
226 idx.push(Key::ROOT.index(0).str("a"), "a", OriginId(1));
227 let a: Vec<Key> = idx.find("a").map(|(k, _)| k).collect();
228 assert_eq!(a, [Key::ROOT.str("a"), Key::ROOT.index(0).str("a")]);
229 assert_eq!(
232 idx.find_for("a", OriginId::HOST),
233 (Some(Key::ROOT.str("a")), 1)
234 );
235 assert_eq!(
236 idx.find_for("a", OriginId(1)),
237 (Some(Key::ROOT.index(0).str("a")), 1)
238 );
239 assert_eq!(idx.find_for("a", OriginId(2)), (None, 0));
240 assert_eq!(idx.find_for("ab", OriginId(1)), (None, 0));
241 assert_eq!(
242 idx.find_for("ab", OriginId::HOST),
243 (Some(Key::ROOT.str("ab")), 1)
244 );
245 idx.push(Key::ROOT.str("g"), "g", OriginId(1));
246 idx.push(Key::ROOT.index(1).str("g"), "g", OriginId(2));
247 assert_eq!(
248 idx.find_for("g", OriginId::HOST),
249 (Some(Key::ROOT.str("g")), 2),
250 "the host sees both guests' and hears of the clash"
251 );
252 assert_eq!(idx.find("ab").count(), 1, "a prefix is not a match");
253 assert_eq!(idx.find("b").count(), 0);
254 assert_eq!(idx.label_of(Key::ROOT.str("ab")), Some("ab"));
255 assert_eq!(idx.label_of(Key::ROOT.str("zz")), None);
256 idx.clear();
257 assert_eq!(idx.find("a").count(), 0);
258 assert_eq!(idx.label_of(Key::ROOT.str("ab")), None);
259 }
260
261 #[test]
262 fn keys_are_stable() {
263 let a = Key::ROOT.str("panel").index(3);
264 let b = Key::ROOT.str("panel").index(3);
265 assert_eq!(a, b);
266 }
267
268 #[test]
269 fn keys_distinguish_paths() {
270 assert_ne!(Key::ROOT.str("a"), Key::ROOT.str("b"));
271 assert_ne!(Key::ROOT.index(0), Key::ROOT.index(1));
272 assert_ne!(Key::ROOT.str("a").str("b"), Key::ROOT.str("b").str("a"));
273 assert_ne!(Key::ROOT.str("1"), Key::ROOT.index(1));
275 assert_ne!(Key::ROOT.str("ab"), Key::ROOT.str("a").str("b"));
277 }
278
279 #[test]
280 fn no_collisions_over_many_indices() {
281 use std::collections::HashSet;
282 let mut seen = HashSet::new();
283 for scope in 0..100u64 {
284 let s = Key::ROOT.index(scope);
285 for i in 0..100u64 {
286 assert!(seen.insert(s.index(i)), "collision at {scope}/{i}");
287 }
288 }
289 }
290}