File Coverage

deps/libgit2/src/xdiff/xprepare.c
Criterion Covered Total %
statement 183 232 78.8
branch 82 122 67.2
condition n/a
subroutine n/a
pod n/a
total 265 354 74.8


line stmt bran cond sub pod time code
1             /*
2             * LibXDiff by Davide Libenzi ( File Differential Library )
3             * Copyright (C) 2003 Davide Libenzi
4             *
5             * This library is free software; you can redistribute it and/or
6             * modify it under the terms of the GNU Lesser General Public
7             * License as published by the Free Software Foundation; either
8             * version 2.1 of the License, or (at your option) any later version.
9             *
10             * This library is distributed in the hope that it will be useful,
11             * but WITHOUT ANY WARRANTY; without even the implied warranty of
12             * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13             * Lesser General Public License for more details.
14             *
15             * You should have received a copy of the GNU Lesser General Public
16             * License along with this library; if not, see
17             * .
18             *
19             * Davide Libenzi
20             *
21             */
22              
23             #include "xinclude.h"
24              
25              
26             #define XDL_KPDIS_RUN 4
27             #define XDL_MAX_EQLIMIT 1024
28             #define XDL_SIMSCAN_WINDOW 100
29             #define XDL_GUESS_NLINES1 256
30             #define XDL_GUESS_NLINES2 20
31              
32              
33             typedef struct s_xdlclass {
34             struct s_xdlclass *next;
35             unsigned long ha;
36             char const *line;
37             long size;
38             long idx;
39             long len1, len2;
40             } xdlclass_t;
41              
42             typedef struct s_xdlclassifier {
43             unsigned int hbits;
44             long hsize;
45             xdlclass_t **rchash;
46             chastore_t ncha;
47             xdlclass_t **rcrecs;
48             long alloc;
49             long count;
50             long flags;
51             } xdlclassifier_t;
52              
53              
54              
55              
56             static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
57             static void xdl_free_classifier(xdlclassifier_t *cf);
58             static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
59             unsigned int hbits, xrecord_t *rec);
60             static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
61             xdlclassifier_t *cf, xdfile_t *xdf);
62             static void xdl_free_ctx(xdfile_t *xdf);
63             static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
64             static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
65             static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
66             static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
67              
68              
69              
70              
71 73           static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
72 73           cf->flags = flags;
73              
74 73           cf->hbits = xdl_hashbits((unsigned int) size);
75 73           cf->hsize = 1 << cf->hbits;
76              
77 73 50         if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {
78              
79 0           return -1;
80             }
81 73 50         if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) {
82              
83 0           xdl_cha_free(&cf->ncha);
84 0           return -1;
85             }
86 73           memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
87              
88 73           cf->alloc = size;
89 73 50         if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) {
90              
91 0           xdl_free(cf->rchash);
92 0           xdl_cha_free(&cf->ncha);
93 0           return -1;
94             }
95              
96 73           cf->count = 0;
97              
98 73           return 0;
99             }
100              
101              
102 73           static void xdl_free_classifier(xdlclassifier_t *cf) {
103              
104 73           xdl_free(cf->rcrecs);
105 73           xdl_free(cf->rchash);
106 73           xdl_cha_free(&cf->ncha);
107 73           }
108              
109              
110 163           static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
111             unsigned int hbits, xrecord_t *rec) {
112             long hi;
113             char const *line;
114             xdlclass_t *rcrec;
115             xdlclass_t **rcrecs;
116              
117 163           line = rec->ptr;
118 163           hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
119 176 100         for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
120 69           if (rcrec->ha == rec->ha &&
121 30           xdl_recmatch(rcrec->line, rcrec->size,
122             rec->ptr, rec->size, cf->flags))
123 26           break;
124              
125 163 100         if (!rcrec) {
126 137 50         if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {
127              
128 0           return -1;
129             }
130 137           rcrec->idx = cf->count++;
131 137 50         if (cf->count > cf->alloc) {
132 0           cf->alloc *= 2;
133 0 0         if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) {
134              
135 0           return -1;
136             }
137 0           cf->rcrecs = rcrecs;
138             }
139 137           cf->rcrecs[rcrec->idx] = rcrec;
140 137           rcrec->line = line;
141 137           rcrec->size = rec->size;
142 137           rcrec->ha = rec->ha;
143 137           rcrec->len1 = rcrec->len2 = 0;
144 137           rcrec->next = cf->rchash[hi];
145 137           cf->rchash[hi] = rcrec;
146             }
147              
148 163 100         (pass == 1) ? rcrec->len1++ : rcrec->len2++;
149              
150 163           rec->ha = (unsigned long) rcrec->idx;
151              
152 163           hi = (long) XDL_HASHLONG(rec->ha, hbits);
153 163           rec->next = rhash[hi];
154 163           rhash[hi] = rec;
155              
156 163           return 0;
157             }
158              
159              
160 146           static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
161             xdlclassifier_t *cf, xdfile_t *xdf) {
162             unsigned int hbits;
163             long nrec, hsize, bsize;
164             unsigned long hav;
165             char const *blk, *cur, *top, *prev;
166             xrecord_t *crec;
167             xrecord_t **recs, **rrecs;
168             xrecord_t **rhash;
169             unsigned long *ha;
170             char *rchg;
171             long *rindex;
172              
173 146           ha = NULL;
174 146           rindex = NULL;
175 146           rchg = NULL;
176 146           rhash = NULL;
177 146           recs = NULL;
178              
179 146 50         if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
180 0           goto abort;
181 146 50         if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
182 0           goto abort;
183              
184 146 50         if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
185 0           hbits = hsize = 0;
186             else {
187 146           hbits = xdl_hashbits((unsigned int) narec);
188 146           hsize = 1 << hbits;
189 146 50         if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
190 0           goto abort;
191 146           memset(rhash, 0, hsize * sizeof(xrecord_t *));
192             }
193              
194 146           nrec = 0;
195 146 50         if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
196 309 100         for (top = blk + bsize; cur < top; ) {
197 163           prev = cur;
198 163           hav = xdl_hash_record(&cur, top, xpp->flags);
199 163 50         if (nrec >= narec) {
200 0           narec *= 2;
201 0 0         if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
202 0           goto abort;
203 0           recs = rrecs;
204             }
205 163 50         if (!(crec = xdl_cha_alloc(&xdf->rcha)))
206 0           goto abort;
207 163           crec->ptr = prev;
208 163           crec->size = (long) (cur - prev);
209 163           crec->ha = hav;
210 163           recs[nrec++] = crec;
211              
212 326           if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
213 163           xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
214 0           goto abort;
215             }
216             }
217              
218 146 50         if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
219 0           goto abort;
220 146           memset(rchg, 0, (nrec + 2) * sizeof(char));
221              
222 146 50         if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
223 0           goto abort;
224 146 50         if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
225 0           goto abort;
226              
227 146           xdf->nrec = nrec;
228 146           xdf->recs = recs;
229 146           xdf->hbits = hbits;
230 146           xdf->rhash = rhash;
231 146           xdf->rchg = rchg + 1;
232 146           xdf->rindex = rindex;
233 146           xdf->nreff = 0;
234 146           xdf->ha = ha;
235 146           xdf->dstart = 0;
236 146           xdf->dend = nrec - 1;
237              
238 146           return 0;
239              
240             abort:
241 0           xdl_free(ha);
242 0           xdl_free(rindex);
243 0           xdl_free(rchg);
244 0           xdl_free(rhash);
245 0           xdl_free(recs);
246 0           xdl_cha_free(&xdf->rcha);
247 146           return -1;
248             }
249              
250              
251 146           static void xdl_free_ctx(xdfile_t *xdf) {
252              
253 146           xdl_free(xdf->rhash);
254 146           xdl_free(xdf->rindex);
255 146           xdl_free(xdf->rchg - 1);
256 146           xdl_free(xdf->ha);
257 146           xdl_free(xdf->recs);
258 146           xdl_cha_free(&xdf->rcha);
259 146           }
260              
261              
262 73           int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
263             xdfenv_t *xe) {
264             long enl1, enl2, sample;
265             xdlclassifier_t cf;
266              
267 73           memset(&cf, 0, sizeof(cf));
268              
269             /*
270             * For histogram diff, we can afford a smaller sample size and
271             * thus a poorer estimate of the number of lines, as the hash
272             * table (rhash) won't be filled up/grown. The number of lines
273             * (nrecs) will be updated correctly anyway by
274             * xdl_prepare_ctx().
275             */
276 73 50         sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
277             ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
278              
279 73           enl1 = xdl_guess_lines(mf1, sample) + 1;
280 73           enl2 = xdl_guess_lines(mf2, sample) + 1;
281              
282 146           if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF &&
283 73           xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
284 0           return -1;
285              
286 73 50         if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
287              
288 0           xdl_free_classifier(&cf);
289 0           return -1;
290             }
291 73 50         if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
292              
293 0           xdl_free_ctx(&xe->xdf1);
294 0           xdl_free_classifier(&cf);
295 0           return -1;
296             }
297              
298 73 50         if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
    50          
