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