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