opthash 0.1.2

Rust implementations of Elastic Hashing and Funnel Hashing
Documentation
use super::layout::GROUP_SIZE;
use super::simd::{eq_mask_16, eq_mask_32, free_mask_16, free_mask_32, preferred_group_width};

pub(crate) const CTRL_EMPTY: u8 = 0;
pub(crate) const CTRL_TOMBSTONE: u8 = 0x80;

pub(crate) trait ControlByte {
    fn is_occupied(&self) -> bool;
    fn is_free(&self) -> bool;
}

impl ControlByte for u8 {
    #[inline]
    fn is_occupied(&self) -> bool {
        *self != CTRL_EMPTY && *self != CTRL_TOMBSTONE
    }

    #[inline]
    fn is_free(&self) -> bool {
        *self == CTRL_EMPTY || *self == CTRL_TOMBSTONE
    }
}

#[inline]
pub(crate) fn control_fingerprint(hash: u64) -> u8 {
    let low = u8::try_from(hash & 0x7F).expect("7-bit fingerprint fits in u8");
    low.max(1)
}

#[inline]
pub(crate) fn fingerprint_bit(fingerprint: u8) -> u128 {
    1u128 << u32::from(fingerprint.saturating_sub(1))
}

#[inline]
pub(crate) fn valid_group_mask(len: usize) -> u16 {
    if len >= GROUP_SIZE {
        u16::MAX
    } else if len == 0 {
        0
    } else {
        (1u16 << len) - 1
    }
}

pub(crate) trait Controls {
    fn find_first_free(&self) -> Option<usize>;
    fn find_first(&self, target: u8) -> Option<usize>;
    fn find_next(&self, target: u8, start: usize) -> Option<usize>;
    fn match_free_group(&self) -> u32;
    fn match_fingerprint_group(&self, target: u8) -> u32;
}

impl Controls for [u8] {
    #[inline]
    fn find_first_free(&self) -> Option<usize> {
        if self.len() < GROUP_SIZE {
            return self.iter().position(ControlByte::is_free);
        }

        let mut index = 0usize;
        let wide = preferred_group_width();
        while wide > GROUP_SIZE && index + wide <= self.len() {
            let mask = self[index..index + wide].match_free_group();
            if mask != 0 {
                return Some(index + mask.trailing_zeros() as usize);
            }
            index += wide;
        }
        while index + GROUP_SIZE <= self.len() {
            let mask = self[index..index + GROUP_SIZE].match_free_group();
            if mask != 0 {
                return Some(index + mask.trailing_zeros() as usize);
            }
            index += GROUP_SIZE;
        }

        for (offset, control) in self[index..].iter().enumerate() {
            if control.is_free() {
                return Some(index + offset);
            }
        }

        None
    }

    #[inline]
    fn find_first(&self, target: u8) -> Option<usize> {
        self.find_next(target, 0)
    }

    #[inline]
    fn find_next(&self, target: u8, start: usize) -> Option<usize> {
        if start >= self.len() {
            return None;
        }

        if self.len() - start < GROUP_SIZE {
            for (offset, &control) in self[start..].iter().enumerate() {
                if control == target {
                    return Some(start + offset);
                }
            }
            return None;
        }

        let mut index = start;
        let wide = preferred_group_width();
        while wide > GROUP_SIZE && index + wide <= self.len() {
            let mask = self[index..index + wide].match_fingerprint_group(target);
            if mask != 0 {
                return Some(index + mask.trailing_zeros() as usize);
            }
            index += wide;
        }
        while index + GROUP_SIZE <= self.len() {
            let mask = self[index..index + GROUP_SIZE].match_fingerprint_group(target);
            if mask != 0 {
                return Some(index + mask.trailing_zeros() as usize);
            }
            index += GROUP_SIZE;
        }

        for (offset, &control) in self[index..].iter().enumerate() {
            if control == target {
                return Some(index + offset);
            }
        }

        None
    }

    #[inline]
    fn match_free_group(&self) -> u32 {
        match self.len() {
            GROUP_SIZE => u32::from(free_mask_16(self.as_ptr())),
            32 => free_mask_32(self.as_ptr()),
            _ => panic!("group matching requires 16 or 32 byte chunks"),
        }
    }

    #[inline]
    fn match_fingerprint_group(&self, target: u8) -> u32 {
        match self.len() {
            GROUP_SIZE => u32::from(eq_mask_16(self.as_ptr(), target)),
            32 => eq_mask_32(self.as_ptr(), target),
            _ => panic!("group matching requires 16 or 32 byte chunks"),
        }
    }
}