Skip to content

Commit

Permalink
stretchy roots tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffwashington committed Apr 26, 2021
1 parent 04e6aeb commit 0aa2aae
Showing 1 changed file with 64 additions and 33 deletions.
97 changes: 64 additions & 33 deletions runtime/src/accounts_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ pub struct RollingBitField {
max: u64, // exclusive
bits: BitVec,
count: usize,
excess: HashSet<u64>,
}
// functionally similar to a hashset
// Relies on there being a sliding window of key values. The key values continue to increase.
Expand All @@ -202,6 +203,7 @@ impl RollingBitField {
count: 0,
min: 0,
max: 0,
excess: HashSet::new(),
}
}

Expand All @@ -210,50 +212,79 @@ impl RollingBitField {
key % self.max_width
}

fn check_range(&self, key: u64) {
assert!(
self.count == 0
|| (self.max.saturating_sub(key) <= self.max_width as u64
&& key.saturating_sub(self.min) < self.max_width as u64),
"out of range: count: {}, min: {}, max: {}, width: {}, key: {}",
self.count,
self.min,
self.max,
self.max_width,
key
);
}

pub fn range_width(&self) -> u64 {
// note that max isn't updated on remove, so it can be above the current max
self.max - self.min
}

pub fn insert(&mut self, key: u64) {
self.check_range(key);
let address = self.get_address(&key);
let value = self.bits.get(address);
if !value {
self.bits.set(address, true);
if self.count == 0 {
self.min = key;
self.max = key + 1;
} else {
self.min = std::cmp::min(self.min, key);
self.max = std::cmp::max(self.max, key + 1);
let bits_empty = self.count == 0 || self.count == self.excess.len();
let update_bits = if bits_empty {
true // nothing in bits, so in range
} else if key < self.min {
// bits not empty and this insert is before min, so add to excess
if self.excess.insert(key) {
self.count += 1;
}
false
} else if key < self.max {
true // fits current bit field range
} else {
// key is >= max
let new_max = key + 1;
loop {
let new_width = new_max.saturating_sub(self.min);
if new_width <= self.max_width {
// this key will fit the max range
break;
}

// have to move min items from bits to excess to make room for this new max
let inserted = self.excess.insert(self.min);
assert!(inserted);

let key = self.min;
let address = self.get_address(&key);
self.bits.set(address, false);
self.purge(&key);
}

true // moved things to excess if necessary
};

if update_bits {
let address = self.get_address(&key);
let value = self.bits.get(address);
if !value {
self.bits.set(address, true);
if bits_empty {
self.min = key;
self.max = key + 1;
} else {
self.min = std::cmp::min(self.min, key);
self.max = std::cmp::max(self.max, key + 1);
}
self.count += 1;
}
self.count += 1;
}
}

pub fn remove(&mut self, key: &u64) {
self.check_range(*key);
let address = self.get_address(key);
let value = self.bits.get(address);
if value {
self.count -= 1;
self.bits.set(address, false);
self.purge(key);
if key >= &self.min {
// if asked to remove something bigger than max, then no-op
if key < &self.max {
let address = self.get_address(key);
if self.bits.get(address) {
self.count -= 1;
self.bits.set(address, false);
self.purge(key);
}
}
} else {
// asked to remove something < min. would be in excess if it exists
if self.excess.remove(key) {
self.count -= 1;
}
}
}

Expand Down

0 comments on commit 0aa2aae

Please sign in to comment.