1#![allow(rustdoc::private_intra_doc_links)] use crate::helpers::*;
52
53#[cfg(feature = "alloc")]
54mod builder;
55#[cfg(feature = "alloc")]
56mod cached_owned;
57
58#[cfg(feature = "alloc")]
59pub use cached_owned::PerfectByteHashMapCacheOwned;
60
61#[cfg(feature = "alloc")] const P_FAST_MAX: u8 = 95;
64
65const Q_FAST_MAX: u8 = 95;
67
68#[cfg(feature = "alloc")] const P_REAL_MAX: u8 = P_FAST_MAX;
72
73#[cfg(feature = "alloc")] const Q_REAL_MAX: u8 = 127;
76
77#[inline]
111pub fn f1(byte: u8, p: u8, n: u8) -> u8 {
112 if n == 0 {
113 byte
114 } else if p == 0 {
115 byte % n
116 } else {
117 let result = byte ^ p ^ byte.wrapping_shr(p as u32);
121 result % n
122 }
123}
124
125#[inline]
159pub fn f2(byte: u8, q: u8, n: u8) -> u8 {
160 if n == 0 {
161 return byte;
162 }
163 let mut result = byte ^ q;
164 for _ in Q_FAST_MAX..q {
168 result = result ^ (result << 1) ^ (result >> 1);
169 }
170 result % n
171}
172
173#[derive(#[automatically_derived]
impl<Store: ::core::fmt::Debug + ?Sized> ::core::fmt::Debug for
PerfectByteHashMap<Store> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"PerfectByteHashMap", &&self.0)
}
}Debug, #[automatically_derived]
impl<Store: ::core::cmp::PartialEq + ?Sized> ::core::cmp::PartialEq for
PerfectByteHashMap<Store> {
#[inline]
fn eq(&self, other: &PerfectByteHashMap<Store>) -> bool {
self.0 == other.0
}
}PartialEq, #[automatically_derived]
impl<Store: ::core::cmp::Eq + ?Sized> ::core::cmp::Eq for
PerfectByteHashMap<Store> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<Store>;
}
}Eq)]
179#[repr(transparent)]
180pub struct PerfectByteHashMap<Store: ?Sized>(Store);
181
182impl<Store> PerfectByteHashMap<Store> {
183 #[inline]
185 pub fn from_store(store: Store) -> Self {
186 Self(store)
187 }
188}
189
190impl<Store> PerfectByteHashMap<Store>
191where
192 Store: AsRef<[u8]> + ?Sized,
193{
194 pub fn get(&self, key: u8) -> Option<usize> {
196 let (p, buffer) = self.0.as_ref().split_first()?;
197 let n_usize = buffer.len() / 2;
199 if n_usize == 0 {
200 return None;
201 }
202 let n = n_usize as u8;
203 let (qq, eks) = buffer.debug_split_at(n_usize);
204 if true {
match (&qq.len(), &eks.len()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(qq.len(), eks.len());
205 let l1 = f1(key, *p, n) as usize;
206 let q = match qq.get(l1) {
Some(x) => x,
None => {
if true {
if !false {
{
::core::panicking::panic_fmt(format_args!("invalid trie"));
}
};
};
return None;
}
}debug_unwrap!(qq.get(l1), return None);
207 let l2 = f2(key, *q, n) as usize;
208 let ek = match eks.get(l2) {
Some(x) => x,
None => {
if true {
if !false {
{
::core::panicking::panic_fmt(format_args!("invalid trie"));
}
};
};
return None;
}
}debug_unwrap!(eks.get(l2), return None);
209 if *ek == key {
210 Some(l2)
211 } else {
212 None
213 }
214 }
215 pub fn num_items(&self) -> usize {
218 self.0.as_ref().len() / 2
219 }
220 pub fn keys(&self) -> &[u8] {
222 let n = self.num_items();
223 self.0.as_ref().debug_split_at(1 + n).1
224 }
225 #[cfg(test)]
227 pub fn p_qmax(&self) -> Option<(u8, u8)> {
228 let (p, buffer) = self.0.as_ref().split_first()?;
229 let n = buffer.len() / 2;
230 if n == 0 {
231 return None;
232 }
233 let (qq, _) = buffer.debug_split_at(n);
234 Some((*p, *qq.iter().max().unwrap()))
235 }
236 pub fn as_bytes(&self) -> &[u8] {
239 self.0.as_ref()
240 }
241
242 #[cfg(all(feature = "alloc", test))]
243 pub(crate) fn check(&self) -> Result<(), (&'static str, u8)> {
244 use alloc::vec;
245 let len = self.num_items();
246 let mut seen = vec![false; len];
247 for b in 0..=255u8 {
248 let get_result = self.get(b);
249 if self.keys().contains(&b) {
250 let i = get_result.ok_or(("expected to find", b))?;
251 if seen[i] {
252 return Err(("seen", b));
253 }
254 seen[i] = true;
255 } else if get_result.is_some() {
256 return Err(("did not expect to find", b));
257 }
258 }
259 Ok(())
260 }
261}
262
263impl PerfectByteHashMap<[u8]> {
264 #[inline]
266 #[allow(unsafe_code)] pub fn from_bytes(bytes: &[u8]) -> &Self {
268 unsafe { &*(bytes as *const [u8] as *const Self) }
270 }
271}
272
273impl<Store> PerfectByteHashMap<Store>
274where
275 Store: AsRef<[u8]> + ?Sized,
276{
277 #[inline]
279 pub fn as_borrowed(&self) -> &PerfectByteHashMap<[u8]> {
280 PerfectByteHashMap::from_bytes(self.0.as_ref())
281 }
282}
283
284#[cfg(all(test, feature = "alloc"))]
285mod tests {
286 use super::*;
287 use alloc::vec::Vec;
288 extern crate std;
289
290 fn random_alphanums(seed: u64, len: usize) -> Vec<u8> {
291 use rand::seq::SliceRandom;
292 use rand::SeedableRng;
293
294 let mut bytes: Vec<u8> =
295 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".into();
296 let mut rng = rand_pcg::Lcg64Xsh32::seed_from_u64(seed);
297 bytes.partial_shuffle(&mut rng, len).0.into()
298 }
299
300 #[test]
301 fn test_smaller() {
302 let mut count_by_p = [0; 256];
303 let mut count_by_qmax = [0; 256];
304 for len in 1..16 {
305 for seed in 0..150 {
306 let keys = random_alphanums(seed, len);
307 let keys_str = core::str::from_utf8(&keys).unwrap();
308 let computed = PerfectByteHashMap::try_new(&keys).expect(keys_str);
309 computed
310 .check()
311 .unwrap_or_else(|_| panic!("{}", std::str::from_utf8(&keys).expect(keys_str)));
312 let (p, qmax) = computed.p_qmax().unwrap();
313 count_by_p[p as usize] += 1;
314 count_by_qmax[qmax as usize] += 1;
315 }
316 }
317 std::println!("count_by_p (smaller): {count_by_p:?}");
318 std::println!("count_by_qmax (smaller): {count_by_qmax:?}");
319 let count_fastq = count_by_qmax[0..=Q_FAST_MAX as usize].iter().sum::<usize>();
320 let count_slowq = count_by_qmax[Q_FAST_MAX as usize + 1..]
321 .iter()
322 .sum::<usize>();
323 std::println!("fastq/slowq: {count_fastq}/{count_slowq}");
324 assert!(count_fastq >= count_slowq * 100);
326 }
327
328 #[test]
329 fn test_larger() {
330 let mut count_by_p = [0; 256];
331 let mut count_by_qmax = [0; 256];
332 for len in 16..60 {
333 for seed in 0..75 {
334 let keys = random_alphanums(seed, len);
335 let keys_str = core::str::from_utf8(&keys).unwrap();
336 let computed = PerfectByteHashMap::try_new(&keys).expect(keys_str);
337 computed
338 .check()
339 .unwrap_or_else(|_| panic!("{}", std::str::from_utf8(&keys).expect(keys_str)));
340 let (p, qmax) = computed.p_qmax().unwrap();
341 count_by_p[p as usize] += 1;
342 count_by_qmax[qmax as usize] += 1;
343 }
344 }
345 std::println!("count_by_p (larger): {count_by_p:?}");
346 std::println!("count_by_qmax (larger): {count_by_qmax:?}");
347 let count_fastq = count_by_qmax[0..=Q_FAST_MAX as usize].iter().sum::<usize>();
348 let count_slowq = count_by_qmax[Q_FAST_MAX as usize + 1..]
349 .iter()
350 .sum::<usize>();
351 std::println!("fastq/slowq: {count_fastq}/{count_slowq}");
352 assert!(count_fastq >= count_slowq * 100);
354 }
355
356 #[test]
357 fn test_hard_cases() {
358 let keys = [
359 0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
360 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
361 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
362 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
363 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
364 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
365 126, 195, 196,
366 ];
367
368 let computed = PerfectByteHashMap::try_new(&keys).unwrap();
369 let (p, qmax) = computed.p_qmax().unwrap();
370 assert_eq!(p, 69);
371 assert_eq!(qmax, 67);
372 }
373
374 #[test]
375 fn test_build_read_small() {
376 #[derive(Debug)]
377 struct TestCase<'a> {
378 keys: &'a str,
379 expected: &'a [u8],
380 reordered_keys: &'a str,
381 }
382 let cases = [
383 TestCase {
384 keys: "ab",
385 expected: &[0, 0, 0, b'b', b'a'],
386 reordered_keys: "ba",
387 },
388 TestCase {
389 keys: "abc",
390 expected: &[0, 0, 0, 0, b'c', b'a', b'b'],
391 reordered_keys: "cab",
392 },
393 TestCase {
394 keys: "ac",
397 expected: &[1, 0, 1, b'c', b'a'],
398 reordered_keys: "ca",
399 },
400 TestCase {
401 keys: "aceg",
402 expected: &[1, 0, 0, 1, 1, b'e', b'a', b'c', b'g'],
403 reordered_keys: "eacg",
404 },
405 TestCase {
406 keys: "abd",
407 expected: &[0, 0, 1, 3, b'a', b'b', b'd'],
408 reordered_keys: "abd",
409 },
410 TestCase {
411 keys: "def",
412 expected: &[0, 0, 0, 0, b'f', b'd', b'e'],
413 reordered_keys: "fde",
414 },
415 TestCase {
416 keys: "fi",
417 expected: &[0, 0, 0, b'f', b'i'],
418 reordered_keys: "fi",
419 },
420 TestCase {
421 keys: "gh",
422 expected: &[0, 0, 0, b'h', b'g'],
423 reordered_keys: "hg",
424 },
425 TestCase {
426 keys: "lm",
427 expected: &[0, 0, 0, b'l', b'm'],
428 reordered_keys: "lm",
429 },
430 TestCase {
431 keys: "aq",
434 expected: &[4, 0, 1, b'a', b'q'],
435 reordered_keys: "aq",
436 },
437 TestCase {
438 keys: "xy",
439 expected: &[0, 0, 0, b'x', b'y'],
440 reordered_keys: "xy",
441 },
442 TestCase {
443 keys: "xyz",
444 expected: &[0, 0, 0, 0, b'x', b'y', b'z'],
445 reordered_keys: "xyz",
446 },
447 TestCase {
448 keys: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
449 expected: &[
450 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 10, 12, 16, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 16,
451 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
452 2, 0, 7, 104, 105, 106, 107, 108, 109, 110, 111, 112, 117, 118, 119, 68, 69,
453 70, 113, 114, 65, 66, 67, 120, 121, 122, 115, 72, 73, 74, 71, 80, 81, 82, 83,
454 84, 85, 86, 87, 88, 89, 90, 75, 76, 77, 78, 79, 103, 97, 98, 99, 116, 100, 102,
455 101,
456 ],
457 reordered_keys: "hijklmnopuvwDEFqrABCxyzsHIJGPQRSTUVWXYZKLMNOgabctdfe",
458 },
459 TestCase {
460 keys: "abcdefghij",
461 expected: &[
462 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 101, 102, 103, 104, 105, 106, 97, 98, 99,
463 ],
464 reordered_keys: "defghijabc",
465 },
466 TestCase {
467 keys: "Jbej",
469 expected: &[2, 0, 0, 102, 0, b'j', b'e', b'b', b'J'],
470 reordered_keys: "jebJ",
471 },
472 TestCase {
473 keys: "JFNv",
475 expected: &[1, 98, 0, 2, 0, b'J', b'F', b'N', b'v'],
476 reordered_keys: "JFNv",
477 },
478 ];
479 for cas in cases {
480 let computed = PerfectByteHashMap::try_new(cas.keys.as_bytes()).expect(cas.keys);
481 assert_eq!(computed.as_bytes(), cas.expected, "{cas:?}");
482 assert_eq!(computed.keys(), cas.reordered_keys.as_bytes(), "{cas:?}");
483 computed.check().expect(cas.keys);
484 }
485 }
486}