Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano
2023-03-12 14:34:41 -07:00
committed by dummy
commit 63264f229d
14 changed files with 3581 additions and 0 deletions

153
xdiff.h Normal file
View File

@@ -0,0 +1,153 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XDIFF_H)
#define XDIFF_H
#ifdef __cplusplus
extern "C" {
#endif /* #ifdef __cplusplus */
/* xpparm_t.flags */
#define XDF_NEED_MINIMAL (1 << 0)
#define XDF_IGNORE_WHITESPACE (1 << 1)
#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 2)
#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 3)
#define XDF_IGNORE_CR_AT_EOL (1 << 4)
#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | \
XDF_IGNORE_WHITESPACE_CHANGE | \
XDF_IGNORE_WHITESPACE_AT_EOL | \
XDF_IGNORE_CR_AT_EOL)
#define XDF_IGNORE_BLANK_LINES (1 << 7)
#define XDF_PATIENCE_DIFF (1 << 14)
#define XDF_HISTOGRAM_DIFF (1 << 15)
#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
#define XDF_INDENT_HEURISTIC (1 << 23)
/* xdemitconf_t.flags */
#define XDL_EMIT_FUNCNAMES (1 << 0)
#define XDL_EMIT_NO_HUNK_HDR (1 << 1)
#define XDL_EMIT_FUNCCONTEXT (1 << 2)
/* merge simplification levels */
#define XDL_MERGE_MINIMAL 0
#define XDL_MERGE_EAGER 1
#define XDL_MERGE_ZEALOUS 2
#define XDL_MERGE_ZEALOUS_ALNUM 3
/* merge favor modes */
#define XDL_MERGE_FAVOR_OURS 1
#define XDL_MERGE_FAVOR_THEIRS 2
#define XDL_MERGE_FAVOR_UNION 3
/* merge output styles */
#define XDL_MERGE_DIFF3 1
#define XDL_MERGE_ZEALOUS_DIFF3 2
typedef struct s_mmfile {
char *ptr;
long size;
} mmfile_t;
typedef struct s_mmbuffer {
char *ptr;
long size;
} mmbuffer_t;
typedef struct s_xpparam {
unsigned long flags;
/* -I<regex> */
regex_t **ignore_regex;
size_t ignore_regex_nr;
/* See Documentation/diff-options.txt. */
char **anchors;
size_t anchors_nr;
} xpparam_t;
typedef struct s_xdemitcb {
void *priv;
int (*out_hunk)(void *,
long old_begin, long old_nr,
long new_begin, long new_nr,
const char *func, long funclen);
int (*out_line)(void *, mmbuffer_t *, int);
} xdemitcb_t;
typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a,
long start_b, long count_b,
void *cb_data);
typedef struct s_xdemitconf {
long ctxlen;
long interhunkctxlen;
unsigned long flags;
find_func_t find_func;
void *find_func_priv;
xdl_emit_hunk_consume_func_t hunk_func;
} xdemitconf_t;
typedef struct s_bdiffparam {
long bsize;
} bdiffparam_t;
#define xdl_malloc(x) xmalloc(x)
#define xdl_calloc(n, sz) xcalloc(n, sz)
#define xdl_free(ptr) free(ptr)
#define xdl_realloc(ptr,x) xrealloc(ptr,x)
void *xdl_mmfile_first(mmfile_t *mmf, long *size);
long xdl_mmfile_size(mmfile_t *mmf);
int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg, xdemitcb_t *ecb);
typedef struct s_xmparam {
xpparam_t xpp;
int marker_size;
int level;
int favor;
int style;
const char *ancestor; /* label for orig */
const char *file1; /* label for mf1 */
const char *file2; /* label for mf2 */
} xmparam_t;
#define DEFAULT_CONFLICT_MARKER_SIZE 7
int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
xmparam_t const *xmp, mmbuffer_t *result);
#ifdef __cplusplus
}
#endif /* #ifdef __cplusplus */
#endif /* #if !defined(XDIFF_H) */

1089
xdiffi.c Normal file

File diff suppressed because it is too large Load Diff

62
xdiffi.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XDIFFI_H)
#define XDIFFI_H
typedef struct s_diffdata {
long nrec;
unsigned long const *ha;
long *rindex;
char *rchg;
} diffdata_t;
typedef struct s_xdalgoenv {
long mxcost;
long snake_cnt;
long heur_min;
} xdalgoenv_t;
typedef struct s_xdchange {
struct s_xdchange *next;
long i1, i2;
long chg1, chg2;
int ignore;
} xdchange_t;
int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
diffdata_t *dd2, long off2, long lim2,
long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);
int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe);
int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);
int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
void xdl_free_script(xdchange_t *xscr);
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
int xdl_do_patience_diff(xpparam_t const *xpp, xdfenv_t *env);
int xdl_do_histogram_diff(xpparam_t const *xpp, xdfenv_t *env);
#endif /* #if !defined(XDIFFI_H) */

330
xemit.c Normal file
View File

