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