|
pancake@372
|
1 |
/* radare - LGPL - Copyright 2009-2010 pancake<nopcode.org> */ |
|
pancake@104
|
2 |
/* Adapted code from: |
|
pancake@104
|
3 |
|
|
pancake@103
|
4 |
bdiff.c - efficient binary diff extension for Mercurial |
|
pancake@103
|
5 |
|
|
pancake@103
|
6 |
Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> |
|
pancake@103
|
7 |
|
|
pancake@103
|
8 |
This software may be used and distributed according to the terms of |
|
pancake@103
|
9 |
the GNU General Public License, incorporated herein by reference. |
|
pancake@103
|
10 |
|
|
pancake@103
|
11 |
Based roughly on Python difflib |
|
pancake@103
|
12 |
*/ |
|
pancake@103
|
13 |
|
|
pancake@104
|
14 |
#include <r_util.h> |
|
pancake@372
|
15 |
#include <r_diff.h> |
|
pancake@104
|
16 |
|
|
pancake@103
|
17 |
#include <stdlib.h> |
|
pancake@103
|
18 |
#include <string.h> |
|
pancake@103
|
19 |
#include <limits.h> |
|
pancake@103
|
20 |
|
|
pancake@103
|
21 |
struct line { |
|
pancake@103
|
22 |
int h, len, n, e; |
|
pancake@103
|
23 |
const char *l; |
|
pancake@103
|
24 |
}; |
|
pancake@103
|
25 |
|
|
pancake@103
|
26 |
struct pos { |
|
pancake@103
|
27 |
int pos, len; |
|
pancake@103
|
28 |
}; |
|
pancake@103
|
29 |
|
|
pancake@103
|
30 |
struct hunk { |
|
pancake@103
|
31 |
int a1, a2, b1, b2; |
|
pancake@103
|
32 |
}; |
|
pancake@103
|
33 |
|
|
pancake@103
|
34 |
struct hunklist { |
|
pancake@103
|
35 |
struct hunk *base, *head; |
|
pancake@103
|
36 |
}; |
|
pancake@103
|
37 |
|
|
pancake@372
|
38 |
static int splitlines(const char *a, int len, struct line **lr) |
|
pancake@103
|
39 |
{ |
|
pancake@103
|
40 |
int h, i; |
|
pancake@103
|
41 |
const char *p, *b = a; |
|
pancake@103
|
42 |
const char * const plast = a + len - 1; |
|
pancake@103
|
43 |
struct line *l; |
|
pancake@103
|
44 |
|
|
pancake@299
|
45 |
if (a == NULL) { |
|
pancake@299
|
46 |
eprintf ("null pointer received\n"); |
|
pancake@299
|
47 |
return 0; |
|
pancake@299
|
48 |
} |
|
pancake@299
|
49 |
|
|
pancake@103
|
50 |
/* count the lines */ |
|
pancake@103
|
51 |
i = 1; /* extra line for sentinel */ |
|
pancake@103
|
52 |
for (p = a; p < a + len; p++) |
|
pancake@103
|
53 |
if (*p == '\n' || p == plast) |
|
pancake@103
|
54 |
i++; |
|
pancake@103
|
55 |
|
|
pancake@103
|
56 |
*lr = l = (struct line *)malloc(sizeof(struct line) * i); |
|
pancake@103
|
57 |
if (!l) |
|
pancake@103
|
58 |
return -1; |
|
pancake@103
|
59 |
|
|
pancake@103
|
60 |
/* build the line array and calculate hashes */ |
|
pancake@103
|
61 |
h = 0; |
|
pancake@103
|
62 |
for (p = a; p < a + len; p++) { |
|
pancake@103
|
63 |
/* Leonid Yuriev's hash */ |
|
pancake@103
|
64 |
h = (h * 1664525) + *p + 1013904223; |
|
pancake@103
|
65 |
|
|
pancake@103
|
66 |
if (*p == '\n' || p == plast) { |
|
pancake@103
|
67 |
l->h = h; |
|
pancake@103
|
68 |
h = 0; |
|
pancake@103
|
69 |
l->len = p - b + 1; |
|
pancake@103
|
70 |
l->l = b; |
|
pancake@103
|
71 |
l->n = INT_MAX; |
|
pancake@103
|
72 |
l++; |
|
pancake@103
|
73 |
b = p + 1; |
|
pancake@103
|
74 |
} |
|
pancake@103
|
75 |
} |
|
pancake@103
|
76 |
|
|
pancake@103
|
77 |
/* set up a sentinel */ |
|
pancake@103
|
78 |
l->h = l->len = 0; |
|
pancake@103
|
79 |
l->l = a + len; |
|
pancake@103
|
80 |
return i - 1; |
|
pancake@103
|
81 |
} |
|
pancake@103
|
82 |
|
|
pancake@372
|
83 |
static int inline cmp(struct line *a, struct line *b) |
|
pancake@103
|
84 |
{ |
|
pancake@103
|
85 |
return a->h != b->h || a->len != b->len || memcmp(a->l, b->l, a->len); |
|
pancake@103
|
86 |
} |
|
pancake@103
|
87 |
|
|
pancake@103
|
88 |
static int equatelines(struct line *a, int an, struct line *b, int bn) |
|
pancake@103
|
89 |
{ |
|
pancake@103
|
90 |
int i, j, buckets = 1, t, scale; |
|
pancake@103
|
91 |
struct pos *h = NULL; |
|
pancake@103
|
92 |
|
|
pancake@103
|
93 |
/* build a hash table of the next highest power of 2 */ |
|
pancake@103
|
94 |
while (buckets < bn + 1) |
|
pancake@103
|
95 |
buckets *= 2; |
|
pancake@103
|
96 |
|
|
pancake@103
|
97 |
/* try to allocate a large hash table to avoid collisions */ |
|
pancake@103
|
98 |
for (scale = 4; scale; scale /= 2) { |
|
pancake@103
|
99 |
h = (struct pos *)malloc(scale * buckets * sizeof(struct pos)); |
|
pancake@103
|
100 |
if (h) |
|
pancake@103
|
101 |
break; |
|
pancake@103
|
102 |
} |
|
pancake@103
|
103 |
|
|
pancake@103
|
104 |
if (!h) |
|
pancake@103
|
105 |
return 0; |
|
pancake@103
|
106 |
|
|
pancake@103
|
107 |
buckets = buckets * scale - 1; |
|
pancake@103
|
108 |
|
|
pancake@103
|
109 |
/* clear the hash table */ |
|
pancake@103
|
110 |
for (i = 0; i <= buckets; i++) { |
|
pancake@103
|
111 |
h[i].pos = INT_MAX; |
|
pancake@103
|
112 |
h[i].len = 0; |
|
pancake@103
|
113 |
} |
|
pancake@103
|
114 |
|
|
pancake@103
|
115 |
/* add lines to the hash table chains */ |
|
pancake@103
|
116 |
for (i = bn - 1; i >= 0; i--) { |
|
pancake@103
|
117 |
/* find the equivalence class */ |
|
pancake@103
|
118 |
for (j = b[i].h & buckets; h[j].pos != INT_MAX; |
|
pancake@103
|
119 |
j = (j + 1) & buckets) |
|
pancake@103
|
120 |
if (!cmp(b + i, b + h[j].pos)) |
|
pancake@103
|
121 |
break; |
|
pancake@103
|
122 |
|
|
pancake@103
|
123 |
/* add to the head of the equivalence class */ |
|
pancake@103
|
124 |
b[i].n = h[j].pos; |
|
pancake@103
|
125 |
b[i].e = j; |
|
pancake@103
|
126 |
h[j].pos = i; |
|
pancake@103
|
127 |
h[j].len++; /* keep track of popularity */ |
|
pancake@103
|
128 |
} |
|
pancake@103
|
129 |
|
|
pancake@103
|
130 |
/* compute popularity threshold */ |
|
pancake@103
|
131 |
t = (bn >= 4000) ? bn / 1000 : bn + 1; |
|
pancake@103
|
132 |
|
|
pancake@103
|
133 |
/* match items in a to their equivalence class in b */ |
|
pancake@103
|
134 |
for (i = 0; i < an; i++) { |
|
pancake@103
|
135 |
/* find the equivalence class */ |
|
pancake@103
|
136 |
for (j = a[i].h & buckets; h[j].pos != INT_MAX; |
|
pancake@103
|
137 |
j = (j + 1) & buckets) |
|
pancake@103
|
138 |
if (!cmp(a + i, b + h[j].pos)) |
|
pancake@103
|
139 |
break; |
|
pancake@103
|
140 |
|
|
pancake@103
|
141 |
a[i].e = j; /* use equivalence class for quick compare */ |
|
pancake@103
|
142 |
if (h[j].len <= t) |
|
pancake@103
|
143 |
a[i].n = h[j].pos; /* point to head of match list */ |
|
pancake@103
|
144 |
else |
|
pancake@103
|
145 |
a[i].n = INT_MAX; /* too popular */ |
|
pancake@103
|
146 |
} |
|
pancake@103
|
147 |
|
|
pancake@103
|
148 |
/* discard hash tables */ |
|
pancake@103
|
149 |
free(h); |
|
pancake@103
|
150 |
return 1; |
|
pancake@103
|
151 |
} |
|
pancake@103
|
152 |
|
|
pancake@103
|
153 |
static int longest_match(struct line *a, struct line *b, struct pos *pos, |
|
pancake@103
|
154 |
int a1, int a2, int b1, int b2, int *omi, int *omj) |
|
pancake@103
|
155 |
{ |
|
pancake@103
|
156 |
int mi = a1, mj = b1, mk = 0, mb = 0, i, j, k; |
|
pancake@103
|
157 |
|
|
pancake@103
|
158 |
for (i = a1; i < a2; i++) { |
|
pancake@103
|
159 |
/* skip things before the current block */ |
|
pancake@103
|
160 |
for (j = a[i].n; j < b1; j = b[j].n) |
|
pancake@103
|
161 |
; |
|
pancake@103
|
162 |
|
|
pancake@103
|
163 |
/* loop through all lines match a[i] in b */ |
|
pancake@103
|
164 |
for (; j < b2; j = b[j].n) { |
|
pancake@103
|
165 |
/* does this extend an earlier match? */ |
|
pancake@103
|
166 |
if (i > a1 && j > b1 && pos[j - 1].pos == i - 1) |
|
pancake@103
|
167 |
k = pos[j - 1].len + 1; |
|
pancake@103
|
168 |
else |
|
pancake@103
|
169 |
k = 1; |
|
pancake@103
|
170 |
pos[j].pos = i; |
|
pancake@103
|
171 |
pos[j].len = k; |
|
pancake@103
|
172 |
|
|
pancake@103
|
173 |
/* best match so far? */ |
|
pancake@103
|
174 |
if (k > mk) { |
|
pancake@103
|
175 |
mi = i; |
|
pancake@103
|
176 |
mj = j; |
|
pancake@103
|
177 |
mk = k; |
|
pancake@103
|
178 |
} |
|
pancake@103
|
179 |
} |
|
pancake@103
|
180 |
} |
|
pancake@103
|
181 |
|
|
pancake@103
|
182 |
if (mk) { |
|
pancake@103
|
183 |
mi = mi - mk + 1; |
|
pancake@103
|
184 |
mj = mj - mk + 1; |
|
pancake@103
|
185 |
} |
|
pancake@103
|
186 |
|
|
pancake@103
|
187 |
/* expand match to include neighboring popular lines */ |
|
pancake@103
|
188 |
while (mi - mb > a1 && mj - mb > b1 && |
|
pancake@103
|
189 |
a[mi - mb - 1].e == b[mj - mb - 1].e) |
|
pancake@103
|
190 |
mb++; |
|
pancake@103
|
191 |
while (mi + mk < a2 && mj + mk < b2 && |
|
pancake@103
|
192 |
a[mi + mk].e == b[mj + mk].e) |
|
pancake@103
|
193 |
mk++; |
|
pancake@103
|
194 |
|
|
pancake@103
|
195 |
*omi = mi - mb; |
|
pancake@103
|
196 |
*omj = mj - mb; |
|
pancake@103
|
197 |
|
|
pancake@103
|
198 |
return mk + mb; |
|
pancake@103
|
199 |
} |
|
pancake@103
|
200 |
|
|
pancake@103
|
201 |
static void recurse(struct line *a, struct line *b, struct pos *pos, |
|
pancake@103
|
202 |
int a1, int a2, int b1, int b2, struct hunklist *l) |
|
pancake@103
|
203 |
{ |
|
pancake@103
|
204 |
int i, j, k; |
|
pancake@103
|
205 |
|
|
pancake@103
|
206 |
/* find the longest match in this chunk */ |
|
pancake@103
|
207 |
k = longest_match(a, b, pos, a1, a2, b1, b2, &i, &j); |
|
pancake@103
|
208 |
if (!k) |
|
pancake@103
|
209 |
return; |
|
pancake@103
|
210 |
|
|
pancake@103
|
211 |
/* and recurse on the remaining chunks on either side */ |
|
pancake@103
|
212 |
recurse(a, b, pos, a1, i, b1, j, l); |
|
pancake@103
|
213 |
l->head->a1 = i; |
|
pancake@103
|
214 |
l->head->a2 = i + k; |
|
pancake@103
|
215 |
l->head->b1 = j; |
|
pancake@103
|
216 |
l->head->b2 = j + k; |
|
pancake@103
|
217 |
l->head++; |
|
pancake@103
|
218 |
recurse(a, b, pos, i + k, a2, j + k, b2, l); |
|
pancake@103
|
219 |
} |
|
pancake@103
|
220 |
|
|
pancake@103
|
221 |
static struct hunklist diff(struct line *a, int an, struct line *b, int bn) |
|
pancake@103
|
222 |
{ |
|
pancake@103
|
223 |
struct hunklist l; |
|
pancake@103
|
224 |
struct hunk *curr; |
|
pancake@103
|
225 |
struct pos *pos; |
|
pancake@103
|
226 |
int t; |
|
pancake@103
|
227 |
|
|
pancake@103
|
228 |
/* allocate and fill arrays */ |
|
pancake@103
|
229 |
t = equatelines(a, an, b, bn); |
|
pancake@103
|
230 |
pos = (struct pos *)calloc(bn ? bn : 1, sizeof(struct pos)); |
|
pancake@103
|
231 |
/* we can't have more matches than lines in the shorter file */ |
|
pancake@103
|
232 |
l.head = l.base = (struct hunk *)malloc(sizeof(struct hunk) * |
|
pancake@103
|
233 |
((an<bn ? an:bn) + 1)); |
|
pancake@103
|
234 |
|
|
pancake@103
|
235 |
if (pos && l.base && t) { |
|
pancake@103
|
236 |
/* generate the matching block list */ |
|
pancake@103
|
237 |
recurse(a, b, pos, 0, an, 0, bn, &l); |
|
pancake@103
|
238 |
l.head->a1 = l.head->a2 = an; |
|
pancake@103
|
239 |
l.head->b1 = l.head->b2 = bn; |
|
pancake@103
|
240 |
l.head++; |
|
pancake@103
|
241 |
} |
|
pancake@103
|
242 |
|
|
pancake@103
|
243 |
free(pos); |
|
pancake@103
|
244 |
|
|
pancake@103
|
245 |
/* normalize the hunk list, try to push each hunk towards the end */ |
|
pancake@103
|
246 |
for (curr = l.base; curr != l.head; curr++) { |
|
pancake@103
|
247 |
struct hunk *next = curr+1; |
|
pancake@103
|
248 |
int shift = 0; |
|
pancake@103
|
249 |
|
|
pancake@103
|
250 |
if (next == l.head) |
|
pancake@103
|
251 |
break; |
|
pancake@103
|
252 |
|
|
pancake@103
|
253 |
if (curr->a2 == next->a1) |
|
pancake@103
|
254 |
while (curr->a2+shift < an && curr->b2+shift < bn |
|
pancake@103
|
255 |
&& !cmp(a+curr->a2+shift, b+curr->b2+shift)) |
|
pancake@103
|
256 |
shift++; |
|
pancake@103
|
257 |
else if (curr->b2 == next->b1) |
|
pancake@103
|
258 |
while (curr->b2+shift < bn && curr->a2+shift < an |
|
pancake@103
|
259 |
&& !cmp(b+curr->b2+shift, a+curr->a2+shift)) |
|
pancake@103
|
260 |
shift++; |
|
pancake@103
|
261 |
if (!shift) |
|
pancake@103
|
262 |
continue; |
|
pancake@103
|
263 |
curr->b2 += shift; |
|
pancake@103
|
264 |
next->b1 += shift; |
|
pancake@103
|
265 |
curr->a2 += shift; |
|
pancake@103
|
266 |
next->a1 += shift; |
|
pancake@103
|
267 |
} |
|
pancake@103
|
268 |
|
|
pancake@103
|
269 |
return l; |
|
pancake@103
|
270 |
} |
|
pancake@103
|
271 |
|
|
pancake@372
|
272 |
//-- |
|
pancake@372
|
273 |
// TODO: implement the r_diff_lines // we need to implement r_file_line_at (file, off); |
|
pancake@485
|
274 |
R_API int r_diff_buffers_delta(RDiff *d, const ut8 *sa, int la, const ut8 *sb, int lb) { |
|
pancake@372
|
275 |
RDiffOp dop; |
|
pancake@372
|
276 |
struct line *al, *bl; |
|
pancake@372
|
277 |
struct hunklist l = { NULL, NULL }; |
|
pancake@103
|
278 |
struct hunk *h; |
|
pancake@372
|
279 |
int an, bn, offa, rlen, offb, len = 0; |
|
pancake@372
|
280 |
int hits = 0; |
|
pancake@103
|
281 |
|
|
pancake@485
|
282 |
an = splitlines((const char *)sa, la, &al); |
|
pancake@485
|
283 |
bn = splitlines((const char*)sb, lb, &bl); |
|
pancake@372
|
284 |
if (!al || !bl) { |
|
pancake@372
|
285 |
eprintf ("bindiff_buffers: Out of memory.\n"); |
|
pancake@372
|
286 |
return -1; |
|
pancake@103
|
287 |
} |
|
pancake@103
|
288 |
|
|
pancake@372
|
289 |
l = diff (al, an, bl, bn); |
|
pancake@372
|
290 |
if (!l.head) { |
|
pancake@372
|
291 |
eprintf ("bindiff_buffers: Out of memory.\n"); |
|
pancake@372
|
292 |
return -1; |
|
pancake@372
|
293 |
} |
|
pancake@103
|
294 |
|
|
pancake@103
|
295 |
la = lb = 0; |
|
pancake@103
|
296 |
for (h = l.base; h != l.head; h++) { |
|
pancake@103
|
297 |
if (h->a1 != la || h->b1 != lb) { |
|
pancake@103
|
298 |
len = bl[h->b1].l - bl[lb].l; |
|
pancake@372
|
299 |
offa = al[la].l - al->l; |
|
pancake@372
|
300 |
offb = al[h->a1].l - al->l; |
|
pancake@372
|
301 |
rlen = offb-offa; |
|
pancake@372
|
302 |
|
|
pancake@372
|
303 |
if (d->callback) { |
|
pancake@372
|
304 |
/* source file */ |
|
pancake@372
|
305 |
dop.a_off = offa; |
|
pancake@372
|
306 |
dop.a_buf = (ut8 *)al[la].l; |
|
pancake@372
|
307 |
dop.a_len = rlen; |
|
pancake@372
|
308 |
|
|
pancake@372
|
309 |
/* destination file */ |
|
pancake@372
|
310 |
dop.b_off = offa; // XXX offb not used?? |
|
pancake@372
|
311 |
dop.b_buf = (ut8 *)bl[lb].l; |
|
pancake@372
|
312 |
dop.b_len = len; |
|
nibble@373
|
313 |
if (!d->callback (d, d->user, &dop)) |
|
nibble@373
|
314 |
break; |
|
pancake@372
|
315 |
} |
|
pancake@372
|
316 |
#if 0 |
|
pancake@372
|
317 |
if (rlen > 0) { |
|
pancake@372
|
318 |
//printf ("Remove %d bytes at %d\n", rlen, offa); |
|
nopcode@619
|
319 |
printf ("r-%d @ 0x%"PFMT64x"\n", rlen, (ut64)offa); |
|
pancake@372
|
320 |
} |
|
pancake@372
|
321 |
printf ("e file.write=true\n"); // XXX |
|
pancake@372
|
322 |
printf ("wx "); |
|
pancake@372
|
323 |
for(i=0;i<len;i++) |
|
pancake@372
|
324 |
printf ("%02x", bl[lb].l[i]); |
|
nopcode@619
|
325 |
printf (" @ 0x%"PFMT64x"\n", (ut64)offa); |
|
pancake@1895
|
326 |
rb += 12 + len; |
|
pancake@372
|
327 |
#endif |
|
pancake@103
|
328 |
} |
|
pancake@103
|
329 |
la = h->a2; |
|
pancake@103
|
330 |
lb = h->b2; |
|
pancake@103
|
331 |
} |
|
pancake@1895
|
332 |
free (al); |
|
pancake@1895
|
333 |
free (bl); |
|
pancake@1895
|
334 |
free (l.base); |
|
pancake@104
|
335 |
|
|
pancake@104
|
336 |
return hits; |
|
pancake@104
|
337 |
} |
|
pancake@104
|
338 |
|