From 69f4438d3586a10a0a4833788b9ba4cb5d2cc8fd Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 8 Sep 2021 22:18:44 +0200 Subject: vim-patch:8.2.3390: included xdiff code is outdated Problem: Included xdiff code is outdated. Solution: Sync with xdiff in git 2.33. (Christian Brabandt, closes vim/vim#8431) https://github.com/vim/vim/commit/ba02e4720f863fdb456e7023520f0a354eec0dcf --- src/nvim/diff.c | 2 +- src/nvim/lua/xdiff.c | 4 +- src/nvim/xdiff/README.txt | 2 +- src/nvim/xdiff/xdiff.h | 37 ++++++---- src/nvim/xdiff/xdiffi.c | 176 +++++++++++++++++++++++++++++--------------- src/nvim/xdiff/xdiffi.h | 2 +- src/nvim/xdiff/xemit.c | 36 ++++++--- src/nvim/xdiff/xemit.h | 2 +- src/nvim/xdiff/xhistogram.c | 16 ++-- src/nvim/xdiff/xinclude.h | 8 +- src/nvim/xdiff/xmacros.h | 2 +- src/nvim/xdiff/xpatience.c | 53 ++++++------- src/nvim/xdiff/xprepare.h | 2 +- src/nvim/xdiff/xtypes.h | 2 +- src/nvim/xdiff/xutils.c | 37 ++++++---- src/nvim/xdiff/xutils.h | 2 +- 16 files changed, 237 insertions(+), 146 deletions(-) (limited to 'src') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 299175b28c..26269a44d2 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1059,7 +1059,7 @@ static int diff_file_internal(diffio_T *diffio) emit_cfg.ctxlen = 0; // don't need any diff_context here emit_cb.priv = &diffio->dio_diff; - emit_cb.outf = xdiff_out; + emit_cb.out_line = xdiff_out; if (xdl_diff(&diffio->dio_orig.din_mmfile, &diffio->dio_new.din_mmfile, ¶m, &emit_cfg, &emit_cb) < 0) { diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index d50f874a82..ae0ea00765 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -8,7 +8,7 @@ #include #include "nvim/vim.h" -#include "nvim/xdiff/xdiff.h" +#include "xdiff/xdiff.h" #include "nvim/lua/xdiff.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" @@ -294,7 +294,7 @@ int nlua_xdl_diff(lua_State *lstate) case kNluaXdiffModeUnified: luaL_buffinit(lstate, &buf); ecb.priv = &buf; - ecb.outf = write_string; + ecb.out_line = write_string; break; case kNluaXdiffModeOnHunkCB: priv = xmalloc(sizeof(*priv)); diff --git a/src/nvim/xdiff/README.txt b/src/nvim/xdiff/README.txt index 1afe74095b..95b2242b87 100644 --- a/src/nvim/xdiff/README.txt +++ b/src/nvim/xdiff/README.txt @@ -1,6 +1,6 @@ The files in this directory come from the xdiff implementation in git. You can find it here: https://github.com/git/git/tree/master/xdiff -The files were last updated 2018 September 10. +The files were last updated August 31, 2021 from git release v.2.33.0 This is originally based on libxdiff, which can be found here: http://www.xmailserver.org/xdiff-lib.html diff --git a/src/nvim/xdiff/xdiff.h b/src/nvim/xdiff/xdiff.h index 8ff4a05bfb..f3dac80d3f 100644 --- a/src/nvim/xdiff/xdiff.h +++ b/src/nvim/xdiff/xdiff.h @@ -25,9 +25,9 @@ #ifdef __cplusplus extern "C" { -#endif // #ifdef __cplusplus +#endif /* #ifdef __cplusplus */ -// xpparm_t.flags +/* xpparm_t.flags */ #define XDF_NEED_MINIMAL (1 << 0) #define XDF_IGNORE_WHITESPACE (1 << 1) @@ -48,22 +48,23 @@ extern "C" { #define XDF_INDENT_HEURISTIC (1 << 23) -// xdemitconf_t.flags +/* 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 +/* 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 +/* merge favor modes */ #define XDL_MERGE_FAVOR_OURS 1 #define XDL_MERGE_FAVOR_THEIRS 2 #define XDL_MERGE_FAVOR_UNION 3 -// merge output styles +/* merge output styles */ #define XDL_MERGE_DIFF3 1 typedef struct s_mmfile { @@ -79,14 +80,24 @@ typedef struct s_mmbuffer { typedef struct s_xpparam { unsigned long flags; - // See Documentation/diff-options.txt. + /* -I */ + #if 0 // unused by Vim + regex_t **ignore_regex; + size_t ignore_regex_nr; +#endif + + /* See Documentation/diff-options.txt. */ char **anchors; size_t anchors_nr; } xpparam_t; typedef struct s_xdemitcb { void *priv; - int (*outf)(void *, mmbuffer_t *, int); + 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); @@ -126,9 +137,9 @@ typedef struct s_xmparam { int level; int favor; int style; - const char *ancestor; // label for orig - const char *file1; // label for mf1 - const char *file2; // label for mf2 + 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 @@ -138,6 +149,6 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, #ifdef __cplusplus } -#endif // #ifdef __cplusplus +#endif /* #ifdef __cplusplus */ -#endif // #if !defined(XDIFF_H) +#endif /* #if !defined(XDIFF_H) */ diff --git a/src/nvim/xdiff/xdiffi.c b/src/nvim/xdiff/xdiffi.c index 3806903986..cfcbb5d982 100644 --- a/src/nvim/xdiff/xdiffi.c +++ b/src/nvim/xdiff/xdiffi.c @@ -38,9 +38,9 @@ typedef struct s_xdpsplit { * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both * the forward diagonal starting from (off1, off2) and the backward diagonal * starting from (lim1, lim2). If the K values on the same diagonal crosses - * returns the furthest point of reach. We might end up having to expensive - * cases using this algorithm is full, so a little bit of heuristic is needed - * to cut the search and to return a suboptimal point. + * returns the furthest point of reach. We might encounter expensive edge cases + * using this algorithm, so a little bit of heuristic is needed to cut the + * search and to return a suboptimal point. */ static long xdl_split(unsigned long const *ha1, long off1, long lim1, unsigned long const *ha2, long off2, long lim2, @@ -63,11 +63,13 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, int got_snake = 0; /* - * We need to extent the diagonal "domain" by one. If the next + * We need to extend the diagonal "domain" by one. If the next * values exits the box boundaries we need to change it in the - * opposite direction because (max - min) must be a power of two. + * opposite direction because (max - min) must be a power of + * two. + * * Also we initialize the external K value to -1 so that we can - * avoid extra conditions check inside the core loop. + * avoid extra conditions in the check inside the core loop. */ if (fmin > dmin) kvdf[--fmin - 1] = -1; @@ -98,11 +100,13 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, } /* - * We need to extent the diagonal "domain" by one. If the next + * We need to extend the diagonal "domain" by one. If the next * values exits the box boundaries we need to change it in the - * opposite direction because (max - min) must be a power of two. + * opposite direction because (max - min) must be a power of + * two. + * * Also we initialize the external K value to -1 so that we can - * avoid extra conditions check inside the core loop. + * avoid extra conditions in the check inside the core loop. */ if (bmin > dmin) kvdb[--bmin - 1] = XDL_LINE_MAX; @@ -138,7 +142,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, /* * If the edit cost is above the heuristic trigger and if * we got a good snake, we sample current diagonals to see - * if some of the, have reached an "interesting" path. Our + * if some of them have reached an "interesting" path. Our * measure is a function of the distance from the diagonal * corner (i1 + i2) penalized with the distance from the * mid diagonal itself. If this value is above the current @@ -196,8 +200,9 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, } /* - * Enough is enough. We spent too much time here and now we collect - * the furthest reaching path using the (i1 + i2) measure. + * Enough is enough. We spent too much time here and now we + * collect the furthest reaching path using the (i1 + i2) + * measure. */ if (ec >= xenv->mxcost) { long fbest, fbest1, bbest, bbest1; @@ -244,9 +249,9 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, /* - * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling - * the box splitting function. Note that the real job (marking changed lines) - * is done in the two boundary reaching checks. + * Rule: "Divide et Impera" (divide & conquer). Recursively split the box in + * sub-boxes by calling the box splitting function. Note that the real job + * (marking changed lines) is done in the two boundary reaching checks. */ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, diffdata_t *dd2, long off2, long lim2, @@ -323,7 +328,9 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, } /* - * Allocate and setup K vectors to be used by the differential algorithm. + * Allocate and setup K vectors to be used by the differential + * algorithm. + * * One is to store the forward path and one to store the backward path. */ ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; @@ -418,13 +425,13 @@ static int xget_indent(xrecord_t *rec) ret += 1; else if (c == '\t') ret += 8 - ret % 8; - // ignore other whitespace characters + /* ignore other whitespace characters */ if (ret >= MAX_INDENT) return MAX_INDENT; } - // The line contains only whitespace. + /* The line contains only whitespace. */ return -1; } @@ -435,7 +442,7 @@ static int xget_indent(xrecord_t *rec) */ #define MAX_BLANKS 20 -// Characteristics measured about a hypothetical split position. +/* Characteristics measured about a hypothetical split position. */ struct split_measurement { /* * Is the split at the end of the file (aside from any blank lines)? @@ -443,8 +450,8 @@ struct split_measurement { int end_of_file; /* - * How much is the line immediately following the split indented (or -1 if - * the line is blank): + * How much is the line immediately following the split indented (or -1 + * if the line is blank): */ int indent; @@ -454,8 +461,8 @@ struct split_measurement { int pre_blank; /* - * How much is the nearest non-blank line above the split indented (or -1 - * if there is no such line)? + * How much is the nearest non-blank line above the split indented (or + * -1 if there is no such line)? */ int pre_indent; @@ -472,10 +479,10 @@ struct split_measurement { }; struct split_score { - // The effective indent of this split (smaller is preferred). + /* The effective indent of this split (smaller is preferred). */ int effective_indent; - // Penalty for this split (smaller is preferred). + /* Penalty for this split (smaller is preferred). */ int penalty; }; @@ -534,16 +541,16 @@ static void measure_split(const xdfile_t *xdf, long split, * integer math. */ -// Penalty if there are no non-blank lines before the split +/* Penalty if there are no non-blank lines before the split */ #define START_OF_FILE_PENALTY 1 -// Penalty if there are no non-blank lines after the split +/* Penalty if there are no non-blank lines after the split */ #define END_OF_FILE_PENALTY 21 -// Multiplier for the number of blank lines around the split +/* Multiplier for the number of blank lines around the split */ #define TOTAL_BLANK_WEIGHT (-30) -// Multiplier for the number of blank lines after the split +/* Multiplier for the number of blank lines after the split */ #define POST_BLANK_WEIGHT 6 /* @@ -581,13 +588,13 @@ static void measure_split(const xdfile_t *xdf, long split, /* * Compute a badness score for the hypothetical split whose measurements are - * stored in m. The weight factors were determined empirically using the tools and - * corpus described in + * stored in m. The weight factors were determined empirically using the tools + * and corpus described in * * https://github.com/mhagger/diff-slider-tools * - * Also see that project if you want to improve the weights based on, for example, - * a larger or more diverse corpus. + * Also see that project if you want to improve the weights based on, for + * example, a larger or more diverse corpus. */ static void score_add_split(const struct split_measurement *m, struct split_score *s) { @@ -610,7 +617,7 @@ static void score_add_split(const struct split_measurement *m, struct split_scor post_blank = (m->indent == -1) ? 1 + m->post_blank : 0; total_blank = m->pre_blank + post_blank; - // Penalties based on nearby blank lines: + /* Penalties based on nearby blank lines: */ s->penalty += TOTAL_BLANK_WEIGHT * total_blank; s->penalty += POST_BLANK_WEIGHT * post_blank; @@ -621,13 +628,13 @@ static void score_add_split(const struct split_measurement *m, struct split_scor any_blanks = (total_blank != 0); - // Note that the effective indent is -1 at the end of the file: + /* Note that the effective indent is -1 at the end of the file: */ s->effective_indent += indent; if (indent == -1) { - // No additional adjustments needed. + /* No additional adjustments needed. */ } else if (m->pre_indent == -1) { - // No additional adjustments needed. + /* No additional adjustments needed. */ } else if (indent > m->pre_indent) { /* * The line is indented more than its predecessor. @@ -669,7 +676,7 @@ static void score_add_split(const struct split_measurement *m, struct split_scor static int score_cmp(struct split_score *s1, struct split_score *s2) { - // -1 if s1.effective_indent < s2->effective_indent, etc. + /* -1 if s1.effective_indent < s2->effective_indent, etc. */ int cmp_indents = ((s1->effective_indent > s2->effective_indent) - (s1->effective_indent < s2->effective_indent)); @@ -809,13 +816,16 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { group_init(xdfo, &go); while (1) { - // If the group is empty in the to-be-compacted file, skip it: + /* + * If the group is empty in the to-be-compacted file, skip it: + */ if (g.end == g.start) goto next; /* * Now shift the change up and then down as far as possible in - * each direction. If it bumps into any other changes, merge them. + * each direction. If it bumps into any other changes, merge + * them. */ do { groupsize = g.end - g.start; @@ -828,7 +838,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { */ end_matching_other = -1; - // Shift the group backward as much as possible: + /* Shift the group backward as much as possible: */ while (!group_slide_up(xdf, &g, flags)) if (group_previous(xdfo, &go)) xdl_bug("group sync broken sliding up"); @@ -842,7 +852,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { if (go.end > go.start) end_matching_other = g.end; - // Now shift the group forward as far as possible: + /* Now shift the group forward as far as possible: */ while (1) { if (group_slide_down(xdf, &g, flags)) break; @@ -858,17 +868,17 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { * If the group can be shifted, then we can possibly use this * freedom to produce a more intuitive diff. * - * The group is currently shifted as far down as possible, so the - * heuristics below only have to handle upwards shifts. + * The group is currently shifted as far down as possible, so + * the heuristics below only have to handle upwards shifts. */ if (g.end == earliest_end) { - // no shifting was possible + /* no shifting was possible */ } else if (end_matching_other != -1) { /* - * Move the possibly merged group of changes back to line - * up with the last group of changes from the other file - * that it can align with. + * Move the possibly merged group of changes back to + * line up with the last group of changes from the + * other file that it can align with. */ while (go.end == go.start) { if (group_slide_up(xdf, &g, flags)) @@ -879,14 +889,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { } else if (flags & XDF_INDENT_HEURISTIC) { /* * Indent heuristic: a group of pure add/delete lines - * implies two splits, one between the end of the "before" - * context and the start of the group, and another between - * the end of the group and the beginning of the "after" - * context. Some splits are aesthetically better and some - * are worse. We compute a badness "score" for each split, - * and add the scores for the two splits to define a - * "score" for each position that the group can be shifted - * to. Then we pick the shift with the lowest score. + * implies two splits, one between the end of the + * "before" context and the start of the group, and + * another between the end of the group and the + * beginning of the "after" context. Some splits are + * aesthetically better and some are worse. We compute + * a badness "score" for each split, and add the scores + * for the two splits to define a "score" for each + * position that the group can be shifted to. Then we + * pick the shift with the lowest score. */ long shift, best_shift = -1; struct split_score best_score; @@ -921,7 +932,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { } next: - // Move past the just-processed group: + /* Move past the just-processed group: */ if (group_next(xdf, &g)) break; if (group_next(xdfo, &go)) @@ -987,7 +998,7 @@ static int xdl_call_hunk_func(xdfenv_t *xe UNUSED, xdchange_t *xscr, xdemitcb_t return 0; } -static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) +static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags) { xdchange_t *xch; @@ -1008,6 +1019,48 @@ static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) } } +#if 0 // unused by Vim +static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) { + regmatch_t regmatch; + int i; + + for (i = 0; i < xpp->ignore_regex_nr; i++) + if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1, + ®match, 0)) + return 1; + + return 0; +} + +static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe, + xpparam_t const *xpp) +{ + xdchange_t *xch; + + for (xch = xscr; xch; xch = xch->next) { + xrecord_t **rec; + int ignore = 1; + long i; + + /* + * Do not override --ignore-blank-lines. + */ + if (xch->ignore) + continue; + + rec = &xe->xdf1.recs[xch->i1]; + for (i = 0; i < xch->chg1 && ignore; i++) + ignore = record_matches_regex(rec[i], xpp); + + rec = &xe->xdf2.recs[xch->i2]; + for (i = 0; i < xch->chg2 && ignore; i++) + ignore = record_matches_regex(rec[i], xpp); + + xch->ignore = ignore; + } +} +#endif + int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb) { xdchange_t *xscr; @@ -1027,7 +1080,12 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, } if (xscr) { if (xpp->flags & XDF_IGNORE_BLANK_LINES) - xdl_mark_ignorable(xscr, &xe, xpp->flags); + xdl_mark_ignorable_lines(xscr, &xe, xpp->flags); + +#if 0 + if (xpp->ignore_regex) + xdl_mark_ignorable_regex(xscr, &xe, xpp); +#endif if (ef(&xe, xscr, ecb, xecfg) < 0) { diff --git a/src/nvim/xdiff/xdiffi.h b/src/nvim/xdiff/xdiffi.h index 467a1e85cd..8f1c7c8b04 100644 --- a/src/nvim/xdiff/xdiffi.h +++ b/src/nvim/xdiff/xdiffi.h @@ -61,4 +61,4 @@ int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *env); -#endif // #if !defined(XDIFFI_H) +#endif /* #if !defined(XDIFFI_H) */ diff --git a/src/nvim/xdiff/xemit.c b/src/nvim/xdiff/xemit.c index f1a45139cc..a0078f928c 100644 --- a/src/nvim/xdiff/xemit.c +++ b/src/nvim/xdiff/xemit.c @@ -31,7 +31,7 @@ static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) { static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) { - long size, psize = (long)strlen(pre); + long size, psize = strlen(pre); char const *rec; size = xdl_get_rec(xdf, ri, &rec); @@ -54,9 +54,9 @@ 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 + unsigned long ignored = 0; /* number of ignored blank lines */ - // remove ignorable changes that are too far before other changes + /* remove ignorable changes that are too far before other changes */ for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) { xch = xchp->next; @@ -99,9 +99,9 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) static long def_ff(const char *rec, long len, char *buf, long sz, void *priv UNUSED) { if (len > 0 && - (isalpha((unsigned char)*rec) || // identifier? - *rec == '_' || // also identifier? - *rec == '$')) { // identifiers from VMS and other esoterico + (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])) @@ -197,7 +197,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { long fs1, i1 = xch->i1; - // Appended chunk? + /* Appended chunk? */ if (i1 >= xe->xdf1.nrec) { long i2 = xch->i2; @@ -225,8 +225,23 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (fs1 < 0) fs1 = 0; if (fs1 < s1) { - s2 -= s1 - fs1; + 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; + } } } @@ -249,7 +264,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (fe1 < 0) fe1 = xe->xdf1.nrec; if (fe1 > e1) { - e2 += fe1 - e1; + e2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec); e1 = fe1; } @@ -281,7 +296,8 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, funclineprev = s1 - 1; } #endif - if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, + 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; diff --git a/src/nvim/xdiff/xemit.h b/src/nvim/xdiff/xemit.h index 3ce7e3dd50..1b9887e670 100644 --- a/src/nvim/xdiff/xemit.h +++ b/src/nvim/xdiff/xemit.h @@ -33,4 +33,4 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, -#endif // #if !defined(XEMIT_H) +#endif /* #if !defined(XEMIT_H) */ diff --git a/src/nvim/xdiff/xhistogram.c b/src/nvim/xdiff/xhistogram.c index 28cf8258e5..8598a8550d 100644 --- a/src/nvim/xdiff/xhistogram.c +++ b/src/nvim/xdiff/xhistogram.c @@ -42,8 +42,6 @@ */ #include "xinclude.h" -#include "xtypes.h" -#include "xdiff.h" #define MAX_PTR INT_MAX #define MAX_CNT INT_MAX @@ -55,8 +53,8 @@ struct histindex { struct record { unsigned int ptr, cnt; struct record *next; - } **records, // an occurrence - **line_map; // map of line to record chain + } **records, /* an occurrence */ + **line_map; /* map of line to record chain */ chastore_t rcha; unsigned int *next_ptrs; unsigned int table_bits, @@ -128,7 +126,7 @@ static int scanA(struct histindex *index, int line1, int count1) */ NEXT_PTR(index, ptr) = rec->ptr; rec->ptr = ptr; - // cap rec->cnt at MAX_CNT + /* cap rec->cnt at MAX_CNT */ rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1); LINE_MAP(index, ptr) = rec; goto continue_scan; @@ -154,7 +152,7 @@ static int scanA(struct histindex *index, int line1, int count1) LINE_MAP(index, ptr) = rec; continue_scan: - ; // no op + ; /* no op */ } return 0; @@ -237,6 +235,8 @@ 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, @@ -266,7 +266,7 @@ static int find_lcs(xpparam_t const *xpp, xdfenv_t *env, index.records = NULL; index.line_map = NULL; - // in case of early xdl_cha_free() + /* in case of early xdl_cha_free() */ index.rcha.head = NULL; index.table_bits = xdl_hashbits(count1); @@ -288,7 +288,7 @@ static int find_lcs(xpparam_t const *xpp, xdfenv_t *env, goto cleanup; memset(index.next_ptrs, 0, sz); - // lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() + /* 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; diff --git a/src/nvim/xdiff/xinclude.h b/src/nvim/xdiff/xinclude.h index 5a359d1431..a412404bfe 100644 --- a/src/nvim/xdiff/xinclude.h +++ b/src/nvim/xdiff/xinclude.h @@ -40,6 +40,7 @@ #if !defined(XINCLUDE_H) #define XINCLUDE_H +// This effectively re-verts b46054b3746271d23feab0 from git #include #include #include @@ -48,7 +49,10 @@ #endif #include #include - +// This include comes from git, so uncomment it +#if 0 +#include "git-compat-util.h" +#endif #include "xmacros.h" #include "xdiff.h" #include "xtypes.h" @@ -58,4 +62,4 @@ #include "xemit.h" -#endif // #if !defined(XINCLUDE_H) +#endif /* #if !defined(XINCLUDE_H) */ diff --git a/src/nvim/xdiff/xmacros.h b/src/nvim/xdiff/xmacros.h index 1167ebbb05..2809a28ca9 100644 --- a/src/nvim/xdiff/xmacros.h +++ b/src/nvim/xdiff/xmacros.h @@ -51,4 +51,4 @@ do { \ } while (0) -#endif // #if !defined(XMACROS_H) +#endif /* #if !defined(XMACROS_H) */ diff --git a/src/nvim/xdiff/xpatience.c b/src/nvim/xdiff/xpatience.c index f6c84c67d8..f78c897ad8 100644 --- a/src/nvim/xdiff/xpatience.c +++ b/src/nvim/xdiff/xpatience.c @@ -20,8 +20,6 @@ * */ #include "xinclude.h" -#include "xtypes.h" -#include "xdiff.h" /* * The basic idea of patience diff is to find lines that are unique in @@ -69,7 +67,7 @@ struct hashmap { */ unsigned anchor : 1; } *entries, *first, *last; - // were common records found? + /* were common records found? */ unsigned long has_matches; mmfile_t *file1, *file2; xdfenv_t *env; @@ -78,21 +76,21 @@ struct hashmap { static int is_anchor(xpparam_t const *xpp, const char *line) { - size_t i; - for (i = 0; i < xpp->anchors_nr; i++) { + int i; + for (i = 0; i < (int)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. +/* 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], *other; + 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) @@ -106,11 +104,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map, int index = (int)((record->ha << 1) % map->alloc); while (map->entries[index].line1) { - other = map->env->xdf1.recs[map->entries[index].line1 - 1]; - if (map->entries[index].hash != record->ha || - !xdl_recmatch(record->ptr, record->size, - other->ptr, other->size, - map->xpp->flags)) { + if (map->entries[index].hash != record->ha) { if (++index >= map->alloc) index = 0; continue; @@ -155,7 +149,7 @@ static int fill_hashmap(mmfile_t *file1, mmfile_t *file2, result->xpp = xpp; result->env = env; - // We know exactly how large we want the hash map + /* We know exactly how large we want the hash map */ result->alloc = count1 * 2; result->entries = (struct entry *) xdl_malloc(result->alloc * sizeof(struct entry)); @@ -163,11 +157,11 @@ static int fill_hashmap(mmfile_t *file1, mmfile_t *file2, return -1; memset(result->entries, 0, result->alloc * sizeof(struct entry)); - // First, fill with entries from the first file + /* First, fill with entries from the first file */ while (count1--) insert_record(xpp, line1++, result, 1); - // Then search for matches in the second file + /* Then search for matches in the second file */ while (count2--) insert_record(xpp, line2++, result, 2); @@ -185,13 +179,13 @@ static int binary_search(struct entry **sequence, int longest, while (left + 1 < right) { int middle = left + (right - left) / 2; - // by construction, no two entries can be equal + /* 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 the index in "sequence", _not_ the sequence length */ return left; } @@ -206,9 +200,10 @@ static int binary_search(struct entry **sequence, int longest, */ static struct entry *find_longest_common_sequence(struct hashmap *map) { - struct entry **sequence = (struct entry **)xdl_malloc(map->nr * sizeof(struct entry *)); + struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *)); 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 @@ -237,13 +232,13 @@ static struct entry *find_longest_common_sequence(struct hashmap *map) } } - // No common unique lines were found + /* No common unique lines were found */ if (!longest) { xdl_free(sequence); return NULL; } - // Iterate starting at the last element, adjusting the "next" members + /* Iterate starting at the last element, adjusting the "next" members */ entry = sequence[longest - 1]; entry->next = NULL; while (entry->previous) { @@ -258,8 +253,7 @@ 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 xdl_recmatch(record1->ptr, record1->size, - record2->ptr, record2->size, map->xpp->flags); + return record1->ha == record2->ha; } static int patience_diff(mmfile_t *file1, mmfile_t *file2, @@ -273,7 +267,7 @@ static int walk_common_sequence(struct hashmap *map, struct entry *first, int next1, next2; for (;;) { - // Try to grow the line ranges of common lines + /* Try to grow the line ranges of common lines */ if (first) { next1 = first->line1; next2 = first->line2; @@ -292,11 +286,8 @@ static int walk_common_sequence(struct hashmap *map, struct entry *first, line2++; } - // Recurse + /* Recurse */ if (next1 > line1 || next2 > line2) { - struct hashmap submap; - - memset(&submap, 0, sizeof(submap)); if (patience_diff(map->file1, map->file2, map->xpp, map->env, line1, next1 - line1, @@ -323,6 +314,8 @@ 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, @@ -343,7 +336,7 @@ static int patience_diff(mmfile_t *file1, mmfile_t *file2, struct entry *first; int result = 0; - // trivial case: one side is empty + /* trivial case: one side is empty */ if (!count1) { while(count2--) env->xdf2.rchg[line2++ - 1] = 1; @@ -359,7 +352,7 @@ static int patience_diff(mmfile_t *file1, mmfile_t *file2, line1, count1, line2, count2)) return -1; - // are there any matching lines at all? + /* are there any matching lines at all? */ if (!map.has_matches) { while(count1--) env->xdf1.rchg[line1++ - 1] = 1; @@ -387,7 +380,7 @@ int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2, if (xdl_prepare_env(file1, file2, xpp, env) < 0) return -1; - // environment is cleaned up in xdl_diff() + /* environment is cleaned up in xdl_diff() */ return patience_diff(file1, file2, xpp, env, 1, env->xdf1.nrec, 1, env->xdf2.nrec); } diff --git a/src/nvim/xdiff/xprepare.h b/src/nvim/xdiff/xprepare.h index b67b3b25ab..947d9fc1bb 100644 --- a/src/nvim/xdiff/xprepare.h +++ b/src/nvim/xdiff/xprepare.h @@ -31,4 +31,4 @@ void xdl_free_env(xdfenv_t *xe); -#endif // #if !defined(XPREPARE_H) +#endif /* #if !defined(XPREPARE_H) */ diff --git a/src/nvim/xdiff/xtypes.h b/src/nvim/xdiff/xtypes.h index 026999c1bf..8442bd436e 100644 --- a/src/nvim/xdiff/xtypes.h +++ b/src/nvim/xdiff/xtypes.h @@ -64,4 +64,4 @@ typedef struct s_xdfenv { -#endif // #if !defined(XTYPES_H) +#endif /* #if !defined(XTYPES_H) */ diff --git a/src/nvim/xdiff/xutils.c b/src/nvim/xdiff/xutils.c index e8c7d2f884..5ef519155d 100644 --- a/src/nvim/xdiff/xutils.c +++ b/src/nvim/xdiff/xutils.c @@ -20,13 +20,9 @@ * */ -#include -#include #include "xinclude.h" - - long xdl_bogosqrt(long n) { long i; @@ -51,10 +47,10 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, 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 = (long)strlen(mb[2].ptr); + mb[2].size = strlen(mb[2].ptr); i++; } - if (ecb->outf(ecb->priv, mb, i) < 0) { + if (ecb->out_line(ecb->priv, mb, i) < 0) { return -1; } @@ -168,7 +164,7 @@ static int ends_with_optional_cr(const char *l, long s, long i) s--; if (s == i) return 1; - // do not ignore CR at the end of an incomplete line + /* do not ignore CR at the end of an incomplete line */ if (complete && s == i + 1 && l[i] == '\r') return 1; return 0; @@ -208,7 +204,7 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) } 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 + /* Skip matching spaces and try again */ while (i1 < s1 && XDL_ISSPACE(l1[i1])) i1++; while (i2 < s2 && XDL_ISSPACE(l2[i2])) @@ -224,7 +220,7 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) i2++; } } else if (flags & XDF_IGNORE_CR_AT_EOL) { - // Find the first difference and see how the line ends + /* Find the first difference and see how the line ends */ while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { i1++; i2++; @@ -261,7 +257,7 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data, for (; ptr < top && *ptr != '\n'; ptr++) { if (cr_at_eol_only) { - // do not ignore CR at the end of an incomplete line + /* do not ignore CR at the end of an incomplete line */ if (*ptr == '\r' && (ptr + 1 < top && ptr[1] == '\n')) continue; @@ -274,7 +270,7 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data, ptr++; at_eol = (top <= ptr + 1 || ptr[1] == '\n'); if (flags & XDF_IGNORE_WHITESPACE) - ; // already handled + ; /* already handled */ else if (flags & XDF_IGNORE_WHITESPACE_CHANGE && !at_eol) { ha += (ha << 5); @@ -344,8 +340,9 @@ int xdl_num_out(char *out, long val) { return str - out; } -int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, - const char *func, long funclen, xdemitcb_t *ecb) { +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]; @@ -387,9 +384,21 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, mb.ptr = buf; mb.size = nb; - if (ecb->outf(ecb->priv, &mb, 1) < 0) + 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; } diff --git a/src/nvim/xdiff/xutils.h b/src/nvim/xdiff/xutils.h index 0bebd93022..fba7bae03c 100644 --- a/src/nvim/xdiff/xutils.h +++ b/src/nvim/xdiff/xutils.h @@ -44,4 +44,4 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp, -#endif // #if !defined(XUTILS_H) +#endif /* #if !defined(XUTILS_H) */ -- cgit From fbe88ef8f5a4c1f7d42f997843bbf27119e28965 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 8 Sep 2021 22:25:19 +0200 Subject: build: move src/nvim/xdiff to src/xdiff Closes #11239 --- src/nvim/CMakeLists.txt | 4 +- src/nvim/xdiff/COPYING | 504 -------------------- src/nvim/xdiff/README.txt | 16 - src/nvim/xdiff/xdiff.h | 154 ------ src/nvim/xdiff/xdiffi.c | 1101 ------------------------------------------- src/nvim/xdiff/xdiffi.h | 64 --- src/nvim/xdiff/xemit.c | 348 -------------- src/nvim/xdiff/xemit.h | 36 -- src/nvim/xdiff/xhistogram.c | 386 --------------- src/nvim/xdiff/xinclude.h | 65 --- src/nvim/xdiff/xmacros.h | 54 --- src/nvim/xdiff/xpatience.c | 386 --------------- src/nvim/xdiff/xprepare.c | 483 ------------------- src/nvim/xdiff/xprepare.h | 34 -- src/nvim/xdiff/xtypes.h | 67 --- src/nvim/xdiff/xutils.c | 434 ----------------- src/nvim/xdiff/xutils.h | 47 -- src/xdiff/COPYING | 504 ++++++++++++++++++++ src/xdiff/README.txt | 16 + src/xdiff/xdiff.h | 154 ++++++ src/xdiff/xdiffi.c | 1101 +++++++++++++++++++++++++++++++++++++++++++ src/xdiff/xdiffi.h | 64 +++ src/xdiff/xemit.c | 348 ++++++++++++++ src/xdiff/xemit.h | 36 ++ src/xdiff/xhistogram.c | 386 +++++++++++++++ src/xdiff/xinclude.h | 65 +++ src/xdiff/xmacros.h | 54 +++ src/xdiff/xpatience.c | 386 +++++++++++++++ src/xdiff/xprepare.c | 483 +++++++++++++++++++ src/xdiff/xprepare.h | 34 ++ src/xdiff/xtypes.h | 67 +++ src/xdiff/xutils.c | 434 +++++++++++++++++ src/xdiff/xutils.h | 47 ++ 33 files changed, 4181 insertions(+), 4181 deletions(-) delete mode 100644 src/nvim/xdiff/COPYING delete mode 100644 src/nvim/xdiff/README.txt delete mode 100644 src/nvim/xdiff/xdiff.h delete mode 100644 src/nvim/xdiff/xdiffi.c delete mode 100644 src/nvim/xdiff/xdiffi.h delete mode 100644 src/nvim/xdiff/xemit.c delete mode 100644 src/nvim/xdiff/xemit.h delete mode 100644 src/nvim/xdiff/xhistogram.c delete mode 100644 src/nvim/xdiff/xinclude.h delete mode 100644 src/nvim/xdiff/xmacros.h delete mode 100644 src/nvim/xdiff/xpatience.c delete mode 100644 src/nvim/xdiff/xprepare.c delete mode 100644 src/nvim/xdiff/xprepare.h delete mode 100644 src/nvim/xdiff/xtypes.h delete mode 100644 src/nvim/xdiff/xutils.c delete mode 100644 src/nvim/xdiff/xutils.h create mode 100644 src/xdiff/COPYING create mode 100644 src/xdiff/README.txt create mode 100644 src/xdiff/xdiff.h create mode 100644 src/xdiff/xdiffi.c create mode 100644 src/xdiff/xdiffi.h create mode 100644 src/xdiff/xemit.c create mode 100644 src/xdiff/xemit.h create mode 100644 src/xdiff/xhistogram.c create mode 100644 src/xdiff/xinclude.h create mode 100644 src/xdiff/xmacros.h create mode 100644 src/xdiff/xpatience.c create mode 100644 src/xdiff/xprepare.c create mode 100644 src/xdiff/xprepare.h create mode 100644 src/xdiff/xtypes.h create mode 100644 src/xdiff/xutils.c create mode 100644 src/xdiff/xutils.h (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 3b2207f80b..4a698052ee 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -87,8 +87,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src) file(GLOB NVIM_SOURCES *.c) file(GLOB NVIM_HEADERS *.h) -file(GLOB XDIFF_SOURCES xdiff/*.c) -file(GLOB XDIFF_HEADERS xdiff/*.h) +file(GLOB XDIFF_SOURCES ../xdiff/*.c) +file(GLOB XDIFF_HEADERS ../xdiff/*.h) foreach(subdir os diff --git a/src/nvim/xdiff/COPYING b/src/nvim/xdiff/COPYING deleted file mode 100644 index f3f1b3b65e..0000000000 --- a/src/nvim/xdiff/COPYING +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/src/nvim/xdiff/README.txt b/src/nvim/xdiff/README.txt deleted file mode 100644 index 95b2242b87..0000000000 --- a/src/nvim/xdiff/README.txt +++ /dev/null @@ -1,16 +0,0 @@ -The files in this directory come from the xdiff implementation in git. -You can find it here: https://github.com/git/git/tree/master/xdiff -The files were last updated August 31, 2021 from git release v.2.33.0 - -This is originally based on libxdiff, which can be found here: -http://www.xmailserver.org/xdiff-lib.html - -The git version was used because it has been maintained and improved. -And since it's part of git it is expected to be reliable. - -The code is distributed under the GNU LGPL license. It is included in the -COPYING file. - -Changes in these files were made to avoid compiler warnings. - -The first work for including xdiff in Vim was done by Christian Brabandt. diff --git a/src/nvim/xdiff/xdiff.h b/src/nvim/xdiff/xdiff.h deleted file mode 100644 index f3dac80d3f..0000000000 --- a/src/nvim/xdiff/xdiff.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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 - -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 */ - #if 0 // unused by Vim - regex_t **ignore_regex; - size_t ignore_regex_nr; -#endif - - /* 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; - -#include "../memory.h" - -#define xdl_malloc(x) xmalloc((x)) -#define xdl_free(ptr) xfree(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) */ diff --git a/src/nvim/xdiff/xdiffi.c b/src/nvim/xdiff/xdiffi.c deleted file mode 100644 index cfcbb5d982..0000000000 --- a/src/nvim/xdiff/xdiffi.c +++ /dev/null @@ -1,1101 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#include "xinclude.h" - -#define XDL_MAX_COST_MIN 256 -#define XDL_HEUR_MIN_COST 256 -#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1) -#define XDL_SNAKE_CNT 20 -#define XDL_K_HEUR 4 - -typedef struct s_xdpsplit { - long i1, i2; - int min_lo, min_hi; -} xdpsplit_t; - -/* - * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers. - * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both - * the forward diagonal starting from (off1, off2) and the backward diagonal - * starting from (lim1, lim2). If the K values on the same diagonal crosses - * returns the furthest point of reach. We might encounter expensive edge cases - * using this algorithm, so a little bit of heuristic is needed to cut the - * search and to return a suboptimal point. - */ -static long xdl_split(unsigned long const *ha1, long off1, long lim1, - unsigned long const *ha2, long off2, long lim2, - long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, - xdalgoenv_t *xenv) { - long dmin = off1 - lim2, dmax = lim1 - off2; - long fmid = off1 - off2, bmid = lim1 - lim2; - long odd = (fmid - bmid) & 1; - long fmin = fmid, fmax = fmid; - long bmin = bmid, bmax = bmid; - long ec, d, i1, i2, prev1, best, dd, v, k; - - /* - * Set initial diagonal values for both forward and backward path. - */ - kvdf[fmid] = off1; - kvdb[bmid] = lim1; - - for (ec = 1;; ec++) { - int got_snake = 0; - - /* - * We need to extend the diagonal "domain" by one. If the next - * values exits the box boundaries we need to change it in the - * opposite direction because (max - min) must be a power of - * two. - * - * Also we initialize the external K value to -1 so that we can - * avoid extra conditions in the check inside the core loop. - */ - if (fmin > dmin) - kvdf[--fmin - 1] = -1; - else - ++fmin; - if (fmax < dmax) - kvdf[++fmax + 1] = -1; - else - --fmax; - - for (d = fmax; d >= fmin; d -= 2) { - if (kvdf[d - 1] >= kvdf[d + 1]) - i1 = kvdf[d - 1] + 1; - else - i1 = kvdf[d + 1]; - prev1 = i1; - i2 = i1 - d; - for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++); - if (i1 - prev1 > xenv->snake_cnt) - got_snake = 1; - kvdf[d] = i1; - if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) { - spl->i1 = i1; - spl->i2 = i2; - spl->min_lo = spl->min_hi = 1; - return ec; - } - } - - /* - * We need to extend the diagonal "domain" by one. If the next - * values exits the box boundaries we need to change it in the - * opposite direction because (max - min) must be a power of - * two. - * - * Also we initialize the external K value to -1 so that we can - * avoid extra conditions in the check inside the core loop. - */ - if (bmin > dmin) - kvdb[--bmin - 1] = XDL_LINE_MAX; - else - ++bmin; - if (bmax < dmax) - kvdb[++bmax + 1] = XDL_LINE_MAX; - else - --bmax; - - for (d = bmax; d >= bmin; d -= 2) { - if (kvdb[d - 1] < kvdb[d + 1]) - i1 = kvdb[d - 1]; - else - i1 = kvdb[d + 1] - 1; - prev1 = i1; - i2 = i1 - d; - for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--); - if (prev1 - i1 > xenv->snake_cnt) - got_snake = 1; - kvdb[d] = i1; - if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) { - spl->i1 = i1; - spl->i2 = i2; - spl->min_lo = spl->min_hi = 1; - return ec; - } - } - - if (need_min) - continue; - - /* - * If the edit cost is above the heuristic trigger and if - * we got a good snake, we sample current diagonals to see - * if some of them have reached an "interesting" path. Our - * measure is a function of the distance from the diagonal - * corner (i1 + i2) penalized with the distance from the - * mid diagonal itself. If this value is above the current - * edit cost times a magic factor (XDL_K_HEUR) we consider - * it interesting. - */ - if (got_snake && ec > xenv->heur_min) { - for (best = 0, d = fmax; d >= fmin; d -= 2) { - dd = d > fmid ? d - fmid: fmid - d; - i1 = kvdf[d]; - i2 = i1 - d; - v = (i1 - off1) + (i2 - off2) - dd; - - if (v > XDL_K_HEUR * ec && v > best && - off1 + xenv->snake_cnt <= i1 && i1 < lim1 && - off2 + xenv->snake_cnt <= i2 && i2 < lim2) { - for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++) - if (k == xenv->snake_cnt) { - best = v; - spl->i1 = i1; - spl->i2 = i2; - break; - } - } - } - if (best > 0) { - spl->min_lo = 1; - spl->min_hi = 0; - return ec; - } - - for (best = 0, d = bmax; d >= bmin; d -= 2) { - dd = d > bmid ? d - bmid: bmid - d; - i1 = kvdb[d]; - i2 = i1 - d; - v = (lim1 - i1) + (lim2 - i2) - dd; - - if (v > XDL_K_HEUR * ec && v > best && - off1 < i1 && i1 <= lim1 - xenv->snake_cnt && - off2 < i2 && i2 <= lim2 - xenv->snake_cnt) { - for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++) - if (k == xenv->snake_cnt - 1) { - best = v; - spl->i1 = i1; - spl->i2 = i2; - break; - } - } - } - if (best > 0) { - spl->min_lo = 0; - spl->min_hi = 1; - return ec; - } - } - - /* - * Enough is enough. We spent too much time here and now we - * collect the furthest reaching path using the (i1 + i2) - * measure. - */ - if (ec >= xenv->mxcost) { - long fbest, fbest1, bbest, bbest1; - - fbest = fbest1 = -1; - for (d = fmax; d >= fmin; d -= 2) { - i1 = XDL_MIN(kvdf[d], lim1); - i2 = i1 - d; - if (lim2 < i2) - i1 = lim2 + d, i2 = lim2; - if (fbest < i1 + i2) { - fbest = i1 + i2; - fbest1 = i1; - } - } - - bbest = bbest1 = XDL_LINE_MAX; - for (d = bmax; d >= bmin; d -= 2) { - i1 = XDL_MAX(off1, kvdb[d]); - i2 = i1 - d; - if (i2 < off2) - i1 = off2 + d, i2 = off2; - if (i1 + i2 < bbest) { - bbest = i1 + i2; - bbest1 = i1; - } - } - - if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) { - spl->i1 = fbest1; - spl->i2 = fbest - fbest1; - spl->min_lo = 1; - spl->min_hi = 0; - } else { - spl->i1 = bbest1; - spl->i2 = bbest - bbest1; - spl->min_lo = 0; - spl->min_hi = 1; - } - return ec; - } - } -} - - -/* - * Rule: "Divide et Impera" (divide & conquer). Recursively split the box in - * sub-boxes by calling the box splitting function. Note that the real job - * (marking changed lines) is done in the two boundary reaching checks. - */ -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) { - unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha; - - /* - * Shrink the box by walking through each diagonal snake (SW and NE). - */ - for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++); - for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--); - - /* - * If one dimension is empty, then all records on the other one must - * be obviously changed. - */ - if (off1 == lim1) { - char *rchg2 = dd2->rchg; - long *rindex2 = dd2->rindex; - - for (; off2 < lim2; off2++) - rchg2[rindex2[off2]] = 1; - } else if (off2 == lim2) { - char *rchg1 = dd1->rchg; - long *rindex1 = dd1->rindex; - - for (; off1 < lim1; off1++) - rchg1[rindex1[off1]] = 1; - } else { - xdpsplit_t spl; - spl.i1 = spl.i2 = 0; - - /* - * Divide ... - */ - if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb, - need_min, &spl, xenv) < 0) { - - return -1; - } - - /* - * ... et Impera. - */ - if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2, - kvdf, kvdb, spl.min_lo, xenv) < 0 || - xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2, - kvdf, kvdb, spl.min_hi, xenv) < 0) { - - return -1; - } - } - - return 0; -} - - -int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, - xdfenv_t *xe) { - long ndiags; - long *kvd, *kvdf, *kvdb; - xdalgoenv_t xenv; - diffdata_t dd1, dd2; - - if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF) - return xdl_do_patience_diff(mf1, mf2, xpp, xe); - - if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) - return xdl_do_histogram_diff(mf1, mf2, xpp, xe); - - if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) { - - return -1; - } - - /* - * Allocate and setup K vectors to be used by the differential - * algorithm. - * - * One is to store the forward path and one to store the backward path. - */ - ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; - if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) { - - xdl_free_env(xe); - return -1; - } - kvdf = kvd; - kvdb = kvdf + ndiags; - kvdf += xe->xdf2.nreff + 1; - kvdb += xe->xdf2.nreff + 1; - - xenv.mxcost = xdl_bogosqrt(ndiags); - if (xenv.mxcost < XDL_MAX_COST_MIN) - xenv.mxcost = XDL_MAX_COST_MIN; - xenv.snake_cnt = XDL_SNAKE_CNT; - xenv.heur_min = XDL_HEUR_MIN_COST; - - dd1.nrec = xe->xdf1.nreff; - dd1.ha = xe->xdf1.ha; - dd1.rchg = xe->xdf1.rchg; - dd1.rindex = xe->xdf1.rindex; - dd2.nrec = xe->xdf2.nreff; - dd2.ha = xe->xdf2.ha; - dd2.rchg = xe->xdf2.rchg; - dd2.rindex = xe->xdf2.rindex; - - if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec, - kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) { - - xdl_free(kvd); - xdl_free_env(xe); - return -1; - } - - xdl_free(kvd); - - return 0; -} - - -static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) { - xdchange_t *xch; - - if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t)))) - return NULL; - - xch->next = xscr; - xch->i1 = i1; - xch->i2 = i2; - xch->chg1 = chg1; - xch->chg2 = chg2; - xch->ignore = 0; - - return xch; -} - - -static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags) -{ - return (rec1->ha == rec2->ha && - xdl_recmatch(rec1->ptr, rec1->size, - rec2->ptr, rec2->size, - flags)); -} - -/* - * If a line is indented more than this, xget_indent() just returns this value. - * This avoids having to do absurd amounts of work for data that are not - * human-readable text, and also ensures that the output of xget_indent fits within - * an int. - */ -#define MAX_INDENT 200 - -/* - * Return the amount of indentation of the specified line, treating TAB as 8 - * columns. Return -1 if line is empty or contains only whitespace. Clamp the - * output value at MAX_INDENT. - */ -static int xget_indent(xrecord_t *rec) -{ - long i; - int ret = 0; - - for (i = 0; i < rec->size; i++) { - char c = rec->ptr[i]; - - if (!XDL_ISSPACE(c)) - return ret; - else if (c == ' ') - ret += 1; - else if (c == '\t') - ret += 8 - ret % 8; - /* ignore other whitespace characters */ - - if (ret >= MAX_INDENT) - return MAX_INDENT; - } - - /* The line contains only whitespace. */ - return -1; -} - -/* - * If more than this number of consecutive blank rows are found, just return - * this value. This avoids requiring O(N^2) work for pathological cases, and - * also ensures that the output of score_split fits in an int. - */ -#define MAX_BLANKS 20 - -/* Characteristics measured about a hypothetical split position. */ -struct split_measurement { - /* - * Is the split at the end of the file (aside from any blank lines)? - */ - int end_of_file; - - /* - * How much is the line immediately following the split indented (or -1 - * if the line is blank): - */ - int indent; - - /* - * How many consecutive lines above the split are blank? - */ - int pre_blank; - - /* - * How much is the nearest non-blank line above the split indented (or - * -1 if there is no such line)? - */ - int pre_indent; - - /* - * How many lines after the line following the split are blank? - */ - int post_blank; - - /* - * How much is the nearest non-blank line after the line following the - * split indented (or -1 if there is no such line)? - */ - int post_indent; -}; - -struct split_score { - /* The effective indent of this split (smaller is preferred). */ - int effective_indent; - - /* Penalty for this split (smaller is preferred). */ - int penalty; -}; - -/* - * Fill m with information about a hypothetical split of xdf above line split. - */ -static void measure_split(const xdfile_t *xdf, long split, - struct split_measurement *m) -{ - long i; - - if (split >= xdf->nrec) { - m->end_of_file = 1; - m->indent = -1; - } else { - m->end_of_file = 0; - m->indent = xget_indent(xdf->recs[split]); - } - - m->pre_blank = 0; - m->pre_indent = -1; - for (i = split - 1; i >= 0; i--) { - m->pre_indent = xget_indent(xdf->recs[i]); - if (m->pre_indent != -1) - break; - m->pre_blank += 1; - if (m->pre_blank == MAX_BLANKS) { - m->pre_indent = 0; - break; - } - } - - m->post_blank = 0; - m->post_indent = -1; - for (i = split + 1; i < xdf->nrec; i++) { - m->post_indent = xget_indent(xdf->recs[i]); - if (m->post_indent != -1) - break; - m->post_blank += 1; - if (m->post_blank == MAX_BLANKS) { - m->post_indent = 0; - break; - } - } -} - -/* - * The empirically-determined weight factors used by score_split() below. - * Larger values means that the position is a less favorable place to split. - * - * Note that scores are only ever compared against each other, so multiplying - * all of these weight/penalty values by the same factor wouldn't change the - * heuristic's behavior. Still, we need to set that arbitrary scale *somehow*. - * In practice, these numbers are chosen to be large enough that they can be - * adjusted relative to each other with sufficient precision despite using - * integer math. - */ - -/* Penalty if there are no non-blank lines before the split */ -#define START_OF_FILE_PENALTY 1 - -/* Penalty if there are no non-blank lines after the split */ -#define END_OF_FILE_PENALTY 21 - -/* Multiplier for the number of blank lines around the split */ -#define TOTAL_BLANK_WEIGHT (-30) - -/* Multiplier for the number of blank lines after the split */ -#define POST_BLANK_WEIGHT 6 - -/* - * Penalties applied if the line is indented more than its predecessor - */ -#define RELATIVE_INDENT_PENALTY (-4) -#define RELATIVE_INDENT_WITH_BLANK_PENALTY 10 - -/* - * Penalties applied if the line is indented less than both its predecessor and - * its successor - */ -#define RELATIVE_OUTDENT_PENALTY 24 -#define RELATIVE_OUTDENT_WITH_BLANK_PENALTY 17 - -/* - * Penalties applied if the line is indented less than its predecessor but not - * less than its successor - */ -#define RELATIVE_DEDENT_PENALTY 23 -#define RELATIVE_DEDENT_WITH_BLANK_PENALTY 17 - -/* - * We only consider whether the sum of the effective indents for splits are - * less than (-1), equal to (0), or greater than (+1) each other. The resulting - * value is multiplied by the following weight and combined with the penalty to - * determine the better of two scores. - */ -#define INDENT_WEIGHT 60 - -/* - * How far do we slide a hunk at most? - */ -#define INDENT_HEURISTIC_MAX_SLIDING 100 - -/* - * Compute a badness score for the hypothetical split whose measurements are - * stored in m. The weight factors were determined empirically using the tools - * and corpus described in - * - * https://github.com/mhagger/diff-slider-tools - * - * Also see that project if you want to improve the weights based on, for - * example, a larger or more diverse corpus. - */ -static void score_add_split(const struct split_measurement *m, struct split_score *s) -{ - /* - * A place to accumulate penalty factors (positive makes this index more - * favored): - */ - int post_blank, total_blank, indent, any_blanks; - - if (m->pre_indent == -1 && m->pre_blank == 0) - s->penalty += START_OF_FILE_PENALTY; - - if (m->end_of_file) - s->penalty += END_OF_FILE_PENALTY; - - /* - * Set post_blank to the number of blank lines following the split, - * including the line immediately after the split: - */ - post_blank = (m->indent == -1) ? 1 + m->post_blank : 0; - total_blank = m->pre_blank + post_blank; - - /* Penalties based on nearby blank lines: */ - s->penalty += TOTAL_BLANK_WEIGHT * total_blank; - s->penalty += POST_BLANK_WEIGHT * post_blank; - - if (m->indent != -1) - indent = m->indent; - else - indent = m->post_indent; - - any_blanks = (total_blank != 0); - - /* Note that the effective indent is -1 at the end of the file: */ - s->effective_indent += indent; - - if (indent == -1) { - /* No additional adjustments needed. */ - } else if (m->pre_indent == -1) { - /* No additional adjustments needed. */ - } else if (indent > m->pre_indent) { - /* - * The line is indented more than its predecessor. - */ - s->penalty += any_blanks ? - RELATIVE_INDENT_WITH_BLANK_PENALTY : - RELATIVE_INDENT_PENALTY; - } else if (indent == m->pre_indent) { - /* - * The line has the same indentation level as its predecessor. - * No additional adjustments needed. - */ - } else { - /* - * The line is indented less than its predecessor. It could be - * the block terminator of the previous block, but it could - * also be the start of a new block (e.g., an "else" block, or - * maybe the previous block didn't have a block terminator). - * Try to distinguish those cases based on what comes next: - */ - if (m->post_indent != -1 && m->post_indent > indent) { - /* - * The following line is indented more. So it is likely - * that this line is the start of a block. - */ - s->penalty += any_blanks ? - RELATIVE_OUTDENT_WITH_BLANK_PENALTY : - RELATIVE_OUTDENT_PENALTY; - } else { - /* - * That was probably the end of a block. - */ - s->penalty += any_blanks ? - RELATIVE_DEDENT_WITH_BLANK_PENALTY : - RELATIVE_DEDENT_PENALTY; - } - } -} - -static int score_cmp(struct split_score *s1, struct split_score *s2) -{ - /* -1 if s1.effective_indent < s2->effective_indent, etc. */ - int cmp_indents = ((s1->effective_indent > s2->effective_indent) - - (s1->effective_indent < s2->effective_indent)); - - return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty); -} - -/* - * Represent a group of changed lines in an xdfile_t (i.e., a contiguous group - * of lines that was inserted or deleted from the corresponding version of the - * file). We consider there to be such a group at the beginning of the file, at - * the end of the file, and between any two unchanged lines, though most such - * groups will usually be empty. - * - * If the first line in a group is equal to the line following the group, then - * the group can be slid down. Similarly, if the last line in a group is equal - * to the line preceding the group, then the group can be slid up. See - * group_slide_down() and group_slide_up(). - * - * Note that loops that are testing for changed lines in xdf->rchg do not need - * index bounding since the array is prepared with a zero at position -1 and N. - */ -struct xdlgroup { - /* - * The index of the first changed line in the group, or the index of - * the unchanged line above which the (empty) group is located. - */ - long start; - - /* - * The index of the first unchanged line after the group. For an empty - * group, end is equal to start. - */ - long end; -}; - -/* - * Initialize g to point at the first group in xdf. - */ -static void group_init(xdfile_t *xdf, struct xdlgroup *g) -{ - g->start = g->end = 0; - while (xdf->rchg[g->end]) - g->end++; -} - -/* - * Move g to describe the next (possibly empty) group in xdf and return 0. If g - * is already at the end of the file, do nothing and return -1. - */ -static inline int group_next(xdfile_t *xdf, struct xdlgroup *g) -{ - if (g->end == xdf->nrec) - return -1; - - g->start = g->end + 1; - for (g->end = g->start; xdf->rchg[g->end]; g->end++) - ; - - return 0; -} - -/* - * Move g to describe the previous (possibly empty) group in xdf and return 0. - * If g is already at the beginning of the file, do nothing and return -1. - */ -static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g) -{ - if (g->start == 0) - return -1; - - g->end = g->start - 1; - for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--) - ; - - return 0; -} - -/* - * If g can be slid toward the end of the file, do so, and if it bumps into a - * following group, expand this group to include it. Return 0 on success or -1 - * if g cannot be slid down. - */ -static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags) -{ - if (g->end < xdf->nrec && - recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) { - xdf->rchg[g->start++] = 0; - xdf->rchg[g->end++] = 1; - - while (xdf->rchg[g->end]) - g->end++; - - return 0; - } else { - return -1; - } -} - -/* - * If g can be slid toward the beginning of the file, do so, and if it bumps - * into a previous group, expand this group to include it. Return 0 on success - * or -1 if g cannot be slid up. - */ -static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags) -{ - if (g->start > 0 && - recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) { - xdf->rchg[--g->start] = 1; - xdf->rchg[--g->end] = 0; - - while (xdf->rchg[g->start - 1]) - g->start--; - - return 0; - } else { - return -1; - } -} - -static void xdl_bug(const char *msg) -{ - fprintf(stderr, "BUG: %s\n", msg); - exit(1); -} - -/* - * Move back and forward change groups for a consistent and pretty diff output. - * This also helps in finding joinable change groups and reducing the diff - * size. - */ -int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { - struct xdlgroup g, go; - long earliest_end, end_matching_other; - long groupsize; - - group_init(xdf, &g); - group_init(xdfo, &go); - - while (1) { - /* - * If the group is empty in the to-be-compacted file, skip it: - */ - if (g.end == g.start) - goto next; - - /* - * Now shift the change up and then down as far as possible in - * each direction. If it bumps into any other changes, merge - * them. - */ - do { - groupsize = g.end - g.start; - - /* - * Keep track of the last "end" index that causes this - * group to align with a group of changed lines in the - * other file. -1 indicates that we haven't found such - * a match yet: - */ - end_matching_other = -1; - - /* Shift the group backward as much as possible: */ - while (!group_slide_up(xdf, &g, flags)) - if (group_previous(xdfo, &go)) - xdl_bug("group sync broken sliding up"); - - /* - * This is this highest that this group can be shifted. - * Record its end index: - */ - earliest_end = g.end; - - if (go.end > go.start) - end_matching_other = g.end; - - /* Now shift the group forward as far as possible: */ - while (1) { - if (group_slide_down(xdf, &g, flags)) - break; - if (group_next(xdfo, &go)) - xdl_bug("group sync broken sliding down"); - - if (go.end > go.start) - end_matching_other = g.end; - } - } while (groupsize != g.end - g.start); - - /* - * If the group can be shifted, then we can possibly use this - * freedom to produce a more intuitive diff. - * - * The group is currently shifted as far down as possible, so - * the heuristics below only have to handle upwards shifts. - */ - - if (g.end == earliest_end) { - /* no shifting was possible */ - } else if (end_matching_other != -1) { - /* - * Move the possibly merged group of changes back to - * line up with the last group of changes from the - * other file that it can align with. - */ - while (go.end == go.start) { - if (group_slide_up(xdf, &g, flags)) - xdl_bug("match disappeared"); - if (group_previous(xdfo, &go)) - xdl_bug("group sync broken sliding to match"); - } - } else if (flags & XDF_INDENT_HEURISTIC) { - /* - * Indent heuristic: a group of pure add/delete lines - * implies two splits, one between the end of the - * "before" context and the start of the group, and - * another between the end of the group and the - * beginning of the "after" context. Some splits are - * aesthetically better and some are worse. We compute - * a badness "score" for each split, and add the scores - * for the two splits to define a "score" for each - * position that the group can be shifted to. Then we - * pick the shift with the lowest score. - */ - long shift, best_shift = -1; - struct split_score best_score; - - shift = earliest_end; - if (g.end - groupsize - 1 > shift) - shift = g.end - groupsize - 1; - if (g.end - INDENT_HEURISTIC_MAX_SLIDING > shift) - shift = g.end - INDENT_HEURISTIC_MAX_SLIDING; - for (; shift <= g.end; shift++) { - struct split_measurement m; - struct split_score score = {0, 0}; - - measure_split(xdf, shift, &m); - score_add_split(&m, &score); - measure_split(xdf, shift - groupsize, &m); - score_add_split(&m, &score); - if (best_shift == -1 || - score_cmp(&score, &best_score) <= 0) { - best_score.effective_indent = score.effective_indent; - best_score.penalty = score.penalty; - best_shift = shift; - } - } - - while (g.end > best_shift) { - if (group_slide_up(xdf, &g, flags)) - xdl_bug("best shift unreached"); - if (group_previous(xdfo, &go)) - xdl_bug("group sync broken sliding to blank line"); - } - } - - next: - /* Move past the just-processed group: */ - if (group_next(xdf, &g)) - break; - if (group_next(xdfo, &go)) - xdl_bug("group sync broken moving to next group"); - } - - if (!group_next(xdfo, &go)) - xdl_bug("group sync broken at end of file"); - - return 0; -} - - -int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) { - xdchange_t *cscr = NULL, *xch; - char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg; - long i1, i2, l1, l2; - - /* - * Trivial. Collects "groups" of changes and creates an edit script. - */ - for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--) - if (rchg1[i1 - 1] || rchg2[i2 - 1]) { - for (l1 = i1; rchg1[i1 - 1]; i1--); - for (l2 = i2; rchg2[i2 - 1]; i2--); - - if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) { - xdl_free_script(cscr); - return -1; - } - cscr = xch; - } - - *xscr = cscr; - - return 0; -} - - -void xdl_free_script(xdchange_t *xscr) { - xdchange_t *xch; - - while ((xch = xscr) != NULL) { - xscr = xscr->next; - xdl_free(xch); - } -} - -static int xdl_call_hunk_func(xdfenv_t *xe UNUSED, xdchange_t *xscr, xdemitcb_t *ecb, - xdemitconf_t const *xecfg) -{ - xdchange_t *xch, *xche; - - for (xch = xscr; xch; xch = xche->next) { - xche = xdl_get_hunk(&xch, xecfg); - if (!xch) - break; - if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1, - xch->i2, xche->i2 + xche->chg2 - xch->i2, - ecb->priv) < 0) - return -1; - } - return 0; -} - -static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags) -{ - xdchange_t *xch; - - for (xch = xscr; xch; xch = xch->next) { - int ignore = 1; - xrecord_t **rec; - long i; - - rec = &xe->xdf1.recs[xch->i1]; - for (i = 0; i < xch->chg1 && ignore; i++) - ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); - - rec = &xe->xdf2.recs[xch->i2]; - for (i = 0; i < xch->chg2 && ignore; i++) - ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); - - xch->ignore = ignore; - } -} - -#if 0 // unused by Vim -static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) { - regmatch_t regmatch; - int i; - - for (i = 0; i < xpp->ignore_regex_nr; i++) - if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1, - ®match, 0)) - return 1; - - return 0; -} - -static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe, - xpparam_t const *xpp) -{ - xdchange_t *xch; - - for (xch = xscr; xch; xch = xch->next) { - xrecord_t **rec; - int ignore = 1; - long i; - - /* - * Do not override --ignore-blank-lines. - */ - if (xch->ignore) - continue; - - rec = &xe->xdf1.recs[xch->i1]; - for (i = 0; i < xch->chg1 && ignore; i++) - ignore = record_matches_regex(rec[i], xpp); - - rec = &xe->xdf2.recs[xch->i2]; - for (i = 0; i < xch->chg2 && ignore; i++) - ignore = record_matches_regex(rec[i], xpp); - - xch->ignore = ignore; - } -} -#endif - -int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, - xdemitconf_t const *xecfg, xdemitcb_t *ecb) { - xdchange_t *xscr; - xdfenv_t xe; - emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff; - - if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) { - - return -1; - } - if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 || - xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 || - xdl_build_script(&xe, &xscr) < 0) { - - xdl_free_env(&xe); - return -1; - } - if (xscr) { - if (xpp->flags & XDF_IGNORE_BLANK_LINES) - xdl_mark_ignorable_lines(xscr, &xe, xpp->flags); - -#if 0 - if (xpp->ignore_regex) - xdl_mark_ignorable_regex(xscr, &xe, xpp); -#endif - - if (ef(&xe, xscr, ecb, xecfg) < 0) { - - xdl_free_script(xscr); - xdl_free_env(&xe); - return -1; - } - xdl_free_script(xscr); - } - xdl_free_env(&xe); - - return 0; -} diff --git a/src/nvim/xdiff/xdiffi.h b/src/nvim/xdiff/xdiffi.h deleted file mode 100644 index 8f1c7c8b04..0000000000 --- a/src/nvim/xdiff/xdiffi.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, - xdfenv_t *env); -int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, - xdfenv_t *env); - -#endif /* #if !defined(XDIFFI_H) */ diff --git a/src/nvim/xdiff/xemit.c b/src/nvim/xdiff/xemit.c deleted file mode 100644 index a0078f928c..0000000000 --- a/src/nvim/xdiff/xemit.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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 == NULL) - 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 + (long)ignored - (lxch->i1 + lxch->chg1) > max_common) { - break; - } else if (!xch->ignore) { - lxch = xch; - ignored = 0; - } else { - ignored += xch->chg2; - } - } - - return lxch; -} - - -#if 0 -static long def_ff(const char *rec, long len, char *buf, long sz, void *priv UNUSED) -{ - 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; -} -#endif - -#if 0 -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, xecfg->find_func_priv); - return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv); -} -#endif - -#if 0 -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; -} -#endif - -struct func_line { - long len; - char buf[80]; -}; - -#if 0 -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; -} -#endif - -#if 0 -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; -} -#endif - -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; -#if 0 - long funclineprev = -1; -#endif - struct func_line func_line; - - func_line.len = 0; - - for (xch = xscr; xch; xch = xche->next) { - xche = xdl_get_hunk(&xch, xecfg); - if (!xch) - break; - - s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); - s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0); - -#if 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: -#endif - 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 0 - 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; - } - } - } -#endif - - /* - * Emit current hunk header. - */ - -#if 0 - if (xecfg->flags & XDL_EMIT_FUNCNAMES) { - get_func_line(xe, xecfg, &func_line, - s1 - 1, funclineprev); - funclineprev = s1 - 1; - } -#endif - 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; -} diff --git a/src/nvim/xdiff/xemit.h b/src/nvim/xdiff/xemit.h deleted file mode 100644 index 1b9887e670..0000000000 --- a/src/nvim/xdiff/xemit.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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) */ diff --git a/src/nvim/xdiff/xhistogram.c b/src/nvim/xdiff/xhistogram.c deleted file mode 100644 index 8598a8550d..0000000000 --- a/src/nvim/xdiff/xhistogram.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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 INT_MAX -#define MAX_CNT INT_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(xpparam_t const *xpp, - xrecord_t *r1, xrecord_t *r2) -{ - return r1->ha == r2->ha && - xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size, - xpp->flags); -} - -#define CMP_ENV(xpp, env, s1, l1, s2, l2) \ - (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2))) - -#define CMP(i, s1, l1, s2, l2) \ - (cmp_recs(i->xpp, 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) -{ - 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 < (int)as && line2 < (int)bs - && CMP(index, 1, as - 1, 2, bs - 1)) { - as--; - bs--; - if (1 < rc) - rc = XDL_MIN(rc, CNT(index, as)); - } - while ((int)ae < LINE_END(1) && (int)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 sz, 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); - sz = index.records_size = 1 << index.table_bits; - sz *= sizeof(struct record *); - if (!(index.records = (struct record **) xdl_malloc(sz))) - goto cleanup; - memset(index.records, 0, sz); - - sz = index.line_map_size = count1; - sz *= sizeof(struct record *); - if (!(index.line_map = (struct record **) xdl_malloc(sz))) - goto cleanup; - memset(index.line_map, 0, sz); - - sz = index.line_map_size; - sz *= sizeof(unsigned int); - if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz))) - goto cleanup; - memset(index.next_ptrs, 0, sz); - - /* 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(mmfile_t *file1, mmfile_t *file2, - xpparam_t const *xpp, xdfenv_t *env) -{ - if (xdl_prepare_env(file1, file2, xpp, env) < 0) - return -1; - - 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); -} diff --git a/src/nvim/xdiff/xinclude.h b/src/nvim/xdiff/xinclude.h deleted file mode 100644 index a412404bfe..0000000000 --- a/src/nvim/xdiff/xinclude.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -// defines HAVE_ATTRIBUTE_UNUSED -#ifdef HAVE_CONFIG_H -# include "../auto/config.h" -#endif - -// Mark unused function arguments with UNUSED, so that gcc -Wunused-parameter -// can be used to check for mistakes. -#ifdef HAVE_ATTRIBUTE_UNUSED -# define UNUSED __attribute__((unused)) -#else -# define UNUSED -#endif - -#if defined(_MSC_VER) -# define inline __inline -#endif - -#if !defined(XINCLUDE_H) -#define XINCLUDE_H - -// This effectively re-verts b46054b3746271d23feab0 from git -#include -#include -#include -#if !defined(_WIN32) -#include -#endif -#include -#include -// This include comes from git, so uncomment it -#if 0 -#include "git-compat-util.h" -#endif -#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) */ diff --git a/src/nvim/xdiff/xmacros.h b/src/nvim/xdiff/xmacros.h deleted file mode 100644 index 2809a28ca9..0000000000 --- a/src/nvim/xdiff/xmacros.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0) -#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) - - -#endif /* #if !defined(XMACROS_H) */ diff --git a/src/nvim/xdiff/xpatience.c b/src/nvim/xdiff/xpatience.c deleted file mode 100644 index f78c897ad8..0000000000 --- a/src/nvim/xdiff/xpatience.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ -#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; - mmfile_t *file1, *file2; - xdfenv_t *env; - xpparam_t const *xpp; -}; - -static int is_anchor(xpparam_t const *xpp, const char *line) -{ - int i; - for (i = 0; i < (int)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(mmfile_t *file1, mmfile_t *file2, - xpparam_t const *xpp, xdfenv_t *env, - struct hashmap *result, - int line1, int count1, int line2, int count2) -{ - result->file1 = file1; - result->file2 = file2; - result->xpp = xpp; - result->env = env; - - /* We know exactly how large we want the hash map */ - result->alloc = count1 * 2; - result->entries = (struct entry *) - xdl_malloc(result->alloc * sizeof(struct entry)); - if (!result->entries) - return -1; - memset(result->entries, 0, result->alloc * sizeof(struct entry)); - - /* 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 struct entry *find_longest_common_sequence(struct hashmap *map) -{ - struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *)); - 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; - - // Added to silence Coverity. - if (sequence == NULL) - return map->first; - - 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) { - xdl_free(sequence); - return NULL; - } - - /* 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; - } - xdl_free(sequence); - return entry; -} - -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(mmfile_t *file1, mmfile_t *file2, - 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->file1, map->file2, - 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(mmfile_t *file1, mmfile_t *file2, - 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(file1, file2, 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; - } - - first = find_longest_common_sequence(&map); - if (first) - result = walk_common_sequence(&map, first, - line1, count1, line2, count2); - else - result = fall_back_to_classic_diff(&map, - line1, count1, line2, count2); - - xdl_free(map.entries); - return result; -} - -int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2, - xpparam_t const *xpp, xdfenv_t *env) -{ - if (xdl_prepare_env(file1, file2, xpp, env) < 0) - return -1; - - /* environment is cleaned up in xdl_diff() */ - return patience_diff(file1, file2, xpp, env, - 1, env->xdf1.nrec, 1, env->xdf2.nrec); -} diff --git a/src/nvim/xdiff/xprepare.c b/src/nvim/xdiff/xprepare.c deleted file mode 100644 index abeb8fb84e..0000000000 --- a/src/nvim/xdiff/xprepare.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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 (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) { - - xdl_cha_free(&cf->ncha); - return -1; - } - memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *)); - - cf->alloc = size; - if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) { - - 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; - xdlclass_t **rcrecs; - - 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 (cf->count > cf->alloc) { - cf->alloc *= 2; - if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) { - - return -1; - } - cf->rcrecs = rcrecs; - } - 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, **rrecs; - 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 (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) - goto abort; - - if (XDF_DIFF_ALG(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 *)); - } - - nrec = 0; - if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) { - for (top = blk + bsize; cur < top; ) { - prev = cur; - hav = xdl_hash_record(&cur, top, xpp->flags); - if (nrec >= narec) { - narec *= 2; - if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) - goto abort; - recs = rrecs; - } - if (!(crec = xdl_cha_alloc(&xdf->rcha))) - goto abort; - crec->ptr = prev; - crec->size = (long) (cur - prev); - crec->ha = hav; - recs[nrec++] = crec; - - if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && - xdl_classify_record(pass, cf, rhash, hbits, crec) < 0) - goto abort; - } - } - - 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)))) - goto abort; - if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) - 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 (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF && - 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; - } - - if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) - 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 (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) { - - return -1; - } - 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++) { - 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; -} diff --git a/src/nvim/xdiff/xprepare.h b/src/nvim/xdiff/xprepare.h deleted file mode 100644 index 947d9fc1bb..0000000000 --- a/src/nvim/xdiff/xprepare.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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) */ diff --git a/src/nvim/xdiff/xtypes.h b/src/nvim/xdiff/xtypes.h deleted file mode 100644 index 8442bd436e..0000000000 --- a/src/nvim/xdiff/xtypes.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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) */ diff --git a/src/nvim/xdiff/xutils.c b/src/nvim/xdiff/xutils.c deleted file mode 100644 index 5ef519155d..0000000000 --- a/src/nvim/xdiff/xutils.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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)) != NULL) { - 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 > (long)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; -} diff --git a/src/nvim/xdiff/xutils.h b/src/nvim/xdiff/xutils.h deleted file mode 100644 index fba7bae03c..0000000000 --- a/src/nvim/xdiff/xutils.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 - * . - * - * Davide Libenzi - * - */ - -#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); - - - -#endif /* #if !defined(XUTILS_H) */ diff --git a/src/xdiff/COPYING b/src/xdiff/COPYING new file mode 100644 index 0000000000..f3f1b3b65e --- /dev/null +++ b/src/xdiff/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/xdiff/README.txt b/src/xdiff/README.txt new file mode 100644 index 0000000000..95b2242b87 --- /dev/null +++ b/src/xdiff/README.txt @@ -0,0 +1,16 @@ +The files in this directory come from the xdiff implementation in git. +You can find it here: https://github.com/git/git/tree/master/xdiff +The files were last updated August 31, 2021 from git release v.2.33.0 + +This is originally based on libxdiff, which can be found here: +http://www.xmailserver.org/xdiff-lib.html + +The git version was used because it has been maintained and improved. +And since it's part of git it is expected to be reliable. + +The code is distributed under the GNU LGPL license. It is included in the +COPYING file. + +Changes in these files were made to avoid compiler warnings. + +The first work for including xdiff in Vim was done by Christian Brabandt. diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h new file mode 100644 index 0000000000..49985a0e9a --- /dev/null +++ b/src/xdiff/xdiff.h @@ -0,0 +1,154 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +#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 + +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 */ + #if 0 // unused by Vim + regex_t **ignore_regex; + size_t ignore_regex_nr; +#endif + + /* 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; + +#include "../nvim/memory.h" + +#define xdl_malloc(x) xmalloc((x)) +#define xdl_free(ptr) xfree(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) */ diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c new file mode 100644 index 0000000000..cfcbb5d982 --- /dev/null +++ b/src/xdiff/xdiffi.c @@ -0,0 +1,1101 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +#include "xinclude.h" + +#define XDL_MAX_COST_MIN 256 +#define XDL_HEUR_MIN_COST 256 +#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1) +#define XDL_SNAKE_CNT 20 +#define XDL_K_HEUR 4 + +typedef struct s_xdpsplit { + long i1, i2; + int min_lo, min_hi; +} xdpsplit_t; + +/* + * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers. + * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both + * the forward diagonal starting from (off1, off2) and the backward diagonal + * starting from (lim1, lim2). If the K values on the same diagonal crosses + * returns the furthest point of reach. We might encounter expensive edge cases + * using this algorithm, so a little bit of heuristic is needed to cut the + * search and to return a suboptimal point. + */ +static long xdl_split(unsigned long const *ha1, long off1, long lim1, + unsigned long const *ha2, long off2, long lim2, + long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, + xdalgoenv_t *xenv) { + long dmin = off1 - lim2, dmax = lim1 - off2; + long fmid = off1 - off2, bmid = lim1 - lim2; + long odd = (fmid - bmid) & 1; + long fmin = fmid, fmax = fmid; + long bmin = bmid, bmax = bmid; + long ec, d, i1, i2, prev1, best, dd, v, k; + + /* + * Set initial diagonal values for both forward and backward path. + */ + kvdf[fmid] = off1; + kvdb[bmid] = lim1; + + for (ec = 1;; ec++) { + int got_snake = 0; + + /* + * We need to extend the diagonal "domain" by one. If the next + * values exits the box boundaries we need to change it in the + * opposite direction because (max - min) must be a power of + * two. + * + * Also we initialize the external K value to -1 so that we can + * avoid extra conditions in the check inside the core loop. + */ + if (fmin > dmin) + kvdf[--fmin - 1] = -1; + else + ++fmin; + if (fmax < dmax) + kvdf[++fmax + 1] = -1; + else + --fmax; + + for (d = fmax; d >= fmin; d -= 2) { + if (kvdf[d - 1] >= kvdf[d + 1]) + i1 = kvdf[d - 1] + 1; + else + i1 = kvdf[d + 1]; + prev1 = i1; + i2 = i1 - d; + for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++); + if (i1 - prev1 > xenv->snake_cnt) + got_snake = 1; + kvdf[d] = i1; + if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) { + spl->i1 = i1; + spl->i2 = i2; + spl->min_lo = spl->min_hi = 1; + return ec; + } + } + + /* + * We need to extend the diagonal "domain" by one. If the next + * values exits the box boundaries we need to change it in the + * opposite direction because (max - min) must be a power of + * two. + * + * Also we initialize the external K value to -1 so that we can + * avoid extra conditions in the check inside the core loop. + */ + if (bmin > dmin) + kvdb[--bmin - 1] = XDL_LINE_MAX; + else + ++bmin; + if (bmax < dmax) + kvdb[++bmax + 1] = XDL_LINE_MAX; + else + --bmax; + + for (d = bmax; d >= bmin; d -= 2) { + if (kvdb[d - 1] < kvdb[d + 1]) + i1 = kvdb[d - 1]; + else + i1 = kvdb[d + 1] - 1; + prev1 = i1; + i2 = i1 - d; + for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--); + if (prev1 - i1 > xenv->snake_cnt) + got_snake = 1; + kvdb[d] = i1; + if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) { + spl->i1 = i1; + spl->i2 = i2; + spl->min_lo = spl->min_hi = 1; + return ec; + } + } + + if (need_min) + continue; + + /* + * If the edit cost is above the heuristic trigger and if + * we got a good snake, we sample current diagonals to see + * if some of them have reached an "interesting" path. Our + * measure is a function of the distance from the diagonal + * corner (i1 + i2) penalized with the distance from the + * mid diagonal itself. If this value is above the current + * edit cost times a magic factor (XDL_K_HEUR) we consider + * it interesting. + */ + if (got_snake && ec > xenv->heur_min) { + for (best = 0, d = fmax; d >= fmin; d -= 2) { + dd = d > fmid ? d - fmid: fmid - d; + i1 = kvdf[d]; + i2 = i1 - d; + v = (i1 - off1) + (i2 - off2) - dd; + + if (v > XDL_K_HEUR * ec && v > best && + off1 + xenv->snake_cnt <= i1 && i1 < lim1 && + off2 + xenv->snake_cnt <= i2 && i2 < lim2) { + for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++) + if (k == xenv->snake_cnt) { + best = v; + spl->i1 = i1; + spl->i2 = i2; + break; + } + } + } + if (best > 0) { + spl->min_lo = 1; + spl->min_hi = 0; + return ec; + } + + for (best = 0, d = bmax; d >= bmin; d -= 2) { + dd = d > bmid ? d - bmid: bmid - d; + i1 = kvdb[d]; + i2 = i1 - d; + v = (lim1 - i1) + (lim2 - i2) - dd; + + if (v > XDL_K_HEUR * ec && v > best && + off1 < i1 && i1 <= lim1 - xenv->snake_cnt && + off2 < i2 && i2 <= lim2 - xenv->snake_cnt) { + for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++) + if (k == xenv->snake_cnt - 1) { + best = v; + spl->i1 = i1; + spl->i2 = i2; + break; + } + } + } + if (best > 0) { + spl->min_lo = 0; + spl->min_hi = 1; + return ec; + } + } + + /* + * Enough is enough. We spent too much time here and now we + * collect the furthest reaching path using the (i1 + i2) + * measure. + */ + if (ec >= xenv->mxcost) { + long fbest, fbest1, bbest, bbest1; + + fbest = fbest1 = -1; + for (d = fmax; d >= fmin; d -= 2) { + i1 = XDL_MIN(kvdf[d], lim1); + i2 = i1 - d; + if (lim2 < i2) + i1 = lim2 + d, i2 = lim2; + if (fbest < i1 + i2) { + fbest = i1 + i2; + fbest1 = i1; + } + } + + bbest = bbest1 = XDL_LINE_MAX; + for (d = bmax; d >= bmin; d -= 2) { + i1 = XDL_MAX(off1, kvdb[d]); + i2 = i1 - d; + if (i2 < off2) + i1 = off2 + d, i2 = off2; + if (i1 + i2 < bbest) { + bbest = i1 + i2; + bbest1 = i1; + } + } + + if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) { + spl->i1 = fbest1; + spl->i2 = fbest - fbest1; + spl->min_lo = 1; + spl->min_hi = 0; + } else { + spl->i1 = bbest1; + spl->i2 = bbest - bbest1; + spl->min_lo = 0; + spl->min_hi = 1; + } + return ec; + } + } +} + + +/* + * Rule: "Divide et Impera" (divide & conquer). Recursively split the box in + * sub-boxes by calling the box splitting function. Note that the real job + * (marking changed lines) is done in the two boundary reaching checks. + */ +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) { + unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha; + + /* + * Shrink the box by walking through each diagonal snake (SW and NE). + */ + for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++); + for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--); + + /* + * If one dimension is empty, then all records on the other one must + * be obviously changed. + */ + if (off1 == lim1) { + char *rchg2 = dd2->rchg; + long *rindex2 = dd2->rindex; + + for (; off2 < lim2; off2++) + rchg2[rindex2[off2]] = 1; + } else if (off2 == lim2) { + char *rchg1 = dd1->rchg; + long *rindex1 = dd1->rindex; + + for (; off1 < lim1; off1++) + rchg1[rindex1[off1]] = 1; + } else { + xdpsplit_t spl; + spl.i1 = spl.i2 = 0; + + /* + * Divide ... + */ + if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb, + need_min, &spl, xenv) < 0) { + + return -1; + } + + /* + * ... et Impera. + */ + if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2, + kvdf, kvdb, spl.min_lo, xenv) < 0 || + xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2, + kvdf, kvdb, spl.min_hi, xenv) < 0) { + + return -1; + } + } + + return 0; +} + + +int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *xe) { + long ndiags; + long *kvd, *kvdf, *kvdb; + xdalgoenv_t xenv; + diffdata_t dd1, dd2; + + if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF) + return xdl_do_patience_diff(mf1, mf2, xpp, xe); + + if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) + return xdl_do_histogram_diff(mf1, mf2, xpp, xe); + + if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) { + + return -1; + } + + /* + * Allocate and setup K vectors to be used by the differential + * algorithm. + * + * One is to store the forward path and one to store the backward path. + */ + ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; + if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) { + + xdl_free_env(xe); + return -1; + } + kvdf = kvd; + kvdb = kvdf + ndiags; + kvdf += xe->xdf2.nreff + 1; + kvdb += xe->xdf2.nreff + 1; + + xenv.mxcost = xdl_bogosqrt(ndiags); + if (xenv.mxcost < XDL_MAX_COST_MIN) + xenv.mxcost = XDL_MAX_COST_MIN; + xenv.snake_cnt = XDL_SNAKE_CNT; + xenv.heur_min = XDL_HEUR_MIN_COST; + + dd1.nrec = xe->xdf1.nreff; + dd1.ha = xe->xdf1.ha; + dd1.rchg = xe->xdf1.rchg; + dd1.rindex = xe->xdf1.rindex; + dd2.nrec = xe->xdf2.nreff; + dd2.ha = xe->xdf2.ha; + dd2.rchg = xe->xdf2.rchg; + dd2.rindex = xe->xdf2.rindex; + + if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec, + kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) { + + xdl_free(kvd); + xdl_free_env(xe); + return -1; + } + + xdl_free(kvd); + + return 0; +} + + +static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) { + xdchange_t *xch; + + if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t)))) + return NULL; + + xch->next = xscr; + xch->i1 = i1; + xch->i2 = i2; + xch->chg1 = chg1; + xch->chg2 = chg2; + xch->ignore = 0; + + return xch; +} + + +static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags) +{ + return (rec1->ha == rec2->ha && + xdl_recmatch(rec1->ptr, rec1->size, + rec2->ptr, rec2->size, + flags)); +} + +/* + * If a line is indented more than this, xget_indent() just returns this value. + * This avoids having to do absurd amounts of work for data that are not + * human-readable text, and also ensures that the output of xget_indent fits within + * an int. + */ +#define MAX_INDENT 200 + +/* + * Return the amount of indentation of the specified line, treating TAB as 8 + * columns. Return -1 if line is empty or contains only whitespace. Clamp the + * output value at MAX_INDENT. + */ +static int xget_indent(xrecord_t *rec) +{ + long i; + int ret = 0; + + for (i = 0; i < rec->size; i++) { + char c = rec->ptr[i]; + + if (!XDL_ISSPACE(c)) + return ret; + else if (c == ' ') + ret += 1; + else if (c == '\t') + ret += 8 - ret % 8; + /* ignore other whitespace characters */ + + if (ret >= MAX_INDENT) + return MAX_INDENT; + } + + /* The line contains only whitespace. */ + return -1; +} + +/* + * If more than this number of consecutive blank rows are found, just return + * this value. This avoids requiring O(N^2) work for pathological cases, and + * also ensures that the output of score_split fits in an int. + */ +#define MAX_BLANKS 20 + +/* Characteristics measured about a hypothetical split position. */ +struct split_measurement { + /* + * Is the split at the end of the file (aside from any blank lines)? + */ + int end_of_file; + + /* + * How much is the line immediately following the split indented (or -1 + * if the line is blank): + */ + int indent; + + /* + * How many consecutive lines above the split are blank? + */ + int pre_blank; + + /* + * How much is the nearest non-blank line above the split indented (or + * -1 if there is no such line)? + */ + int pre_indent; + + /* + * How many lines after the line following the split are blank? + */ + int post_blank; + + /* + * How much is the nearest non-blank line after the line following the + * split indented (or -1 if there is no such line)? + */ + int post_indent; +}; + +struct split_score { + /* The effective indent of this split (smaller is preferred). */ + int effective_indent; + + /* Penalty for this split (smaller is preferred). */ + int penalty; +}; + +/* + * Fill m with information about a hypothetical split of xdf above line split. + */ +static void measure_split(const xdfile_t *xdf, long split, + struct split_measurement *m) +{ + long i; + + if (split >= xdf->nrec) { + m->end_of_file = 1; + m->indent = -1; + } else { + m->end_of_file = 0; + m->indent = xget_indent(xdf->recs[split]); + } + + m->pre_blank = 0; + m->pre_indent = -1; + for (i = split - 1; i >= 0; i--) { + m->pre_indent = xget_indent(xdf->recs[i]); + if (m->pre_indent != -1) + break; + m->pre_blank += 1; + if (m->pre_blank == MAX_BLANKS) { + m->pre_indent = 0; + break; + } + } + + m->post_blank = 0; + m->post_indent = -1; + for (i = split + 1; i < xdf->nrec; i++) { + m->post_indent = xget_indent(xdf->recs[i]); + if (m->post_indent != -1) + break; + m->post_blank += 1; + if (m->post_blank == MAX_BLANKS) { + m->post_indent = 0; + break; + } + } +} + +/* + * The empirically-determined weight factors used by score_split() below. + * Larger values means that the position is a less favorable place to split. + * + * Note that scores are only ever compared against each other, so multiplying + * all of these weight/penalty values by the same factor wouldn't change the + * heuristic's behavior. Still, we need to set that arbitrary scale *somehow*. + * In practice, these numbers are chosen to be large enough that they can be + * adjusted relative to each other with sufficient precision despite using + * integer math. + */ + +/* Penalty if there are no non-blank lines before the split */ +#define START_OF_FILE_PENALTY 1 + +/* Penalty if there are no non-blank lines after the split */ +#define END_OF_FILE_PENALTY 21 + +/* Multiplier for the number of blank lines around the split */ +#define TOTAL_BLANK_WEIGHT (-30) + +/* Multiplier for the number of blank lines after the split */ +#define POST_BLANK_WEIGHT 6 + +/* + * Penalties applied if the line is indented more than its predecessor + */ +#define RELATIVE_INDENT_PENALTY (-4) +#define RELATIVE_INDENT_WITH_BLANK_PENALTY 10 + +/* + * Penalties applied if the line is indented less than both its predecessor and + * its successor + */ +#define RELATIVE_OUTDENT_PENALTY 24 +#define RELATIVE_OUTDENT_WITH_BLANK_PENALTY 17 + +/* + * Penalties applied if the line is indented less than its predecessor but not + * less than its successor + */ +#define RELATIVE_DEDENT_PENALTY 23 +#define RELATIVE_DEDENT_WITH_BLANK_PENALTY 17 + +/* + * We only consider whether the sum of the effective indents for splits are + * less than (-1), equal to (0), or greater than (+1) each other. The resulting + * value is multiplied by the following weight and combined with the penalty to + * determine the better of two scores. + */ +#define INDENT_WEIGHT 60 + +/* + * How far do we slide a hunk at most? + */ +#define INDENT_HEURISTIC_MAX_SLIDING 100 + +/* + * Compute a badness score for the hypothetical split whose measurements are + * stored in m. The weight factors were determined empirically using the tools + * and corpus described in + * + * https://github.com/mhagger/diff-slider-tools + * + * Also see that project if you want to improve the weights based on, for + * example, a larger or more diverse corpus. + */ +static void score_add_split(const struct split_measurement *m, struct split_score *s) +{ + /* + * A place to accumulate penalty factors (positive makes this index more + * favored): + */ + int post_blank, total_blank, indent, any_blanks; + + if (m->pre_indent == -1 && m->pre_blank == 0) + s->penalty += START_OF_FILE_PENALTY; + + if (m->end_of_file) + s->penalty += END_OF_FILE_PENALTY; + + /* + * Set post_blank to the number of blank lines following the split, + * including the line immediately after the split: + */ + post_blank = (m->indent == -1) ? 1 + m->post_blank : 0; + total_blank = m->pre_blank + post_blank; + + /* Penalties based on nearby blank lines: */ + s->penalty += TOTAL_BLANK_WEIGHT * total_blank; + s->penalty += POST_BLANK_WEIGHT * post_blank; + + if (m->indent != -1) + indent = m->indent; + else + indent = m->post_indent; + + any_blanks = (total_blank != 0); + + /* Note that the effective indent is -1 at the end of the file: */ + s->effective_indent += indent; + + if (indent == -1) { + /* No additional adjustments needed. */ + } else if (m->pre_indent == -1) { + /* No additional adjustments needed. */ + } else if (indent > m->pre_indent) { + /* + * The line is indented more than its predecessor. + */ + s->penalty += any_blanks ? + RELATIVE_INDENT_WITH_BLANK_PENALTY : + RELATIVE_INDENT_PENALTY; + } else if (indent == m->pre_indent) { + /* + * The line has the same indentation level as its predecessor. + * No additional adjustments needed. + */ + } else { + /* + * The line is indented less than its predecessor. It could be + * the block terminator of the previous block, but it could + * also be the start of a new block (e.g., an "else" block, or + * maybe the previous block didn't have a block terminator). + * Try to distinguish those cases based on what comes next: + */ + if (m->post_indent != -1 && m->post_indent > indent) { + /* + * The following line is indented more. So it is likely + * that this line is the start of a block. + */ + s->penalty += any_blanks ? + RELATIVE_OUTDENT_WITH_BLANK_PENALTY : + RELATIVE_OUTDENT_PENALTY; + } else { + /* + * That was probably the end of a block. + */ + s->penalty += any_blanks ? + RELATIVE_DEDENT_WITH_BLANK_PENALTY : + RELATIVE_DEDENT_PENALTY; + } + } +} + +static int score_cmp(struct split_score *s1, struct split_score *s2) +{ + /* -1 if s1.effective_indent < s2->effective_indent, etc. */ + int cmp_indents = ((s1->effective_indent > s2->effective_indent) - + (s1->effective_indent < s2->effective_indent)); + + return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty); +} + +/* + * Represent a group of changed lines in an xdfile_t (i.e., a contiguous group + * of lines that was inserted or deleted from the corresponding version of the + * file). We consider there to be such a group at the beginning of the file, at + * the end of the file, and between any two unchanged lines, though most such + * groups will usually be empty. + * + * If the first line in a group is equal to the line following the group, then + * the group can be slid down. Similarly, if the last line in a group is equal + * to the line preceding the group, then the group can be slid up. See + * group_slide_down() and group_slide_up(). + * + * Note that loops that are testing for changed lines in xdf->rchg do not need + * index bounding since the array is prepared with a zero at position -1 and N. + */ +struct xdlgroup { + /* + * The index of the first changed line in the group, or the index of + * the unchanged line above which the (empty) group is located. + */ + long start; + + /* + * The index of the first unchanged line after the group. For an empty + * group, end is equal to start. + */ + long end; +}; + +/* + * Initialize g to point at the first group in xdf. + */ +static void group_init(xdfile_t *xdf, struct xdlgroup *g) +{ + g->start = g->end = 0; + while (xdf->rchg[g->end]) + g->end++; +} + +/* + * Move g to describe the next (possibly empty) group in xdf and return 0. If g + * is already at the end of the file, do nothing and return -1. + */ +static inline int group_next(xdfile_t *xdf, struct xdlgroup *g) +{ + if (g->end == xdf->nrec) + return -1; + + g->start = g->end + 1; + for (g->end = g->start; xdf->rchg[g->end]; g->end++) + ; + + return 0; +} + +/* + * Move g to describe the previous (possibly empty) group in xdf and return 0. + * If g is already at the beginning of the file, do nothing and return -1. + */ +static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g) +{ + if (g->start == 0) + return -1; + + g->end = g->start - 1; + for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--) + ; + + return 0; +} + +/* + * If g can be slid toward the end of the file, do so, and if it bumps into a + * following group, expand this group to include it. Return 0 on success or -1 + * if g cannot be slid down. + */ +static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags) +{ + if (g->end < xdf->nrec && + recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) { + xdf->rchg[g->start++] = 0; + xdf->rchg[g->end++] = 1; + + while (xdf->rchg[g->end]) + g->end++; + + return 0; + } else { + return -1; + } +} + +/* + * If g can be slid toward the beginning of the file, do so, and if it bumps + * into a previous group, expand this group to include it. Return 0 on success + * or -1 if g cannot be slid up. + */ +static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags) +{ + if (g->start > 0 && + recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) { + xdf->rchg[--g->start] = 1; + xdf->rchg[--g->end] = 0; + + while (xdf->rchg[g->start - 1]) + g->start--; + + return 0; + } else { + return -1; + } +} + +static void xdl_bug(const char *msg) +{ + fprintf(stderr, "BUG: %s\n", msg); + exit(1); +} + +/* + * Move back and forward change groups for a consistent and pretty diff output. + * This also helps in finding joinable change groups and reducing the diff + * size. + */ +int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { + struct xdlgroup g, go; + long earliest_end, end_matching_other; + long groupsize; + + group_init(xdf, &g); + group_init(xdfo, &go); + + while (1) { + /* + * If the group is empty in the to-be-compacted file, skip it: + */ + if (g.end == g.start) + goto next; + + /* + * Now shift the change up and then down as far as possible in + * each direction. If it bumps into any other changes, merge + * them. + */ + do { + groupsize = g.end - g.start; + + /* + * Keep track of the last "end" index that causes this + * group to align with a group of changed lines in the + * other file. -1 indicates that we haven't found such + * a match yet: + */ + end_matching_other = -1; + + /* Shift the group backward as much as possible: */ + while (!group_slide_up(xdf, &g, flags)) + if (group_previous(xdfo, &go)) + xdl_bug("group sync broken sliding up"); + + /* + * This is this highest that this group can be shifted. + * Record its end index: + */ + earliest_end = g.end; + + if (go.end > go.start) + end_matching_other = g.end; + + /* Now shift the group forward as far as possible: */ + while (1) { + if (group_slide_down(xdf, &g, flags)) + break; + if (group_next(xdfo, &go)) + xdl_bug("group sync broken sliding down"); + + if (go.end > go.start) + end_matching_other = g.end; + } + } while (groupsize != g.end - g.start); + + /* + * If the group can be shifted, then we can possibly use this + * freedom to produce a more intuitive diff. + * + * The group is currently shifted as far down as possible, so + * the heuristics below only have to handle upwards shifts. + */ + + if (g.end == earliest_end) { + /* no shifting was possible */ + } else if (end_matching_other != -1) { + /* + * Move the possibly merged group of changes back to + * line up with the last group of changes from the + * other file that it can align with. + */ + while (go.end == go.start) { + if (group_slide_up(xdf, &g, flags)) + xdl_bug("match disappeared"); + if (group_previous(xdfo, &go)) + xdl_bug("group sync broken sliding to match"); + } + } else if (flags & XDF_INDENT_HEURISTIC) { + /* + * Indent heuristic: a group of pure add/delete lines + * implies two splits, one between the end of the + * "before" context and the start of the group, and + * another between the end of the group and the + * beginning of the "after" context. Some splits are + * aesthetically better and some are worse. We compute + * a badness "score" for each split, and add the scores + * for the two splits to define a "score" for each + * position that the group can be shifted to. Then we + * pick the shift with the lowest score. + */ + long shift, best_shift = -1; + struct split_score best_score; + + shift = earliest_end; + if (g.end - groupsize - 1 > shift) + shift = g.end - groupsize - 1; + if (g.end - INDENT_HEURISTIC_MAX_SLIDING > shift) + shift = g.end - INDENT_HEURISTIC_MAX_SLIDING; + for (; shift <= g.end; shift++) { + struct split_measurement m; + struct split_score score = {0, 0}; + + measure_split(xdf, shift, &m); + score_add_split(&m, &score); + measure_split(xdf, shift - groupsize, &m); + score_add_split(&m, &score); + if (best_shift == -1 || + score_cmp(&score, &best_score) <= 0) { + best_score.effective_indent = score.effective_indent; + best_score.penalty = score.penalty; + best_shift = shift; + } + } + + while (g.end > best_shift) { + if (group_slide_up(xdf, &g, flags)) + xdl_bug("best shift unreached"); + if (group_previous(xdfo, &go)) + xdl_bug("group sync broken sliding to blank line"); + } + } + + next: + /* Move past the just-processed group: */ + if (group_next(xdf, &g)) + break; + if (group_next(xdfo, &go)) + xdl_bug("group sync broken moving to next group"); + } + + if (!group_next(xdfo, &go)) + xdl_bug("group sync broken at end of file"); + + return 0; +} + + +int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) { + xdchange_t *cscr = NULL, *xch; + char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg; + long i1, i2, l1, l2; + + /* + * Trivial. Collects "groups" of changes and creates an edit script. + */ + for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--) + if (rchg1[i1 - 1] || rchg2[i2 - 1]) { + for (l1 = i1; rchg1[i1 - 1]; i1--); + for (l2 = i2; rchg2[i2 - 1]; i2--); + + if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) { + xdl_free_script(cscr); + return -1; + } + cscr = xch; + } + + *xscr = cscr; + + return 0; +} + + +void xdl_free_script(xdchange_t *xscr) { + xdchange_t *xch; + + while ((xch = xscr) != NULL) { + xscr = xscr->next; + xdl_free(xch); + } +} + +static int xdl_call_hunk_func(xdfenv_t *xe UNUSED, xdchange_t *xscr, xdemitcb_t *ecb, + xdemitconf_t const *xecfg) +{ + xdchange_t *xch, *xche; + + for (xch = xscr; xch; xch = xche->next) { + xche = xdl_get_hunk(&xch, xecfg); + if (!xch) + break; + if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1, + xch->i2, xche->i2 + xche->chg2 - xch->i2, + ecb->priv) < 0) + return -1; + } + return 0; +} + +static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags) +{ + xdchange_t *xch; + + for (xch = xscr; xch; xch = xch->next) { + int ignore = 1; + xrecord_t **rec; + long i; + + rec = &xe->xdf1.recs[xch->i1]; + for (i = 0; i < xch->chg1 && ignore; i++) + ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); + + rec = &xe->xdf2.recs[xch->i2]; + for (i = 0; i < xch->chg2 && ignore; i++) + ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); + + xch->ignore = ignore; + } +} + +#if 0 // unused by Vim +static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) { + regmatch_t regmatch; + int i; + + for (i = 0; i < xpp->ignore_regex_nr; i++) + if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1, + ®match, 0)) + return 1; + + return 0; +} + +static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe, + xpparam_t const *xpp) +{ + xdchange_t *xch; + + for (xch = xscr; xch; xch = xch->next) { + xrecord_t **rec; + int ignore = 1; + long i; + + /* + * Do not override --ignore-blank-lines. + */ + if (xch->ignore) + continue; + + rec = &xe->xdf1.recs[xch->i1]; + for (i = 0; i < xch->chg1 && ignore; i++) + ignore = record_matches_regex(rec[i], xpp); + + rec = &xe->xdf2.recs[xch->i2]; + for (i = 0; i < xch->chg2 && ignore; i++) + ignore = record_matches_regex(rec[i], xpp); + + xch->ignore = ignore; + } +} +#endif + +int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdemitconf_t const *xecfg, xdemitcb_t *ecb) { + xdchange_t *xscr; + xdfenv_t xe; + emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff; + + if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) { + + return -1; + } + if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 || + xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 || + xdl_build_script(&xe, &xscr) < 0) { + + xdl_free_env(&xe); + return -1; + } + if (xscr) { + if (xpp->flags & XDF_IGNORE_BLANK_LINES) + xdl_mark_ignorable_lines(xscr, &xe, xpp->flags); + +#if 0 + if (xpp->ignore_regex) + xdl_mark_ignorable_regex(xscr, &xe, xpp); +#endif + + if (ef(&xe, xscr, ecb, xecfg) < 0) { + + xdl_free_script(xscr); + xdl_free_env(&xe); + return -1; + } + xdl_free_script(xscr); + } + xdl_free_env(&xe); + + return 0; +} diff --git a/src/xdiff/xdiffi.h b/src/xdiff/xdiffi.h new file mode 100644 index 0000000000..8f1c7c8b04 --- /dev/null +++ b/src/xdiff/xdiffi.h @@ -0,0 +1,64 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +#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(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *env); +int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *env); + +#endif /* #if !defined(XDIFFI_H) */ diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c new file mode 100644 index 0000000000..a0078f928c --- /dev/null +++ b/src/xdiff/xemit.c @@ -0,0 +1,348 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +#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 == NULL) + 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 + (long)ignored - (lxch->i1 + lxch->chg1) > max_common) { + break; + } else if (!xch->ignore) { + lxch = xch; + ignored = 0; + } else { + ignored += xch->chg2; + } + } + + return lxch; +} + + +#if 0 +static long def_ff(const char *rec, long len, char *buf, long sz, void *priv UNUSED) +{ + 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; +} +#endif + +#if 0 +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, xecfg->find_func_priv); + return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv); +} +#endif + +#if 0 +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; +} +#endif + +struct func_line { + long len; + char buf[80]; +}; + +#if 0 +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; +} +#endif + +#if 0 +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; +} +#endif + +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; +#if 0 + long funclineprev = -1; +#endif + struct func_line func_line; + + func_line.len = 0; + + for (xch = xscr; xch; xch = xche->next) { + xche = xdl_get_hunk(&xch, xecfg); + if (!xch) + break; + + s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); + s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0); + +#if 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: +#endif + 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 0 + 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; + } + } + } +#endif + + /* + * Emit current hunk header. + */ + +#if 0 + if (xecfg->flags & XDL_EMIT_FUNCNAMES) { + get_func_line(xe, xecfg, &func_line, + s1 - 1, funclineprev); + funclineprev = s1 - 1; + } +#endif + 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; +} diff --git a/src/xdiff/xemit.h b/src/xdiff/xemit.h new file mode 100644 index 0000000000..1b9887e670 --- /dev/null +++ b/src/xdiff/xemit.h @@ -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 + * . + * + * Davide Libenzi + * + */ + +#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) */ diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c new file mode 100644 index 0000000000..8598a8550d --- /dev/null +++ b/src/xdiff/xhistogram.c @@ -0,0 +1,386 @@ +/* + * 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 INT_MAX +#define MAX_CNT INT_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(xpparam_t const *xpp, + xrecord_t *r1, xrecord_t *r2) +{ + return r1->ha == r2->ha && + xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size, + xpp->flags); +} + +#define CMP_ENV(xpp, env, s1, l1, s2, l2) \ + (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2))) + +#define CMP(i, s1, l1, s2, l2) \ + (cmp_recs(i->xpp, 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) +{ + 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 < (int)as && line2 < (int)bs + && CMP(index, 1, as - 1, 2, bs - 1)) { + as--; + bs--; + if (1 < rc) + rc = XDL_MIN(rc, CNT(index, as)); + } + while ((int)ae < LINE_END(1) && (int)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 sz, 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); + sz = index.records_size = 1 << index.table_bits; + sz *= sizeof(struct record *); + if (!(index.records = (struct record **) xdl_malloc(sz))) + goto cleanup; + memset(index.records, 0, sz); + + sz = index.line_map_size = count1; + sz *= sizeof(struct record *); + if (!(index.line_map = (struct record **) xdl_malloc(sz))) + goto cleanup; + memset(index.line_map, 0, sz); + + sz = index.line_map_size; + sz *= sizeof(unsigned int); + if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz))) + goto cleanup; + memset(index.next_ptrs, 0, sz); + + /* 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(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env) +{ + if (xdl_prepare_env(file1, file2, xpp, env) < 0) + return -1; + + 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); +} diff --git a/src/xdiff/xinclude.h b/src/xdiff/xinclude.h new file mode 100644 index 0000000000..a412404bfe --- /dev/null +++ b/src/xdiff/xinclude.h @@ -0,0 +1,65 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +// defines HAVE_ATTRIBUTE_UNUSED +#ifdef HAVE_CONFIG_H +# include "../auto/config.h" +#endif + +// Mark unused function arguments with UNUSED, so that gcc -Wunused-parameter +// can be used to check for mistakes. +#ifdef HAVE_ATTRIBUTE_UNUSED +# define UNUSED __attribute__((unused)) +#else +# define UNUSED +#endif + +#if defined(_MSC_VER) +# define inline __inline +#endif + +#if !defined(XINCLUDE_H) +#define XINCLUDE_H + +// This effectively re-verts b46054b3746271d23feab0 from git +#include +#include +#include +#if !defined(_WIN32) +#include +#endif +#include +#include +// This include comes from git, so uncomment it +#if 0 +#include "git-compat-util.h" +#endif +#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) */ diff --git a/src/xdiff/xmacros.h b/src/xdiff/xmacros.h new file mode 100644 index 0000000000..2809a28ca9 --- /dev/null +++ b/src/xdiff/xmacros.h @@ -0,0 +1,54 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +#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_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0) +#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) + + +#endif /* #if !defined(XMACROS_H) */ diff --git a/src/xdiff/xpatience.c b/src/xdiff/xpatience.c new file mode 100644 index 0000000000..f78c897ad8 --- /dev/null +++ b/src/xdiff/xpatience.c @@ -0,0 +1,386 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ +#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; + mmfile_t *file1, *file2; + xdfenv_t *env; + xpparam_t const *xpp; +}; + +static int is_anchor(xpparam_t const *xpp, const char *line) +{ + int i; + for (i = 0; i < (int)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(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env, + struct hashmap *result, + int line1, int count1, int line2, int count2) +{ + result->file1 = file1; + result->file2 = file2; + result->xpp = xpp; + result->env = env; + + /* We know exactly how large we want the hash map */ + result->alloc = count1 * 2; + result->entries = (struct entry *) + xdl_malloc(result->alloc * sizeof(struct entry)); + if (!result->entries) + return -1; + memset(result->entries, 0, result->alloc * sizeof(struct entry)); + + /* 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 struct entry *find_longest_common_sequence(struct hashmap *map) +{ + struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *)); + 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; + + // Added to silence Coverity. + if (sequence == NULL) + return map->first; + + 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) { + xdl_free(sequence); + return NULL; + } + + /* 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; + } + xdl_free(sequence); + return entry; +} + +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(mmfile_t *file1, mmfile_t *file2, + 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->file1, map->file2, + 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(mmfile_t *file1, mmfile_t *file2, + 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(file1, file2, 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; + } + + first = find_longest_common_sequence(&map); + if (first) + result = walk_common_sequence(&map, first, + line1, count1, line2, count2); + else + result = fall_back_to_classic_diff(&map, + line1, count1, line2, count2); + + xdl_free(map.entries); + return result; +} + +int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env) +{ + if (xdl_prepare_env(file1, file2, xpp, env) < 0) + return -1; + + /* environment is cleaned up in xdl_diff() */ + return patience_diff(file1, file2, xpp, env, + 1, env->xdf1.nrec, 1, env->xdf2.nrec); +} diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c new file mode 100644 index 0000000000..abeb8fb84e --- /dev/null +++ b/src/xdiff/xprepare.c @@ -0,0 +1,483 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +#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 (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) { + + xdl_cha_free(&cf->ncha); + return -1; + } + memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *)); + + cf->alloc = size; + if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) { + + 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; + xdlclass_t **rcrecs; + + 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 (cf->count > cf->alloc) { + cf->alloc *= 2; + if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) { + + return -1; + } + cf->rcrecs = rcrecs; + } + 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, **rrecs; + 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 (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) + goto abort; + + if (XDF_DIFF_ALG(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 *)); + } + + nrec = 0; + if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) { + for (top = blk + bsize; cur < top; ) { + prev = cur; + hav = xdl_hash_record(&cur, top, xpp->flags); + if (nrec >= narec) { + narec *= 2; + if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) + goto abort; + recs = rrecs; + } + if (!(crec = xdl_cha_alloc(&xdf->rcha))) + goto abort; + crec->ptr = prev; + crec->size = (long) (cur - prev); + crec->ha = hav; + recs[nrec++] = crec; + + if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && + xdl_classify_record(pass, cf, rhash, hbits, crec) < 0) + goto abort; + } + } + + 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)))) + goto abort; + if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) + 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 (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF && + 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; + } + + if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) + 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 (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) { + + return -1; + } + 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++) { + 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; +} diff --git a/src/xdiff/xprepare.h b/src/xdiff/xprepare.h new file mode 100644 index 0000000000..947d9fc1bb --- /dev/null +++ b/src/xdiff/xprepare.h @@ -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 + * . + * + * Davide Libenzi + * + */ + +#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) */ diff --git a/src/xdiff/xtypes.h b/src/xdiff/xtypes.h new file mode 100644 index 0000000000..8442bd436e --- /dev/null +++ b/src/xdiff/xtypes.h @@ -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 + * . + * + * Davide Libenzi + * + */ + +#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) */ diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c new file mode 100644 index 0000000000..5ef519155d --- /dev/null +++ b/src/xdiff/xutils.c @@ -0,0 +1,434 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +#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)) != NULL) { + 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 > (long)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; +} diff --git a/src/xdiff/xutils.h b/src/xdiff/xutils.h new file mode 100644 index 0000000000..fba7bae03c --- /dev/null +++ b/src/xdiff/xutils.h @@ -0,0 +1,47 @@ +/* + * 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 + * . + * + * Davide Libenzi + * + */ + +#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); + + + +#endif /* #if !defined(XUTILS_H) */ -- cgit