#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
#[rkyv(derive(Clone, Copy, Debug, PartialEq, Eq, Hash))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct BitMask<const N: usize>(pub u64);
impl<const N: usize> BitMask<N> {
pub const ALL_ACTIVE: Self = {
assert!(N <= 64, "BitMask<N>: N must be <= 64");
let bits = if N >= 64 {
u64::MAX
} else {
(1u64 << N).wrapping_sub(1)
};
Self(bits)
};
pub const NONE_ACTIVE: Self = Self(0);
#[inline(always)]
pub const fn leading_k(k: usize) -> Self {
let k = if k > N { N } else { k };
let bits = if k >= 64 {
u64::MAX
} else {
(1u64 << k).wrapping_sub(1)
};
Self(bits)
}
#[inline(always)]
pub fn from_bools(bits: &[bool]) -> Self {
debug_assert_eq!(
bits.len(),
N,
"BitMask::from_bools: slice length must equal N"
);
let mut m = 0u64;
for (i, &b) in bits.iter().enumerate().take(N) {
m |= (b as u64) << i;
}
Self(m)
}
#[inline(always)]
pub fn popcount(self) -> u32 {
self.0.count_ones()
}
#[inline(always)]
pub fn is_all_active(self) -> bool {
self.0 == Self::ALL_ACTIVE.0
}
#[inline(always)]
pub fn is_none_active(self) -> bool {
self.0 == 0
}
#[inline(always)]
pub fn is_lane_active(self, i: usize) -> bool {
debug_assert!(i < N, "BitMask::is_lane_active: lane index out of range");
(self.0 >> i) & 1 == 1
}
#[inline(always)]
pub fn and(self, other: Self) -> Self {
Self(self.0 & other.0)
}
#[inline(always)]
pub fn or(self, other: Self) -> Self {
Self(self.0 | other.0)
}
pub fn to_bools(self) -> [bool; N]
where
[bool; N]: Sized,
{
core::array::from_fn(|i| (self.0 >> i) & 1 == 1)
}
}
impl<const N: usize> BitMask<N> {
#[inline(always)]
pub unsafe fn to_native_mask<T, Arch>(self) -> Arch::Mask
where
T: crate::scalar::Scalar,
Arch: crate::kernel::SimdKernel<T>,
{
Arch::mask_from_bitmask(self.0)
}
}
#[derive(Clone, Copy, Debug)]
pub struct BitMaskIter<const N: usize> {
remaining: u64,
}
impl<const N: usize> Iterator for BitMaskIter<N> {
type Item = usize;
#[inline(always)]
fn next(&mut self) -> Option<usize> {
if self.remaining == 0 {
return None;
}
let idx = self.remaining.trailing_zeros() as usize;
if idx >= N {
return None;
}
self.remaining &= self.remaining.wrapping_sub(1);
Some(idx)
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.remaining.count_ones() as usize;
(n, Some(n))
}
}
impl<const N: usize> ExactSizeIterator for BitMaskIter<N> {}
impl<const N: usize> DoubleEndedIterator for BitMaskIter<N> {
#[inline(always)]
fn next_back(&mut self) -> Option<usize> {
if self.remaining == 0 {
return None;
}
let idx = 63 - self.remaining.leading_zeros() as usize;
if idx >= N {
return None;
}
self.remaining &= !(1u64 << idx);
Some(idx)
}
}
impl<const N: usize> IntoIterator for BitMask<N> {
type Item = usize;
type IntoIter = BitMaskIter<N>;
#[inline(always)]
fn into_iter(self) -> BitMaskIter<N> {
BitMaskIter { remaining: self.0 }
}
}
impl<const N: usize> BitMask<N> {
#[inline(always)]
pub fn active_lanes(self) -> BitMaskIter<N> {
self.into_iter()
}
}
impl<const N: usize> Default for BitMask<N> {
#[inline(always)]
fn default() -> Self {
Self::NONE_ACTIVE
}
}
impl<const N: usize> core::ops::BitAnd for BitMask<N> {
type Output = Self;
#[inline(always)]
fn bitand(self, rhs: Self) -> Self {
self.and(rhs)
}
}
impl<const N: usize> core::ops::BitOr for BitMask<N> {
type Output = Self;
#[inline(always)]
fn bitor(self, rhs: Self) -> Self {
self.or(rhs)
}
}
impl<const N: usize> core::ops::Not for BitMask<N> {
type Output = Self;
#[inline(always)]
fn not(self) -> Self {
Self(!self.0 & Self::ALL_ACTIVE.0)
}
}
#[cfg(test)]
mod rkyv_tests {
use super::*;
#[test]
#[cfg_attr(miri, ignore)]
fn test_bitmask_rkyv() {
let mask = BitMask::<8>::from_bools(&[true, false, true, true, false, false, true, false]);
let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&mask).unwrap();
let archived = rkyv::access::<rkyv::Archived<BitMask<8>>, rkyv::rancor::Error>(&bytes)
.expect("validated access");
let deserialized: BitMask<8> =
rkyv::deserialize::<_, rkyv::rancor::Error>(archived).unwrap();
assert_eq!(deserialized, mask);
assert_eq!(deserialized.0, mask.0);
}
}