@@ -0,0 +1,330 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
*rec = xdf->recs[ri]->ptr;
return xdf->recs[ri]->size;
}
static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
long size, psize = strlen(pre);
char const *rec;
size = xdl_get_rec(xdf, ri, &rec);
if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {
return -1;
}
return 0;
}
/*
* Starting at the passed change atom, find the latest change atom to be included
* inside the differential hunk according to the specified configuration.
* Also advance xscr if the first changes must be discarded.
*/
xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
{
xdchange_t *xch, *xchp, *lxch;
long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
long max_ignorable = xecfg->ctxlen;
unsigned long ignored = 0; /* number of ignored blank lines */
/* remove ignorable changes that are too far before other changes */
for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
xch = xchp->next;
if (xch == NULL ||
xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
*xscr = xch;
}
if (!*xscr)
return NULL;
lxch = *xscr;
for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
long distance = xch->i1 - (xchp->i1 + xchp->chg1);
if (distance > max_common)
break;
if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
lxch = xch;
ignored = 0;
} else if (distance < max_ignorable && xch->ignore) {
ignored += xch->chg2;
} else if (lxch != xchp &&
xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) {
break;
} else if (!xch->ignore) {
lxch = xch;
ignored = 0;
} else {
ignored += xch->chg2;
}
}
return lxch;
}
static long def_ff(const char *rec, long len, char *buf, long sz)
{
if (len > 0 &&
(isalpha((unsigned char)*rec) || /* identifier? */
*rec == '_' || /* also identifier? */
*rec == '$')) { /* identifiers from VMS and other esoterico */
if (len > sz)
len = sz;
while (0 < len && isspace((unsigned char)rec[len - 1]))
len--;
memcpy(buf, rec, len);
return len;
}
return -1;
}
static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,
char *buf, long sz)
{
const char *rec;
long len = xdl_get_rec(xdf, ri, &rec);
if (!xecfg->find_func)
return def_ff(rec, len, buf, sz);
return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
}
static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)
{
char dummy[1];
return match_func_rec(xdf, xecfg, ri, dummy, sizeof(dummy)) >= 0;
}
struct func_line {
long len;
char buf[80];
};
static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
struct func_line *func_line, long start, long limit)
{
long l, size, step = (start > limit) ? -1 : 1;
char *buf, dummy[1];
buf = func_line ? func_line->buf : dummy;
size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
long len = match_func_rec(&xe->xdf1, xecfg, l, buf, size);
if (len >= 0) {
if (func_line)
func_line->len = len;
return l;
}
}
return -1;
}
static int is_empty_rec(xdfile_t *xdf, long ri)
{
const char *rec;
long len = xdl_get_rec(xdf, ri, &rec);
while (len > 0 && XDL_ISSPACE(*rec)) {
rec++;
len--;
}
return !len;
}
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg) {
long s1, s2, e1, e2, lctx;
xdchange_t *xch, *xche;
long funclineprev = -1;
struct func_line func_line = { 0 };
for (xch = xscr; xch; xch = xche->next) {
xdchange_t *xchp = xch;
xche = xdl_get_hunk(&xch, xecfg);
if (!xch)
break;
pre_context_calculation:
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
long fs1, i1 = xch->i1;
/* Appended chunk? */
if (i1 >= xe->xdf1.nrec) {
long i2 = xch->i2;
/*
* We don't need additional context if
* a whole function was added.
*/
while (i2 < xe->xdf2.nrec) {
if (is_func_rec(&xe->xdf2, xecfg, i2))
goto post_context_calculation;
i2++;
}
/*
* Otherwise get more context from the
* pre-image.
*/
i1 = xe->xdf1.nrec - 1;
}
fs1 = get_func_line(xe, xecfg, NULL, i1, -1);
while (fs1 > 0 && !is_empty_rec(&xe->xdf1, fs1 - 1) &&
!is_func_rec(&xe->xdf1, xecfg, fs1 - 1))
fs1--;
if (fs1 < 0)
fs1 = 0;
if (fs1 < s1) {
s2 = XDL_MAX(s2 - (s1 - fs1), 0);
s1 = fs1;
/*
* Did we extend context upwards into an
* ignored change?
*/
while (xchp != xch &&
xchp->i1 + xchp->chg1 <= s1 &&
xchp->i2 + xchp->chg2 <= s2)
xchp = xchp->next;
/* If so, show it after all. */
if (xchp != xch) {
xch = xchp;
goto pre_context_calculation;
}
}
}
post_context_calculation:
lctx = xecfg->ctxlen;
lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
e1 = xche->i1 + xche->chg1 + lctx;
e2 = xche->i2 + xche->chg2 + lctx;
if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
long fe1 = get_func_line(xe, xecfg, NULL,
xche->i1 + xche->chg1,
xe->xdf1.nrec);
while (fe1 > 0 && is_empty_rec(&xe->xdf1, fe1 - 1))
fe1--;
if (fe1 < 0)
fe1 = xe->xdf1.nrec;
if (fe1 > e1) {
e2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec);
e1 = fe1;
}
/*
* Overlap with next change? Then include it
* in the current hunk and start over to find
* its new end.
*/
if (xche->next) {
long l = XDL_MIN(xche->next->i1,
xe->xdf1.nrec - 1);
if (l - xecfg->ctxlen <= e1 ||
get_func_line(xe, xecfg, NULL, l, e1) < 0) {
xche = xche->next;
goto post_context_calculation;
}
}
}
/*
* Emit current hunk header.
*/
if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
get_func_line(xe, xecfg, &func_line,
s1 - 1, funclineprev);
funclineprev = s1 - 1;
}
if (!(xecfg->flags & XDL_EMIT_NO_HUNK_HDR) &&
xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
func_line.buf, func_line.len, ecb) < 0)
return -1;
/*
* Emit pre-context.
*/
for (; s2 < xch->i2; s2++)
if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
return -1;
for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
/*
* Merge previous with current change atom.
*/
for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
return -1;
/*
* Removes lines from the first file.
*/
for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)
return -1;
/*
* Adds lines from the second file.
*/
for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)
return -1;
if (xch == xche)
break;
s1 = xch->i1 + xch->chg1;
s2 = xch->i2 + xch->chg2;
}
/*
* Emit post-context.
*/
for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++)
if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
return -1;
}
return 0;
}

36
xemit.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XEMIT_H)
#define XEMIT_H
typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
#endif /* #if !defined(XEMIT_H) */

370
xhistogram.c Normal file
View File

