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