File Coverage

deps/libgit2/src/libgit2/xdiff/xprepare.c
Criterion Covered Total %
statement 181 229 79.0
branch 84 126 66.6
condition n/a
subroutine n/a
pod n/a
total 265 355 74.6


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           hbits = xdl_hashbits((unsigned int) narec);
185 146           hsize = 1 << hbits;
186 146 50         if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
187 0           goto abort;
188 146           memset(rhash, 0, hsize * sizeof(xrecord_t *));
189              
190 146           nrec = 0;
191 146 50         if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
192 309 100         for (top = blk + bsize; cur < top; ) {
193 163           prev = cur;
194 163           hav = xdl_hash_record(&cur, top, xpp->flags);
195 163 50         if (nrec >= narec) {
196 0           narec *= 2;
197 0 0         if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
198 0           goto abort;
199 0           recs = rrecs;
200             }
201 163 50         if (!(crec = xdl_cha_alloc(&xdf->rcha)))
202 0           goto abort;
203 163           crec->ptr = prev;
204 163           crec->size = (long) (cur - prev);
205 163           crec->ha = hav;
206 163           recs[nrec++] = crec;
207 163 50         if (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
208 0           goto abort;
209             }
210             }
211              
212 146 50         if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
213 0           goto abort;
214 146           memset(rchg, 0, (nrec + 2) * sizeof(char));
215              
216 146 50         if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
    50          
217 146           (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
218 146 50         if (!(rindex = xdl_malloc((nrec + 1) * sizeof(*rindex))))
219 0           goto abort;
220 146 50         if (!(ha = xdl_malloc((nrec + 1) * sizeof(*ha))))
221 0           goto abort;
222             }
223              
224 146           xdf->nrec = nrec;
225 146           xdf->recs = recs;
226 146           xdf->hbits = hbits;
227 146           xdf->rhash = rhash;
228 146           xdf->rchg = rchg + 1;
229 146           xdf->rindex = rindex;
230 146           xdf->nreff = 0;
231 146           xdf->ha = ha;
232 146           xdf->dstart = 0;
233 146           xdf->dend = nrec - 1;
234              
235 146           return 0;
236              
237             abort:
238 0           xdl_free(ha);
239 0           xdl_free(rindex);
240 0           xdl_free(rchg);
241 0           xdl_free(rhash);
242 0           xdl_free(recs);
243 0           xdl_cha_free(&xdf->rcha);
244 146           return -1;
245             }
246              
247              
248 146           static void xdl_free_ctx(xdfile_t *xdf) {
249              
250 146           xdl_free(xdf->rhash);
251 146           xdl_free(xdf->rindex);
252 146           xdl_free(xdf->rchg - 1);
253 146           xdl_free(xdf->ha);
254 146           xdl_free(xdf->recs);
255 146           xdl_cha_free(&xdf->rcha);
256 146           }
257              
258              
259 73           int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
260             xdfenv_t *xe) {
261             long enl1, enl2, sample;
262             xdlclassifier_t cf;
263              
264 73           memset(&cf, 0, sizeof(cf));
265              
266             /*
267             * For histogram diff, we can afford a smaller sample size and
268             * thus a poorer estimate of the number of lines, as the hash
269             * table (rhash) won't be filled up/grown. The number of lines
270             * (nrecs) will be updated correctly anyway by
271             * xdl_prepare_ctx().
272             */
273 73 50         sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
274             ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
275              
276 73           enl1 = xdl_guess_lines(mf1, sample) + 1;
277 73           enl2 = xdl_guess_lines(mf2, sample) + 1;
278              
279 73 50         if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
280 0           return -1;
281              
282 73 50         if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
283              
284 0           xdl_free_classifier(&cf);
285 0           return -1;
286             }
287 73 50         if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
288              
289 0           xdl_free_ctx(&xe->xdf1);
290 0           xdl_free_classifier(&cf);
291 0           return -1;
292             }
293              
294 73 50         if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
    50          