@@ -0,0 +1,370 @@
/*
* Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in JGit's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "xinclude.h"
#define MAX_PTR UINT_MAX
#define MAX_CNT UINT_MAX
#define LINE_END(n) (line##n + count##n - 1)
#define LINE_END_PTR(n) (*line##n + *count##n - 1)
struct histindex {
struct record {
unsigned int ptr, cnt;
struct record *next;
} **records, /* an occurrence */
**line_map; /* map of line to record chain */
chastore_t rcha;
unsigned int *next_ptrs;
unsigned int table_bits,
records_size,
line_map_size;
unsigned int max_chain_length,
key_shift,
ptr_shift;
unsigned int cnt,
has_common;
xdfenv_t *env;
xpparam_t const *xpp;
};
struct region {
unsigned int begin1, end1;
unsigned int begin2, end2;
};
#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
#define NEXT_PTR(index, ptr) \
(index->next_ptrs[(ptr) - index->ptr_shift])
#define CNT(index, ptr) \
((LINE_MAP(index, ptr))->cnt)
#define REC(env, s, l) \
(env->xdf##s.recs[l - 1])
static int cmp_recs(xrecord_t *r1, xrecord_t *r2)
{
return r1->ha == r2->ha;
}
#define CMP(i, s1, l1, s2, l2) \
(cmp_recs(REC(i->env, s1, l1), REC(i->env, s2, l2)))
#define TABLE_HASH(index, side, line) \
XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
static int scanA(struct histindex *index, int line1, int count1)
{
unsigned int ptr, tbl_idx;
unsigned int chain_len;
struct record **rec_chain, *rec;
for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
tbl_idx = TABLE_HASH(index, 1, ptr);
rec_chain = index->records + tbl_idx;
rec = *rec_chain;
chain_len = 0;
while (rec) {
if (CMP(index, 1, rec->ptr, 1, ptr)) {
/*
* ptr is identical to another element. Insert
* it onto the front of the existing element
* chain.
*/
NEXT_PTR(index, ptr) = rec->ptr;
rec->ptr = ptr;
/* cap rec->cnt at MAX_CNT */
rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
LINE_MAP(index, ptr) = rec;
goto continue_scan;
}
rec = rec->next;
chain_len++;
}
if (chain_len == index->max_chain_length)
return -1;
/*
* This is the first time we have ever seen this particular
* element in the sequence. Construct a new chain for it.
*/
if (!(rec = xdl_cha_alloc(&index->rcha)))
return -1;
rec->ptr = ptr;
rec->cnt = 1;
rec->next = *rec_chain;
*rec_chain = rec;
LINE_MAP(index, ptr) = rec;
continue_scan:
; /* no op */
}
return 0;
}
static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr,
int line1, int count1, int line2, int count2)
{
unsigned int b_next = b_ptr + 1;
struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
unsigned int as, ae, bs, be, np, rc;
int should_break;
for (; rec; rec = rec->next) {
if (rec->cnt > index->cnt) {
if (!index->has_common)
index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
continue;
}
as = rec->ptr;
if (!CMP(index, 1, as, 2, b_ptr))
continue;
index->has_common = 1;
for (;;) {
should_break = 0;
np = NEXT_PTR(index, as);
bs = b_ptr;
ae = as;
be = bs;
rc = rec->cnt;
while (line1 < as && line2 < bs
&& CMP(index, 1, as - 1, 2, bs - 1)) {
as--;
bs--;
if (1 < rc)
rc = XDL_MIN(rc, CNT(index, as));
}
while (ae < LINE_END(1) && be < LINE_END(2)
&& CMP(index, 1, ae + 1, 2, be + 1)) {
ae++;
be++;
if (1 < rc)
rc = XDL_MIN(rc, CNT(index, ae));
}
if (b_next <= be)
b_next = be + 1;
if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
lcs->begin1 = as;
lcs->begin2 = bs;
lcs->end1 = ae;
lcs->end2 = be;
index->cnt = rc;
}
if (np == 0)
break;
while (np <= ae) {
np = NEXT_PTR(index, np);
if (np == 0) {
should_break = 1;
break;
}
}
if (should_break)
break;
as = np;
}
}
return b_next;
}
static int fall_back_to_classic_diff(xpparam_t const *xpp, xdfenv_t *env,
int line1, int count1, int line2, int count2)
{
xpparam_t xpparam;
memset(&xpparam, 0, sizeof(xpparam));
xpparam.flags = xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
return xdl_fall_back_diff(env, &xpparam,
line1, count1, line2, count2);
}
static inline void free_index(struct histindex *index)
{
xdl_free(index->records);
xdl_free(index->line_map);
xdl_free(index->next_ptrs);
xdl_cha_free(&index->rcha);
}
static int find_lcs(xpparam_t const *xpp, xdfenv_t *env,
struct region *lcs,
int line1, int count1, int line2, int count2)
{
int b_ptr;
int ret = -1;
struct histindex index;
memset(&index, 0, sizeof(index));
index.env = env;
index.xpp = xpp;
index.records = NULL;
index.line_map = NULL;
/* in case of early xdl_cha_free() */
index.rcha.head = NULL;
index.table_bits = xdl_hashbits(count1);
index.records_size = 1 << index.table_bits;
if (!XDL_CALLOC_ARRAY(index.records, index.records_size))
goto cleanup;
index.line_map_size = count1;
if (!XDL_CALLOC_ARRAY(index.line_map, index.line_map_size))
goto cleanup;
if (!XDL_CALLOC_ARRAY(index.next_ptrs, index.line_map_size))
goto cleanup;
/* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
goto cleanup;
index.ptr_shift = line1;
index.max_chain_length = 64;
if (scanA(&index, line1, count1))
goto cleanup;
index.cnt = index.max_chain_length + 1;
for (b_ptr = line2; b_ptr <= LINE_END(2); )
b_ptr = try_lcs(&index, lcs, b_ptr, line1, count1, line2, count2);
if (index.has_common && index.max_chain_length < index.cnt)
ret = 1;
else
ret = 0;
cleanup:
free_index(&index);
return ret;
}
static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env,
int line1, int count1, int line2, int count2)
{
struct region lcs;
int lcs_found;
int result;
redo:
result = -1;
if (count1 <= 0 && count2 <= 0)
return 0;
if (LINE_END(1) >= MAX_PTR)
return -1;
if (!count1) {
while(count2--)
env->xdf2.rchg[line2++ - 1] = 1;
return 0;
} else if (!count2) {
while(count1--)
env->xdf1.rchg[line1++ - 1] = 1;
return 0;
}
memset(&lcs, 0, sizeof(lcs));
lcs_found = find_lcs(xpp, env, &lcs, line1, count1, line2, count2);
if (lcs_found < 0)
goto out;
else if (lcs_found)
result = fall_back_to_classic_diff(xpp, env, line1, count1, line2, count2);
else {
if (lcs.begin1 == 0 && lcs.begin2 == 0) {
while (count1--)
env->xdf1.rchg[line1++ - 1] = 1;
while (count2--)
env->xdf2.rchg[line2++ - 1] = 1;
result = 0;
} else {
result = histogram_diff(xpp, env,
line1, lcs.begin1 - line1,
line2, lcs.begin2 - line2);
if (result)
goto out;
/*
* result = histogram_diff(xpp, env,
* lcs.end1 + 1, LINE_END(1) - lcs.end1,
* lcs.end2 + 1, LINE_END(2) - lcs.end2);
* but let's optimize tail recursion ourself:
*/
count1 = LINE_END(1) - lcs.end1;
line1 = lcs.end1 + 1;
count2 = LINE_END(2) - lcs.end2;
line2 = lcs.end2 + 1;
goto redo;
}
}
out:
return result;
}
int xdl_do_histogram_diff(xpparam_t const *xpp, xdfenv_t *env)
{
return histogram_diff(xpp, env,
env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
}

36
xinclude.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XINCLUDE_H)
#define XINCLUDE_H
#include "git-compat-util.h"
#include "xmacros.h"
#include "xdiff.h"
#include "xtypes.h"
#include "xutils.h"
#include "xprepare.h"
#include "xdiffi.h"
#include "xemit.h"
#endif /* #if !defined(XINCLUDE_H) */

