##// END OF EJS Templates
mpatch.c: Added preliminary support for py3k....
Renato Cunha -
r11360:2ac98313 default
parent child Browse files
Show More
@@ -1,444 +1,470
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 <Python.h>
23 #include <Python.h>
24 #include <stdlib.h>
24 #include <stdlib.h>
25 #include <string.h>
25 #include <string.h>
26
26
27 #include "util.h"
28
27 /* Definitions to get compatibility with python 2.4 and earlier which
29 /* Definitions to get compatibility with python 2.4 and earlier which
28 does not have Py_ssize_t. See also PEP 353.
30 does not have Py_ssize_t. See also PEP 353.
29 Note: msvc (8 or earlier) does not have ssize_t, so we use Py_ssize_t.
31 Note: msvc (8 or earlier) does not have ssize_t, so we use Py_ssize_t.
30 */
32 */
31 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
33 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
32 typedef int Py_ssize_t;
34 typedef int Py_ssize_t;
33 #define PY_SSIZE_T_MAX INT_MAX
35 #define PY_SSIZE_T_MAX INT_MAX
34 #define PY_SSIZE_T_MIN INT_MIN
36 #define PY_SSIZE_T_MIN INT_MIN
35 #endif
37 #endif
36
38
37 #ifdef _WIN32
39 #ifdef _WIN32
38 #ifdef _MSC_VER
40 #ifdef _MSC_VER
39 /* msvc 6.0 has problems */
41 /* msvc 6.0 has problems */
40 #define inline __inline
42 #define inline __inline
41 typedef unsigned long uint32_t;
43 typedef unsigned long uint32_t;
42 #else
44 #else
43 #include <stdint.h>
45 #include <stdint.h>
44 #endif
46 #endif
45 static uint32_t ntohl(uint32_t x)
47 static uint32_t ntohl(uint32_t x)
46 {
48 {
47 return ((x & 0x000000ffUL) << 24) |
49 return ((x & 0x000000ffUL) << 24) |
48 ((x & 0x0000ff00UL) << 8) |
50 ((x & 0x0000ff00UL) << 8) |
49 ((x & 0x00ff0000UL) >> 8) |
51 ((x & 0x00ff0000UL) >> 8) |
50 ((x & 0xff000000UL) >> 24);
52 ((x & 0xff000000UL) >> 24);
51 }
53 }
52 #else
54 #else
53 /* not windows */
55 /* not windows */
54 #include <sys/types.h>
56 #include <sys/types.h>
55 #if defined __BEOS__ && !defined __HAIKU__
57 #if defined __BEOS__ && !defined __HAIKU__
56 #include <ByteOrder.h>
58 #include <ByteOrder.h>
57 #else
59 #else
58 #include <arpa/inet.h>
60 #include <arpa/inet.h>
59 #endif
61 #endif
60 #include <inttypes.h>
62 #include <inttypes.h>
61 #endif
63 #endif
62
64
63 static char mpatch_doc[] = "Efficient binary patching.";
65 static char mpatch_doc[] = "Efficient binary patching.";
64 static PyObject *mpatch_Error;
66 static PyObject *mpatch_Error;
65
67
66 struct frag {
68 struct frag {
67 int start, end, len;
69 int start, end, len;
68 const char *data;
70 const char *data;
69 };
71 };
70
72
71 struct flist {
73 struct flist {
72 struct frag *base, *head, *tail;
74 struct frag *base, *head, *tail;
73 };
75 };
74
76
75 static struct flist *lalloc(int size)
77 static struct flist *lalloc(int size)
76 {
78 {
77 struct flist *a = NULL;
79 struct flist *a = NULL;
78
80
79 if (size < 1)
81 if (size < 1)
80 size = 1;
82 size = 1;
81
83
82 a = (struct flist *)malloc(sizeof(struct flist));
84 a = (struct flist *)malloc(sizeof(struct flist));
83 if (a) {
85 if (a) {
84 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
86 a->base = (struct frag *)malloc(sizeof(struct frag) * size);
85 if (a->base) {
87 if (a->base) {
86 a->head = a->tail = a->base;
88 a->head = a->tail = a->base;
87 return a;
89 return a;
88 }
90 }
89 free(a);
91 free(a);
90 a = NULL;
92 a = NULL;
91 }
93 }
92 if (!PyErr_Occurred())
94 if (!PyErr_Occurred())
93 PyErr_NoMemory();
95 PyErr_NoMemory();
94 return NULL;
96 return NULL;
95 }
97 }
96
98
97 static void lfree(struct flist *a)
99 static void lfree(struct flist *a)
98 {
100 {
99 if (a) {
101 if (a) {
100 free(a->base);
102 free(a->base);
101 free(a);
103 free(a);
102 }
104 }
103 }
105 }
104
106
105 static int lsize(struct flist *a)
107 static int lsize(struct flist *a)
106 {
108 {
107 return a->tail - a->head;
109 return a->tail - a->head;
108 }
110 }
109
111
110 /* move hunks in source that are less cut to dest, compensating
112 /* move hunks in source that are less cut to dest, compensating
111 for changes in offset. the last hunk may be split if necessary.
113 for changes in offset. the last hunk may be split if necessary.
112 */
114 */
113 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
115 static int gather(struct flist *dest, struct flist *src, int cut, int offset)
114 {
116 {
115 struct frag *d = dest->tail, *s = src->head;
117 struct frag *d = dest->tail, *s = src->head;
116 int postend, c, l;
118 int postend, c, l;
117
119
118 while (s != src->tail) {
120 while (s != src->tail) {
119 if (s->start + offset >= cut)
121 if (s->start + offset >= cut)
120 break; /* we've gone far enough */
122 break; /* we've gone far enough */
121
123
122 postend = offset + s->start + s->len;
124 postend = offset + s->start + s->len;
123 if (postend <= cut) {
125 if (postend <= cut) {
124 /* save this hunk */
126 /* save this hunk */
125 offset += s->start + s->len - s->end;
127 offset += s->start + s->len - s->end;
126 *d++ = *s++;
128 *d++ = *s++;
127 }
129 }
128 else {
130 else {
129 /* break up this hunk */
131 /* break up this hunk */
130 c = cut - offset;
132 c = cut - offset;
131 if (s->end < c)
133 if (s->end < c)
132 c = s->end;
134 c = s->end;
133 l = cut - offset - s->start;
135 l = cut - offset - s->start;
134 if (s->len < l)
136 if (s->len < l)
135 l = s->len;
137 l = s->len;
136
138
137 offset += s->start + l - c;
139 offset += s->start + l - c;
138
140
139 d->start = s->start;
141 d->start = s->start;
140 d->end = c;
142 d->end = c;
141 d->len = l;
143 d->len = l;
142 d->data = s->data;
144 d->data = s->data;
143 d++;
145 d++;
144 s->start = c;
146 s->start = c;
145 s->len = s->len - l;
147 s->len = s->len - l;
146 s->data = s->data + l;
148 s->data = s->data + l;
147
149
148 break;
150 break;
149 }
151 }
150 }
152 }
151
153
152 dest->tail = d;
154 dest->tail = d;
153 src->head = s;
155 src->head = s;
154 return offset;
156 return offset;
155 }
157 }
156
158
157 /* like gather, but with no output list */
159 /* like gather, but with no output list */
158 static int discard(struct flist *src, int cut, int offset)
160 static int discard(struct flist *src, int cut, int offset)
159 {
161 {
160 struct frag *s = src->head;
162 struct frag *s = src->head;
161 int postend, c, l;
163 int postend, c, l;
162
164
163 while (s != src->tail) {
165 while (s != src->tail) {
164 if (s->start + offset >= cut)
166 if (s->start + offset >= cut)
165 break;
167 break;
166
168
167 postend = offset + s->start + s->len;
169 postend = offset + s->start + s->len;
168 if (postend <= cut) {
170 if (postend <= cut) {
169 offset += s->start + s->len - s->end;
171 offset += s->start + s->len - s->end;
170 s++;
172 s++;
171 }
173 }
172 else {
174 else {
173 c = cut - offset;
175 c = cut - offset;
174 if (s->end < c)
176 if (s->end < c)
175 c = s->end;
177 c = s->end;
176 l = cut - offset - s->start;
178 l = cut - offset - s->start;
177 if (s->len < l)
179 if (s->len < l)
178 l = s->len;
180 l = s->len;
179
181
180 offset += s->start + l - c;
182 offset += s->start + l - c;
181 s->start = c;
183 s->start = c;
182 s->len = s->len - l;
184 s->len = s->len - l;
183 s->data = s->data + l;
185 s->data = s->data + l;
184
186
185 break;
187 break;
186 }
188 }
187 }
189 }
188
190
189 src->head = s;
191 src->head = s;
190 return offset;
192 return offset;
191 }
193 }
192
194
193 /* combine hunk lists a and b, while adjusting b for offset changes in a/
195 /* combine hunk lists a and b, while adjusting b for offset changes in a/
194 this deletes a and b and returns the resultant list. */
196 this deletes a and b and returns the resultant list. */
195 static struct flist *combine(struct flist *a, struct flist *b)
197 static struct flist *combine(struct flist *a, struct flist *b)
196 {
198 {
197 struct flist *c = NULL;
199 struct flist *c = NULL;
198 struct frag *bh, *ct;
200 struct frag *bh, *ct;
199 int offset = 0, post;
201 int offset = 0, post;
200
202
201 if (a && b)
203 if (a && b)
202 c = lalloc((lsize(a) + lsize(b)) * 2);
204 c = lalloc((lsize(a) + lsize(b)) * 2);
203
205
204 if (c) {
206 if (c) {
205
207
206 for (bh = b->head; bh != b->tail; bh++) {
208 for (bh = b->head; bh != b->tail; bh++) {
207 /* save old hunks */
209 /* save old hunks */
208 offset = gather(c, a, bh->start, offset);
210 offset = gather(c, a, bh->start, offset);
209
211
210 /* discard replaced hunks */
212 /* discard replaced hunks */
211 post = discard(a, bh->end, offset);
213 post = discard(a, bh->end, offset);
212
214
213 /* insert new hunk */
215 /* insert new hunk */
214 ct = c->tail;
216 ct = c->tail;
215 ct->start = bh->start - offset;
217 ct->start = bh->start - offset;
216 ct->end = bh->end - post;
218 ct->end = bh->end - post;
217 ct->len = bh->len;
219 ct->len = bh->len;
218 ct->data = bh->data;
220 ct->data = bh->data;
219 c->tail++;
221 c->tail++;
220 offset = post;
222 offset = post;
221 }
223 }
222
224
223 /* hold on to tail from a */
225 /* hold on to tail from a */
224 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
226 memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
225 c->tail += lsize(a);
227 c->tail += lsize(a);
226 }
228 }
227
229
228 lfree(a);
230 lfree(a);
229 lfree(b);
231 lfree(b);
230 return c;
232 return c;
231 }
233 }
232
234
233 /* decode a binary patch into a hunk list */
235 /* decode a binary patch into a hunk list */
234 static struct flist *decode(const char *bin, int len)
236 static struct flist *decode(const char *bin, int len)
235 {
237 {
236 struct flist *l;
238 struct flist *l;
237 struct frag *lt;
239 struct frag *lt;
238 const char *data = bin + 12, *end = bin + len;
240 const char *data = bin + 12, *end = bin + len;
239 char decode[12]; /* for dealing with alignment issues */
241 char decode[12]; /* for dealing with alignment issues */
240
242
241 /* assume worst case size, we won't have many of these lists */
243 /* assume worst case size, we won't have many of these lists */
242 l = lalloc(len / 12);
244 l = lalloc(len / 12);
243 if (!l)
245 if (!l)
244 return NULL;
246 return NULL;
245
247
246 lt = l->tail;
248 lt = l->tail;
247
249
248 while (data <= end) {
250 while (data <= end) {
249 memcpy(decode, bin, 12);
251 memcpy(decode, bin, 12);
250 lt->start = ntohl(*(uint32_t *)decode);
252 lt->start = ntohl(*(uint32_t *)decode);
251 lt->end = ntohl(*(uint32_t *)(decode + 4));
253 lt->end = ntohl(*(uint32_t *)(decode + 4));
252 lt->len = ntohl(*(uint32_t *)(decode + 8));
254 lt->len = ntohl(*(uint32_t *)(decode + 8));
253 if (lt->start > lt->end)
255 if (lt->start > lt->end)
254 break; /* sanity check */
256 break; /* sanity check */
255 bin = data + lt->len;
257 bin = data + lt->len;
256 if (bin < data)
258 if (bin < data)
257 break; /* big data + big (bogus) len can wrap around */
259 break; /* big data + big (bogus) len can wrap around */
258 lt->data = data;
260 lt->data = data;
259 data = bin + 12;
261 data = bin + 12;
260 lt++;
262 lt++;
261 }
263 }
262
264
263 if (bin != end) {
265 if (bin != end) {
264 if (!PyErr_Occurred())
266 if (!PyErr_Occurred())
265 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
267 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
266 lfree(l);
268 lfree(l);
267 return NULL;
269 return NULL;
268 }
270 }
269
271
270 l->tail = lt;
272 l->tail = lt;
271 return l;
273 return l;
272 }
274 }
273
275
274 /* calculate the size of resultant text */
276 /* calculate the size of resultant text */
275 static int calcsize(int len, struct flist *l)
277 static int calcsize(int len, struct flist *l)
276 {
278 {
277 int outlen = 0, last = 0;
279 int outlen = 0, last = 0;
278 struct frag *f = l->head;
280 struct frag *f = l->head;
279
281
280 while (f != l->tail) {
282 while (f != l->tail) {
281 if (f->start < last || f->end > len) {
283 if (f->start < last || f->end > len) {
282 if (!PyErr_Occurred())
284 if (!PyErr_Occurred())
283 PyErr_SetString(mpatch_Error,
285 PyErr_SetString(mpatch_Error,
284 "invalid patch");
286 "invalid patch");
285 return -1;
287 return -1;
286 }
288 }
287 outlen += f->start - last;
289 outlen += f->start - last;
288 last = f->end;
290 last = f->end;
289 outlen += f->len;
291 outlen += f->len;
290 f++;
292 f++;
291 }
293 }
292
294
293 outlen += len - last;
295 outlen += len - last;
294 return outlen;
296 return outlen;
295 }
297 }
296
298
297 static int apply(char *buf, const char *orig, int len, struct flist *l)
299 static int apply(char *buf, const char *orig, int len, struct flist *l)
298 {
300 {
299 struct frag *f = l->head;
301 struct frag *f = l->head;
300 int last = 0;
302 int last = 0;
301 char *p = buf;
303 char *p = buf;
302
304
303 while (f != l->tail) {
305 while (f != l->tail) {
304 if (f->start < last || f->end > len) {
306 if (f->start < last || f->end > len) {
305 if (!PyErr_Occurred())
307 if (!PyErr_Occurred())
306 PyErr_SetString(mpatch_Error,
308 PyErr_SetString(mpatch_Error,
307 "invalid patch");
309 "invalid patch");
308 return 0;
310 return 0;
309 }
311 }
310 memcpy(p, orig + last, f->start - last);
312 memcpy(p, orig + last, f->start - last);
311 p += f->start - last;
313 p += f->start - last;
312 memcpy(p, f->data, f->len);
314 memcpy(p, f->data, f->len);
313 last = f->end;
315 last = f->end;
314 p += f->len;
316 p += f->len;
315 f++;
317 f++;
316 }
318 }
317 memcpy(p, orig + last, len - last);
319 memcpy(p, orig + last, len - last);
318 return 1;
320 return 1;
319 }
321 }
320
322
321 /* recursively generate a patch of all bins between start and end */
323 /* recursively generate a patch of all bins between start and end */
322 static struct flist *fold(PyObject *bins, int start, int end)
324 static struct flist *fold(PyObject *bins, int start, int end)
323 {
325 {
324 int len;
326 int len;
325 Py_ssize_t blen;
327 Py_ssize_t blen;
326 const char *buffer;
328 const char *buffer;
327
329
328 if (start + 1 == end) {
330 if (start + 1 == end) {
329 /* trivial case, output a decoded list */
331 /* trivial case, output a decoded list */
330 PyObject *tmp = PyList_GetItem(bins, start);
332 PyObject *tmp = PyList_GetItem(bins, start);
331 if (!tmp)
333 if (!tmp)
332 return NULL;
334 return NULL;
333 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
335 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
334 return NULL;
336 return NULL;
335 return decode(buffer, blen);
337 return decode(buffer, blen);
336 }
338 }
337
339
338 /* divide and conquer, memory management is elsewhere */
340 /* divide and conquer, memory management is elsewhere */
339 len = (end - start) / 2;
341 len = (end - start) / 2;
340 return combine(fold(bins, start, start + len),
342 return combine(fold(bins, start, start + len),
341 fold(bins, start + len, end));
343 fold(bins, start + len, end));
342 }
344 }
343
345
344 static PyObject *
346 static PyObject *
345 patches(PyObject *self, PyObject *args)
347 patches(PyObject *self, PyObject *args)
346 {
348 {
347 PyObject *text, *bins, *result;
349 PyObject *text, *bins, *result;
348 struct flist *patch;
350 struct flist *patch;
349 const char *in;
351 const char *in;
350 char *out;
352 char *out;
351 int len, outlen;
353 int len, outlen;
352 Py_ssize_t inlen;
354 Py_ssize_t inlen;
353
355
354 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
356 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
355 return NULL;
357 return NULL;
356
358
357 len = PyList_Size(bins);
359 len = PyList_Size(bins);
358 if (!len) {
360 if (!len) {
359 /* nothing to do */
361 /* nothing to do */
360 Py_INCREF(text);
362 Py_INCREF(text);
361 return text;
363 return text;
362 }
364 }
363
365
364 if (PyObject_AsCharBuffer(text, &in, &inlen))
366 if (PyObject_AsCharBuffer(text, &in, &inlen))
365 return NULL;
367 return NULL;
366
368
367 patch = fold(bins, 0, len);
369 patch = fold(bins, 0, len);
368 if (!patch)
370 if (!patch)
369 return NULL;
371 return NULL;
370
372
371 outlen = calcsize(inlen, patch);
373 outlen = calcsize(inlen, patch);
372 if (outlen < 0) {
374 if (outlen < 0) {
373 result = NULL;
375 result = NULL;
374 goto cleanup;
376 goto cleanup;
375 }
377 }
376 result = PyString_FromStringAndSize(NULL, outlen);
378 result = PyBytes_FromStringAndSize(NULL, outlen);
377 if (!result) {
379 if (!result) {
378 result = NULL;
380 result = NULL;
379 goto cleanup;
381 goto cleanup;
380 }
382 }
381 out = PyString_AsString(result);
383 out = PyBytes_AsString(result);
382 if (!apply(out, in, inlen, patch)) {
384 if (!apply(out, in, inlen, patch)) {
383 Py_DECREF(result);
385 Py_DECREF(result);
384 result = NULL;
386 result = NULL;
385 }
387 }
386 cleanup:
388 cleanup:
387 lfree(patch);
389 lfree(patch);
388 return result;
390 return result;
389 }
391 }
390
392
391 /* calculate size of a patched file directly */
393 /* calculate size of a patched file directly */
392 static PyObject *
394 static PyObject *
393 patchedsize(PyObject *self, PyObject *args)
395 patchedsize(PyObject *self, PyObject *args)
394 {
396 {
395 long orig, start, end, len, outlen = 0, last = 0;
397 long orig, start, end, len, outlen = 0, last = 0;
396 int patchlen;
398 int patchlen;
397 char *bin, *binend, *data;
399 char *bin, *binend, *data;
398 char decode[12]; /* for dealing with alignment issues */
400 char decode[12]; /* for dealing with alignment issues */
399
401
400 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
402 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
401 return NULL;
403 return NULL;
402
404
403 binend = bin + patchlen;
405 binend = bin + patchlen;
404 data = bin + 12;
406 data = bin + 12;
405
407
406 while (data <= binend) {
408 while (data <= binend) {
407 memcpy(decode, bin, 12);
409 memcpy(decode, bin, 12);
408 start = ntohl(*(uint32_t *)decode);
410 start = ntohl(*(uint32_t *)decode);
409 end = ntohl(*(uint32_t *)(decode + 4));
411 end = ntohl(*(uint32_t *)(decode + 4));
410 len = ntohl(*(uint32_t *)(decode + 8));
412 len = ntohl(*(uint32_t *)(decode + 8));
411 if (start > end)
413 if (start > end)
412 break; /* sanity check */
414 break; /* sanity check */
413 bin = data + len;
415 bin = data + len;
414 if (bin < data)
416 if (bin < data)
415 break; /* big data + big (bogus) len can wrap around */
417 break; /* big data + big (bogus) len can wrap around */
416 data = bin + 12;
418 data = bin + 12;
417 outlen += start - last;
419 outlen += start - last;
418 last = end;
420 last = end;
419 outlen += len;
421 outlen += len;
420 }
422 }
421
423
422 if (bin != binend) {
424 if (bin != binend) {
423 if (!PyErr_Occurred())
425 if (!PyErr_Occurred())
424 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
426 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
425 return NULL;
427 return NULL;
426 }
428 }
427
429
428 outlen += orig - last;
430 outlen += orig - last;
429 return Py_BuildValue("l", outlen);
431 return Py_BuildValue("l", outlen);
430 }
432 }
431
433
432 static PyMethodDef methods[] = {
434 static PyMethodDef methods[] = {
433 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
435 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
434 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
436 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
435 {NULL, NULL}
437 {NULL, NULL}
436 };
438 };
437
439
440 #ifdef IS_PY3K
441 static struct PyModuleDef mpatch_module = {
442 PyModuleDef_HEAD_INIT,
443 "mpatch",
444 mpatch_doc,
445 -1,
446 methods
447 };
448
449 PyMODINIT_FUNC PyInit_mpatch(void)
450 {
451 PyObject *m;
452
453 m = PyModule_Create(&mpatch_module);
454 if (m == NULL)
455 return NULL;
456
457 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
458 Py_INCREF(mpatch_Error);
459 PyModule_AddObject(m, "mpatchError", mpatch_Error);
460
461 return m;
462 }
463 #else
438 PyMODINIT_FUNC
464 PyMODINIT_FUNC
439 initmpatch(void)
465 initmpatch(void)
440 {
466 {
441 Py_InitModule3("mpatch", methods, mpatch_doc);
467 Py_InitModule3("mpatch", methods, mpatch_doc);
442 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
468 mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
443 }
469 }
444
470 #endif
General Comments 0
You need to be logged in to leave comments. Login now