Blame


1 3b0f3d61 2020-01-22 neels /* Implementation of the Patience Diff algorithm invented by Bram Cohen:
2 3b0f3d61 2020-01-22 neels * Divide a diff problem into smaller chunks by an LCS of common-unique lines. */
3 3b0f3d61 2020-01-22 neels /*
4 3b0f3d61 2020-01-22 neels * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
5 3b0f3d61 2020-01-22 neels *
6 3b0f3d61 2020-01-22 neels * Permission to use, copy, modify, and distribute this software for any
7 3b0f3d61 2020-01-22 neels * purpose with or without fee is hereby granted, provided that the above
8 3b0f3d61 2020-01-22 neels * copyright notice and this permission notice appear in all copies.
9 3b0f3d61 2020-01-22 neels *
10 3b0f3d61 2020-01-22 neels * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 3b0f3d61 2020-01-22 neels * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 3b0f3d61 2020-01-22 neels * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 3b0f3d61 2020-01-22 neels * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 3b0f3d61 2020-01-22 neels * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 3b0f3d61 2020-01-22 neels * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 3b0f3d61 2020-01-22 neels * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 3b0f3d61 2020-01-22 neels */
18 3b0f3d61 2020-01-22 neels
19 3b0f3d61 2020-01-22 neels #include <assert.h>
20 e10a628a 2020-09-16 stsp #include <inttypes.h>
21 e10a628a 2020-09-16 stsp #include <errno.h>
22 e10a628a 2020-09-16 stsp #include <stdbool.h>
23 e10a628a 2020-09-16 stsp #include <stdio.h>
24 e10a628a 2020-09-16 stsp #include <stdlib.h>
25 e10a628a 2020-09-16 stsp
26 e10a628a 2020-09-16 stsp #include <diff/arraylist.h>
27 3b0f3d61 2020-01-22 neels #include <diff/diff_main.h>
28 3b0f3d61 2020-01-22 neels
29 e4464189 2020-09-20 stsp #include "diff_debug.h"
30 3b0f3d61 2020-01-22 neels
31 3b0f3d61 2020-01-22 neels /* Set unique_here = true for all atoms that exist exactly once in this list. */
32 44cf4950 2020-09-20 neels static int
33 61a7b578 2020-05-06 neels diff_atoms_mark_unique(struct diff_data *d, unsigned int *unique_count)
34 3b0f3d61 2020-01-22 neels {
35 3b0f3d61 2020-01-22 neels struct diff_atom *i;
36 3b0f3d61 2020-01-22 neels unsigned int count = 0;
37 3b0f3d61 2020-01-22 neels diff_data_foreach_atom(i, d) {
38 3b0f3d61 2020-01-22 neels i->patience.unique_here = true;
39 3b0f3d61 2020-01-22 neels i->patience.unique_in_both = true;
40 3b0f3d61 2020-01-22 neels count++;
41 3b0f3d61 2020-01-22 neels }
42 3b0f3d61 2020-01-22 neels diff_data_foreach_atom(i, d) {
43 3b0f3d61 2020-01-22 neels struct diff_atom *j;
44 3b0f3d61 2020-01-22 neels
45 3b0f3d61 2020-01-22 neels if (!i->patience.unique_here)
46 3b0f3d61 2020-01-22 neels continue;
47 3b0f3d61 2020-01-22 neels
48 3b0f3d61 2020-01-22 neels diff_data_foreach_atom_from(i + 1, j, d) {
49 b3fb4686 2020-09-20 neels bool same;
50 b3fb4686 2020-09-20 neels int r = diff_atom_same(&same, i, j);
51 b3fb4686 2020-09-20 neels if (r)
52 44cf4950 2020-09-20 neels return r;
53 b3fb4686 2020-09-20 neels if (!same)
54 b3fb4686 2020-09-20 neels continue;
55 b3fb4686 2020-09-20 neels if (i->patience.unique_here) {
56 b3fb4686 2020-09-20 neels i->patience.unique_here = false;
57 b3fb4686 2020-09-20 neels i->patience.unique_in_both = false;
58 3b0f3d61 2020-01-22 neels count--;
59 3b0f3d61 2020-01-22 neels }
60 b3fb4686 2020-09-20 neels j->patience.unique_here = false;
61 b3fb4686 2020-09-20 neels j->patience.unique_in_both = false;
62 b3fb4686 2020-09-20 neels count--;
63 3b0f3d61 2020-01-22 neels }
64 3b0f3d61 2020-01-22 neels }
65 3b0f3d61 2020-01-22 neels if (unique_count)
66 3b0f3d61 2020-01-22 neels *unique_count = count;
67 44cf4950 2020-09-20 neels return 0;
68 3b0f3d61 2020-01-22 neels }
69 3b0f3d61 2020-01-22 neels
70 0d27172a 2020-05-06 neels /* Mark those lines as atom->patience.unique_in_both = true that appear exactly
71 0d27172a 2020-05-06 neels * once in each side. */
72 44cf4950 2020-09-20 neels static int
73 61a7b578 2020-05-06 neels diff_atoms_mark_unique_in_both(struct diff_data *left, struct diff_data *right,
74 61a7b578 2020-05-06 neels unsigned int *unique_in_both_count)
75 3b0f3d61 2020-01-22 neels {
76 0d27172a 2020-05-06 neels /* Derive the final unique_in_both count without needing an explicit
77 0d27172a 2020-05-06 neels * iteration. So this is just some optimiziation to save one iteration
78 0d27172a 2020-05-06 neels * in the end. */
79 3b0f3d61 2020-01-22 neels unsigned int unique_in_both;
80 44cf4950 2020-09-20 neels int r;
81 3b0f3d61 2020-01-22 neels
82 44cf4950 2020-09-20 neels r = diff_atoms_mark_unique(left, &unique_in_both);
83 44cf4950 2020-09-20 neels if (r)
84 44cf4950 2020-09-20 neels return r;
85 44cf4950 2020-09-20 neels r = diff_atoms_mark_unique(right, NULL);
86 44cf4950 2020-09-20 neels if (r)
87 44cf4950 2020-09-20 neels return r;
88 3b0f3d61 2020-01-22 neels
89 3b0f3d61 2020-01-22 neels debug("unique_in_both %u\n", unique_in_both);
90 3b0f3d61 2020-01-22 neels
91 3b0f3d61 2020-01-22 neels struct diff_atom *i;
92 3b0f3d61 2020-01-22 neels diff_data_foreach_atom(i, left) {
93 3b0f3d61 2020-01-22 neels if (!i->patience.unique_here)
94 3b0f3d61 2020-01-22 neels continue;
95 3b0f3d61 2020-01-22 neels struct diff_atom *j;
96 3b0f3d61 2020-01-22 neels int found_in_b = 0;
97 3b0f3d61 2020-01-22 neels diff_data_foreach_atom(j, right) {
98 b3fb4686 2020-09-20 neels bool same;
99 b3fb4686 2020-09-20 neels int r = diff_atom_same(&same, i, j);
100 b3fb4686 2020-09-20 neels if (r)
101 44cf4950 2020-09-20 neels return r;
102 b3fb4686 2020-09-20 neels if (!same)
103 3b0f3d61 2020-01-22 neels continue;
104 3b0f3d61 2020-01-22 neels if (!j->patience.unique_here) {
105 3b0f3d61 2020-01-22 neels found_in_b = 2; /* or more */
106 3b0f3d61 2020-01-22 neels break;
107 3b0f3d61 2020-01-22 neels } else {
108 3b0f3d61 2020-01-22 neels found_in_b = 1;
109 3b0f3d61 2020-01-22 neels j->patience.pos_in_other = i;
110 3b0f3d61 2020-01-22 neels i->patience.pos_in_other = j;
111 3b0f3d61 2020-01-22 neels }
112 3b0f3d61 2020-01-22 neels }
113 3b0f3d61 2020-01-22 neels
114 3b0f3d61 2020-01-22 neels if (found_in_b == 0 || found_in_b > 1) {
115 3b0f3d61 2020-01-22 neels i->patience.unique_in_both = false;
116 3b0f3d61 2020-01-22 neels unique_in_both--;
117 0d27172a 2020-05-06 neels debug("unique_in_both %u (%d) ", unique_in_both,
118 0d27172a 2020-05-06 neels found_in_b);
119 3b0f3d61 2020-01-22 neels debug_dump_atom(left, NULL, i);
120 3b0f3d61 2020-01-22 neels }
121 3b0f3d61 2020-01-22 neels }
122 3b0f3d61 2020-01-22 neels
123 0d27172a 2020-05-06 neels /* Still need to unmark right[*]->patience.unique_in_both for atoms that
124 0d27172a 2020-05-06 neels * don't exist in left */
125 3b0f3d61 2020-01-22 neels diff_data_foreach_atom(i, right) {
126 3b0f3d61 2020-01-22 neels if (!i->patience.unique_here
127 3b0f3d61 2020-01-22 neels || !i->patience.unique_in_both)
128 3b0f3d61 2020-01-22 neels continue;
129 3b0f3d61 2020-01-22 neels struct diff_atom *j;
130 3b0f3d61 2020-01-22 neels bool found_in_a = false;
131 3b0f3d61 2020-01-22 neels diff_data_foreach_atom(j, left) {
132 b3fb4686 2020-09-20 neels bool same;
133 b3fb4686 2020-09-20 neels int r;
134 3b0f3d61 2020-01-22 neels if (!j->patience.unique_in_both)
135 3b0f3d61 2020-01-22 neels continue;
136 b3fb4686 2020-09-20 neels r = diff_atom_same(&same, i, j);
137 b3fb4686 2020-09-20 neels if (r)
138 44cf4950 2020-09-20 neels return r;
139 b3fb4686 2020-09-20 neels if (!same)
140 3b0f3d61 2020-01-22 neels continue;
141 3b0f3d61 2020-01-22 neels found_in_a = true;
142 3b0f3d61 2020-01-22 neels break;
143 3b0f3d61 2020-01-22 neels }
144 3b0f3d61 2020-01-22 neels
145 3b0f3d61 2020-01-22 neels if (!found_in_a)
146 3b0f3d61 2020-01-22 neels i->patience.unique_in_both = false;
147 3b0f3d61 2020-01-22 neels }
148 3b0f3d61 2020-01-22 neels
149 3b0f3d61 2020-01-22 neels if (unique_in_both_count)
150 3b0f3d61 2020-01-22 neels *unique_in_both_count = unique_in_both;
151 44cf4950 2020-09-20 neels return 0;
152 3b0f3d61 2020-01-22 neels }
153 3b0f3d61 2020-01-22 neels
154 44cf4950 2020-09-20 neels static int
155 0d27172a 2020-05-06 neels diff_atoms_swallow_identical_neighbors(struct diff_data *left,
156 0d27172a 2020-05-06 neels struct diff_data *right,
157 61a7b578 2020-05-06 neels unsigned int *unique_in_both_count)
158 bb7fb738 2020-01-27 neels {
159 0d27172a 2020-05-06 neels debug("trivially combine identical lines"
160 0d27172a 2020-05-06 neels " around unique_in_both lines\n");
161 3b0f3d61 2020-01-22 neels
162 bb7fb738 2020-01-27 neels unsigned int l_idx;
163 bb7fb738 2020-01-27 neels unsigned int next_l_idx;
164 bb7fb738 2020-01-27 neels unsigned int l_min = 0;
165 bb7fb738 2020-01-27 neels unsigned int r_min = 0;
166 bb7fb738 2020-01-27 neels for (l_idx = 0; l_idx < left->atoms.len; l_idx = next_l_idx) {
167 bb7fb738 2020-01-27 neels next_l_idx = l_idx + 1;
168 bb7fb738 2020-01-27 neels struct diff_atom *l = &left->atoms.head[l_idx];
169 bb7fb738 2020-01-27 neels
170 bb7fb738 2020-01-27 neels if (!l->patience.unique_in_both)
171 bb7fb738 2020-01-27 neels continue;
172 bb7fb738 2020-01-27 neels
173 bb7fb738 2020-01-27 neels debug("check identical lines around ");
174 bb7fb738 2020-01-27 neels debug_dump_atom(left, right, l);
175 bb7fb738 2020-01-27 neels
176 0d27172a 2020-05-06 neels unsigned int r_idx = diff_atom_idx(right,
177 0d27172a 2020-05-06 neels l->patience.pos_in_other);
178 bb7fb738 2020-01-27 neels
179 d362ea2e 2020-07-25 stsp struct diff_range identical_l;
180 d362ea2e 2020-07-25 stsp struct diff_range identical_r;
181 bb7fb738 2020-01-27 neels
182 bb7fb738 2020-01-27 neels /* Swallow upwards.
183 0d27172a 2020-05-06 neels * Each common-unique line swallows identical lines upwards and
184 0d27172a 2020-05-06 neels * downwards.
185 0d27172a 2020-05-06 neels * All common-unique lines that were part of the identical lines
186 0d27172a 2020-05-06 neels * following below were already swallowed in the previous
187 0d27172a 2020-05-06 neels * iteration, so we will never hit another common-unique line
188 0d27172a 2020-05-06 neels * above. */
189 bb7fb738 2020-01-27 neels for (identical_l.start = l_idx, identical_r.start = r_idx;
190 b3fb4686 2020-09-20 neels identical_l.start > l_min && identical_r.start > r_min;
191 b3fb4686 2020-09-20 neels identical_l.start--, identical_r.start--) {
192 b3fb4686 2020-09-20 neels bool same;
193 b3fb4686 2020-09-20 neels int r = diff_atom_same(&same,
194 0d27172a 2020-05-06 neels &left->atoms.head[identical_l.start - 1],
195 0d27172a 2020-05-06 neels &right->atoms.head[identical_r.start - 1]);
196 b3fb4686 2020-09-20 neels if (r)
197 44cf4950 2020-09-20 neels return r;
198 b3fb4686 2020-09-20 neels if (!same)
199 b3fb4686 2020-09-20 neels break;
200 b3fb4686 2020-09-20 neels }
201 bb7fb738 2020-01-27 neels
202 bb7fb738 2020-01-27 neels /* Swallow downwards */
203 bb7fb738 2020-01-27 neels for (identical_l.end = l_idx + 1, identical_r.end = r_idx + 1;
204 bb7fb738 2020-01-27 neels identical_l.end < left->atoms.len
205 b3fb4686 2020-09-20 neels && identical_r.end < right->atoms.len;
206 bb7fb738 2020-01-27 neels identical_l.end++, identical_r.end++,
207 bb7fb738 2020-01-27 neels next_l_idx++) {
208 0d27172a 2020-05-06 neels struct diff_atom *l_end;
209 0d27172a 2020-05-06 neels struct diff_atom *r_end;
210 b3fb4686 2020-09-20 neels bool same;
211 b3fb4686 2020-09-20 neels int r = diff_atom_same(&same,
212 b3fb4686 2020-09-20 neels &left->atoms.head[identical_l.end],
213 b3fb4686 2020-09-20 neels &right->atoms.head[identical_r.end]);
214 b3fb4686 2020-09-20 neels if (r)
215 44cf4950 2020-09-20 neels return r;
216 b3fb4686 2020-09-20 neels if (!same)
217 b3fb4686 2020-09-20 neels break;
218 0d27172a 2020-05-06 neels l_end = &left->atoms.head[identical_l.end];
219 ab699b5c 2020-05-12 stsp r_end = &right->atoms.head[identical_r.end];
220 0d27172a 2020-05-06 neels if (!l_end->patience.unique_in_both)
221 0d27172a 2020-05-06 neels continue;
222 0d27172a 2020-05-06 neels /* Part of a chunk of identical lines, remove from
223 0d27172a 2020-05-06 neels * listing of unique_in_both lines */
224 0d27172a 2020-05-06 neels l_end->patience.unique_in_both = false;
225 0d27172a 2020-05-06 neels r_end->patience.unique_in_both = false;
226 0d27172a 2020-05-06 neels (*unique_in_both_count)--;
227 bb7fb738 2020-01-27 neels }
228 bb7fb738 2020-01-27 neels
229 bb7fb738 2020-01-27 neels l->patience.identical_lines = identical_l;
230 0d27172a 2020-05-06 neels l->patience.pos_in_other->patience.identical_lines =
231 0d27172a 2020-05-06 neels identical_r;
232 bb7fb738 2020-01-27 neels
233 bb7fb738 2020-01-27 neels l_min = identical_l.end;
234 bb7fb738 2020-01-27 neels r_min = identical_r.end;
235 bb7fb738 2020-01-27 neels
236 d362ea2e 2020-07-25 stsp if (!diff_range_empty(&l->patience.identical_lines)) {
237 0d27172a 2020-05-06 neels debug("common-unique line at l=%u r=%u swallowed"
238 0d27172a 2020-05-06 neels " identical lines l=%u-%u r=%u-%u\n",
239 bb7fb738 2020-01-27 neels l_idx, r_idx,
240 bb7fb738 2020-01-27 neels identical_l.start, identical_l.end,
241 bb7fb738 2020-01-27 neels identical_r.start, identical_r.end);
242 bb7fb738 2020-01-27 neels }
243 bb7fb738 2020-01-27 neels debug("next_l_idx = %u\n", next_l_idx);
244 bb7fb738 2020-01-27 neels }
245 44cf4950 2020-09-20 neels return 0;
246 bb7fb738 2020-01-27 neels }
247 bb7fb738 2020-01-27 neels
248 0d27172a 2020-05-06 neels /* binary search to find the stack to put this atom "card" on. */
249 0d27172a 2020-05-06 neels static int
250 0d27172a 2020-05-06 neels find_target_stack(struct diff_atom *atom,
251 0d27172a 2020-05-06 neels struct diff_atom **patience_stacks,
252 0d27172a 2020-05-06 neels unsigned int patience_stacks_count)
253 0d27172a 2020-05-06 neels {
254 0d27172a 2020-05-06 neels unsigned int lo = 0;
255 0d27172a 2020-05-06 neels unsigned int hi = patience_stacks_count;
256 0d27172a 2020-05-06 neels while (lo < hi) {
257 0d27172a 2020-05-06 neels unsigned int mid = (lo + hi) >> 1;
258 0d27172a 2020-05-06 neels
259 0d27172a 2020-05-06 neels if (patience_stacks[mid]->patience.pos_in_other
260 0d27172a 2020-05-06 neels < atom->patience.pos_in_other)
261 0d27172a 2020-05-06 neels lo = mid + 1;
262 0d27172a 2020-05-06 neels else
263 0d27172a 2020-05-06 neels hi = mid;
264 0d27172a 2020-05-06 neels }
265 0d27172a 2020-05-06 neels return lo;
266 0d27172a 2020-05-06 neels }
267 0d27172a 2020-05-06 neels
268 0d27172a 2020-05-06 neels /* Among the lines that appear exactly once in each side, find the longest
269 0d27172a 2020-05-06 neels * streak that appear in both files in the same order (with other stuff allowed
270 0d27172a 2020-05-06 neels * to interleave). Use patience sort for that, as in the Patience Diff
271 0d27172a 2020-05-06 neels * algorithm.
272 0d27172a 2020-05-06 neels * See https://bramcohen.livejournal.com/73318.html and, for a much more
273 0d27172a 2020-05-06 neels * detailed explanation,
274 3b0f3d61 2020-01-22 neels * https://blog.jcoglan.com/2017/09/19/the-patience-diff-algorithm/ */
275 3e6cba3a 2020-08-13 stsp int
276 0d27172a 2020-05-06 neels diff_algo_patience(const struct diff_algo_config *algo_config,
277 0d27172a 2020-05-06 neels struct diff_state *state)
278 3b0f3d61 2020-01-22 neels {
279 44cf4950 2020-09-20 neels int rc;
280 3b0f3d61 2020-01-22 neels
281 3b0f3d61 2020-01-22 neels struct diff_data *left = &state->left;
282 3b0f3d61 2020-01-22 neels struct diff_data *right = &state->right;
283 3b0f3d61 2020-01-22 neels
284 3b0f3d61 2020-01-22 neels unsigned int unique_in_both_count;
285 3b0f3d61 2020-01-22 neels
286 3b0f3d61 2020-01-22 neels debug("\n** %s\n", __func__);
287 3b0f3d61 2020-01-22 neels
288 0d27172a 2020-05-06 neels /* Find those lines that appear exactly once in 'left' and exactly once
289 0d27172a 2020-05-06 neels * in 'right'. */
290 44cf4950 2020-09-20 neels rc = diff_atoms_mark_unique_in_both(left, right, &unique_in_both_count);
291 44cf4950 2020-09-20 neels if (rc)
292 44cf4950 2020-09-20 neels return rc;
293 3b0f3d61 2020-01-22 neels
294 3b0f3d61 2020-01-22 neels debug("unique_in_both_count %u\n", unique_in_both_count);
295 3b0f3d61 2020-01-22 neels debug("left:\n");
296 3b0f3d61 2020-01-22 neels debug_dump(left);
297 3b0f3d61 2020-01-22 neels debug("right:\n");
298 3b0f3d61 2020-01-22 neels debug_dump(right);
299 3b0f3d61 2020-01-22 neels
300 3b0f3d61 2020-01-22 neels if (!unique_in_both_count) {
301 0d27172a 2020-05-06 neels /* Cannot apply Patience, tell the caller to use fallback_algo
302 0d27172a 2020-05-06 neels * instead. */
303 3b0f3d61 2020-01-22 neels return DIFF_RC_USE_DIFF_ALGO_FALLBACK;
304 3b0f3d61 2020-01-22 neels }
305 3b0f3d61 2020-01-22 neels
306 44cf4950 2020-09-20 neels rc = diff_atoms_swallow_identical_neighbors(left, right,
307 44cf4950 2020-09-20 neels &unique_in_both_count);
308 44cf4950 2020-09-20 neels if (rc)
309 44cf4950 2020-09-20 neels return rc;
310 bb7fb738 2020-01-27 neels debug("After swallowing identical neighbors: unique_in_both = %u\n",
311 bb7fb738 2020-01-27 neels unique_in_both_count);
312 bb7fb738 2020-01-27 neels
313 44cf4950 2020-09-20 neels rc = ENOMEM;
314 44cf4950 2020-09-20 neels
315 0d27172a 2020-05-06 neels /* An array of Longest Common Sequence is the result of the below
316 0d27172a 2020-05-06 neels * subscope: */
317 3b0f3d61 2020-01-22 neels unsigned int lcs_count = 0;
318 3b0f3d61 2020-01-22 neels struct diff_atom **lcs = NULL;
319 3b0f3d61 2020-01-22 neels struct diff_atom *lcs_tail = NULL;
320 3b0f3d61 2020-01-22 neels
321 3b0f3d61 2020-01-22 neels {
322 0d27172a 2020-05-06 neels /* This subscope marks the lifetime of the atom_pointers
323 0d27172a 2020-05-06 neels * allocation */
324 3b0f3d61 2020-01-22 neels
325 3b0f3d61 2020-01-22 neels /* One chunk of storage for atom pointers */
326 0d27172a 2020-05-06 neels struct diff_atom **atom_pointers;
327 0d27172a 2020-05-06 neels atom_pointers = recallocarray(NULL, 0, unique_in_both_count * 2,
328 0d27172a 2020-05-06 neels sizeof(struct diff_atom*));
329 3b0f3d61 2020-01-22 neels
330 0d27172a 2020-05-06 neels /* Half for the list of atoms that still need to be put on
331 0d27172a 2020-05-06 neels * stacks */
332 3b0f3d61 2020-01-22 neels struct diff_atom **uniques = atom_pointers;
333 3b0f3d61 2020-01-22 neels
334 0d27172a 2020-05-06 neels /* Half for the patience sort state's "card stacks" -- we
335 0d27172a 2020-05-06 neels * remember only each stack's topmost "card" */
336 0d27172a 2020-05-06 neels struct diff_atom **patience_stacks;
337 0d27172a 2020-05-06 neels patience_stacks = atom_pointers + unique_in_both_count;
338 3b0f3d61 2020-01-22 neels unsigned int patience_stacks_count = 0;
339 3b0f3d61 2020-01-22 neels
340 3b0f3d61 2020-01-22 neels /* Take all common, unique items from 'left' ... */
341 3b0f3d61 2020-01-22 neels
342 3b0f3d61 2020-01-22 neels struct diff_atom *atom;
343 3b0f3d61 2020-01-22 neels struct diff_atom **uniques_end = uniques;
344 3b0f3d61 2020-01-22 neels diff_data_foreach_atom(atom, left) {
345 3b0f3d61 2020-01-22 neels if (!atom->patience.unique_in_both)
346 3b0f3d61 2020-01-22 neels continue;
347 3b0f3d61 2020-01-22 neels *uniques_end = atom;
348 3b0f3d61 2020-01-22 neels uniques_end++;
349 3b0f3d61 2020-01-22 neels }
350 3b0f3d61 2020-01-22 neels
351 3b0f3d61 2020-01-22 neels /* ...and sort them to the order found in 'right'.
352 0d27172a 2020-05-06 neels * The idea is to find the leftmost stack that has a higher line
353 0d27172a 2020-05-06 neels * number and add it to the stack's top.
354 0d27172a 2020-05-06 neels * If there is no such stack, open a new one on the right. The
355 0d27172a 2020-05-06 neels * line number is derived from the atom*, which are array items
356 0d27172a 2020-05-06 neels * and hence reflect the relative position in the source file.
357 0d27172a 2020-05-06 neels * So we got the common-uniques from 'left' and sort them
358 0d27172a 2020-05-06 neels * according to atom->patience.pos_in_other. */
359 3b0f3d61 2020-01-22 neels unsigned int i;
360 3b0f3d61 2020-01-22 neels for (i = 0; i < unique_in_both_count; i++) {
361 3b0f3d61 2020-01-22 neels atom = uniques[i];
362 3b0f3d61 2020-01-22 neels unsigned int target_stack;
363 0d27172a 2020-05-06 neels target_stack = find_target_stack(atom, patience_stacks,
364 0d27172a 2020-05-06 neels patience_stacks_count);
365 3b0f3d61 2020-01-22 neels assert(target_stack <= patience_stacks_count);
366 3b0f3d61 2020-01-22 neels patience_stacks[target_stack] = atom;
367 3b0f3d61 2020-01-22 neels if (target_stack == patience_stacks_count)
368 3b0f3d61 2020-01-22 neels patience_stacks_count++;
369 3b0f3d61 2020-01-22 neels
370 0d27172a 2020-05-06 neels /* Record a back reference to the next stack on the
371 0d27172a 2020-05-06 neels * left, which will form the final longest sequence
372 3b0f3d61 2020-01-22 neels * later. */
373 0d27172a 2020-05-06 neels atom->patience.prev_stack = target_stack ?
374 0d27172a 2020-05-06 neels patience_stacks[target_stack - 1] : NULL;
375 3b0f3d61 2020-01-22 neels
376 3b0f3d61 2020-01-22 neels }
377 3b0f3d61 2020-01-22 neels
378 0d27172a 2020-05-06 neels /* backtrace through prev_stack references to form the final
379 0d27172a 2020-05-06 neels * longest common sequence */
380 3b0f3d61 2020-01-22 neels lcs_tail = patience_stacks[patience_stacks_count - 1];
381 3b0f3d61 2020-01-22 neels lcs_count = patience_stacks_count;
382 3b0f3d61 2020-01-22 neels
383 0d27172a 2020-05-06 neels /* uniques and patience_stacks are no longer needed.
384 0d27172a 2020-05-06 neels * Backpointers are in atom->patience.prev_stack */
385 3b0f3d61 2020-01-22 neels free(atom_pointers);
386 3b0f3d61 2020-01-22 neels }
387 3b0f3d61 2020-01-22 neels
388 3b0f3d61 2020-01-22 neels lcs = recallocarray(NULL, 0, lcs_count, sizeof(struct diff_atom*));
389 3b0f3d61 2020-01-22 neels struct diff_atom **lcs_backtrace_pos = &lcs[lcs_count - 1];
390 3b0f3d61 2020-01-22 neels struct diff_atom *atom;
391 3b0f3d61 2020-01-22 neels for (atom = lcs_tail; atom; atom = atom->patience.prev_stack, lcs_backtrace_pos--) {
392 3b0f3d61 2020-01-22 neels assert(lcs_backtrace_pos >= lcs);
393 3b0f3d61 2020-01-22 neels *lcs_backtrace_pos = atom;
394 3b0f3d61 2020-01-22 neels }
395 3b0f3d61 2020-01-22 neels
396 3b0f3d61 2020-01-22 neels unsigned int i;
397 3b0f3d61 2020-01-22 neels if (DEBUG) {
398 3b0f3d61 2020-01-22 neels debug("\npatience LCS:\n");
399 3b0f3d61 2020-01-22 neels for (i = 0; i < lcs_count; i++) {
400 3b0f3d61 2020-01-22 neels debug_dump_atom(left, right, lcs[i]);
401 3b0f3d61 2020-01-22 neels }
402 3b0f3d61 2020-01-22 neels }
403 3b0f3d61 2020-01-22 neels
404 3b0f3d61 2020-01-22 neels
405 0d27172a 2020-05-06 neels /* TODO: For each common-unique line found (now listed in lcs), swallow
406 0d27172a 2020-05-06 neels * lines upwards and downwards that are identical on each side. Requires
407 0d27172a 2020-05-06 neels * a way to represent atoms being glued to adjacent atoms. */
408 3b0f3d61 2020-01-22 neels
409 3b0f3d61 2020-01-22 neels debug("\ntraverse LCS, possibly recursing:\n");
410 3b0f3d61 2020-01-22 neels
411 0d27172a 2020-05-06 neels /* Now we have pinned positions in both files at which it makes sense to
412 0d27172a 2020-05-06 neels * divide the diff problem into smaller chunks. Go into the next round:
413 0d27172a 2020-05-06 neels * look at each section in turn, trying to again find common-unique
414 0d27172a 2020-05-06 neels * lines in those smaller sections. As soon as no more are found, the
415 0d27172a 2020-05-06 neels * remaining smaller sections are solved by Myers. */
416 3b0f3d61 2020-01-22 neels unsigned int left_pos = 0;
417 3b0f3d61 2020-01-22 neels unsigned int right_pos = 0;
418 3b0f3d61 2020-01-22 neels for (i = 0; i <= lcs_count; i++) {
419 3b0f3d61 2020-01-22 neels struct diff_atom *atom;
420 bb7fb738 2020-01-27 neels struct diff_atom *atom_r;
421 3b0f3d61 2020-01-22 neels unsigned int left_idx;
422 3b0f3d61 2020-01-22 neels unsigned int right_idx;
423 3b0f3d61 2020-01-22 neels
424 3b0f3d61 2020-01-22 neels if (i < lcs_count) {
425 3b0f3d61 2020-01-22 neels atom = lcs[i];
426 bb7fb738 2020-01-27 neels atom_r = atom->patience.pos_in_other;
427 15ac50e2 2020-05-05 neels debug("lcs[%u] = left[%ld] = right[%ld]\n", i,
428 bb7fb738 2020-01-27 neels diff_atom_idx(left, atom), diff_atom_idx(right, atom_r));
429 bb7fb738 2020-01-27 neels left_idx = atom->patience.identical_lines.start;
430 bb7fb738 2020-01-27 neels right_idx = atom_r->patience.identical_lines.start;
431 bb7fb738 2020-01-27 neels debug(" identical lines l %u-%u r %u-%u\n",
432 bb7fb738 2020-01-27 neels atom->patience.identical_lines.start, atom->patience.identical_lines.end,
433 bb7fb738 2020-01-27 neels atom_r->patience.identical_lines.start, atom_r->patience.identical_lines.end);
434 3b0f3d61 2020-01-22 neels } else {
435 3b0f3d61 2020-01-22 neels atom = NULL;
436 bb7fb738 2020-01-27 neels atom_r = NULL;
437 3b0f3d61 2020-01-22 neels left_idx = left->atoms.len;
438 3b0f3d61 2020-01-22 neels right_idx = right->atoms.len;
439 3b0f3d61 2020-01-22 neels }
440 3b0f3d61 2020-01-22 neels
441 0d27172a 2020-05-06 neels /* 'atom' now marks an atom that matches on both sides according
442 0d27172a 2020-05-06 neels * to patience-diff (a common-unique identical atom in both
443 0d27172a 2020-05-06 neels * files).
444 0d27172a 2020-05-06 neels * Handle the section before and the atom itself; the section
445 0d27172a 2020-05-06 neels * after will be handled by the next loop iteration -- note that
446 0d27172a 2020-05-06 neels * i loops to last element + 1 ("i <= lcs_count"), so that there
447 0d27172a 2020-05-06 neels * will be another final iteration to pick up the last remaining
448 0d27172a 2020-05-06 neels * items after the last LCS atom.
449 3b0f3d61 2020-01-22 neels * The sections before might also be empty on left and/or right.
450 0d27172a 2020-05-06 neels * left_pos and right_pos mark the indexes of the first atoms
451 0d27172a 2020-05-06 neels * that have not yet been handled in the previous loop
452 0d27172a 2020-05-06 neels * iteration. left_idx and right_idx mark the indexes of the
453 0d27172a 2020-05-06 neels * matching atom on left and right, respectively. */
454 3b0f3d61 2020-01-22 neels
455 0d27172a 2020-05-06 neels debug("iteration %u left_pos %u left_idx %u"
456 0d27172a 2020-05-06 neels " right_pos %u right_idx %u\n",
457 3b0f3d61 2020-01-22 neels i, left_pos, left_idx, right_pos, right_idx);
458 3b0f3d61 2020-01-22 neels
459 3b0f3d61 2020-01-22 neels /* Section before the matching atom */
460 3b0f3d61 2020-01-22 neels struct diff_atom *left_atom = &left->atoms.head[left_pos];
461 3b0f3d61 2020-01-22 neels unsigned int left_section_len = left_idx - left_pos;
462 3b0f3d61 2020-01-22 neels
463 3b0f3d61 2020-01-22 neels struct diff_atom *right_atom = &(right->atoms.head[right_pos]);
464 3b0f3d61 2020-01-22 neels unsigned int right_section_len = right_idx - right_pos;
465 3b0f3d61 2020-01-22 neels
466 3b0f3d61 2020-01-22 neels if (left_section_len && right_section_len) {
467 0d27172a 2020-05-06 neels /* Record an unsolved chunk, the caller will apply
468 0d27172a 2020-05-06 neels * inner_algo() on this chunk. */
469 3b0f3d61 2020-01-22 neels if (!diff_state_add_chunk(state, false,
470 3b0f3d61 2020-01-22 neels left_atom, left_section_len,
471 0d27172a 2020-05-06 neels right_atom,
472 0d27172a 2020-05-06 neels right_section_len))
473 3b0f3d61 2020-01-22 neels goto return_rc;
474 3b0f3d61 2020-01-22 neels } else if (left_section_len && !right_section_len) {
475 0d27172a 2020-05-06 neels /* Only left atoms and none on the right, they form a
476 0d27172a 2020-05-06 neels * "minus" chunk, then. */
477 3b0f3d61 2020-01-22 neels if (!diff_state_add_chunk(state, true,
478 3b0f3d61 2020-01-22 neels left_atom, left_section_len,
479 3b0f3d61 2020-01-22 neels right_atom, 0))
480 3b0f3d61 2020-01-22 neels goto return_rc;
481 3b0f3d61 2020-01-22 neels } else if (!left_section_len && right_section_len) {
482 0d27172a 2020-05-06 neels /* No left atoms, only atoms on the right, they form a
483 0d27172a 2020-05-06 neels * "plus" chunk, then. */
484 3b0f3d61 2020-01-22 neels if (!diff_state_add_chunk(state, true,
485 3b0f3d61 2020-01-22 neels left_atom, 0,
486 3b0f3d61 2020-01-22 neels right_atom, right_section_len))
487 3b0f3d61 2020-01-22 neels goto return_rc;
488 3b0f3d61 2020-01-22 neels }
489 0d27172a 2020-05-06 neels /* else: left_section_len == 0 and right_section_len == 0, i.e.
490 0d27172a 2020-05-06 neels * nothing here. */
491 3b0f3d61 2020-01-22 neels
492 0d27172a 2020-05-06 neels /* The atom found to match on both sides forms a chunk of equals
493 0d27172a 2020-05-06 neels * on each side. In the very last iteration of this loop, there
494 0d27172a 2020-05-06 neels * is no matching atom, we were just cleaning out the remaining
495 0d27172a 2020-05-06 neels * lines. */
496 3b0f3d61 2020-01-22 neels if (atom) {
497 0d27172a 2020-05-06 neels void *ok;
498 0d27172a 2020-05-06 neels ok = diff_state_add_chunk(state, true,
499 0d27172a 2020-05-06 neels left->atoms.head
500 0d27172a 2020-05-06 neels + atom->patience.identical_lines.start,
501 d362ea2e 2020-07-25 stsp diff_range_len(&atom->patience.identical_lines),
502 0d27172a 2020-05-06 neels right->atoms.head
503 0d27172a 2020-05-06 neels + atom_r->patience.identical_lines.start,
504 d362ea2e 2020-07-25 stsp diff_range_len(&atom_r->patience.identical_lines));
505 0d27172a 2020-05-06 neels if (!ok)
506 3b0f3d61 2020-01-22 neels goto return_rc;
507 bb7fb738 2020-01-27 neels left_pos = atom->patience.identical_lines.end;
508 bb7fb738 2020-01-27 neels right_pos = atom_r->patience.identical_lines.end;
509 bb7fb738 2020-01-27 neels } else {
510 bb7fb738 2020-01-27 neels left_pos = left_idx + 1;
511 bb7fb738 2020-01-27 neels right_pos = right_idx + 1;
512 3b0f3d61 2020-01-22 neels }
513 0d27172a 2020-05-06 neels debug("end of iteration %u left_pos %u left_idx %u"
514 0d27172a 2020-05-06 neels " right_pos %u right_idx %u\n",
515 bb7fb738 2020-01-27 neels i, left_pos, left_idx, right_pos, right_idx);
516 3b0f3d61 2020-01-22 neels }
517 3b0f3d61 2020-01-22 neels debug("** END %s\n", __func__);
518 3b0f3d61 2020-01-22 neels
519 3b0f3d61 2020-01-22 neels rc = DIFF_RC_OK;
520 3b0f3d61 2020-01-22 neels
521 3b0f3d61 2020-01-22 neels return_rc:
522 3b0f3d61 2020-01-22 neels free(lcs);
523 3b0f3d61 2020-01-22 neels return rc;
524 3b0f3d61 2020-01-22 neels }