use bitvec::prelude::{BitBox, BitVec, Msb0, bitvec};
use get_size2::GetSize;
#[derive(Clone, Debug, Eq, Hash, PartialEq, GetSize)]
pub struct RankBitBox {
#[get_size(size_fn = bit_box_size)]
bits: RankBitBoxStorage,
chunk_ranks: Box<[u32]>,
}
pub type RankBitBoxStorage = BitBox<Chunk, Msb0>;
pub type RankBitBoxVec = BitVec<Chunk, Msb0>;
fn bit_box_size(bits: &RankBitBoxStorage) -> usize {
std::mem::size_of_val(bits.as_raw_slice())
}
#[cfg(target_pointer_width = "64")]
type Chunk = u64;
#[cfg(not(target_pointer_width = "64"))]
type Chunk = u32;
const CHUNK_SIZE: usize = Chunk::BITS as usize;
impl RankBitBox {
pub fn bits_with_capacity(cap: usize) -> RankBitBoxVec {
bitvec![Chunk, Msb0; 0; cap]
}
pub fn from_bits(bits: RankBitBoxVec) -> Self {
let chunk_ranks = bits
.as_raw_slice()
.iter()
.scan(0u32, |rank, chunk| {
let result = *rank;
*rank += chunk.count_ones();
Some(result)
})
.collect();
let bits = bits.into();
Self { bits, chunk_ranks }
}
#[inline]
pub fn len(&self) -> usize {
self.bits.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.bits.is_empty()
}
#[inline]
pub fn get_bit(&self, index: usize) -> Option<bool> {
self.bits.get(index).map(|bit| *bit)
}
#[inline]
pub fn iter_ones(&self) -> impl DoubleEndedIterator<Item = usize> + '_ {
self.bits.iter_ones()
}
#[inline]
pub fn rank(&self, index: usize) -> u32 {
let chunk_index = index / CHUNK_SIZE;
let index_within_chunk = index % CHUNK_SIZE;
let chunk_rank = self.chunk_ranks[chunk_index];
if index_within_chunk == 0 {
return chunk_rank;
}
let chunk = self.bits.as_raw_slice()[chunk_index];
let chunk_mask = Chunk::MAX << (CHUNK_SIZE - index_within_chunk);
let rank_within_chunk = (chunk & chunk_mask).count_ones();
chunk_rank + rank_within_chunk
}
}
#[cfg(test)]
mod tests {
use std::mem::size_of;
use get_size2::GetSize;
use super::{CHUNK_SIZE, Chunk, RankBitBox};
#[test]
fn heap_size_includes_bits_and_chunk_ranks() {
let bit_count = CHUNK_SIZE + 1;
let bits = RankBitBox::from_bits(RankBitBox::bits_with_capacity(bit_count));
let chunk_count = bit_count.div_ceil(CHUNK_SIZE);
assert_eq!(
bits.get_heap_size(),
chunk_count * (size_of::<Chunk>() + size_of::<u32>())
);
}
}