use std::sync::Arc;
use super::layer::Layer;
use super::{CommitGraph, GraphError, bad};
use crate::Objects;
impl Objects {
pub fn with_commit_graph(mut self, graph: CommitGraph) -> Result<Self, GraphError> {
self.attach_commit_graph(graph)?;
Ok(self)
}
pub fn without_commit_graph(mut self) -> Self {
self.commit_graph = None;
self
}
pub(crate) fn attach_commit_graph(&mut self, graph: CommitGraph) -> Result<(), GraphError> {
if graph.format != self.object_format() {
return Err(GraphError::HashFormat);
}
if let Err(reason) = graph.history {
return Err(bad(reason));
}
self.commit_graph = Some(Arc::new(graph));
Ok(())
}
}
pub(super) type Prepared = Result<HistoryIndex, &'static str>;
pub(super) fn prepare<B: AsRef<[u8]>>(layers: &[Layer<B>]) -> Prepared {
let count = layers
.last()
.map_or(0, |layer| layer.start() + layer.count());
let mut levels = Vec::with_capacity(count);
let mut dates = Vec::with_capacity(count);
let mut corrected = layers
.iter()
.all(|layer| layer.count() == 0 || layer.has_corrected_dates());
let mut topological = true;
for layer in layers {
for index in 0..layer.count() {
let level = layer.level(index);
topological &= level != 0 && level < 0x3fffffff;
levels.push(level);
if corrected {
dates.push(layer.corrected_date(index).unwrap_or(0));
}
}
}
let mut parent_starts = Vec::with_capacity(count + 1);
let mut parents = Vec::with_capacity(count);
parent_starts.push(0);
for layer in layers {
for index in 0..layer.count() {
let child = layer.start() + index;
let first = parents.len();
layer.parents_into(index, &mut parents);
for parent in &parents[first..] {
let parent = *parent as usize;
corrected = corrected && dates[parent] < dates[child];
topological = topological && levels[parent] < levels[child];
}
parent_starts.push(u32::try_from(parents.len()).map_err(|_| "parent count")?);
}
}
let (order, generations) = if corrected {
(GenerationOrder::CorrectedDates, dates)
} else if topological {
let levels = levels.into_iter().map(u64::from).collect();
(GenerationOrder::Topological, levels)
} else {
check_ancestry(&parent_starts, &parents)?;
return Ok(HistoryIndex::default());
};
Ok(HistoryIndex {
order,
generations,
parent_starts,
parents,
})
}
impl CommitGraph {
fn ordered(&self) -> Option<&HistoryIndex> {
self.history
.as_ref()
.ok()
.filter(|index| !matches!(index.order, GenerationOrder::Unavailable))
}
pub(crate) fn generation(&self, id: crate::ObjectId) -> Option<u64> {
let index = self.ordered()?;
Some(index.generations[self.locate(id)?])
}
pub(crate) fn position(&self, id: crate::ObjectId) -> Option<u32> {
self.ordered()?;
u32::try_from(self.locate(id)?).ok()
}
pub(crate) fn positions(&self) -> usize {
self.len()
}
pub(crate) fn id_at(&self, position: u32) -> crate::ObjectId {
let (layer, index) = self.layer_at(position as usize);
layer.id(index)
}
pub(crate) fn generation_at(&self, position: u32) -> u64 {
self.ordered()
.map_or(0, |index| index.generations[position as usize])
}
pub(crate) fn parent_positions(&self, position: u32) -> &[u32] {
let Some(index) = self.ordered() else {
return &[];
};
let position = position as usize;
let starts = &index.parent_starts;
&index.parents[starts[position] as usize..starts[position + 1] as usize]
}
}
fn check_ancestry(parent_starts: &[u32], parents: &[u32]) -> Result<(), &'static str> {
let count = parent_starts.len() - 1;
let mut state = vec![0u8; count];
let mut stack = Vec::new();
for root in 0..count {
if state[root] == 2 {
continue;
}
stack.push((root, false));
while let Some((index, exiting)) = stack.pop() {
if exiting {
state[index] = 2;
continue;
}
if state[index] == 2 {
continue;
}
if state[index] == 1 {
return Err("cyclic indexed ancestry");
}
state[index] = 1;
stack.push((index, true));
let range = parent_starts[index] as usize..parent_starts[index + 1] as usize;
for parent in parents[range].iter().rev() {
stack.push((*parent as usize, false));
}
}
}
Ok(())
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) enum GenerationOrder {
#[default]
Unavailable,
Topological,
CorrectedDates,
}
#[derive(Clone, Debug, Default)]
pub(crate) struct HistoryIndex {
order: GenerationOrder,
generations: Vec<u64>,
parent_starts: Vec<u32>,
parents: Vec<u32>,
}