use codec::{Decode, Encode}; use num_traits::PrimInt; use scale_info::TypeInfo; use sp_arithmetic::traits::AtLeast8BitUnsigned; use sp_runtime::{traits::UniqueSaturatedInto, RuntimeDebug}; use sp_std::{cmp::{min, max}, marker::PhantomData, vec, vec::Vec}; use ghost_traits::bounded_bitmap as traits; use frame_support::{pallet_prelude::MaxEncodedLen, traits::Get, BoundedVec}; use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, Shr}; const fn inner_bits() -> usize { core::mem::size_of::() * 8 } const fn inner_mask() -> usize { let bits_per_element = inner_bits::(); bits_per_element - 1 } const fn inner_shift() -> u32 { let bits_per_element = inner_bits::(); bits_per_element.trailing_zeros() } pub fn validate_bitmap_sizes(authorities: u32) { assert!( B::total_bits_capacity() >= authorities, "CRITICAL CONFIG ERROR: max authorities does not fit into max chunks!", ); } #[derive(Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(MaxChunks))] pub struct BoundedBitmap where MaxChunks: Get, { data: BoundedVec, active_bits_count: u32, } impl BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { fn combine(&self, rhs: &Self, op: F) -> Self where Inner: Copy, F: Fn(Inner, Inner) -> Inner, { let active_bits_count = max(self.active_bits_count, rhs.active_bits_count); let bounded_len = min(MaxChunks::get() as usize, max(self.data.len(), rhs.data.len())); let mut raw_vec = vec![Inner::zero(); bounded_len]; for index in 0..bounded_len { let a = self.data.get(index).copied().unwrap_or_else(Inner::zero); let b = rhs.data.get(index).copied().unwrap_or_else(Inner::zero); raw_vec[index] = op(a, b); } let active_bits_count_usize: usize = active_bits_count.unique_saturated_into(); let target_chunk = active_bits_count_usize >> inner_shift::(); let reminder = active_bits_count_usize & inner_mask::(); if reminder > 0 { if target_chunk < raw_vec.len() { let mask_u64 = (Inner::one() << reminder) - Inner::one(); let masked: Inner = mask_u64.unique_saturated_into(); raw_vec[target_chunk] &= masked; } let after_target_chunk = target_chunk.saturating_add(1); if after_target_chunk < raw_vec.len() { raw_vec[after_target_chunk..].fill(Inner::zero()); } } Self { active_bits_count, data: BoundedVec::::try_from(raw_vec) .expect("Should never happen because bounded_len caps at MaxChunks"), } } fn empty(n: N) -> Self where N: UniqueSaturatedInto, { let mut n: usize = n.unique_saturated_into(); let max_chunks: usize = MaxChunks::get().unique_saturated_into(); let max_bits = max_chunks.saturating_mul(inner_bits::()); if n > max_bits { n = max_bits; } let reminder = n & inner_mask::(); let elements = n >> inner_shift::(); let total_size = if reminder > 0 { elements.saturating_add(1) } else { elements }; let raw_vec = vec![Inner::zero(); min(total_size, MaxChunks::get() as usize)]; let bounded_data = BoundedVec::::try_from(raw_vec) .expect("Should never happen because bounded_len caps at MaxChunks"); Self { data: bounded_data, active_bits_count: n.unique_saturated_into(), } } fn empty_from(bitmap: &Self) -> Self { Self::empty(bitmap.active_bits_count()) } fn from_vec(values: &Vec) -> Self where Outer: AtLeast8BitUnsigned + TryInto + PartialEq + Copy, { let value_bits = inner_bits::(); let n = values.len().saturating_mul(value_bits); let mut new_bitmap = Self::empty(n); for value in values.iter() { new_bitmap.insert(*value); } new_bitmap } fn from_bitmask(values: &Vec) -> Self where Outer: AtLeast8BitUnsigned + PrimInt, { let value_bits = inner_bits::(); let n = values.iter().enumerate().rev() .find(|(_, &val)| val != Outer::zero()) .map(|(idx, &val)| { let active_bits = value_bits - (val.leading_zeros() as usize); idx.saturating_mul(value_bits).saturating_add(active_bits) }) .unwrap_or(0); let mut new_bitmap = Self::empty(n); for (value_index, value) in values.iter().enumerate() { for bit_index in 0..value_bits { let bit_mask = Outer::one() << bit_index; if (*value & bit_mask) == Outer::zero() { continue; } let index = value_index .saturating_mul(value_bits) .saturating_add(bit_index); if let None = new_bitmap.insert(index as u32) { return new_bitmap; } } } new_bitmap } fn all_ones(n: N) -> Self where N: UniqueSaturatedInto, { let n: usize = n.unique_saturated_into(); let mut new_bitmap = Self::empty(n); let reminder = n & inner_mask::(); new_bitmap .data .iter_mut() .rev() .skip(if reminder > 0 { 1 } else { 0 }) .for_each(|inner| *inner = !Inner::zero()); if reminder > 0 { if let Some(inner) = new_bitmap.data.iter_mut().last() { let mask_u64 = (1u64 << reminder) - 1; let masked: Inner = mask_u64.unique_saturated_into(); *inner = masked; } } new_bitmap } fn nullify(&mut self) { self.data .iter_mut() .for_each(|inner| *inner = Inner::zero()); } fn truncate(&mut self) { let Some(highest_bit) = self.highest_bit::() else { self.active_bits_count = 0; return; }; let element_index: usize = highest_bit >> inner_shift::(); let required_elements_count = element_index.saturating_add(1); let new_highest_bit = highest_bit.saturating_add(1); let reminder = new_highest_bit & inner_mask::(); self.data.truncate(required_elements_count); if reminder > 0 { if let Some(target_element) = self.data.get_mut(element_index) { let mask_u64 = (1u64 << reminder) - 1; let masked: Inner = mask_u64.unique_saturated_into(); *target_element &= masked; } } self.active_bits_count = new_highest_bit as u32; } fn insert>(&mut self, i: N) -> Option { let index: usize = i.try_into().ok()?; if index >= self.active_bits_count as usize { return None; } let element_index = index >> inner_shift::(); let bit_shift = index & inner_mask::(); self.data.get_mut(element_index).map(|element| { *element |= Inner::one() << bit_shift; i }) } fn remove>(&mut self, i: N) -> Option { let index: usize = i.try_into().ok()?; if index >= self.active_bits_count as usize { return None; } let element_index = (index >> inner_shift::()) as usize; let bit_shift = index & inner_mask::(); self.data.get_mut(element_index).map(|element| { *element &= !(Inner::one() << bit_shift); i }) } fn active_bits_count(&self) -> u32 { self.active_bits_count } fn highest_bit(&self) -> Option where usize: UniqueSaturatedInto, { let shift = inner_shift::(); self.data .iter() .enumerate() .rev() .find_map(|(element_index, &element)| { if element.is_zero() { return None; } let mut temp = element; let mut bit_index = 0; loop { temp = temp >> 1u32; if temp.is_zero() { break; } bit_index += 1; } let bit_offset = element_index.checked_shl(shift).unwrap_or(usize::MAX); let index = bit_offset.saturating_add(bit_index); Some(index.unique_saturated_into()) }) } fn count_ones(&self) -> N where u32: UniqueSaturatedInto, { let ones = self .data .iter() .map(|&element| element.count_ones()) .sum::(); ones.unique_saturated_into() } fn count_zeros(&self) -> N where u32: UniqueSaturatedInto, { let zeros = self .active_bits_count .saturating_sub(self.count_ones::()); zeros.unique_saturated_into() } fn is_empty(&self) -> bool { self.data.iter().all(|inner| *inner == Inner::zero()) } fn contains(&self, index: N) -> bool where N: UniqueSaturatedInto, { let index: usize = index.unique_saturated_into(); if index >= self.active_bits_count as usize { return false; } let element_index = (index >> inner_shift::()) as usize; let bit_shift = index & inner_mask::(); self.data .get(element_index) .map(|element| { let mask = Inner::one() << bit_shift; (*element & mask) != Inner::zero() }) .unwrap_or(false) } } impl traits::BoundedBitmapMetadata for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, { fn total_bits_capacity() -> u32 { let inner_bits = inner_bits::() as u32; MaxChunks::get().saturating_mul(inner_bits) } fn chunk_size() -> u32 { inner_bits::() as u32 } } impl traits::BoundedBitmapWriter for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { fn insert>(&mut self, i: N) -> Option { self.insert(i) } fn remove>(&mut self, i: N) -> Option { self.remove(i) } fn truncate(&mut self) { self.truncate(); } fn nullify(&mut self) { self.nullify(); } } impl traits::BoundedBitmapReader for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { fn active_bits_count(&self) -> u32 { self.active_bits_count() } fn is_empty(&self) -> bool { self.is_empty() } fn highest_bit(&self) -> Option where usize: UniqueSaturatedInto, { self.highest_bit() } fn count_ones(&self) -> N where u32: UniqueSaturatedInto, { self.count_ones() } fn count_zeros(&self) -> N where u32: UniqueSaturatedInto, { self.count_zeros() } fn contains(&self, index: N) -> bool where N: UniqueSaturatedInto, { self.contains(index) } } impl traits::BoundedBitmapGenerator for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { fn empty(n: N) -> Self where N: UniqueSaturatedInto, { Self::empty(n) } fn all_ones(n: N) -> Self where N: UniqueSaturatedInto, { Self::all_ones(n) } fn empty_from(bitmap_ref: &Self) -> Self { Self::empty_from(bitmap_ref) } fn from_bitmask(values_ref: &Vec) -> Self where Outer: AtLeast8BitUnsigned + PrimInt, { Self::from_bitmask(values_ref) } fn from_vec(values_ref: &Vec) -> Self where Outer: AtLeast8BitUnsigned + TryInto + PartialEq + Copy, { Self::from_vec(values_ref) } } impl traits::BoundedBitmapIterable for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { type IntoIter<'a, ItemType> = BoundedBitmapIter<'a, Inner, MaxChunks, ItemType> where Self: 'a, ItemType: 'static, u32: UniqueSaturatedInto; fn iter(&self) -> Self::IntoIter<'_, ItemType> where ItemType: 'static, u32: UniqueSaturatedInto, { BoundedBitmapIter { bounded_bitmap: self, current_bit: 0, _marker: sp_std::marker::PhantomData, } } } macro_rules! impl_bitmap_binary_ops { ($trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident, $op:tt) => ( impl $trait for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { type Output = BoundedBitmap; fn $method(self, rhs: Self) -> Self::Output { self.combine(&rhs, |a, b| a $op b) } } impl<'a, Inner, MaxChunks> $trait<&'a BoundedBitmap> for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { type Output = BoundedBitmap; fn $method(self, rhs: &'a BoundedBitmap) -> Self::Output { self.combine(&rhs, |a, b| a $op b) } } impl $trait_assign for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { fn $method_assign(&mut self, rhs: BoundedBitmap) { *self = self.combine(&rhs, |a, b| a $op b) } } impl<'a, Inner, MaxChunks> $trait_assign<&'a BoundedBitmap> for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { fn $method_assign(&mut self, rhs: &'a BoundedBitmap) { *self = self.combine(rhs, |a, b| a $op b) } } impl<'a, 'b, Inner, MaxChunks> $trait<&'b BoundedBitmap> for &'a BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { type Output = BoundedBitmap; fn $method( self, rhs: &'b BoundedBitmap, ) -> Self::Output { self.combine(rhs, |a, b| a $op b) } } impl<'a, Inner, MaxChunks> $trait> for &'a BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { type Output = BoundedBitmap; fn $method( self, rhs: BoundedBitmap, ) -> Self::Output { self.combine(&rhs, |a, b| a $op b) } } ); } impl_bitmap_binary_ops!(BitOr, bitor, BitOrAssign, bitor_assign, |); impl_bitmap_binary_ops!(BitAnd, bitand, BitAndAssign, bitand_assign, &); impl_bitmap_binary_ops!(BitXor, bitxor, BitXorAssign, bitxor_assign, ^); impl Clone for BoundedBitmap where MaxChunks: Get, Inner: Copy, { fn clone(&self) -> Self { let raw_vec: Vec = self.data.to_vec(); let bounded_data = BoundedVec::::try_from(raw_vec).unwrap_or_default(); Self { data: bounded_data, active_bits_count: self.active_bits_count, } } } impl Not for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { type Output = Self; fn not(self) -> Self::Output { let n = self.active_bits_count; let full_bitmap = BoundedBitmap::::all_ones(n); full_bitmap ^ self } } impl<'a, Inner, MaxChunks> Not for &'a BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { type Output = BoundedBitmap; fn not(self) -> Self::Output { let n = self.active_bits_count; let full_bitmap = BoundedBitmap::::all_ones(n); full_bitmap ^ self } } impl PartialEq for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + Copy + PartialEq, { fn eq(&self, other: &Self) -> bool { self.active_bits_count == other.active_bits_count && self.data == other.data } } impl Eq for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + Copy + Eq, { } impl Default for BoundedBitmap where MaxChunks: Get, Inner: AtLeast8BitUnsigned + PrimInt + Copy + core::fmt::Debug, Inner: BitAnd + BitOr + BitXor + Not, Inner: BitAndAssign + BitOrAssign + BitXorAssign, Inner: Shl + Shr, { fn default() -> Self { let num_chunks = MaxChunks::get() as usize; let bits_per_chunk = inner_bits::(); let total_bits = num_chunks * bits_per_chunk; Self::empty(total_bits) } } pub struct BoundedBitmapIter<'a, Inner, MaxChunks, ItemType> where MaxChunks: Get, { bounded_bitmap: &'a BoundedBitmap, current_bit: u32, _marker: PhantomData, } impl<'a, Inner, MaxChunks, ItemType> Iterator for BoundedBitmapIter<'a, Inner, MaxChunks, ItemType> where MaxChunks: Get, Inner: AtLeast8BitUnsigned + Copy, Inner: BitAnd + Not, u32: UniqueSaturatedInto, { type Item = ItemType; fn next(&mut self) -> Option { while self.current_bit < self.bounded_bitmap.active_bits_count { let index_u32 = self.current_bit; let index_usize = index_u32 as usize; self.current_bit = self.current_bit.saturating_add(1); let element_index = index_usize >> inner_shift::(); let bit_shift = (index_usize & inner_mask::()) as u32; match self.bounded_bitmap.data.get(element_index) { Some(element) => { if let Some(mask) = Inner::one().checked_shl(bit_shift) { if !(*element & mask).is_zero() { return Some(index_u32.unique_saturated_into()); } } } None => { self.current_bit = self.bounded_bitmap.active_bits_count; return None; } } } None } } #[cfg(test)] mod tests { use super::*; use frame_support::traits::Get; use sp_runtime::traits::{One, Zero}; use super::traits::*; #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)] pub struct Entries4; impl Get for Entries4 { fn get() -> u32 { 4 } } #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)] pub struct Entries8; impl Get for Entries8 { fn get() -> u32 { 8 } } #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)] pub struct Entries16; impl Get for Entries16 { fn get() -> u32 { 16 } } #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)] pub struct Entries128; impl Get for Entries128 { fn get() -> u32 { 128 } } #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)] pub struct Entries512; impl Get for Entries512 { fn get() -> u32 { 512 } } #[derive(Clone, Copy, PartialEq, Eq, core::fmt::Debug, Encode, Decode, MaxEncodedLen)] pub struct Entries1024; impl Get for Entries1024 { fn get() -> u32 { 1024 } } macro_rules! test_bitmap { ($mod_name:ident, $inner_type:ty, $max_chunks:ty) => { mod $mod_name { use super::*; type TestBitmap = BoundedBitmap<$inner_type, $max_chunks>; #[test] fn test_empty_initialization_and_saturation() { let bits_per_element = inner_bits::<$inner_type>(); let max_allowed_elements = <$max_chunks>::get() as usize; let absolute_max_bits = max_allowed_elements.saturating_mul(bits_per_element); let bitmap = TestBitmap::empty(3usize); assert_eq!(bitmap.active_bits_count(), 3); assert_eq!(bitmap.data.len(), 1); let huge_bitmap = TestBitmap::empty(absolute_max_bits + 1000); assert_eq!(huge_bitmap.active_bits_count, absolute_max_bits as u32); assert_eq!(huge_bitmap.data.len(), max_allowed_elements); } #[test] fn test_sequential_insert_and_out_of_bounds() { let bits_per_element = inner_bits::<$inner_type>(); let mut bitmap = TestBitmap::empty(bits_per_element * 2); assert!(bitmap.insert(0u32).is_some()); assert_eq!(bitmap.count_ones::(), 1); let boundary_bit = bits_per_element as u32; assert!(bitmap.insert(boundary_bit).is_some()); assert_eq!(bitmap.count_ones::(), 2); let out_of_bounds_bit = (bits_per_element * 2) as u32; assert!(bitmap.insert(out_of_bounds_bit).is_none()); assert_eq!(bitmap.count_ones::(), 2); } #[test] fn test_truncate_and_memory_releasing() { let bits_per_element = inner_bits::<$inner_type>(); let mut bitmap = TestBitmap::empty(bits_per_element * 2); assert_eq!(bitmap.data.len(), 2); bitmap.insert(0u32).unwrap(); bitmap.truncate(); assert_eq!(bitmap.data.len(), 1); assert_eq!(bitmap.active_bits_count, 1); } #[test] fn test_not_inversion_and_tail_nullifying() { let mut bitmap = TestBitmap::empty(5usize); bitmap.insert(0u32).unwrap(); let inverted = !bitmap; if let Some(&last_element) = inverted.data.get(0) { let expected_mask = (<$inner_type>::one() << 5) - <$inner_type>::one(); assert_eq!(last_element & !expected_mask, <$inner_type>::zero()); } } #[test] fn test_bitor_different_sizes() { let bits_per_element = inner_bits::<$inner_type>(); let mut bitmap_a = TestBitmap::empty(5usize); bitmap_a.insert(2u32).unwrap(); let mut bitmap_b = TestBitmap::empty(bits_per_element + 5); let far_bit = (bits_per_element + 1) as u32; bitmap_b.insert(far_bit).unwrap(); let result = bitmap_a | bitmap_b; assert_eq!(result.active_bits_count, (bits_per_element + 5) as u32); assert_eq!(result.count_ones::(), 2); assert_eq!(result.data.len(), 2); } #[test] fn test_bitxor_different_sizes() { let bits_per_element = inner_bits::<$inner_type>(); let mut bitmap_a = TestBitmap::empty(10usize); let mut bitmap_b = TestBitmap::empty(bits_per_element + 10); bitmap_a.insert(5u32).unwrap(); bitmap_b.insert(5u32).unwrap(); bitmap_a.insert(8u32).unwrap(); let far_bit = (bits_per_element + 2) as u32; bitmap_b.insert(far_bit).unwrap(); let result = bitmap_a ^ bitmap_b; assert_eq!(result.active_bits_count, (bits_per_element + 10) as u32); assert_eq!(result.count_ones::(), 2); } #[test] fn test_bitand_different_sizes() { let bits_per_element = inner_bits::<$inner_type>(); let bigger_length = (bits_per_element + 5) as u32; let mut bitmap_a = TestBitmap::empty(5usize); bitmap_a.insert(2u32).unwrap(); let mut bitmap_b = TestBitmap::empty(bigger_length); bitmap_b.insert(2u32).unwrap(); let far_bit = (bits_per_element + 1) as u32; bitmap_b.insert(far_bit).unwrap(); let mut result = bitmap_a & bitmap_b; assert_eq!(result.count_ones::(), 1); assert_eq!(result.active_bits_count, bigger_length); result.truncate(); assert_eq!(result.data.len(), 1); } #[test] fn test_bitor_assign_different_sizes() { let bits_per_element = inner_bits::<$inner_type>(); let mut bitmap_a = TestBitmap::empty(5usize); bitmap_a.insert(1u32).unwrap(); let mut bitmap_b = TestBitmap::empty(bits_per_element + 5); let far_bit = (bits_per_element + 1) as u32; bitmap_b.insert(far_bit).unwrap(); bitmap_a |= bitmap_b; assert_eq!(bitmap_a.active_bits_count, (bits_per_element + 5) as u32); assert_eq!(bitmap_a.count_ones::(), 2); assert_eq!(bitmap_a.data.len(), 2); } #[test] fn test_and_assign_different_sizes() { let bits_per_element = inner_bits::<$inner_type>(); let bigger_length = (bits_per_element + 5) as u32; let mut bitmap_a = TestBitmap::empty(5usize); bitmap_a.insert(2u32).unwrap(); let mut bitmap_b = TestBitmap::empty(bigger_length); bitmap_b.insert(2u32).unwrap(); let far_bit = (bits_per_element + 1) as u32; bitmap_b.insert(far_bit).unwrap(); bitmap_a &= bitmap_b; assert_eq!(bitmap_a.active_bits_count(), bigger_length); assert_eq!(bitmap_a.count_ones::(), 1); bitmap_a.truncate(); assert_eq!(bitmap_a.active_bits_count(), 3); assert_eq!(bitmap_a.data.len(), 1); } #[test] fn test_xor_assign_different_sizes() { let bits_per_element = inner_bits::<$inner_type>(); let bigger_length = (bits_per_element + 5) as u32; let mut bitmap_a = TestBitmap::empty(10usize); let mut bitmap_b = TestBitmap::empty(bigger_length); bitmap_a.insert(5u32).unwrap(); bitmap_b.insert(5u32).unwrap(); bitmap_a.insert(8u32).unwrap(); let far_bit = (bits_per_element + 2) as u32; bitmap_b.insert(far_bit).unwrap(); bitmap_a ^= bitmap_b; assert_eq!(bitmap_a.active_bits_count(), bigger_length); assert_eq!(bitmap_a.count_ones::(), 2); assert_eq!(bitmap_a.data.len(), 2); } #[test] fn test_iterator_only_iterates_over_ones() { let max_allowed_chunks = <$max_chunks>::get(); let mut bitmap = TestBitmap::default(); let bit_1 = 0u32; let bit_2 = max_allowed_chunks.saturating_div(2); let bit_3 = max_allowed_chunks.saturating_sub(1); bitmap.insert(bit_1).unwrap(); bitmap.insert(bit_2).unwrap(); bitmap.insert(bit_3).unwrap(); let mut iter = bitmap.iter(); assert_eq!(iter.next(), Some(bit_1)); assert_eq!(iter.next(), Some(bit_2)); assert_eq!(iter.next(), Some(bit_3)); assert_eq!(iter.next(), None); } #[test] fn test_default_and_nullify_and_contains() { let default_bitmap = TestBitmap::default(); let num_chunks = <$max_chunks>::get(); let bits_per_chunk = inner_bits::<$inner_type>(); let total_bits = (num_chunks as usize) * bits_per_chunk; assert_eq!(default_bitmap.active_bits_count(), total_bits as u32); assert_eq!(default_bitmap.count_ones::(), 0); let reminder = total_bits & inner_mask::<$inner_type>(); let mut expected = total_bits >> inner_shift::<$inner_type>(); if reminder > 0 { expected = expected.saturating_add(1); } assert_eq!(default_bitmap.data.len(), expected); let mut bitmap = TestBitmap::empty(10usize); assert!(!bitmap.contains(5u32)); bitmap.insert(5u32).unwrap(); assert!(bitmap.contains(5u32)); assert!(!bitmap.contains(6u32)); bitmap.remove(5u32); assert!(!bitmap.contains(5u32)); assert_eq!(bitmap.count_ones::(), 0); let mut bitmap_to_nullify = TestBitmap::empty(30usize); bitmap_to_nullify.insert(14u32).unwrap(); bitmap_to_nullify.insert(15u32).unwrap(); bitmap_to_nullify.nullify(); assert_eq!(bitmap_to_nullify.count_ones::(), 0); assert_eq!(bitmap_to_nullify.active_bits_count(), 30); for element in bitmap_to_nullify.data.iter() { assert_eq!(*element, <$inner_type>::zero()); } } #[test] fn test_count_zeros() { let bits_per_element = inner_bits::<$inner_type>(); let bitmap = TestBitmap::empty(bits_per_element + 10); let total_zeros: u32 = bitmap.count_zeros(); assert_eq!(total_zeros, (bits_per_element + 10) as u32); let mut mut_bitmap = TestBitmap::empty(10usize); mut_bitmap.insert(3u32).unwrap(); mut_bitmap.insert(7u32).unwrap(); let zeros: u32 = mut_bitmap.count_zeros(); assert_eq!(zeros, 8); } #[test] fn test_empty_from() { let mut original = TestBitmap::empty(25usize); original.insert(10u32).unwrap(); let brand_new = TestBitmap::empty_from(&original); assert_eq!(brand_new.active_bits_count(), 25); assert_eq!(brand_new.count_ones::(), 0); } #[test] fn test_from_vec() { let values_vec: Vec = (0..10).filter(|i| i % 2 == 1).collect(); let bitmap = TestBitmap::from_vec(&values_vec); (0..10).for_each(|i| { if i % 2 == 0 { assert!(!bitmap.contains(i)); } else { assert!(bitmap.contains(i)); } }); assert_eq!(bitmap.count_ones::(), values_vec.len()); } #[test] fn test_from_bitmask_where_each_u8_is_a_bit() { let byte1 = 77u8; let byte2 = 42u8; let byte3 = 69u8; let raw_bits: Vec = vec![byte1, byte2, byte3]; let bitmap = TestBitmap::from_bitmask(&raw_bits); let count_ones = raw_bits.iter().map(|b| b.count_ones()).sum::(); let filled_bits = (raw_bits.len() as u32 - 1) * 8; let expected_bits = filled_bits + (8 - byte3.leading_zeros()); assert_eq!(bitmap.active_bits_count(), expected_bits); assert_eq!(bitmap.count_ones::(), count_ones); // 77 - 10110010 assert!(bitmap.contains(0u32)); assert!(!bitmap.contains(1u32)); assert!(bitmap.contains(2u32)); assert!(bitmap.contains(3u32)); assert!(!bitmap.contains(4u32)); assert!(!bitmap.contains(5u32)); assert!(bitmap.contains(6u32)); assert!(!bitmap.contains(7u32)); // 42 - 01010100 assert!(!bitmap.contains(8u32)); assert!(bitmap.contains(9u32)); assert!(!bitmap.contains(10u32)); assert!(bitmap.contains(11u32)); assert!(!bitmap.contains(12u32)); assert!(bitmap.contains(13u32)); assert!(!bitmap.contains(14u32)); assert!(!bitmap.contains(15u32)); // 69 - 10100010 assert!(bitmap.contains(16u32)); assert!(!bitmap.contains(17u32)); assert!(bitmap.contains(18u32)); assert!(!bitmap.contains(19u32)); assert!(!bitmap.contains(20u32)); assert!(!bitmap.contains(21u32)); assert!(bitmap.contains(22u32)); assert!(!bitmap.contains(23u32)); } } }; } // 1. Matrix rows for u8 test_bitmap!(matrix_u8_entries4, u8, Entries4); test_bitmap!(matrix_u8_entries8, u8, Entries8); test_bitmap!(matrix_u8_entries16, u8, Entries16); test_bitmap!(matrix_u8_entries128, u8, Entries128); test_bitmap!(matrix_u8_entries512, u8, Entries512); test_bitmap!(matrix_u8_entries1024, u8, Entries1024); // 2. Matrix rows for u16 test_bitmap!(matrix_u16_entries4, u16, Entries4); test_bitmap!(matrix_u16_entries8, u16, Entries8); test_bitmap!(matrix_u16_entries16, u16, Entries16); test_bitmap!(matrix_u16_entries128, u16, Entries128); test_bitmap!(matrix_u16_entries512, u16, Entries512); test_bitmap!(matrix_u16_entries1024, u16, Entries1024); // 3. Matrix rows for u32 test_bitmap!(matrix_u32_entries4, u32, Entries4); test_bitmap!(matrix_u32_entries8, u32, Entries8); test_bitmap!(matrix_u32_entries16, u32, Entries16); test_bitmap!(matrix_u32_entries128, u32, Entries128); test_bitmap!(matrix_u32_entries512, u32, Entries512); test_bitmap!(matrix_u32_entries1024, u32, Entries1024); // 4. Matrix rows for u64 test_bitmap!(matrix_u64_entries4, u64, Entries4); test_bitmap!(matrix_u64_entries8, u64, Entries8); test_bitmap!(matrix_u64_entries16, u64, Entries16); test_bitmap!(matrix_u64_entries128, u64, Entries128); test_bitmap!(matrix_u64_entries512, u64, Entries512); test_bitmap!(matrix_u64_entries1024, u64, Entries1024); // 5. Matrix rows for u128 test_bitmap!(matrix_u128_entries4, u128, Entries4); test_bitmap!(matrix_u128_entries8, u128, Entries8); test_bitmap!(matrix_u128_entries16, u128, Entries16); test_bitmap!(matrix_u128_entries128, u128, Entries128); test_bitmap!(matrix_u128_entries512, u128, Entries512); test_bitmap!(matrix_u128_entries1024, u128, Entries1024); }