Prune list robustness (#2976)

* make prune list deserialization more robust (1-index vs 0 values)

* cleanup
This commit is contained in:
Antioch Peverell 2019-07-29 12:42:12 +01:00 committed by GitHub
parent d918c5fe84
commit aa5c428eb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 32 deletions

View file

@ -51,17 +51,25 @@ pub struct PruneList {
}
impl PruneList {
/// Instantiate a new empty prune list
pub fn new() -> PruneList {
/// Instantiate a new prune list from the provided path and bitmap.
pub fn new(path: Option<PathBuf>, mut bitmap: Bitmap) -> PruneList {
// Note: prune list is 1-indexed so remove any 0 value for safety.
bitmap.remove(0);
PruneList {
path: None,
bitmap: Bitmap::create(),
path,
bitmap,
pruned_cache: Bitmap::create(),
shift_cache: vec![],
leaf_shift_cache: vec![],
}
}
/// Instatiate a new empty prune list.
pub fn empty() -> PruneList {
PruneList::new(None, Bitmap::create())
}
/// Open an existing prune_list or create a new one.
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<PruneList> {
let file_path = PathBuf::from(path.as_ref());
@ -71,13 +79,7 @@ impl PruneList {
Bitmap::create()
};
let mut prune_list = PruneList {
path: Some(file_path),
bitmap,
pruned_cache: Bitmap::create(),
shift_cache: vec![],
leaf_shift_cache: vec![],
};
let mut prune_list = PruneList::new(Some(file_path), bitmap);
// Now built the shift and pruned caches from the bitmap we read from disk.
prune_list.init_caches();
@ -96,7 +98,8 @@ impl PruneList {
Ok(prune_list)
}
fn init_caches(&mut self) {
/// Init our internal shift caches.
pub fn init_caches(&mut self) {
self.build_shift_cache();
self.build_leaf_shift_cache();
self.build_pruned_cache();
@ -152,9 +155,9 @@ impl PruneList {
}
if idx > self.shift_cache.len() as u64 {
self.shift_cache[self.shift_cache.len() - 1]
self.shift_cache[self.shift_cache.len().saturating_sub(1)]
} else {
self.shift_cache[idx as usize - 1]
self.shift_cache[(idx as usize).saturating_sub(1)]
}
}
@ -164,9 +167,9 @@ impl PruneList {
}
self.shift_cache.clear();
for pos in self.bitmap.iter() {
for pos in self.bitmap.iter().filter(|x| *x > 0) {
let pos = pos as u64;
let prev_shift = self.get_shift(pos - 1);
let prev_shift = self.get_shift(pos.saturating_sub(1));
let curr_shift = if self.is_pruned_root(pos) {
let height = bintree_postorder_height(pos);
@ -193,9 +196,9 @@ impl PruneList {
}
if idx > self.leaf_shift_cache.len() as u64 {
self.leaf_shift_cache[self.leaf_shift_cache.len() - 1]
self.leaf_shift_cache[self.leaf_shift_cache.len().saturating_sub(1)]
} else {
self.leaf_shift_cache[idx as usize - 1]
self.leaf_shift_cache[(idx as usize).saturating_sub(1)]
}
}
@ -206,9 +209,9 @@ impl PruneList {
self.leaf_shift_cache.clear();
for pos in self.bitmap.iter() {
for pos in self.bitmap.iter().filter(|x| *x > 0) {
let pos = pos as u64;
let prev_shift = self.get_leaf_shift(pos - 1);
let prev_shift = self.get_leaf_shift(pos.saturating_sub(1));
let curr_shift = if self.is_pruned_root(pos) {
let height = bintree_postorder_height(pos);
@ -229,6 +232,8 @@ impl PruneList {
/// list if pruning the additional node means a parent can get pruned as
/// well.
pub fn add(&mut self, pos: u64) {
assert!(pos > 0, "prune list 1-indexed, 0 not valid pos");
let mut current = pos;
loop {
let (parent, sibling) = family(current);
@ -257,12 +262,13 @@ impl PruneList {
/// Convert the prune_list to a vec of pos.
pub fn to_vec(&self) -> Vec<u64> {
self.bitmap.to_vec().into_iter().map(|x| x as u64).collect()
self.bitmap.iter().map(|x| x as u64).collect()
}
/// Is the pos pruned?
/// Assumes the pruned_cache is fully built and up to date.
pub fn is_pruned(&self, pos: u64) -> bool {
assert!(pos > 0, "prune list 1-indexed, 0 not valid pos");
self.pruned_cache.contains(pos as u32)
}
@ -283,12 +289,7 @@ impl PruneList {
/// Is the specified position a root of a pruned subtree?
pub fn is_pruned_root(&self, pos: u64) -> bool {
assert!(pos > 0, "prune list 1-indexed, 0 not valid pos");
self.bitmap.contains(pos as u32)
}
}
impl Default for PruneList {
fn default() -> Self {
Self::new()
}
}

View file

@ -15,10 +15,26 @@
use grin_store as store;
use crate::store::prune_list::PruneList;
use croaring::Bitmap;
// Prune list is 1-indexed but we implement this internally with a bitmap that supports a 0 value.
// We need to make sure we safely handle 0 safely.
#[test]
fn test_zero_value() {
// Create a bitmap with a 0 value in it.
let mut bitmap = Bitmap::create();
bitmap.add(0);
// Instantiate a prune list from our existing bitmap.
let pl = PruneList::new(None, bitmap);
// Our prune list should be empty (0 filtered out during creation).
assert!(pl.is_empty());
}
#[test]
fn test_is_pruned() {
let mut pl = PruneList::new();
let mut pl = PruneList::empty();
assert_eq!(pl.len(), 0);
assert_eq!(pl.is_pruned(1), false);
@ -62,7 +78,7 @@ fn test_is_pruned() {
#[test]
fn test_get_leaf_shift() {
let mut pl = PruneList::new();
let mut pl = PruneList::empty();
// start with an empty prune list (nothing shifted)
assert_eq!(pl.len(), 0);
@ -135,7 +151,7 @@ fn test_get_leaf_shift() {
// now check we can prune some unconnected nodes in arbitrary order
// and that leaf_shift is correct for various pos
let mut pl = PruneList::new();
let mut pl = PruneList::empty();
pl.add(5);
pl.add(11);
pl.add(12);
@ -154,7 +170,7 @@ fn test_get_leaf_shift() {
#[test]
fn test_get_shift() {
let mut pl = PruneList::new();
let mut pl = PruneList::empty();
assert!(pl.is_empty());
assert_eq!(pl.get_shift(1), 0);
assert_eq!(pl.get_shift(2), 0);
@ -231,7 +247,7 @@ fn test_get_shift() {
// and check we shift by a large number (hopefully the correct number...)
assert_eq!(pl.get_shift(1010), 996);
let mut pl = PruneList::new();
let mut pl = PruneList::empty();
pl.add(9);
pl.add(8);
pl.add(5);