use crate::RsVec;
use std::mem::size_of;
const VECTOR_SIZE: usize = 16;
impl RsVec {
#[must_use]
pub fn bit_set_iter0(&self) -> BitSetIter<'_, true> {
BitSetIter::new(self)
}
#[must_use]
pub fn bit_set_iter1(&self) -> BitSetIter<'_, false> {
BitSetIter::new(self)
}
}
pub struct BitSetIter<'a, const ZERO: bool> {
vec: &'a RsVec,
base: usize,
offsets: [u32; VECTOR_SIZE],
content_len: u8,
cursor: u8,
}
impl<'a, const ZERO: bool> BitSetIter<'a, ZERO> {
pub(super) fn new(vec: &'a RsVec) -> Self {
let mut iter = Self {
vec,
base: 0,
offsets: [0; VECTOR_SIZE],
content_len: 0,
cursor: 0,
};
if vec.len() > VECTOR_SIZE {
iter.load_chunk(vec.get_bits_unchecked(0, VECTOR_SIZE) as u16);
}
iter
}
fn load_chunk(&mut self, data: u16) {
use std::arch::x86_64::{__mmask16, _mm512_mask_compressstoreu_epi32, _mm512_setr_epi32};
unsafe {
let offsets = _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
assert!(VECTOR_SIZE <= size_of::<u16>() * 8, "change data types");
let mut mask = __mmask16::from(data);
if ZERO {
mask = !mask;
}
_mm512_mask_compressstoreu_epi32(self.offsets.as_mut_ptr() as *mut _, mask, offsets);
self.content_len = mask.count_ones() as u8;
self.cursor = 0;
}
}
fn load_next_chunk(&mut self) -> Option<()> {
while self.cursor == self.content_len {
if self.base + VECTOR_SIZE >= self.vec.len() {
return None;
}
self.base += VECTOR_SIZE;
let data = self.vec.get_bits_unchecked(self.base, VECTOR_SIZE) as u16;
self.load_chunk(data);
}
Some(())
}
}
impl<const ZERO: bool> Iterator for BitSetIter<'_, ZERO> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.base >= self.vec.len() {
return None;
}
if self.cursor == self.content_len {
if self.load_next_chunk().is_none() {
if ZERO {
while self.base < self.vec.len() && self.vec.get_unchecked(self.base) != 0 {
self.base += 1;
}
} else {
while self.base < self.vec.len() && self.vec.get_unchecked(self.base) != 1 {
self.base += 1;
}
}
return if self.base < self.vec.len() {
self.base += 1;
Some(self.base - 1)
} else {
None
};
}
}
let offset = self.offsets[self.cursor as usize];
self.cursor += 1;
Some(self.base + offset as usize)
}
}