##// END OF EJS Templates
cext: use modern buffer protocol in patches()...
Gregory Szorc -
r40027:77492c10 default
parent child Browse files
Show More
@@ -1,201 +1,204 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 Py_buffer buffer;
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;
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_GetBuffer(text, &buffer, PyBUF_CONTIG_RO)) {
91 return NULL;
91 return NULL;
92 }
92
93
93 patch = mpatch_fold(bins, cpygetitem, 0, len);
94 patch = mpatch_fold(bins, cpygetitem, 0, len);
94 if (!patch) { /* error already set or memory error */
95 if (!patch) { /* error already set or memory error */
95 if (!PyErr_Occurred())
96 if (!PyErr_Occurred())
96 PyErr_NoMemory();
97 PyErr_NoMemory();
97 return NULL;
98 result = NULL;
99 goto cleanup;
98 }
100 }
99
101
100 outlen = mpatch_calcsize(inlen, patch);
102 outlen = mpatch_calcsize(buffer.len, patch);
101 if (outlen < 0) {
103 if (outlen < 0) {
102 r = (int)outlen;
104 r = (int)outlen;
103 result = NULL;
105 result = NULL;
104 goto cleanup;
106 goto cleanup;
105 }
107 }
106 result = PyBytes_FromStringAndSize(NULL, outlen);
108 result = PyBytes_FromStringAndSize(NULL, outlen);
107 if (!result) {
109 if (!result) {
108 result = NULL;
110 result = NULL;
109 goto cleanup;
111 goto cleanup;
110 }
112 }
111 out = PyBytes_AsString(result);
113 out = PyBytes_AsString(result);
112 /* clang-format off */
114 /* clang-format off */
113 {
115 {
114 Py_BEGIN_ALLOW_THREADS
116 Py_BEGIN_ALLOW_THREADS
115 r = mpatch_apply(out, in, inlen, patch);
117 r = mpatch_apply(out, buffer.buf, buffer.len, patch);
116 Py_END_ALLOW_THREADS
118 Py_END_ALLOW_THREADS
117 }
119 }
118 /* clang-format on */
120 /* clang-format on */
119 if (r < 0) {
121 if (r < 0) {
120 Py_DECREF(result);
122 Py_DECREF(result);
121 result = NULL;
123 result = NULL;
122 }
124 }
123 cleanup:
125 cleanup:
124 mpatch_lfree(patch);
126 mpatch_lfree(patch);
127 PyBuffer_Release(&buffer);
125 if (!result && !PyErr_Occurred())
128 if (!result && !PyErr_Occurred())
126 setpyerr(r);
129 setpyerr(r);
127 return result;
130 return result;
128 }
131 }
129
132
130 /* calculate size of a patched file directly */
133 /* calculate size of a patched file directly */
131 static PyObject *patchedsize(PyObject *self, PyObject *args)
134 static PyObject *patchedsize(PyObject *self, PyObject *args)
132 {
135 {
133 long orig, start, end, len, outlen = 0, last = 0, pos = 0;
136 long orig, start, end, len, outlen = 0, last = 0, pos = 0;
134 Py_ssize_t patchlen;
137 Py_ssize_t patchlen;
135 char *bin;
138 char *bin;
136
139
137 if (!PyArg_ParseTuple(args, PY23("ls#", "ly#"), &orig, &bin, &patchlen))
140 if (!PyArg_ParseTuple(args, PY23("ls#", "ly#"), &orig, &bin, &patchlen))
138 return NULL;
141 return NULL;
139
142
140 while (pos >= 0 && pos < patchlen) {
143 while (pos >= 0 && pos < patchlen) {
141 start = getbe32(bin + pos);
144 start = getbe32(bin + pos);
142 end = getbe32(bin + pos + 4);
145 end = getbe32(bin + pos + 4);
143 len = getbe32(bin + pos + 8);
146 len = getbe32(bin + pos + 8);
144 if (start > end)
147 if (start > end)
145 break; /* sanity check */
148 break; /* sanity check */
146 pos += 12 + len;
149 pos += 12 + len;
147 outlen += start - last;
150 outlen += start - last;
148 last = end;
151 last = end;
149 outlen += len;
152 outlen += len;
150 }
153 }
151
154
152 if (pos != patchlen) {
155 if (pos != patchlen) {
153 if (!PyErr_Occurred())
156 if (!PyErr_Occurred())
154 PyErr_SetString(mpatch_Error,
157 PyErr_SetString(mpatch_Error,
155 "patch cannot be decoded");
158 "patch cannot be decoded");
156 return NULL;
159 return NULL;
157 }
160 }
158
161
159 outlen += orig - last;
162 outlen += orig - last;
160 return Py_BuildValue("l", outlen);
163 return Py_BuildValue("l", outlen);
161 }
164 }
162
165
163 static PyMethodDef methods[] = {
166 static PyMethodDef methods[] = {
164 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
167 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
165 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
168 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
166 {NULL, NULL},
169 {NULL, NULL},
167 };
170 };
168
171
169 static const int version = 1;
172 static const int version = 1;
170
173
171 #ifdef IS_PY3K
174 #ifdef IS_PY3K
172 static struct PyModuleDef mpatch_module = {
175 static struct PyModuleDef mpatch_module = {
173 PyModuleDef_HEAD_INIT, "mpatch", mpatch_doc, -1, methods,
176 PyModuleDef_HEAD_INIT, "mpatch", mpatch_doc, -1, methods,
174 };
177 };
175
178
176 PyMODINIT_FUNC PyInit_mpatch(void)
179 PyMODINIT_FUNC PyInit_mpatch(void)
177 {
180 {
178 PyObject *m;
181 PyObject *m;
179
182
180 m = PyModule_Create(&mpatch_module);
183 m = PyModule_Create(&mpatch_module);
181 if (m == NULL)
184 if (m == NULL)
182 return NULL;
185 return NULL;
183
186
184 mpatch_Error =
187 mpatch_Error =
185 PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL);
188 PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL);
186 Py_INCREF(mpatch_Error);
189 Py_INCREF(mpatch_Error);
187 PyModule_AddObject(m, "mpatchError", mpatch_Error);
190 PyModule_AddObject(m, "mpatchError", mpatch_Error);
188 PyModule_AddIntConstant(m, "version", version);
191 PyModule_AddIntConstant(m, "version", version);
189
192
190 return m;
193 return m;
191 }
194 }
192 #else
195 #else
193 PyMODINIT_FUNC initmpatch(void)
196 PyMODINIT_FUNC initmpatch(void)
194 {
197 {
195 PyObject *m;
198 PyObject *m;
196 m = Py_InitModule3("mpatch", methods, mpatch_doc);
199 m = Py_InitModule3("mpatch", methods, mpatch_doc);
197 mpatch_Error =
200 mpatch_Error =
198 PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL);
201 PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL);
199 PyModule_AddIntConstant(m, "version", version);
202 PyModule_AddIntConstant(m, "version", version);
200 }
203 }
201 #endif
204 #endif
General Comments 0
You need to be logged in to leave comments. Login now