299 73 50         (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
300 73           xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
301              
302 0           xdl_free_ctx(&xe->xdf2);
303 0           xdl_free_ctx(&xe->xdf1);
304 0           xdl_free_classifier(&cf);
305 0           return -1;
306             }
307              
308 73 50         if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)
309 73           xdl_free_classifier(&cf);
310              
311 73           return 0;
312             }
313              
314              
315 73           void xdl_free_env(xdfenv_t *xe) {
316              
317 73           xdl_free_ctx(&xe->xdf2);
318 73           xdl_free_ctx(&xe->xdf1);
319 73           }
320              
321              
322 3           static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
323             long r, rdis0, rpdis0, rdis1, rpdis1;
324              
325             /*
326             * Limits the window the is examined during the similar-lines
327             * scan. The loops below stops when dis[i - r] == 1 (line that
328             * has no match), but there are corner cases where the loop
329             * proceed all the way to the extremities by causing huge
330             * performance penalties in case of big files.
331             */
332 3 50         if (i - s > XDL_SIMSCAN_WINDOW)
333 0           s = i - XDL_SIMSCAN_WINDOW;
334 3 50         if (e - i > XDL_SIMSCAN_WINDOW)
335 0           e = i + XDL_SIMSCAN_WINDOW;
336              
337             /*
338             * Scans the lines before 'i' to find a run of lines that either
339             * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
340             * Note that we always call this function with dis[i] > 1, so the
341             * current line (i) is already a multimatch line.
342             */
343 7 100         for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
344 4 100         if (!dis[i - r])
345 3           rdis0++;
346 1 50         else if (dis[i - r] == 2)
347 1           rpdis0++;
348             else
349 0           break;
350             }
351             /*
352             * If the run before the line 'i' found only multimatch lines, we
353             * return 0 and hence we don't make the current line (i) discarded.
354             * We want to discard multimatch lines only when they appear in the
355             * middle of runs with nomatch lines (dis[j] == 0).
356             */
357 3 100         if (rdis0 == 0)
358 1           return 0;
359 6 100         for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
360 4 100         if (!dis[i + r])
361 3           rdis1++;
362 1 50         else if (dis[i + r] == 2)
363 1           rpdis1++;
364             else
365 0           break;
366             }
367             /*
368             * If the run after the line 'i' found only multimatch lines, we
369             * return 0 and hence we don't make the current line (i) discarded.
370             */
371 2 50         if (rdis1 == 0)
372 0           return 0;
373 2           rdis1 += rdis0;
374 2           rpdis1 += rpdis0;
375              
376 2           return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
377             }
378              
379              
380             /*
381             * Try to reduce the problem complexity, discard records that have no
382             * matches on the other file. Also, lines that have multiple matches
383             * might be potentially discarded if they happear in a run of discardable.
384             */
385 73           static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
386             long i, nm, nreff, mlim;
387             xrecord_t **recs;
388             xdlclass_t *rcrec;
389             char *dis, *dis1, *dis2;
390              
391 73 50         if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
392              
393 0           return -1;
394             }
395 73           memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
396 73           dis1 = dis;
397 73           dis2 = dis1 + xdf1->nrec + 1;
398              
399 73 50         if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
400 0           mlim = XDL_MAX_EQLIMIT;
401 124 100         for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
402 51           rcrec = cf->rcrecs[(*recs)->ha];
403 51 50         nm = rcrec ? rcrec->len2 : 0;
404 51 100         dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
    100          