71
xmacros.h Normal file
View File

@@ -0,0 +1,71 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XMACROS_H)
#define XMACROS_H
#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))
#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b)))
#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
#define XDL_LE32_PUT(p, v) \
do { \
unsigned char *__p = (unsigned char *) (p); \
*__p++ = (unsigned char) (v); \
*__p++ = (unsigned char) ((v) >> 8); \
*__p++ = (unsigned char) ((v) >> 16); \
*__p = (unsigned char) ((v) >> 24); \
} while (0)
#define XDL_LE32_GET(p, v) \
do { \
unsigned char const *__p = (unsigned char const *) (p); \
(v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \
((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \
} while (0)
/* Allocate an array of nr elements, returns NULL on failure */
#define XDL_ALLOC_ARRAY(p, nr) \
((p) = SIZE_MAX / sizeof(*(p)) >= (size_t)(nr) \
? xdl_malloc((nr) * sizeof(*(p))) \
: NULL)
/* Allocate an array of nr zeroed out elements, returns NULL on failure */
#define XDL_CALLOC_ARRAY(p, nr) ((p) = xdl_calloc(nr, sizeof(*(p))))
/*
* Ensure array p can accommodate at least nr elements, growing the
* array and updating alloc (which is the number of allocated
* elements) as necessary. Frees p and returns -1 on failure, returns
* 0 on success
*/
#define XDL_ALLOC_GROW(p, nr, alloc) \
(-!((nr) <= (alloc) || \
((p) = xdl_alloc_grow_helper((p), (nr), &(alloc), sizeof(*(p))))))
#endif /* #if !defined(XMACROS_H) */

373
xpatience.c Normal file
View File

@@ -0,0 +1,373 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003-2016 Davide Libenzi, Johannes E. Schindelin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
/*
* The basic idea of patience diff is to find lines that are unique in
* both files. These are intuitively the ones that we want to see as
* common lines.
*
* The maximal ordered sequence of such line pairs (where ordered means
* that the order in the sequence agrees with the order of the lines in
* both files) naturally defines an initial set of common lines.
*
* Now, the algorithm tries to extend the set of common lines by growing
* the line ranges where the files have identical lines.
*
* Between those common lines, the patience diff algorithm is applied
* recursively, until no unique line pairs can be found; these line ranges
* are handled by the well-known Myers algorithm.
*/
#define NON_UNIQUE ULONG_MAX
/*
* This is a hash mapping from line hash to line numbers in the first and
* second file.
*/
struct hashmap {
int nr, alloc;
struct entry {
unsigned long hash;
/*
* 0 = unused entry, 1 = first line, 2 = second, etc.
* line2 is NON_UNIQUE if the line is not unique
* in either the first or the second file.
*/
unsigned long line1, line2;
/*
* "next" & "previous" are used for the longest common
* sequence;
* initially, "next" reflects only the order in file1.
*/
struct entry *next, *previous;
/*
* If 1, this entry can serve as an anchor. See
* Documentation/diff-options.txt for more information.
*/
unsigned anchor : 1;
} *entries, *first, *last;
/* were common records found? */
unsigned long has_matches;
xdfenv_t *env;
xpparam_t const *xpp;
};
static int is_anchor(xpparam_t const *xpp, const char *line)
{
int i;
for (i = 0; i < xpp->anchors_nr; i++) {
if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i])))
return 1;
}
return 0;
}
/* The argument "pass" is 1 for the first file, 2 for the second. */
static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,
int pass)
{
xrecord_t **records = pass == 1 ?
map->env->xdf1.recs : map->env->xdf2.recs;
xrecord_t *record = records[line - 1];
/*
* After xdl_prepare_env() (or more precisely, due to
* xdl_classify_record()), the "ha" member of the records (AKA lines)
* is _not_ the hash anymore, but a linearized version of it. In
* other words, the "ha" member is guaranteed to start with 0 and
* the second record's ha can only be 0 or 1, etc.
*
* So we multiply ha by 2 in the hope that the hashing was
* "unique enough".
*/
int index = (int)((record->ha << 1) % map->alloc);
while (map->entries[index].line1) {
if (map->entries[index].hash != record->ha) {
if (++index >= map->alloc)
index = 0;
continue;
}
if (pass == 2)
map->has_matches = 1;
if (pass == 1 || map->entries[index].line2)
map->entries[index].line2 = NON_UNIQUE;
else
map->entries[index].line2 = line;
return;
}
if (pass == 2)
return;
map->entries[index].line1 = line;
map->entries[index].hash = record->ha;
map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);
if (!map->first)
map->first = map->entries + index;
if (map->last) {
map->last->next = map->entries + index;
map->entries[index].previous = map->last;
}
map->last = map->entries + index;
map->nr++;
}
/*
* This function has to be called for each recursion into the inter-hunk
* parts, as previously non-unique lines can become unique when being
* restricted to a smaller part of the files.
*
* It is assumed that env has been prepared using xdl_prepare().
*/
static int fill_hashmap(xpparam_t const *xpp, xdfenv_t *env,
struct hashmap *result,
int line1, int count1, int line2, int count2)
{
result->xpp = xpp;
result->env = env;
/* We know exactly how large we want the hash map */
result->alloc = count1 * 2;
if (!XDL_CALLOC_ARRAY(result->entries, result->alloc))
return -1;
/* First, fill with entries from the first file */
while (count1--)
insert_record(xpp, line1++, result, 1);
/* Then search for matches in the second file */
while (count2--)
insert_record(xpp, line2++, result, 2);
return 0;
}
/*
* Find the longest sequence with a smaller last element (meaning a smaller
* line2, as we construct the sequence with entries ordered by line1).
*/
static int binary_search(struct entry **sequence, int longest,
struct entry *entry)
{
int left = -1, right = longest;
while (left + 1 < right) {
int middle = left + (right - left) / 2;
/* by construction, no two entries can be equal */
if (sequence[middle]->line2 > entry->line2)
right = middle;
else
left = middle;
}
/* return the index in "sequence", _not_ the sequence length */
return left;
}
/*
* The idea is to start with the list of common unique lines sorted by
* the order in file1. For each of these pairs, the longest (partial)
* sequence whose last element's line2 is smaller is determined.
*
* For efficiency, the sequences are kept in a list containing exactly one
* item per sequence length: the sequence with the smallest last
* element (in terms of line2).
*/
static int find_longest_common_sequence(struct hashmap *map, struct entry **res)
{
struct entry **sequence;
int longest = 0, i;
struct entry *entry;
/*
* If not -1, this entry in sequence must never be overridden.
* Therefore, overriding entries before this has no effect, so
* do not do that either.
*/
int anchor_i = -1;
if (!XDL_ALLOC_ARRAY(sequence, map->nr))
return -1;
for (entry = map->first; entry; entry = entry->next) {
if (!entry->line2 || entry->line2 == NON_UNIQUE)
continue;
i = binary_search(sequence, longest, entry);
entry->previous = i < 0 ? NULL : sequence[i];
++i;
if (i <= anchor_i)
continue;
sequence[i] = entry;
if (entry->anchor) {
anchor_i = i;
longest = anchor_i + 1;
} else if (i == longest) {
longest++;
}
}
/* No common unique lines were found */
if (!longest) {
*res = NULL;
xdl_free(sequence);
return 0;
}
/* Iterate starting at the last element, adjusting the "next" members */
entry = sequence[longest - 1];
entry->next = NULL;
while (entry->previous) {
entry->previous->next = entry;
entry = entry->previous;
}
*res = entry;
xdl_free(sequence);
return 0;
}
static int match(struct hashmap *map, int line1, int line2)
{
xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
return record1->ha == record2->ha;
}
static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,
int line1, int count1, int line2, int count2);
static int walk_common_sequence(struct hashmap *map, struct entry *first,
int line1, int count1, int line2, int count2)
{
int end1 = line1 + count1, end2 = line2 + count2;
int next1, next2;
for (;;) {
/* Try to grow the line ranges of common lines */
if (first) {
next1 = first->line1;
next2 = first->line2;
while (next1 > line1 && next2 > line2 &&
match(map, next1 - 1, next2 - 1)) {
next1--;
next2--;
}
} else {
next1 = end1;
next2 = end2;
}
while (line1 < next1 && line2 < next2 &&
match(map, line1, line2)) {
line1++;
line2++;
}
/* Recurse */
if (next1 > line1 || next2 > line2) {
if (patience_diff(map->xpp, map->env,
line1, next1 - line1,
line2, next2 - line2))
return -1;
}
if (!first)
return 0;
while (first->next &&
first->next->line1 == first->line1 + 1 &&
first->next->line2 == first->line2 + 1)
first = first->next;
line1 = first->line1 + 1;
line2 = first->line2 + 1;
first = first->next;
}
}
static int fall_back_to_classic_diff(struct hashmap *map,
int line1, int count1, int line2, int count2)
{
xpparam_t xpp;
memset(&xpp, 0, sizeof(xpp));
xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
return xdl_fall_back_diff(map->env, &xpp,
line1, count1, line2, count2);
}
/*
* Recursively find the longest common sequence of unique lines,
* and if none was found, ask xdl_do_diff() to do the job.
*
* This function assumes that env was prepared with xdl_prepare_env().
*/
static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,
int line1, int count1, int line2, int count2)
{
struct hashmap map;
struct entry *first;
int result = 0;
/* trivial case: one side is empty */
if (!count1) {
while(count2--)
env->xdf2.rchg[line2++ - 1] = 1;
return 0;
} else if (!count2) {
while(count1--)
env->xdf1.rchg[line1++ - 1] = 1;
return 0;
}
memset(&map, 0, sizeof(map));
if (fill_hashmap(xpp, env, &map,
line1, count1, line2, count2))
return -1;
/* are there any matching lines at all? */
if (!map.has_matches) {
while(count1--)
env->xdf1.rchg[line1++ - 1] = 1;
while(count2--)
env->xdf2.rchg[line2++ - 1] = 1;
xdl_free(map.entries);
return 0;
}
result = find_longest_common_sequence(&map, &first);
if (result)
goto out;
if (first)
result = walk_common_sequence(&map, first,
line1, count1, line2, count2);
else
result = fall_back_to_classic_diff(&map,
line1, count1, line2, count2);
out:
xdl_free(map.entries);
return result;
}
int xdl_do_patience_diff(xpparam_t const *xpp, xdfenv_t *env)
{
return patience_diff(xpp, env, 1, env->xdf1.nrec, 1, env->xdf2.nrec);
}

