use std::collections::BTreeMap;
use crate::container::{Descriptor, ObjectSource};
use crate::error::{Error, Result};
use crate::store::{Id, ObjectResolver, ObjectStore, hydrate};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RootAccount {
pub root_bytes: u64,
pub standalone_bytes: u64,
pub reachable_objects: u64,
pub amortized_bytes: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountReport {
pub roots: Vec<RootAccount>,
pub standalone_bytes: u64,
pub unique_reachable_bytes: u64,
pub amortized_bytes: u64,
pub unique_objects: u64,
pub unique_object_bytes: u64,
pub dangling: Vec<Id>,
}
pub fn account<R: ObjectResolver + ObjectStore>(
roots: &[Descriptor],
store: &R,
) -> Result<AccountReport> {
let mut objects: BTreeMap<Id, (u64, Vec<usize>)> = BTreeMap::new();
for (i, root) in roots.iter().enumerate() {
for src in &root.objects {
if let ObjectSource::External { id, len } = src {
let entry = objects.entry(*id).or_insert((*len, Vec::new()));
if !entry.1.contains(&i) {
entry.1.push(i);
}
}
}
}
let mut dangling: Vec<Id> = Vec::new();
for id in objects.keys() {
if !store.contains(id)? {
dangling.push(*id);
}
}
if !dangling.is_empty() {
return Err(Error::missing_external_object(format!(
"{} reachable object(s) are not present in the store; the closure is not valid",
dangling.len()
)));
}
let mut roots_out: Vec<RootAccount> = Vec::with_capacity(roots.len());
let mut standalone_bytes: u64 = 0;
let mut root_total: u64 = 0;
for root in roots {
let root_bytes = root.serialize()?.0.len() as u64;
root_total += root_bytes;
let mut standalone = root.clone();
hydrate(&mut standalone, store)?;
let standalone_len = standalone.serialize()?.0.len() as u64;
standalone_bytes += standalone_len;
roots_out.push(RootAccount {
root_bytes,
standalone_bytes: standalone_len,
reachable_objects: 0,
amortized_bytes: root_bytes,
});
}
let mut unique_object_bytes: u64 = 0;
for (len, refs) in objects.values() {
unique_object_bytes += *len;
let k = refs.len() as u64;
if k == 0 {
continue;
}
let q = len / k;
let r = len % k;
for (j, &root_index) in refs.iter().enumerate() {
let mut add = q;
if (j as u64) < r {
add += 1;
}
roots_out[root_index].amortized_bytes += add;
roots_out[root_index].reachable_objects += 1;
}
}
let unique_reachable_bytes = root_total + unique_object_bytes;
let amortized_bytes: u64 = roots_out.iter().map(|r| r.amortized_bytes).sum();
debug_assert_eq!(
amortized_bytes, unique_reachable_bytes,
"Σ amortized must equal unique reachable (the split telescopes)"
);
Ok(AccountReport {
roots: roots_out,
standalone_bytes,
unique_reachable_bytes,
amortized_bytes,
unique_objects: objects.len() as u64,
unique_object_bytes,
dangling,
})
}