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