405             }
406              
407 73 50         if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
408 0           mlim = XDL_MAX_EQLIMIT;
409 155 100         for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
410 82           rcrec = cf->rcrecs[(*recs)->ha];
411 82 50         nm = rcrec ? rcrec->len1 : 0;
412 82 100         dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
    50          
413             }
414              
415 124 100         for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
416 51           i <= xdf1->dend; i++, recs++) {
417 51 100         if (dis1[i] == 1 ||
    100          
418 1 50         (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
419 6           xdf1->rindex[nreff] = i;
420 6           xdf1->ha[nreff] = (*recs)->ha;
421 6           nreff++;
422             } else
423 45           xdf1->rchg[i] = 1;
424             }
425 73           xdf1->nreff = nreff;
426              
427 155 100         for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
428 82           i <= xdf2->dend; i++, recs++) {
429 82 50         if (dis2[i] == 1 ||
    100          
430 2 50         (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
431 2           xdf2->rindex[nreff] = i;
432 2           xdf2->ha[nreff] = (*recs)->ha;
433 2           nreff++;
434             } else
435 80           xdf2->rchg[i] = 1;
436             }
437 73           xdf2->nreff = nreff;
438              
439 73           xdl_free(dis);
440              
441 73           return 0;
442             }
443              
444              
445             /*
446             * Early trim initial and terminal matching records.
447             */
448 73           static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
449             long i, lim;
450             xrecord_t **recs1, **recs2;
451              
452 73           recs1 = xdf1->recs;
453 73           recs2 = xdf2->recs;
454 82 100         for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
455 9           i++, recs1++, recs2++)
456 53 100         if ((*recs1)->ha != (*recs2)->ha)
457 44           break;
458              
459 73           xdf1->dstart = xdf2->dstart = i;
460              
461 73           recs1 = xdf1->recs + xdf1->nrec - 1;
462 73           recs2 = xdf2->recs + xdf2->nrec - 1;
463 79 100         for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
464 50 100         if ((*recs1)->ha != (*recs2)->ha)
465 44           break;
466              
467 73           xdf1->dend = xdf1->nrec - i - 1;
468 73           xdf2->dend = xdf2->nrec - i - 1;
469              
470 73           return 0;
471             }
472              
473              
474 73           static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
475              
476 146           if (xdl_trim_ends(xdf1, xdf2) < 0 ||
477 73           xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
478              
479 0           return -1;
480             }
481              
482 73           return 0;
483             }