X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=diff-delta.c;h=2ed5984b1c360194d53fd287962d7857e19a097c;hb=00333ca35c5986e44d4cd4fd59433200d916f77e;hp=0730b24df8fe679d16e1cd03d066b4d7f33e73cd;hpb=1025fe51bf540bc4c63bce2553a43b1c753e1c40;p=git.git diff --git a/diff-delta.c b/diff-delta.c index 0730b24d..2ed5984b 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -20,93 +20,63 @@ #include #include +#include #include "delta.h" +/* block size: min = 16, max = 64k, power of 2 */ +#define BLK_SIZE 16 + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define GR_PRIME 0x9e370001 +#define HASH(v, shift) (((unsigned int)(v) * GR_PRIME) >> (shift)) + struct index { const unsigned char *ptr; + unsigned int val; struct index *next; }; static struct index ** delta_index(const unsigned char *buf, unsigned long bufsize, - unsigned long trg_bufsize, unsigned int *hash_shift) { - unsigned long hsize; - unsigned int i, hshift, hlimit, *hash_count; + unsigned int hsize, hshift, entries, blksize, i; const unsigned char *data; struct index *entry, **hash; void *mem; /* determine index hash size */ - hsize = bufsize / 4; - for (i = 8; (1 << i) < hsize && i < 24; i += 2); + entries = (bufsize + BLK_SIZE - 1) / BLK_SIZE; + hsize = entries / 4; + for (i = 4; (1 << i) < hsize && i < 16; i++); hsize = 1 << i; - hshift = (i - 8) / 2; + hshift = 32 - i; *hash_shift = hshift; /* allocate lookup index */ - mem = malloc(hsize * sizeof(*hash) + bufsize * sizeof(*entry)); + mem = malloc(hsize * sizeof(*hash) + entries * sizeof(*entry)); if (!mem) return NULL; hash = mem; entry = mem + hsize * sizeof(*hash); memset(hash, 0, hsize * sizeof(*hash)); - /* allocate an array to count hash entries */ - hash_count = calloc(hsize, sizeof(*hash_count)); - if (!hash_count) { - free(hash); - return NULL; - } - - /* then populate the index */ - data = buf + bufsize - 2; - while (data > buf) { - entry->ptr = --data; - i = data[0] ^ ((data[1] ^ (data[2] << hshift)) << hshift); + /* then populate it */ + data = buf + entries * BLK_SIZE - BLK_SIZE; + blksize = bufsize - (data - buf); + while (data >= buf) { + unsigned int val = adler32(0, data, blksize); + i = HASH(val, hshift); + entry->ptr = data; + entry->val = val; entry->next = hash[i]; hash[i] = entry++; - hash_count[i]++; + blksize = BLK_SIZE; + data -= BLK_SIZE; } - /* - * Determine a limit on the number of entries in the same hash - * bucket. This guard us against patological data sets causing - * really bad hash distribution with most entries in the same hash - * bucket that would bring us to O(m*n) computing costs (m and n - * corresponding to reference and target buffer sizes). - * - * The more the target buffer is large, the more it is important to - * have small entry lists for each hash buckets. With such a limit - * the cost is bounded to something more like O(m+n). - */ - hlimit = (1 << 26) / trg_bufsize; - if (hlimit < 16) - hlimit = 16; - - /* - * Now make sure none of the hash buckets has more entries than - * we're willing to test. Otherwise we short-circuit the entry - * list uniformly to still preserve a good repartition across - * the reference buffer. - */ - for (i = 0; i < hsize; i++) { - if (hash_count[i] < hlimit) - continue; - entry = hash[i]; - do { - struct index *keep = entry; - int skip = hash_count[i] / hlimit / 2; - do { - entry = entry->next; - } while(--skip && entry); - keep->next = entry; - } while(entry); - } - free(hash_count); - return hash; } @@ -130,7 +100,7 @@ void *diff_delta(void *from_buf, unsigned long from_size, if (!from_size || !to_size) return NULL; - hash = delta_index(from_buf, from_size, to_size, &hash_shift); + hash = delta_index(from_buf, from_size, &hash_shift); if (!hash) return NULL; @@ -171,25 +141,29 @@ void *diff_delta(void *from_buf, unsigned long from_size, while (data < top) { unsigned int moff = 0, msize = 0; - if (data + 3 <= top) { - i = data[0] ^ ((data[1] ^ (data[2] << hash_shift)) << hash_shift); - for (entry = hash[i]; entry; entry = entry->next) { - const unsigned char *ref = entry->ptr; - const unsigned char *src = data; - unsigned int ref_size = ref_top - ref; - if (ref_size > top - src) - ref_size = top - src; - if (ref_size > 0x10000) - ref_size = 0x10000; - if (ref_size <= msize) + unsigned int blksize = MIN(top - data, BLK_SIZE); + unsigned int val = adler32(0, data, blksize); + i = HASH(val, hash_shift); + for (entry = hash[i]; entry; entry = entry->next) { + const unsigned char *ref = entry->ptr; + const unsigned char *src = data; + unsigned int ref_size = ref_top - ref; + if (entry->val != val) + continue; + if (ref_size > top - src) + ref_size = top - src; + while (ref_size && *src++ == *ref) { + ref++; + ref_size--; + } + ref_size = ref - entry->ptr; + if (ref_size > msize) { + /* this is our best match so far */ + moff = entry->ptr - ref_data; + msize = ref_size; + if (msize >= 0x10000) { + msize = 0x10000; break; - if (*ref != *src) - continue; - while (ref_size-- && *++src == *++ref); - if (msize < ref - entry->ptr) { - /* this is our best match so far */ - msize = ref - entry->ptr; - moff = entry->ptr - ref_data; } } }