mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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:
+122
-145
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user