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