comparison mercurial/cext/mpatch.c @ 32371:151cc3b3d799

mpatch: switch to policy importer
author Yuya Nishihara <yuya@tcha.org>
date Sat, 13 Aug 2016 12:18:58 +0900
parents mercurial/mpatch_module.c@5fc3459d0493
children b90e8da190da
comparison
equal deleted inserted replaced
32370:017ad85e5ac8 32371:151cc3b3d799
1 /*
2 mpatch.c - efficient binary patching for Mercurial
3
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.
6
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
9 single hunk list. This hunk list is then applied to the original
10 text.
11
12 The text (or binary) fragments are copied directly from their source
13 Python objects into a preallocated output string to avoid the
14 allocation of intermediate Python objects. Working memory is about 2x
15 the total number of hunks.
16
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
18
19 This software may be used and distributed according to the terms
20 of the GNU General Public License, incorporated herein by reference.
21 */
22
23 #define PY_SSIZE_T_CLEAN
24 #include <Python.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "util.h"
29 #include "bitmanipulation.h"
30 #include "compat.h"
31 #include "mpatch.h"
32
33 static char mpatch_doc[] = "Efficient binary patching.";
34 static PyObject *mpatch_Error;
35
36 static void setpyerr(int r)
37 {
38 switch (r) {
39 case MPATCH_ERR_NO_MEM:
40 PyErr_NoMemory();
41 break;
42 case MPATCH_ERR_CANNOT_BE_DECODED:
43 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
44 break;
45 case MPATCH_ERR_INVALID_PATCH:
46 PyErr_SetString(mpatch_Error, "invalid patch");
47 break;
48 }
49 }
50
51 struct mpatch_flist *cpygetitem(void *bins, ssize_t pos)
52 {
53 const char *buffer;
54 struct mpatch_flist *res;
55 ssize_t blen;
56 int r;
57
58 PyObject *tmp = PyList_GetItem((PyObject*)bins, pos);
59 if (!tmp)
60 return NULL;
61 if (PyObject_AsCharBuffer(tmp, &buffer, (Py_ssize_t*)&blen))
62 return NULL;
63 if ((r = mpatch_decode(buffer, blen, &res)) < 0) {
64 if (!PyErr_Occurred())
65 setpyerr(r);
66 return NULL;
67 }
68 return res;
69 }
70
71 static PyObject *
72 patches(PyObject *self, PyObject *args)
73 {
74 PyObject *text, *bins, *result;
75 struct mpatch_flist *patch;
76 const char *in;
77 int r = 0;
78 char *out;
79 Py_ssize_t len, outlen, inlen;
80
81 if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins))
82 return NULL;
83
84 len = PyList_Size(bins);
85 if (!len) {
86 /* nothing to do */
87 Py_INCREF(text);
88 return text;
89 }
90
91 if (PyObject_AsCharBuffer(text, &in, &inlen))
92 return NULL;
93
94 patch = mpatch_fold(bins, cpygetitem, 0, len);
95 if (!patch) { /* error already set or memory error */
96 if (!PyErr_Occurred())
97 PyErr_NoMemory();
98 return NULL;
99 }
100
101 outlen = mpatch_calcsize(inlen, patch);
102 if (outlen < 0) {
103 r = (int)outlen;
104 result = NULL;
105 goto cleanup;
106 }
107 result = PyBytes_FromStringAndSize(NULL, outlen);
108 if (!result) {
109 result = NULL;
110 goto cleanup;
111 }
112 out = PyBytes_AsString(result);
113 if ((r = mpatch_apply(out, in, inlen, patch)) < 0) {
114 Py_DECREF(result);
115 result = NULL;
116 }
117 cleanup:
118 mpatch_lfree(patch);
119 if (!result && !PyErr_Occurred())
120 setpyerr(r);
121 return result;
122 }
123
124 /* calculate size of a patched file directly */
125 static PyObject *
126 patchedsize(PyObject *self, PyObject *args)
127 {
128 long orig, start, end, len, outlen = 0, last = 0, pos = 0;
129 Py_ssize_t patchlen;
130 char *bin;
131
132 if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
133 return NULL;
134
135 while (pos >= 0 && pos < patchlen) {
136 start = getbe32(bin + pos);
137 end = getbe32(bin + pos + 4);
138 len = getbe32(bin + pos + 8);
139 if (start > end)
140 break; /* sanity check */
141 pos += 12 + len;
142 outlen += start - last;
143 last = end;
144 outlen += len;
145 }
146
147 if (pos != patchlen) {
148 if (!PyErr_Occurred())
149 PyErr_SetString(mpatch_Error, "patch cannot be decoded");
150 return NULL;
151 }
152
153 outlen += orig - last;
154 return Py_BuildValue("l", outlen);
155 }
156
157 static PyMethodDef methods[] = {
158 {"patches", patches, METH_VARARGS, "apply a series of patches\n"},
159 {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"},
160 {NULL, NULL}
161 };
162
163 static const int version = 1;
164
165 #ifdef IS_PY3K
166 static struct PyModuleDef mpatch_module = {
167 PyModuleDef_HEAD_INIT,
168 "mpatch",
169 mpatch_doc,
170 -1,
171 methods
172 };
173
174 PyMODINIT_FUNC PyInit_mpatch(void)
175 {
176 PyObject *m;
177
178 m = PyModule_Create(&mpatch_module);
179 if (m == NULL)
180 return NULL;
181
182 mpatch_Error = PyErr_NewException("mercurial.cext.mpatch.mpatchError",
183 NULL, NULL);
184 Py_INCREF(mpatch_Error);
185 PyModule_AddObject(m, "mpatchError", mpatch_Error);
186 PyModule_AddIntConstant(m, "version", version);
187
188 return m;
189 }
190 #else
191 PyMODINIT_FUNC
192 initmpatch(void)
193 {
194 PyObject *m;
195 m = Py_InitModule3("mpatch", methods, mpatch_doc);
196 mpatch_Error = PyErr_NewException("mercurial.cext.mpatch.mpatchError",
197 NULL, NULL);
198 PyModule_AddIntConstant(m, "version", version);
199 }
200 #endif