##// END OF EJS Templates
mpatch: be more careful about parsing binary patch data (SEC)...
Augie Fackler -
r38245:90a27496 stable
parent child Browse files
Show More
@@ -1,279 +1,281 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, 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 #include <stdlib.h>
23 #include <stdlib.h>
24 #include <string.h>
24 #include <string.h>
25
25
26 #include "bitmanipulation.h"
26 #include "bitmanipulation.h"
27 #include "compat.h"
27 #include "compat.h"
28 #include "mpatch.h"
28 #include "mpatch.h"
29
29
30 static struct mpatch_flist *lalloc(ssize_t size)
30 static struct mpatch_flist *lalloc(ssize_t size)
31 {
31 {
32 struct mpatch_flist *a = NULL;
32 struct mpatch_flist *a = NULL;
33
33
34 if (size < 1)
34 if (size < 1)
35 size = 1;
35 size = 1;
36
36
37 a = (struct mpatch_flist *)malloc(sizeof(struct mpatch_flist));
37 a = (struct mpatch_flist *)malloc(sizeof(struct mpatch_flist));
38 if (a) {
38 if (a) {
39 a->base = (struct mpatch_frag *)malloc(
39 a->base = (struct mpatch_frag *)malloc(
40 sizeof(struct mpatch_frag) * size);
40 sizeof(struct mpatch_frag) * size);
41 if (a->base) {
41 if (a->base) {
42 a->head = a->tail = a->base;
42 a->head = a->tail = a->base;
43 return a;
43 return a;
44 }
44 }
45 free(a);
45 free(a);
46 }
46 }
47 return NULL;
47 return NULL;
48 }
48 }
49
49
50 void mpatch_lfree(struct mpatch_flist *a)
50 void mpatch_lfree(struct mpatch_flist *a)
51 {
51 {
52 if (a) {
52 if (a) {
53 free(a->base);
53 free(a->base);
54 free(a);
54 free(a);
55 }
55 }
56 }
56 }
57
57
58 static ssize_t lsize(struct mpatch_flist *a)
58 static ssize_t lsize(struct mpatch_flist *a)
59 {
59 {
60 return a->tail - a->head;
60 return a->tail - a->head;
61 }
61 }
62
62
63 /* move hunks in source that are less cut to dest, compensating
63 /* move hunks in source that are less cut to dest, compensating
64 for changes in offset. the last hunk may be split if necessary.
64 for changes in offset. the last hunk may be split if necessary.
65 */
65 */
66 static int gather(struct mpatch_flist *dest, struct mpatch_flist *src, int cut,
66 static int gather(struct mpatch_flist *dest, struct mpatch_flist *src, int cut,
67 int offset)
67 int offset)
68 {
68 {
69 struct mpatch_frag *d = dest->tail, *s = src->head;
69 struct mpatch_frag *d = dest->tail, *s = src->head;
70 int postend, c, l;
70 int postend, c, l;
71
71
72 while (s != src->tail) {
72 while (s != src->tail) {
73 if (s->start + offset >= cut)
73 if (s->start + offset >= cut)
74 break; /* we've gone far enough */
74 break; /* we've gone far enough */
75
75
76 postend = offset + s->start + s->len;
76 postend = offset + s->start + s->len;
77 if (postend <= cut) {
77 if (postend <= cut) {
78 /* save this hunk */
78 /* save this hunk */
79 offset += s->start + s->len - s->end;
79 offset += s->start + s->len - s->end;
80 *d++ = *s++;
80 *d++ = *s++;
81 } else {
81 } else {
82 /* break up this hunk */
82 /* break up this hunk */
83 c = cut - offset;
83 c = cut - offset;
84 if (s->end < c)
84 if (s->end < c)
85 c = s->end;
85 c = s->end;
86 l = cut - offset - s->start;
86 l = cut - offset - s->start;
87 if (s->len < l)
87 if (s->len < l)
88 l = s->len;
88 l = s->len;
89
89
90 offset += s->start + l - c;
90 offset += s->start + l - c;
91
91
92 d->start = s->start;
92 d->start = s->start;
93 d->end = c;
93 d->end = c;
94 d->len = l;
94 d->len = l;
95 d->data = s->data;
95 d->data = s->data;
96 d++;
96 d++;
97 s->start = c;
97 s->start = c;
98 s->len = s->len - l;
98 s->len = s->len - l;
99 s->data = s->data + l;
99 s->data = s->data + l;
100
100
101 break;
101 break;
102 }
102 }
103 }
103 }
104
104
105 dest->tail = d;
105 dest->tail = d;
106 src->head = s;
106 src->head = s;
107 return offset;
107 return offset;
108 }
108 }
109
109
110 /* like gather, but with no output list */
110 /* like gather, but with no output list */
111 static int discard(struct mpatch_flist *src, int cut, int offset)
111 static int discard(struct mpatch_flist *src, int cut, int offset)
112 {
112 {
113 struct mpatch_frag *s = src->head;
113 struct mpatch_frag *s = src->head;
114 int postend, c, l;
114 int postend, c, l;
115
115
116 while (s != src->tail) {
116 while (s != src->tail) {
117 if (s->start + offset >= cut)
117 if (s->start + offset >= cut)
118 break;
118 break;
119
119
120 postend = offset + s->start + s->len;
120 postend = offset + s->start + s->len;
121 if (postend <= cut) {
121 if (postend <= cut) {
122 offset += s->start + s->len - s->end;
122 offset += s->start + s->len - s->end;
123 s++;
123 s++;
124 } else {
124 } else {
125 c = cut - offset;
125 c = cut - offset;
126 if (s->end < c)
126 if (s->end < c)
127 c = s->end;
127 c = s->end;
128 l = cut - offset - s->start;
128 l = cut - offset - s->start;
129 if (s->len < l)
129 if (s->len < l)
130 l = s->len;
130 l = s->len;
131
131
132 offset += s->start + l - c;
132 offset += s->start + l - c;
133 s->start = c;
133 s->start = c;
134 s->len = s->len - l;
134 s->len = s->len - l;
135 s->data = s->data + l;
135 s->data = s->data + l;
136
136
137 break;
137 break;
138 }
138 }
139 }
139 }
140
140
141 src->head = s;
141 src->head = s;
142 return offset;
142 return offset;
143 }
143 }
144
144
145 /* combine hunk lists a and b, while adjusting b for offset changes in a/
145 /* combine hunk lists a and b, while adjusting b for offset changes in a/
146 this deletes a and b and returns the resultant list. */
146 this deletes a and b and returns the resultant list. */
147 static struct mpatch_flist *combine(struct mpatch_flist *a,
147 static struct mpatch_flist *combine(struct mpatch_flist *a,
148 struct mpatch_flist *b)
148 struct mpatch_flist *b)
149 {
149 {
150 struct mpatch_flist *c = NULL;
150 struct mpatch_flist *c = NULL;
151 struct mpatch_frag *bh, *ct;
151 struct mpatch_frag *bh, *ct;
152 int offset = 0, post;
152 int offset = 0, post;
153
153
154 if (a && b)
154 if (a && b)
155 c = lalloc((lsize(a) + lsize(b)) * 2);
155 c = lalloc((lsize(a) + lsize(b)) * 2);
156
156
157 if (c) {
157 if (c) {
158
158
159 for (bh = b->head; bh != b->tail; bh++) {
159 for (bh = b->head; bh != b->tail; bh++) {
160 /* save old hunks */
160 /* save old hunks */
161 offset = gather(c, a, bh->start, offset);
161 offset = gather(c, a, bh->start, offset);
162
162
163 /* discard replaced hunks */
163 /* discard replaced hunks */
164 post = discard(a, bh->end, offset);
164 post = discard(a, bh->end, offset);
165
165
166 /* insert new hunk */
166 /* insert new hunk */
167 ct = c->tail;
167 ct = c->tail;
168 ct->start = bh->start - offset;
168 ct->start = bh->start - offset;
169 ct->end = bh->end - post;
169 ct->end = bh->end - post;
170 ct->len = bh->len;
170 ct->len = bh->len;
171 ct->data = bh->data;
171 ct->data = bh->data;
172 c->tail++;
172 c->tail++;
173 offset = post;
173 offset = post;
174 }
174 }
175
175
176 /* hold on to tail from a */
176 /* hold on to tail from a */
177 memcpy(c->tail, a->head, sizeof(struct mpatch_frag) * lsize(a));
177 memcpy(c->tail, a->head, sizeof(struct mpatch_frag) * lsize(a));
178 c->tail += lsize(a);
178 c->tail += lsize(a);
179 }
179 }
180
180
181 mpatch_lfree(a);
181 mpatch_lfree(a);
182 mpatch_lfree(b);
182 mpatch_lfree(b);
183 return c;
183 return c;
184 }
184 }
185
185
186 /* decode a binary patch into a hunk list */
186 /* decode a binary patch into a hunk list */
187 int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist **res)
187 int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist **res)
188 {
188 {
189 struct mpatch_flist *l;
189 struct mpatch_flist *l;
190 struct mpatch_frag *lt;
190 struct mpatch_frag *lt;
191 int pos = 0;
191 int pos = 0;
192
192
193 /* assume worst case size, we won't have many of these lists */
193 /* assume worst case size, we won't have many of these lists */
194 l = lalloc(len / 12 + 1);
194 l = lalloc(len / 12 + 1);
195 if (!l)
195 if (!l)
196 return MPATCH_ERR_NO_MEM;
196 return MPATCH_ERR_NO_MEM;
197
197
198 lt = l->tail;
198 lt = l->tail;
199
199
200 while (pos >= 0 && pos < len) {
200 /* We check against len-11 to ensure we have at least 12 bytes
201 left in the patch so we can read our three be32s out of it. */
202 while (pos >= 0 && pos < (len - 11)) {
201 lt->start = getbe32(bin + pos);
203 lt->start = getbe32(bin + pos);
202 lt->end = getbe32(bin + pos + 4);
204 lt->end = getbe32(bin + pos + 4);
203 lt->len = getbe32(bin + pos + 8);
205 lt->len = getbe32(bin + pos + 8);
204 lt->data = bin + pos + 12;
206 lt->data = bin + pos + 12;
205 pos += 12 + lt->len;
207 pos += 12 + lt->len;
206 if (lt->start > lt->end || lt->len < 0)
208 if (lt->start > lt->end || lt->len < 0)
207 break; /* sanity check */
209 break; /* sanity check */
208 lt++;
210 lt++;
209 }
211 }
210
212
211 if (pos != len) {
213 if (pos != len) {
212 mpatch_lfree(l);
214 mpatch_lfree(l);
213 return MPATCH_ERR_CANNOT_BE_DECODED;
215 return MPATCH_ERR_CANNOT_BE_DECODED;
214 }
216 }
215
217
216 l->tail = lt;
218 l->tail = lt;
217 *res = l;
219 *res = l;
218 return 0;
220 return 0;
219 }
221 }
220
222
221 /* calculate the size of resultant text */
223 /* calculate the size of resultant text */
222 ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l)
224 ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l)
223 {
225 {
224 ssize_t outlen = 0, last = 0;
226 ssize_t outlen = 0, last = 0;
225 struct mpatch_frag *f = l->head;
227 struct mpatch_frag *f = l->head;
226
228
227 while (f != l->tail) {
229 while (f != l->tail) {
228 if (f->start < last || f->end > len) {
230 if (f->start < last || f->end > len) {
229 return MPATCH_ERR_INVALID_PATCH;
231 return MPATCH_ERR_INVALID_PATCH;
230 }
232 }
231 outlen += f->start - last;
233 outlen += f->start - last;
232 last = f->end;
234 last = f->end;
233 outlen += f->len;
235 outlen += f->len;
234 f++;
236 f++;
235 }
237 }
236
238
237 outlen += len - last;
239 outlen += len - last;
238 return outlen;
240 return outlen;
239 }
241 }
240
242
241 int mpatch_apply(char *buf, const char *orig, ssize_t len,
243 int mpatch_apply(char *buf, const char *orig, ssize_t len,
242 struct mpatch_flist *l)
244 struct mpatch_flist *l)
243 {
245 {
244 struct mpatch_frag *f = l->head;
246 struct mpatch_frag *f = l->head;
245 int last = 0;
247 int last = 0;
246 char *p = buf;
248 char *p = buf;
247
249
248 while (f != l->tail) {
250 while (f != l->tail) {
249 if (f->start < last || f->end > len) {
251 if (f->start < last || f->end > len) {
250 return MPATCH_ERR_INVALID_PATCH;
252 return MPATCH_ERR_INVALID_PATCH;
251 }
253 }
252 memcpy(p, orig + last, f->start - last);
254 memcpy(p, orig + last, f->start - last);
253 p += f->start - last;
255 p += f->start - last;
254 memcpy(p, f->data, f->len);
256 memcpy(p, f->data, f->len);
255 last = f->end;
257 last = f->end;
256 p += f->len;
258 p += f->len;
257 f++;
259 f++;
258 }
260 }
259 memcpy(p, orig + last, len - last);
261 memcpy(p, orig + last, len - last);
260 return 0;
262 return 0;
261 }
263 }
262
264
263 /* recursively generate a patch of all bins between start and end */
265 /* recursively generate a patch of all bins between start and end */
264 struct mpatch_flist *
266 struct mpatch_flist *
265 mpatch_fold(void *bins, struct mpatch_flist *(*get_next_item)(void *, ssize_t),
267 mpatch_fold(void *bins, struct mpatch_flist *(*get_next_item)(void *, ssize_t),
266 ssize_t start, ssize_t end)
268 ssize_t start, ssize_t end)
267 {
269 {
268 ssize_t len;
270 ssize_t len;
269
271
270 if (start + 1 == end) {
272 if (start + 1 == end) {
271 /* trivial case, output a decoded list */
273 /* trivial case, output a decoded list */
272 return get_next_item(bins, start);
274 return get_next_item(bins, start);
273 }
275 }
274
276
275 /* divide and conquer, memory management is elsewhere */
277 /* divide and conquer, memory management is elsewhere */
276 len = (end - start) / 2;
278 len = (end - start) / 2;
277 return combine(mpatch_fold(bins, get_next_item, start, start + len),
279 return combine(mpatch_fold(bins, get_next_item, start, start + len),
278 mpatch_fold(bins, get_next_item, start + len, end));
280 mpatch_fold(bins, get_next_item, start + len, end));
279 }
281 }
General Comments 0
You need to be logged in to leave comments. Login now