Adds `iter_ones()` to RollingBitField (#33956)
This commit is contained in:
parent
ee29647f67
commit
70d97d3261
|
@ -5335,6 +5335,7 @@ dependencies = [
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tar",
|
"tar",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"test-case",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ solana-accounts-db = { path = ".", features = ["dev-context-only-utils"] }
|
||||||
solana-logger = { workspace = true }
|
solana-logger = { workspace = true }
|
||||||
solana-sdk = { workspace = true, features = ["dev-context-only-utils"] }
|
solana-sdk = { workspace = true, features = ["dev-context-only-utils"] }
|
||||||
static_assertions = { workspace = true }
|
static_assertions = { workspace = true }
|
||||||
|
test-case = { workspace = true }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
//! Relies on there being a sliding window of key values. The key values continue to increase.
|
//! Relies on there being a sliding window of key values. The key values continue to increase.
|
||||||
//! Old key values are removed from the lesser values and do not accumulate.
|
//! Old key values are removed from the lesser values and do not accumulate.
|
||||||
|
|
||||||
use {bv::BitVec, solana_nohash_hasher::IntSet, solana_sdk::clock::Slot};
|
mod iterators;
|
||||||
|
use {
|
||||||
|
bv::BitVec, iterators::RollingBitFieldOnesIter, solana_nohash_hasher::IntSet,
|
||||||
|
solana_sdk::clock::Slot,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, AbiExample, Clone)]
|
#[derive(Debug, Default, AbiExample, Clone)]
|
||||||
pub struct RollingBitField {
|
pub struct RollingBitField {
|
||||||
|
@ -283,6 +287,14 @@ impl RollingBitField {
|
||||||
}
|
}
|
||||||
all
|
all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the rolling bit field
|
||||||
|
///
|
||||||
|
/// The iterator yields all the 'set' bits.
|
||||||
|
/// Note, the iteration order of the bits in 'excess' is not deterministic.
|
||||||
|
pub fn iter_ones(&self) -> RollingBitFieldOnesIter<'_> {
|
||||||
|
RollingBitFieldOnesIter::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
//! Iterators for RollingBitField
|
||||||
|
|
||||||
|
use {super::RollingBitField, std::ops::Range};
|
||||||
|
|
||||||
|
/// Iterate over the 'set' bits of a RollingBitField
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RollingBitFieldOnesIter<'a> {
|
||||||
|
rolling_bit_field: &'a RollingBitField,
|
||||||
|
excess_iter: std::collections::hash_set::Iter<'a, u64>,
|
||||||
|
bit_range: Range<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RollingBitFieldOnesIter<'a> {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(rolling_bit_field: &'a RollingBitField) -> Self {
|
||||||
|
Self {
|
||||||
|
rolling_bit_field,
|
||||||
|
excess_iter: rolling_bit_field.excess.iter(),
|
||||||
|
bit_range: rolling_bit_field.min..rolling_bit_field.max_exclusive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for RollingBitFieldOnesIter<'_> {
|
||||||
|
type Item = u64;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
// Iterate over the excess first
|
||||||
|
if let Some(excess) = self.excess_iter.next() {
|
||||||
|
return Some(*excess);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then iterate over the bit vec
|
||||||
|
loop {
|
||||||
|
// If there are no more bits in the range, then we've iterated over everything and are done
|
||||||
|
let Some(bit) = self.bit_range.next() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.rolling_bit_field.contains_assume_in_range(&bit) {
|
||||||
|
break Some(bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use {super::*, test_case::test_case};
|
||||||
|
|
||||||
|
#[test_case(128, vec![]; "empty")]
|
||||||
|
#[test_case(128, vec![128_007, 128_017, 128_107]; "without excess")]
|
||||||
|
#[test_case(128, vec![128_007, 128_017, 128_107, 3, 30, 300]; "with excess")]
|
||||||
|
// Even though these values are within the range, in an absolute sense,
|
||||||
|
// they will wrap around after multiples of 16.
|
||||||
|
#[test_case(16, vec![35, 40, 45 ])]
|
||||||
|
#[test_case(16, vec![ 40, 45, 50 ])]
|
||||||
|
#[test_case(16, vec![ 45, 50, 55 ])]
|
||||||
|
#[test_case(16, vec![ 50, 55, 60 ])]
|
||||||
|
#[test_case(16, vec![ 55, 60, 65 ])]
|
||||||
|
#[test_case(16, vec![ 60, 65, 70])]
|
||||||
|
fn test_rolling_bit_field_ones_iter(num_bits: u64, mut expected: Vec<u64>) {
|
||||||
|
let mut rolling_bit_field = RollingBitField::new(num_bits);
|
||||||
|
for val in &expected {
|
||||||
|
rolling_bit_field.insert(*val);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut actual: Vec<_> = rolling_bit_field.iter_ones().collect();
|
||||||
|
|
||||||
|
// Since iteration order of the 'excess' is not deterministic, sort the 'actual'
|
||||||
|
// and 'expected' vectors to ensure they can compare deterministically.
|
||||||
|
actual.sort_unstable();
|
||||||
|
expected.sort_unstable();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue