Skip to content

Commit c9d46eb

Browse files
nikomatsakisMark-Simulacrum
authored andcommitted
Rework DepthFirstSearch API
This expands the API to be more flexible, allowing for more visitation patterns on graphs. This will be useful to avoid extra datasets (and allocations) in cases where the expanded DFS API is sufficient. This also fixes a bug with the previous DFS constructor, which left the start node not marked as visited (even though it was immediately returned).
1 parent 11bbb52 commit c9d46eb

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

Diff for: compiler/rustc_data_structures/src/graph/iterate/mod.rs

+52-2
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,58 @@ impl<G> DepthFirstSearch<'graph, G>
8383
where
8484
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
8585
{
86-
pub fn new(graph: &'graph G, start_node: G::Node) -> Self {
87-
Self { graph, stack: vec![start_node], visited: BitSet::new_empty(graph.num_nodes()) }
86+
pub fn new(graph: &'graph G) -> Self {
87+
Self { graph, stack: vec![], visited: BitSet::new_empty(graph.num_nodes()) }
88+
}
89+
90+
/// Version of `push_start_node` that is convenient for chained
91+
/// use.
92+
pub fn with_start_node(mut self, start_node: G::Node) -> Self {
93+
self.push_start_node(start_node);
94+
self
95+
}
96+
97+
/// Pushes another start node onto the stack. If the node
98+
/// has not already been visited, then you will be able to
99+
/// walk its successors (and so forth) after the current
100+
/// contents of the stack are drained. If multiple start nodes
101+
/// are added into the walk, then their mutual successors
102+
/// will all be walked. You can use this method once the
103+
/// iterator has been completely drained to add additional
104+
/// start nodes.
105+
pub fn push_start_node(&mut self, start_node: G::Node) {
106+
if self.visited.insert(start_node) {
107+
self.stack.push(start_node);
108+
}
109+
}
110+
111+
/// Searches all nodes reachable from the current start nodes.
112+
/// This is equivalent to just invoke `next` repeatedly until
113+
/// you get a `None` result.
114+
pub fn complete_search(&mut self) {
115+
while let Some(_) = self.next() {}
116+
}
117+
118+
/// Returns true if node has been visited thus far.
119+
/// A node is considered "visited" once it is pushed
120+
/// onto the internal stack; it may not yet have been yielded
121+
/// from the iterator. This method is best used after
122+
/// the iterator is completely drained.
123+
pub fn visited(&self, node: G::Node) -> bool {
124+
self.visited.contains(node)
125+
}
126+
}
127+
128+
impl<G> std::fmt::Debug for DepthFirstSearch<'_, G>
129+
where
130+
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
131+
{
132+
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133+
let mut f = fmt.debug_set();
134+
for n in self.visited.iter() {
135+
f.entry(&n);
136+
}
137+
f.finish()
88138
}
89139
}
90140

Diff for: compiler/rustc_data_structures/src/graph/iterate/tests.rs

+16
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,19 @@ fn is_cyclic() {
2020
assert!(!is_cyclic(&diamond_acyclic));
2121
assert!(is_cyclic(&diamond_cyclic));
2222
}
23+
24+
#[test]
25+
fn dfs() {
26+
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3), (3, 0)]);
27+
28+
let result: Vec<usize> = DepthFirstSearch::new(&graph).with_start_node(0).collect();
29+
assert_eq!(result, vec![0, 2, 3, 1]);
30+
}
31+
32+
#[test]
33+
fn dfs_debug() {
34+
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3), (3, 0)]);
35+
let mut dfs = DepthFirstSearch::new(&graph).with_start_node(0);
36+
dfs.complete_search();
37+
assert_eq!(format!("{{0, 1, 2, 3}}"), format!("{:?}", dfs));
38+
}

Diff for: compiler/rustc_data_structures/src/graph/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ where
3232
where
3333
Self: WithNumNodes,
3434
{
35-
iterate::DepthFirstSearch::new(self, from)
35+
iterate::DepthFirstSearch::new(self).with_start_node(from)
3636
}
3737
}
3838

0 commit comments

Comments
 (0)