Reuse dependency orderings in graph

This avoids a potential panic when adding new nodes to the graph, as
there is no feasible way to overflow IDs any more.
This commit is contained in:
2023-08-27 15:48:57 +02:00
parent 0ae544a07a
commit 909e934572

View File

@@ -25,8 +25,9 @@ where
V: Eq + Hash + Copy, V: Eq + Hash + Copy,
{ {
nodes: HashMap<V, Node<V>>, nodes: HashMap<V, Node<V>>,
/// Next topological sort order // Instead of reordering the orders in the graph whenever a node is deleted, we maintain a list
next_ord: Order, // of unused ids that can be handed out later again.
unused_order: Vec<Order>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -55,11 +56,17 @@ where
/// ///
/// New nodes are appended to the end of the topological order when added. /// New nodes are appended to the end of the topological order when added.
fn add_node(&mut self, n: V) -> (&mut HashSet<V>, &mut HashSet<V>, Order) { fn add_node(&mut self, n: V) -> (&mut HashSet<V>, &mut HashSet<V>, Order) {
let next_ord = &mut self.next_ord; // need to compute next id before the call to entry() to avoid duplicate borrow of nodes
let fallback_id = self.nodes.len();
let node = self.nodes.entry(n).or_insert_with(|| { let node = self.nodes.entry(n).or_insert_with(|| {
let order = *next_ord; let order = if let Some(id) = self.unused_order.pop() {
*next_ord = next_ord.checked_add(1).expect("Topological order overflow"); // Reuse discarded ordering entry
id
} else {
// Allocate new order id
fallback_id
};
Node { Node {
ord: Cell::new(order), ord: Cell::new(order),
@@ -77,8 +84,11 @@ where
Some(Node { Some(Node {
out_edges, out_edges,
in_edges, in_edges,
.. ord,
}) => { }) => {
// Return ordering to the pool of unused ones
self.unused_order.push(ord.get());
out_edges.into_iter().for_each(|m| { out_edges.into_iter().for_each(|m| {
self.nodes.get_mut(&m).unwrap().in_edges.remove(&n); self.nodes.get_mut(&m).unwrap().in_edges.remove(&n);
}); });