Auto merge of #147802 - nnethercote:chunk_domain_size, r=Mark-Simulacrum

Store `chunk_domain_size` explicitly in `Chunk`.



Currently we compute it on demand, but it's a little simpler and slightly faster to store it.

r? @ghost
This commit is contained in:
bors
2026-04-07 22:55:42 +00:00
2 changed files with 201 additions and 187 deletions
+122 -145
View File
@@ -490,15 +490,17 @@ pub struct ChunkedBitSet<T> {
marker: PhantomData<T>,
}
// NOTE: The chunk size is computed on-the-fly on each manipulation of a chunk.
// This avoids storing it, as it's almost always CHUNK_BITS except for the last one.
// NOTE: The chunk domain size is stored in each variant because it keeps the
// size of `Chunk` smaller than if it were stored outside the variants.
// We have also tried computing it on the fly, but that was slightly more
// complex and slower than storing it. See #145480 and #147802.
#[derive(Clone, Debug, PartialEq, Eq)]
enum Chunk {
/// A chunk that is all zeros; we don't represent the zeros explicitly.
Zeros,
Zeros { chunk_domain_size: ChunkSize },
/// A chunk that is all ones; we don't represent the ones explicitly.
Ones,
Ones { chunk_domain_size: ChunkSize },
/// A chunk that has a mix of zeros and ones, which are represented
/// explicitly and densely. It never has all zeros or all ones.
@@ -514,6 +516,7 @@ enum Chunk {
/// when a `Mixed` chunk is union'd into a `Zeros` chunk. When we do need
/// to modify a chunk we use `Rc::make_mut`.
Mixed {
chunk_domain_size: ChunkSize,
/// Count of set bits (1s) in this chunk's words.
///
/// Invariant: `0 < ones_count < chunk_domain_size`.
@@ -534,22 +537,6 @@ pub fn domain_size(&self) -> usize {
self.domain_size
}
#[inline]
fn last_chunk_size(&self) -> ChunkSize {
let n = self.domain_size % CHUNK_BITS;
if n == 0 { CHUNK_BITS as ChunkSize } else { n as ChunkSize }
}
/// All the chunks have a chunk_domain_size of `CHUNK_BITS` except the final one.
#[inline]
fn chunk_domain_size(&self, chunk: usize) -> ChunkSize {
if chunk == self.chunks.len() - 1 {
self.last_chunk_size()
} else {
CHUNK_BITS as ChunkSize
}
}
#[cfg(test)]
fn assert_valid(&self) {
if self.domain_size == 0 {
@@ -559,9 +546,8 @@ fn assert_valid(&self) {
assert!((self.chunks.len() - 1) * CHUNK_BITS <= self.domain_size);
assert!(self.chunks.len() * CHUNK_BITS >= self.domain_size);
for (chunk_index, chunk) in self.chunks.iter().enumerate() {
let chunk_domain_size = self.chunk_domain_size(chunk_index);
chunk.assert_valid(chunk_domain_size);
for chunk in self.chunks.iter() {
chunk.assert_valid();
}
}
}
@@ -572,7 +558,28 @@ fn new(domain_size: usize, is_empty: bool) -> Self {
let chunks = if domain_size == 0 {
Box::new([])
} else {
vec![if is_empty { Zeros } else { Ones }; num_chunks(domain_size)].into_boxed_slice()
let num_chunks = domain_size.index().div_ceil(CHUNK_BITS);
let mut last_chunk_domain_size = domain_size % CHUNK_BITS;
if last_chunk_domain_size == 0 {
last_chunk_domain_size = CHUNK_BITS;
};
// All the chunks are the same except the last one which might have a different
// `chunk_domain_size`.
let (normal_chunk, final_chunk) = if is_empty {
(
Zeros { chunk_domain_size: CHUNK_BITS as ChunkSize },
Zeros { chunk_domain_size: last_chunk_domain_size as ChunkSize },
)
} else {
(
Ones { chunk_domain_size: CHUNK_BITS as ChunkSize },
Ones { chunk_domain_size: last_chunk_domain_size as ChunkSize },
)
};
let mut chunks = vec![normal_chunk; num_chunks].into_boxed_slice();
*chunks.as_mut().last_mut().unwrap() = final_chunk;
chunks
};
ChunkedBitSet { domain_size, chunks, marker: PhantomData }
}
@@ -590,7 +597,8 @@ pub fn new_filled(domain_size: usize) -> Self {
}
pub fn clear(&mut self) {
self.chunks.fill_with(|| Chunk::Zeros);
// Not the most efficient implementation, but this function isn't hot.
*self = ChunkedBitSet::new_empty(self.domain_size);
}
#[cfg(test)]
@@ -600,15 +608,11 @@ fn chunks(&self) -> &[Chunk] {
/// Count the number of bits in the set.
pub fn count(&self) -> usize {
self.chunks
.iter()
.enumerate()
.map(|(index, chunk)| chunk.count(self.chunk_domain_size(index)))
.sum()
self.chunks.iter().map(|chunk| chunk.count()).sum()
}
pub fn is_empty(&self) -> bool {
self.chunks.iter().all(|chunk| matches!(chunk, Zeros))
self.chunks.iter().all(|chunk| matches!(chunk, Zeros { .. }))
}
/// Returns `true` if `self` contains `elem`.
@@ -617,9 +621,9 @@ pub fn contains(&self, elem: T) -> bool {
assert!(elem.index() < self.domain_size);
let chunk = &self.chunks[chunk_index(elem)];
match &chunk {
Zeros => false,
Ones => true,
Mixed { ones_count: _, words } => {
Zeros { .. } => false,
Ones { .. } => true,
Mixed { words, .. } => {
let (word_index, mask) = chunk_word_index_and_mask(elem);
(words[word_index] & mask) != 0
}
@@ -635,10 +639,9 @@ pub fn iter(&self) -> ChunkedBitIter<'_, T> {
pub fn insert(&mut self, elem: T) -> bool {
assert!(elem.index() < self.domain_size);
let chunk_index = chunk_index(elem);
let chunk_domain_size = self.chunk_domain_size(chunk_index);
let chunk = &mut self.chunks[chunk_index];
match *chunk {
Zeros => {
Zeros { chunk_domain_size } => {
if chunk_domain_size > 1 {
let mut words = {
// We take some effort to avoid copying the words.
@@ -650,14 +653,14 @@ pub fn insert(&mut self, elem: T) -> bool {
let (word_index, mask) = chunk_word_index_and_mask(elem);
words_ref[word_index] |= mask;
*chunk = Mixed { ones_count: 1, words };
*chunk = Mixed { chunk_domain_size, ones_count: 1, words };
} else {
*chunk = Ones;
*chunk = Ones { chunk_domain_size };
}
true
}
Ones => false,
Mixed { ref mut ones_count, ref mut words } => {
Ones { .. } => false,
Mixed { chunk_domain_size, ref mut ones_count, ref mut words } => {
// We skip all the work if the bit is already set.
let (word_index, mask) = chunk_word_index_and_mask(elem);
if (words[word_index] & mask) == 0 {
@@ -666,7 +669,7 @@ pub fn insert(&mut self, elem: T) -> bool {
let words = Rc::make_mut(words);
words[word_index] |= mask;
} else {
*chunk = Ones;
*chunk = Ones { chunk_domain_size };
}
true
} else {
@@ -678,18 +681,18 @@ pub fn insert(&mut self, elem: T) -> bool {
/// Sets all bits to true.
pub fn insert_all(&mut self) {
self.chunks.fill_with(|| Chunk::Ones);
// Not the most efficient implementation, but this function isn't hot.
*self = ChunkedBitSet::new_filled(self.domain_size);
}
/// Returns `true` if the set has changed.
pub fn remove(&mut self, elem: T) -> bool {
assert!(elem.index() < self.domain_size);
let chunk_index = chunk_index(elem);
let chunk_domain_size = self.chunk_domain_size(chunk_index);
let chunk = &mut self.chunks[chunk_index];
match *chunk {
Zeros => false,
Ones => {
Zeros { .. } => false,
Ones { chunk_domain_size } => {
if chunk_domain_size > 1 {
let mut words = {
// We take some effort to avoid copying the words.
@@ -708,13 +711,13 @@ pub fn remove(&mut self, elem: T) -> bool {
);
let (word_index, mask) = chunk_word_index_and_mask(elem);
words_ref[word_index] &= !mask;
*chunk = Mixed { ones_count: chunk_domain_size - 1, words };
*chunk = Mixed { chunk_domain_size, ones_count: chunk_domain_size - 1, words };
} else {
*chunk = Zeros;
*chunk = Zeros { chunk_domain_size };
}
true
}
Mixed { ref mut ones_count, ref mut words } => {
Mixed { chunk_domain_size, ref mut ones_count, ref mut words } => {
// We skip all the work if the bit is already clear.
let (word_index, mask) = chunk_word_index_and_mask(elem);
if (words[word_index] & mask) != 0 {
@@ -723,7 +726,7 @@ pub fn remove(&mut self, elem: T) -> bool {
let words = Rc::make_mut(words);
words[word_index] &= !mask;
} else {
*chunk = Zeros
*chunk = Zeros { chunk_domain_size }
}
true
} else {
@@ -734,12 +737,11 @@ pub fn remove(&mut self, elem: T) -> bool {
}
fn chunk_iter(&self, chunk_index: usize) -> ChunkIter<'_> {
let chunk_domain_size = self.chunk_domain_size(chunk_index);
match self.chunks.get(chunk_index) {
Some(Zeros) => ChunkIter::Zeros,
Some(Ones) => ChunkIter::Ones(0..chunk_domain_size as usize),
Some(Mixed { ones_count: _, words }) => {
let num_words = num_words(chunk_domain_size as usize);
Some(Zeros { .. }) => ChunkIter::Zeros,
Some(Ones { chunk_domain_size }) => ChunkIter::Ones(0..*chunk_domain_size as usize),
Some(Mixed { chunk_domain_size, words, .. }) => {
let num_words = num_words(*chunk_domain_size as usize);
ChunkIter::Mixed(BitIter::new(&words[0..num_words]))
}
None => ChunkIter::Finished,
@@ -753,39 +755,29 @@ impl<T: Idx> BitRelations<ChunkedBitSet<T>> for ChunkedBitSet<T> {
fn union(&mut self, other: &ChunkedBitSet<T>) -> bool {
assert_eq!(self.domain_size, other.domain_size);
let num_chunks = self.chunks.len();
debug_assert_eq!(num_chunks, other.chunks.len());
let last_chunk_size = self.last_chunk_size();
debug_assert_eq!(last_chunk_size, other.last_chunk_size());
let mut changed = false;
for (chunk_index, (mut self_chunk, other_chunk)) in
self.chunks.iter_mut().zip(other.chunks.iter()).enumerate()
{
let chunk_domain_size = if chunk_index + 1 == num_chunks {
last_chunk_size
} else {
CHUNK_BITS as ChunkSize
};
for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
match (&mut self_chunk, &other_chunk) {
(_, Zeros) | (Ones, _) => {}
(Zeros, _) | (Mixed { .. }, Ones) => {
(_, Zeros { .. }) | (Ones { .. }, _) => {}
(Zeros { .. }, _) | (Mixed { .. }, Ones { .. }) => {
// `other_chunk` fully overwrites `self_chunk`
*self_chunk = other_chunk.clone();
changed = true;
}
(
Mixed { ones_count: self_chunk_ones, words: self_chunk_words },
Mixed { ones_count: _, words: other_chunk_words },
Mixed {
chunk_domain_size,
ones_count: self_chunk_ones_count,
words: self_chunk_words,
},
Mixed { words: other_chunk_words, .. },
) => {
// First check if the operation would change
// `self_chunk.words`. If not, we can avoid allocating some
// words, and this happens often enough that it's a
// performance win. Also, we only need to operate on the
// in-use words, hence the slicing.
let num_words = num_words(chunk_domain_size as usize);
let num_words = num_words(*chunk_domain_size as usize);
// If both sides are the same, nothing will change. This
// case is very common and it's a pretty fast check, so
@@ -813,9 +805,10 @@ fn union(&mut self, other: &ChunkedBitSet<T>) -> bool {
op,
);
debug_assert!(has_changed);
*self_chunk_ones = count_ones(&self_chunk_words[0..num_words]) as ChunkSize;
if *self_chunk_ones == chunk_domain_size {
*self_chunk = Ones;
*self_chunk_ones_count =
count_ones(&self_chunk_words[0..num_words]) as ChunkSize;
if *self_chunk_ones_count == *chunk_domain_size {
*self_chunk = Ones { chunk_domain_size: *chunk_domain_size };
}
changed = true;
}
@@ -827,53 +820,49 @@ fn union(&mut self, other: &ChunkedBitSet<T>) -> bool {
fn subtract(&mut self, other: &ChunkedBitSet<T>) -> bool {
assert_eq!(self.domain_size, other.domain_size);
let num_chunks = self.chunks.len();
debug_assert_eq!(num_chunks, other.chunks.len());
let last_chunk_size = self.last_chunk_size();
debug_assert_eq!(last_chunk_size, other.last_chunk_size());
let mut changed = false;
for (chunk_index, (mut self_chunk, other_chunk)) in
self.chunks.iter_mut().zip(other.chunks.iter()).enumerate()
{
let chunk_domain_size = if chunk_index + 1 == num_chunks {
last_chunk_size
} else {
CHUNK_BITS as ChunkSize
};
for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
match (&mut self_chunk, &other_chunk) {
(Zeros, _) | (_, Zeros) => {}
(Ones | Mixed { .. }, Ones) => {
(Zeros { .. }, _) | (_, Zeros { .. }) => {}
(Ones { chunk_domain_size } | Mixed { chunk_domain_size, .. }, Ones { .. }) => {
changed = true;
*self_chunk = Zeros;
*self_chunk = Zeros { chunk_domain_size: *chunk_domain_size };
}
(Ones, Mixed { ones_count: other_chunk_ones, words: other_chunk_words }) => {
(
Ones { chunk_domain_size },
Mixed { ones_count: other_chunk_ones_count, words: other_chunk_words, .. },
) => {
changed = true;
let num_words = num_words(chunk_domain_size as usize);
let num_words = num_words(*chunk_domain_size as usize);
debug_assert!(num_words > 0 && num_words <= CHUNK_WORDS);
let mut tail_mask =
1 << (chunk_domain_size - ((num_words - 1) * WORD_BITS) as u16) - 1;
1 << (*chunk_domain_size - ((num_words - 1) * WORD_BITS) as u16) - 1;
let mut self_chunk_words = **other_chunk_words;
for word in self_chunk_words[0..num_words].iter_mut().rev() {
*word = !*word & tail_mask;
tail_mask = Word::MAX;
}
let self_chunk_ones = chunk_domain_size - *other_chunk_ones;
let self_chunk_ones_count = *chunk_domain_size - *other_chunk_ones_count;
debug_assert_eq!(
self_chunk_ones,
self_chunk_ones_count,
count_ones(&self_chunk_words[0..num_words]) as ChunkSize
);
*self_chunk =
Mixed { ones_count: self_chunk_ones, words: Rc::new(self_chunk_words) };
*self_chunk = Mixed {
chunk_domain_size: *chunk_domain_size,
ones_count: self_chunk_ones_count,
words: Rc::new(self_chunk_words),
};
}
(
Mixed { ones_count: self_chunk_ones, words: self_chunk_words },
Mixed { ones_count: _, words: other_chunk_words },
Mixed {
chunk_domain_size,
ones_count: self_chunk_ones_count,
words: self_chunk_words,
},
Mixed { words: other_chunk_words, .. },
) => {
// See `ChunkedBitSet::union` for details on what is happening here.
let num_words = num_words(chunk_domain_size as usize);
let num_words = num_words(*chunk_domain_size as usize);
let op = |a: Word, b: Word| a & !b;
if !bitwise_changes(
&self_chunk_words[0..num_words],
@@ -890,9 +879,10 @@ fn subtract(&mut self, other: &ChunkedBitSet<T>) -> bool {
op,
);
debug_assert!(has_changed);
*self_chunk_ones = count_ones(&self_chunk_words[0..num_words]) as ChunkSize;
if *self_chunk_ones == 0 {
*self_chunk = Zeros;
*self_chunk_ones_count =
count_ones(&self_chunk_words[0..num_words]) as ChunkSize;
if *self_chunk_ones_count == 0 {
*self_chunk = Zeros { chunk_domain_size: *chunk_domain_size };
}
changed = true;
}
@@ -904,34 +894,24 @@ fn subtract(&mut self, other: &ChunkedBitSet<T>) -> bool {
fn intersect(&mut self, other: &ChunkedBitSet<T>) -> bool {
assert_eq!(self.domain_size, other.domain_size);
let num_chunks = self.chunks.len();
debug_assert_eq!(num_chunks, other.chunks.len());
let last_chunk_size = self.last_chunk_size();
debug_assert_eq!(last_chunk_size, other.last_chunk_size());
let mut changed = false;
for (chunk_index, (mut self_chunk, other_chunk)) in
self.chunks.iter_mut().zip(other.chunks.iter()).enumerate()
{
let chunk_domain_size = if chunk_index + 1 == num_chunks {
last_chunk_size
} else {
CHUNK_BITS as ChunkSize
};
for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
match (&mut self_chunk, &other_chunk) {
(Zeros, _) | (_, Ones) => {}
(Ones, Zeros | Mixed { .. }) | (Mixed { .. }, Zeros) => {
(Zeros { .. }, _) | (_, Ones { .. }) => {}
(Ones { .. }, Zeros { .. } | Mixed { .. }) | (Mixed { .. }, Zeros { .. }) => {
changed = true;
*self_chunk = other_chunk.clone();
}
(
Mixed { ones_count: self_chunk_ones, words: self_chunk_words },
Mixed { ones_count: _, words: other_chunk_words },
Mixed {
chunk_domain_size,
ones_count: self_chunk_ones_count,
words: self_chunk_words,
},
Mixed { words: other_chunk_words, .. },
) => {
// See `ChunkedBitSet::union` for details on what is happening here.
let num_words = num_words(chunk_domain_size as usize);
let num_words = num_words(*chunk_domain_size as usize);
let op = |a, b| a & b;
if !bitwise_changes(
&self_chunk_words[0..num_words],
@@ -948,9 +928,10 @@ fn intersect(&mut self, other: &ChunkedBitSet<T>) -> bool {
op,
);
debug_assert!(has_changed);
*self_chunk_ones = count_ones(&self_chunk_words[0..num_words]) as ChunkSize;
if *self_chunk_ones == 0 {
*self_chunk = Zeros;
*self_chunk_ones_count =
count_ones(&self_chunk_words[0..num_words]) as ChunkSize;
if *self_chunk_ones_count == 0 {
*self_chunk = Zeros { chunk_domain_size: *chunk_domain_size };
}
changed = true;
}
@@ -1026,11 +1007,13 @@ fn next(&mut self) -> Option<T> {
impl Chunk {
#[cfg(test)]
fn assert_valid(&self, chunk_domain_size: ChunkSize) {
assert!(chunk_domain_size as usize <= CHUNK_BITS);
fn assert_valid(&self) {
match *self {
Zeros | Ones => {}
Mixed { ones_count, ref words } => {
Zeros { chunk_domain_size } | Ones { chunk_domain_size } => {
assert!(chunk_domain_size as usize <= CHUNK_BITS);
}
Mixed { chunk_domain_size, ones_count, ref words } => {
assert!(chunk_domain_size as usize <= CHUNK_BITS);
assert!(0 < ones_count && ones_count < chunk_domain_size);
// Check the number of set bits matches `count`.
@@ -1046,11 +1029,11 @@ fn assert_valid(&self, chunk_domain_size: ChunkSize) {
}
/// Count the number of 1s in the chunk.
fn count(&self, chunk_domain_size: ChunkSize) -> usize {
fn count(&self) -> usize {
match *self {
Zeros => 0,
Ones => chunk_domain_size as usize,
Mixed { ones_count, words: _ } => usize::from(ones_count),
Zeros { .. } => 0,
Ones { chunk_domain_size } => chunk_domain_size as usize,
Mixed { ones_count, .. } => usize::from(ones_count),
}
}
}
@@ -1718,12 +1701,6 @@ fn num_words<T: Idx>(domain_size: T) -> usize {
domain_size.index().div_ceil(WORD_BITS)
}
#[inline]
fn num_chunks<T: Idx>(domain_size: T) -> usize {
assert!(domain_size.index() > 0);
domain_size.index().div_ceil(CHUNK_BITS)
}
#[inline]
fn word_index_and_mask<T: Idx>(elem: T) -> (usize, Word) {
let elem = elem.index();
+79 -42
View File
@@ -120,9 +120,12 @@ fn chunked_bitset() {
let mut b1 = ChunkedBitSet::<usize>::new_empty(1);
assert_eq!(
b1,
ChunkedBitSet { domain_size: 1, chunks: Box::new([Zeros]), marker: PhantomData }
ChunkedBitSet {
domain_size: 1,
chunks: Box::new([Zeros { chunk_domain_size: 1 }]),
marker: PhantomData
}
);
assert_eq!(b1.chunk_domain_size(0), 1);
b1.assert_valid();
assert!(!b1.contains(0));
@@ -130,12 +133,12 @@ fn chunked_bitset() {
assert!(b1.insert(0));
assert!(b1.contains(0));
assert_eq!(b1.count(), 1);
assert_eq!(b1.chunks(), [Ones]);
assert_eq!(b1.chunks(), [Ones { chunk_domain_size: 1 }]);
assert!(!b1.insert(0));
assert!(b1.remove(0));
assert!(!b1.contains(0));
assert_eq!(b1.count(), 0);
assert_eq!(b1.chunks(), [Zeros]);
assert_eq!(b1.chunks(), [Zeros { chunk_domain_size: 1 }]);
b1.assert_valid();
//-----------------------------------------------------------------------
@@ -143,9 +146,12 @@ fn chunked_bitset() {
let mut b100 = ChunkedBitSet::<usize>::new_filled(100);
assert_eq!(
b100,
ChunkedBitSet { domain_size: 100, chunks: Box::new([Ones]), marker: PhantomData }
ChunkedBitSet {
domain_size: 100,
chunks: Box::new([Ones { chunk_domain_size: 100 }]),
marker: PhantomData
}
);
assert_eq!(b100.chunk_domain_size(0), 100);
b100.assert_valid();
for i in 0..100 {
@@ -154,7 +160,7 @@ fn chunked_bitset() {
assert_eq!(b100.count(), 100);
assert!(b100.remove(3));
assert!(b100.insert(3));
assert_eq!(b100.chunks(), vec![Ones]);
assert_eq!(b100.chunks(), vec![Ones { chunk_domain_size: 100 }]);
assert!(
b100.remove(20) && b100.remove(30) && b100.remove(40) && b100.remove(99) && b100.insert(30)
);
@@ -162,9 +168,10 @@ fn chunked_bitset() {
assert!(!b100.contains(20) && b100.contains(30) && !b100.contains(99) && b100.contains(50));
assert_eq!(
b100.chunks(),
#[rustfmt::skip]
vec![Mixed {
chunk_domain_size: 100,
ones_count: 97,
#[rustfmt::skip]
words: Rc::new([
0b11111111_11111111_11111110_11111111_11111111_11101111_11111111_11111111,
0b00000000_00000000_00000000_00000111_11111111_11111111_11111111_11111111,
@@ -181,7 +188,7 @@ fn chunked_bitset() {
}
}
assert_eq!(num_removed, 97);
assert_eq!(b100.chunks(), vec![Zeros]);
assert_eq!(b100.chunks(), vec![Zeros { chunk_domain_size: 100 }]);
b100.assert_valid();
//-----------------------------------------------------------------------
@@ -189,21 +196,29 @@ fn chunked_bitset() {
let mut b2548 = ChunkedBitSet::<usize>::new_empty(2548);
assert_eq!(
b2548,
ChunkedBitSet { domain_size: 2548, chunks: Box::new([Zeros, Zeros]), marker: PhantomData }
ChunkedBitSet {
domain_size: 2548,
chunks: Box::new([Zeros { chunk_domain_size: 2048 }, Zeros { chunk_domain_size: 500 }]),
marker: PhantomData
}
);
assert_eq!(b2548.chunk_domain_size(0), 2048);
assert_eq!(b2548.chunk_domain_size(1), 500);
b2548.assert_valid();
b2548.insert(14);
b2548.remove(14);
assert_eq!(b2548.chunks(), vec![Zeros, Zeros]);
assert_eq!(
b2548.chunks(),
vec![Zeros { chunk_domain_size: 2048 }, Zeros { chunk_domain_size: 500 }]
);
b2548.insert_all();
for i in 0..2548 {
assert!(b2548.contains(i));
}
assert_eq!(b2548.count(), 2548);
assert_eq!(b2548.chunks(), vec![Ones, Ones]);
assert_eq!(
b2548.chunks(),
vec![Ones { chunk_domain_size: 2048 }, Ones { chunk_domain_size: 500 }]
);
b2548.assert_valid();
//-----------------------------------------------------------------------
@@ -211,10 +226,15 @@ fn chunked_bitset() {
let mut b4096 = ChunkedBitSet::<usize>::new_empty(4096);
assert_eq!(
b4096,
ChunkedBitSet { domain_size: 4096, chunks: Box::new([Zeros, Zeros]), marker: PhantomData }
ChunkedBitSet {
domain_size: 4096,
chunks: Box::new([
Zeros { chunk_domain_size: 2048 },
Zeros { chunk_domain_size: 2048 }
]),
marker: PhantomData
}
);
assert_eq!(b4096.chunk_domain_size(0), 2048);
assert_eq!(b4096.chunk_domain_size(1), 2048);
b4096.assert_valid();
for i in 0..4096 {
@@ -228,14 +248,22 @@ fn chunked_bitset() {
b4096.chunks(),
#[rustfmt::skip]
vec![
Mixed { ones_count: 1, words:Rc::new([
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
])},
Mixed { ones_count: 1, words: Rc::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x8000_0000_0000_0000
])},
Mixed {
chunk_domain_size: 2048,
ones_count: 1,
words: Rc::new([
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
])
},
Mixed {
chunk_domain_size: 2048,
ones_count: 1,
words: Rc::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x8000_0000_0000_0000
])
},
],
);
assert_eq!(b4096.count(), 2);
@@ -248,15 +276,16 @@ fn chunked_bitset() {
b10000,
ChunkedBitSet {
domain_size: 10000,
chunks: Box::new([Zeros, Zeros, Zeros, Zeros, Zeros,]),
chunks: Box::new([
Zeros { chunk_domain_size: 2048 },
Zeros { chunk_domain_size: 2048 },
Zeros { chunk_domain_size: 2048 },
Zeros { chunk_domain_size: 2048 },
Zeros { chunk_domain_size: 1808 }
]),
marker: PhantomData,
}
);
assert_eq!(b10000.chunk_domain_size(0), 2048);
assert_eq!(b10000.chunk_domain_size(1), 2048);
assert_eq!(b10000.chunk_domain_size(2), 2048);
assert_eq!(b10000.chunk_domain_size(3), 2048);
assert_eq!(b10000.chunk_domain_size(4), 1808);
b10000.assert_valid();
assert!(b10000.insert(3000) && b10000.insert(5000));
@@ -264,17 +293,25 @@ fn chunked_bitset() {
b10000.chunks(),
#[rustfmt::skip]
vec![
Zeros,
Mixed { ones_count: 1, words: Rc::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0100_0000_0000_0000, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
])},
Mixed { ones_count: 1, words: Rc::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0100, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
])},
Zeros,
Zeros,
Zeros { chunk_domain_size: 2048 },
Mixed {
chunk_domain_size: 2048,
ones_count: 1,
words: Rc::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0100_0000_0000_0000, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
])
},
Mixed {
chunk_domain_size: 2048,
ones_count: 1,
words: Rc::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0100, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
])
},
Zeros { chunk_domain_size: 2048 },
Zeros { chunk_domain_size: 1808 },
],
);
let mut b10000b = ChunkedBitSet::<usize>::new_empty(10000);