diff options
Diffstat (limited to 'src/diff/dijkstra.rs')
-rw-r--r-- | src/diff/dijkstra.rs | 148 |
1 files changed, 81 insertions, 67 deletions
diff --git a/src/diff/dijkstra.rs b/src/diff/dijkstra.rs index 6e311da60..029c28465 100644 --- a/src/diff/dijkstra.rs +++ b/src/diff/dijkstra.rs @@ -5,11 +5,11 @@ use std::{cmp::Reverse, env}; use crate::{ diff::changes::ChangeMap, - diff::graph::{populate_change_map, set_neighbours, Edge, Vertex}, + diff::graph::{populate_change_map, set_neighbours, Edge, Vertex, VertexId}, hash::DftHashMap, parse::syntax::Syntax, }; -use bumpalo::Bump; +use id_arena::Arena; use itertools::Itertools; use radix_heap::RadixHeapMap; @@ -17,32 +17,36 @@ use radix_heap::RadixHeapMap; pub struct ExceededGraphLimit {} /// Return the shortest route from `start` to the end vertex. -fn shortest_vertex_path<'s, 'b>( - start: &'b Vertex<'s, 'b>, - vertex_arena: &'b Bump, +fn shortest_vertex_path<'s>( + start: VertexId<'s>, + vertex_arena: &mut Arena<Vertex<'s>>, size_hint: usize, graph_limit: usize, -) -> Result<Vec<&'b Vertex<'s, 'b>>, ExceededGraphLimit> { +) -> Result<Vec<VertexId<'s>>, ExceededGraphLimit> { // We want to visit nodes with the shortest distance first, but // RadixHeapMap is a max-heap. Ensure nodes are wrapped with // Reverse to flip comparisons. - let mut heap: RadixHeapMap<Reverse<_>, &'b Vertex<'s, 'b>> = RadixHeapMap::new(); + let mut heap: RadixHeapMap<Reverse<_>, VertexId<'s>> = RadixHeapMap::new(); heap.push(Reverse(0), start); let mut seen = DftHashMap::default(); seen.reserve(size_hint); - let end: &'b Vertex<'s, 'b> = loop { + let end: VertexId<'s> = loop { match heap.pop() { - Some((Reverse(distance), current)) => { - if current.is_end() { - break current; + Some((Reverse(distance), current_id)) => { + { + if (vertex_arena[current_id]).is_end() { + break current_id; + } } - set_neighbours(current, vertex_arena, &mut seen); + set_neighbours(current_id, vertex_arena, &mut seen); + let current = &vertex_arena[current_id]; for neighbour in current.neighbours.borrow().as_ref().unwrap() { - let (edge, next) = neighbour; + let (edge, next_id) = neighbour; + let next = &vertex_arena[*next_id]; let distance_to_next = distance + edge.cost(); let found_shorter_route = match next.predecessor.get() { @@ -51,15 +55,16 @@ fn shortest_vertex_path<'s, 'b>( }; if found_shorter_route { - next.predecessor.replace(Some((distance_to_next, current))); - heap.push(Reverse(distance_to_next), next); + next.predecessor + .replace(Some((distance_to_next, current_id))); + heap.push(Reverse(distance_to_next), *next_id); } } if seen.len() > graph_limit { info!( - "Reached graph limit, arena consumed {} bytes", - vertex_arena.allocated_bytes(), + "Reached graph limit, arena allocated {} items", + vertex_arena.len(), ); return Err(ExceededGraphLimit {}); } @@ -69,38 +74,39 @@ fn shortest_vertex_path<'s, 'b>( }; info!( - "Saw {} vertices (a Vertex is {} bytes), arena consumed {} bytes, with {} vertices left on heap.", + "Saw {} vertices (a Vertex is {} bytes), arena allocated {} items, with {} vertices left on heap.", seen.len(), std::mem::size_of::<Vertex>(), - vertex_arena.allocated_bytes(), + vertex_arena.len(), heap.len(), ); let mut current = Some((0, end)); - let mut vertex_route: Vec<&'b Vertex<'s, 'b>> = vec![]; - while let Some((_, node)) = current { - vertex_route.push(node); - current = node.predecessor.get(); + let mut vertex_route: Vec<VertexId<'s>> = vec![]; + while let Some((_, node_id)) = current { + vertex_route.push(node_id); + current = vertex_arena[node_id].predecessor.get(); } vertex_route.reverse(); Ok(vertex_route) } -fn shortest_path_with_edges<'s, 'b>( - route: &[&'b Vertex<'s, 'b>], -) -> Vec<(Edge, &'b Vertex<'s, 'b>)> { +fn shortest_path_with_edges<'s>( + route: &[VertexId<'s>], + vertex_arena: &Arena<Vertex<'s>>, +) -> Vec<(Edge, VertexId<'s>)> { let mut prev = route.first().expect("Expected non-empty route"); let mut cost = 0; let mut res = vec![]; - for vertex in route.iter().skip(1) { - let edge = edge_between(prev, vertex); + for vertex_id in route.iter().skip(1) { + let edge = edge_between(*prev, *vertex_id, vertex_arena); res.push((edge, *prev)); cost += edge.cost(); - prev = vertex; + prev = vertex_id; } debug!("Found a path of {} with cost {}.", route.len(), cost); @@ -111,27 +117,34 @@ fn shortest_path_with_edges<'s, 'b>( /// /// The vec returned does not return the very last vertex. This is /// necessary because a route of N vertices only has N-1 edges. -fn shortest_path<'s, 'b>( - start: Vertex<'s, 'b>, - vertex_arena: &'b Bump, +fn shortest_path<'s>( + start: Vertex<'s>, + vertex_arena: &mut Arena<Vertex<'s>>, size_hint: usize, graph_limit: usize, -) -> Result<Vec<(Edge, &'b Vertex<'s, 'b>)>, ExceededGraphLimit> { - let start: &'b Vertex<'s, 'b> = vertex_arena.alloc(start); +) -> Result<Vec<(Edge, VertexId<'s>)>, ExceededGraphLimit> { + let start: VertexId<'s> = vertex_arena.alloc(start); let vertex_path = shortest_vertex_path(start, vertex_arena, size_hint, graph_limit)?; - Ok(shortest_path_with_edges(&vertex_path)) + Ok(shortest_path_with_edges(&vertex_path, vertex_arena)) } -fn edge_between<'s, 'b>(before: &Vertex<'s, 'b>, after: &Vertex<'s, 'b>) -> Edge { - assert_ne!(before, after); +fn edge_between<'s>( + before_id: VertexId<'s>, + after_id: VertexId<'s>, + vertex_arena: &Arena<Vertex<'s>>, +) -> Edge { + assert_ne!(before_id, after_id); + let before = &vertex_arena[before_id]; + let after = &vertex_arena[after_id]; let mut shortest_edge: Option<Edge> = None; if let Some(neighbours) = &*before.neighbours.borrow() { for neighbour in neighbours { - let (edge, next) = *neighbour; + let (edge, next_id) = *neighbour; + // If there are multiple edges that can take us to `next`, // prefer the shortest. - if *next == *after { + if next_id == after_id { let is_shorter = match shortest_edge { Some(prev_edge) => edge.cost() < prev_edge.cost(), None => true, @@ -213,9 +226,9 @@ pub fn mark_syntax<'a>( let size_hint = std::cmp::min(lhs_node_count * rhs_node_count, graph_limit); let start = Vertex::new(lhs_syntax, rhs_syntax); - let vertex_arena = Bump::new(); + let mut vertex_arena = Arena::new(); - let route = shortest_path(start, &vertex_arena, size_hint, graph_limit)?; + let route = shortest_path(start, &mut vertex_arena, size_hint, graph_limit)?; let print_length = if env::var("DFT_VERBOSE").is_ok() { 50 @@ -227,7 +240,8 @@ pub fn mark_syntax<'a>( print_length, route .iter() - .map(|(edge, v)| { + .map(|(edge, v_id)| { + let v = &vertex_arena[*v_id]; format!( "{:20} {:20} --- {:3} {:?}", v.lhs_syntax @@ -242,7 +256,7 @@ pub fn mark_syntax<'a>( .collect_vec() ); - populate_change_map(&route, change_map); + populate_change_map(&route, &vertex_arena, change_map); Ok(()) } @@ -258,7 +272,7 @@ mod tests { }; use itertools::Itertools; - use typed_arena::Arena; + use typed_arena::Arena as TypedArena; fn pos_helper(line: u32) -> Vec<SingleLineSpan> { vec![SingleLineSpan { @@ -270,7 +284,7 @@ mod tests { #[test] fn identical_atoms() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = Syntax::new_atom(&arena, pos_helper(0), "foo", AtomKind::Normal); // Same content as LHS. @@ -278,8 +292,8 @@ mod tests { init_all_info(&[lhs], &[rhs]); let start = Vertex::new(Some(lhs), Some(rhs)); - let vertex_arena = Bump::new(); - let route = shortest_path(start, &vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); + let mut vertex_arena = Arena::<Vertex>::new(); + let route = shortest_path(start, &mut vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); let actions = route.iter().map(|(action, _)| *action).collect_vec(); assert_eq!( @@ -293,7 +307,7 @@ mod tests { #[test] fn extra_atom_lhs() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = vec![Syntax::new_list( &arena, @@ -320,8 +334,8 @@ mod tests { init_all_info(&lhs, &rhs); let start = Vertex::new(lhs.get(0).copied(), rhs.get(0).copied()); - let vertex_arena = Bump::new(); - let route = shortest_path(start, &vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); + let mut vertex_arena = Arena::new(); + let route = shortest_path(start, &mut vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); let actions = route.iter().map(|(action, _)| *action).collect_vec(); assert_eq!( @@ -337,7 +351,7 @@ mod tests { #[test] fn repeated_atoms() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = vec![Syntax::new_list( &arena, @@ -362,8 +376,8 @@ mod tests { init_all_info(&lhs, &rhs); let start = Vertex::new(lhs.get(0).copied(), rhs.get(0).copied()); - let vertex_arena = Bump::new(); - let route = shortest_path(start, &vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); + let mut vertex_arena = Arena::new(); + let route = shortest_path(start, &mut vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); let actions = route.iter().map(|(action, _)| *action).collect_vec(); assert_eq!( @@ -380,7 +394,7 @@ mod tests { #[test] fn atom_after_empty_list() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = vec![Syntax::new_list( &arena, @@ -408,8 +422,8 @@ mod tests { init_all_info(&lhs, &rhs); let start = Vertex::new(lhs.get(0).copied(), rhs.get(0).copied()); - let vertex_arena = Bump::new(); - let route = shortest_path(start, &vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); + let mut vertex_arena = Arena::new(); + let route = shortest_path(start, &mut vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); let actions = route.iter().map(|(action, _)| *action).collect_vec(); assert_eq!( @@ -431,7 +445,7 @@ mod tests { #[test] fn replace_similar_comment() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = vec![Syntax::new_atom( &arena, @@ -449,8 +463,8 @@ mod tests { init_all_info(&lhs, &rhs); let start = Vertex::new(lhs.get(0).copied(), rhs.get(0).copied()); - let vertex_arena = Bump::new(); - let route = shortest_path(start, &vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); + let mut vertex_arena = Arena::new(); + let route = shortest_path(start, &mut vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); let actions = route.iter().map(|(action, _)| *action).collect_vec(); assert_eq!( @@ -463,7 +477,7 @@ mod tests { #[test] fn replace_very_different_comment() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = vec![Syntax::new_atom( &arena, @@ -481,8 +495,8 @@ mod tests { init_all_info(&lhs, &rhs); let start = Vertex::new(lhs.get(0).copied(), rhs.get(0).copied()); - let vertex_arena = Bump::new(); - let route = shortest_path(start, &vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); + let mut vertex_arena = Arena::new(); + let route = shortest_path(start, &mut vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); let actions = route.iter().map(|(action, _)| *action).collect_vec(); assert_eq!( @@ -495,7 +509,7 @@ mod tests { #[test] fn replace_comment_prefer_most_similar() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = vec![ Syntax::new_atom( @@ -521,8 +535,8 @@ mod tests { init_all_info(&lhs, &rhs); let start = Vertex::new(lhs.get(0).copied(), rhs.get(0).copied()); - let vertex_arena = Bump::new(); - let route = shortest_path(start, &vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); + let mut vertex_arena = Arena::new(); + let route = shortest_path(start, &mut vertex_arena, 0, DEFAULT_GRAPH_LIMIT).unwrap(); let actions = route.iter().map(|(action, _)| *action).collect_vec(); assert_eq!( @@ -538,7 +552,7 @@ mod tests { #[test] fn mark_syntax_equal_atoms() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = Syntax::new_atom(&arena, pos_helper(1), "foo", AtomKind::Normal); let rhs = Syntax::new_atom(&arena, pos_helper(1), "foo", AtomKind::Normal); init_all_info(&[lhs], &[rhs]); @@ -552,7 +566,7 @@ mod tests { #[test] fn mark_syntax_different_atoms() { - let arena = Arena::new(); + let arena = TypedArena::new(); let lhs = Syntax::new_atom(&arena, pos_helper(1), "foo", AtomKind::Normal); let rhs = Syntax::new_atom(&arena, pos_helper(1), "bar", AtomKind::Normal); init_all_info(&[lhs], &[rhs]); |