1use crate::prefetch;
52#[cfg(not(feature = "std"))]
53use alloc::{vec, vec::Vec};
54use core::iter;
55
56const FINGERPRINT_SHIFT: u32 = 56;
58
59const EMPTY: u32 = u32::MAX;
63
64const MAX_DISPLACEMENT: u32 = (1 << 24) - 2;
69
70const DISPLACEMENT_UNIT: u32 = 1 << 8;
72
73const MIN_SLOTS: usize = 16;
75
76#[inline]
78const fn slot_meta(hash: u64, displacement: u32) -> u32 {
79 (displacement << 8) | (hash >> FINGERPRINT_SHIFT) as u32
80}
81
82#[inline]
92pub const fn partition(hash: u64, partitions: usize) -> usize {
93 assert!(
94 partitions.is_power_of_two() && partitions <= 1 << 16,
95 "partitions must be a power of two no larger than 2^16"
96 );
97 (hash >> (FINGERPRINT_SHIFT - partitions.trailing_zeros())) as usize & (partitions - 1)
98}
99
100#[repr(C, packed)]
102struct Slot<H> {
103 meta: u32,
105 handle: H,
107}
108
109impl<H: Copy> Clone for Slot<H> {
110 fn clone(&self) -> Self {
111 *self
112 }
113}
114
115impl<H: Copy> Copy for Slot<H> {}
116
117pub struct Table<H> {
146 slots: Vec<Slot<H>>,
148 len: usize,
149}
150
151impl<H> core::fmt::Debug for Table<H> {
152 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153 f.debug_struct("Table")
154 .field("len", &self.len)
155 .field("slots", &self.slots.len())
156 .finish()
157 }
158}
159
160impl<H: Copy + Eq + Default> Table<H> {
161 pub const fn new() -> Self {
163 Self {
164 slots: Vec::new(),
165 len: 0,
166 }
167 }
168
169 pub fn with_capacity(entries: usize) -> Self {
175 if entries == 0 {
176 return Self::new();
177 }
178 Self {
179 slots: Self::empty_slots(Self::slots_for(entries)),
180 len: 0,
181 }
182 }
183
184 fn slots_for(entries: usize) -> usize {
190 entries
191 .checked_mul(4)
192 .and_then(|scaled| scaled.div_ceil(3).checked_next_power_of_two())
193 .expect("capacity overflow")
194 .max(MIN_SLOTS)
195 }
196
197 fn empty_slots(count: usize) -> Vec<Slot<H>> {
199 vec![
200 Slot {
201 meta: EMPTY,
202 handle: H::default(),
203 };
204 count
205 ]
206 }
207
208 pub const fn len(&self) -> usize {
210 self.len
211 }
212
213 pub const fn is_empty(&self) -> bool {
215 self.len == 0
216 }
217
218 pub const fn capacity(&self) -> usize {
220 self.slots.len() * 3 / 4
221 }
222
223 pub fn reserve(&mut self, additional: usize, mut rehash: impl FnMut(H) -> u64) {
231 let entries = self.len.checked_add(additional).expect("capacity overflow");
232 if entries > self.capacity() {
233 self.rebuild(Self::slots_for(entries), &mut rehash);
234 }
235 }
236
237 #[inline]
243 pub fn get(&self, hash: u64) -> impl Iterator<Item = H> + '_ {
244 self.probe(hash).map(|slot| self.slots[slot].handle)
245 }
246
247 #[inline]
250 pub fn prefetch(&self, hash: u64) {
251 if !prefetch::ENABLED {
252 return;
253 }
254 let mask = self.slots.len().wrapping_sub(1);
255 if let Some(slot) = self.slots.get(hash as usize & mask) {
256 prefetch::prefetch(slot);
257 }
258 }
259
260 #[inline]
270 pub fn insert(&mut self, hash: u64, handle: H, mut rehash: impl FnMut(H) -> u64) {
271 if self.len == self.capacity() {
272 self.grow(&mut rehash);
273 }
274 self.place(hash, handle);
275 self.len += 1;
276 }
277
278 #[inline]
290 pub fn find_or_insert(
291 &mut self,
292 hash: u64,
293 matches: impl FnMut(H) -> bool,
294 handle: H,
295 mut rehash: impl FnMut(H) -> u64,
296 ) -> (H, bool) {
297 if self.len == self.capacity() {
298 self.grow(&mut rehash);
299 }
300 let (target, displacement) = match self.walk(hash, matches) {
301 Ok(found) => return (found, false),
302 Err(stop) => stop,
303 };
304 self.place_at(target, displacement, hash, handle);
305 self.len += 1;
306 (handle, true)
307 }
308
309 #[inline]
315 pub fn remove(&mut self, hash: u64, handle: H) -> bool {
316 let Some(mut hole) = self.probe(hash).find(|&slot| {
317 let candidate = self.slots[slot].handle;
318 candidate == handle
319 }) else {
320 return false;
321 };
322
323 let mask = self.slots.len() - 1;
325 loop {
326 let next = (hole + 1) & mask;
327 let Slot { meta, handle } = self.slots[next];
328 if meta == EMPTY || meta < DISPLACEMENT_UNIT {
329 break;
330 }
331 self.slots[hole] = Slot {
332 meta: meta - DISPLACEMENT_UNIT,
333 handle,
334 };
335 hole = next;
336 }
337 self.slots[hole].meta = EMPTY;
338 self.len -= 1;
339 true
340 }
341
342 #[inline]
347 fn probe(&self, hash: u64) -> impl Iterator<Item = usize> + '_ {
348 let mask = self.slots.len().wrapping_sub(1);
349 let mut slot = hash as usize & mask;
350 let mut displacement = 0;
351 iter::from_fn(move || {
352 loop {
353 let meta = self.slots.get(slot)?.meta;
354 if meta == EMPTY || meta >> 8 < displacement {
355 return None;
356 }
357 let current = slot;
358 let matched = meta == slot_meta(hash, displacement);
359 slot = (slot + 1) & mask;
360 displacement += 1;
361 if matched {
362 return Some(current);
363 }
364 }
365 })
366 }
367
368 #[inline]
376 fn walk(&self, hash: u64, mut matches: impl FnMut(H) -> bool) -> Result<H, (usize, u32)> {
377 let mask = self.slots.len() - 1;
378 let mut slot = hash as usize & mask;
379 let mut displacement = 0;
380 loop {
381 let Slot { meta, handle } = self.slots[slot];
382 if meta == EMPTY || meta >> 8 < displacement {
383 return Err((slot, displacement));
384 }
385 if meta == slot_meta(hash, displacement) && matches(handle) {
386 return Ok(handle);
387 }
388 assert!(
389 displacement < MAX_DISPLACEMENT,
390 "the run from the home slot of hash {hash:#x} must end within the maximum displacement"
391 );
392 slot = (slot + 1) & mask;
393 displacement += 1;
394 }
395 }
396
397 #[inline]
404 fn place(&mut self, hash: u64, handle: H) {
405 let (target, displacement) = match self.walk(hash, |_| false) {
406 Ok(_) => unreachable!("a walk that accepts no candidate must stop at a slot"),
407 Err(stop) => stop,
408 };
409 self.place_at(target, displacement, hash, handle);
410 }
411
412 #[inline]
418 fn place_at(&mut self, target: usize, displacement: u32, hash: u64, handle: H) {
419 let mask = self.slots.len() - 1;
421 let mut slot = target;
422 loop {
423 let meta = self.slots[slot].meta;
424 if meta == EMPTY {
425 break;
426 }
427 assert!(
428 meta >> 8 < MAX_DISPLACEMENT,
429 "shifting the run from slot {target} must not move an entry past the maximum displacement"
430 );
431 slot = (slot + 1) & mask;
432 }
433
434 while slot != target {
436 let previous = slot.wrapping_sub(1) & mask;
437 let Slot { meta, handle } = self.slots[previous];
438 self.slots[slot] = Slot {
439 meta: meta + DISPLACEMENT_UNIT,
440 handle,
441 };
442 slot = previous;
443 }
444 self.slots[target] = Slot {
445 meta: slot_meta(hash, displacement),
446 handle,
447 };
448 }
449
450 fn grow(&mut self, rehash: &mut impl FnMut(H) -> u64) {
452 self.rebuild((self.slots.len() * 2).max(MIN_SLOTS), rehash);
453 }
454
455 fn rebuild(&mut self, slots: usize, rehash: &mut impl FnMut(H) -> u64) {
458 let mut table = Self {
459 slots: Self::empty_slots(slots),
460 len: self.len,
461 };
462 for Slot { meta, handle } in self.slots.iter().copied() {
463 if meta != EMPTY {
464 table.place(rehash(handle), handle);
465 }
466 }
467 *self = table;
468 }
469}
470
471impl<H: Copy + Eq + Default> Default for Table<H> {
472 fn default() -> Self {
473 Self::new()
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480 use crate::{TestRng, test_rng};
481 use core::cell::Cell;
482 use rand::{Rng as _, RngExt as _};
483 use std::collections::BTreeMap;
484
485 impl<H: Copy + Eq + Default> Table<H> {
486 fn metas(&self) -> Vec<u32> {
488 self.slots.iter().map(|slot| slot.meta).collect()
489 }
490
491 fn check_invariants(&self, hash_of: impl Fn(H) -> u64) {
493 let slots = self.slots.len();
494 assert_eq!(self.capacity(), slots * 3 / 4);
495 assert!(self.len <= self.capacity());
496 if slots == 0 {
497 return;
498 }
499 assert!(slots.is_power_of_two() && slots >= MIN_SLOTS);
500 let mask = slots - 1;
501 let mut occupied = 0;
502 for (slot, Slot { meta, handle }) in self.slots.iter().copied().enumerate() {
503 if meta == EMPTY {
504 continue;
505 }
506 occupied += 1;
507 let hash = hash_of(handle);
508 let displacement = slot.wrapping_sub(hash as usize) & mask;
509 assert!(displacement <= MAX_DISPLACEMENT as usize);
510 assert_eq!(meta, slot_meta(hash, displacement as u32));
511
512 let next = self.slots[(slot + 1) & mask].meta;
514 assert!(next == EMPTY || next >> 8 <= (meta >> 8) + 1);
515 }
516 assert_eq!(occupied, self.len);
517 }
518 }
519
520 const fn spread(i: u64) -> u64 {
522 i.wrapping_mul(0x9E37_79B9_7F4A_7C15)
523 }
524
525 fn sorted<H: Ord>(handles: impl Iterator<Item = H>) -> Vec<H> {
526 let mut handles: Vec<H> = handles.collect();
527 handles.sort();
528 handles
529 }
530
531 #[test]
532 fn test_find_or_insert_finds_or_places() {
533 let hashes = [spread(1), spread(1), spread(2)];
536 let rehash = |handle: usize| hashes[handle];
537 let mut table = Table::new();
538 assert_eq!(
539 table.find_or_insert(hashes[0], |_| false, 0, rehash),
540 (0, true)
541 );
542 assert_eq!(
543 table.find_or_insert(hashes[0], |handle| handle == 0, 7, rehash),
544 (0, false)
545 );
546 assert_eq!(
547 table.find_or_insert(hashes[1], |handle| handle == 1, 1, rehash),
548 (1, true)
549 );
550 assert_eq!(table.get(hashes[0]).collect::<Vec<_>>(), vec![0, 1]);
551
552 let mut offered = Vec::new();
554 let accept = |handle| {
555 offered.push(handle);
556 true
557 };
558 assert_eq!(
559 table.find_or_insert(hashes[0], accept, 7, rehash),
560 (0, false)
561 );
562 assert_eq!(offered, vec![0]);
563
564 assert_eq!(
566 table.find_or_insert(hashes[2], |handle| handle == 2, 2, rehash),
567 (2, true)
568 );
569 assert_eq!(table.len(), 3);
570 table.check_invariants(rehash);
571 }
572
573 #[test]
574 fn test_find_or_insert_grows_and_prefetch_is_harmless() {
575 let mut rng = test_rng();
579 let hashes: Vec<u64> = (0..1000).map(|_| rng.random()).collect();
580 let rehash = |handle: usize| hashes[handle];
581 Table::<usize>::new().prefetch(hashes[0]);
582 let mut table = Table::new();
583 for (handle, &hash) in hashes.iter().enumerate() {
584 table.prefetch(hash);
585 assert_eq!(
586 table.find_or_insert(hash, |candidate| candidate == handle, handle, rehash),
587 (handle, true)
588 );
589 assert_eq!(
590 table.find_or_insert(hash, |candidate| candidate == handle, usize::MAX, rehash),
591 (handle, false)
592 );
593 }
594 assert_eq!(table.len(), hashes.len());
595 table.check_invariants(rehash);
596 for (handle, &hash) in hashes.iter().enumerate() {
597 assert!(table.get(hash).any(|candidate| candidate == handle));
598 }
599 }
600
601 #[test]
602 fn test_find_or_insert_grows_a_full_table_before_walking() {
603 let hashes: Vec<u64> = (0..=12)
607 .map(|handle| 0xAB00_0000_0000_000F | ((handle % 2) << 4))
608 .collect();
609 let rehash = |handle: usize| hashes[handle];
610 let full = || {
611 let mut table = Table::new();
612 for (handle, &hash) in hashes.iter().enumerate().take(12) {
613 table.insert(hash, handle, rehash);
614 }
615 assert_eq!(table.len(), table.capacity());
616 table
617 };
618
619 for (accept, want) in [(1, (12, true)), (2, (2, false))] {
623 let mut table = full();
624 let mut offered = Vec::new();
625 let found = table.find_or_insert(
626 hashes[12],
627 |handle| {
628 offered.push(handle);
629 handle == accept
630 },
631 12,
632 rehash,
633 );
634 assert_eq!(found, want);
635 assert_eq!(table.capacity(), 24);
636 assert!(!offered.is_empty() && offered.iter().all(|handle| handle % 2 == 0));
637 table.check_invariants(rehash);
638 }
639 }
640
641 #[test]
642 fn test_find_or_insert_offers_the_candidates_of_get() {
643 let mut rng = test_rng();
647 let mut hashes = vec![
648 0xAB00_0000_0000_0005,
649 0xAB00_0100_0000_0005,
650 0xAB00_0000_0000_0006,
651 0xCD00_0000_0000_0005,
652 ];
653 hashes.extend((0..200).map(|_| rng.next_u64()));
654 let seeded = hashes.len();
655 hashes.extend_from_within(..seeded);
656 hashes.extend((0..50).map(|_| rng.next_u64()));
657 let rehash = |handle: usize| hashes[handle];
658
659 let mut table = Table::new();
661 for (handle, &hash) in hashes.iter().enumerate().take(seeded) {
662 table.insert(hash, handle, rehash);
663 }
664 table.reserve(hashes.len() - seeded, rehash);
665
666 for (handle, &hash) in hashes.iter().enumerate().skip(seeded) {
669 let before: Vec<usize> = table.get(hash).collect();
670 let mut offered = Vec::new();
671 let matches = |candidate: usize| {
672 offered.push(candidate);
673 false
674 };
675 assert_eq!(
676 table.find_or_insert(hash, matches, handle, rehash),
677 (handle, true)
678 );
679 assert_eq!(
680 offered, before,
681 "the walk for hash {hash:#x} must offer the candidates get yields, in order"
682 );
683 let after: Vec<usize> = table.get(hash).collect();
684 assert_eq!(
685 after,
686 [before, vec![handle]].concat(),
687 "the handle inserted under hash {hash:#x} must follow its existing candidates"
688 );
689 }
690 table.check_invariants(rehash);
691 }
692
693 #[test]
694 fn test_basic_insert_get_remove() {
695 let hashes = [spread(1), spread(1), spread(2), spread(3), spread(4)];
697 let rehash = |handle: usize| hashes[handle];
698 let mut table = Table::new();
699 assert!(table.is_empty());
700 assert_eq!(table.slots.capacity(), 0);
701 assert_eq!(table.get(hashes[0]).next(), None);
702 assert!(!table.remove(hashes[0], 0));
703
704 for (handle, &hash) in hashes.iter().enumerate().take(4) {
705 table.insert(hash, handle, rehash);
706 }
707 assert_eq!(table.len(), 4);
708 assert_eq!(sorted(table.get(hashes[0])), [0, 1]);
709 assert_eq!(sorted(table.get(hashes[2])), [2]);
710 assert_eq!(sorted(table.get(hashes[3])), [3]);
711 assert_eq!(table.get(hashes[4]).next(), None);
712
713 assert!(table.remove(hashes[0], 0));
715 assert_eq!(sorted(table.get(hashes[0])), [1]);
716 assert_eq!(table.len(), 3);
717
718 let metas = table.metas();
720 assert!(!table.remove(hashes[0], 0));
721 assert!(!table.remove(hashes[4], 4));
722 assert_eq!(table.metas(), metas);
723 assert_eq!(table.len(), 3);
724
725 table.insert(hashes[1], 1, rehash);
727 assert_eq!(sorted(table.get(hashes[1])), [1, 1]);
728 assert!(table.remove(hashes[1], 1));
729 assert_eq!(sorted(table.get(hashes[1])), [1]);
730 table.check_invariants(rehash);
731 }
732
733 #[test]
734 fn test_fingerprint_false_candidates() {
735 let keys = ["a", "b", "c", "d"];
738 let hashes = [
739 0xAB00_0000_0000_0005,
740 0xAB00_0100_0000_0005,
741 0xAB00_0000_0000_0006,
742 0xCD00_0000_0000_0005,
743 ];
744 let rehash = |handle: usize| hashes[handle];
745 let mut table = Table::new();
746 for (handle, &hash) in hashes.iter().enumerate() {
747 table.insert(hash, handle, rehash);
748 }
749
750 assert_eq!(sorted(table.get(hashes[0])), [0, 1]);
752 let confirmed: Vec<_> = table.get(hashes[0]).filter(|&h| keys[h] == "a").collect();
753 assert_eq!(confirmed, [0]);
754 assert_eq!(sorted(table.get(hashes[2])), [2]);
755 assert_eq!(sorted(table.get(hashes[3])), [3]);
756 table.check_invariants(rehash);
757 }
758
759 #[test]
760 fn test_growth_rehashes_every_entry() {
761 let mut rng = test_rng();
762 let mut hashes = Vec::new();
763 let calls = Cell::new(0);
764 let mut table = Table::new();
765 let mut growths = 0;
766 for handle in 0..5_000 {
767 hashes.push(rng.next_u64());
768 let (slots, len, before) = (table.slots.len(), table.len(), calls.get());
769 table.insert(hashes[handle], handle, |handle| {
770 calls.set(calls.get() + 1);
771 hashes[handle]
772 });
773 assert_eq!(table.len(), handle + 1);
774 if table.slots.len() == slots {
775 assert_eq!(calls.get(), before);
776 continue;
777 }
778
779 growths += 1;
781 assert_eq!(table.slots.len(), (slots * 2).max(MIN_SLOTS));
782 assert_eq!(len, slots * 3 / 4);
783 assert_eq!(calls.get() - before, len);
784 table.check_invariants(|handle| hashes[handle]);
785 for (handle, &hash) in hashes.iter().enumerate() {
786 assert!(table.get(hash).any(|h| h == handle));
787 }
788 }
789 assert_eq!(growths, 10);
790 assert_eq!(table.slots.len(), 8_192);
791 }
792
793 #[test]
794 fn test_shared_hash_run() {
795 const ENTRIES: usize = 2_000;
798 let mut table = Table::new();
799 for handle in 0..ENTRIES {
800 table.insert(spread(1), handle, |_| spread(1));
801 }
802 assert_eq!(table.slots.len(), 4_096);
803 assert_eq!(table.get(spread(1)).count(), ENTRIES);
804 table.check_invariants(|_| spread(1));
805
806 for handle in (0..ENTRIES).step_by(2) {
807 assert!(table.remove(spread(1), handle));
808 }
809 assert_eq!(
810 sorted(table.get(spread(1))),
811 sorted((1..ENTRIES).step_by(2))
812 );
813 table.check_invariants(|_| spread(1));
814 }
815
816 #[test]
817 fn test_matches_multimap_model() {
818 const HANDLES: usize = 1_024;
819 const HASHES: usize = 512;
820 const OPERATIONS: usize = 50_000;
821 let mut rng = TestRng::new(5);
822
823 let mut pool: Vec<u64> = Vec::with_capacity(HASHES);
827 for i in 0..HASHES {
828 let fingerprint = rng.random_range(0..4u64) << 56;
829 let middle = rng.next_u64() & 0x00FF_FFFF_FF00_0000;
830 let low = if i % 2 == 1 {
831 pool[i - 1]
832 } else {
833 rng.next_u64()
834 } & 0xFF_FFFF;
835 pool.push(fingerprint | middle | low);
836 }
837 let hash_of: Vec<u64> = (0..HANDLES)
838 .map(|_| pool[rng.random_range(0..pool.len())])
839 .collect();
840 let rehash_calls = Cell::new(0);
841 let rehash = |handle: usize| {
842 rehash_calls.set(rehash_calls.get() + 1);
843 hash_of[handle]
844 };
845
846 let mut model: BTreeMap<u64, Vec<usize>> = BTreeMap::new();
847 let mut table = Table::new();
848 let (mut removed, mut false_candidates, mut max_len) = (0, 0, 0);
849 for op in 0..OPERATIONS {
850 let inserts = if (op / 5_000) % 2 == 0 { 6 } else { 3 };
852 let roll = rng.random_range(0..10);
853 let (slots, calls) = (table.slots.len(), rehash_calls.get());
854 let hash = if roll < inserts {
855 let handle = rng.random_range(0..HANDLES);
856 table.insert(hash_of[handle], handle, rehash);
857 model.entry(hash_of[handle]).or_default().push(handle);
858 hash_of[handle]
859 } else if roll < 9 {
860 let handle = rng.random_range(0..HANDLES);
861 let hash = hash_of[handle];
862 let handles = model.get_mut(&hash);
863 let position = handles
864 .as_ref()
865 .and_then(|handles| handles.iter().position(|&h| h == handle));
866 assert_eq!(table.remove(hash, handle), position.is_some());
867 if let (Some(handles), Some(position)) = (handles, position) {
868 handles.swap_remove(position);
869 if handles.is_empty() {
870 model.remove(&hash);
871 }
872 removed += 1;
873 }
874 hash
875 } else if rng.random_bool(0.5) {
876 pool[rng.random_range(0..pool.len())]
877 } else {
878 rng.next_u64()
879 };
880 if table.slots.len() == slots {
881 assert_eq!(rehash_calls.get(), calls);
882 }
883 assert_eq!(table.len(), model.values().map(Vec::len).sum::<usize>());
884 max_len = max_len.max(table.len());
885
886 let mask = table.slots.len().wrapping_sub(1);
888 let expected = sorted(
889 model
890 .iter()
891 .filter(|&(&other, _)| {
892 other >> 56 == hash >> 56 && (other ^ hash) & mask as u64 == 0
893 })
894 .flat_map(|(_, handles)| handles.iter().copied()),
895 );
896 let actual = sorted(table.get(hash));
897 assert_eq!(actual, expected);
898 false_candidates += actual.iter().filter(|&&h| hash_of[h] != hash).count();
899
900 if op % 1_000 == 0 {
901 table.check_invariants(|handle| hash_of[handle]);
902 }
903 }
904 table.check_invariants(|handle| hash_of[handle]);
905 assert!(removed > 10_000 && false_candidates > 1_000 && max_len > 2_000);
906 }
907
908 #[test]
909 fn test_with_capacity_never_rehashes() {
910 let table = Table::<u64>::with_capacity(0);
911 assert_eq!(table.slots.capacity(), 0);
912
913 let mut rng = test_rng();
914 for entries in [1, 12, 13, 100, 384, 385, 1_000] {
915 let mut table = Table::with_capacity(entries);
916 let slots = table.slots.len();
917 assert!(table.capacity() >= entries);
918 assert!(slots == MIN_SLOTS || slots / 2 * 3 / 4 < entries);
919 for handle in 0..entries {
920 table.insert(rng.next_u64(), handle, |_| {
921 unreachable!("rehash without growth")
922 });
923 }
924 assert_eq!(table.slots.len(), slots);
925 assert_eq!(table.len(), entries);
926 }
927 }
928
929 #[test]
930 fn test_reserve_grows_once_and_keeps_entries() {
931 let mut rng = test_rng();
932 let hashes: Vec<u64> = (0..1_000).map(|_| rng.next_u64()).collect();
933 let rehash = |handle: usize| hashes[handle];
934 let calls = Cell::new(0);
935 let counted = |handle: usize| {
936 calls.set(calls.get() + 1);
937 hashes[handle]
938 };
939
940 let mut table = Table::new();
942 table.reserve(0, rehash);
943 assert_eq!(table.slots.capacity(), 0);
944
945 for (handle, &hash) in hashes.iter().enumerate().take(600) {
947 table.insert(hash, handle, rehash);
948 }
949 let slots = table.slots.len();
950 table.reserve(1, counted);
951 assert_eq!(table.slots.len(), slots);
952 assert_eq!(calls.get(), 0);
953
954 table.reserve(hashes.len() - 600, counted);
958 assert!(table.capacity() >= hashes.len());
959 assert_eq!(calls.get(), 600);
960 let slots = table.slots.len();
961 for (handle, &hash) in hashes.iter().enumerate().skip(600) {
962 table.insert(hash, handle, |_| {
963 unreachable!("reserved inserts must not rehash")
964 });
965 }
966 assert_eq!(table.slots.len(), slots);
967 assert_eq!(table.len(), hashes.len());
968 table.check_invariants(rehash);
969 for (handle, &hash) in hashes.iter().enumerate() {
970 assert!(table.get(hash).any(|candidate| candidate == handle));
971 }
972 }
973
974 #[test]
975 fn test_partition_reads_neither_home_nor_fingerprint_bits() {
976 assert_eq!(partition(0xAB << (FINGERPRINT_SHIFT - 8), 256), 0xAB);
978
979 let mut rng = test_rng();
982 for _ in 0..1_000 {
983 let hash: u64 = rng.random();
984 for partitions in [1, 2, 256, 1 << 16] {
985 let group = partition(hash, partitions);
986 assert!(
987 group < partitions,
988 "group must be below the partition count"
989 );
990 assert_eq!(
991 partition(hash ^ (0xFF << FINGERPRINT_SHIFT), partitions),
992 group
993 );
994 assert_eq!(partition(hash ^ ((1 << 40) - 1), partitions), group);
995 }
996 }
997 }
998
999 #[test]
1000 #[should_panic(expected = "partitions must be a power of two")]
1001 fn test_partition_rejects_a_count_that_is_not_a_power_of_two() {
1002 partition(0, 3);
1003 }
1004}