use std::io::{self, BufWriter, Write};
use std::path::{Path, PathBuf};
use croaring::{Bitmap, Portable};
use crate::core::core::pmmr::{bintree_postorder_height, family, path};
use crate::{read_bitmap, save_via_temp_file};
pub struct PruneList {
path: Option<PathBuf>,
bitmap: Bitmap,
pruned_cache: Bitmap,
shift_cache: Vec<u64>,
leaf_shift_cache: Vec<u64>,
}
impl PruneList {
pub fn new(path: Option<PathBuf>, mut bitmap: Bitmap) -> PruneList {
bitmap.remove(0);
PruneList {
path,
bitmap,
pruned_cache: Bitmap::new(),
shift_cache: vec![],
leaf_shift_cache: vec![],
}
}
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()
};
let mut prune_list = PruneList::new(Some(file_path), bitmap);
prune_list.init_caches();
if !prune_list.bitmap.is_empty() {
debug!("bitmap {} pos ({} bytes), pruned_cache {} pos ({} bytes), shift_cache {}, leaf_shift_cache {}",
prune_list.bitmap.cardinality(),
prune_list.bitmap.get_serialized_size_in_bytes::<Portable>(),
prune_list.pruned_cache.cardinality(),
prune_list.pruned_cache.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();
self.build_pruned_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", |w| {
let mut w = BufWriter::new(w);
w.write_all(&self.bitmap.serialize::<Portable>())?;
w.flush()
})?;
}
self.init_caches();
Ok(())
}
pub fn get_total_shift(&self) -> u64 {
self.get_shift(self.bitmap.maximum().unwrap_or_default() as u64)
}
pub fn get_total_leaf_shift(&self) -> u64 {
self.get_leaf_shift(self.bitmap.maximum().unwrap_or_default() as u64)
}
pub fn get_shift(&self, pos: u64) -> u64 {
if self.bitmap.is_empty() {
return 0;
}
let idx = self.bitmap.rank(pos as u32);
if idx == 0 {
return 0;
}
if idx > self.shift_cache.len() as u64 {
self.shift_cache[self.shift_cache.len().saturating_sub(1)]
} else {
self.shift_cache[(idx as usize).saturating_sub(1)]
}
}
fn build_shift_cache(&mut self) {
if self.bitmap.is_empty() {
return;
}
self.shift_cache.clear();
for pos in self.bitmap.iter().filter(|x| *x > 0) {
let pos = pos as u64;
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);
2 * ((1 << height) - 1)
} else {
0
};
self.shift_cache.push(prev_shift + curr_shift);
}
}
pub fn get_leaf_shift(&self, pos: u64) -> u64 {
if self.bitmap.is_empty() {
return 0;
}
let idx = self.bitmap.rank(pos as u32);
if idx == 0 {
return 0;
}
if idx > self.leaf_shift_cache.len() as u64 {
self.leaf_shift_cache[self.leaf_shift_cache.len().saturating_sub(1)]
} else {
self.leaf_shift_cache[(idx as usize).saturating_sub(1)]
}
}
fn build_leaf_shift_cache(&mut self) {
if self.bitmap.is_empty() {
return;
}
self.leaf_shift_cache.clear();
for pos in self.bitmap.iter().filter(|x| *x > 0) {
let pos = pos as u64;
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);
if height == 0 {
0
} else {
1 << height
}
} else {
0
};
self.leaf_shift_cache.push(prev_shift + curr_shift);
}
}
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);
if self.bitmap.contains(sibling as u32) || self.pruned_cache.contains(sibling as u32) {
self.pruned_cache.add(current as u32);
self.bitmap.remove(sibling as u32);
current = parent;
} else {
self.pruned_cache.add(current as u32);
self.bitmap.add(current as u32);
break;
}
}
}
pub fn len(&self) -> u64 {
self.bitmap.cardinality()
}
pub fn is_empty(&self) -> bool {
self.bitmap.is_empty()
}
pub fn to_vec(&self) -> Vec<u64> {
self.bitmap.iter().map(|x| x as u64).collect()
}
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)
}
fn build_pruned_cache(&mut self) {
if self.bitmap.is_empty() {
return;
}
self.pruned_cache =
Bitmap::with_container_capacity(self.bitmap.maximum().unwrap_or_default());
for pos in 1..=self.bitmap.maximum().unwrap_or_default() {
let path = path(pos as u64, self.bitmap.maximum().unwrap_or_default() as u64);
let pruned = path.into_iter().any(|x| self.bitmap.contains(x as u32));
if pruned {
self.pruned_cache.add(pos as u32)
}
}
self.pruned_cache.run_optimize();
}
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)
}
}