use std::path::{Path, PathBuf};
use croaring::{Bitmap, Portable};
use crate::core::core::hash::Hashed;
use crate::core::core::pmmr;
use crate::core::core::BlockHeader;
use crate::prune_list::PruneList;
use crate::{read_bitmap, save_via_temp_file};
use std::fs::File;
use std::io::{self, BufWriter, Write};
pub struct LeafSet {
path: PathBuf,
bitmap: Bitmap,
bitmap_bak: Bitmap,
}
impl LeafSet {
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<LeafSet> {
let file_path = path.as_ref();
let bitmap = if file_path.exists() {
read_bitmap(&file_path)?
} else {
Bitmap::new()
};
if !bitmap.is_empty() {
debug!(
"bitmap {} pos ({} bytes)",
bitmap.cardinality(),
bitmap.get_serialized_size_in_bytes::<Portable>(),
);
}
Ok(LeafSet {
path: file_path.to_path_buf(),
bitmap_bak: bitmap.clone(),
bitmap,
})
}
pub fn copy_snapshot<P: AsRef<Path>>(path: P, cp_path: P) -> io::Result<()> {
let cp_file_path = cp_path.as_ref();
if !cp_file_path.exists() {
debug!(
"leaf_set: rewound leaf file not found: {}",
cp_file_path.display()
);
return Ok(());
}
let bitmap = read_bitmap(&cp_file_path)?;
debug!(
"leaf_set: copying rewound file {} to {}",
cp_file_path.display(),
path.as_ref().display()
);
let mut leaf_set = LeafSet {
path: path.as_ref().to_path_buf(),
bitmap_bak: bitmap.clone(),
bitmap,
};
leaf_set.flush()?;
Ok(())
}
fn unpruned_pre_cutoff(&self, cutoff_pos: u64, prune_list: &PruneList) -> Bitmap {
(1..=cutoff_pos)
.filter(|&x| pmmr::is_leaf(x))
.filter(|&x| !prune_list.is_pruned(x))
.map(|x| x as u32)
.collect()
}
pub fn removed_pre_cutoff(
&self,
cutoff_pos: u64,
rewind_rm_pos: &Bitmap,
prune_list: &PruneList,
) -> Bitmap {
let mut bitmap = self.bitmap.clone();
let to_remove = ((cutoff_pos + 1) as u32)..bitmap.maximum().unwrap_or_default();
bitmap.remove_range(to_remove);
bitmap.or_inplace(&rewind_rm_pos);
bitmap
.flip(1..(cutoff_pos + 1) as u32)
.and(&self.unpruned_pre_cutoff(cutoff_pos, prune_list))
}
pub fn rewind(&mut self, cutoff_pos: u64, rewind_rm_pos: &Bitmap) {
let to_remove = ((cutoff_pos + 1) as u32)..self.bitmap.maximum().unwrap_or_default();
self.bitmap.remove_range(to_remove);
self.bitmap.or_inplace(&rewind_rm_pos);
}
pub fn add(&mut self, pos: u64) {
self.bitmap.add(pos as u32);
}
pub fn remove(&mut self, pos: u64) {
self.bitmap.remove(pos as u32);
}
pub fn snapshot(&self, header: &BlockHeader) -> io::Result<()> {
let mut cp_bitmap = self.bitmap.clone();
cp_bitmap.run_optimize();
let cp_path = format!("{}.{}", self.path.to_str().unwrap(), header.hash());
let mut file = BufWriter::new(File::create(cp_path)?);
file.write_all(&cp_bitmap.serialize::<Portable>())?;
file.flush()?;
Ok(())
}
pub fn flush(&mut self) -> io::Result<()> {
self.bitmap.run_optimize();
save_via_temp_file(&self.path, ".tmp", |w| {
let mut w = BufWriter::new(w);
w.write_all(&self.bitmap.serialize::<Portable>())?;
w.flush()
})?;
self.bitmap_bak = self.bitmap.clone();
Ok(())
}
pub fn discard(&mut self) {
self.bitmap = self.bitmap_bak.clone();
}
pub fn includes(&self, pos: u64) -> bool {
self.bitmap.contains(pos as u32)
}
pub fn len(&self) -> usize {
self.bitmap.cardinality() as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
self.bitmap.iter().map(|x| x as u64)
}
}