libr/diff/bdiff.c
author pancake@pair
Sat, 04 Feb 2012 03:51:22 +0100
changeset 2007 ef840cc3d292
parent 619 e9e25f49d3fb
permissions -rw-r--r--
* Initial support for the z80 CPU
- assembler, disassembler and basic code analysis
- code analysis is very primitive atm
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