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 1dfba055 2020-10-07 stsp #include <arraylist.h>
27 1dfba055 2020-10-07 stsp #include <diff_main.h>
28 3b0f3d61 2020-01-22 neels
29 85ab4559 2020-09-22 stsp #include "diff_internal.h"
30 2a1b94d0 2020-09-26 stsp #include "diff_debug.h"
31 3b0f3d61 2020-01-22 neels
32 6c8c5d3f 2020-10-12 neels /* Per-atom state for the Patience Diff algorithm */
33 6c8c5d3f 2020-10-12 neels struct atom_patience {
34 6c8c5d3f 2020-10-12 neels bool unique_in_both;
35 6c8c5d3f 2020-10-12 neels struct diff_atom *pos_in_other;
36 6c8c5d3f 2020-10-12 neels struct diff_atom *prev_stack;
37 6c8c5d3f 2020-10-12 neels struct diff_range identical_lines;
38 6c8c5d3f 2020-10-12 neels };
39 6c8c5d3f 2020-10-12 neels
40 6c8c5d3f 2020-10-12 neels /* A diff_atom has a backpointer to the root diff_data. That points to the
41 6c8c5d3f 2020-10-12 neels * current diff_data, a possibly smaller section of the root. That current
42 6c8c5d3f 2020-10-12 neels * diff_data->algo_data is a pointer to an array of struct atom_patience. The
43 6c8c5d3f 2020-10-12 neels * atom's index in current diff_data gives the index in the atom_patience array.
44 6c8c5d3f 2020-10-12 neels */
45 6c8c5d3f 2020-10-12 neels #define PATIENCE(ATOM) \
46 6c8c5d3f 2020-10-12 neels (((struct atom_patience*)((ATOM)->root->current->algo_data))\
47 6c8c5d3f 2020-10-12 neels [diff_atom_idx((ATOM)->root->current, ATOM)])
48 6c8c5d3f 2020-10-12 neels
49 12c5bde7 2020-10-16 stsp int diff_atoms_mergesort_compar(const void *_a, const void *_b)
50 3b0f3d61 2020-01-22 neels {
51 60b6e08b 2020-10-12 neels const struct diff_atom *a = *(struct diff_atom**)_a;
52 60b6e08b 2020-10-12 neels const struct diff_atom *b = *(struct diff_atom**)_b;
53 60b6e08b 2020-10-12 neels int cmp;
54 65688edf 2020-10-15 neels int rc = 0;
55 60b6e08b 2020-10-12 neels
56 227cd87e 2020-10-15 stsp /* If there's been an error (e.g. I/O error) in a previous compar, we
57 12c5bde7 2020-10-16 stsp * have no way to abort the mergesort but just report the rc and stop
58 227cd87e 2020-10-15 stsp * comparing. Make sure to catch errors on either side. If atoms are
59 227cd87e 2020-10-15 stsp * from more than one diff_data, make sure the error, if any, spreads
60 227cd87e 2020-10-15 stsp * to all of them, so we can cut short all future comparisons. */
61 65688edf 2020-10-15 neels if (a->root->err)
62 65688edf 2020-10-15 neels rc = a->root->err;
63 65688edf 2020-10-15 neels if (b->root->err)
64 65688edf 2020-10-15 neels rc = b->root->err;
65 65688edf 2020-10-15 neels if (rc) {
66 60b6e08b 2020-10-12 neels a->root->err = rc;
67 60b6e08b 2020-10-12 neels b->root->err = rc;
68 65688edf 2020-10-15 neels /* just return 'equal' to not swap more positions */
69 60b6e08b 2020-10-12 neels return 0;
70 3b0f3d61 2020-01-22 neels }
71 3b0f3d61 2020-01-22 neels
72 60b6e08b 2020-10-12 neels /* Sort by the simplistic hash */
73 60b6e08b 2020-10-12 neels if (a->hash < b->hash)
74 60b6e08b 2020-10-12 neels return -1;
75 60b6e08b 2020-10-12 neels if (a->hash > b->hash)
76 60b6e08b 2020-10-12 neels return 1;
77 3b0f3d61 2020-01-22 neels
78 60b6e08b 2020-10-12 neels /* If hashes are the same, the lines may still differ. Do a full cmp. */
79 60b6e08b 2020-10-12 neels rc = diff_atom_cmp(&cmp, a, b);
80 60b6e08b 2020-10-12 neels
81 60b6e08b 2020-10-12 neels if (rc) {
82 60b6e08b 2020-10-12 neels /* Mark the I/O error so that the caller can find out about it.
83 60b6e08b 2020-10-12 neels * For the case atoms are from more than one diff_data, mark in
84 60b6e08b 2020-10-12 neels * both. */
85 60b6e08b 2020-10-12 neels a->root->err = rc;
86 60b6e08b 2020-10-12 neels if (a->root != b->root)
87 60b6e08b 2020-10-12 neels b->root->err = rc;
88 60b6e08b 2020-10-12 neels return 0;
89 3b0f3d61 2020-01-22 neels }
90 60b6e08b 2020-10-12 neels
91 60b6e08b 2020-10-12 neels return cmp;
92 3b0f3d61 2020-01-22 neels }
93 3b0f3d61 2020-01-22 neels
94 60b6e08b 2020-10-12 neels /* Sort an array of struct diff_atom* in-place. */
95 12c5bde7 2020-10-16 stsp static int diff_atoms_sort(struct diff_atom *atoms[],
96 60b6e08b 2020-10-12 neels size_t atoms_count)
97 3b0f3d61 2020-01-22 neels {
98 12c5bde7 2020-10-16 stsp /*
99 12c5bde7 2020-10-16 stsp * Use mergesort(3) bcause it provides stable ordering
100 12c5bde7 2020-10-16 stsp * even if two or more elements are equal.
101 12c5bde7 2020-10-16 stsp */
102 12c5bde7 2020-10-16 stsp mergesort(atoms, atoms_count, sizeof(struct diff_atom*),
103 12c5bde7 2020-10-16 stsp diff_atoms_mergesort_compar);
104 60b6e08b 2020-10-12 neels return atoms[0]->root->err;
105 60b6e08b 2020-10-12 neels }
106 3b0f3d61 2020-01-22 neels
107 60b6e08b 2020-10-12 neels static int
108 12c5bde7 2020-10-16 stsp diff_atoms_mark_unique_in_both(struct diff_data *left, struct diff_data *right,
109 12c5bde7 2020-10-16 stsp unsigned int *unique_in_both_count_p)
110 60b6e08b 2020-10-12 neels {
111 60b6e08b 2020-10-12 neels struct diff_atom *a;
112 60b6e08b 2020-10-12 neels struct diff_atom *b;
113 f087e968 2020-10-16 stsp struct diff_atom **all_atoms;
114 60b6e08b 2020-10-12 neels unsigned int len = 0;
115 60b6e08b 2020-10-12 neels unsigned int i;
116 60b6e08b 2020-10-12 neels unsigned int unique_in_both_count = 0;
117 60b6e08b 2020-10-12 neels int rc;
118 f087e968 2020-10-16 stsp
119 f087e968 2020-10-16 stsp all_atoms = calloc(left->atoms.len + right->atoms.len,
120 f087e968 2020-10-16 stsp sizeof(struct diff_atom *));
121 f087e968 2020-10-16 stsp if (all_atoms == NULL)
122 f087e968 2020-10-16 stsp return ENOMEM;
123 f087e968 2020-10-16 stsp
124 6c8c5d3f 2020-10-12 neels left->err = 0;
125 6c8c5d3f 2020-10-12 neels right->err = 0;
126 60b6e08b 2020-10-12 neels left->root->err = 0;
127 60b6e08b 2020-10-12 neels right->root->err = 0;
128 60b6e08b 2020-10-12 neels diff_data_foreach_atom(a, left) {
129 60b6e08b 2020-10-12 neels all_atoms[len++] = a;
130 60b6e08b 2020-10-12 neels }
131 60b6e08b 2020-10-12 neels diff_data_foreach_atom(b, right) {
132 60b6e08b 2020-10-12 neels all_atoms[len++] = b;
133 60b6e08b 2020-10-12 neels }
134 60b6e08b 2020-10-12 neels
135 12c5bde7 2020-10-16 stsp rc = diff_atoms_sort(all_atoms, len);
136 60b6e08b 2020-10-12 neels if (rc)
137 60b6e08b 2020-10-12 neels goto free_and_exit;
138 60b6e08b 2020-10-12 neels
139 60b6e08b 2020-10-12 neels /* Now we have a sorted array of atom pointers. All similar lines are
140 60b6e08b 2020-10-12 neels * adjacent. Walk through the array and mark those that are unique on
141 60b6e08b 2020-10-12 neels * each side, but exist once in both sources. */
142 60b6e08b 2020-10-12 neels for (i = 0; i < len; i++) {
143 60b6e08b 2020-10-12 neels bool same;
144 60b6e08b 2020-10-12 neels unsigned int j;
145 60b6e08b 2020-10-12 neels unsigned int count_first_side = 1;
146 60b6e08b 2020-10-12 neels unsigned int count_other_side = 0;
147 60b6e08b 2020-10-12 neels a = all_atoms[i];
148 60b6e08b 2020-10-12 neels
149 60b6e08b 2020-10-12 neels for (j = i+1; j < len; j++) {
150 60b6e08b 2020-10-12 neels b = all_atoms[j];
151 60b6e08b 2020-10-12 neels rc = diff_atom_same(&same, a, b);
152 60b6e08b 2020-10-12 neels if (rc)
153 60b6e08b 2020-10-12 neels goto free_and_exit;
154 60b6e08b 2020-10-12 neels if (!same)
155 3b0f3d61 2020-01-22 neels break;
156 60b6e08b 2020-10-12 neels /* A following atom is the same. See on which side the
157 60b6e08b 2020-10-12 neels * repetition counts. */
158 60b6e08b 2020-10-12 neels if (a->root == b->root)
159 60b6e08b 2020-10-12 neels count_first_side ++;
160 60b6e08b 2020-10-12 neels else
161 60b6e08b 2020-10-12 neels count_other_side ++;
162 3b0f3d61 2020-01-22 neels }
163 3b0f3d61 2020-01-22 neels
164 60b6e08b 2020-10-12 neels /* Counted a section of similar atoms, put the results back to
165 60b6e08b 2020-10-12 neels * the atoms. */
166 60b6e08b 2020-10-12 neels if ((count_first_side == 1)
167 60b6e08b 2020-10-12 neels && (count_other_side == 1)) {
168 60b6e08b 2020-10-12 neels b = all_atoms[i+1];
169 6c8c5d3f 2020-10-12 neels PATIENCE(a).unique_in_both = true;
170 6c8c5d3f 2020-10-12 neels PATIENCE(a).pos_in_other = b;
171 6c8c5d3f 2020-10-12 neels PATIENCE(b).unique_in_both = true;
172 6c8c5d3f 2020-10-12 neels PATIENCE(b).pos_in_other = a;
173 60b6e08b 2020-10-12 neels unique_in_both_count++;
174 3b0f3d61 2020-01-22 neels }
175 3b0f3d61 2020-01-22 neels }
176 60b6e08b 2020-10-12 neels *unique_in_both_count_p = unique_in_both_count;
177 60b6e08b 2020-10-12 neels rc = 0;
178 60b6e08b 2020-10-12 neels free_and_exit:
179 60b6e08b 2020-10-12 neels free(all_atoms);
180 60b6e08b 2020-10-12 neels return rc;
181 3b0f3d61 2020-01-22 neels }
182 3b0f3d61 2020-01-22 neels
183 44cf4950 2020-09-20 neels static int
184 0d27172a 2020-05-06 neels diff_atoms_swallow_identical_neighbors(struct diff_data *left,
185 0d27172a 2020-05-06 neels struct diff_data *right,
186 61a7b578 2020-05-06 neels unsigned int *unique_in_both_count)
187 bb7fb738 2020-01-27 neels {
188 0d27172a 2020-05-06 neels debug("trivially combine identical lines"
189 0d27172a 2020-05-06 neels " around unique_in_both lines\n");
190 3b0f3d61 2020-01-22 neels
191 bb7fb738 2020-01-27 neels unsigned int l_idx;
192 bb7fb738 2020-01-27 neels unsigned int next_l_idx;
193 bb7fb738 2020-01-27 neels unsigned int l_min = 0;
194 bb7fb738 2020-01-27 neels unsigned int r_min = 0;
195 bb7fb738 2020-01-27 neels for (l_idx = 0; l_idx < left->atoms.len; l_idx = next_l_idx) {
196 bb7fb738 2020-01-27 neels next_l_idx = l_idx + 1;
197 bb7fb738 2020-01-27 neels struct diff_atom *l = &left->atoms.head[l_idx];
198 bb7fb738 2020-01-27 neels
199 6c8c5d3f 2020-10-12 neels if (!PATIENCE(l).unique_in_both)
200 bb7fb738 2020-01-27 neels continue;
201 bb7fb738 2020-01-27 neels
202 bb7fb738 2020-01-27 neels debug("check identical lines around ");
203 bb7fb738 2020-01-27 neels debug_dump_atom(left, right, l);
204 bb7fb738 2020-01-27 neels
205 6c8c5d3f 2020-10-12 neels unsigned int r_idx = diff_atom_idx(right, PATIENCE(l).pos_in_other);
206 bb7fb738 2020-01-27 neels
207 d362ea2e 2020-07-25 stsp struct diff_range identical_l;
208 d362ea2e 2020-07-25 stsp struct diff_range identical_r;
209 bb7fb738 2020-01-27 neels
210 bb7fb738 2020-01-27 neels /* Swallow upwards.
211 0d27172a 2020-05-06 neels * Each common-unique line swallows identical lines upwards and
212 0d27172a 2020-05-06 neels * downwards.
213 0d27172a 2020-05-06 neels * All common-unique lines that were part of the identical lines
214 0d27172a 2020-05-06 neels * following below were already swallowed in the previous
215 0d27172a 2020-05-06 neels * iteration, so we will never hit another common-unique line
216 0d27172a 2020-05-06 neels * above. */
217 bb7fb738 2020-01-27 neels for (identical_l.start = l_idx, identical_r.start = r_idx;
218 b3fb4686 2020-09-20 neels identical_l.start > l_min && identical_r.start > r_min;
219 b3fb4686 2020-09-20 neels identical_l.start--, identical_r.start--) {
220 b3fb4686 2020-09-20 neels bool same;
221 b3fb4686 2020-09-20 neels int r = diff_atom_same(&same,
222 0d27172a 2020-05-06 neels &left->atoms.head[identical_l.start - 1],
223 0d27172a 2020-05-06 neels &right->atoms.head[identical_r.start - 1]);
224 b3fb4686 2020-09-20 neels if (r)
225 44cf4950 2020-09-20 neels return r;
226 b3fb4686 2020-09-20 neels if (!same)
227 b3fb4686 2020-09-20 neels break;
228 b3fb4686 2020-09-20 neels }
229 bb7fb738 2020-01-27 neels
230 bb7fb738 2020-01-27 neels /* Swallow downwards */
231 bb7fb738 2020-01-27 neels for (identical_l.end = l_idx + 1, identical_r.end = r_idx + 1;
232 bb7fb738 2020-01-27 neels identical_l.end < left->atoms.len
233 b3fb4686 2020-09-20 neels && identical_r.end < right->atoms.len;
234 bb7fb738 2020-01-27 neels identical_l.end++, identical_r.end++,
235 bb7fb738 2020-01-27 neels next_l_idx++) {
236 0d27172a 2020-05-06 neels struct diff_atom *l_end;
237 0d27172a 2020-05-06 neels struct diff_atom *r_end;
238 b3fb4686 2020-09-20 neels bool same;
239 b3fb4686 2020-09-20 neels int r = diff_atom_same(&same,
240 b3fb4686 2020-09-20 neels &left->atoms.head[identical_l.end],
241 b3fb4686 2020-09-20 neels &right->atoms.head[identical_r.end]);
242 b3fb4686 2020-09-20 neels if (r)
243 44cf4950 2020-09-20 neels return r;
244 b3fb4686 2020-09-20 neels if (!same)
245 b3fb4686 2020-09-20 neels break;
246 0d27172a 2020-05-06 neels l_end = &left->atoms.head[identical_l.end];
247 ab699b5c 2020-05-12 stsp r_end = &right->atoms.head[identical_r.end];
248 6c8c5d3f 2020-10-12 neels if (!PATIENCE(l_end).unique_in_both)
249 0d27172a 2020-05-06 neels continue;
250 0d27172a 2020-05-06 neels /* Part of a chunk of identical lines, remove from
251 0d27172a 2020-05-06 neels * listing of unique_in_both lines */
252 6c8c5d3f 2020-10-12 neels PATIENCE(l_end).unique_in_both = false;
253 6c8c5d3f 2020-10-12 neels PATIENCE(r_end).unique_in_both = false;
254 0d27172a 2020-05-06 neels (*unique_in_both_count)--;
255 bb7fb738 2020-01-27 neels }
256 bb7fb738 2020-01-27 neels
257 6c8c5d3f 2020-10-12 neels PATIENCE(l).identical_lines = identical_l;
258 6c8c5d3f 2020-10-12 neels PATIENCE(PATIENCE(l).pos_in_other).identical_lines =
259 0d27172a 2020-05-06 neels identical_r;
260 bb7fb738 2020-01-27 neels
261 bb7fb738 2020-01-27 neels l_min = identical_l.end;
262 bb7fb738 2020-01-27 neels r_min = identical_r.end;
263 bb7fb738 2020-01-27 neels
264 6c8c5d3f 2020-10-12 neels if (!diff_range_empty(&PATIENCE(l).identical_lines)) {
265 0d27172a 2020-05-06 neels debug("common-unique line at l=%u r=%u swallowed"
266 0d27172a 2020-05-06 neels " identical lines l=%u-%u r=%u-%u\n",
267 bb7fb738 2020-01-27 neels l_idx, r_idx,
268 bb7fb738 2020-01-27 neels identical_l.start, identical_l.end,
269 bb7fb738 2020-01-27 neels identical_r.start, identical_r.end);
270 bb7fb738 2020-01-27 neels }
271 bb7fb738 2020-01-27 neels debug("next_l_idx = %u\n", next_l_idx);
272 bb7fb738 2020-01-27 neels }
273 44cf4950 2020-09-20 neels return 0;
274 bb7fb738 2020-01-27 neels }
275 bb7fb738 2020-01-27 neels
276 0d27172a 2020-05-06 neels /* binary search to find the stack to put this atom "card" on. */
277 0d27172a 2020-05-06 neels static int
278 0d27172a 2020-05-06 neels find_target_stack(struct diff_atom *atom,
279 0d27172a 2020-05-06 neels struct diff_atom **patience_stacks,
280 0d27172a 2020-05-06 neels unsigned int patience_stacks_count)
281 0d27172a 2020-05-06 neels {
282 0d27172a 2020-05-06 neels unsigned int lo = 0;
283 0d27172a 2020-05-06 neels unsigned int hi = patience_stacks_count;
284 0d27172a 2020-05-06 neels while (lo < hi) {
285 0d27172a 2020-05-06 neels unsigned int mid = (lo + hi) >> 1;
286 0d27172a 2020-05-06 neels
287 6c8c5d3f 2020-10-12 neels if (PATIENCE(patience_stacks[mid]).pos_in_other
288 6c8c5d3f 2020-10-12 neels < PATIENCE(atom).pos_in_other)
289 0d27172a 2020-05-06 neels lo = mid + 1;
290 0d27172a 2020-05-06 neels else
291 0d27172a 2020-05-06 neels hi = mid;
292 0d27172a 2020-05-06 neels }
293 0d27172a 2020-05-06 neels return lo;
294 0d27172a 2020-05-06 neels }
295 0d27172a 2020-05-06 neels
296 0d27172a 2020-05-06 neels /* Among the lines that appear exactly once in each side, find the longest
297 0d27172a 2020-05-06 neels * streak that appear in both files in the same order (with other stuff allowed
298 0d27172a 2020-05-06 neels * to interleave). Use patience sort for that, as in the Patience Diff
299 0d27172a 2020-05-06 neels * algorithm.
300 0d27172a 2020-05-06 neels * See https://bramcohen.livejournal.com/73318.html and, for a much more
301 0d27172a 2020-05-06 neels * detailed explanation,
302 3b0f3d61 2020-01-22 neels * https://blog.jcoglan.com/2017/09/19/the-patience-diff-algorithm/ */
303 3e6cba3a 2020-08-13 stsp int
304 0d27172a 2020-05-06 neels diff_algo_patience(const struct diff_algo_config *algo_config,
305 0d27172a 2020-05-06 neels struct diff_state *state)
306 3b0f3d61 2020-01-22 neels {
307 44cf4950 2020-09-20 neels int rc;
308 3b0f3d61 2020-01-22 neels struct diff_data *left = &state->left;
309 3b0f3d61 2020-01-22 neels struct diff_data *right = &state->right;
310 6c8c5d3f 2020-10-12 neels struct atom_patience *atom_patience_left =
311 6c8c5d3f 2020-10-12 neels calloc(left->atoms.len, sizeof(struct atom_patience));
312 6c8c5d3f 2020-10-12 neels struct atom_patience *atom_patience_right =
313 6c8c5d3f 2020-10-12 neels calloc(right->atoms.len, sizeof(struct atom_patience));
314 3b0f3d61 2020-01-22 neels unsigned int unique_in_both_count;
315 6c8c5d3f 2020-10-12 neels struct diff_atom **lcs = NULL;
316 3b0f3d61 2020-01-22 neels
317 3b0f3d61 2020-01-22 neels debug("\n** %s\n", __func__);
318 3b0f3d61 2020-01-22 neels
319 6c8c5d3f 2020-10-12 neels left->root->current = left;
320 6c8c5d3f 2020-10-12 neels right->root->current = right;
321 6c8c5d3f 2020-10-12 neels left->algo_data = atom_patience_left;
322 6c8c5d3f 2020-10-12 neels right->algo_data = atom_patience_right;
323 6c8c5d3f 2020-10-12 neels
324 0d27172a 2020-05-06 neels /* Find those lines that appear exactly once in 'left' and exactly once
325 0d27172a 2020-05-06 neels * in 'right'. */
326 12c5bde7 2020-10-16 stsp rc = diff_atoms_mark_unique_in_both(left, right, &unique_in_both_count);
327 44cf4950 2020-09-20 neels if (rc)
328 6c8c5d3f 2020-10-12 neels goto free_and_exit;
329 3b0f3d61 2020-01-22 neels
330 3b0f3d61 2020-01-22 neels debug("unique_in_both_count %u\n", unique_in_both_count);
331 3b0f3d61 2020-01-22 neels debug("left:\n");
332 3b0f3d61 2020-01-22 neels debug_dump(left);
333 3b0f3d61 2020-01-22 neels debug("right:\n");
334 3b0f3d61 2020-01-22 neels debug_dump(right);
335 3b0f3d61 2020-01-22 neels
336 3b0f3d61 2020-01-22 neels if (!unique_in_both_count) {
337 0d27172a 2020-05-06 neels /* Cannot apply Patience, tell the caller to use fallback_algo
338 0d27172a 2020-05-06 neels * instead. */
339 6c8c5d3f 2020-10-12 neels rc = DIFF_RC_USE_DIFF_ALGO_FALLBACK;
340 6c8c5d3f 2020-10-12 neels goto free_and_exit;
341 3b0f3d61 2020-01-22 neels }
342 3b0f3d61 2020-01-22 neels
343 44cf4950 2020-09-20 neels rc = diff_atoms_swallow_identical_neighbors(left, right,
344 44cf4950 2020-09-20 neels &unique_in_both_count);
345 44cf4950 2020-09-20 neels if (rc)
346 6c8c5d3f 2020-10-12 neels goto free_and_exit;
347 bb7fb738 2020-01-27 neels debug("After swallowing identical neighbors: unique_in_both = %u\n",
348 bb7fb738 2020-01-27 neels unique_in_both_count);
349 bb7fb738 2020-01-27 neels
350 44cf4950 2020-09-20 neels rc = ENOMEM;
351 44cf4950 2020-09-20 neels
352 0d27172a 2020-05-06 neels /* An array of Longest Common Sequence is the result of the below
353 0d27172a 2020-05-06 neels * subscope: */
354 3b0f3d61 2020-01-22 neels unsigned int lcs_count = 0;
355 3b0f3d61 2020-01-22 neels struct diff_atom *lcs_tail = NULL;
356 3b0f3d61 2020-01-22 neels
357 3b0f3d61 2020-01-22 neels {
358 0d27172a 2020-05-06 neels /* This subscope marks the lifetime of the atom_pointers
359 0d27172a 2020-05-06 neels * allocation */
360 3b0f3d61 2020-01-22 neels
361 3b0f3d61 2020-01-22 neels /* One chunk of storage for atom pointers */
362 0d27172a 2020-05-06 neels struct diff_atom **atom_pointers;
363 0d27172a 2020-05-06 neels atom_pointers = recallocarray(NULL, 0, unique_in_both_count * 2,
364 0d27172a 2020-05-06 neels sizeof(struct diff_atom*));
365 3b0f3d61 2020-01-22 neels
366 0d27172a 2020-05-06 neels /* Half for the list of atoms that still need to be put on
367 0d27172a 2020-05-06 neels * stacks */
368 3b0f3d61 2020-01-22 neels struct diff_atom **uniques = atom_pointers;
369 3b0f3d61 2020-01-22 neels
370 0d27172a 2020-05-06 neels /* Half for the patience sort state's "card stacks" -- we
371 0d27172a 2020-05-06 neels * remember only each stack's topmost "card" */
372 0d27172a 2020-05-06 neels struct diff_atom **patience_stacks;
373 0d27172a 2020-05-06 neels patience_stacks = atom_pointers + unique_in_both_count;
374 3b0f3d61 2020-01-22 neels unsigned int patience_stacks_count = 0;
375 3b0f3d61 2020-01-22 neels
376 3b0f3d61 2020-01-22 neels /* Take all common, unique items from 'left' ... */
377 3b0f3d61 2020-01-22 neels
378 3b0f3d61 2020-01-22 neels struct diff_atom *atom;
379 3b0f3d61 2020-01-22 neels struct diff_atom **uniques_end = uniques;
380 3b0f3d61 2020-01-22 neels diff_data_foreach_atom(atom, left) {
381 6c8c5d3f 2020-10-12 neels if (!PATIENCE(atom).unique_in_both)
382 3b0f3d61 2020-01-22 neels continue;
383 3b0f3d61 2020-01-22 neels *uniques_end = atom;
384 3b0f3d61 2020-01-22 neels uniques_end++;
385 3b0f3d61 2020-01-22 neels }
386 3b0f3d61 2020-01-22 neels
387 3b0f3d61 2020-01-22 neels /* ...and sort them to the order found in 'right'.
388 0d27172a 2020-05-06 neels * The idea is to find the leftmost stack that has a higher line
389 0d27172a 2020-05-06 neels * number and add it to the stack's top.
390 0d27172a 2020-05-06 neels * If there is no such stack, open a new one on the right. The
391 0d27172a 2020-05-06 neels * line number is derived from the atom*, which are array items
392 0d27172a 2020-05-06 neels * and hence reflect the relative position in the source file.
393 0d27172a 2020-05-06 neels * So we got the common-uniques from 'left' and sort them
394 6c8c5d3f 2020-10-12 neels * according to PATIENCE(atom).pos_in_other. */
395 3b0f3d61 2020-01-22 neels unsigned int i;
396 3b0f3d61 2020-01-22 neels for (i = 0; i < unique_in_both_count; i++) {
397 3b0f3d61 2020-01-22 neels atom = uniques[i];
398 3b0f3d61 2020-01-22 neels unsigned int target_stack;
399 0d27172a 2020-05-06 neels target_stack = find_target_stack(atom, patience_stacks,
400 0d27172a 2020-05-06 neels patience_stacks_count);
401 3b0f3d61 2020-01-22 neels assert(target_stack <= patience_stacks_count);
402 3b0f3d61 2020-01-22 neels patience_stacks[target_stack] = atom;
403 3b0f3d61 2020-01-22 neels if (target_stack == patience_stacks_count)
404 3b0f3d61 2020-01-22 neels patience_stacks_count++;
405 3b0f3d61 2020-01-22 neels
406 0d27172a 2020-05-06 neels /* Record a back reference to the next stack on the
407 0d27172a 2020-05-06 neels * left, which will form the final longest sequence
408 3b0f3d61 2020-01-22 neels * later. */
409 6c8c5d3f 2020-10-12 neels PATIENCE(atom).prev_stack = target_stack ?
410 0d27172a 2020-05-06 neels patience_stacks[target_stack - 1] : NULL;
411 3b0f3d61 2020-01-22 neels
412 3b0f3d61 2020-01-22 neels }
413 3b0f3d61 2020-01-22 neels
414 0d27172a 2020-05-06 neels /* backtrace through prev_stack references to form the final
415 0d27172a 2020-05-06 neels * longest common sequence */
416 3b0f3d61 2020-01-22 neels lcs_tail = patience_stacks[patience_stacks_count - 1];
417 3b0f3d61 2020-01-22 neels lcs_count = patience_stacks_count;
418 3b0f3d61 2020-01-22 neels
419 0d27172a 2020-05-06 neels /* uniques and patience_stacks are no longer needed.
420 6c8c5d3f 2020-10-12 neels * Backpointers are in PATIENCE(atom).prev_stack */
421 3b0f3d61 2020-01-22 neels free(atom_pointers);
422 3b0f3d61 2020-01-22 neels }
423 3b0f3d61 2020-01-22 neels
424 3b0f3d61 2020-01-22 neels lcs = recallocarray(NULL, 0, lcs_count, sizeof(struct diff_atom*));
425 3b0f3d61 2020-01-22 neels struct diff_atom **lcs_backtrace_pos = &lcs[lcs_count - 1];
426 3b0f3d61 2020-01-22 neels struct diff_atom *atom;
427 6c8c5d3f 2020-10-12 neels for (atom = lcs_tail; atom; atom = PATIENCE(atom).prev_stack, lcs_backtrace_pos--) {
428 3b0f3d61 2020-01-22 neels assert(lcs_backtrace_pos >= lcs);
429 3b0f3d61 2020-01-22 neels *lcs_backtrace_pos = atom;
430 3b0f3d61 2020-01-22 neels }
431 3b0f3d61 2020-01-22 neels
432 3b0f3d61 2020-01-22 neels unsigned int i;
433 3b0f3d61 2020-01-22 neels if (DEBUG) {
434 3b0f3d61 2020-01-22 neels debug("\npatience LCS:\n");
435 3b0f3d61 2020-01-22 neels for (i = 0; i < lcs_count; i++) {
436 3b0f3d61 2020-01-22 neels debug_dump_atom(left, right, lcs[i]);
437 3b0f3d61 2020-01-22 neels }
438 3b0f3d61 2020-01-22 neels }
439 3b0f3d61 2020-01-22 neels
440 3b0f3d61 2020-01-22 neels
441 0d27172a 2020-05-06 neels /* TODO: For each common-unique line found (now listed in lcs), swallow
442 0d27172a 2020-05-06 neels * lines upwards and downwards that are identical on each side. Requires
443 0d27172a 2020-05-06 neels * a way to represent atoms being glued to adjacent atoms. */
444 3b0f3d61 2020-01-22 neels
445 3b0f3d61 2020-01-22 neels debug("\ntraverse LCS, possibly recursing:\n");
446 3b0f3d61 2020-01-22 neels
447 0d27172a 2020-05-06 neels /* Now we have pinned positions in both files at which it makes sense to
448 0d27172a 2020-05-06 neels * divide the diff problem into smaller chunks. Go into the next round:
449 0d27172a 2020-05-06 neels * look at each section in turn, trying to again find common-unique
450 0d27172a 2020-05-06 neels * lines in those smaller sections. As soon as no more are found, the
451 0d27172a 2020-05-06 neels * remaining smaller sections are solved by Myers. */
452 3b0f3d61 2020-01-22 neels unsigned int left_pos = 0;
453 3b0f3d61 2020-01-22 neels unsigned int right_pos = 0;
454 3b0f3d61 2020-01-22 neels for (i = 0; i <= lcs_count; i++) {
455 3b0f3d61 2020-01-22 neels struct diff_atom *atom;
456 bb7fb738 2020-01-27 neels struct diff_atom *atom_r;
457 3b0f3d61 2020-01-22 neels unsigned int left_idx;
458 3b0f3d61 2020-01-22 neels unsigned int right_idx;
459 3b0f3d61 2020-01-22 neels
460 3b0f3d61 2020-01-22 neels if (i < lcs_count) {
461 3b0f3d61 2020-01-22 neels atom = lcs[i];
462 6c8c5d3f 2020-10-12 neels atom_r = PATIENCE(atom).pos_in_other;
463 2a1b94d0 2020-09-26 stsp debug("lcs[%u] = left[%u] = right[%u]\n", i,
464 bb7fb738 2020-01-27 neels diff_atom_idx(left, atom), diff_atom_idx(right, atom_r));
465 6c8c5d3f 2020-10-12 neels left_idx = PATIENCE(atom).identical_lines.start;
466 6c8c5d3f 2020-10-12 neels right_idx = PATIENCE(atom_r).identical_lines.start;
467 bb7fb738 2020-01-27 neels debug(" identical lines l %u-%u r %u-%u\n",
468 6c8c5d3f 2020-10-12 neels PATIENCE(atom).identical_lines.start, PATIENCE(atom).identical_lines.end,
469 6c8c5d3f 2020-10-12 neels PATIENCE(atom_r).identical_lines.start, PATIENCE(atom_r).identical_lines.end);
470 3b0f3d61 2020-01-22 neels } else {
471 3b0f3d61 2020-01-22 neels atom = NULL;
472 bb7fb738 2020-01-27 neels atom_r = NULL;
473 3b0f3d61 2020-01-22 neels left_idx = left->atoms.len;
474 3b0f3d61 2020-01-22 neels right_idx = right->atoms.len;
475 3b0f3d61 2020-01-22 neels }
476 3b0f3d61 2020-01-22 neels
477 0d27172a 2020-05-06 neels /* 'atom' now marks an atom that matches on both sides according
478 0d27172a 2020-05-06 neels * to patience-diff (a common-unique identical atom in both
479 0d27172a 2020-05-06 neels * files).
480 0d27172a 2020-05-06 neels * Handle the section before and the atom itself; the section
481 0d27172a 2020-05-06 neels * after will be handled by the next loop iteration -- note that
482 0d27172a 2020-05-06 neels * i loops to last element + 1 ("i <= lcs_count"), so that there
483 0d27172a 2020-05-06 neels * will be another final iteration to pick up the last remaining
484 0d27172a 2020-05-06 neels * items after the last LCS atom.
485 3b0f3d61 2020-01-22 neels * The sections before might also be empty on left and/or right.
486 0d27172a 2020-05-06 neels * left_pos and right_pos mark the indexes of the first atoms
487 0d27172a 2020-05-06 neels * that have not yet been handled in the previous loop
488 0d27172a 2020-05-06 neels * iteration. left_idx and right_idx mark the indexes of the
489 0d27172a 2020-05-06 neels * matching atom on left and right, respectively. */
490 3b0f3d61 2020-01-22 neels
491 0d27172a 2020-05-06 neels debug("iteration %u left_pos %u left_idx %u"
492 0d27172a 2020-05-06 neels " right_pos %u right_idx %u\n",
493 3b0f3d61 2020-01-22 neels i, left_pos, left_idx, right_pos, right_idx);
494 3b0f3d61 2020-01-22 neels
495 3b0f3d61 2020-01-22 neels /* Section before the matching atom */
496 3b0f3d61 2020-01-22 neels struct diff_atom *left_atom = &left->atoms.head[left_pos];
497 3b0f3d61 2020-01-22 neels unsigned int left_section_len = left_idx - left_pos;
498 3b0f3d61 2020-01-22 neels
499 3b0f3d61 2020-01-22 neels struct diff_atom *right_atom = &(right->atoms.head[right_pos]);
500 3b0f3d61 2020-01-22 neels unsigned int right_section_len = right_idx - right_pos;
501 3b0f3d61 2020-01-22 neels
502 3b0f3d61 2020-01-22 neels if (left_section_len && right_section_len) {
503 0d27172a 2020-05-06 neels /* Record an unsolved chunk, the caller will apply
504 0d27172a 2020-05-06 neels * inner_algo() on this chunk. */
505 3b0f3d61 2020-01-22 neels if (!diff_state_add_chunk(state, false,
506 3b0f3d61 2020-01-22 neels left_atom, left_section_len,
507 0d27172a 2020-05-06 neels right_atom,
508 0d27172a 2020-05-06 neels right_section_len))
509 6c8c5d3f 2020-10-12 neels goto free_and_exit;
510 3b0f3d61 2020-01-22 neels } else if (left_section_len && !right_section_len) {
511 0d27172a 2020-05-06 neels /* Only left atoms and none on the right, they form a
512 0d27172a 2020-05-06 neels * "minus" chunk, then. */
513 3b0f3d61 2020-01-22 neels if (!diff_state_add_chunk(state, true,
514 3b0f3d61 2020-01-22 neels left_atom, left_section_len,
515 3b0f3d61 2020-01-22 neels right_atom, 0))
516 6c8c5d3f 2020-10-12 neels goto free_and_exit;
517 3b0f3d61 2020-01-22 neels } else if (!left_section_len && right_section_len) {
518 0d27172a 2020-05-06 neels /* No left atoms, only atoms on the right, they form a
519 0d27172a 2020-05-06 neels * "plus" chunk, then. */
520 3b0f3d61 2020-01-22 neels if (!diff_state_add_chunk(state, true,
521 3b0f3d61 2020-01-22 neels left_atom, 0,
522 3b0f3d61 2020-01-22 neels right_atom, right_section_len))
523 6c8c5d3f 2020-10-12 neels goto free_and_exit;
524 3b0f3d61 2020-01-22 neels }
525 0d27172a 2020-05-06 neels /* else: left_section_len == 0 and right_section_len == 0, i.e.
526 0d27172a 2020-05-06 neels * nothing here. */
527 3b0f3d61 2020-01-22 neels
528 0d27172a 2020-05-06 neels /* The atom found to match on both sides forms a chunk of equals
529 0d27172a 2020-05-06 neels * on each side. In the very last iteration of this loop, there
530 0d27172a 2020-05-06 neels * is no matching atom, we were just cleaning out the remaining
531 0d27172a 2020-05-06 neels * lines. */
532 3b0f3d61 2020-01-22 neels if (atom) {
533 0d27172a 2020-05-06 neels void *ok;
534 0d27172a 2020-05-06 neels ok = diff_state_add_chunk(state, true,
535 0d27172a 2020-05-06 neels left->atoms.head
536 6c8c5d3f 2020-10-12 neels + PATIENCE(atom).identical_lines.start,
537 6c8c5d3f 2020-10-12 neels diff_range_len(&PATIENCE(atom).identical_lines),
538 0d27172a 2020-05-06 neels right->atoms.head
539 6c8c5d3f 2020-10-12 neels + PATIENCE(atom_r).identical_lines.start,
540 6c8c5d3f 2020-10-12 neels diff_range_len(&PATIENCE(atom_r).identical_lines));
541 0d27172a 2020-05-06 neels if (!ok)
542 6c8c5d3f 2020-10-12 neels goto free_and_exit;
543 6c8c5d3f 2020-10-12 neels left_pos = PATIENCE(atom).identical_lines.end;
544 6c8c5d3f 2020-10-12 neels right_pos = PATIENCE(atom_r).identical_lines.end;
545 bb7fb738 2020-01-27 neels } else {
546 bb7fb738 2020-01-27 neels left_pos = left_idx + 1;
547 bb7fb738 2020-01-27 neels right_pos = right_idx + 1;
548 3b0f3d61 2020-01-22 neels }
549 0d27172a 2020-05-06 neels debug("end of iteration %u left_pos %u left_idx %u"
550 0d27172a 2020-05-06 neels " right_pos %u right_idx %u\n",
551 bb7fb738 2020-01-27 neels i, left_pos, left_idx, right_pos, right_idx);
552 3b0f3d61 2020-01-22 neels }
553 3b0f3d61 2020-01-22 neels debug("** END %s\n", __func__);
554 3b0f3d61 2020-01-22 neels
555 3b0f3d61 2020-01-22 neels rc = DIFF_RC_OK;
556 3b0f3d61 2020-01-22 neels
557 6c8c5d3f 2020-10-12 neels free_and_exit:
558 6c8c5d3f 2020-10-12 neels left->root->current = NULL;
559 6c8c5d3f 2020-10-12 neels right->root->current = NULL;
560 6c8c5d3f 2020-10-12 neels free(atom_patience_left);
561 6c8c5d3f 2020-10-12 neels free(atom_patience_right);
562 6c8c5d3f 2020-10-12 neels if (lcs)
563 6c8c5d3f 2020-10-12 neels free(lcs);
564 3b0f3d61 2020-01-22 neels return rc;
565 3b0f3d61 2020-01-22 neels }