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