##// END OF EJS Templates
mpatch: unify mpatchError (issue5182)...
timeless -
r28782:f736f98e default
parent child Browse files
Show More
@@ -1,420 +1,422
1 /*
1 /*
2 mpatch.c - efficient binary patching for Mercurial
2 mpatch.c - efficient binary patching for Mercurial
3
3
4 This implements a patch algorithm that's O(m + nlog n) where m is the
4 This implements a patch algorithm that's O(m + nlog n) where m is the
5 size of the output and n is the number of patches.
5 size of the output and n is the number of patches.
6
6
7 Given a list of binary patches, it unpacks each into a hunk list,
7 Given a list of binary patches, it unpacks each into a hunk list,
8 then combines the hunk lists with a treewise recursion to form a
8 then combines the hunk lists with a treewise recursion to form a
9 single hunk list. This hunk list is then applied to the original
9 single hunk list. This hunk list is then applied to the original
10 text.
10 text.
11
11
12 The text (or binary) fragments are copied directly from their source
12 The text (or binary) fragments are copied directly from their source
13 Python objects into a preallocated output string to avoid the
13 Python objects into a preallocated output string to avoid the
14 allocation of intermediate Python objects. Working memory is about 2x
14 allocation of intermediate Python objects. Working memory is about 2x
15 the total number of hunks.
15 the total number of hunks.
16
16
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
18
18
19 This software may be used and distributed according to the terms
19 This software may be used and distributed according to the terms
20 of the GNU General Public License, incorporated herein by reference.
20 of the GNU General Public License, incorporated herein by reference.
21 */
21 */
22
22
23 #define PY_SSIZE_T_CLEAN
23 #define PY_SSIZE_T_CLEAN
24 #include <Python.h>
24 #include <Python.h>
25 #include <stdlib.h>
25 #include <stdlib.h>
26 #include <string.h>
26 #include <string.h>
27
27
28 #include "util.h"
28 #include "util.h"
29
29
30 static char mpatch_doc[] = "Efficient binary patching.";
30 static char mpatch_doc[] = "Efficient binary patching.";
31 static PyObject *mpatch_Error;
31 static PyObject *mpatch_Error;
32
32
33 struct frag {
33 struct frag {
34 int start, end, len;
34 int start, end, len;
35 const char *data;
35 const char *data;
36 };
36 };
37
37
38 struct flist {
38 struct flist {
39 struct frag *base, *head, *tail;
39 struct frag *base, *head, *tail;
40 };
40 };
41
41
42 static struct flist *lalloc(Py_ssize_t size)
42 static struct flist *lalloc(Py_ssize_t size)
43 {
43 {
44 struct flist *a = NULL;
44 struct flist *a = NULL;
45
45
46 if (size < 1)
46 if (size < 1)
47 size = 1;
47 size = 1;
48
48
49 a = (struct flist *)malloc(sizeof(struct flist));
49 a = (struct flist *)malloc(sizeof(struct flist));
50 if (a) {
50 if (a) {
51 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
51 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
52 if (a->base) {
52 if (a->base) {
53 a->head = a->tail = a->base;
53 a->head = a->tail = a->base;
54 return a;
54 return a;
55 }
55 }
56 free(a);
56 free(a);
57 a = NULL;
57 a = NULL;
58 }
58 }
59 if (!PyErr_Occurred())
59 if (!PyErr_Occurred())
60 PyErr_NoMemory();
60 PyErr_NoMemory();
61 return NULL;
61 return NULL;
62 }
62 }
63
63
64 static void lfree(struct flist *a)
64 static void lfree(struct flist *a)
65 {
65 {
66 if (a) {
66 if (a) {
67 free(a->base);
67 free(a->base);
68 free(a);
68 free(a);
69 }
69 }
70 }
70 }
71
71
72 static Py_ssize_t lsize(struct flist *a)
72 static Py_ssize_t lsize(struct flist *a)
73 {
73 {
74 return a->tail - a->head;
74 return a->tail - a->head;
75 }
75 }
76
76
77 /* move hunks in source that are less cut to dest, compensating
77 /* move hunks in source that are less cut to dest, compensating
78 for changes in offset. the last hunk may be split if necessary.
78 for changes in offset. the last hunk may be split if necessary.
79 */
79 */
80 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
80 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
81 {
81 {
82 struct frag *d = dest->tail, *s = src->head;
82 struct frag *d = dest->tail, *s = src->head;
83 int postend, c, l;
83 int postend, c, l;
84
84
85 while (s != src->tail) {
85 while (s != src->tail) {
86 if (s->start + offset >= cut)
86 if (s->start + offset >= cut)
87 break; /* we've gone far enough */
87 break; /* we've gone far enough */
88
88
89 postend = offset + s->start + s->len;
89 postend = offset + s->start + s->len;
90 if (postend <= cut) {
90 if (postend <= cut) {
91 /* save this hunk */
91 /* save this hunk */
92 offset += s->start + s->len - s->end;
92 offset += s->start + s->len - s->end;
93 *d++ = *s++;
93 *d++ = *s++;
94 }
94 }
95 else {
95 else {
96 /* break up this hunk */
96 /* break up this hunk */
97 c = cut - offset;
97 c = cut - offset;
98 if (s->end < c)
98 if (s->end < c)
99 c = s->end;
99 c = s->end;
100 l = cut - offset - s->start;
100 l = cut - offset - s->start;
101 if (s->len < l)
101 if (s->len < l)
102 l = s->len;
102 l = s->len;
103
103
104 offset += s->start + l - c;
104 offset += s->start + l - c;
105
105
106 d->start = s->start;
106 d->start = s->start;
107 d->end = c;
107 d->end = c;
108 d->len = l;
108 d->len = l;
109 d->data = s->data;
109 d->data = s->data;
110 d++;
110 d++;
111 s->start = c;
111 s->start = c;
112 s->len = s->len - l;
112 s->len = s->len - l;
113 s->data = s->data + l;
113 s->data = s->data + l;
114
114
115 break;
115 break;
116 }
116 }
117 }
117 }
118
118
119 dest->tail = d;
119 dest->tail = d;
120 src->head = s;
120 src->head = s;
121 return offset;
121 return offset;
122 }
122 }
123
123
124 /* like gather, but with no output list */
124 /* like gather, but with no output list */
125 static int discard(struct flist *src, int cut, int offset)
125 static int discard(struct flist *src, int cut, int offset)
126 {
126 {
127 struct frag *s = src->head;
127 struct frag *s = src->head;
128 int postend, c, l;
128 int postend, c, l;
129
129
130 while (s != src->tail) {
130 while (s != src->tail) {
131 if (s->start + offset >= cut)
131 if (s->start + offset >= cut)
132 break;
132 break;
133
133
134 postend = offset + s->start + s->len;
134 postend = offset + s->start + s->len;
135 if (postend <= cut) {
135 if (postend <= cut) {
136 offset += s->start + s->len - s->end;
136 offset += s->start + s->len - s->end;
137 s++;
137 s++;
138 }
138 }
139 else {
139 else {
140 c = cut - offset;
140 c = cut - offset;
141 if (s->end < c)
141 if (s->end < c)
142 c = s->end;
142 c = s->end;
143 l = cut - offset - s->start;
143 l = cut - offset - s->start;
144 if (s->len < l)
144 if (s->len < l)
145 l = s->len;
145 l = s->len;
146
146
147 offset += s->start + l - c;
147 offset += s->start + l - c;
148 s->start = c;
148 s->start = c;
149 s->len = s->len - l;
149 s->len = s->len - l;
150 s->data = s->data + l;
150 s->data = s->data + l;
151
151
152 break;
152 break;
153 }
153 }
154 }
154 }
155
155
156 src->head = s;
156 src->head = s;
157 return offset;
157 return offset;
158 }
158 }
159
159
160 /* combine hunk lists a and b, while adjusting b for offset changes in a/
160 /* combine hunk lists a and b, while adjusting b for offset changes in a/
161 this deletes a and b and returns the resultant list. */
161 this deletes a and b and returns the resultant list. */
162 static struct flist *combine(struct flist *a, struct flist *b)
162 static struct flist *combine(struct flist *a, struct flist *b)
163 {
163 {
164 struct flist *c = NULL;
164 struct flist *c = NULL;
165 struct frag *bh, *ct;
165 struct frag *bh, *ct;
166 int offset = 0, post;
166 int offset = 0, post;
167
167
168 if (a && b)
168 if (a && b)
169 c = lalloc((lsize(a) + lsize(b)) * 2);
169 c = lalloc((lsize(a) + lsize(b)) * 2);
170
170
171 if (c) {
171 if (c) {
172
172
173 for (bh = b->head; bh != b->tail; bh++) {
173 for (bh = b->head; bh != b->tail; bh++) {
174 /* save old hunks */
174 /* save old hunks */
175 offset = gather(c, a, bh->start, offset);
175 offset = gather(c, a, bh->start, offset);
176
176
177 /* discard replaced hunks */
177 /* discard replaced hunks */
178 post = discard(a, bh->end, offset);
178 post = discard(a, bh->end, offset);
179
179
180 /* insert new hunk */
180 /* insert new hunk */
181 ct = c->tail;
181 ct = c->tail;
182 ct->start = bh->start - offset;
182 ct->start = bh->start - offset;
183 ct->end = bh->end - post;
183 ct->end = bh->end - post;
184 ct->len = bh->len;
184 ct->len = bh->len;
185 ct->data = bh->data;
185 ct->data = bh->data;
186 c->tail++;
186 c->tail++;
187 offset = post;
187 offset = post;
188 }
188 }
189
189
190 /* hold on to tail from a */
190 /* hold on to tail from a */
191 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
191 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
192 c->tail += lsize(a);
192 c->tail += lsize(a);
193 }
193 }
194
194
195 lfree(a);
195 lfree(a);
196 lfree(b);
196 lfree(b);
197 return c;
197 return c;
198 }
198 }
199
199
200 /* decode a binary patch into a hunk list */
200 /* decode a binary patch into a hunk list */
201 static struct flist *decode(const char *bin, Py_ssize_t len)
201 static struct flist *decode(const char *bin, Py_ssize_t len)
202 {
202 {
203 struct flist *l;
203 struct flist *l;
204 struct frag *lt;
204 struct frag *lt;
205 int pos = 0;
205 int pos = 0;
206
206
207 /* assume worst case size, we won't have many of these lists */
207 /* assume worst case size, we won't have many of these lists */
208 l = lalloc(len / 12 + 1);
208 l = lalloc(len / 12 + 1);
209 if (!l)
209 if (!l)
210 return NULL;
210 return NULL;
211
211
212 lt = l->tail;
212 lt = l->tail;
213
213
214 while (pos >= 0 && pos < len) {
214 while (pos >= 0 && pos < len) {
215 lt->start = getbe32(bin + pos);
215 lt->start = getbe32(bin + pos);
216 lt->end = getbe32(bin + pos + 4);
216 lt->end = getbe32(bin + pos + 4);
217 lt->len = getbe32(bin + pos + 8);
217 lt->len = getbe32(bin + pos + 8);
218 lt->data = bin + pos + 12;
218 lt->data = bin + pos + 12;
219 pos += 12 + lt->len;
219 pos += 12 + lt->len;
220 if (lt->start > lt->end || lt->len < 0)
220 if (lt->start > lt->end || lt->len < 0)
221 break; /* sanity check */
221 break; /* sanity check */
222 lt++;
222 lt++;
223 }
223 }
224
224
225 if (pos != len) {
225 if (pos != len) {
226 if (!PyErr_Occurred())
226 if (!PyErr_Occurred())
227 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
227 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
228 lfree(l);
228 lfree(l);
229 return NULL;
229 return NULL;
230 }
230 }
231
231
232 l->tail = lt;
232 l->tail = lt;
233 return l;
233 return l;
234 }
234 }
235
235
236 /* calculate the size of resultant text */
236 /* calculate the size of resultant text */
237 static Py_ssize_t calcsize(Py_ssize_t len, struct flist *l)
237 static Py_ssize_t calcsize(Py_ssize_t len, struct flist *l)
238 {
238 {
239 Py_ssize_t outlen = 0, last = 0;
239 Py_ssize_t outlen = 0, last = 0;
240 struct frag *f = l->head;
240 struct frag *f = l->head;
241
241
242 while (f != l->tail) {
242 while (f != l->tail) {
243 if (f->start < last || f->end > len) {
243 if (f->start < last || f->end > len) {
244 if (!PyErr_Occurred())
244 if (!PyErr_Occurred())
245 PyErr_SetString(mpatch_Error,
245 PyErr_SetString(mpatch_Error,
246 "invalid patch");
246 "invalid patch");
247 return -1;
247 return -1;
248 }
248 }
249 outlen += f->start - last;
249 outlen += f->start - last;
250 last = f->end;
250 last = f->end;
251 outlen += f->len;
251 outlen += f->len;
252 f++;
252 f++;
253 }
253 }
254
254
255 outlen += len - last;
255 outlen += len - last;
256 return outlen;
256 return outlen;
257 }
257 }
258
258
259 static int apply(char *buf, const char *orig, Py_ssize_t len, struct flist *l)
259 static int apply(char *buf, const char *orig, Py_ssize_t len, struct flist *l)
260 {
260 {
261 struct frag *f = l->head;
261 struct frag *f = l->head;
262 int last = 0;
262 int last = 0;
263 char *p = buf;
263 char *p = buf;
264
264
265 while (f != l->tail) {
265 while (f != l->tail) {
266 if (f->start < last || f->end > len) {
266 if (f->start < last || f->end > len) {
267 if (!PyErr_Occurred())
267 if (!PyErr_Occurred())
268 PyErr_SetString(mpatch_Error,
268 PyErr_SetString(mpatch_Error,
269 "invalid patch");
269 "invalid patch");
270 return 0;
270 return 0;
271 }
271 }
272 memcpy(p, orig + last, f->start - last);
272 memcpy(p, orig + last, f->start - last);
273 p += f->start - last;
273 p += f->start - last;
274 memcpy(p, f->data, f->len);
274 memcpy(p, f->data, f->len);
275 last = f->end;
275 last = f->end;
276 p += f->len;
276 p += f->len;
277 f++;
277 f++;
278 }
278 }
279 memcpy(p, orig + last, len - last);
279 memcpy(p, orig + last, len - last);
280 return 1;
280 return 1;
281 }
281 }
282
282
283 /* recursively generate a patch of all bins between start and end */
283 /* recursively generate a patch of all bins between start and end */
284 static struct flist *fold(PyObject *bins, Py_ssize_t start, Py_ssize_t end)
284 static struct flist *fold(PyObject *bins, Py_ssize_t start, Py_ssize_t end)
285 {
285 {
286 Py_ssize_t len, blen;
286 Py_ssize_t len, blen;
287 const char *buffer;
287 const char *buffer;
288
288
289 if (start + 1 == end) {
289 if (start + 1 == end) {
290 /* trivial case, output a decoded list */
290 /* trivial case, output a decoded list */
291 PyObject *tmp = PyList_GetItem(bins, start);
291 PyObject *tmp = PyList_GetItem(bins, start);
292 if (!tmp)
292 if (!tmp)
293 return NULL;
293 return NULL;
294 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
294 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
295 return NULL;
295 return NULL;
296 return decode(buffer, blen);
296 return decode(buffer, blen);
297 }
297 }
298
298
299 /* divide and conquer, memory management is elsewhere */
299 /* divide and conquer, memory management is elsewhere */
300 len = (end - start) / 2;
300 len = (end - start) / 2;
301 return combine(fold(bins, start, start + len),
301 return combine(fold(bins, start, start + len),
302 fold(bins, start + len, end));
302 fold(bins, start + len, end));
303 }
303 }
304
304
305 static PyObject *
305 static PyObject *
306 patches(PyObject *self, PyObject *args)
306 patches(PyObject *self, PyObject *args)
307 {
307 {
308 PyObject *text, *bins, *result;
308 PyObject *text, *bins, *result;
309 struct flist *patch;
309 struct flist *patch;
310 const char *in;
310 const char *in;
311 char *out;
311 char *out;
312 Py_ssize_t len, outlen, inlen;
312 Py_ssize_t len, outlen, inlen;
313
313
314 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
314 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
315 return NULL;
315 return NULL;
316
316
317 len = PyList_Size(bins);
317 len = PyList_Size(bins);
318 if (!len) {
318 if (!len) {
319 /* nothing to do */
319 /* nothing to do */
320 Py_INCREF(text);
320 Py_INCREF(text);
321 return text;
321 return text;
322 }
322 }
323
323
324 if (PyObject_AsCharBuffer(text, &in, &inlen))
324 if (PyObject_AsCharBuffer(text, &in, &inlen))
325 return NULL;
325 return NULL;
326
326
327 patch = fold(bins, 0, len);
327 patch = fold(bins, 0, len);
328 if (!patch)
328 if (!patch)
329 return NULL;
329 return NULL;
330
330
331 outlen = calcsize(inlen, patch);
331 outlen = calcsize(inlen, patch);
332 if (outlen < 0) {
332 if (outlen < 0) {
333 result = NULL;
333 result = NULL;
334 goto cleanup;
334 goto cleanup;
335 }
335 }
336 result = PyBytes_FromStringAndSize(NULL, outlen);
336 result = PyBytes_FromStringAndSize(NULL, outlen);
337 if (!result) {
337 if (!result) {
338 result = NULL;
338 result = NULL;
339 goto cleanup;
339 goto cleanup;
340 }
340 }
341 out = PyBytes_AsString(result);
341 out = PyBytes_AsString(result);
342 if (!apply(out, in, inlen, patch)) {
342 if (!apply(out, in, inlen, patch)) {
343 Py_DECREF(result);
343 Py_DECREF(result);
344 result = NULL;
344 result = NULL;
345 }
345 }
346 cleanup:
346 cleanup:
347 lfree(patch);
347 lfree(patch);
348 return result;
348 return result;
349 }
349 }
350
350
351 /* calculate size of a patched file directly */
351 /* calculate size of a patched file directly */
352 static PyObject *
352 static PyObject *
353 patchedsize(PyObject *self, PyObject *args)
353 patchedsize(PyObject *self, PyObject *args)
354 {
354 {
355 long orig, start, end, len, outlen = 0, last = 0, pos = 0;
355 long orig, start, end, len, outlen = 0, last = 0, pos = 0;
356 Py_ssize_t patchlen;
356 Py_ssize_t patchlen;
357 char *bin;
357 char *bin;
358
358
359 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
359 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
360 return NULL;
360 return NULL;
361
361
362 while (pos >= 0 && pos < patchlen) {
362 while (pos >= 0 && pos < patchlen) {
363 start = getbe32(bin + pos);
363 start = getbe32(bin + pos);
364 end = getbe32(bin + pos + 4);
364 end = getbe32(bin + pos + 4);
365 len = getbe32(bin + pos + 8);
365 len = getbe32(bin + pos + 8);
366 if (start > end)
366 if (start > end)
367 break; /* sanity check */
367 break; /* sanity check */
368 pos += 12 + len;
368 pos += 12 + len;
369 outlen += start - last;
369 outlen += start - last;
370 last = end;
370 last = end;
371 outlen += len;
371 outlen += len;
372 }
372 }
373
373
374 if (pos != patchlen) {
374 if (pos != patchlen) {
375 if (!PyErr_Occurred())
375 if (!PyErr_Occurred())
376 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
376 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
377 return NULL;
377 return NULL;
378 }
378 }
379
379
380 outlen += orig - last;
380 outlen += orig - last;
381 return Py_BuildValue("l", outlen);
381 return Py_BuildValue("l", outlen);
382 }
382 }
383
383
384 static PyMethodDef methods[] = {
384 static PyMethodDef methods[] = {
385 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
385 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
386 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
386 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
387 {NULL, NULL}
387 {NULL, NULL}
388 };
388 };
389
389
390 #ifdef IS_PY3K
390 #ifdef IS_PY3K
391 static struct PyModuleDef mpatch_module = {
391 static struct PyModuleDef mpatch_module = {
392 PyModuleDef_HEAD_INIT,
392 PyModuleDef_HEAD_INIT,
393 "mpatch",
393 "mpatch",
394 mpatch_doc,
394 mpatch_doc,
395 -1,
395 -1,
396 methods
396 methods
397 };
397 };
398
398
399 PyMODINIT_FUNC PyInit_mpatch(void)
399 PyMODINIT_FUNC PyInit_mpatch(void)
400 {
400 {
401 PyObject *m;
401 PyObject *m;
402
402
403 m = PyModule_Create(&mpatch_module);
403 m = PyModule_Create(&mpatch_module);
404 if (m == NULL)
404 if (m == NULL)
405 return NULL;
405 return NULL;
406
406
407 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
407 mpatch_Error = PyErr_NewException("mercurial.mpatch.mpatchError",
408 NULL, NULL);
408 Py_INCREF(mpatch_Error);
409 Py_INCREF(mpatch_Error);
409 PyModule_AddObject(m, "mpatchError", mpatch_Error);
410 PyModule_AddObject(m, "mpatchError", mpatch_Error);
410
411
411 return m;
412 return m;
412 }
413 }
413 #else
414 #else
414 PyMODINIT_FUNC
415 PyMODINIT_FUNC
415 initmpatch(void)
416 initmpatch(void)
416 {
417 {
417 Py_InitModule3("mpatch", methods, mpatch_doc);
418 Py_InitModule3("mpatch", methods, mpatch_doc);
418 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
419 mpatch_Error = PyErr_NewException("mercurial.mpatch.mpatchError",
420 NULL, NULL);
419 }
421 }
420 #endif
422 #endif
@@ -1,120 +1,127
1 # mpatch.py - Python implementation of mpatch.c
1 # mpatch.py - Python implementation of mpatch.c
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import cStringIO
10 import cStringIO
11 import struct
11 import struct
12
12
13 StringIO = cStringIO.StringIO
13 StringIO = cStringIO.StringIO
14
14
15 class mpatchError(Exception):
16 """error raised when a delta cannot be decoded
17 """
18
15 # This attempts to apply a series of patches in time proportional to
19 # This attempts to apply a series of patches in time proportional to
16 # the total size of the patches, rather than patches * len(text). This
20 # the total size of the patches, rather than patches * len(text). This
17 # means rather than shuffling strings around, we shuffle around
21 # means rather than shuffling strings around, we shuffle around
18 # pointers to fragments with fragment lists.
22 # pointers to fragments with fragment lists.
19 #
23 #
20 # When the fragment lists get too long, we collapse them. To do this
24 # When the fragment lists get too long, we collapse them. To do this
21 # efficiently, we do all our operations inside a buffer created by
25 # efficiently, we do all our operations inside a buffer created by
22 # mmap and simply use memmove. This avoids creating a bunch of large
26 # mmap and simply use memmove. This avoids creating a bunch of large
23 # temporary string buffers.
27 # temporary string buffers.
24
28
25 def _pull(dst, src, l): # pull l bytes from src
29 def _pull(dst, src, l): # pull l bytes from src
26 while l:
30 while l:
27 f = src.pop()
31 f = src.pop()
28 if f[0] > l: # do we need to split?
32 if f[0] > l: # do we need to split?
29 src.append((f[0] - l, f[1] + l))
33 src.append((f[0] - l, f[1] + l))
30 dst.append((l, f[1]))
34 dst.append((l, f[1]))
31 return
35 return
32 dst.append(f)
36 dst.append(f)
33 l -= f[0]
37 l -= f[0]
34
38
35 def _move(m, dest, src, count):
39 def _move(m, dest, src, count):
36 """move count bytes from src to dest
40 """move count bytes from src to dest
37
41
38 The file pointer is left at the end of dest.
42 The file pointer is left at the end of dest.
39 """
43 """
40 m.seek(src)
44 m.seek(src)
41 buf = m.read(count)
45 buf = m.read(count)
42 m.seek(dest)
46 m.seek(dest)
43 m.write(buf)
47 m.write(buf)
44
48
45 def _collect(m, buf, list):
49 def _collect(m, buf, list):
46 start = buf
50 start = buf
47 for l, p in reversed(list):
51 for l, p in reversed(list):
48 _move(m, buf, p, l)
52 _move(m, buf, p, l)
49 buf += l
53 buf += l
50 return (buf - start, start)
54 return (buf - start, start)
51
55
52 def patches(a, bins):
56 def patches(a, bins):
53 if not bins:
57 if not bins:
54 return a
58 return a
55
59
56 plens = [len(x) for x in bins]
60 plens = [len(x) for x in bins]
57 pl = sum(plens)
61 pl = sum(plens)
58 bl = len(a) + pl
62 bl = len(a) + pl
59 tl = bl + bl + pl # enough for the patches and two working texts
63 tl = bl + bl + pl # enough for the patches and two working texts
60 b1, b2 = 0, bl
64 b1, b2 = 0, bl
61
65
62 if not tl:
66 if not tl:
63 return a
67 return a
64
68
65 m = StringIO()
69 m = StringIO()
66
70
67 # load our original text
71 # load our original text
68 m.write(a)
72 m.write(a)
69 frags = [(len(a), b1)]
73 frags = [(len(a), b1)]
70
74
71 # copy all the patches into our segment so we can memmove from them
75 # copy all the patches into our segment so we can memmove from them
72 pos = b2 + bl
76 pos = b2 + bl
73 m.seek(pos)
77 m.seek(pos)
74 for p in bins: m.write(p)
78 for p in bins: m.write(p)
75
79
76 for plen in plens:
80 for plen in plens:
77 # if our list gets too long, execute it
81 # if our list gets too long, execute it
78 if len(frags) > 128:
82 if len(frags) > 128:
79 b2, b1 = b1, b2
83 b2, b1 = b1, b2
80 frags = [_collect(m, b1, frags)]
84 frags = [_collect(m, b1, frags)]
81
85
82 new = []
86 new = []
83 end = pos + plen
87 end = pos + plen
84 last = 0
88 last = 0
85 while pos < end:
89 while pos < end:
86 m.seek(pos)
90 m.seek(pos)
91 try:
87 p1, p2, l = struct.unpack(">lll", m.read(12))
92 p1, p2, l = struct.unpack(">lll", m.read(12))
93 except struct.error:
94 raise mpatchError("patch cannot be decoded")
88 _pull(new, frags, p1 - last) # what didn't change
95 _pull(new, frags, p1 - last) # what didn't change
89 _pull([], frags, p2 - p1) # what got deleted
96 _pull([], frags, p2 - p1) # what got deleted
90 new.append((l, pos + 12)) # what got added
97 new.append((l, pos + 12)) # what got added
91 pos += l + 12
98 pos += l + 12
92 last = p2
99 last = p2
93 frags.extend(reversed(new)) # what was left at the end
100 frags.extend(reversed(new)) # what was left at the end
94
101
95 t = _collect(m, b2, frags)
102 t = _collect(m, b2, frags)
96
103
97 m.seek(t[1])
104 m.seek(t[1])
98 return m.read(t[0])
105 return m.read(t[0])
99
106
100 def patchedsize(orig, delta):
107 def patchedsize(orig, delta):
101 outlen, last, bin = 0, 0, 0
108 outlen, last, bin = 0, 0, 0
102 binend = len(delta)
109 binend = len(delta)
103 data = 12
110 data = 12
104
111
105 while data <= binend:
112 while data <= binend:
106 decode = delta[bin:bin + 12]
113 decode = delta[bin:bin + 12]
107 start, end, length = struct.unpack(">lll", decode)
114 start, end, length = struct.unpack(">lll", decode)
108 if start > end:
115 if start > end:
109 break
116 break
110 bin = data + length
117 bin = data + length
111 data = bin + 12
118 data = bin + 12
112 outlen += start - last
119 outlen += start - last
113 last = end
120 last = end
114 outlen += length
121 outlen += length
115
122
116 if bin != binend:
123 if bin != binend:
117 raise ValueError("patch cannot be decoded")
124 raise mpatchError("patch cannot be decoded")
118
125
119 outlen += orig - last
126 outlen += orig - last
120 return outlen
127 return outlen
@@ -1,15 +1,15
1 Test for CVE-2016-3630
1 Test for CVE-2016-3630
2
2
3 $ hg init
3 $ hg init
4
4
5 >>> open("a.i", "w").write(
5 >>> open("a.i", "w").write(
6 ... """eJxjYGZgZIAAYQYGxhgom+k/FMx8YKx9ZUaKSOyqo4cnuKb8mbqHV5cBCVTMWb1Cwqkhe4Gsg9AD
6 ... """eJxjYGZgZIAAYQYGxhgom+k/FMx8YKx9ZUaKSOyqo4cnuKb8mbqHV5cBCVTMWb1Cwqkhe4Gsg9AD
7 ... Joa3dYtcYYYBAQ8Qr4OqZAYRICPTSr5WKd/42rV36d+8/VmrNpv7NP1jQAXrQE4BqQUARngwVA=="""
7 ... Joa3dYtcYYYBAQ8Qr4OqZAYRICPTSr5WKd/42rV36d+8/VmrNpv7NP1jQAXrQE4BqQUARngwVA=="""
8 ... .decode("base64").decode("zlib"))
8 ... .decode("base64").decode("zlib"))
9
9
10 $ hg debugindex a.i
10 $ hg debugindex a.i
11 rev offset length delta linkrev nodeid p1 p2
11 rev offset length delta linkrev nodeid p1 p2
12 0 0 19 -1 2 99e0332bd498 000000000000 000000000000
12 0 0 19 -1 2 99e0332bd498 000000000000 000000000000
13 1 19 12 0 3 6674f57a23d8 99e0332bd498 000000000000
13 1 19 12 0 3 6674f57a23d8 99e0332bd498 000000000000
14 $ hg debugdata a.i 1 2>&1 | grep decoded
14 $ hg debugdata a.i 1 2>&1 | egrep 'Error:.*decoded'
15 mpatch.mpatchError: patch cannot be decoded
15 mercurial.mpatch.mpatchError: patch cannot be decoded
General Comments 0
You need to be logged in to leave comments. Login now