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