use std::num::NonZeroU64; pub const GROUP_SIZE: usize = 8; type GroupWord = u64; type NonZeroGroupWord = NonZeroU64; pub struct GroupQuery { eq_mask: GroupWord, empty_mask: GroupWord, } #[inline] fn repeat(byte: u8) -> GroupWord { GroupWord::from_ne_bytes([byte; GROUP_SIZE]) } impl GroupQuery { #[inline] pub fn from(group: &[u8; GROUP_SIZE], h2: u8) -> GroupQuery { // Adapted from this gem: // https://github.com/rust-lang/hashbrown/blob/bbb5d3bb1c23569c15e54c670bc0c3669ae3e7dc/src/raw/generic.rs#L93-L109 // which in turn is based on // http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord // // Note the mask generated by the code below can contain false // positives. But we don't care because it's rare and we need // to compare keys anyway. In other words, a false positive here // has pretty much the same effect as a hash collision, something // that we need to deal with in any case anyway. let group = GroupWord::from_le_bytes(*group); let cmp = group ^ repeat(h2); let high_bit_greater_than_128 = (!cmp) & repeat(0x80); let high_bit_greater_than_128_or_zero = cmp.wrapping_sub(repeat(0x01)); let eq_mask = high_bit_greater_than_128_or_zero & high_bit_greater_than_128; let empty_mask = group & repeat(0x80); GroupQuery { eq_mask, empty_mask, } } #[inline] pub fn any_empty(&self) -> bool { self.empty_mask != 0 } #[inline] pub fn first_empty(&self) -> Option { Some((NonZeroGroupWord::new(self.empty_mask)?.trailing_zeros() / 8) as usize) } } impl Iterator for GroupQuery { type Item = usize; #[inline] fn next(&mut self) -> Option { let index = NonZeroGroupWord::new(self.eq_mask)?.trailing_zeros() / 8; // Clear the lowest bit // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan self.eq_mask &= self.eq_mask - 1; Some(index as usize) } }