odht 0.3.1

A Rust crate for hash tables that can be mapped from disk into memory without the need for up-front decoding.
Documentation
cfg_if::cfg_if! {
    if #[cfg(all(
        target_feature = "sse2",
        any(target_arch = "x86", target_arch = "x86_64"),
        not(miri),
        not(feature = "no_simd"),
    ))] {
        mod sse2;
        use sse2 as imp;
    } else {
        mod no_simd;
        use no_simd as imp;
    }
}

pub use imp::GroupQuery;
pub use imp::GROUP_SIZE;

// While GROUP_SIZE is target-dependent for performance reasons,
// we always do probing as if it was the same as REFERENCE_GROUP_SIZE.
// This way the same slot indices will be assigned to the same
// entries no matter the underlying target. This allows the
// binary format of the table to be portable.
pub const REFERENCE_GROUP_SIZE: usize = 16;

#[cfg(test)]
mod tests {
    use super::*;

    const EMPTY_GROUP: [u8; GROUP_SIZE] = [255; GROUP_SIZE];

    fn full_group() -> [u8; GROUP_SIZE] {
        let mut group = [0; GROUP_SIZE];
        for i in 0..group.len() {
            group[i] = i as u8;
        }
        group
    }

    #[test]
    fn full() {
        let mut q = GroupQuery::from(&full_group(), 42);

        assert_eq!(Iterator::count(&mut q), 0);
        assert!(!q.any_empty());
        assert_eq!(q.first_empty(), None);
    }

    #[test]
    fn all_empty() {
        let mut q = GroupQuery::from(&EMPTY_GROUP, 31);

        assert_eq!(Iterator::count(&mut q), 0);
        assert!(q.any_empty());
        assert_eq!(q.first_empty(), Some(0));
    }

    #[test]
    fn partially_filled() {
        for filled_up_to_index in 0..=(GROUP_SIZE - 2) {
            let mut group = EMPTY_GROUP;

            for i in 0..=filled_up_to_index {
                group[i] = 42;
            }

            let mut q = GroupQuery::from(&group, 77);

            assert_eq!(Iterator::count(&mut q), 0);
            assert!(q.any_empty());
            assert_eq!(q.first_empty(), Some(filled_up_to_index + 1));
        }
    }

    #[test]
    fn match_iter() {
        let expected: Vec<_> = (0..GROUP_SIZE).filter(|x| x % 3 == 0).collect();

        let mut group = full_group();

        for i in &expected {
            group[*i] = 103;
        }

        let mut q = GroupQuery::from(&group, 103);

        let matches: Vec<usize> = (&mut q).collect();

        assert_eq!(matches, expected);
        assert!(!q.any_empty());
        assert_eq!(q.first_empty(), None);
    }

    #[test]
    fn match_iter_with_empty() {
        let expected: Vec<_> = (0..GROUP_SIZE).filter(|x| x % 3 == 2).collect();

        let mut group = full_group();

        for i in &expected {
            group[*i] = 99;
        }

        // Clear a few slots
        group[3] = 255;
        group[4] = 255;
        group[GROUP_SIZE - 1] = 255;

        let mut q = GroupQuery::from(&group, 99);

        let matches: Vec<usize> = (&mut q).collect();

        assert_eq!(matches, expected);
        assert!(q.any_empty());
        assert_eq!(q.first_empty(), Some(3));
    }
}