##// END OF EJS Templates
patches: release the GIL while applying the patch...
Boris Feld -
r36381:a2d11d23 default
parent child Browse files
Show More
@@ -1,195 +1,201 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 #define PY_SSIZE_T_CLEAN
23 #define PY_SSIZE_T_CLEAN
24 #include <Python.h>
24 #include <Python.h>
25 #include <stdlib.h>
25 #include <stdlib.h>
26 #include <string.h>
26 #include <string.h>
27
27
28 #include "bitmanipulation.h"
28 #include "bitmanipulation.h"
29 #include "compat.h"
29 #include "compat.h"
30 #include "mpatch.h"
30 #include "mpatch.h"
31 #include "util.h"
31 #include "util.h"
32
32
33 static char mpatch_doc[] = "Efficient binary patching.";
33 static char mpatch_doc[] = "Efficient binary patching.";
34 static PyObject *mpatch_Error;
34 static PyObject *mpatch_Error;
35
35
36 static void setpyerr(int r)
36 static void setpyerr(int r)
37 {
37 {
38 switch (r) {
38 switch (r) {
39 case MPATCH_ERR_NO_MEM:
39 case MPATCH_ERR_NO_MEM:
40 PyErr_NoMemory();
40 PyErr_NoMemory();
41 break;
41 break;
42 case MPATCH_ERR_CANNOT_BE_DECODED:
42 case MPATCH_ERR_CANNOT_BE_DECODED:
43 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
43 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
44 break;
44 break;
45 case MPATCH_ERR_INVALID_PATCH:
45 case MPATCH_ERR_INVALID_PATCH:
46 PyErr_SetString(mpatch_Error, "invalid patch");
46 PyErr_SetString(mpatch_Error, "invalid patch");
47 break;
47 break;
48 }
48 }
49 }
49 }
50
50
51 struct mpatch_flist *cpygetitem(void *bins, ssize_t pos)
51 struct mpatch_flist *cpygetitem(void *bins, ssize_t pos)
52 {
52 {
53 const char *buffer;
53 const char *buffer;
54 struct mpatch_flist *res;
54 struct mpatch_flist *res;
55 ssize_t blen;
55 ssize_t blen;
56 int r;
56 int r;
57
57
58 PyObject *tmp = PyList_GetItem((PyObject *)bins, pos);
58 PyObject *tmp = PyList_GetItem((PyObject *)bins, pos);
59 if (!tmp)
59 if (!tmp)
60 return NULL;
60 return NULL;
61 if (PyObject_AsCharBuffer(tmp, &buffer, (Py_ssize_t *)&blen))
61 if (PyObject_AsCharBuffer(tmp, &buffer, (Py_ssize_t *)&blen))
62 return NULL;
62 return NULL;
63 if ((r = mpatch_decode(buffer, blen, &res)) < 0) {
63 if ((r = mpatch_decode(buffer, blen, &res)) < 0) {
64 if (!PyErr_Occurred())
64 if (!PyErr_Occurred())
65 setpyerr(r);
65 setpyerr(r);
66 return NULL;
66 return NULL;
67 }
67 }
68 return res;
68 return res;
69 }
69 }
70
70
71 static PyObject *patches(PyObject *self, PyObject *args)
71 static PyObject *patches(PyObject *self, PyObject *args)
72 {
72 {
73 PyObject *text, *bins, *result;
73 PyObject *text, *bins, *result;
74 struct mpatch_flist *patch;
74 struct mpatch_flist *patch;
75 const char *in;
75 const char *in;
76 int r = 0;
76 int r = 0;
77 char *out;
77 char *out;
78 Py_ssize_t len, outlen, inlen;
78 Py_ssize_t len, outlen, inlen;
79
79
80 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
80 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
81 return NULL;
81 return NULL;
82
82
83 len = PyList_Size(bins);
83 len = PyList_Size(bins);
84 if (!len) {
84 if (!len) {
85 /* nothing to do */
85 /* nothing to do */
86 Py_INCREF(text);
86 Py_INCREF(text);
87 return text;
87 return text;
88 }
88 }
89
89
90 if (PyObject_AsCharBuffer(text, &in, &inlen))
90 if (PyObject_AsCharBuffer(text, &in, &inlen))
91 return NULL;
91 return NULL;
92
92
93 patch = mpatch_fold(bins, cpygetitem, 0, len);
93 patch = mpatch_fold(bins, cpygetitem, 0, len);
94 if (!patch) { /* error already set or memory error */
94 if (!patch) { /* error already set or memory error */
95 if (!PyErr_Occurred())
95 if (!PyErr_Occurred())
96 PyErr_NoMemory();
96 PyErr_NoMemory();
97 return NULL;
97 return NULL;
98 }
98 }
99
99
100 outlen = mpatch_calcsize(inlen, patch);
100 outlen = mpatch_calcsize(inlen, patch);
101 if (outlen < 0) {
101 if (outlen < 0) {
102 r = (int)outlen;
102 r = (int)outlen;
103 result = NULL;
103 result = NULL;
104 goto cleanup;
104 goto cleanup;
105 }
105 }
106 result = PyBytes_FromStringAndSize(NULL, outlen);
106 result = PyBytes_FromStringAndSize(NULL, outlen);
107 if (!result) {
107 if (!result) {
108 result = NULL;
108 result = NULL;
109 goto cleanup;
109 goto cleanup;
110 }
110 }
111 out = PyBytes_AsString(result);
111 out = PyBytes_AsString(result);
112 r = mpatch_apply(out, in, inlen, patch);
112 /* clang-format off */
113 {
114 Py_BEGIN_ALLOW_THREADS
115 r = mpatch_apply(out, in, inlen, patch);
116 Py_END_ALLOW_THREADS
117 }
118 /* clang-format on */
113 if (r < 0) {
119 if (r < 0) {
114 Py_DECREF(result);
120 Py_DECREF(result);
115 result = NULL;
121 result = NULL;
116 }
122 }
117 cleanup:
123 cleanup:
118 mpatch_lfree(patch);
124 mpatch_lfree(patch);
119 if (!result && !PyErr_Occurred())
125 if (!result && !PyErr_Occurred())
120 setpyerr(r);
126 setpyerr(r);
121 return result;
127 return result;
122 }
128 }
123
129
124 /* calculate size of a patched file directly */
130 /* calculate size of a patched file directly */
125 static PyObject *patchedsize(PyObject *self, PyObject *args)
131 static PyObject *patchedsize(PyObject *self, PyObject *args)
126 {
132 {
127 long orig, start, end, len, outlen = 0, last = 0, pos = 0;
133 long orig, start, end, len, outlen = 0, last = 0, pos = 0;
128 Py_ssize_t patchlen;
134 Py_ssize_t patchlen;
129 char *bin;
135 char *bin;
130
136
131 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
137 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
132 return NULL;
138 return NULL;
133
139
134 while (pos >= 0 && pos < patchlen) {
140 while (pos >= 0 && pos < patchlen) {
135 start = getbe32(bin + pos);
141 start = getbe32(bin + pos);
136 end = getbe32(bin + pos + 4);
142 end = getbe32(bin + pos + 4);
137 len = getbe32(bin + pos + 8);
143 len = getbe32(bin + pos + 8);
138 if (start > end)
144 if (start > end)
139 break; /* sanity check */
145 break; /* sanity check */
140 pos += 12 + len;
146 pos += 12 + len;
141 outlen += start - last;
147 outlen += start - last;
142 last = end;
148 last = end;
143 outlen += len;
149 outlen += len;
144 }
150 }
145
151
146 if (pos != patchlen) {
152 if (pos != patchlen) {
147 if (!PyErr_Occurred())
153 if (!PyErr_Occurred())
148 PyErr_SetString(mpatch_Error,
154 PyErr_SetString(mpatch_Error,
149 "patch cannot be decoded");
155 "patch cannot be decoded");
150 return NULL;
156 return NULL;
151 }
157 }
152
158
153 outlen += orig - last;
159 outlen += orig - last;
154 return Py_BuildValue("l", outlen);
160 return Py_BuildValue("l", outlen);
155 }
161 }
156
162
157 static PyMethodDef methods[] = {
163 static PyMethodDef methods[] = {
158 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
164 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
159 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
165 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
160 {NULL, NULL},
166 {NULL, NULL},
161 };
167 };
162
168
163 static const int version = 1;
169 static const int version = 1;
164
170
165 #ifdef IS_PY3K
171 #ifdef IS_PY3K
166 static struct PyModuleDef mpatch_module = {
172 static struct PyModuleDef mpatch_module = {
167 PyModuleDef_HEAD_INIT, "mpatch", mpatch_doc, -1, methods,
173 PyModuleDef_HEAD_INIT, "mpatch", mpatch_doc, -1, methods,
168 };
174 };
169
175
170 PyMODINIT_FUNC PyInit_mpatch(void)
176 PyMODINIT_FUNC PyInit_mpatch(void)
171 {
177 {
172 PyObject *m;
178 PyObject *m;
173
179
174 m = PyModule_Create(&mpatch_module);
180 m = PyModule_Create(&mpatch_module);
175 if (m == NULL)
181 if (m == NULL)
176 return NULL;
182 return NULL;
177
183
178 mpatch_Error =
184 mpatch_Error =
179 PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL);
185 PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL);
180 Py_INCREF(mpatch_Error);
186 Py_INCREF(mpatch_Error);
181 PyModule_AddObject(m, "mpatchError", mpatch_Error);
187 PyModule_AddObject(m, "mpatchError", mpatch_Error);
182 PyModule_AddIntConstant(m, "version", version);
188 PyModule_AddIntConstant(m, "version", version);
183
189
184 return m;
190 return m;
185 }
191 }
186 #else
192 #else
187 PyMODINIT_FUNC initmpatch(void)
193 PyMODINIT_FUNC initmpatch(void)
188 {
194 {
189 PyObject *m;
195 PyObject *m;
190 m = Py_InitModule3("mpatch", methods, mpatch_doc);
196 m = Py_InitModule3("mpatch", methods, mpatch_doc);
191 mpatch_Error =
197 mpatch_Error =
192 PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL);
198 PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL);
193 PyModule_AddIntConstant(m, "version", version);
199 PyModule_AddIntConstant(m, "version", version);
194 }
200 }
195 #endif
201 #endif
General Comments 0
You need to be logged in to leave comments. Login now