#![forbid(unsafe_code)]
use crate::core::extent::ChunkId;
use crate::core::representation::Representation;
use crate::store::{ExtentUpdate, Store, StoreError};
pub const REBASE_DEPTH_THRESHOLD: u8 = 2;
pub const fn depth_of(desc: &Representation) -> u8 {
match desc {
Representation::ExactRef { .. } | Representation::BaseResidual { .. } => 1,
_ => 0,
}
}
pub fn chain_depth(store: &Store, desc: &Representation) -> u8 {
let limits = *store.limits();
let mut depth = 0u8;
let mut chain: Vec<Representation> = vec![desc.clone()];
while depth < limits.max_reference_depth {
let cur = &chain[chain.len() - 1];
let next_id = match cur {
Representation::ExactRef { target, .. } => Some(*target),
Representation::BaseResidual { base, .. } => Some(*base),
_ => None,
};
let Some(id) = next_id else { break };
let Some(desc_bytes) = store.chunk_descriptor(&id).ok().flatten() else {
break; };
let Ok(next) = crate::format::descriptor::decode(
&desc_bytes,
limits.max_descriptor_bytes,
limits.max_inline_bytes,
limits.max_palette,
limits.max_period,
limits.max_chunk_size,
) else {
break;
};
depth = depth.saturating_add(1);
chain.push(next);
}
depth
}
pub fn chain_contains(
store: &Store,
base: &crate::core::candidate::BaseChunk,
target: &ChunkId,
) -> bool {
let limits = *store.limits();
let mut visited: Vec<ChunkId> = Vec::new();
let mut cur_id = base.id;
for _ in 0..limits.max_reference_depth {
if &cur_id == target {
return true;
}
if visited.contains(&cur_id) {
return false; }
visited.push(cur_id);
let Some(desc_bytes) = store.chunk_descriptor(&cur_id).ok().flatten() else {
return false;
};
let Ok(desc) = crate::format::descriptor::decode(
&desc_bytes,
limits.max_descriptor_bytes,
limits.max_inline_bytes,
limits.max_palette,
limits.max_period,
limits.max_chunk_size,
) else {
return false;
};
let next = match &desc {
Representation::ExactRef { target: t, .. } => Some(*t),
Representation::BaseResidual { base: b, .. } => Some(*b),
_ => None,
};
let Some(next) = next else {
return false;
};
cur_id = next;
}
false
}
pub fn flatten_if_deep(
store: &Store,
start: u64,
desc: &Representation,
bytes: &[u8],
cid: &ChunkId,
) -> Result<Option<ExtentUpdate>, StoreError> {
if chain_depth(store, desc) < REBASE_DEPTH_THRESHOLD {
return Ok(None);
}
let limits = *store.limits();
let policy = *store.policy();
let update = Store::encode_chunk(bytes, start, *cid, &limits, &policy)?;
let back = crate::core::materialize::materialize_to_vec(&update.descriptor, store, &limits)
.map_err(|e| StoreError::Descriptor(e.to_string()))?;
if back != bytes {
return Ok(None); }
Ok(Some(update))
}