461
xprepare.c Normal file
View File

@@ -0,0 +1,461 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#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 {
struct s_xdlclass *next;
unsigned long ha;
char const *line;
long size;
long idx;
long len1, len2;
} xdlclass_t;
typedef struct s_xdlclassifier {
unsigned int hbits;
long hsize;
xdlclass_t **rchash;
chastore_t ncha;
xdlclass_t **rcrecs;
long alloc;
long count;
long flags;
} xdlclassifier_t;
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(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
unsigned int hbits, xrecord_t *rec);
static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
xdlclassifier_t *cf, xdfile_t *xdf);
static void xdl_free_ctx(xdfile_t *xdf);
static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
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;
if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {
return -1;
}
if (!XDL_CALLOC_ARRAY(cf->rchash, cf->hsize)) {
xdl_cha_free(&cf->ncha);
return -1;
}
cf->alloc = size;
if (!XDL_ALLOC_ARRAY(cf->rcrecs, cf->alloc)) {
xdl_free(cf->rchash);
xdl_cha_free(&cf->ncha);
return -1;
}
cf->count = 0;
return 0;
}
static void xdl_free_classifier(xdlclassifier_t *cf) {
xdl_free(cf->rcrecs);
xdl_free(cf->rchash);
xdl_cha_free(&cf->ncha);
}
static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
unsigned int hbits, xrecord_t *rec) {
long hi;
char const *line;
xdlclass_t *rcrec;
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 &&
xdl_recmatch(rcrec->line, rcrec->size,
rec->ptr, rec->size, cf->flags))
break;
if (!rcrec) {
if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {
return -1;
}
rcrec->idx = cf->count++;
if (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))
return -1;
cf->rcrecs[rcrec->idx] = rcrec;
rcrec->line = line;
rcrec->size = rec->size;
rcrec->ha = rec->ha;
rcrec->len1 = rcrec->len2 = 0;
rcrec->next = cf->rchash[hi];
cf->rchash[hi] = rcrec;
}
(pass == 1) ? rcrec->len1++ : rcrec->len2++;
rec->ha = (unsigned long) rcrec->idx;
hi = (long) XDL_HASHLONG(rec->ha, hbits);
rec->next = rhash[hi];
rhash[hi] = rec;
return 0;
}
static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
xdlclassifier_t *cf, xdfile_t *xdf) {
unsigned int hbits;
long nrec, hsize, bsize;
unsigned long hav;
char const *blk, *cur, *top, *prev;
xrecord_t *crec;
xrecord_t **recs;
xrecord_t **rhash;
unsigned long *ha;
char *rchg;
long *rindex;
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 (!XDL_ALLOC_ARRAY(recs, narec))
goto abort;
hbits = xdl_hashbits((unsigned int) narec);
hsize = 1 << hbits;
if (!XDL_CALLOC_ARRAY(rhash, hsize))
goto abort;
nrec = 0;
if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
for (top = blk + bsize; cur < top; ) {
prev = cur;
hav = xdl_hash_record(&cur, top, xpp->flags);
if (XDL_ALLOC_GROW(recs, nrec + 1, narec))
goto abort;
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(pass, cf, rhash, hbits, crec) < 0)
goto abort;
}
}
if (!XDL_CALLOC_ARRAY(rchg, nrec + 2))
goto abort;
if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
(XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
if (!XDL_ALLOC_ARRAY(rindex, nrec + 1))
goto abort;
if (!XDL_ALLOC_ARRAY(ha, nrec + 1))
goto abort;
}
xdf->nrec = nrec;
xdf->recs = recs;
xdf->hbits = hbits;
xdf->rhash = rhash;
xdf->rchg = rchg + 1;
xdf->rindex = rindex;
xdf->nreff = 0;
xdf->ha = ha;
xdf->dstart = 0;
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;
}
static void xdl_free_ctx(xdfile_t *xdf) {
xdl_free(xdf->rhash);
xdl_free(xdf->rindex);
xdl_free(xdf->rchg - 1);
xdl_free(xdf->ha);
xdl_free(xdf->recs);
xdl_cha_free(&xdf->rcha);
}
int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe) {
long enl1, enl2, sample;
xdlclassifier_t cf;
memset(&cf, 0, sizeof(cf));
/*
* 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 = (XDF_DIFF_ALG(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, xpp->flags) < 0)
return -1;
if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
xdl_free_classifier(&cf);
return -1;
}
if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
xdl_free_ctx(&xe->xdf1);
xdl_free_classifier(&cf);
return -1;
}
if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
(XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
xdl_free_ctx(&xe->xdf2);
xdl_free_ctx(&xe->xdf1);
xdl_free_classifier(&cf);
return -1;
}
xdl_free_classifier(&cf);
return 0;
}
void xdl_free_env(xdfenv_t *xe) {
xdl_free_ctx(&xe->xdf2);
xdl_free_ctx(&xe->xdf1);
}
static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
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])
rdis0++;
else if (dis[i - r] == 2)
rpdis0++;
else
break;
}
/*
* 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])
rdis1++;
else if (dis[i + r] == 2)
rpdis1++;
else
break;
}
/*
* 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);
}
/*
* Try to reduce the problem complexity, discard records that have no
* matches on the other file. Also, lines that have multiple matches
* might be potentially discarded if they happear in a run of discardable.
*/
static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
long i, nm, nreff, mlim;
xrecord_t **recs;
xdlclass_t *rcrec;
char *dis, *dis1, *dis2;
if (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))
return -1;
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++) {
rcrec = cf->rcrecs[(*recs)->ha];
nm = rcrec ? rcrec->len2 : 0;
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++) {
rcrec = cf->rcrecs[(*recs)->ha];
nm = rcrec ? rcrec->len1 : 0;
dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
}
for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
i <= xdf1->dend; i++, recs++) {
if (dis1[i] == 1 ||
(dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
xdf1->rindex[nreff] = i;
xdf1->ha[nreff] = (*recs)->ha;
nreff++;
} else
xdf1->rchg[i] = 1;
}
xdf1->nreff = nreff;
for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
i <= xdf2->dend; i++, recs++) {
if (dis2[i] == 1 ||
(dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
xdf2->rindex[nreff] = i;
xdf2->ha[nreff] = (*recs)->ha;
nreff++;
} else
xdf2->rchg[i] = 1;
}
xdf2->nreff = nreff;
xdl_free(dis);
return 0;
}
/*
* Early trim initial and terminal matching records.
*/
static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
long i, lim;
xrecord_t **recs1, **recs2;
recs1 = xdf1->recs;
recs2 = xdf2->recs;
for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
i++, recs1++, recs2++)
if ((*recs1)->ha != (*recs2)->ha)
break;
xdf1->dstart = xdf2->dstart = i;
recs1 = xdf1->recs + xdf1->nrec - 1;
recs2 = xdf2->recs + xdf2->nrec - 1;
for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
if ((*recs1)->ha != (*recs2)->ha)
break;
xdf1->dend = xdf1->nrec - i - 1;
xdf2->dend = xdf2->nrec - i - 1;
return 0;
}
static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
if (xdl_trim_ends(xdf1, xdf2) < 0 ||
xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
return -1;
}
return 0;
}

