use std::convert::TryFrom;
use std::time::Instant;
use bit_vec::BitVec;
use croaring::Bitmap;
use crate::core::core::hash::{DefaultHashable, Hash};
use crate::core::core::pmmr::{self, ReadonlyPMMR, VecBackend, PMMR};
use crate::core::ser::{self, FixedLength, PMMRable, Readable, Reader, Writeable, Writer};
use crate::error::{Error, ErrorKind};
#[derive(Clone)]
pub struct BitmapAccumulator {
backend: VecBackend<BitmapChunk>,
}
impl BitmapAccumulator {
pub fn new() -> BitmapAccumulator {
BitmapAccumulator {
backend: VecBackend::new_hash_only(),
}
}
pub fn init<T: IntoIterator<Item = u64>>(&mut self, idx: T, size: u64) -> Result<(), Error> {
self.apply_from(idx, 0, size)
}
pub fn chunk_start_idx(idx: u64) -> u64 {
idx & !0x3ff
}
fn chunk_idx(idx: u64) -> u64 {
idx / 1024
}
fn apply_from<T>(&mut self, idx: T, from_idx: u64, size: u64) -> Result<(), Error>
where
T: IntoIterator<Item = u64>,
{
let now = Instant::now();
let from_chunk_idx = BitmapAccumulator::chunk_idx(from_idx);
let mut chunk_idx = from_chunk_idx;
let mut chunk = BitmapChunk::new();
let mut idx_iter = idx.into_iter().filter(|&x| x < size).peekable();
while let Some(x) = idx_iter.peek() {
if *x < chunk_idx * 1024 {
idx_iter.next();
} else if *x < (chunk_idx + 1) * 1024 {
let idx = idx_iter.next().expect("next after peek");
chunk.set(idx % 1024, true);
} else {
self.append_chunk(chunk)?;
chunk_idx += 1;
chunk = BitmapChunk::new();
}
}
if chunk.any() {
self.append_chunk(chunk)?;
}
debug!(
"applied {} chunks from idx {} to idx {} ({}ms)",
1 + chunk_idx - from_chunk_idx,
from_chunk_idx,
chunk_idx,
now.elapsed().as_millis(),
);
Ok(())
}
pub fn apply<T, U>(&mut self, invalidated_idx: T, idx: U, size: u64) -> Result<(), Error>
where
T: IntoIterator<Item = u64>,
U: IntoIterator<Item = u64>,
{
if let Some(from_idx) = invalidated_idx.into_iter().next() {
self.rewind_prior(from_idx)?;
self.pad_left(from_idx)?;
self.apply_from(idx, from_idx, size)?;
}
Ok(())
}
fn rewind_prior(&mut self, from_idx: u64) -> Result<(), Error> {
let chunk_idx = BitmapAccumulator::chunk_idx(from_idx);
let last_pos = self.backend.size();
let mut pmmr = PMMR::at(&mut self.backend, last_pos);
let chunk_pos = pmmr::insertion_to_pmmr_index(chunk_idx + 1);
let rewind_pos = chunk_pos.saturating_sub(1);
pmmr.rewind(rewind_pos, &Bitmap::create())
.map_err(|e| ErrorKind::Other(e))?;
Ok(())
}
fn pad_left(&mut self, from_idx: u64) -> Result<(), Error> {
let chunk_idx = BitmapAccumulator::chunk_idx(from_idx);
let current_chunk_idx = pmmr::n_leaves(self.backend.size());
for _ in current_chunk_idx..chunk_idx {
self.append_chunk(BitmapChunk::new())?;
}
Ok(())
}
pub fn append_chunk(&mut self, chunk: BitmapChunk) -> Result<u64, Error> {
let last_pos = self.backend.size();
PMMR::at(&mut self.backend, last_pos)
.push(&chunk)
.map_err(|e| ErrorKind::Other(e).into())
}
pub fn root(&self) -> Hash {
ReadonlyPMMR::at(&self.backend, self.backend.size()).root()
}
}
#[derive(Clone, Debug)]
pub struct BitmapChunk(BitVec);
impl BitmapChunk {
const LEN_BITS: usize = 1024;
const LEN_BYTES: usize = Self::LEN_BITS / 8;
pub fn new() -> BitmapChunk {
BitmapChunk(BitVec::from_elem(Self::LEN_BITS, false))
}
pub fn set(&mut self, idx: u64, value: bool) {
let idx = usize::try_from(idx).expect("usize from u64");
assert!(idx < Self::LEN_BITS);
self.0.set(idx, value)
}
pub fn any(&self) -> bool {
self.0.any()
}
}
impl PMMRable for BitmapChunk {
type E = Self;
fn as_elmt(&self) -> Self::E {
self.clone()
}
}
impl FixedLength for BitmapChunk {
const LEN: usize = Self::LEN_BYTES;
}
impl DefaultHashable for BitmapChunk {}
impl Writeable for BitmapChunk {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
self.0.to_bytes().write(writer)
}
}
impl Readable for BitmapChunk {
fn read(_reader: &mut dyn Reader) -> Result<BitmapChunk, ser::Error> {
Ok(BitmapChunk::new())
}
}