/* * 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(); }