use std::path::{Path, PathBuf};
use std::{
io::{self, Write},
ops::Range,
};
use croaring::{Bitmap, Portable};
use grin_core::core::pmmr;
use crate::grin_core::core::pmmr::{bintree_leftmost, bintree_postorder_height, family};
use crate::{read_bitmap, save_via_temp_file};
use std::cmp::min;
#[derive(Debug)]
pub struct PruneList {
path: Option<PathBuf>,
bitmap: Bitmap,
shift_cache: Vec<u64>,
leaf_shift_cache: Vec<u64>,
}
impl PruneList {
pub fn new(path: Option<PathBuf>, bitmap: Bitmap) -> PruneList {
assert!(!bitmap.contains(0));
let mut prune_list = PruneList {
path,
bitmap: Bitmap::new(),
shift_cache: vec![],
leaf_shift_cache: vec![],
};
for pos1 in bitmap.iter() {
prune_list.append(pos1 as u64 - 1)
}
prune_list.bitmap.run_optimize();
prune_list
}
pub fn empty() -> PruneList {
PruneList::new(None, Bitmap::new())
}
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<PruneList> {
let file_path = PathBuf::from(path.as_ref());
let bitmap = if file_path.exists() {
read_bitmap(&file_path)?
} else {
Bitmap::new()
};
assert!(!bitmap.contains(0));
let mut prune_list = PruneList::new(Some(file_path), bitmap);
prune_list.init_caches();
if !prune_list.bitmap.is_empty() {
debug!(
"bitmap {} pos ({} bytes), shift_cache {}, leaf_shift_cache {}",
prune_list.bitmap.cardinality(),
prune_list.bitmap.get_serialized_size_in_bytes::<Portable>(),
prune_list.shift_cache.len(),
prune_list.leaf_shift_cache.len(),
);
}
Ok(prune_list)
}
pub fn init_caches(&mut self) {
self.build_shift_cache();
self.build_leaf_shift_cache();
}
pub fn flush(&mut self) -> io::Result<()> {
self.bitmap.run_optimize();
if let Some(ref path) = self.path {
save_via_temp_file(path, ".tmp", |file| {
file.write_all(&self.bitmap.serialize::<Portable>())
})?;
}
Ok(())
}
pub fn get_total_shift(&self) -> u64 {
self.get_shift(self.bitmap.maximum().unwrap_or(1) as u64 - 1)
}
pub fn get_total_leaf_shift(&self) -> u64 {
self.get_leaf_shift(self.bitmap.maximum().unwrap_or(1) as u64 - 1)
}
pub fn get_shift(&self, pos0: u64) -> u64 {
let idx = self.bitmap.rank(1 + pos0 as u32);
if idx == 0 {
return 0;
}
self.shift_cache[min(idx as usize, self.shift_cache.len()) - 1]
}
fn build_shift_cache(&mut self) {
self.shift_cache.clear();
for pos1 in self.bitmap.iter() {
let pos0 = pos1 as u64 - 1;
let prev_shift = if pos0 == 0 {
0
} else {
self.get_shift(pos0 - 1)
};
let curr_shift = if self.is_pruned_root(pos0) {
let height = bintree_postorder_height(pos0);
2 * ((1 << height) - 1)
} else {
0
};
self.shift_cache.push(prev_shift + curr_shift);
}
}
fn calculate_next_shift(&self, pos0: u64) -> u64 {
let prev_shift = if pos0 == 0 {
0
} else {
self.get_shift(pos0 - 1)
};
let shift = if self.is_pruned_root(pos0) {
let height = bintree_postorder_height(pos0);
2 * ((1 << height) - 1)
} else {
0
};
prev_shift + shift
}
pub fn get_leaf_shift(&self, pos0: u64) -> u64 {
let idx = self.bitmap.rank(1 + pos0 as u32);
if idx == 0 {
return 0;
}
self.leaf_shift_cache[min(idx as usize, self.leaf_shift_cache.len()) - 1]
}
fn build_leaf_shift_cache(&mut self) {
self.leaf_shift_cache.clear();
for pos1 in self.bitmap.iter() {
let pos0 = pos1 as u64 - 1;
let prev_shift = if pos0 == 0 {
0
} else {
self.get_leaf_shift(pos0 - 1)
};
let curr_shift = if self.is_pruned_root(pos0) {
let height = bintree_postorder_height(pos0);
if height == 0 {
0
} else {
1 << height
}
} else {
0
};
self.leaf_shift_cache.push(prev_shift + curr_shift);
}
}
fn calculate_next_leaf_shift(&self, pos0: u64) -> u64 {
let prev_shift = if pos0 == 0 {
0
} else {
self.get_leaf_shift(pos0 - 1)
};
let shift = if self.is_pruned_root(pos0) {
let height = bintree_postorder_height(pos0);
if height == 0 {
0
} else {
1 << height
}
} else {
0
};
prev_shift + shift
}
fn cleanup_subtree(&mut self, pos0: u64) {
let lc0 = bintree_leftmost(pos0) as u32;
let size = self.bitmap.maximum().unwrap_or(0);
if lc0 >= size {
return;
}
let cleanup_pos1 = (lc0 + 1)..=size;
let idx = self.bitmap.rank(lc0);
self.shift_cache.truncate(idx as usize);
self.leaf_shift_cache.truncate(idx as usize);
self.bitmap.remove_range(cleanup_pos1)
}
fn append_single(&mut self, pos0: u64) {
assert!(
pos0 >= self.bitmap.maximum().unwrap_or(0) as u64,
"prune list append only"
);
self.bitmap.add(1 + pos0 as u32);
self.shift_cache.push(self.calculate_next_shift(pos0));
self.leaf_shift_cache
.push(self.calculate_next_leaf_shift(pos0));
}
pub fn append(&mut self, pos0: u64) {
let max = self.bitmap.maximum().unwrap_or(0) as u64;
assert!(
pos0 >= max,
"prune list append only - pos={} bitmap.maximum={}",
pos0,
max
);
let (parent0, sibling0) = family(pos0);
if self.is_pruned(sibling0) {
self.append(parent0)
} else {
self.cleanup_subtree(pos0);
self.append_single(pos0);
}
}
pub fn len(&self) -> u64 {
self.bitmap.cardinality()
}
pub fn is_empty(&self) -> bool {
self.bitmap.is_empty()
}
pub fn is_pruned(&self, pos0: u64) -> bool {
if self.is_pruned_root(pos0) {
return true;
}
let rank = self.bitmap.rank(1 + pos0 as u32);
if let Some(root) = self.bitmap.select(rank as u32) {
let range = pmmr::bintree_range(root as u64 - 1);
range.contains(&pos0)
} else {
false
}
}
pub fn to_vec(&self) -> Vec<u64> {
self.bitmap.iter().map(|x| x as u64).collect()
}
pub fn shift_cache(&self) -> &[u64] {
self.shift_cache.as_slice()
}
pub fn leaf_shift_cache(&self) -> &[u64] {
self.leaf_shift_cache.as_slice()
}
pub fn is_pruned_root(&self, pos0: u64) -> bool {
self.bitmap.contains(1 + pos0 as u32)
}
pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
self.bitmap.iter().map(|x| x as u64)
}
pub fn pruned_bintree_range_iter(&self) -> impl Iterator<Item = Range<u64>> + '_ {
self.iter().map(|x| {
let rng = pmmr::bintree_range(x - 1);
(1 + rng.start)..(1 + rng.end)
})
}
pub fn unpruned_iter(&self, cutoff_pos: u64) -> impl Iterator<Item = u64> + '_ {
UnprunedIterator::new(self.pruned_bintree_range_iter())
.take_while(move |x| *x <= cutoff_pos)
}
pub fn unpruned_leaf_iter(&self, cutoff_pos: u64) -> impl Iterator<Item = u64> + '_ {
self.unpruned_iter(cutoff_pos)
.filter(|x| pmmr::is_leaf(*x - 1))
}
pub fn bitmap(&self) -> Bitmap {
self.bitmap.clone()
}
}
struct UnprunedIterator<I> {
inner: I,
current_excl_range: Option<Range<u64>>,
current_pos: u64,
}
impl<I: Iterator<Item = Range<u64>>> UnprunedIterator<I> {
fn new(mut inner: I) -> UnprunedIterator<I> {
let current_excl_range = inner.next();
UnprunedIterator {
inner,
current_excl_range,
current_pos: 1,
}
}
}
impl<I: Iterator<Item = Range<u64>>> Iterator for UnprunedIterator<I> {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
if let Some(range) = &self.current_excl_range {
if self.current_pos < range.start {
let next = self.current_pos;
self.current_pos += 1;
Some(next)
} else {
self.current_pos = range.end;
self.current_excl_range = self.inner.next();
self.next()
}
} else {
let next = self.current_pos;
self.current_pos += 1;
Some(next)
}
}
}