java_diff_utils_rs/algorithm/myers/
myers.rs1use super::path_node::PathNode;
2use crate::algorithm::change::Change;
3use crate::algorithm::diff_algorithm_listener::DiffAlgorithmListener;
4use crate::algorithm::DiffAlgorithm;
5use crate::patch::delta_type::DeltaType;
6
7#[derive(Default)]
8pub struct DiffWorkspace {
9 arena: Vec<PathNode>,
10 diagonal: Vec<Option<usize>>,
11}
12
13impl DiffWorkspace {
14 pub fn new() -> Self {
15 Self::default()
16 }
17
18 pub fn clear(&mut self) {
19 self.arena.clear();
20 self.diagonal.fill(None);
21 }
22}
23
24pub struct MyersDiff<T> {
25 equalizer: Option<Box<dyn Fn(&T, &T) -> bool>>,
26}
27
28impl<T> Default for MyersDiff<T> {
29 fn default() -> Self {
30 Self { equalizer: None }
31 }
32}
33
34impl<T> MyersDiff<T> {
35 pub fn new() -> Self {
36 Self::default()
37 }
38
39 pub fn with_equalizer<F>(equalizer: F) -> Self
40 where
41 F: Fn(&T, &T) -> bool + 'static,
42 {
43 Self {
44 equalizer: Some(Box::new(equalizer)),
45 }
46 }
47}
48
49impl<T: PartialEq> DiffAlgorithm<T> for MyersDiff<T> {
50 fn diff_with_listener(
51 &self,
52 source: &[T],
53 target: &[T],
54 listener: &mut dyn DiffAlgorithmListener,
55 ) -> Vec<Change> {
56 if let Some(ref eq) = self.equalizer {
57 compute_diff_with_listener(source, target, eq, listener)
58 } else {
59 compute_diff_with_listener(source, target, |a, b| a == b, listener)
60 }
61 }
62}
63
64pub fn compute_diff<T: PartialEq>(source: &[T], target: &[T]) -> Vec<Change> {
65 compute_diff_with(source, target, |a, b| a == b)
66}
67
68pub fn compute_diff_with<T, F>(source: &[T], target: &[T], equalizer: F) -> Vec<Change>
69where
70 F: Fn(&T, &T) -> bool,
71{
72 let mut ws = DiffWorkspace::new();
73 compute_diff_with_workspace_and_listener(source, target, equalizer, &mut ws, None)
74}
75
76pub fn compute_diff_with_listener<T, F>(
77 source: &[T],
78 target: &[T],
79 equalizer: F,
80 listener: &mut dyn DiffAlgorithmListener,
81) -> Vec<Change>
82where
83 F: Fn(&T, &T) -> bool,
84{
85 let mut ws = DiffWorkspace::new();
86 compute_diff_with_workspace_and_listener(source, target, equalizer, &mut ws, Some(listener))
87}
88
89pub fn compute_diff_with_workspace<T, F>(
90 source: &[T],
91 target: &[T],
92 equalizer: F,
93 ws: &mut DiffWorkspace,
94) -> Vec<Change>
95where
96 F: Fn(&T, &T) -> bool,
97{
98 compute_diff_with_workspace_and_listener(source, target, equalizer, ws, None)
99}
100
101pub fn compute_diff_with_workspace_and_listener<T, F>(
102 source: &[T],
103 target: &[T],
104 equalizer: F,
105 ws: &mut DiffWorkspace,
106 mut listener: Option<&mut dyn DiffAlgorithmListener>,
107) -> Vec<Change>
108where
109 F: Fn(&T, &T) -> bool,
110{
111 if let Some(ref mut l) = listener {
112 l.diff_start();
113 }
114
115 if source.is_empty() && target.is_empty() {
116 if let Some(ref mut l) = listener {
117 l.diff_end();
118 }
119 return Vec::new();
120 }
121
122 ws.clear();
123
124 let head_idx = build_path(source, target, &equalizer, ws, listener.as_deref_mut());
126
127 let result = if let Some(idx) = head_idx {
128 build_revision(&ws.arena, idx)
129 } else {
130 Vec::new()
131 };
132
133 if let Some(ref mut l) = listener {
134 l.diff_end();
135 }
136
137 result
138}
139
140pub fn build_path<'a, T, F>(
141 orig: &[T],
142 rev: &[T],
143 equalizer: &F,
144 ws: &mut DiffWorkspace,
145 mut listener: Option<&'a mut (dyn DiffAlgorithmListener + '_)>,
147) -> Option<usize>
148where
149 F: Fn(&T, &T) -> bool,
150{
151 let n = orig.len();
152 let m = rev.len();
153 let max = n + m + 1;
154 let size = 1 + 2 * max;
155 let middle = max;
156
157 ws.arena.clear();
158 ws.arena.reserve(max * 2);
159
160 if ws.diagonal.len() < size {
161 ws.diagonal.resize(size, None);
162 } else {
163 ws.diagonal.fill(None);
164 }
165
166 ws.arena.push(PathNode {
167 i: 0,
168 j: -1,
169 is_snake: true,
170 is_bootstrap: true,
171 prev: None,
172 });
173 ws.diagonal[middle + 1] = Some(0);
174
175 for d in 0..max {
176 let d_isize = d as isize;
177
178 if let Some(ref mut l) = listener {
180 l.path_node(d, max, d);
181 }
182
183 for k in (-d_isize..=d_isize).step_by(2) {
184 let kmiddle = (middle as isize + k) as usize;
185 let kplus = kmiddle + 1;
186 let kminus = kmiddle - 1;
187
188 let (i_start, prev_idx) = if k == -d_isize {
189 let p = ws.diagonal[kplus].unwrap_or(0);
190 (ws.arena[p].i, p)
191 } else if k != d_isize {
192 let pm = ws.diagonal[kminus];
193 let pp = ws.diagonal[kplus];
194
195 match (pm, pp) {
196 (Some(pm), Some(pp)) => {
197 if ws.arena[pm].i < ws.arena[pp].i {
198 (ws.arena[pp].i, pp)
199 } else {
200 (ws.arena[pm].i + 1, pm)
201 }
202 }
203 (None, Some(pp)) => (ws.arena[pp].i, pp),
204 (Some(pm), None) => (ws.arena[pm].i + 1, pm),
205 (None, None) => (0, 0),
206 }
207 } else {
208 let p = ws.diagonal[kminus].unwrap_or(0);
209 (ws.arena[p].i + 1, p)
210 };
211
212 let mut i = i_start;
213 let mut j = i as isize - k;
214
215 let collapsed_prev = PathNode::previous_snake(&ws.arena, prev_idx);
216
217 let node_idx = ws.arena.len();
218 ws.arena.push(PathNode {
219 i,
220 j,
221 is_snake: false,
222 is_bootstrap: false,
223 prev: collapsed_prev,
224 });
225
226 while i < n && j >= 0 && (j as usize) < m && equalizer(&orig[i], &rev[j as usize]) {
227 i += 1;
228 j += 1;
229 }
230
231 let final_node_idx = if i != ws.arena[node_idx].i {
232 let snake_idx = ws.arena.len();
233 ws.arena.push(PathNode {
234 i,
235 j,
236 is_snake: true,
237 is_bootstrap: false,
238 prev: Some(node_idx),
239 });
240 snake_idx
241 } else {
242 node_idx
243 };
244
245 ws.diagonal[kmiddle] = Some(final_node_idx);
246
247 if i >= n && j >= 0 && (j as usize) >= m {
248 return Some(final_node_idx);
249 }
250 }
251 }
252
253 None
254}
255
256fn build_revision(arena: &[PathNode], head_idx: usize) -> Vec<Change> {
257 let mut raw_changes = Vec::new();
258 let mut curr_idx = Some(head_idx);
259
260 if let Some(idx) = curr_idx {
261 if arena[idx].is_snake {
262 curr_idx = arena[idx].prev;
263 }
264 }
265
266 loop {
267 let idx = match curr_idx {
268 Some(i) => i,
269 None => break,
270 };
271 let node = &arena[idx];
272
273 let prev_idx = match node.prev {
274 Some(p) => p,
275 None => break,
276 };
277
278 if arena[prev_idx].j < 0 {
279 break;
280 }
281
282 let i = node.i;
283 let j = node.j.max(0) as usize;
284
285 let path_idx = prev_idx;
286 let path_node = &arena[path_idx];
287 let ianchor = path_node.i;
288 let janchor = path_node.j.max(0) as usize;
289
290 let delta_type = match (ianchor == i, janchor == j) {
291 (true, false) => DeltaType::Insert,
292 (false, true) => DeltaType::Delete,
293 _ => DeltaType::Change,
294 };
295
296 raw_changes.push(Change {
297 delta_type,
298 start_original: ianchor,
299 end_original: i,
300 start_revised: janchor,
301 end_revised: j,
302 });
303
304 curr_idx = if arena[path_idx].is_snake {
305 arena[path_idx].prev
306 } else {
307 Some(path_idx)
308 };
309 }
310
311 raw_changes.reverse();
312 raw_changes
313}