bsdiff4 1.3.0__tar.gz → 1.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bsdiff4-1.3.0/bsdiff4.egg-info → bsdiff4-1.3.1}/PKG-INFO +1 -1
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/bsdiff4/__init__.py +1 -1
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/bsdiff4/cli.py +0 -2
- bsdiff4-1.3.1/bsdiff4/core.c +643 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/bsdiff4/format.py +16 -8
- bsdiff4-1.3.1/bsdiff4/test_all.py +282 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1/bsdiff4.egg-info}/PKG-INFO +1 -1
- bsdiff4-1.3.0/bsdiff4/core.c +0 -552
- bsdiff4-1.3.0/bsdiff4/test_all.py +0 -164
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/LICENSE +0 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/MANIFEST.in +0 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/README.rst +0 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/bsdiff4.egg-info/SOURCES.txt +0 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/bsdiff4.egg-info/dependency_links.txt +0 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/bsdiff4.egg-info/entry_points.txt +0 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/bsdiff4.egg-info/top_level.txt +0 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/setup.cfg +0 -0
- {bsdiff4-1.3.0 → bsdiff4-1.3.1}/setup.py +0 -0
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The code below is mostly derived from cx_bsdiff (written by Anthony
|
|
3
|
+
Tuininga, http://cx-bsdiff.sourceforge.net/). The cx_bsdiff code in
|
|
4
|
+
turn was derived from bsdiff, the standalone utility produced for BSD
|
|
5
|
+
which can be found at http://www.daemonology.net/bsdiff.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#define PY_SSIZE_T_CLEAN
|
|
9
|
+
#include <Python.h>
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/* swap two suffix array entries */
|
|
13
|
+
static inline void
|
|
14
|
+
swap_indices(Py_ssize_t *a, Py_ssize_t *b)
|
|
15
|
+
{
|
|
16
|
+
Py_ssize_t tmp = *a;
|
|
17
|
+
*a = *b;
|
|
18
|
+
*b = tmp;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* sort and rank a small suffix group */
|
|
22
|
+
static void
|
|
23
|
+
split_small(Py_ssize_t *I, Py_ssize_t *V, Py_ssize_t start,
|
|
24
|
+
Py_ssize_t len, Py_ssize_t h)
|
|
25
|
+
{
|
|
26
|
+
Py_ssize_t i, j, k, x;
|
|
27
|
+
|
|
28
|
+
for (k = start; k < start + len; k += j) {
|
|
29
|
+
j = 1;
|
|
30
|
+
x = V[I[k] + h];
|
|
31
|
+
for (i = 1; k + i < start + len; i++) {
|
|
32
|
+
if (V[I[k + i] + h] < x) {
|
|
33
|
+
x = V[I[k + i] + h];
|
|
34
|
+
j = 0;
|
|
35
|
+
}
|
|
36
|
+
if (V[I[k + i] + h] == x) {
|
|
37
|
+
swap_indices(&I[k + j], &I[k + i]);
|
|
38
|
+
j++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (i = 0; i < j; i++)
|
|
42
|
+
V[I[k + i]] = k + j - 1;
|
|
43
|
+
if (j == 1)
|
|
44
|
+
I[k] = -1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* partition a suffix group around a pivot rank */
|
|
49
|
+
static void
|
|
50
|
+
partition_group(Py_ssize_t *I, Py_ssize_t *V, Py_ssize_t start,
|
|
51
|
+
Py_ssize_t len, Py_ssize_t h,
|
|
52
|
+
Py_ssize_t *lower, Py_ssize_t *upper)
|
|
53
|
+
{
|
|
54
|
+
Py_ssize_t i, j, k, x, jj = 0, kk = 0;
|
|
55
|
+
|
|
56
|
+
x = V[I[start + len / 2] + h];
|
|
57
|
+
for (i = start; i < start + len; i++) {
|
|
58
|
+
if (V[I[i] + h] < x)
|
|
59
|
+
jj++;
|
|
60
|
+
if (V[I[i] + h] == x)
|
|
61
|
+
kk++;
|
|
62
|
+
}
|
|
63
|
+
jj += start;
|
|
64
|
+
kk += jj;
|
|
65
|
+
|
|
66
|
+
j = 0;
|
|
67
|
+
k = 0;
|
|
68
|
+
i = start;
|
|
69
|
+
while (i < jj) {
|
|
70
|
+
if (V[I[i] + h] < x) {
|
|
71
|
+
i++;
|
|
72
|
+
} else if (V[I[i] + h] == x) {
|
|
73
|
+
swap_indices(&I[i], &I[jj + j]);
|
|
74
|
+
j++;
|
|
75
|
+
} else {
|
|
76
|
+
swap_indices(&I[i], &I[kk + k]);
|
|
77
|
+
k++;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
while (jj + j < kk) {
|
|
82
|
+
if (V[I[jj + j] + h] == x) {
|
|
83
|
+
j++;
|
|
84
|
+
} else {
|
|
85
|
+
swap_indices(&I[jj + j], &I[kk + k]);
|
|
86
|
+
k++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
*lower = jj;
|
|
91
|
+
*upper = kk;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* recursively split and rank a suffix group */
|
|
95
|
+
static void
|
|
96
|
+
split(Py_ssize_t *I, Py_ssize_t *V, Py_ssize_t start,
|
|
97
|
+
Py_ssize_t len, Py_ssize_t h)
|
|
98
|
+
{
|
|
99
|
+
Py_ssize_t i, lower, upper;
|
|
100
|
+
|
|
101
|
+
if (len < 16) {
|
|
102
|
+
split_small(I, V, start, len, h);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
partition_group(I, V, start, len, h, &lower, &upper);
|
|
107
|
+
|
|
108
|
+
if (lower > start)
|
|
109
|
+
split(I, V, start, lower - start, h);
|
|
110
|
+
|
|
111
|
+
for (i = lower; i < upper; i++)
|
|
112
|
+
V[I[i]] = upper - 1;
|
|
113
|
+
if (upper - lower == 1)
|
|
114
|
+
I[lower] = -1;
|
|
115
|
+
if (start + len > upper)
|
|
116
|
+
split(I, V, upper, start + len - upper, h);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* construct the suffix array for old */
|
|
120
|
+
static void
|
|
121
|
+
qsufsort(Py_ssize_t *I, Py_ssize_t *V, unsigned char *old, Py_ssize_t oldsize)
|
|
122
|
+
{
|
|
123
|
+
Py_ssize_t buckets[256], i, h, len;
|
|
124
|
+
|
|
125
|
+
for (i = 0; i < 256; i++)
|
|
126
|
+
buckets[i] = 0;
|
|
127
|
+
for (i = 0; i < oldsize; i++)
|
|
128
|
+
buckets[old[i]]++;
|
|
129
|
+
for (i = 1; i < 256; i++)
|
|
130
|
+
buckets[i] += buckets[i - 1];
|
|
131
|
+
for (i = 255; i > 0; i--)
|
|
132
|
+
buckets[i] = buckets[i - 1];
|
|
133
|
+
buckets[0] = 0;
|
|
134
|
+
|
|
135
|
+
for (i = 0; i < oldsize; i++)
|
|
136
|
+
I[++buckets[old[i]]] = i;
|
|
137
|
+
I[0] = oldsize;
|
|
138
|
+
for (i = 0; i < oldsize; i++)
|
|
139
|
+
V[i] = buckets[old[i]];
|
|
140
|
+
V[oldsize] = 0;
|
|
141
|
+
for (i = 1; i < 256; i++)
|
|
142
|
+
if (buckets[i] == buckets[i - 1] + 1)
|
|
143
|
+
I[buckets[i]] = -1;
|
|
144
|
+
I[0] = -1;
|
|
145
|
+
|
|
146
|
+
for (h = 1; I[0] != -(oldsize + 1); h += h) {
|
|
147
|
+
len = 0;
|
|
148
|
+
for (i = 0; i < oldsize + 1;) {
|
|
149
|
+
if (I[i] < 0) {
|
|
150
|
+
len -= I[i];
|
|
151
|
+
i -= I[i];
|
|
152
|
+
} else {
|
|
153
|
+
if (len)
|
|
154
|
+
I[i - len] = -len;
|
|
155
|
+
len = V[I[i]] + 1 - i;
|
|
156
|
+
split(I, V, i, len, h);
|
|
157
|
+
i += len;
|
|
158
|
+
len=0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (len)
|
|
162
|
+
I[i - len] = -len;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (i = 0; i < oldsize + 1; i++)
|
|
166
|
+
I[V[i]] = i;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* return the length of the common prefix */
|
|
170
|
+
static Py_ssize_t
|
|
171
|
+
matchlen(unsigned char *old, Py_ssize_t oldsize,
|
|
172
|
+
unsigned char *new, Py_ssize_t newsize)
|
|
173
|
+
{
|
|
174
|
+
Py_ssize_t i;
|
|
175
|
+
|
|
176
|
+
for (i = 0; (i < oldsize) && (i < newsize); i++)
|
|
177
|
+
if (old[i] != new[i])
|
|
178
|
+
break;
|
|
179
|
+
return i;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* find the suffix with the longest prefix match */
|
|
183
|
+
static Py_ssize_t
|
|
184
|
+
search(Py_ssize_t *I,
|
|
185
|
+
unsigned char *old, Py_ssize_t oldsize,
|
|
186
|
+
unsigned char *new, Py_ssize_t newsize,
|
|
187
|
+
Py_ssize_t st, Py_ssize_t en, Py_ssize_t *pos)
|
|
188
|
+
{
|
|
189
|
+
Py_ssize_t x, y;
|
|
190
|
+
|
|
191
|
+
if (en - st < 2) {
|
|
192
|
+
x = matchlen(old + I[st], oldsize - I[st], new, newsize);
|
|
193
|
+
y = matchlen(old + I[en], oldsize - I[en], new, newsize);
|
|
194
|
+
|
|
195
|
+
if (x > y) {
|
|
196
|
+
*pos = I[st];
|
|
197
|
+
return x;
|
|
198
|
+
} else {
|
|
199
|
+
*pos = I[en];
|
|
200
|
+
return y;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
x = st + (en - st) / 2;
|
|
205
|
+
if (memcmp(old + I[x], new, Py_MIN(oldsize - I[x], newsize)) < 0) {
|
|
206
|
+
return search(I, old, oldsize, new, newsize, x, en, pos);
|
|
207
|
+
} else {
|
|
208
|
+
return search(I, old, oldsize, new, newsize, st, x, pos);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
typedef struct {
|
|
213
|
+
PyObject *controls; /* list of control tuples */
|
|
214
|
+
unsigned char *diff; /* difference block */
|
|
215
|
+
unsigned char *extra; /* extra block */
|
|
216
|
+
Py_ssize_t diff_len; /* length of difference block */
|
|
217
|
+
Py_ssize_t extra_len; /* length of extra block */
|
|
218
|
+
} DiffResult;
|
|
219
|
+
|
|
220
|
+
/* release resources owned by a diff result */
|
|
221
|
+
static void
|
|
222
|
+
clear_diff_result(DiffResult *result)
|
|
223
|
+
{
|
|
224
|
+
Py_XDECREF(result->controls);
|
|
225
|
+
PyMem_Free(result->diff);
|
|
226
|
+
PyMem_Free(result->extra);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* append a control tuple */
|
|
230
|
+
static int
|
|
231
|
+
append_control(PyObject *controls, Py_ssize_t x, Py_ssize_t y, Py_ssize_t z)
|
|
232
|
+
{
|
|
233
|
+
PyObject *tuple;
|
|
234
|
+
int res;
|
|
235
|
+
|
|
236
|
+
tuple = Py_BuildValue("(nnn)", x, y, z);
|
|
237
|
+
if (!tuple)
|
|
238
|
+
return -1;
|
|
239
|
+
res = PyList_Append(controls, tuple);
|
|
240
|
+
Py_DECREF(tuple);
|
|
241
|
+
return res;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* find the best forward match length */
|
|
245
|
+
static Py_ssize_t
|
|
246
|
+
forward_match_length(const char *origData, Py_ssize_t origDataLength,
|
|
247
|
+
const char *newData, Py_ssize_t lastscan,
|
|
248
|
+
Py_ssize_t lastpos, Py_ssize_t scan)
|
|
249
|
+
{
|
|
250
|
+
Py_ssize_t i, score = 0, best_score = 0, best_length = 0;
|
|
251
|
+
|
|
252
|
+
for (i = 0; (lastscan + i < scan) &&
|
|
253
|
+
(lastpos + i < origDataLength);) {
|
|
254
|
+
if (origData[lastpos + i] == newData[lastscan + i])
|
|
255
|
+
score++;
|
|
256
|
+
i++;
|
|
257
|
+
if (score * 2 - i > best_score * 2 - best_length) {
|
|
258
|
+
best_score = score;
|
|
259
|
+
best_length = i;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return best_length;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* find the best backward match length */
|
|
266
|
+
static Py_ssize_t
|
|
267
|
+
backward_match_length(const char *origData, const char *newData,
|
|
268
|
+
Py_ssize_t lastscan, Py_ssize_t scan, Py_ssize_t pos)
|
|
269
|
+
{
|
|
270
|
+
Py_ssize_t i, score = 0, best_score = 0, best_length = 0;
|
|
271
|
+
|
|
272
|
+
for (i = 1; (scan >= lastscan + i) && (pos >= i); i++) {
|
|
273
|
+
if (origData[pos - i] == newData[scan - i])
|
|
274
|
+
score++;
|
|
275
|
+
if (score * 2 - i > best_score * 2 - best_length) {
|
|
276
|
+
best_score = score;
|
|
277
|
+
best_length = i;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return best_length;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* resolve overlap between forward and backward matches */
|
|
284
|
+
static void
|
|
285
|
+
adjust_overlap(const char *origData, const char *newData,
|
|
286
|
+
Py_ssize_t lastscan, Py_ssize_t lastpos, Py_ssize_t scan,
|
|
287
|
+
Py_ssize_t pos, Py_ssize_t *lenf, Py_ssize_t *lenb)
|
|
288
|
+
{
|
|
289
|
+
Py_ssize_t i, score = 0, best_score = 0, best_length = 0;
|
|
290
|
+
Py_ssize_t overlap;
|
|
291
|
+
|
|
292
|
+
if (lastscan + *lenf <= scan - *lenb)
|
|
293
|
+
return;
|
|
294
|
+
overlap = (lastscan + *lenf) - (scan - *lenb);
|
|
295
|
+
|
|
296
|
+
for (i = 0; i < overlap; i++) {
|
|
297
|
+
if (newData[lastscan + *lenf - overlap + i] ==
|
|
298
|
+
origData[lastpos + *lenf - overlap + i])
|
|
299
|
+
score++;
|
|
300
|
+
if (newData[scan - *lenb + i] == origData[pos - *lenb + i])
|
|
301
|
+
score--;
|
|
302
|
+
if (score > best_score) {
|
|
303
|
+
best_score = score;
|
|
304
|
+
best_length = i + 1;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
*lenf += best_length - overlap;
|
|
309
|
+
*lenb -= best_length;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* construct the control, diff and extra blocks */
|
|
313
|
+
static int
|
|
314
|
+
compute_diff(char *origData, Py_ssize_t origDataLength,
|
|
315
|
+
char *newData, Py_ssize_t newDataLength, DiffResult *result)
|
|
316
|
+
{
|
|
317
|
+
Py_ssize_t lastscan, lastpos, lastoffset, oldscore, scsc;
|
|
318
|
+
Py_ssize_t *I = NULL, *V = NULL;
|
|
319
|
+
Py_ssize_t scan, pos, len, lenf, lenb, i;
|
|
320
|
+
|
|
321
|
+
result->controls = PyList_New(0);
|
|
322
|
+
if (!result->controls)
|
|
323
|
+
goto error;
|
|
324
|
+
|
|
325
|
+
/* perform sort on original data */
|
|
326
|
+
I = PyMem_Malloc((origDataLength + 1) * sizeof(Py_ssize_t));
|
|
327
|
+
if (!I) {
|
|
328
|
+
PyErr_NoMemory();
|
|
329
|
+
goto error;
|
|
330
|
+
}
|
|
331
|
+
V = PyMem_Malloc((origDataLength + 1) * sizeof(Py_ssize_t));
|
|
332
|
+
if (!V) {
|
|
333
|
+
PyErr_NoMemory();
|
|
334
|
+
goto error;
|
|
335
|
+
}
|
|
336
|
+
Py_BEGIN_ALLOW_THREADS /* release GIL */
|
|
337
|
+
qsufsort(I, V, (unsigned char *) origData, origDataLength);
|
|
338
|
+
Py_END_ALLOW_THREADS
|
|
339
|
+
PyMem_Free(V);
|
|
340
|
+
V = NULL;
|
|
341
|
+
|
|
342
|
+
/* allocate memory for the diff and extra blocks */
|
|
343
|
+
result->diff = PyMem_Malloc(newDataLength + 1);
|
|
344
|
+
if (!result->diff) {
|
|
345
|
+
PyErr_NoMemory();
|
|
346
|
+
goto error;
|
|
347
|
+
}
|
|
348
|
+
result->extra = PyMem_Malloc(newDataLength + 1);
|
|
349
|
+
if (!result->extra) {
|
|
350
|
+
PyErr_NoMemory();
|
|
351
|
+
goto error;
|
|
352
|
+
}
|
|
353
|
+
result->diff_len = 0;
|
|
354
|
+
result->extra_len = 0;
|
|
355
|
+
|
|
356
|
+
/* perform the diff */
|
|
357
|
+
len = 0;
|
|
358
|
+
scan = 0;
|
|
359
|
+
lastscan = 0;
|
|
360
|
+
lastpos = 0;
|
|
361
|
+
lastoffset = 0;
|
|
362
|
+
pos = 0;
|
|
363
|
+
while (scan < newDataLength) {
|
|
364
|
+
if (PyErr_CheckSignals() < 0)
|
|
365
|
+
goto error;
|
|
366
|
+
oldscore = 0;
|
|
367
|
+
|
|
368
|
+
Py_BEGIN_ALLOW_THREADS /* release GIL */
|
|
369
|
+
for (scsc = scan += len; scan < newDataLength; scan++) {
|
|
370
|
+
len = search(I, (unsigned char *) origData, origDataLength,
|
|
371
|
+
(unsigned char *) newData + scan,
|
|
372
|
+
newDataLength - scan, 0, origDataLength, &pos);
|
|
373
|
+
for (; scsc < scan + len; scsc++)
|
|
374
|
+
if ((scsc + lastoffset < origDataLength) &&
|
|
375
|
+
(origData[scsc + lastoffset] == newData[scsc]))
|
|
376
|
+
oldscore++;
|
|
377
|
+
if (((len == oldscore) && (len != 0)) || (len > oldscore + 8))
|
|
378
|
+
break;
|
|
379
|
+
if ((scan + lastoffset < origDataLength) &&
|
|
380
|
+
(origData[scan + lastoffset] == newData[scan]))
|
|
381
|
+
oldscore--;
|
|
382
|
+
}
|
|
383
|
+
Py_END_ALLOW_THREADS
|
|
384
|
+
|
|
385
|
+
if ((len != oldscore) || (scan == newDataLength)) {
|
|
386
|
+
lenf = forward_match_length(origData, origDataLength, newData,
|
|
387
|
+
lastscan, lastpos, scan);
|
|
388
|
+
lenb = 0;
|
|
389
|
+
if (scan < newDataLength)
|
|
390
|
+
lenb = backward_match_length(origData, newData,
|
|
391
|
+
lastscan, scan, pos);
|
|
392
|
+
adjust_overlap(origData, newData, lastscan, lastpos, scan, pos,
|
|
393
|
+
&lenf, &lenb);
|
|
394
|
+
|
|
395
|
+
for (i = 0; i < lenf; i++)
|
|
396
|
+
result->diff[result->diff_len + i] =
|
|
397
|
+
newData[lastscan + i] - origData[lastpos + i];
|
|
398
|
+
for (i = 0; i < (scan - lenb) - (lastscan + lenf); i++)
|
|
399
|
+
result->extra[result->extra_len + i] =
|
|
400
|
+
newData[lastscan + lenf + i];
|
|
401
|
+
|
|
402
|
+
result->diff_len += lenf;
|
|
403
|
+
result->extra_len += (scan - lenb) - (lastscan + lenf);
|
|
404
|
+
|
|
405
|
+
if (append_control(result->controls,
|
|
406
|
+
lenf,
|
|
407
|
+
(scan - lenb) - (lastscan + lenf),
|
|
408
|
+
(pos - lenb) - (lastpos + lenf)) < 0)
|
|
409
|
+
goto error;
|
|
410
|
+
|
|
411
|
+
lastscan = scan - lenb;
|
|
412
|
+
lastpos = pos - lenb;
|
|
413
|
+
lastoffset = pos - scan;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
PyMem_Free(I);
|
|
418
|
+
return 0;
|
|
419
|
+
|
|
420
|
+
error:
|
|
421
|
+
PyMem_Free(I);
|
|
422
|
+
PyMem_Free(V);
|
|
423
|
+
return -1;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* convert a diff result into Python objects */
|
|
427
|
+
static PyObject *
|
|
428
|
+
build_diff_result(DiffResult *result)
|
|
429
|
+
{
|
|
430
|
+
PyObject *diff, *extra, *res;
|
|
431
|
+
|
|
432
|
+
diff = PyBytes_FromStringAndSize((char *) result->diff, result->diff_len);
|
|
433
|
+
if (!diff)
|
|
434
|
+
return NULL;
|
|
435
|
+
extra = PyBytes_FromStringAndSize((char *) result->extra,
|
|
436
|
+
result->extra_len);
|
|
437
|
+
if (!extra) {
|
|
438
|
+
Py_DECREF(diff);
|
|
439
|
+
return NULL;
|
|
440
|
+
}
|
|
441
|
+
res = PyTuple_Pack(3, result->controls, diff, extra);
|
|
442
|
+
Py_DECREF(diff);
|
|
443
|
+
Py_DECREF(extra);
|
|
444
|
+
return res;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* return the control, diff and extra blocks for two byte strings */
|
|
448
|
+
static PyObject *
|
|
449
|
+
diff(PyObject *self, PyObject *args)
|
|
450
|
+
{
|
|
451
|
+
DiffResult result = {NULL, NULL, NULL, 0, 0};
|
|
452
|
+
Py_ssize_t origDataLength, newDataLength;
|
|
453
|
+
char *origData, *newData;
|
|
454
|
+
PyObject *res;
|
|
455
|
+
|
|
456
|
+
if (!PyArg_ParseTuple(args, "s#s#",
|
|
457
|
+
&origData, &origDataLength,
|
|
458
|
+
&newData, &newDataLength))
|
|
459
|
+
return NULL;
|
|
460
|
+
|
|
461
|
+
if (compute_diff(origData, origDataLength,
|
|
462
|
+
newData, newDataLength, &result) < 0)
|
|
463
|
+
res = NULL;
|
|
464
|
+
else
|
|
465
|
+
res = build_diff_result(&result);
|
|
466
|
+
clear_diff_result(&result);
|
|
467
|
+
return res;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/* update the source offset while checking for overflow */
|
|
471
|
+
static int
|
|
472
|
+
update_oldpos(long long *oldpos, long long increment)
|
|
473
|
+
{
|
|
474
|
+
if ((increment > 0 && *oldpos > PY_LLONG_MAX - increment) ||
|
|
475
|
+
(increment < 0 && *oldpos < PY_LLONG_MIN - increment)) {
|
|
476
|
+
PyErr_SetString(PyExc_ValueError, "corrupt patch (offset overflow)");
|
|
477
|
+
return -1;
|
|
478
|
+
}
|
|
479
|
+
*oldpos += increment;
|
|
480
|
+
return 0;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* apply control, diff and extra blocks to the original data */
|
|
484
|
+
static PyObject *
|
|
485
|
+
patch(PyObject *self, PyObject *args)
|
|
486
|
+
{
|
|
487
|
+
char *origData, *newData, *diffBlock, *extraBlock;
|
|
488
|
+
Py_ssize_t origDataLength, newDataLength, diffBlockLength, extraBlockLength;
|
|
489
|
+
Py_ssize_t diffRemaining, extraRemaining;
|
|
490
|
+
PyObject *controlTuples, *tuple, *result;
|
|
491
|
+
Py_ssize_t newpos, x, y, i, j, numTuples;
|
|
492
|
+
long long oldpos, z;
|
|
493
|
+
|
|
494
|
+
if (!PyArg_ParseTuple(args, "s#nO!s#s#",
|
|
495
|
+
&origData, &origDataLength, &newDataLength,
|
|
496
|
+
&PyList_Type, &controlTuples,
|
|
497
|
+
&diffBlock, &diffBlockLength,
|
|
498
|
+
&extraBlock, &extraBlockLength))
|
|
499
|
+
return NULL;
|
|
500
|
+
|
|
501
|
+
if (newDataLength < 0) {
|
|
502
|
+
PyErr_SetString(PyExc_ValueError, "negative new data length");
|
|
503
|
+
return NULL;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
result = PyBytes_FromStringAndSize(NULL, newDataLength);
|
|
507
|
+
if (!result)
|
|
508
|
+
return NULL;
|
|
509
|
+
newData = PyBytes_AS_STRING(result);
|
|
510
|
+
|
|
511
|
+
oldpos = 0;
|
|
512
|
+
newpos = 0;
|
|
513
|
+
diffRemaining = diffBlockLength;
|
|
514
|
+
extraRemaining = extraBlockLength;
|
|
515
|
+
numTuples = PyList_GET_SIZE(controlTuples);
|
|
516
|
+
for (i = 0; i < numTuples; i++) {
|
|
517
|
+
tuple = PyList_GET_ITEM(controlTuples, i);
|
|
518
|
+
if (!PyTuple_Check(tuple)) {
|
|
519
|
+
PyErr_SetString(PyExc_TypeError, "expecting tuple");
|
|
520
|
+
goto error;
|
|
521
|
+
}
|
|
522
|
+
if (!PyArg_ParseTuple(tuple, "nnL", &x, &y, &z))
|
|
523
|
+
goto error;
|
|
524
|
+
if (x < 0 || x > newDataLength - newpos || x > diffRemaining) {
|
|
525
|
+
PyErr_SetString(PyExc_ValueError, "corrupt patch (overflow)");
|
|
526
|
+
goto error;
|
|
527
|
+
}
|
|
528
|
+
memcpy(newData + newpos, diffBlock, x);
|
|
529
|
+
diffBlock += x;
|
|
530
|
+
diffRemaining -= x;
|
|
531
|
+
/* Check bounds without overflowing oldpos + j. */
|
|
532
|
+
for (j = 0; j < x; j++)
|
|
533
|
+
if (oldpos >= -j && oldpos < origDataLength - j)
|
|
534
|
+
newData[newpos + j] += origData[oldpos + j];
|
|
535
|
+
newpos += x;
|
|
536
|
+
if (update_oldpos(&oldpos, x) < 0)
|
|
537
|
+
goto error;
|
|
538
|
+
if (y < 0 || y > newDataLength - newpos || y > extraRemaining) {
|
|
539
|
+
PyErr_SetString(PyExc_ValueError, "corrupt patch (overflow)");
|
|
540
|
+
goto error;
|
|
541
|
+
}
|
|
542
|
+
memcpy(newData + newpos, extraBlock, y);
|
|
543
|
+
extraBlock += y;
|
|
544
|
+
extraRemaining -= y;
|
|
545
|
+
newpos += y;
|
|
546
|
+
if (update_oldpos(&oldpos, z) < 0)
|
|
547
|
+
goto error;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* confirm that a valid patch was applied */
|
|
551
|
+
if (newpos != newDataLength ||
|
|
552
|
+
diffRemaining != 0 || extraRemaining != 0) {
|
|
553
|
+
PyErr_SetString(PyExc_ValueError, "corrupt patch (underflow)");
|
|
554
|
+
goto error;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return result;
|
|
558
|
+
|
|
559
|
+
error:
|
|
560
|
+
Py_DECREF(result);
|
|
561
|
+
return NULL;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
/* encode an integer as 8 bytes */
|
|
566
|
+
static PyObject *
|
|
567
|
+
encode_int64(PyObject *self, PyObject *value)
|
|
568
|
+
{
|
|
569
|
+
PY_INT64_T x;
|
|
570
|
+
char str[8], sign = 0x00;
|
|
571
|
+
int i;
|
|
572
|
+
|
|
573
|
+
x = PyLong_AsLongLong(value);
|
|
574
|
+
if (x == -1 && PyErr_Occurred())
|
|
575
|
+
return NULL;
|
|
576
|
+
|
|
577
|
+
if (x == PY_LLONG_MIN) {
|
|
578
|
+
PyErr_SetString(PyExc_OverflowError, "cannot encode -2**63");
|
|
579
|
+
return NULL;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (x < 0) {
|
|
583
|
+
x = -x;
|
|
584
|
+
sign = 0x80;
|
|
585
|
+
}
|
|
586
|
+
for (i = 0; i < 8; i++) {
|
|
587
|
+
str[i] = x & 0xff;
|
|
588
|
+
x >>= 8;
|
|
589
|
+
}
|
|
590
|
+
str[7] |= sign;
|
|
591
|
+
return PyBytes_FromStringAndSize(str, 8);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/* decode an integer from 8 bytes */
|
|
595
|
+
static PyObject *
|
|
596
|
+
decode_int64(PyObject *self, PyObject *bytes)
|
|
597
|
+
{
|
|
598
|
+
PY_UINT64_T value = 0;
|
|
599
|
+
PY_INT64_T magnitude;
|
|
600
|
+
Py_ssize_t size;
|
|
601
|
+
char *str;
|
|
602
|
+
int i;
|
|
603
|
+
|
|
604
|
+
if (PyBytes_AsStringAndSize(bytes, &str, &size) < 0)
|
|
605
|
+
return NULL;
|
|
606
|
+
if (size != 8) {
|
|
607
|
+
PyErr_SetString(PyExc_ValueError, "8 bytes expected");
|
|
608
|
+
return NULL;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
for (i = 0; i < 8; i++)
|
|
612
|
+
value |= ((PY_UINT64_T) (unsigned char) str[i]) << (i * 8);
|
|
613
|
+
|
|
614
|
+
magnitude = value & PY_LLONG_MAX;
|
|
615
|
+
return PyLong_FromLongLong(value >> 63 ? -magnitude : magnitude);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
/* declaration of methods supported by this module */
|
|
620
|
+
static PyMethodDef module_functions[] = {
|
|
621
|
+
{"diff", diff, METH_VARARGS},
|
|
622
|
+
{"patch", patch, METH_VARARGS},
|
|
623
|
+
{"encode_int64", encode_int64, METH_O},
|
|
624
|
+
{"decode_int64", decode_int64, METH_O},
|
|
625
|
+
{NULL} /* Sentinel */
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
static PyModuleDef moduledef = {
|
|
629
|
+
PyModuleDef_HEAD_INIT, "core", 0, -1, module_functions,
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
/* initialize extension module */
|
|
633
|
+
PyMODINIT_FUNC
|
|
634
|
+
PyInit_core(void)
|
|
635
|
+
{
|
|
636
|
+
PyObject *m;
|
|
637
|
+
|
|
638
|
+
m = PyModule_Create(&moduledef);
|
|
639
|
+
if (m == NULL)
|
|
640
|
+
return NULL;
|
|
641
|
+
|
|
642
|
+
return m;
|
|
643
|
+
}
|