CommitLineData
65416758
JH
1#include "cache.h"
2#include "diff.h"
3#include "diffcore.h"
ba23bbc8
JH
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
c06c7966
JH
27/* Wild guess at the initial hash size */
28#define INITIAL_HASH_SIZE 10
ba23bbc8
JH
29#define HASHBASE 65537 /* next_prime(2^16) */
30
c06c7966
JH
31struct spanhash {
32 unsigned long hashval;
33 unsigned long cnt;
34};
35struct spanhash_top {
36 int alloc_log2;
37 int free;
38 struct spanhash data[FLEX_ARRAY];
39};
40
41static struct spanhash *spanhash_find(struct spanhash_top *top, unsigned long hashval)
42{
43 int sz = 1 << top->alloc_log2;
44 int bucket = hashval & (sz - 1);
45 while (1) {
46 struct spanhash *h = &(top->data[bucket++]);
47 if (!h->cnt)
48 return NULL;
49 if (h->hashval == hashval)
50 return h;
51 if (sz <= bucket)
52 bucket = 0;
53 }
54}
55
56static struct spanhash_top *spanhash_rehash(struct spanhash_top *orig)
57{
58 struct spanhash_top *new;
59 int i;
60 int osz = 1 << orig->alloc_log2;
61 int sz = osz << 1;
62
63 new = xmalloc(sizeof(*orig) + sizeof(struct spanhash) * sz);
64 new->alloc_log2 = orig->alloc_log2 + 1;
65 new->free = osz;
66 memset(new->data, 0, sizeof(struct spanhash) * sz);
67 for (i = 0; i < osz; i++) {
68 struct spanhash *o = &(orig->data[i]);
69 int bucket;
70 if (!o->cnt)
71 continue;
72 bucket = o->hashval & (sz - 1);
73 while (1) {
74 struct spanhash *h = &(new->data[bucket++]);
75 if (!h->cnt) {
76 h->hashval = o->hashval;
77 h->cnt = o->cnt;
78 new->free--;
79 break;
80 }
81 if (sz <= bucket)
82 bucket = 0;
83 }
84 }
85 free(orig);
86 return new;
87}
88
89static struct spanhash_top *add_spanhash(struct spanhash_top *top,
90 unsigned long hashval)
91{
92 int bucket, lim;
93 struct spanhash *h;
94
95 lim = (1 << top->alloc_log2);
96 bucket = hashval & (lim - 1);
97 while (1) {
98 h = &(top->data[bucket++]);
99 if (!h->cnt) {
100 h->hashval = hashval;
101 h->cnt = 1;
102 top->free--;
103 if (top->free < 0)
104 return spanhash_rehash(top);
105 return top;
106 }
107 if (h->hashval == hashval) {
108 h->cnt++;
109 return top;
110 }
111 if (lim <= bucket)
112 bucket = 0;
113 }
114}
115
116static struct spanhash_top *hash_chars(unsigned char *buf, unsigned long sz)
65416758 117{
c06c7966
JH
118 int i;
119 unsigned long accum1, accum2, hashval;
120 struct spanhash_top *hash;
121
122 i = INITIAL_HASH_SIZE;
123 hash = xmalloc(sizeof(*hash) + sizeof(struct spanhash) * (1<<i));
124 hash->alloc_log2 = i;
125 hash->free = (1<<i)/2;
126 memset(hash->data, 0, sizeof(struct spanhash) * (1<<i));
65416758 127
ba23bbc8
JH
128 /* an 8-byte shift register made of accum1 and accum2. New
129 * bytes come at LSB of accum2, and shifted up to accum1
130 */
131 for (i = accum1 = accum2 = 0; i < 7; i++, sz--) {
132 accum1 = (accum1 << 8) | (accum2 >> 24);
133 accum2 = (accum2 << 8) | *buf++;
134 }
135 while (sz) {
136 accum1 = (accum1 << 8) | (accum2 >> 24);
137 accum2 = (accum2 << 8) | *buf++;
c06c7966
JH
138 hashval = (accum1 + accum2 * 0x61) % HASHBASE;
139 hash = add_spanhash(hash, hashval);
ba23bbc8 140 sz--;
65416758 141 }
c06c7966 142 return hash;
65416758
JH
143}
144
145int diffcore_count_changes(void *src, unsigned long src_size,
146 void *dst, unsigned long dst_size,
c06c7966
JH
147 void **src_count_p,
148 void **dst_count_p,
65416758
JH
149 unsigned long delta_limit,
150 unsigned long *src_copied,
151 unsigned long *literal_added)
152{
c06c7966
JH
153 int i, ssz;
154 struct spanhash_top *src_count, *dst_count;
ba23bbc8
JH
155 unsigned long sc, la;
156
157 if (src_size < 8 || dst_size < 8)
158 return -1;
159
c06c7966
JH
160 src_count = dst_count = NULL;
161 if (src_count_p)
162 src_count = *src_count_p;
163 if (!src_count) {
164 src_count = hash_chars(src, src_size);
165 if (src_count_p)
166 *src_count_p = src_count;
167 }
168 if (dst_count_p)
169 dst_count = *dst_count_p;
170 if (!dst_count) {
171 dst_count = hash_chars(dst, dst_size);
172 if (dst_count_p)
173 *dst_count_p = dst_count;
174 }
ba23bbc8 175 sc = la = 0;
c06c7966
JH
176
177 ssz = 1 << src_count->alloc_log2;
178 for (i = 0; i < ssz; i++) {
179 struct spanhash *s = &(src_count->data[i]);
180 struct spanhash *d;
181 unsigned dst_cnt, src_cnt;
182 if (!s->cnt)
183 continue;
184 src_cnt = s->cnt;
185 d = spanhash_find(dst_count, s->hashval);
186 dst_cnt = d ? d->cnt : 0;
187 if (src_cnt < dst_cnt) {
188 la += dst_cnt - src_cnt;
189 sc += src_cnt;
ba23bbc8 190 }
c06c7966
JH
191 else
192 sc += dst_cnt;
ba23bbc8 193 }
c06c7966
JH
194
195 if (!src_count_p)
196 free(src_count);
197 if (!dst_count_p)
198 free(dst_count);
ba23bbc8
JH
199 *src_copied = sc;
200 *literal_added = la;
ba23bbc8 201 return 0;
65416758 202}