34
xprepare.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XPREPARE_H)
#define XPREPARE_H
int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe);
void xdl_free_env(xdfenv_t *xe);
#endif /* #if !defined(XPREPARE_H) */

67
xtypes.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XTYPES_H)
#define XTYPES_H
typedef struct s_chanode {
struct s_chanode *next;
long icurr;
} chanode_t;
typedef struct s_chastore {
chanode_t *head, *tail;
long isize, nsize;
chanode_t *ancur;
chanode_t *sncur;
long scurr;
} chastore_t;
typedef struct s_xrecord {
struct s_xrecord *next;
char const *ptr;
long size;
unsigned long ha;
} xrecord_t;
typedef struct s_xdfile {
chastore_t rcha;
long nrec;
unsigned int hbits;
xrecord_t **rhash;
long dstart, dend;
xrecord_t **recs;
char *rchg;
long *rindex;
long nreff;
unsigned long *ha;
} xdfile_t;
typedef struct s_xdfenv {
xdfile_t xdf1, xdf2;
} xdfenv_t;
#endif /* #if !defined(XTYPES_H) */

451
xutils.c Normal file
View File

@@ -0,0 +1,451 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#include "xinclude.h"
long xdl_bogosqrt(long n) {
long i;
/*
* Classical integer square root approximation using shifts.
*/
for (i = 1; n > 0; n >>= 2)
i <<= 1;
return i;
}
int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
xdemitcb_t *ecb) {
int i = 2;
mmbuffer_t mb[3];
mb[0].ptr = (char *) pre;
mb[0].size = psize;
mb[1].ptr = (char *) rec;
mb[1].size = size;
if (size > 0 && rec[size - 1] != '\n') {
mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
mb[2].size = strlen(mb[2].ptr);
i++;
}
if (ecb->out_line(ecb->priv, mb, i) < 0) {
return -1;
}
return 0;
}
void *xdl_mmfile_first(mmfile_t *mmf, long *size)
{
*size = mmf->size;
return mmf->ptr;
}
long xdl_mmfile_size(mmfile_t *mmf)
{
return mmf->size;
}
int xdl_cha_init(chastore_t *cha, long isize, long icount) {
cha->head = cha->tail = NULL;
cha->isize = isize;
cha->nsize = icount * isize;
cha->ancur = cha->sncur = NULL;
cha->scurr = 0;
return 0;
}
void xdl_cha_free(chastore_t *cha) {
chanode_t *cur, *tmp;
for (cur = cha->head; (tmp = cur) != NULL;) {
cur = cur->next;
xdl_free(tmp);
}
}
void *xdl_cha_alloc(chastore_t *cha) {
chanode_t *ancur;
void *data;
if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
return NULL;
}
ancur->icurr = 0;
ancur->next = NULL;
if (cha->tail)
cha->tail->next = ancur;
if (!cha->head)
cha->head = ancur;
cha->tail = ancur;
cha->ancur = ancur;
}
data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
ancur->icurr += cha->isize;
return data;
}
long xdl_guess_lines(mmfile_t *mf, long sample) {
long nl = 0, size, tsize = 0;
char const *data, *cur, *top;
if ((cur = data = xdl_mmfile_first(mf, &size))) {
for (top = data + size; nl < sample && cur < top; ) {
nl++;
if (!(cur = memchr(cur, '\n', top - cur)))
cur = top;
else
cur++;
}
tsize += (long) (cur - data);
}
if (nl && tsize)
nl = xdl_mmfile_size(mf) / (tsize / nl);
return nl + 1;
}
int xdl_blankline(const char *line, long size, long flags)
{
long i;
if (!(flags & XDF_WHITESPACE_FLAGS))
return (size <= 1);
for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
;
return (i == size);
}
/*
* Have we eaten everything on the line, except for an optional
* CR at the very end?
*/
static int ends_with_optional_cr(const char *l, long s, long i)
{
int complete = s && l[s-1] == '\n';
if (complete)
s--;
if (s == i)
return 1;
/* do not ignore CR at the end of an incomplete line */
if (complete && s == i + 1 && l[i] == '\r')
return 1;
return 0;
}
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
{
int i1, i2;
if (s1 == s2 && !memcmp(l1, l2, s1))
return 1;
if (!(flags & XDF_WHITESPACE_FLAGS))
return 0;
i1 = 0;
i2 = 0;
/*
* -w matches everything that matches with -b, and -b in turn
* matches everything that matches with --ignore-space-at-eol,
* which in turn matches everything that matches with --ignore-cr-at-eol.
*
* Each flavor of ignoring needs different logic to skip whitespaces
* while we have both sides to compare.
*/
if (flags & XDF_IGNORE_WHITESPACE) {
goto skip_ws;
while (i1 < s1 && i2 < s2) {
if (l1[i1++] != l2[i2++])
return 0;
skip_ws:
while (i1 < s1 && XDL_ISSPACE(l1[i1]))
i1++;
while (i2 < s2 && XDL_ISSPACE(l2[i2]))
i2++;
}
} else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
while (i1 < s1 && i2 < s2) {
if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
/* Skip matching spaces and try again */
while (i1 < s1 && XDL_ISSPACE(l1[i1]))
i1++;
while (i2 < s2 && XDL_ISSPACE(l2[i2]))
i2++;
continue;
}
if (l1[i1++] != l2[i2++])
return 0;
}
} else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
i1++;
i2++;
}
} else if (flags & XDF_IGNORE_CR_AT_EOL) {
/* Find the first difference and see how the line ends */
while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
i1++;
i2++;
}
return (ends_with_optional_cr(l1, s1, i1) &&
ends_with_optional_cr(l2, s2, i2));
}
/*
* After running out of one side, the remaining side must have
* nothing but whitespace for the lines to match. Note that
* ignore-whitespace-at-eol case may break out of the loop
* while there still are characters remaining on both lines.
*/
if (i1 < s1) {
while (i1 < s1 && XDL_ISSPACE(l1[i1]))
i1++;
if (s1 != i1)
return 0;
}
if (i2 < s2) {
while (i2 < s2 && XDL_ISSPACE(l2[i2]))
i2++;
return (s2 == i2);
}
return 1;
}
static unsigned long xdl_hash_record_with_whitespace(char const **data,
char const *top, long flags) {
unsigned long ha = 5381;
char const *ptr = *data;
int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL;
for (; ptr < top && *ptr != '\n'; ptr++) {
if (cr_at_eol_only) {
/* do not ignore CR at the end of an incomplete line */
if (*ptr == '\r' &&
(ptr + 1 < top && ptr[1] == '\n'))
continue;
}
else if (XDL_ISSPACE(*ptr)) {
const char *ptr2 = ptr;
int at_eol;
while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
&& ptr[1] != '\n')
ptr++;
at_eol = (top <= ptr + 1 || ptr[1] == '\n');
if (flags & XDF_IGNORE_WHITESPACE)
; /* already handled */
else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
&& !at_eol) {
ha += (ha << 5);
ha ^= (unsigned long) ' ';
}
else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
&& !at_eol) {
while (ptr2 != ptr + 1) {
ha += (ha << 5);
ha ^= (unsigned long) *ptr2;
ptr2++;
}
}
continue;
}
ha += (ha << 5);
ha ^= (unsigned long) *ptr;
}
*data = ptr < top ? ptr + 1: ptr;
return ha;
}
unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
unsigned long ha = 5381;
char const *ptr = *data;
if (flags & XDF_WHITESPACE_FLAGS)
return xdl_hash_record_with_whitespace(data, top, flags);
for (; ptr < top && *ptr != '\n'; ptr++) {
ha += (ha << 5);
ha ^= (unsigned long) *ptr;
}
*data = ptr < top ? ptr + 1: ptr;
return ha;
}
unsigned int xdl_hashbits(unsigned int size) {
unsigned int val = 1, bits = 0;
for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
return bits ? bits: 1;
}
int xdl_num_out(char *out, long val) {
char *ptr, *str = out;
char buf[32];
ptr = buf + sizeof(buf) - 1;
*ptr = '\0';
if (val < 0) {
*--ptr = '-';
val = -val;
}
for (; val && ptr > buf; val /= 10)
*--ptr = "0123456789"[val % 10];
if (*ptr)
for (; *ptr; ptr++, str++)
*str = *ptr;
else
*str++ = '0';
*str = '\0';
return str - out;
}
static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2,
const char *func, long funclen,
xdemitcb_t *ecb) {
int nb = 0;
mmbuffer_t mb;
char buf[128];
memcpy(buf, "@@ -", 4);
nb += 4;
nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
if (c1 != 1) {
memcpy(buf + nb, ",", 1);
nb += 1;
nb += xdl_num_out(buf + nb, c1);
}
memcpy(buf + nb, " +", 2);
nb += 2;
nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
if (c2 != 1) {
memcpy(buf + nb, ",", 1);
nb += 1;
nb += xdl_num_out(buf + nb, c2);
}
memcpy(buf + nb, " @@", 3);
nb += 3;
if (func && funclen) {
buf[nb++] = ' ';
if (funclen > sizeof(buf) - nb - 1)
funclen = sizeof(buf) - nb - 1;
memcpy(buf + nb, func, funclen);
nb += funclen;
}
buf[nb++] = '\n';
mb.ptr = buf;
mb.size = nb;
if (ecb->out_line(ecb->priv, &mb, 1) < 0)
return -1;
return 0;
}
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
const char *func, long funclen,
xdemitcb_t *ecb) {
if (!ecb->out_hunk)
return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb);
if (ecb->out_hunk(ecb->priv,
c1 ? s1 : s1 - 1, c1,
c2 ? s2 : s2 - 1, c2,
func, funclen) < 0)
return -1;
return 0;
}
int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
int line1, int count1, int line2, int count2)
{
/*
* This probably does not work outside Git, since
* we have a very simple mmfile structure.
*
* Note: ideally, we would reuse the prepared environment, but
* the libxdiff interface does not (yet) allow for diffing only
* ranges of lines instead of the whole files.
*/
mmfile_t subfile1, subfile2;
xdfenv_t env;
subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
return -1;
memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
xdl_free_env(&env);
return 0;
}
void* xdl_alloc_grow_helper(void *p, long nr, long *alloc, size_t size)
{
void *tmp = NULL;
size_t n = ((LONG_MAX - 16) / 2 >= *alloc) ? 2 * *alloc + 16 : LONG_MAX;
if (nr > n)
n = nr;
if (SIZE_MAX / size >= n)
tmp = xdl_realloc(p, n * size);
if (tmp) {
*alloc = n;
} else {
xdl_free(p);
*alloc = 0;
}
return tmp;
}

48
xutils.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* LibXDiff by Davide Libenzi ( File Differential Library )
* Copyright (C) 2003 Davide Libenzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Davide Libenzi <davidel@xmailserver.org>
*
*/
#if !defined(XUTILS_H)
#define XUTILS_H
long xdl_bogosqrt(long n);
int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
xdemitcb_t *ecb);
int xdl_cha_init(chastore_t *cha, long isize, long icount);
void xdl_cha_free(chastore_t *cha);
void *xdl_cha_alloc(chastore_t *cha);
long xdl_guess_lines(mmfile_t *mf, long sample);
int xdl_blankline(const char *line, long size, long flags);
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
unsigned long xdl_hash_record(char const **data, char const *top, long flags);
unsigned int xdl_hashbits(unsigned int size);
int xdl_num_out(char *out, long val);
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
const char *func, long funclen, xdemitcb_t *ecb);
int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
int line1, int count1, int line2, int count2);
/* Do not call this function, use XDL_ALLOC_GROW instead */
void* xdl_alloc_grow_helper(void* p, long nr, long* alloc, size_t size);
#endif /* #if !defined(XUTILS_H) */