diffcore-delta.con commit diffcore-delta.c: update the comment on the algorithm. (af3abef)
   1#include "cache.h"
   2#include "diff.h"
   3#include "diffcore.h"
   4
   5/*
   6 * Idea here is very simple.
   7 *
   8 * Almost all data we are interested in are text, but sometimes we have
   9 * to deal with binary data.  So we cut them into chunks delimited by
  10 * LF byte, or 64-byte sequence, whichever comes first, and hash them.
  11 *
  12 * For those chunks, if the source buffer has more instances of it
  13 * than the destination buffer, that means the difference are the
  14 * number of bytes not copied from source to destination.  If the
  15 * counts are the same, everything was copied from source to
  16 * destination.  If the destination has more, everything was copied,
  17 * and destination added more.
  18 *
  19 * We are doing an approximation so we do not really have to waste
  20 * memory by actually storing the sequence.  We just hash them into
  21 * somewhere around 2^16 hashbuckets and count the occurrences.
  22 */
  23
  24/* Wild guess at the initial hash size */
  25#define INITIAL_HASH_SIZE 9
  26
  27/* We leave more room in smaller hash but do not let it
  28 * grow to have unused hole too much.
  29 */
  30#define INITIAL_FREE(sz_log2) ((1<<(sz_log2))*(sz_log2-3)/(sz_log2))
  31
  32/* A prime rather carefully chosen between 2^16..2^17, so that
  33 * HASHBASE < INITIAL_FREE(17).  We want to keep the maximum hashtable
  34 * size under the current 2<<17 maximum, which can hold this many
  35 * different values before overflowing to hashtable of size 2<<18.
  36 */
  37#define HASHBASE 107927
  38
  39struct spanhash {
  40        unsigned int hashval;
  41        unsigned int cnt;
  42};
  43struct spanhash_top {
  44        int alloc_log2;
  45        int free;
  46        struct spanhash data[FLEX_ARRAY];
  47};
  48
  49static struct spanhash *spanhash_find(struct spanhash_top *top,
  50                                      unsigned int hashval)
  51{
  52        int sz = 1 << top->alloc_log2;
  53        int bucket = hashval & (sz - 1);
  54        while (1) {
  55                struct spanhash *h = &(top->data[bucket++]);
  56                if (!h->cnt)
  57                        return NULL;
  58                if (h->hashval == hashval)
  59                        return h;
  60                if (sz <= bucket)
  61                        bucket = 0;
  62        }
  63}
  64
  65static struct spanhash_top *spanhash_rehash(struct spanhash_top *orig)
  66{
  67        struct spanhash_top *new;
  68        int i;
  69        int osz = 1 << orig->alloc_log2;
  70        int sz = osz << 1;
  71
  72        new = xmalloc(sizeof(*orig) + sizeof(struct spanhash) * sz);
  73        new->alloc_log2 = orig->alloc_log2 + 1;
  74        new->free = INITIAL_FREE(new->alloc_log2);
  75        memset(new->data, 0, sizeof(struct spanhash) * sz);
  76        for (i = 0; i < osz; i++) {
  77                struct spanhash *o = &(orig->data[i]);
  78                int bucket;
  79                if (!o->cnt)
  80                        continue;
  81                bucket = o->hashval & (sz - 1);
  82                while (1) {
  83                        struct spanhash *h = &(new->data[bucket++]);
  84                        if (!h->cnt) {
  85                                h->hashval = o->hashval;
  86                                h->cnt = o->cnt;
  87                                new->free--;
  88                                break;
  89                        }
  90                        if (sz <= bucket)
  91                                bucket = 0;
  92                }
  93        }
  94        free(orig);
  95        return new;
  96}
  97
  98static struct spanhash_top *add_spanhash(struct spanhash_top *top,
  99                                         unsigned int hashval, int cnt)
 100{
 101        int bucket, lim;
 102        struct spanhash *h;
 103
 104        lim = (1 << top->alloc_log2);
 105        bucket = hashval & (lim - 1);
 106        while (1) {
 107                h = &(top->data[bucket++]);
 108                if (!h->cnt) {
 109                        h->hashval = hashval;
 110                        h->cnt = cnt;
 111                        top->free--;
 112                        if (top->free < 0)
 113                                return spanhash_rehash(top);
 114                        return top;
 115                }
 116                if (h->hashval == hashval) {
 117                        h->cnt += cnt;
 118                        return top;
 119                }
 120                if (lim <= bucket)
 121                        bucket = 0;
 122        }
 123}
 124
 125static struct spanhash_top *hash_chars(unsigned char *buf, unsigned int sz)
 126{
 127        int i, n;
 128        unsigned int accum1, accum2, hashval;
 129        struct spanhash_top *hash;
 130
 131        i = INITIAL_HASH_SIZE;
 132        hash = xmalloc(sizeof(*hash) + sizeof(struct spanhash) * (1<<i));
 133        hash->alloc_log2 = i;
 134        hash->free = INITIAL_FREE(i);
 135        memset(hash->data, 0, sizeof(struct spanhash) * (1<<i));
 136
 137        n = 0;
 138        accum1 = accum2 = 0;
 139        while (sz) {
 140                unsigned int c = *buf++;
 141                unsigned int old_1 = accum1;
 142                sz--;
 143                accum1 = (accum1 << 7) ^ (accum2 >> 25);
 144                accum2 = (accum2 << 7) ^ (old_1 >> 25);
 145                accum1 += c;
 146                if (++n < 64 && c != '\n')
 147                        continue;
 148                hashval = (accum1 + accum2 * 0x61) % HASHBASE;
 149                hash = add_spanhash(hash, hashval, n);
 150                n = 0;
 151                accum1 = accum2 = 0;
 152        }
 153        return hash;
 154}
 155
 156int diffcore_count_changes(struct diff_filespec *src,
 157                           struct diff_filespec *dst,
 158                           void **src_count_p,
 159                           void **dst_count_p,
 160                           unsigned long delta_limit,
 161                           unsigned long *src_copied,
 162                           unsigned long *literal_added)
 163{
 164        int i, ssz;
 165        struct spanhash_top *src_count, *dst_count;
 166        unsigned long sc, la;
 167
 168        src_count = dst_count = NULL;
 169        if (src_count_p)
 170                src_count = *src_count_p;
 171        if (!src_count) {
 172                src_count = hash_chars(src->data, src->size);
 173                if (src_count_p)
 174                        *src_count_p = src_count;
 175        }
 176        if (dst_count_p)
 177                dst_count = *dst_count_p;
 178        if (!dst_count) {
 179                dst_count = hash_chars(dst->data, dst->size);
 180                if (dst_count_p)
 181                        *dst_count_p = dst_count;
 182        }
 183        sc = la = 0;
 184
 185        ssz = 1 << src_count->alloc_log2;
 186        for (i = 0; i < ssz; i++) {
 187                struct spanhash *s = &(src_count->data[i]);
 188                struct spanhash *d;
 189                unsigned dst_cnt, src_cnt;
 190                if (!s->cnt)
 191                        continue;
 192                src_cnt = s->cnt;
 193                d = spanhash_find(dst_count, s->hashval);
 194                dst_cnt = d ? d->cnt : 0;
 195                if (src_cnt < dst_cnt) {
 196                        la += dst_cnt - src_cnt;
 197                        sc += src_cnt;
 198                }
 199                else
 200                        sc += dst_cnt;
 201        }
 202
 203        if (!src_count_p)
 204                free(src_count);
 205        if (!dst_count_p)
 206                free(dst_count);
 207        *src_copied = sc;
 208        *literal_added = la;
 209        return 0;
 210}