use super::arc_emit::{Emission, EmitError, emit_controlled};
use super::arc_sweep::{Probe, Slice, Sweep, SweepError, sweep_controlled};
use super::plan_graph::{Components, GraphError, PlanGraph};
use super::store::AuthorityError;
use formualizer_common::ExcelError;
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum TopologyError {
Authority(AuthorityError),
Sweep(SweepError),
Emit(EmitError),
Graph(GraphError),
InvalidWitness,
}
impl From<AuthorityError> for TopologyError {
fn from(e: AuthorityError) -> Self {
Self::Authority(e)
}
}
#[derive(Debug)]
pub(crate) struct Topology {
pub sweep: Sweep,
pub emission: Emission,
pub graph: PlanGraph,
pub components: Components,
pub peak_heap_bytes: u64,
}
impl Topology {
pub fn heap_bytes(&self) -> u64 {
self.sweep.heap_bytes()
+ self.emission.heap_bytes()
+ self.graph.heap_bytes()
+ self.components.heap_bytes()
}
pub fn total_work(&self) -> u64 {
self.sweep.work.total()
+ self.emission.work.total()
+ self.graph.work.total()
+ self.components.work.total()
}
}
fn add(a: u64, b: u64) -> Result<u64, AuthorityError> {
a.checked_add(b).ok_or(AuthorityError::Alloc)
}
fn remaining(limit: Option<u64>, held: u64) -> Result<Option<u64>, AuthorityError> {
limit
.map(|limit| {
limit.checked_sub(held).ok_or(AuthorityError::Admission {
resource: "scratch",
needed: held,
limit,
})
})
.transpose()
}
pub(crate) fn topology(
slices: &[Slice],
probes: &[Probe],
scratch_limit: Option<u64>,
arc_limit: Option<u64>,
discovery_limit: Option<u64>,
) -> Result<Topology, TopologyError> {
topology_controlled(
slices,
probes,
scratch_limit,
arc_limit,
discovery_limit,
|_| Ok(()),
)
}
pub(crate) fn topology_controlled(
slices: &[Slice],
probes: &[Probe],
scratch_limit: Option<u64>,
arc_limit: Option<u64>,
discovery_limit: Option<u64>,
mut checkpoint: impl FnMut(u64) -> Result<(), ExcelError>,
) -> Result<Topology, TopologyError> {
let sweep = sweep_controlled(
slices,
probes,
scratch_limit,
discovery_limit,
&mut checkpoint,
)
.map_err(TopologyError::Sweep)?;
let mut peak = sweep.peak_heap_bytes;
let mut held = sweep.heap_bytes();
let emission = emit_controlled(
slices.len(),
&sweep.columns,
&sweep.hits,
remaining(scratch_limit, held)?,
arc_limit,
&mut checkpoint,
)
.map_err(TopologyError::Emit)?;
peak = peak.max(add(held, emission.peak_heap_bytes)?);
held = add(held, emission.heap_bytes())?;
let graph = PlanGraph::build_controlled(
emission.nodes,
&emission.arcs,
remaining(scratch_limit, held)?,
&mut checkpoint,
)
.map_err(TopologyError::Graph)?;
peak = peak.max(add(held, graph.peak_heap_bytes)?);
held = add(held, graph.heap_bytes())?;
let components = graph
.components_controlled(remaining(scratch_limit, held)?, &mut checkpoint)
.map_err(TopologyError::Graph)?;
peak = peak.max(add(held, components.peak_heap_bytes)?);
Ok(Topology {
sweep,
emission,
graph,
components,
peak_heap_bytes: peak,
})
}