Merge branch 'maint'
[gitweb.git] / xdiff / xprepare.c
index 27a08791a2eee9f5f0565509e2c6bf2aa7b768c8..620fc9a657e2246d3a382c916c2cdd4f820c0c44 100644 (file)
 #include "xinclude.h"
 
 
-
 #define XDL_KPDIS_RUN 4
-
+#define XDL_MAX_EQLIMIT 1024
+#define XDL_SIMSCAN_WINDOW 100
+#define XDL_GUESS_NLINES1 256
+#define XDL_GUESS_NLINES2 20
 
 
 typedef struct s_xdlclass {
@@ -42,12 +44,13 @@ typedef struct s_xdlclassifier {
        xdlclass_t **rchash;
        chastore_t ncha;
        long count;
+       long flags;
 } xdlclassifier_t;
 
 
 
 
-static int xdl_init_classifier(xdlclassifier_t *cf, long size);
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
 static void xdl_free_classifier(xdlclassifier_t *cf);
 static int xdl_classify_record(xdlclassifier_t *cf, xrecord_t **rhash, unsigned int hbits,
                               xrecord_t *rec);
@@ -62,8 +65,8 @@ static int xdl_optimize_ctxs(xdfile_t *xdf1, xdfile_t *xdf2);
 
 
 
-static int xdl_init_classifier(xdlclassifier_t *cf, long size) {
-       long i;
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
+       cf->flags = flags;
 
        cf->hbits = xdl_hashbits((unsigned int) size);
        cf->hsize = 1 << cf->hbits;
@@ -77,8 +80,7 @@ static int xdl_init_classifier(xdlclassifier_t *cf, long size) {
                xdl_cha_free(&cf->ncha);
                return -1;
        }
-       for (i = 0; i < cf->hsize; i++)
-               cf->rchash[i] = NULL;
+       memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
 
        cf->count = 0;
 
@@ -102,8 +104,9 @@ static int xdl_classify_record(xdlclassifier_t *cf, xrecord_t **rhash, unsigned
        line = rec->ptr;
        hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
        for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
-               if (rcrec->ha == rec->ha && rcrec->size == rec->size &&
-                   !memcmp(line, rcrec->line, rec->size))
+               if (rcrec->ha == rec->ha &&
+                               xdl_recmatch(rcrec->line, rcrec->size,
+                                       rec->ptr, rec->size, cf->flags))
                        break;
 
        if (!rcrec) {
@@ -132,7 +135,7 @@ static int xdl_classify_record(xdlclassifier_t *cf, xrecord_t **rhash, unsigned
 static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
                           xdlclassifier_t *cf, xdfile_t *xdf) {
        unsigned int hbits;
-       long i, nrec, hsize, bsize;
+       long nrec, hsize, bsize;
        unsigned long hav;
        char const *blk, *cur, *top, *prev;
        xrecord_t *crec;
@@ -142,96 +145,59 @@ static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
        char *rchg;
        long *rindex;
 
-       if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0) {
-
-               return -1;
+       ha = NULL;
+       rindex = NULL;
+       rchg = NULL;
+       rhash = NULL;
+       recs = NULL;
+
+       if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
+               goto abort;
+       if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
+               goto abort;
+
+       if (xpp->flags & XDF_HISTOGRAM_DIFF)
+               hbits = hsize = 0;
+       else {
+               hbits = xdl_hashbits((unsigned int) narec);
+               hsize = 1 << hbits;
+               if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+                       goto abort;
+               memset(rhash, 0, hsize * sizeof(xrecord_t *));
        }
-       if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) {
-
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
-
-       hbits = xdl_hashbits((unsigned int) narec);
-       hsize = 1 << hbits;
-       if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) {
-
-               xdl_free(recs);
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
-       for (i = 0; i < hsize; i++)
-               rhash[i] = NULL;
 
        nrec = 0;
        if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
-               for (top = blk + bsize;;) {
-                       if (cur >= top) {
-                               if (!(cur = blk = xdl_mmfile_next(mf, &bsize)))
-                                       break;
-                               top = blk + bsize;
-                       }
+               for (top = blk + bsize; cur < top; ) {
                        prev = cur;
-                       hav = xdl_hash_record(&cur, top);
+                       hav = xdl_hash_record(&cur, top, xpp->flags);
                        if (nrec >= narec) {
                                narec *= 2;
-                               if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) {
-
-                                       xdl_free(rhash);
-                                       xdl_free(recs);
-                                       xdl_cha_free(&xdf->rcha);
-                                       return -1;
-                               }
+                               if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
+                                       goto abort;
                                recs = rrecs;
                        }
-                       if (!(crec = xdl_cha_alloc(&xdf->rcha))) {
-
-                               xdl_free(rhash);
-                               xdl_free(recs);
-                               xdl_cha_free(&xdf->rcha);
-                               return -1;
-                       }
+                       if (!(crec = xdl_cha_alloc(&xdf->rcha)))
+                               goto abort;
                        crec->ptr = prev;
                        crec->size = (long) (cur - prev);
                        crec->ha = hav;
                        recs[nrec++] = crec;
 
-                       if (xdl_classify_record(cf, rhash, hbits, crec) < 0) {
-
-                               xdl_free(rhash);
-                               xdl_free(recs);
-                               xdl_cha_free(&xdf->rcha);
-                               return -1;
-                       }
+                       if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+                               xdl_classify_record(cf, rhash, hbits, crec) < 0)
+                               goto abort;
                }
        }
 
-       if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char)))) {
-
-               xdl_free(rhash);
-               xdl_free(recs);
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
+       if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
+               goto abort;
        memset(rchg, 0, (nrec + 2) * sizeof(char));
 
-       if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long)))) {
-
-               xdl_free(rchg);
-               xdl_free(rhash);
-               xdl_free(recs);
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
-       if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) {
-
-               xdl_free(rindex);
-               xdl_free(rchg);
-               xdl_free(rhash);
-               xdl_free(recs);
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
+       if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
+               goto abort;
+       if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
+               goto abort;
 
        xdf->nrec = nrec;
        xdf->recs = recs;
@@ -245,6 +211,15 @@ static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
        xdf->dend = nrec - 1;
 
        return 0;
+
+abort:
+       xdl_free(ha);
+       xdl_free(rindex);
+       xdl_free(rchg);
+       xdl_free(rhash);
+       xdl_free(recs);
+       xdl_cha_free(&xdf->rcha);
+       return -1;
 }
 
 
