#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::hash::Hash;
#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet};
#[cfg(not(feature = "std"))]
use hashbrown::{HashMap, HashSet};
pub struct Cfg<N> {
entry: N,
exit: N,
nodes: Vec<N>,
succs: HashMap<N, Vec<N>>,
preds: HashMap<N, Vec<N>>,
}
impl<N: Eq + Hash + Clone> Cfg<N> {
pub fn builder(entry: N, exit: N) -> CfgBuilder<N> {
CfgBuilder::new(entry, exit)
}
pub fn entry(&self) -> &N {
&self.entry
}
pub fn exit(&self) -> &N {
&self.exit
}
pub fn nodes(&self) -> &[N] {
&self.nodes
}
pub fn successors(&self, node: &N) -> &[N] {
self.succs.get(node).map(Vec::as_slice).unwrap_or_default()
}
pub fn predecessors(&self, node: &N) -> &[N] {
self.preds.get(node).map(Vec::as_slice).unwrap_or_default()
}
}
pub struct CfgBuilder<N> {
entry: N,
exit: N,
nodes: Vec<N>,
seen: HashSet<N>,
succs: HashMap<N, Vec<N>>,
preds: HashMap<N, Vec<N>>,
}
impl<N: Eq + Hash + Clone> CfgBuilder<N> {
fn new(entry: N, exit: N) -> Self {
let mut builder = Self {
entry: entry.clone(),
exit: exit.clone(),
nodes: Vec::new(),
seen: HashSet::new(),
succs: HashMap::new(),
preds: HashMap::new(),
};
builder.register(entry);
builder.register(exit);
builder
}
#[must_use]
pub fn edge(mut self, from: N, to: N) -> Self {
self.register(from.clone());
self.register(to.clone());
let succs = self.succs.entry(from.clone()).or_default();
if !succs.contains(&to) {
succs.push(to.clone());
}
let preds = self.preds.entry(to).or_default();
if !preds.contains(&from) {
preds.push(from);
}
self
}
pub fn build(self) -> Cfg<N> {
Cfg {
entry: self.entry,
exit: self.exit,
nodes: self.nodes,
succs: self.succs,
preds: self.preds,
}
}
fn register(&mut self, node: N) {
if self.seen.insert(node.clone()) {
self.nodes.push(node);
}
}
}