#include "cache.h" #include "diff.h" #include "diffcore.h" struct linehash { unsigned long bytes; unsigned long hash; }; static unsigned long hash_extended_line(const unsigned char **buf_p, unsigned long left) { /* An extended line is zero or more whitespace letters (including LF) * followed by one non whitespace letter followed by zero or more * non LF, and terminated with by a LF (or EOF). */ const unsigned char *bol = *buf_p; const unsigned char *buf = bol; unsigned long hashval = 0; while (left) { unsigned c = *buf++; if (!c) goto binary; left--; if (' ' < c) { hashval = c; break; } } while (left) { unsigned c = *buf++; if (!c) goto binary; left--; if (c == '\n') break; if (' ' < c) hashval = hashval * 11 + c; } *buf_p = buf; return hashval; binary: *buf_p = NULL; return 0; } static int linehash_compare(const void *a_, const void *b_) { struct linehash *a = (struct linehash *) a_; struct linehash *b = (struct linehash *) b_; if (a->hash < b->hash) return -1; if (a->hash > b->hash) return 1; return 0; } static struct linehash *hash_lines(const unsigned char *buf, unsigned long size) { const unsigned char *eobuf = buf + size; struct linehash *line = NULL; int alloc = 0, used = 0; while (buf < eobuf) { const unsigned char *ptr = buf; unsigned long hash = hash_extended_line(&buf, eobuf-ptr); if (!buf) { free(line); return NULL; } if (alloc <= used) { alloc = alloc_nr(alloc); line = xrealloc(line, sizeof(*line) * alloc); } line[used].bytes = buf - ptr; line[used].hash = hash; used++; } qsort(line, used, sizeof(*line), linehash_compare); /* Terminate the list */ if (alloc <= used) line = xrealloc(line, sizeof(*line) * (used+1)); line[used].bytes = line[used].hash = 0; return line; } int diffcore_count_changes(void *src, unsigned long src_size, void *dst, unsigned long dst_size, unsigned long delta_limit, unsigned long *src_copied, unsigned long *literal_added) { struct linehash *src_lines, *dst_lines; unsigned long sc, la; src_lines = hash_lines(src, src_size); if (!src_lines) return -1; dst_lines = hash_lines(dst, dst_size); if (!dst_lines) { free(src_lines); return -1; } sc = la = 0; while (src_lines->bytes && dst_lines->bytes) { int cmp = linehash_compare(src_lines, dst_lines); if (!cmp) { sc += src_lines->bytes; src_lines++; dst_lines++; continue; } if (cmp < 0) { src_lines++; continue; } la += dst_lines->bytes; dst_lines++; } while (dst_lines->bytes) { la += dst_lines->bytes; dst_lines++; } *src_copied = sc; *literal_added = la; return 0; }