@@ -261,13 +236,23 @@ static void xdl_free_ctx(xdfile_t *xdf) {
 
 int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                    xdfenv_t *xe) {
-       long enl1, enl2;
+       long enl1, enl2, sample;
        xdlclassifier_t cf;
 
-       enl1 = xdl_guess_lines(mf1) + 1;
-       enl2 = xdl_guess_lines(mf2) + 1;
+       /*
+        * For histogram diff, we can afford a smaller sample size and
+        * thus a poorer estimate of the number of lines, as the hash
+        * table (rhash) won't be filled up/grown. The number of lines
+        * (nrecs) will be updated correctly anyway by
+        * xdl_prepare_ctx().
+        */
+       sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+
+       enl1 = xdl_guess_lines(mf1, sample) + 1;
+       enl2 = xdl_guess_lines(mf2, sample) + 1;
 
-       if (xdl_init_classifier(&cf, enl1 + enl2 + 1) < 0) {
+       if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+               xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
 
                return -1;
        }
@@ -284,9 +269,12 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                return -1;
        }
 
-       xdl_free_classifier(&cf);
+       if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+               xdl_free_classifier(&cf);
 
-       if (xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) {
+       if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
+                       !(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+                       xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) {
 
                xdl_free_ctx(&xe->xdf2);
                xdl_free_ctx(&xe->xdf1);
@@ -305,26 +293,60 @@ void xdl_free_env(xdfenv_t *xe) {
 
 
 static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
-       long r, rdis, rpdis;
-
-       for (r = 1, rdis = 0, rpdis = 1; (i - r) >= s; r++) {
+       long r, rdis0, rpdis0, rdis1, rpdis1;
+
+       /*
+        * Limits the window the is examined during the similar-lines
+        * scan. The loops below stops when dis[i - r] == 1 (line that
+        * has no match), but there are corner cases where the loop
+        * proceed all the way to the extremities by causing huge
+        * performance penalties in case of big files.
+        */
+       if (i - s > XDL_SIMSCAN_WINDOW)
+               s = i - XDL_SIMSCAN_WINDOW;
+       if (e - i > XDL_SIMSCAN_WINDOW)
+               e = i + XDL_SIMSCAN_WINDOW;
+
+       /*
+        * Scans the lines before 'i' to find a run of lines that either
+        * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
+        * Note that we always call this function with dis[i] > 1, so the
+        * current line (i) is already a multimatch line.
+        */
+       for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
                if (!dis[i - r])
-                       rdis++;
+                       rdis0++;
                else if (dis[i - r] == 2)
-                       rpdis++;
+                       rpdis0++;
                else
                        break;
        }
-       for (r = 1; (i + r) <= e; r++) {
+       /*
+        * If the run before the line 'i' found only multimatch lines, we
+        * return 0 and hence we don't make the current line (i) discarded.
+        * We want to discard multimatch lines only when they appear in the
+        * middle of runs with nomatch lines (dis[j] == 0).
+        */
+       if (rdis0 == 0)
+               return 0;
+       for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
                if (!dis[i + r])
-                       rdis++;
+                       rdis1++;
                else if (dis[i + r] == 2)
-                       rpdis++;
+                       rpdis1++;
                else
                        break;
        }
-
-       return rpdis * XDL_KPDIS_RUN < (rpdis + rdis);
+       /*
+        * If the run after the line 'i' found only multimatch lines, we
+        * return 0 and hence we don't make the current line (i) discarded.
+        */
+       if (rdis1 == 0)
+               return 0;
+       rdis1 += rdis0;
+       rpdis1 += rpdis0;
+
+       return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
 }
 
 
@@ -334,34 +356,40 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
  * might be potentially discarded if they happear in a run of discardable.
  */
 static int xdl_cleanup_records(xdfile_t *xdf1, xdfile_t *xdf2) {
-       long i, rhi, nreff;
+       long i, nm, rhi, nreff, mlim;
        unsigned long hav;
        xrecord_t **recs;
        xrecord_t *rec;
        char *dis, *dis1, *dis2;
 
-       if (!(dis = (char *) xdl_malloc((xdf1->nrec + xdf2->nrec + 2) * sizeof(char)))) {
+       if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
 
                return -1;
        }
-       memset(dis, 0, (xdf1->nrec + xdf2->nrec + 2) * sizeof(char));
+       memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
        dis1 = dis;
        dis2 = dis1 + xdf1->nrec + 1;
 
+       if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
+               mlim = XDL_MAX_EQLIMIT;
        for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
                hav = (*recs)->ha;
                rhi = (long) XDL_HASHLONG(hav, xdf2->hbits);
-               for (rec = xdf2->rhash[rhi]; rec; rec = rec->next)
-                       if (rec->ha == hav && ++dis1[i] == 2)
+               for (nm = 0, rec = xdf2->rhash[rhi]; rec; rec = rec->next)
+                       if (rec->ha == hav && ++nm == mlim)
                                break;
+               dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
        }
 
+       if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
+               mlim = XDL_MAX_EQLIMIT;
        for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
                hav = (*recs)->ha;
                rhi = (long) XDL_HASHLONG(hav, xdf1->hbits);
-               for (rec = xdf1->rhash[rhi]; rec; rec = rec->next)
-                       if (rec->ha == hav && ++dis2[i] == 2)
+               for (nm = 0, rec = xdf1->rhash[rhi]; rec; rec = rec->next)
+                       if (rec->ha == hav && ++nm == mlim)
                                break;
+               dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
        }
 
        for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
@@ -433,4 +461,3 @@ static int xdl_optimize_ctxs(xdfile_t *xdf1, xdfile_t *xdf2) {
 
        return 0;
 }
-