pub mod dispatch;
#[cfg(feature = "simd")]
pub mod simd;
#[cfg(not(feature = "simd"))]
pub mod std;
use crate::{Bitmask, BitmaskVT};
use core::mem;
pub type Word = u64;
pub const WORD_BITS: usize = mem::size_of::<Word>() * 8;
#[inline(always)]
pub fn words_for(len: usize) -> usize {
(len + WORD_BITS - 1) / WORD_BITS
}
#[inline(always)]
pub unsafe fn mask_bits_as_words(bits: &[u8]) -> *const Word {
bits.as_ptr() as *const Word
}
#[inline(always)]
pub unsafe fn mask_bits_as_words_mut(bits: &mut [u8]) -> *mut Word {
bits.as_mut_ptr() as *mut Word
}
#[inline(always)]
pub fn new_mask(len: usize) -> Bitmask {
Bitmask::new_set_all(len, false)
}
#[inline(always)]
pub fn bitmask_window_bytes(mask: &Bitmask, offset: usize, len: usize) -> &[u8] {
let start = offset / 8;
let end = (offset + len + 7) / 8;
&mask.bits[start..end]
}
#[inline(always)]
pub fn bitmask_window_bytes_mut(mask: &mut Bitmask, offset: usize, len: usize) -> &mut [u8] {
let start = offset / 8;
let end = (offset + len + 7) / 8;
&mut mask.bits[start..end]
}
#[inline(always)]
pub fn clear_trailing_bits(bm: &mut Bitmask) {
let len = bm.len();
if len == 0 {
return;
}
let used = len & 7;
if used != 0 {
let last = bm.bits.last_mut().unwrap();
*last &= (1u8 << used) - 1;
}
}
#[inline(always)]
pub fn popcount_bits(m: BitmaskVT<'_>) -> usize {
#[cfg(feature = "simd")]
{
use crate::kernels::bitmask::simd::popcount_mask_simd;
popcount_mask_simd::<8>(m)
}
#[cfg(not(feature = "simd"))]
{
use crate::kernels::bitmask::std::popcount_mask;
popcount_mask(m)
}
}
#[inline]
pub fn merge_bitmasks_to_new(
lhs: Option<&Bitmask>,
rhs: Option<&Bitmask>,
len: usize,
) -> Option<Bitmask> {
match (lhs, rhs) {
(None, None) => None,
(Some(l), None) | (None, Some(l)) => {
debug_assert!(l.len() >= len, "Bitmask too short in merge");
let mut out = Bitmask::new_set_all(len, true);
for i in 0..len {
out.set(i, l.get(i));
}
Some(out)
}
(Some(l), Some(r)) => {
debug_assert!(l.len() >= len, "Left Bitmask too short in merge");
debug_assert!(r.len() >= len, "Right Bitmask too short in merge");
let mut out = Bitmask::new_set_all(len, true);
for i in 0..len {
out.set(i, l.get(i) && r.get(i));
}
Some(out)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Bitmask;
#[test]
fn test_words_for() {
assert_eq!(words_for(0), 0);
assert_eq!(words_for(1), 1);
assert_eq!(words_for(63), 1);
assert_eq!(words_for(64), 1);
assert_eq!(words_for(65), 2);
assert_eq!(words_for(128), 2);
assert_eq!(words_for(129), 3);
}
#[test]
fn test_mask_bits_as_words_and_mut() {
let mut mask = Bitmask::new_set_all(128, false);
mask.set(0, true);
mask.set(63, true);
mask.set(64, true);
mask.set(127, true);
let bits = &mask.bits;
unsafe {
let words = mask_bits_as_words(bits);
assert_eq!(*words, 1u64 | (1u64 << 63));
assert_eq!(*words.add(1), 1u64 | (1u64 << 63));
}
let mut mask = Bitmask::new_set_all(128, false);
let bits = &mut mask.bits;
unsafe {
let words_mut = mask_bits_as_words_mut(bits);
*words_mut = 0xDEADBEEFDEADBEEF;
*words_mut.add(1) = 0xCAFEBABECAFEBABE;
}
assert_eq!(mask.bits[0], 0xEF);
assert_eq!(mask.bits[7], 0xDE);
assert_eq!(mask.bits[8], 0xBE);
assert_eq!(mask.bits[15], 0xCA);
}
#[test]
fn test_bitmask_window_bytes() {
let mut mask = Bitmask::new_set_all(24, false);
mask.set(0, true);
mask.set(7, true);
mask.set(8, true);
mask.set(15, true);
mask.set(16, true);
mask.set(23, true);
let bytes = bitmask_window_bytes(&mask, 8, 16);
assert_eq!(bytes.len(), 2); assert_eq!(bytes[0], 0b10000001); assert_eq!(bytes[1], 0b10000001); }
#[test]
fn test_bitmask_window_bytes_mut() {
let mut mask = Bitmask::new_set_all(16, false);
{
let window = bitmask_window_bytes_mut(&mut mask, 0, 16);
window[0] = 0xAA;
window[1] = 0x55;
}
assert_eq!(mask.bits[0], 0xAA);
assert_eq!(mask.bits[1], 0x55);
}
#[test]
fn test_clear_trailing_bits() {
let mut mask = Bitmask::new_set_all(10, true);
clear_trailing_bits(&mut mask);
assert_eq!(mask.bits[1], 0x03);
for i in 0..10 {
assert!(mask.get(i));
}
for i in 10..16 {
assert!(!mask.get(i));
}
}
#[test]
fn binop_into_matches_allocating() {
use crate::kernels::bitmask::dispatch::{
and_masks, and_masks_into, or_masks, or_masks_into, xor_masks, xor_masks_into,
};
for &len in &[1usize, 7, 8, 63, 64, 65, 127, 128, 200, 1000] {
let mut a = Bitmask::new_set_all(len, false);
let mut b = Bitmask::new_set_all(len, false);
for i in 0..len {
if i % 3 == 0 {
a.set(i, true);
}
if i % 5 == 0 {
b.set(i, true);
}
}
let cases: [(Bitmask, fn(&mut Bitmask, usize, BitmaskVT, BitmaskVT)); 3] = [
(and_masks((&a, 0, len), (&b, 0, len)), and_masks_into),
(or_masks((&a, 0, len), (&b, 0, len)), or_masks_into),
(xor_masks((&a, 0, len), (&b, 0, len)), xor_masks_into),
];
for (reference, into_fn) in cases {
let mut out = Bitmask::new_set_all(len, false);
into_fn(&mut out, 0, (&a, 0, len), (&b, 0, len));
for i in 0..len {
assert_eq!(out.get(i), reference.get(i), "len {len} bit {i}");
}
}
}
}
#[test]
fn unop_into_matches_allocating() {
use crate::kernels::bitmask::dispatch::{not_mask, not_mask_into};
for &len in &[1usize, 7, 64, 65, 127, 200] {
let mut src = Bitmask::new_set_all(len, false);
for i in 0..len {
if i % 2 == 0 {
src.set(i, true);
}
}
let reference = not_mask((&src, 0, len));
let mut out = Bitmask::new_set_all(len, false);
not_mask_into(&mut out, 0, (&src, 0, len));
for i in 0..len {
assert_eq!(out.get(i), reference.get(i), "len {len} bit {i}");
}
}
}
#[test]
fn binop_into_writes_only_its_window() {
use crate::kernels::bitmask::dispatch::and_masks_into;
let win_len = 70usize;
let out_off = 128usize; let win_end = out_off + win_len;
let mut a = Bitmask::new_set_all(win_len, false);
let mut b = Bitmask::new_set_all(win_len, false);
for i in 0..win_len {
if i % 3 == 0 {
a.set(i, true);
}
if i % 4 == 0 {
b.set(i, true);
}
}
let mut out = Bitmask::new_set_all(256, true);
and_masks_into(&mut out, out_off, (&a, 0, win_len), (&b, 0, win_len));
for i in 0..out_off {
assert!(out.get(i), "bit {i} before the window was disturbed");
}
for i in 0..win_len {
let expect = a.get(i) & b.get(i);
assert_eq!(out.get(out_off + i), expect, "window bit {i}");
}
for i in win_end..256 {
assert!(out.get(i), "bit {i} past the window was disturbed");
}
}
}