295 73 50         (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
296 73           xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
297              
298 0           xdl_free_ctx(&xe->xdf2);
299 0           xdl_free_ctx(&xe->xdf1);
300 0           xdl_free_classifier(&cf);
301 0           return -1;
302             }
303              
304 73           xdl_free_classifier(&cf);
305              
306 73           return 0;
307             }
308              
309              
310 73           void xdl_free_env(xdfenv_t *xe) {
311              
312 73           xdl_free_ctx(&xe->xdf2);
313 73           xdl_free_ctx(&xe->xdf1);
314 73           }
315              
316              
317 3           static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
318             long r, rdis0, rpdis0, rdis1, rpdis1;
319              
320             /*
321             * Limits the window the is examined during the similar-lines
322             * scan. The loops below stops when dis[i - r] == 1 (line that
323             * has no match), but there are corner cases where the loop
324             * proceed all the way to the extremities by causing huge
325             * performance penalties in case of big files.
326             */
327 3 50         if (i - s > XDL_SIMSCAN_WINDOW)
328 0           s = i - XDL_SIMSCAN_WINDOW;
329 3 50         if (e - i > XDL_SIMSCAN_WINDOW)
330 0           e = i + XDL_SIMSCAN_WINDOW;
331              
332             /*
333             * Scans the lines before 'i' to find a run of lines that either
334             * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
335             * Note that we always call this function with dis[i] > 1, so the
336             * current line (i) is already a multimatch line.
337             */
338 7 100         for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
339 4 100         if (!dis[i - r])
340 3           rdis0++;
341 1 50         else if (dis[i - r] == 2)
342 1           rpdis0++;
343             else
344 0           break;
345             }
346             /*
347             * If the run before the line 'i' found only multimatch lines, we
348             * return 0 and hence we don't make the current line (i) discarded.
349             * We want to discard multimatch lines only when they appear in the
350             * middle of runs with nomatch lines (dis[j] == 0).
351             */
352 3 100         if (rdis0 == 0)
353 1           return 0;
354 6 100         for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
355 4 100         if (!dis[i + r])
356 3           rdis1++;
357 1 50         else if (dis[i + r] == 2)
358 1           rpdis1++;
359             else
360 0           break;
361             }
362             /*
363             * If the run after the line 'i' found only multimatch lines, we
364             * return 0 and hence we don't make the current line (i) discarded.
365             */
366 2 50         if (rdis1 == 0)
367 0           return 0;
368 2           rdis1 += rdis0;
369 2           rpdis1 += rpdis0;
370              
371 2           return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
372             }
373              
374              
375             /*
376             * Try to reduce the problem complexity, discard records that have no
377             * matches on the other file. Also, lines that have multiple matches
378             * might be potentially discarded if they happear in a run of discardable.
379             */
380 73           static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
381             long i, nm, nreff, mlim;
382             xrecord_t **recs;
383             xdlclass_t *rcrec;
384             char *dis, *dis1, *dis2;
385              
386 73 50         if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
387              
388 0           return -1;
389             }
390 73           memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
391 73           dis1 = dis;
392 73           dis2 = dis1 + xdf1->nrec + 1;
393              
394 73 50         if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
395 0           mlim = XDL_MAX_EQLIMIT;
396 124 100         for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
397 51           rcrec = cf->rcrecs[(*recs)->ha];
398 51 50         nm = rcrec ? rcrec->len2 : 0;
399 51 100         dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
    100          
400             }
401              
402 73 50         if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
403 0           mlim = XDL_MAX_EQLIMIT;
404 155 100         for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
405 82           rcrec = cf->rcrecs[(*recs)->ha];
406 82 50         nm = rcrec ? rcrec->len1 : 0;
407 82 100         dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
    50          
408             }
409              
410 124 100         for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
411 51           i <= xdf1->dend; i++, recs++) {
412 51 100         if (dis1[i] == 1 ||
    100          
413 1 50         (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
414 6           xdf1->rindex[nreff] = i;
415 6           xdf1->ha[nreff] = (*recs)->ha;
416 6           nreff++;
417             } else
418 45           xdf1->rchg[i] = 1;
419             }
420 73           xdf1->nreff = nreff;
421              
422 155 100         for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
423 82           i <= xdf2->dend; i++, recs++) {
424 82 50         if (dis2[i] == 1 ||
    100          
425 2 50         (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
426 2           xdf2->rindex[nreff] = i;
427 2           xdf2->ha[nreff] = (*recs)->ha;
428 2           nreff++;
429             } else
430 80           xdf2->rchg[i] = 1;
431             }
432 73           xdf2->nreff = nreff;
433              
434 73           xdl_free(dis);
435              
436 73           return 0;
437             }
438              
439              
440             /*
441             * Early trim initial and terminal matching records.
442             */
443 73           static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
444             long i, lim;
445             xrecord_t **recs1, **recs2;
446              
447 73           recs1 = xdf1->recs;
448 73           recs2 = xdf2->recs;
449 82 100         for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
450 9           i++, recs1++, recs2++)
451 53 100         if ((*recs1)->ha != (*recs2)->ha)
452 44           break;
453              
454 73           xdf1->dstart = xdf2->dstart = i;
455              
456 73           recs1 = xdf1->recs + xdf1->nrec - 1;
457 73           recs2 = xdf2->recs + xdf2->nrec - 1;
458 79 100         for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
459 50 100         if ((*recs1)->ha != (*recs2)->ha)
460 44           break;
461              
462 73           xdf1->dend = xdf1->nrec - i - 1;
463 73           xdf2->dend = xdf2->nrec - i - 1;
464              
465 73           return 0;
466             }
467              
468              
469 73           static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
470              
471 146           if (xdl_trim_ends(xdf1, xdf2) < 0 ||
472 73           xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
473              
474 0           return -1;
475             }
476              
477 73           return 0;
478             }