use std::collections::HashSet;
use std::time::Instant;
use knot_types::Oid;
use super::bitset::Bitset;
use super::reader::Bitmaps;
use super::revindex::RevIndex;
use knot_git::GitError;
use knot_git::Repo;
pub(super) struct Indexed<'a> {
pub(super) rev: &'a RevIndex,
pub(super) maps: &'a Bitmaps<'a>,
}
pub(super) fn count(
repo: &Repo,
tip: Oid,
deadline: Instant,
indexed: Option<Indexed<'_>>,
) -> Result<Option<u64>, GitError> {
let history = super::history::History::new(repo);
let mut bits = indexed.as_ref().map(|index| Bitset::zeros(index.rev.len()));
let mut outside = HashSet::new();
let mut commits = vec![tip];
while let Some(commit) = commits.pop() {
if Instant::now() >= deadline {
return Ok(None);
}
let located = match &indexed {
Some(index) => index.rev.locate(commit)?,
None => None,
};
match (located, &indexed, bits.as_mut()) {
(Some((position, bit)), Some(index), Some(bits)) => {
if bits.get(bit) {
continue;
}
if let Some(stored) = index.maps.bitmap(position)? {
bits.union_with(&stored);
continue;
}
bits.set(bit);
}
_ => {
if !outside.insert(commit) {
continue;
}
}
}
let (_, parents) = history.commit_tree_and_parents(commit)?;
commits.extend(parents.into_iter().map(Oid::from));
}
let in_bitmap = match (indexed, bits) {
(Some(index), Some(bits)) => {
let [commit_type, ..] = index.maps.types()?;
let mut mask = Bitset::zeros(index.rev.len());
mask.union_ewah(&commit_type, index.rev.len())?;
bits.count_common(&mask)
}
_ => 0,
};
Ok(Some(in_bitmap + outside.len() as u64))
}