/* * Returns true when moving line 'from' to line 'to' seems to be cost * effective. 'blank' indicates whether the line 'to' would become blank. */ static inline bool cost_effective(const int from, const int to, const bool blank) { int new_from; if (from == to) return FALSE; new_from = OLDNUM(from); if (new_from == _NEWINDEX) new_from = from; /* * On the left side of >= is the cost before moving; * on the right side -- cost after moving. */ return (((blank ? update_cost_from_blank(NEWTEXT(to)) : update_cost(OLDTEXT(to),NEWTEXT(to))) + update_cost(OLDTEXT(new_from),NEWTEXT(from))) >= ((new_from==from ? update_cost_from_blank(NEWTEXT(from)) : update_cost(OLDTEXT(new_from),NEWTEXT(from))) + update_cost(OLDTEXT(from),NEWTEXT(to)))) ? TRUE : FALSE; }
_nc_hash_map(void) { HASHMAP *sp; register int i; int start, shift, size; if (screen_lines > lines_alloc) { if (hashtab) free(hashtab); hashtab = typeMalloc(HASHMAP, (screen_lines + 1) * 2); if (!hashtab) { if (oldhash) { FreeAndNull(oldhash); } lines_alloc = 0; return; } lines_alloc = screen_lines; } if (oldhash && newhash) { /* re-hash only changed lines */ for (i = 0; i < screen_lines; i++) { if (PENDING(i)) newhash[i] = hash(NEWTEXT(i)); } } else { /* re-hash all */ if (oldhash == 0) oldhash = typeCalloc(unsigned long, (unsigned) screen_lines); if (newhash == 0) newhash = typeCalloc(unsigned long, (unsigned) screen_lines); if (!oldhash || !newhash) return; /* malloc failure */ for (i = 0; i < screen_lines; i++) { newhash[i] = hash(NEWTEXT(i)); oldhash[i] = hash(OLDTEXT(i)); } } #ifdef HASH_VERIFY for (i = 0; i < screen_lines; i++) { if (newhash[i] != hash(NEWTEXT(i))) fprintf(stderr, "error in newhash[%d]\n", i); if (oldhash[i] != hash(OLDTEXT(i))) fprintf(stderr, "error in oldhash[%d]\n", i); } #endif /* * Set up and count line-hash values. */ memset(hashtab, '\0', sizeof(*hashtab) * (screen_lines + 1) * 2); for (i = 0; i < screen_lines; i++) { unsigned long hashval = oldhash[i]; for (sp = hashtab; sp->hashval; sp++) if (sp->hashval == hashval) break; sp->hashval = hashval; /* in case this is a new entry */ sp->oldcount++; sp->oldindex = i; } for (i = 0; i < screen_lines; i++) { unsigned long hashval = newhash[i]; for (sp = hashtab; sp->hashval; sp++) if (sp->hashval == hashval) break; sp->hashval = hashval; /* in case this is a new entry */ sp->newcount++; sp->newindex = i; OLDNUM(i) = _NEWINDEX; /* initialize old indices array */ } /* * Mark line pairs corresponding to unique hash pairs. * * We don't mark lines with offset 0, because it can make fail * extending hunks by cost_effective. Otherwise, it does not * have any side effects. */ for (sp = hashtab; sp->hashval; sp++) if (sp->oldcount == 1 && sp->newcount == 1 && sp->oldindex != sp->newindex) { TR(TRACE_UPDATE | TRACE_MOVE, ("new line %d is hash-identical to old line %d (unique)", sp->newindex, sp->oldindex)); OLDNUM(sp->newindex) = sp->oldindex; } grow_hunks(); /* * Eliminate bad or impossible shifts -- this includes removing * those hunks which could not grow because of conflicts, as well * those which are to be moved too far, they are likely to destroy * more than carry. */ for (i = 0; i < screen_lines;) { while (i < screen_lines && OLDNUM(i) == _NEWINDEX) i++; if (i >= screen_lines) break; start = i; shift = OLDNUM(i) - i; i++; while (i < screen_lines && OLDNUM(i) != _NEWINDEX && OLDNUM(i) - i == shift) i++; size = i - start; if (size < 3 || size + min(size / 8, 2) < abs(shift)) { while (start < i) { OLDNUM(start) = _NEWINDEX; start++; } } } /* After clearing invalid hunks, try grow the rest. */ grow_hunks(); }
static void grow_hunks(void) { int start, end, shift; int back_limit, forward_limit; /* limits for cells to fill */ int back_ref_limit, forward_ref_limit; /* limits for refrences */ int i; int next_hunk; /* * This is tricky part. We have unique pairs to use as anchors. * Use these to deduce the presence of spans of identical lines. */ back_limit = 0; back_ref_limit = 0; i = 0; while (i < screen_lines && OLDNUM(i) == _NEWINDEX) i++; for (; i < screen_lines; i = next_hunk) { start = i; shift = OLDNUM(i) - i; /* get forward limit */ i = start + 1; while (i < screen_lines && OLDNUM(i) != _NEWINDEX && OLDNUM(i) - i == shift) i++; end = i; while (i < screen_lines && OLDNUM(i) == _NEWINDEX) i++; next_hunk = i; forward_limit = i; if (i >= screen_lines || OLDNUM(i) >= i) forward_ref_limit = i; else forward_ref_limit = OLDNUM(i); i = start - 1; /* grow back */ if (shift < 0) back_limit = back_ref_limit + (-shift); while (i >= back_limit) { if (newhash[i] == oldhash[i + shift] || cost_effective(i + shift, i, shift < 0)) { OLDNUM(i) = i + shift; TR(TRACE_UPDATE | TRACE_MOVE, ("connected new line %d to old line %d (backward continuation)", i, i + shift)); } else { TR(TRACE_UPDATE | TRACE_MOVE, ("not connecting new line %d to old line %d (backward continuation)", i, i + shift)); break; } i--; } i = end; /* grow forward */ if (shift > 0) forward_limit = forward_ref_limit - shift; while (i < forward_limit) { if (newhash[i] == oldhash[i + shift] || cost_effective(i + shift, i, shift > 0)) { OLDNUM(i) = i + shift; TR(TRACE_UPDATE | TRACE_MOVE, ("connected new line %d to old line %d (forward continuation)", i, i + shift)); } else { TR(TRACE_UPDATE | TRACE_MOVE, ("not connecting new line %d to old line %d (forward continuation)", i, i + shift)); break; } i++; } back_ref_limit = back_limit = i; if (shift > 0) back_ref_limit += shift; } }