##// END OF EJS Templates
Set correct exception for another possible malloc error in mpatch.c
Thomas Arendsen Hein -
r2048:8f9660c5 default
parent child Browse files
Show More
@@ -1,368 +1,368 b''
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 Matt Mackall <mpm@selenic.com>
17 Copyright 2005 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 #include <Python.h>
23 #include <Python.h>
24 #include <stdlib.h>
24 #include <stdlib.h>
25 #include <string.h>
25 #include <string.h>
26 #ifdef _WIN32
26 #ifdef _WIN32
27 #ifdef _MSC_VER
27 #ifdef _MSC_VER
28 #define inline __inline
28 #define inline __inline
29 typedef unsigned long uint32_t;
29 typedef unsigned long uint32_t;
30 #else
30 #else
31 #include <stdint.h>
31 #include <stdint.h>
32 #endif
32 #endif
33 static uint32_t ntohl(uint32_t x)
33 static uint32_t ntohl(uint32_t x)
34 {
34 {
35 return ((x & 0x000000ffUL) << 24) |
35 return ((x & 0x000000ffUL) << 24) |
36 ((x & 0x0000ff00UL) << 8) |
36 ((x & 0x0000ff00UL) << 8) |
37 ((x & 0x00ff0000UL) >> 8) |
37 ((x & 0x00ff0000UL) >> 8) |
38 ((x & 0xff000000UL) >> 24);
38 ((x & 0xff000000UL) >> 24);
39 }
39 }
40 #else
40 #else
41 #include <sys/types.h>
41 #include <sys/types.h>
42 #include <arpa/inet.h>
42 #include <arpa/inet.h>
43 #endif
43 #endif
44
44
45 static char mpatch_doc[] = "Efficient binary patching.";
45 static char mpatch_doc[] = "Efficient binary patching.";
46 static PyObject *mpatch_Error;
46 static PyObject *mpatch_Error;
47
47
48 struct frag {
48 struct frag {
49 int start, end, len;
49 int start, end, len;
50 char *data;
50 char *data;
51 };
51 };
52
52
53 struct flist {
53 struct flist {
54 struct frag *base, *head, *tail;
54 struct frag *base, *head, *tail;
55 };
55 };
56
56
57 static struct flist *lalloc(int size)
57 static struct flist *lalloc(int size)
58 {
58 {
59 struct flist *a = NULL;
59 struct flist *a = NULL;
60
60
61 a = (struct flist *)malloc(sizeof(struct flist));
61 a = (struct flist *)malloc(sizeof(struct flist));
62 if (a) {
62 if (a) {
63 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
63 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
64 if (!a->base) {
64 if (a->base) {
65 free(a);
66 a = NULL;
67 } else
68 a->head = a->tail = a->base;
65 a->head = a->tail = a->base;
69 return a;
66 return a;
67 }
68 free(a);
69 a = NULL;
70 }
70 }
71 if (!PyErr_Occurred())
71 if (!PyErr_Occurred())
72 PyErr_NoMemory();
72 PyErr_NoMemory();
73 return NULL;
73 return NULL;
74 }
74 }
75
75
76 static void lfree(struct flist *a)
76 static void lfree(struct flist *a)
77 {
77 {
78 if (a) {
78 if (a) {
79 free(a->base);
79 free(a->base);
80 free(a);
80 free(a);
81 }
81 }
82 }
82 }
83
83
84 static int lsize(struct flist *a)
84 static int lsize(struct flist *a)
85 {
85 {
86 return a->tail - a->head;
86 return a->tail - a->head;
87 }
87 }
88
88
89 /* move hunks in source that are less cut to dest, compensating
89 /* move hunks in source that are less cut to dest, compensating
90 for changes in offset. the last hunk may be split if necessary.
90 for changes in offset. the last hunk may be split if necessary.
91 */
91 */
92 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
92 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
93 {
93 {
94 struct frag *d = dest->tail, *s = src->head;
94 struct frag *d = dest->tail, *s = src->head;
95 int postend, c, l;
95 int postend, c, l;
96
96
97 while (s != src->tail) {
97 while (s != src->tail) {
98 if (s->start + offset >= cut)
98 if (s->start + offset >= cut)
99 break; /* we've gone far enough */
99 break; /* we've gone far enough */
100
100
101 postend = offset + s->start + s->len;
101 postend = offset + s->start + s->len;
102 if (postend <= cut) {
102 if (postend <= cut) {
103 /* save this hunk */
103 /* save this hunk */
104 offset += s->start + s->len - s->end;
104 offset += s->start + s->len - s->end;
105 *d++ = *s++;
105 *d++ = *s++;
106 }
106 }
107 else {
107 else {
108 /* break up this hunk */
108 /* break up this hunk */
109 c = cut - offset;
109 c = cut - offset;
110 if (s->end < c)
110 if (s->end < c)
111 c = s->end;
111 c = s->end;
112 l = cut - offset - s->start;
112 l = cut - offset - s->start;
113 if (s->len < l)
113 if (s->len < l)
114 l = s->len;
114 l = s->len;
115
115
116 offset += s->start + l - c;
116 offset += s->start + l - c;
117
117
118 d->start = s->start;
118 d->start = s->start;
119 d->end = c;
119 d->end = c;
120 d->len = l;
120 d->len = l;
121 d->data = s->data;
121 d->data = s->data;
122 d++;
122 d++;
123 s->start = c;
123 s->start = c;
124 s->len = s->len - l;
124 s->len = s->len - l;
125 s->data = s->data + l;
125 s->data = s->data + l;
126
126
127 break;
127 break;
128 }
128 }
129 }
129 }
130
130
131 dest->tail = d;
131 dest->tail = d;
132 src->head = s;
132 src->head = s;
133 return offset;
133 return offset;
134 }
134 }
135
135
136 /* like gather, but with no output list */
136 /* like gather, but with no output list */
137 static int discard(struct flist *src, int cut, int offset)
137 static int discard(struct flist *src, int cut, int offset)
138 {
138 {
139 struct frag *s = src->head;
139 struct frag *s = src->head;
140 int postend, c, l;
140 int postend, c, l;
141
141
142 while (s != src->tail) {
142 while (s != src->tail) {
143 if (s->start + offset >= cut)
143 if (s->start + offset >= cut)
144 break;
144 break;
145
145
146 postend = offset + s->start + s->len;
146 postend = offset + s->start + s->len;
147 if (postend <= cut) {
147 if (postend <= cut) {
148 offset += s->start + s->len - s->end;
148 offset += s->start + s->len - s->end;
149 s++;
149 s++;
150 }
150 }
151 else {
151 else {
152 c = cut - offset;
152 c = cut - offset;
153 if (s->end < c)
153 if (s->end < c)
154 c = s->end;
154 c = s->end;
155 l = cut - offset - s->start;
155 l = cut - offset - s->start;
156 if (s->len < l)
156 if (s->len < l)
157 l = s->len;
157 l = s->len;
158
158
159 offset += s->start + l - c;
159 offset += s->start + l - c;
160 s->start = c;
160 s->start = c;
161 s->len = s->len - l;
161 s->len = s->len - l;
162 s->data = s->data + l;
162 s->data = s->data + l;
163
163
164 break;
164 break;
165 }
165 }
166 }
166 }
167
167
168 src->head = s;
168 src->head = s;
169 return offset;
169 return offset;
170 }
170 }
171
171
172 /* combine hunk lists a and b, while adjusting b for offset changes in a/
172 /* combine hunk lists a and b, while adjusting b for offset changes in a/
173 this deletes a and b and returns the resultant list. */
173 this deletes a and b and returns the resultant list. */
174 static struct flist *combine(struct flist *a, struct flist *b)
174 static struct flist *combine(struct flist *a, struct flist *b)
175 {
175 {
176 struct flist *c = NULL;
176 struct flist *c = NULL;
177 struct frag *bh, *ct;
177 struct frag *bh, *ct;
178 int offset = 0, post;
178 int offset = 0, post;
179
179
180 if (a && b)
180 if (a && b)
181 c = lalloc((lsize(a) + lsize(b)) * 2);
181 c = lalloc((lsize(a) + lsize(b)) * 2);
182
182
183 if (c) {
183 if (c) {
184
184
185 for (bh = b->head; bh != b->tail; bh++) {
185 for (bh = b->head; bh != b->tail; bh++) {
186 /* save old hunks */
186 /* save old hunks */
187 offset = gather(c, a, bh->start, offset);
187 offset = gather(c, a, bh->start, offset);
188
188
189 /* discard replaced hunks */
189 /* discard replaced hunks */
190 post = discard(a, bh->end, offset);
190 post = discard(a, bh->end, offset);
191
191
192 /* insert new hunk */
192 /* insert new hunk */
193 ct = c->tail;
193 ct = c->tail;
194 ct->start = bh->start - offset;
194 ct->start = bh->start - offset;
195 ct->end = bh->end - post;
195 ct->end = bh->end - post;
196 ct->len = bh->len;
196 ct->len = bh->len;
197 ct->data = bh->data;
197 ct->data = bh->data;
198 c->tail++;
198 c->tail++;
199 offset = post;
199 offset = post;
200 }
200 }
201
201
202 /* hold on to tail from a */
202 /* hold on to tail from a */
203 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
203 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
204 c->tail += lsize(a);
204 c->tail += lsize(a);
205 }
205 }
206
206
207 lfree(a);
207 lfree(a);
208 lfree(b);
208 lfree(b);
209 return c;
209 return c;
210 }
210 }
211
211
212 /* decode a binary patch into a hunk list */
212 /* decode a binary patch into a hunk list */
213 static struct flist *decode(char *bin, int len)
213 static struct flist *decode(char *bin, int len)
214 {
214 {
215 struct flist *l;
215 struct flist *l;
216 struct frag *lt;
216 struct frag *lt;
217 char *end = bin + len;
217 char *end = bin + len;
218 char decode[12]; /* for dealing with alignment issues */
218 char decode[12]; /* for dealing with alignment issues */
219
219
220 /* assume worst case size, we won't have many of these lists */
220 /* assume worst case size, we won't have many of these lists */
221 l = lalloc(len / 12);
221 l = lalloc(len / 12);
222 if (!l)
222 if (!l)
223 return NULL;
223 return NULL;
224
224
225 lt = l->tail;
225 lt = l->tail;
226
226
227 while (bin < end) {
227 while (bin < end) {
228 memcpy(decode, bin, 12);
228 memcpy(decode, bin, 12);
229 lt->start = ntohl(*(uint32_t *)decode);
229 lt->start = ntohl(*(uint32_t *)decode);
230 lt->end = ntohl(*(uint32_t *)(decode + 4));
230 lt->end = ntohl(*(uint32_t *)(decode + 4));
231 lt->len = ntohl(*(uint32_t *)(decode + 8));
231 lt->len = ntohl(*(uint32_t *)(decode + 8));
232 lt->data = bin + 12;
232 lt->data = bin + 12;
233 bin += 12 + lt->len;
233 bin += 12 + lt->len;
234 lt++;
234 lt++;
235 }
235 }
236
236
237 if (bin != end) {
237 if (bin != end) {
238 if (!PyErr_Occurred())
238 if (!PyErr_Occurred())
239 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
239 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
240 lfree(l);
240 lfree(l);
241 return NULL;
241 return NULL;
242 }
242 }
243
243
244 l->tail = lt;
244 l->tail = lt;
245 return l;
245 return l;
246 }
246 }
247
247
248 /* calculate the size of resultant text */
248 /* calculate the size of resultant text */
249 static int calcsize(int len, struct flist *l)
249 static int calcsize(int len, struct flist *l)
250 {
250 {
251 int outlen = 0, last = 0;
251 int outlen = 0, last = 0;
252 struct frag *f = l->head;
252 struct frag *f = l->head;
253
253
254 while (f != l->tail) {
254 while (f != l->tail) {
255 if (f->start < last || f->end > len) {
255 if (f->start < last || f->end > len) {
256 if (!PyErr_Occurred())
256 if (!PyErr_Occurred())
257 PyErr_SetString(mpatch_Error,
257 PyErr_SetString(mpatch_Error,
258 "invalid patch");
258 "invalid patch");
259 return -1;
259 return -1;
260 }
260 }
261 outlen += f->start - last;
261 outlen += f->start - last;
262 last = f->end;
262 last = f->end;
263 outlen += f->len;
263 outlen += f->len;
264 f++;
264 f++;
265 }
265 }
266
266
267 outlen += len - last;
267 outlen += len - last;
268 return outlen;
268 return outlen;
269 }
269 }
270
270
271 static int apply(char *buf, char *orig, int len, struct flist *l)
271 static int apply(char *buf, char *orig, int len, struct flist *l)
272 {
272 {
273 struct frag *f = l->head;
273 struct frag *f = l->head;
274 int last = 0;
274 int last = 0;
275 char *p = buf;
275 char *p = buf;
276
276
277 while (f != l->tail) {
277 while (f != l->tail) {
278 if (f->start < last || f->end > len) {
278 if (f->start < last || f->end > len) {
279 if (!PyErr_Occurred())
279 if (!PyErr_Occurred())
280 PyErr_SetString(mpatch_Error,
280 PyErr_SetString(mpatch_Error,
281 "invalid patch");
281 "invalid patch");
282 return 0;
282 return 0;
283 }
283 }
284 memcpy(p, orig + last, f->start - last);
284 memcpy(p, orig + last, f->start - last);
285 p += f->start - last;
285 p += f->start - last;
286 memcpy(p, f->data, f->len);
286 memcpy(p, f->data, f->len);
287 last = f->end;
287 last = f->end;
288 p += f->len;
288 p += f->len;
289 f++;
289 f++;
290 }
290 }
291 memcpy(p, orig + last, len - last);
291 memcpy(p, orig + last, len - last);
292 return 1;
292 return 1;
293 }
293 }
294
294
295 /* recursively generate a patch of all bins between start and end */
295 /* recursively generate a patch of all bins between start and end */
296 static struct flist *fold(PyObject *bins, int start, int end)
296 static struct flist *fold(PyObject *bins, int start, int end)
297 {
297 {
298 int len;
298 int len;
299
299
300 if (start + 1 == end) {
300 if (start + 1 == end) {
301 /* trivial case, output a decoded list */
301 /* trivial case, output a decoded list */
302 PyObject *tmp = PyList_GetItem(bins, start);
302 PyObject *tmp = PyList_GetItem(bins, start);
303 if (!tmp)
303 if (!tmp)
304 return NULL;
304 return NULL;
305 return decode(PyString_AsString(tmp), PyString_Size(tmp));
305 return decode(PyString_AsString(tmp), PyString_Size(tmp));
306 }
306 }
307
307
308 /* divide and conquer, memory management is elsewhere */
308 /* divide and conquer, memory management is elsewhere */
309 len = (end - start) / 2;
309 len = (end - start) / 2;
310 return combine(fold(bins, start, start + len),
310 return combine(fold(bins, start, start + len),
311 fold(bins, start + len, end));
311 fold(bins, start + len, end));
312 }
312 }
313
313
314 static PyObject *
314 static PyObject *
315 patches(PyObject *self, PyObject *args)
315 patches(PyObject *self, PyObject *args)
316 {
316 {
317 PyObject *text, *bins, *result;
317 PyObject *text, *bins, *result;
318 struct flist *patch;
318 struct flist *patch;
319 char *in, *out;
319 char *in, *out;
320 int len, outlen;
320 int len, outlen;
321
321
322 if (!PyArg_ParseTuple(args, "SO:mpatch", &text, &bins))
322 if (!PyArg_ParseTuple(args, "SO:mpatch", &text, &bins))
323 return NULL;
323 return NULL;
324
324
325 len = PyList_Size(bins);
325 len = PyList_Size(bins);
326 if (!len) {
326 if (!len) {
327 /* nothing to do */
327 /* nothing to do */
328 Py_INCREF(text);
328 Py_INCREF(text);
329 return text;
329 return text;
330 }
330 }
331
331
332 patch = fold(bins, 0, len);
332 patch = fold(bins, 0, len);
333 if (!patch)
333 if (!patch)
334 return NULL;
334 return NULL;
335
335
336 outlen = calcsize(PyString_Size(text), patch);
336 outlen = calcsize(PyString_Size(text), patch);
337 if (outlen < 0) {
337 if (outlen < 0) {
338 result = NULL;
338 result = NULL;
339 goto cleanup;
339 goto cleanup;
340 }
340 }
341 result = PyString_FromStringAndSize(NULL, outlen);
341 result = PyString_FromStringAndSize(NULL, outlen);
342 if (!result) {
342 if (!result) {
343 result = NULL;
343 result = NULL;
344 goto cleanup;
344 goto cleanup;
345 }
345 }
346 in = PyString_AsString(text);
346 in = PyString_AsString(text);
347 out = PyString_AsString(result);
347 out = PyString_AsString(result);
348 if (!apply(out, in, PyString_Size(text), patch)) {
348 if (!apply(out, in, PyString_Size(text), patch)) {
349 Py_DECREF(result);
349 Py_DECREF(result);
350 result = NULL;
350 result = NULL;
351 }
351 }
352 cleanup:
352 cleanup:
353 lfree(patch);
353 lfree(patch);
354 return result;
354 return result;
355 }
355 }
356
356
357 static PyMethodDef methods[] = {
357 static PyMethodDef methods[] = {
358 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
358 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
359 {NULL, NULL}
359 {NULL, NULL}
360 };
360 };
361
361
362 PyMODINIT_FUNC
362 PyMODINIT_FUNC
363 initmpatch(void)
363 initmpatch(void)
364 {
364 {
365 Py_InitModule3("mpatch", methods, mpatch_doc);
365 Py_InitModule3("mpatch", methods, mpatch_doc);
366 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
366 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
367 }
367 }
368
368
General Comments 0
You need to be logged in to leave comments. Login now