#[cfg(not(feature = "std"))]
use alloc::{collections::VecDeque, vec::Vec};
use bytes::{Buf, BufMut};
use commonware_codec::{util::at_least, EncodeSize, Error as CodecError, Read, ReadExt, Write};
use core::{
fmt::{self, Formatter, Write as _},
iter,
ops::{BitAnd, BitOr, BitXor, Index, Range},
};
#[cfg(feature = "std")]
use std::collections::VecDeque;
mod prunable;
pub use prunable::Prunable;
pub mod historical;
pub const DEFAULT_CHUNK_SIZE: usize = 8;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct BitMap<const N: usize = DEFAULT_CHUNK_SIZE> {
chunks: VecDeque<[u8; N]>,
len: u64,
}
impl<const N: usize> BitMap<N> {
const _CHUNK_SIZE_NON_ZERO_ASSERT: () = assert!(N > 0, "chunk size must be > 0");
pub const CHUNK_SIZE_BITS: u64 = (N * 8) as u64;
const EMPTY_CHUNK: [u8; N] = [0u8; N];
const FULL_CHUNK: [u8; N] = [u8::MAX; N];
pub const fn new() -> Self {
#[allow(path_statements)]
Self::_CHUNK_SIZE_NON_ZERO_ASSERT;
Self {
chunks: VecDeque::new(),
len: 0,
}
}
pub fn with_capacity(size: u64) -> Self {
#[allow(path_statements)]
Self::_CHUNK_SIZE_NON_ZERO_ASSERT;
Self {
chunks: VecDeque::with_capacity(size.div_ceil(Self::CHUNK_SIZE_BITS) as usize),
len: 0,
}
}
pub fn zeroes(size: u64) -> Self {
#[allow(path_statements)]
Self::_CHUNK_SIZE_NON_ZERO_ASSERT;
let num_chunks = size.div_ceil(Self::CHUNK_SIZE_BITS) as usize;
let mut chunks = VecDeque::with_capacity(num_chunks);
for _ in 0..num_chunks {
chunks.push_back(Self::EMPTY_CHUNK);
}
Self { chunks, len: size }
}
pub fn ones(size: u64) -> Self {
#[allow(path_statements)]
Self::_CHUNK_SIZE_NON_ZERO_ASSERT;
let num_chunks = size.div_ceil(Self::CHUNK_SIZE_BITS) as usize;
let mut chunks = VecDeque::with_capacity(num_chunks);
for _ in 0..num_chunks {
chunks.push_back(Self::FULL_CHUNK);
}
let mut result = Self { chunks, len: size };
result.clear_trailing_bits();
result
}
#[inline]
pub const fn len(&self) -> u64 {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub const fn is_chunk_aligned(&self) -> bool {
self.len.is_multiple_of(Self::CHUNK_SIZE_BITS)
}
fn chunks_len(&self) -> usize {
self.chunks.len()
}
#[inline]
pub fn get(&self, bit: u64) -> bool {
let chunk = self.get_chunk_containing(bit);
Self::get_bit_from_chunk(chunk, bit)
}
#[inline]
fn get_chunk_containing(&self, bit: u64) -> &[u8; N] {
assert!(
bit < self.len(),
"bit {} out of bounds (len: {})",
bit,
self.len()
);
&self.chunks[Self::to_chunk_index(bit)]
}
#[inline]
pub(super) fn get_chunk(&self, chunk: usize) -> &[u8; N] {
assert!(
chunk < self.chunks.len(),
"chunk {} out of bounds (chunks: {})",
chunk,
self.chunks.len()
);
&self.chunks[chunk]
}
#[inline]
pub const fn get_bit_from_chunk(chunk: &[u8; N], bit: u64) -> bool {
let byte = Self::chunk_byte_offset(bit);
let byte = chunk[byte];
let mask = Self::chunk_byte_bitmask(bit);
(byte & mask) != 0
}
#[inline]
fn last_chunk(&self) -> (&[u8; N], u64) {
let rem = self.len % Self::CHUNK_SIZE_BITS;
let bits_in_last_chunk = if rem == 0 { Self::CHUNK_SIZE_BITS } else { rem };
(self.chunks.back().unwrap(), bits_in_last_chunk)
}
pub fn push(&mut self, bit: bool) {
if self.is_chunk_aligned() {
self.chunks.push_back(Self::EMPTY_CHUNK);
}
if bit {
let last_chunk = self.chunks.back_mut().unwrap();
let chunk_byte = Self::chunk_byte_offset(self.len);
last_chunk[chunk_byte] |= Self::chunk_byte_bitmask(self.len);
}
self.len += 1;
}
pub fn pop(&mut self) -> bool {
assert!(!self.is_empty(), "Cannot pop from empty bitmap");
let last_bit_pos = self.len - 1;
let bit = Self::get_bit_from_chunk(self.chunks.back().unwrap(), last_bit_pos);
self.len -= 1;
if bit {
let chunk_byte = Self::chunk_byte_offset(last_bit_pos);
let mask = Self::chunk_byte_bitmask(last_bit_pos);
self.chunks.back_mut().unwrap()[chunk_byte] &= !mask;
}
if self.is_chunk_aligned() {
self.chunks.pop_back();
}
bit
}
pub(super) fn pop_chunk(&mut self) -> [u8; N] {
assert!(
self.len() >= Self::CHUNK_SIZE_BITS,
"cannot pop chunk: bitmap has fewer than CHUNK_SIZE_BITS bits"
);
assert!(
self.is_chunk_aligned(),
"cannot pop chunk when not chunk aligned"
);
let chunk = self.chunks.pop_back().expect("chunk must exist");
self.len -= Self::CHUNK_SIZE_BITS;
chunk
}
#[inline]
pub fn flip(&mut self, bit: u64) {
self.assert_bit(bit);
let chunk = Self::to_chunk_index(bit);
let byte = Self::chunk_byte_offset(bit);
let mask = Self::chunk_byte_bitmask(bit);
self.chunks[chunk][byte] ^= mask;
}
pub fn flip_all(&mut self) {
for chunk in &mut self.chunks {
for byte in chunk {
*byte = !*byte;
}
}
self.clear_trailing_bits();
}
pub fn set(&mut self, bit: u64, value: bool) {
assert!(
bit < self.len(),
"bit {} out of bounds (len: {})",
bit,
self.len()
);
let chunk = &mut self.chunks[Self::to_chunk_index(bit)];
let byte = Self::chunk_byte_offset(bit);
let mask = Self::chunk_byte_bitmask(bit);
if value {
chunk[byte] |= mask;
} else {
chunk[byte] &= !mask;
}
}
#[inline]
pub fn set_all(&mut self, bit: bool) {
let value = if bit { u8::MAX } else { 0 };
for chunk in &mut self.chunks {
chunk.fill(value);
}
if bit {
self.clear_trailing_bits();
}
}
fn push_byte(&mut self, byte: u8) {
assert!(
self.len.is_multiple_of(8),
"cannot add byte when not byte aligned"
);
if self.is_chunk_aligned() {
self.chunks.push_back(Self::EMPTY_CHUNK);
}
let chunk_byte = Self::chunk_byte_offset(self.len);
self.chunks.back_mut().unwrap()[chunk_byte] = byte;
self.len += 8;
}
pub fn push_chunk(&mut self, chunk: &[u8; N]) {
assert!(
self.is_chunk_aligned(),
"cannot add chunk when not chunk aligned"
);
self.chunks.push_back(*chunk);
self.len += Self::CHUNK_SIZE_BITS;
}
fn clear_trailing_bits(&mut self) -> bool {
if self.chunks.is_empty() {
return false;
}
let pos_in_chunk = self.len % Self::CHUNK_SIZE_BITS;
if pos_in_chunk == 0 {
return false;
}
let mut flipped_any = false;
let last_chunk = self.chunks.back_mut().unwrap();
let last_byte_index = ((pos_in_chunk - 1) / 8) as usize;
for byte in last_chunk.iter_mut().skip(last_byte_index + 1) {
if *byte != 0 {
flipped_any = true;
*byte = 0;
}
}
let bits_in_last_byte = pos_in_chunk % 8;
if bits_in_last_byte != 0 {
let mask = (1u8 << bits_in_last_byte) - 1;
let old_byte = last_chunk[last_byte_index];
let new_byte = old_byte & mask;
if old_byte != new_byte {
flipped_any = true;
last_chunk[last_byte_index] = new_byte;
}
}
flipped_any
}
fn prune_chunks(&mut self, chunks: usize) {
assert!(
chunks <= self.chunks.len(),
"cannot prune {chunks} chunks, only {} available",
self.chunks.len()
);
self.chunks.drain(..chunks);
let bits_removed = (chunks as u64) * Self::CHUNK_SIZE_BITS;
self.len = self.len.saturating_sub(bits_removed);
}
pub(super) fn prepend_chunk(&mut self, chunk: &[u8; N]) {
self.chunks.push_front(*chunk);
self.len += Self::CHUNK_SIZE_BITS;
}
pub(super) fn set_chunk_by_index(&mut self, chunk_index: usize, chunk_data: &[u8; N]) {
assert!(
chunk_index < self.chunks.len(),
"chunk index {chunk_index} out of bounds (chunks_len: {})",
self.chunks.len()
);
self.chunks[chunk_index].copy_from_slice(chunk_data);
}
#[inline]
pub fn count_ones(&self) -> u64 {
let (front, back) = self.chunks.as_slices();
Self::count_ones_in_chunk_slice(front) + Self::count_ones_in_chunk_slice(back)
}
#[inline]
fn count_ones_in_chunk_slice(chunks: &[[u8; N]]) -> u64 {
let mut total = 0u64;
let mut words = chunks.as_flattened().chunks_exact(8);
for word in &mut words {
total += u64::from_le_bytes(word.try_into().unwrap()).count_ones() as u64;
}
for byte in words.remainder() {
total += byte.count_ones() as u64;
}
total
}
#[inline]
pub fn count_zeros(&self) -> u64 {
self.len() - self.count_ones()
}
#[inline]
pub(super) const fn chunk_byte_bitmask(bit: u64) -> u8 {
1 << (bit % 8)
}
#[inline]
pub(super) const fn chunk_byte_offset(bit: u64) -> usize {
((bit / 8) % N as u64) as usize
}
#[inline]
pub(super) fn to_chunk_index(bit: u64) -> usize {
let chunk = bit / Self::CHUNK_SIZE_BITS;
assert!(
chunk <= usize::MAX as u64,
"chunk overflow: {chunk} exceeds usize::MAX",
);
chunk as usize
}
pub const fn iter(&self) -> Iterator<'_, N> {
Iterator {
bitmap: self,
pos: 0,
}
}
#[inline]
fn binary_op<F: Fn(u8, u8) -> u8>(&mut self, other: &Self, op: F) {
self.assert_eq_len(other);
for (a_chunk, b_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
for (a_byte, b_byte) in a_chunk.iter_mut().zip(b_chunk.iter()) {
*a_byte = op(*a_byte, *b_byte);
}
}
self.clear_trailing_bits();
}
pub fn and(&mut self, other: &Self) {
self.binary_op(other, |a, b| a & b);
}
pub fn or(&mut self, other: &Self) {
self.binary_op(other, |a, b| a | b);
}
pub fn xor(&mut self, other: &Self) {
self.binary_op(other, |a, b| a ^ b);
}
#[inline(always)]
fn assert_bit(&self, bit: u64) {
assert!(
bit < self.len(),
"bit {} out of bounds (len: {})",
bit,
self.len()
);
}
#[inline(always)]
fn assert_eq_len(&self, other: &Self) {
assert_eq!(
self.len(),
other.len(),
"BitMap lengths don't match: {} vs {}",
self.len(),
other.len()
);
}
pub fn is_unset(&self, range: Range<u64>) -> bool {
assert!(
range.end <= self.len(),
"range end {} out of bounds (len: {})",
range.end,
self.len()
);
if range.start >= range.end {
return true;
}
let start = range.start;
let end = range.end;
let end = end - 1;
let first_chunk = Self::to_chunk_index(start);
let last_chunk = Self::to_chunk_index(end);
let zero = [0u8; N];
for full_chunk in (first_chunk + 1)..last_chunk {
if self.chunks[full_chunk] != zero {
return false;
}
}
let start_byte = Self::chunk_byte_offset(start);
let end_byte = Self::chunk_byte_offset(end);
let start_mask = (0xFFu16 << ((start & 0b111) as u32)) as u8;
let end_mask = (0xFFu16 >> (7 - ((end & 0b111) as u32))) as u8;
let first = &self.chunks[first_chunk];
let first_end_byte = if first_chunk == last_chunk {
end_byte
} else {
N - 1
};
for (i, &byte) in first
.iter()
.enumerate()
.take(first_end_byte + 1)
.skip(start_byte)
{
let mut mask = 0xFFu8;
if i == start_byte {
mask &= start_mask;
}
if first_chunk == last_chunk && i == end_byte {
mask &= end_mask;
}
if (byte & mask) != 0 {
return false;
}
}
if first_chunk == last_chunk {
return true;
}
let last = &self.chunks[last_chunk];
for (i, &byte) in last.iter().enumerate().take(end_byte + 1) {
let mask = if i == end_byte { end_mask } else { 0xFF };
if (byte & mask) != 0 {
return false;
}
}
true
}
}
impl<const N: usize> Default for BitMap<N> {
fn default() -> Self {
Self::new()
}
}
impl<T: AsRef<[bool]>, const N: usize> From<T> for BitMap<N> {
fn from(t: T) -> Self {
let bools = t.as_ref();
let mut bv = Self::with_capacity(bools.len() as u64);
for &b in bools {
bv.push(b);
}
bv
}
}
impl<const N: usize> From<BitMap<N>> for Vec<bool> {
fn from(bv: BitMap<N>) -> Self {
bv.iter().collect()
}
}
impl<const N: usize> fmt::Debug for BitMap<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
const MAX_DISPLAY: u64 = 64;
const HALF_DISPLAY: u64 = MAX_DISPLAY / 2;
let write_bit = |formatter: &mut Formatter<'_>, bit: u64| -> core::fmt::Result {
formatter.write_char(if self.get(bit) { '1' } else { '0' })
};
f.write_str("BitMap[")?;
let len = self.len();
if len <= MAX_DISPLAY {
for i in 0..len {
write_bit(f, i)?;
}
} else {
for i in 0..HALF_DISPLAY {
write_bit(f, i)?;
}
f.write_str("...")?;
for i in (len - HALF_DISPLAY)..len {
write_bit(f, i)?;
}
}
f.write_str("]")
}
}
impl<const N: usize> Index<u64> for BitMap<N> {
type Output = bool;
#[inline]
fn index(&self, bit: u64) -> &Self::Output {
self.assert_bit(bit);
let value = self.get(bit);
if value {
&true
} else {
&false
}
}
}
impl<const N: usize> BitAnd for &BitMap<N> {
type Output = BitMap<N>;
fn bitand(self, rhs: Self) -> Self::Output {
self.assert_eq_len(rhs);
let mut result = self.clone();
result.and(rhs);
result
}
}
impl<const N: usize> BitOr for &BitMap<N> {
type Output = BitMap<N>;
fn bitor(self, rhs: Self) -> Self::Output {
self.assert_eq_len(rhs);
let mut result = self.clone();
result.or(rhs);
result
}
}
impl<const N: usize> BitXor for &BitMap<N> {
type Output = BitMap<N>;
fn bitxor(self, rhs: Self) -> Self::Output {
self.assert_eq_len(rhs);
let mut result = self.clone();
result.xor(rhs);
result
}
}
impl<const N: usize> Write for BitMap<N> {
fn write(&self, buf: &mut impl BufMut) {
self.len().write(buf);
for chunk in &self.chunks {
for &byte in chunk {
byte.write(buf);
}
}
}
}
impl<const N: usize> Read for BitMap<N> {
type Cfg = u64;
fn read_cfg(buf: &mut impl Buf, max_len: &Self::Cfg) -> Result<Self, CodecError> {
let len = u64::read(buf)?;
if len > *max_len {
return Err(CodecError::InvalidLength(len as usize));
}
let num_chunks = len.div_ceil(Self::CHUNK_SIZE_BITS) as usize;
let mut chunks = VecDeque::with_capacity(num_chunks);
for _ in 0..num_chunks {
at_least(buf, N)?;
let mut chunk = [0u8; N];
buf.copy_to_slice(&mut chunk);
chunks.push_back(chunk);
}
let mut result = Self { chunks, len };
if result.clear_trailing_bits() {
return Err(CodecError::Invalid(
"BitMap",
"Invalid trailing bits in encoded data",
));
}
Ok(result)
}
}
impl<const N: usize> EncodeSize for BitMap<N> {
fn encode_size(&self) -> usize {
self.len().encode_size() + (self.chunks.len() * N)
}
}
pub struct Iterator<'a, const N: usize> {
bitmap: &'a BitMap<N>,
pos: u64,
}
impl<const N: usize> iter::Iterator for Iterator<'_, N> {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.bitmap.len() {
return None;
}
let bit = self.bitmap.get(self.pos);
self.pos += 1;
Some(bit)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.bitmap.len().saturating_sub(self.pos);
let capped = remaining.min(usize::MAX as u64) as usize;
(capped, Some(capped))
}
}
impl<const N: usize> ExactSizeIterator for Iterator<'_, N> {}
#[cfg(feature = "arbitrary")]
impl<const N: usize> arbitrary::Arbitrary<'_> for BitMap<N> {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let size = u.int_in_range(0..=1024)?;
let mut bits = Self::with_capacity(size);
for _ in 0..size {
bits.push(u.arbitrary::<bool>()?);
}
Ok(bits)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hex;
use bytes::BytesMut;
use commonware_codec::{Decode, Encode};
#[test]
fn test_constructors() {
let bv: BitMap<4> = BitMap::new();
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
let bv: BitMap<4> = Default::default();
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
let bv: BitMap<4> = BitMap::with_capacity(0);
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
let bv: BitMap<4> = BitMap::with_capacity(10);
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
}
#[test]
fn test_zeroes() {
let bv: BitMap<1> = BitMap::zeroes(0);
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
assert_eq!(bv.count_ones(), 0);
assert_eq!(bv.count_zeros(), 0);
let bv: BitMap<1> = BitMap::zeroes(1);
assert_eq!(bv.len(), 1);
assert!(!bv.is_empty());
assert_eq!(bv.len(), 1);
assert!(!bv.get(0));
assert_eq!(bv.count_ones(), 0);
assert_eq!(bv.count_zeros(), 1);
let bv: BitMap<1> = BitMap::zeroes(10);
assert_eq!(bv.len(), 10);
assert!(!bv.is_empty());
assert_eq!(bv.len(), 10);
for i in 0..10 {
assert!(!bv.get(i as u64));
}
assert_eq!(bv.count_ones(), 0);
assert_eq!(bv.count_zeros(), 10);
}
#[test]
fn test_ones() {
let bv: BitMap<1> = BitMap::ones(0);
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
assert_eq!(bv.count_ones(), 0);
assert_eq!(bv.count_zeros(), 0);
let bv: BitMap<1> = BitMap::ones(1);
assert_eq!(bv.len(), 1);
assert!(!bv.is_empty());
assert_eq!(bv.len(), 1);
assert!(bv.get(0));
assert_eq!(bv.count_ones(), 1);
assert_eq!(bv.count_zeros(), 0);
let bv: BitMap<1> = BitMap::ones(10);
assert_eq!(bv.len(), 10);
assert!(!bv.is_empty());
assert_eq!(bv.len(), 10);
for i in 0..10 {
assert!(bv.get(i as u64));
}
assert_eq!(bv.count_ones(), 10);
assert_eq!(bv.count_zeros(), 0);
}
#[test]
fn test_invariant_trailing_bits_are_zero() {
fn check_trailing_bits_zero<const N: usize>(bitmap: &BitMap<N>) {
let (last_chunk, next_bit) = bitmap.last_chunk();
for bit_idx in next_bit..((N * 8) as u64) {
let byte_idx = (bit_idx / 8) as usize;
let bit_in_byte = bit_idx % 8;
let mask = 1u8 << bit_in_byte;
assert_eq!(last_chunk[byte_idx] & mask, 0);
}
}
let bv: BitMap<4> = BitMap::ones(15);
check_trailing_bits_zero(&bv);
let bv: BitMap<4> = BitMap::ones(33);
check_trailing_bits_zero(&bv);
let mut bv: BitMap<4> = BitMap::new();
for i in 0..37 {
bv.push(i % 2 == 0);
check_trailing_bits_zero(&bv);
}
let mut bv: BitMap<4> = BitMap::ones(40);
check_trailing_bits_zero(&bv);
for _ in 0..15 {
bv.pop();
check_trailing_bits_zero(&bv);
}
let mut bv: BitMap<4> = BitMap::ones(25);
bv.flip_all();
check_trailing_bits_zero(&bv);
let bv1: BitMap<4> = BitMap::ones(20);
let bv2: BitMap<4> = BitMap::zeroes(20);
let mut bv_and = bv1.clone();
bv_and.and(&bv2);
check_trailing_bits_zero(&bv_and);
let mut bv_or = bv1.clone();
bv_or.or(&bv2);
check_trailing_bits_zero(&bv_or);
let mut bv_xor = bv1;
bv_xor.xor(&bv2);
check_trailing_bits_zero(&bv_xor);
let original: BitMap<4> = BitMap::ones(27);
let encoded = original.encode();
let decoded: BitMap<4> =
BitMap::decode_cfg(&mut encoded.as_ref(), &(usize::MAX as u64)).unwrap();
check_trailing_bits_zero(&decoded);
let mut bv_clean: BitMap<4> = BitMap::ones(20);
assert!(!bv_clean.clear_trailing_bits());
let mut bv_dirty: BitMap<4> = BitMap::ones(20);
let last_chunk = bv_dirty.chunks.back_mut().unwrap();
last_chunk[3] |= 0xF0; assert!(bv_dirty.clear_trailing_bits());
assert!(!bv_dirty.clear_trailing_bits());
check_trailing_bits_zero(&bv_dirty);
}
#[test]
fn test_get_set() {
let mut bv: BitMap<4> = BitMap::new();
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
bv.push(true);
bv.push(false);
bv.push(true);
assert_eq!(bv.len(), 3);
assert!(!bv.is_empty());
assert!(bv.get(0));
assert!(!bv.get(1));
assert!(bv.get(2));
bv.set(1, true);
assert!(bv.get(1));
bv.set(2, false);
assert!(!bv.get(2));
bv.flip(0); assert!(!bv.get(0));
bv.flip(0); assert!(bv.get(0));
}
#[test]
fn test_chunk_operations() {
let mut bv: BitMap<4> = BitMap::new();
let test_chunk = hex!("0xABCDEF12");
bv.push_chunk(&test_chunk);
assert_eq!(bv.len(), 32);
let chunk = bv.get_chunk(0);
assert_eq!(chunk, &test_chunk);
let chunk = bv.get_chunk_containing(0);
assert_eq!(chunk, &test_chunk);
let (last_chunk, next_bit) = bv.last_chunk();
assert_eq!(next_bit, BitMap::<4>::CHUNK_SIZE_BITS); assert_eq!(last_chunk, &test_chunk); }
#[test]
fn test_pop() {
let mut bv: BitMap<3> = BitMap::new();
bv.push(true);
assert!(bv.pop());
assert_eq!(bv.len(), 0);
bv.push(false);
assert!(!bv.pop());
assert_eq!(bv.len(), 0);
bv.push(true);
bv.push(false);
bv.push(true);
assert!(bv.pop());
assert_eq!(bv.len(), 2);
assert!(!bv.pop());
assert_eq!(bv.len(), 1);
assert!(bv.pop());
assert_eq!(bv.len(), 0);
for i in 0..100 {
bv.push(i % 2 == 0);
}
assert_eq!(bv.len(), 100);
for i in (0..100).rev() {
assert_eq!(bv.pop(), i % 2 == 0);
}
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
}
#[test]
fn test_pop_chunk() {
let mut bv: BitMap<3> = BitMap::new();
const CHUNK_SIZE: u64 = BitMap::<3>::CHUNK_SIZE_BITS;
let chunk1 = hex!("0xAABBCC");
bv.push_chunk(&chunk1);
assert_eq!(bv.len(), CHUNK_SIZE);
let popped = bv.pop_chunk();
assert_eq!(popped, chunk1);
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
let chunk2 = hex!("0x112233");
let chunk3 = hex!("0x445566");
let chunk4 = hex!("0x778899");
bv.push_chunk(&chunk2);
bv.push_chunk(&chunk3);
bv.push_chunk(&chunk4);
assert_eq!(bv.len(), CHUNK_SIZE * 3);
assert_eq!(bv.pop_chunk(), chunk4);
assert_eq!(bv.len(), CHUNK_SIZE * 2);
assert_eq!(bv.pop_chunk(), chunk3);
assert_eq!(bv.len(), CHUNK_SIZE);
assert_eq!(bv.pop_chunk(), chunk2);
assert_eq!(bv.len(), 0);
let first_chunk = hex!("0xAABBCC");
let second_chunk = hex!("0x112233");
bv.push_chunk(&first_chunk);
bv.push_chunk(&second_chunk);
assert_eq!(bv.pop_chunk(), second_chunk);
assert_eq!(bv.len(), CHUNK_SIZE);
for i in 0..CHUNK_SIZE {
let byte_idx = (i / 8) as usize;
let bit_idx = i % 8;
let expected = (first_chunk[byte_idx] >> bit_idx) & 1 == 1;
assert_eq!(bv.get(i), expected);
}
assert_eq!(bv.pop_chunk(), first_chunk);
assert_eq!(bv.len(), 0);
}
#[test]
#[should_panic(expected = "cannot pop chunk when not chunk aligned")]
fn test_pop_chunk_not_aligned() {
let mut bv: BitMap<3> = BitMap::new();
bv.push_chunk(&[0xFF; 3]);
bv.push(true);
bv.pop_chunk();
}
#[test]
#[should_panic(expected = "cannot pop chunk: bitmap has fewer than CHUNK_SIZE_BITS bits")]
fn test_pop_chunk_insufficient_bits() {
let mut bv: BitMap<3> = BitMap::new();
bv.push(true);
bv.push(false);
bv.pop_chunk();
}
#[test]
fn test_byte_operations() {
let mut bv: BitMap<4> = BitMap::new();
bv.push_byte(0xFF);
assert_eq!(bv.len(), 8);
for i in 0..8 {
assert!(bv.get(i as u64));
}
bv.push_byte(0x00);
assert_eq!(bv.len(), 16);
for i in 8..16 {
assert!(!bv.get(i as u64));
}
}
#[test]
fn test_count_operations() {
let mut bv: BitMap<4> = BitMap::new();
assert_eq!(bv.count_ones(), 0);
assert_eq!(bv.count_zeros(), 0);
bv.push(true);
bv.push(false);
bv.push(true);
bv.push(true);
bv.push(false);
assert_eq!(bv.count_ones(), 3);
assert_eq!(bv.count_zeros(), 2);
assert_eq!(bv.len(), 5);
let mut bv2: BitMap<4> = BitMap::new();
bv2.push_byte(0xFF); bv2.push_byte(0x00); bv2.push_byte(0xAA);
assert_eq!(bv2.count_ones(), 12);
assert_eq!(bv2.count_zeros(), 12);
assert_eq!(bv2.len(), 24);
}
#[test]
fn test_set_all() {
let mut bv: BitMap<1> = BitMap::new();
bv.push(true);
bv.push(false);
bv.push(true);
bv.push(false);
bv.push(true);
bv.push(false);
bv.push(true);
bv.push(false);
bv.push(true);
bv.push(false);
assert_eq!(bv.len(), 10);
assert_eq!(bv.count_ones(), 5);
assert_eq!(bv.count_zeros(), 5);
bv.set_all(true);
assert_eq!(bv.len(), 10);
assert_eq!(bv.count_ones(), 10);
assert_eq!(bv.count_zeros(), 0);
bv.set_all(false);
assert_eq!(bv.len(), 10);
assert_eq!(bv.count_ones(), 0);
assert_eq!(bv.count_zeros(), 10);
}
#[test]
fn test_flip_all() {
let mut bv: BitMap<4> = BitMap::new();
bv.push(true);
bv.push(false);
bv.push(true);
bv.push(false);
bv.push(true);
let original_ones = bv.count_ones();
let original_zeros = bv.count_zeros();
let original_len = bv.len();
bv.flip_all();
assert_eq!(bv.len(), original_len);
assert_eq!(bv.count_ones(), original_zeros);
assert_eq!(bv.count_zeros(), original_ones);
assert!(!bv.get(0));
assert!(bv.get(1));
assert!(!bv.get(2));
assert!(bv.get(3));
assert!(!bv.get(4));
}
#[test]
fn test_bitwise_and() {
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
let pattern1 = [true, false, true, true, false];
let pattern2 = [true, true, false, true, false];
let expected = [true, false, false, true, false];
for &bit in &pattern1 {
bv1.push(bit);
}
for &bit in &pattern2 {
bv2.push(bit);
}
bv1.and(&bv2);
assert_eq!(bv1.len(), 5);
for (i, &expected_bit) in expected.iter().enumerate() {
assert_eq!(bv1.get(i as u64), expected_bit);
}
}
#[test]
fn test_bitwise_or() {
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
let pattern1 = [true, false, true, true, false];
let pattern2 = [true, true, false, true, false];
let expected = [true, true, true, true, false];
for &bit in &pattern1 {
bv1.push(bit);
}
for &bit in &pattern2 {
bv2.push(bit);
}
bv1.or(&bv2);
assert_eq!(bv1.len(), 5);
for (i, &expected_bit) in expected.iter().enumerate() {
assert_eq!(bv1.get(i as u64), expected_bit);
}
}
#[test]
fn test_bitwise_xor() {
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
let pattern1 = [true, false, true, true, false];
let pattern2 = [true, true, false, true, false];
let expected = [false, true, true, false, false];
for &bit in &pattern1 {
bv1.push(bit);
}
for &bit in &pattern2 {
bv2.push(bit);
}
bv1.xor(&bv2);
assert_eq!(bv1.len(), 5);
for (i, &expected_bit) in expected.iter().enumerate() {
assert_eq!(bv1.get(i as u64), expected_bit);
}
}
#[test]
fn test_multi_chunk_operations() {
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
let chunk1 = hex!("0xAABBCCDD"); let chunk2 = hex!("0x55667788");
bv1.push_chunk(&chunk1);
bv1.push_chunk(&chunk1);
bv2.push_chunk(&chunk2);
bv2.push_chunk(&chunk2);
assert_eq!(bv1.len(), 64);
assert_eq!(bv2.len(), 64);
let mut bv_and = bv1.clone();
bv_and.and(&bv2);
let mut bv_or = bv1.clone();
bv_or.or(&bv2);
let mut bv_xor = bv1.clone();
bv_xor.xor(&bv2);
assert_eq!(bv_and.len(), 64);
assert_eq!(bv_or.len(), 64);
assert_eq!(bv_xor.len(), 64);
assert!(bv_and.count_ones() <= bv1.count_ones());
assert!(bv_and.count_ones() <= bv2.count_ones());
assert!(bv_or.count_ones() >= bv1.count_ones());
assert!(bv_or.count_ones() >= bv2.count_ones());
}
#[test]
fn test_partial_chunk_operations() {
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
for i in 0..35 {
bv1.push(i % 2 == 0);
bv2.push(i % 3 == 0);
}
assert_eq!(bv1.len(), 35);
assert_eq!(bv2.len(), 35);
let mut bv_and = bv1.clone();
bv_and.and(&bv2);
let mut bv_or = bv1.clone();
bv_or.or(&bv2);
let mut bv_xor = bv1.clone();
bv_xor.xor(&bv2);
assert_eq!(bv_and.len(), 35);
assert_eq!(bv_or.len(), 35);
assert_eq!(bv_xor.len(), 35);
let mut bv_inv = bv1.clone();
let original_ones = bv_inv.count_ones();
let original_zeros = bv_inv.count_zeros();
bv_inv.flip_all();
assert_eq!(bv_inv.count_ones(), original_zeros);
assert_eq!(bv_inv.count_zeros(), original_ones);
}
#[test]
#[should_panic(expected = "bit 1 out of bounds (len: 1)")]
fn test_flip_out_of_bounds() {
let mut bv: BitMap<4> = BitMap::new();
bv.push(true);
bv.flip(1); }
#[test]
#[should_panic(expected = "BitMap lengths don't match: 2 vs 1")]
fn test_and_length_mismatch() {
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
bv1.push(true);
bv1.push(false);
bv2.push(true);
bv1.and(&bv2);
}
#[test]
#[should_panic(expected = "BitMap lengths don't match: 1 vs 2")]
fn test_or_length_mismatch() {
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
bv1.push(true);
bv2.push(true);
bv2.push(false);
bv1.or(&bv2);
}
#[test]
#[should_panic(expected = "BitMap lengths don't match: 3 vs 2")]
fn test_xor_length_mismatch() {
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
bv1.push(true);
bv1.push(false);
bv1.push(true);
bv2.push(true);
bv2.push(false);
bv1.xor(&bv2);
}
#[test]
fn test_equality() {
assert_eq!(BitMap::<4>::new(), BitMap::<4>::new());
assert_eq!(BitMap::<8>::new(), BitMap::<8>::new());
let pattern = [true, false, true, true, false, false, true, false, true];
let bv4: BitMap<4> = pattern.as_ref().into();
assert_eq!(bv4, BitMap::<4>::from(pattern.as_ref()));
let bv8: BitMap<8> = pattern.as_ref().into();
assert_eq!(bv8, BitMap::<8>::from(pattern.as_ref()));
let mut bv1: BitMap<4> = BitMap::new();
let mut bv2: BitMap<4> = BitMap::new();
for i in 0..33 {
let bit = i % 3 == 0;
bv1.push(bit);
bv2.push(bit);
}
assert_eq!(bv1, bv2);
bv1.push(true);
assert_ne!(bv1, bv2);
bv1.pop(); assert_eq!(bv1, bv2);
bv1.flip(15);
assert_ne!(bv1, bv2);
bv1.flip(15); assert_eq!(bv1, bv2);
let mut bv_ops1 = BitMap::<16>::ones(25);
let mut bv_ops2 = BitMap::<16>::ones(25);
bv_ops1.flip_all();
bv_ops2.flip_all();
assert_eq!(bv_ops1, bv_ops2);
let mask_bits: Vec<bool> = (0..33).map(|i| i % 3 == 0).collect();
let mask = BitMap::<4>::from(mask_bits);
bv1.and(&mask);
bv2.and(&mask);
assert_eq!(bv1, bv2);
}
#[test]
fn test_different_chunk_sizes() {
let mut bv8: BitMap<8> = BitMap::new();
let mut bv16: BitMap<16> = BitMap::new();
let mut bv32: BitMap<32> = BitMap::new();
let chunk8 = [0xFF; 8];
let chunk16 = [0xAA; 16];
let chunk32 = [0x55; 32];
bv8.push_chunk(&chunk8);
bv16.push_chunk(&chunk16);
bv32.push_chunk(&chunk32);
bv8.push(true);
bv8.push(false);
assert_eq!(bv8.len(), 64 + 2);
assert_eq!(bv8.count_ones(), 64 + 1); assert_eq!(bv8.count_zeros(), 1);
bv16.push(true);
bv16.push(false);
assert_eq!(bv16.len(), 128 + 2);
assert_eq!(bv16.count_ones(), 64 + 1); assert_eq!(bv16.count_zeros(), 64 + 1);
bv32.push(true);
bv32.push(false);
assert_eq!(bv32.len(), 256 + 2);
assert_eq!(bv32.count_ones(), 128 + 1); assert_eq!(bv32.count_zeros(), 128 + 1);
}
#[test]
fn test_iterator() {
let bv: BitMap<4> = BitMap::new();
let mut iter = bv.iter();
assert_eq!(iter.next(), None);
assert_eq!(iter.size_hint(), (0, Some(0)));
let pattern = [true, false, true, false, true];
let bv: BitMap<4> = pattern.as_ref().into();
let collected: Vec<bool> = bv.iter().collect();
assert_eq!(collected, pattern);
let mut iter = bv.iter();
assert_eq!(iter.size_hint(), (5, Some(5)));
assert_eq!(iter.next(), Some(true));
assert_eq!(iter.size_hint(), (4, Some(4)));
let iter = bv.iter();
assert_eq!(iter.len(), 5);
let mut large_bv: BitMap<8> = BitMap::new();
for i in 0..100 {
large_bv.push(i % 3 == 0);
}
let collected: Vec<bool> = large_bv.iter().collect();
assert_eq!(collected.len(), 100);
for (i, &bit) in collected.iter().enumerate() {
assert_eq!(bit, i % 3 == 0);
}
}
#[test]
fn test_iterator_edge_cases() {
let mut bv: BitMap<4> = BitMap::new();
bv.push(true);
let collected: Vec<bool> = bv.iter().collect();
assert_eq!(collected, vec![true]);
let mut bv: BitMap<4> = BitMap::new();
for i in 0..32 {
bv.push(i % 2 == 0);
}
bv.push(true);
bv.push(false);
bv.push(true);
let collected: Vec<bool> = bv.iter().collect();
assert_eq!(collected.len(), 35);
for (i, &bit) in collected.iter().enumerate().take(32) {
assert_eq!(bit, i % 2 == 0);
}
assert!(collected[32]);
assert!(!collected[33]);
assert!(collected[34]);
}
#[test]
fn test_codec_roundtrip() {
let original: BitMap<4> = BitMap::new();
let encoded = original.encode();
let decoded = BitMap::decode_cfg(&mut encoded.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(original, decoded);
let pattern = [true, false, true, false, true];
let original: BitMap<4> = pattern.as_ref().into();
let encoded = original.encode();
let decoded = BitMap::decode_cfg(&mut encoded.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(original, decoded);
for (i, &expected) in pattern.iter().enumerate() {
assert_eq!(decoded.get(i as u64), expected);
}
let mut large_original: BitMap<8> = BitMap::new();
for i in 0..100 {
large_original.push(i % 7 == 0);
}
let encoded = large_original.encode();
let decoded = BitMap::decode_cfg(&mut encoded.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(large_original, decoded);
assert_eq!(decoded.len(), 100);
for i in 0..100 {
assert_eq!(decoded.get(i as u64), i % 7 == 0);
}
}
#[test]
fn test_codec_different_chunk_sizes() {
let pattern = [true, false, true, true, false, false, true];
let bv4: BitMap<4> = pattern.as_ref().into();
let bv8: BitMap<8> = pattern.as_ref().into();
let bv16: BitMap<16> = pattern.as_ref().into();
let encoded4 = bv4.encode();
let decoded4 = BitMap::decode_cfg(&mut encoded4.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(bv4, decoded4);
let encoded8 = bv8.encode();
let decoded8 = BitMap::decode_cfg(&mut encoded8.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(bv8, decoded8);
let encoded16 = bv16.encode();
let decoded16 = BitMap::decode_cfg(&mut encoded16.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(bv16, decoded16);
for (i, &expected) in pattern.iter().enumerate() {
let i = i as u64;
assert_eq!(decoded4.get(i), expected);
assert_eq!(decoded8.get(i), expected);
assert_eq!(decoded16.get(i), expected);
}
}
#[test]
fn test_codec_edge_cases() {
let mut bv: BitMap<4> = BitMap::new();
for i in 0..32 {
bv.push(i % 2 == 0);
}
let encoded = bv.encode();
let decoded = BitMap::decode_cfg(&mut encoded.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(bv, decoded);
assert_eq!(decoded.len(), 32);
let mut bv2: BitMap<4> = BitMap::new();
for i in 0..35 {
bv2.push(i % 3 == 0);
}
let encoded2 = bv2.encode();
let decoded2 = BitMap::decode_cfg(&mut encoded2.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(bv2, decoded2);
assert_eq!(decoded2.len(), 35);
}
#[test]
fn test_encode_size() {
let bv: BitMap<4> = BitMap::new();
let encoded = bv.encode();
assert_eq!(bv.encode_size(), encoded.len());
let pattern = [true, false, true, false, true];
let bv: BitMap<4> = pattern.as_ref().into();
let encoded = bv.encode();
assert_eq!(bv.encode_size(), encoded.len());
let mut large_bv: BitMap<8> = BitMap::new();
for i in 0..100 {
large_bv.push(i % 2 == 0);
}
let encoded = large_bv.encode();
assert_eq!(large_bv.encode_size(), encoded.len());
}
#[test]
fn test_codec_empty_chunk_optimization() {
let bv_empty: BitMap<4> = BitMap::new();
let encoded_empty = bv_empty.encode();
let decoded_empty: BitMap<4> =
BitMap::decode_cfg(&mut encoded_empty.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(bv_empty, decoded_empty);
assert_eq!(bv_empty.len(), decoded_empty.len());
assert_eq!(encoded_empty.len(), bv_empty.len().encode_size());
let mut bv_exact: BitMap<4> = BitMap::new();
for _ in 0..32 {
bv_exact.push(true);
}
let encoded_exact = bv_exact.encode();
let decoded_exact: BitMap<4> =
BitMap::decode_cfg(&mut encoded_exact.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(bv_exact, decoded_exact);
let mut bv_partial: BitMap<4> = BitMap::new();
for _ in 0..35 {
bv_partial.push(true);
}
let encoded_partial = bv_partial.encode();
let decoded_partial: BitMap<4> =
BitMap::decode_cfg(&mut encoded_partial.as_ref(), &(usize::MAX as u64)).unwrap();
assert_eq!(bv_partial, decoded_partial);
assert_eq!(bv_partial.len(), decoded_partial.len());
assert!(encoded_exact.len() < encoded_partial.len());
assert_eq!(encoded_exact.len(), bv_exact.len().encode_size() + 4); assert_eq!(encoded_partial.len(), bv_partial.len().encode_size() + 8); }
#[test]
fn test_codec_error_cases() {
let mut buf = BytesMut::new();
100u64.write(&mut buf);
for _ in 0..4 {
[0u8; 4].write(&mut buf);
}
let result = BitMap::<4>::decode_cfg(&mut buf, &99);
assert!(matches!(result, Err(CodecError::InvalidLength(100))));
let mut buf = BytesMut::new();
100u64.write(&mut buf); [0u8; 4].write(&mut buf);
[0u8; 4].write(&mut buf);
[0u8; 4].write(&mut buf);
let result = BitMap::<4>::decode_cfg(&mut buf, &(usize::MAX as u64));
assert!(result.is_err());
let original: BitMap<4> = BitMap::ones(20);
let mut buf = BytesMut::new();
original.write(&mut buf);
let corrupted_data = buf.freeze();
let mut corrupted_bytes = corrupted_data.to_vec();
let last_byte_idx = corrupted_bytes.len() - 1;
corrupted_bytes[last_byte_idx] |= 0xF0;
let result = BitMap::<4>::read_cfg(&mut corrupted_bytes.as_slice(), &(usize::MAX as u64));
assert!(matches!(
result,
Err(CodecError::Invalid(
"BitMap",
"Invalid trailing bits in encoded data"
))
));
}
#[test]
fn test_codec_range_config() {
let mut original: BitMap<4> = BitMap::new();
for i in 0..100 {
original.push(i % 3 == 0);
}
let mut buf = BytesMut::new();
original.write(&mut buf);
let result = BitMap::<4>::decode_cfg(&mut buf.as_ref(), &50);
assert!(matches!(result, Err(CodecError::InvalidLength(100))));
let decoded = BitMap::<4>::decode_cfg(&mut buf.as_ref(), &100).unwrap();
assert_eq!(decoded.len(), 100);
assert_eq!(decoded, original);
let decoded = BitMap::<4>::decode_cfg(&mut buf.as_ref(), &101).unwrap();
assert_eq!(decoded.len(), 100);
assert_eq!(decoded, original);
let empty = BitMap::<4>::new();
let mut buf = BytesMut::new();
empty.write(&mut buf);
let decoded = BitMap::<4>::decode_cfg(&mut buf.as_ref(), &0).unwrap();
assert_eq!(decoded.len(), 0);
assert!(decoded.is_empty());
let decoded = BitMap::<4>::decode_cfg(&mut buf.as_ref(), &1).unwrap();
assert_eq!(decoded.len(), 0);
assert!(decoded.is_empty());
}
#[test]
fn test_from() {
let vec_bool = vec![true, false, true, false, true];
let bv: BitMap<4> = vec_bool.into();
assert_eq!(bv.len(), 5);
assert_eq!(bv.count_ones(), 3);
assert_eq!(bv.count_zeros(), 2);
for (i, &expected) in [true, false, true, false, true].iter().enumerate() {
assert_eq!(bv.get(i as u64), expected);
}
let array = [false, true, true, false];
let bv: BitMap<4> = (&array).into();
assert_eq!(bv.len(), 4);
assert_eq!(bv.count_ones(), 2);
assert_eq!(bv.count_zeros(), 2);
for (i, &expected) in array.iter().enumerate() {
assert_eq!(bv.get(i as u64), expected);
}
let empty: Vec<bool> = vec![];
let bv: BitMap<4> = empty.into();
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
let large: Vec<bool> = (0..100).map(|i| i % 3 == 0).collect();
let bv: BitMap<8> = large.clone().into();
assert_eq!(bv.len(), 100);
for (i, &expected) in large.iter().enumerate() {
assert_eq!(bv.get(i as u64), expected);
}
}
#[test]
fn test_debug_formatting() {
let bv: BitMap<4> = BitMap::new();
let debug_str = format!("{bv:?}");
assert_eq!(debug_str, "BitMap[]");
let bv: BitMap<4> = [true, false, true, false, true].as_ref().into();
let debug_str = format!("{bv:?}");
assert_eq!(debug_str, "BitMap[10101]");
let pattern: Vec<bool> = (0..64).map(|i| i % 2 == 0).collect();
let bv: BitMap<8> = pattern.into();
let debug_str = format!("{bv:?}");
let expected_pattern = "1010".repeat(16); assert_eq!(debug_str, format!("BitMap[{expected_pattern}]"));
let large_pattern: Vec<bool> = (0..100).map(|i| i % 2 == 0).collect();
let bv: BitMap<16> = large_pattern.into();
let debug_str = format!("{bv:?}");
let first_32 = "10".repeat(16); let last_32 = "10".repeat(16); let expected = format!("BitMap[{first_32}...{last_32}]");
assert_eq!(debug_str, expected);
let bv: BitMap<4> = [true].as_ref().into();
assert_eq!(format!("{bv:?}"), "BitMap[1]");
let bv: BitMap<4> = [false].as_ref().into();
assert_eq!(format!("{bv:?}"), "BitMap[0]");
let pattern: Vec<bool> = (0..65).map(|i| i == 0 || i == 64).collect(); let bv: BitMap<16> = pattern.into();
let debug_str = format!("{bv:?}");
let first_32 = "1".to_string() + &"0".repeat(31);
let last_32 = "0".repeat(31) + "1";
let expected = format!("BitMap[{first_32}...{last_32}]");
assert_eq!(debug_str, expected);
}
#[test]
fn test_from_different_chunk_sizes() {
let pattern = [true, false, true, true, false, false, true];
let bv4: BitMap<4> = pattern.as_ref().into();
let bv8: BitMap<8> = pattern.as_ref().into();
let bv16: BitMap<16> = pattern.as_ref().into();
for bv in [&bv4] {
assert_eq!(bv.len(), 7);
assert_eq!(bv.count_ones(), 4);
assert_eq!(bv.count_zeros(), 3);
for (i, &expected) in pattern.iter().enumerate() {
assert_eq!(bv.get(i as u64), expected);
}
}
assert_eq!(bv8.len(), 7);
assert_eq!(bv8.count_ones(), 4);
assert_eq!(bv8.count_zeros(), 3);
for (i, &expected) in pattern.iter().enumerate() {
assert_eq!(bv8.get(i as u64), expected);
}
assert_eq!(bv16.len(), 7);
assert_eq!(bv16.count_ones(), 4);
assert_eq!(bv16.count_zeros(), 3);
for (i, &expected) in pattern.iter().enumerate() {
assert_eq!(bv16.get(i as u64), expected);
}
}
#[test]
fn test_prune_chunks() {
let mut bv: BitMap<4> = BitMap::new();
bv.push_chunk(&[1, 2, 3, 4]);
bv.push_chunk(&[5, 6, 7, 8]);
bv.push_chunk(&[9, 10, 11, 12]);
assert_eq!(bv.len(), 96);
assert_eq!(bv.get_chunk(0), &[1, 2, 3, 4]);
bv.prune_chunks(1);
assert_eq!(bv.len(), 64);
assert_eq!(bv.get_chunk(0), &[5, 6, 7, 8]);
assert_eq!(bv.get_chunk(1), &[9, 10, 11, 12]);
bv.prune_chunks(1);
assert_eq!(bv.len(), 32);
assert_eq!(bv.get_chunk(0), &[9, 10, 11, 12]);
}
#[test]
#[should_panic(expected = "cannot prune")]
fn test_prune_too_many_chunks() {
let mut bv: BitMap<4> = BitMap::new();
bv.push_chunk(&[1, 2, 3, 4]);
bv.push_chunk(&[5, 6, 7, 8]);
bv.push(true);
bv.prune_chunks(4);
}
#[test]
fn test_prune_with_partial_last_chunk() {
let mut bv: BitMap<4> = BitMap::new();
bv.push_chunk(&[1, 2, 3, 4]);
bv.push_chunk(&[5, 6, 7, 8]);
bv.push(true);
bv.push(false);
assert_eq!(bv.len(), 66);
bv.prune_chunks(1);
assert_eq!(bv.len(), 34);
assert_eq!(bv.get_chunk(0), &[5, 6, 7, 8]);
assert!(bv.get(32));
assert!(!bv.get(33));
}
#[test]
fn test_prune_all_chunks_resets_next_bit() {
let mut bv: BitMap<4> = BitMap::new();
bv.push_chunk(&[1, 2, 3, 4]);
bv.push_chunk(&[5, 6, 7, 8]);
bv.push(true);
bv.push(false);
bv.push(true);
assert_eq!(bv.len(), 67);
bv.prune_chunks(3);
assert_eq!(bv.len(), 0);
assert!(bv.is_empty());
bv.push(true);
assert_eq!(bv.len(), 1);
assert!(bv.get(0));
}
#[test]
fn test_is_chunk_aligned() {
let bv: BitMap<4> = BitMap::new();
assert!(bv.is_chunk_aligned());
let mut bv4: BitMap<4> = BitMap::new();
assert!(bv4.is_chunk_aligned());
for i in 1..=32 {
bv4.push(i % 2 == 0);
if i == 32 {
assert!(bv4.is_chunk_aligned()); } else {
assert!(!bv4.is_chunk_aligned()); }
}
for i in 33..=64 {
bv4.push(i % 2 == 0);
if i == 64 {
assert!(bv4.is_chunk_aligned()); } else {
assert!(!bv4.is_chunk_aligned()); }
}
let mut bv: BitMap<8> = BitMap::new();
assert!(bv.is_chunk_aligned());
bv.push_chunk(&[0xFF; 8]);
assert!(bv.is_chunk_aligned()); bv.push_chunk(&[0xAA; 8]);
assert!(bv.is_chunk_aligned()); bv.push(true);
assert!(!bv.is_chunk_aligned());
let mut bv: BitMap<4> = BitMap::new();
for _ in 0..4 {
bv.push_byte(0xFF);
}
assert!(bv.is_chunk_aligned());
bv.pop();
assert!(!bv.is_chunk_aligned());
let bv_zeroes: BitMap<4> = BitMap::zeroes(64);
assert!(bv_zeroes.is_chunk_aligned());
let bv_ones: BitMap<4> = BitMap::ones(96);
assert!(bv_ones.is_chunk_aligned());
let bv_partial: BitMap<4> = BitMap::zeroes(65);
assert!(!bv_partial.is_chunk_aligned());
}
#[test]
fn test_unprune_restores_length() {
let mut prunable: Prunable<4> = Prunable::new_with_pruned_chunks(1).unwrap();
assert_eq!(prunable.len(), Prunable::<4>::CHUNK_SIZE_BITS);
assert_eq!(prunable.pruned_chunks(), 1);
let chunk = [0xDE, 0xAD, 0xBE, 0xEF];
prunable.unprune_chunks(&[chunk]);
assert_eq!(prunable.pruned_chunks(), 0);
assert_eq!(prunable.len(), Prunable::<4>::CHUNK_SIZE_BITS);
assert_eq!(prunable.get_chunk_containing(0), &chunk);
}
mod proptests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn is_unset_matches_naive(
bits in prop::collection::vec(any::<bool>(), 1..=512usize),
start in 0u64..=512,
end in 0u64..=512,
) {
let bitmap: BitMap = BitMap::from(bits.as_slice());
let len = bitmap.len();
let start = start.min(len);
let end = end.max(start).min(len);
let range = start..end;
let expected = range.clone().all(|i| !bitmap.get(i));
prop_assert_eq!(bitmap.is_unset(range), expected);
}
}
}
#[test]
fn is_unset_all_zeros() {
let bitmap = BitMap::<8>::zeroes(256);
assert!(bitmap.is_unset(0..256));
}
#[test]
fn is_unset_all_ones() {
let bitmap = BitMap::<8>::ones(256);
assert!(!bitmap.is_unset(0..256));
}
#[test]
fn is_unset_single_bit() {
let mut bitmap = BitMap::<8>::zeroes(64);
bitmap.set(31, true);
assert!(bitmap.is_unset(0..31));
assert!(!bitmap.is_unset(0..32));
assert!(!bitmap.is_unset(31..32));
assert!(bitmap.is_unset(32..64));
}
#[test]
fn is_unset_empty_range() {
let bitmap = BitMap::<8>::ones(64);
assert!(bitmap.is_unset(0..0));
assert!(bitmap.is_unset(32..32));
assert!(bitmap.is_unset(64..64));
}
#[test]
fn is_unset_chunk_boundaries() {
let mut bitmap = BitMap::<1>::zeroes(32);
bitmap.set(7, true);
assert!(bitmap.is_unset(0..7));
assert!(!bitmap.is_unset(0..8));
assert!(bitmap.is_unset(8..32));
}
#[test]
fn is_unset_small_chunk_multi_span() {
let mut bitmap = BitMap::<4>::zeroes(128);
bitmap.set(96, true);
assert!(bitmap.is_unset(0..96));
assert!(!bitmap.is_unset(0..97));
assert!(bitmap.is_unset(97..128));
}
#[test]
#[should_panic(expected = "out of bounds")]
fn is_unset_out_of_bounds() {
let bitmap = BitMap::<8>::zeroes(64);
bitmap.is_unset(0..65);
}
#[cfg(feature = "arbitrary")]
mod conformance {
use super::*;
use commonware_codec::conformance::CodecConformance;
commonware_conformance::conformance_tests! {
CodecConformance<BitMap>
}
}
}