mpatch.c
204 lines
| 4.5 KiB
| text/x-c
|
CLexer
|
r32371 | /* | ||
mpatch.c - efficient binary patching for Mercurial | ||||
This implements a patch algorithm that's O(m + nlog n) where m is the | ||||
size of the output and n is the number of patches. | ||||
Given a list of binary patches, it unpacks each into a hunk list, | ||||
then combines the hunk lists with a treewise recursion to form a | ||||
single hunk list. This hunk list is then applied to the original | ||||
text. | ||||
The text (or binary) fragments are copied directly from their source | ||||
Python objects into a preallocated output string to avoid the | ||||
allocation of intermediate Python objects. Working memory is about 2x | ||||
the total number of hunks. | ||||
|
r47575 | Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com> | ||
|
r32371 | |||
This software may be used and distributed according to the terms | ||||
of the GNU General Public License, incorporated herein by reference. | ||||
*/ | ||||
#define PY_SSIZE_T_CLEAN | ||||
#include <Python.h> | ||||
#include <stdlib.h> | ||||
#include <string.h> | ||||
#include "bitmanipulation.h" | ||||
#include "compat.h" | ||||
#include "mpatch.h" | ||||
|
r34439 | #include "util.h" | ||
|
r32371 | |||
static char mpatch_doc[] = "Efficient binary patching."; | ||||
static PyObject *mpatch_Error; | ||||
static void setpyerr(int r) | ||||
{ | ||||
switch (r) { | ||||
case MPATCH_ERR_NO_MEM: | ||||
PyErr_NoMemory(); | ||||
break; | ||||
case MPATCH_ERR_CANNOT_BE_DECODED: | ||||
PyErr_SetString(mpatch_Error, "patch cannot be decoded"); | ||||
break; | ||||
case MPATCH_ERR_INVALID_PATCH: | ||||
PyErr_SetString(mpatch_Error, "invalid patch"); | ||||
break; | ||||
} | ||||
} | ||||
struct mpatch_flist *cpygetitem(void *bins, ssize_t pos) | ||||
{ | ||||
|
r40028 | Py_buffer buffer; | ||
struct mpatch_flist *res = NULL; | ||||
|
r32371 | int r; | ||
|
r36245 | PyObject *tmp = PyList_GetItem((PyObject *)bins, pos); | ||
|
r41367 | if (!tmp) { | ||
|
r32371 | return NULL; | ||
|
r41367 | } | ||
if (PyObject_GetBuffer(tmp, &buffer, PyBUF_CONTIG_RO)) { | ||||
|
r32371 | return NULL; | ||
|
r41367 | } | ||
|
r40028 | if ((r = mpatch_decode(buffer.buf, buffer.len, &res)) < 0) { | ||
|
r41367 | if (!PyErr_Occurred()) { | ||
|
r32371 | setpyerr(r); | ||
|
r41367 | } | ||
|
r40028 | res = NULL; | ||
|
r32371 | } | ||
|
r40028 | |||
PyBuffer_Release(&buffer); | ||||
|
r32371 | return res; | ||
} | ||||
|
r36245 | static PyObject *patches(PyObject *self, PyObject *args) | ||
|
r32371 | { | ||
PyObject *text, *bins, *result; | ||||
struct mpatch_flist *patch; | ||||
|
r40027 | Py_buffer buffer; | ||
|
r32371 | int r = 0; | ||
char *out; | ||||
|
r40027 | Py_ssize_t len, outlen; | ||
|
r32371 | |||
|
r41367 | if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins)) { | ||
|
r32371 | return NULL; | ||
|
r41367 | } | ||
|
r32371 | |||
len = PyList_Size(bins); | ||||
if (!len) { | ||||
/* nothing to do */ | ||||
Py_INCREF(text); | ||||
return text; | ||||
} | ||||
|
r40027 | if (PyObject_GetBuffer(text, &buffer, PyBUF_CONTIG_RO)) { | ||
|
r32371 | return NULL; | ||
|
r40027 | } | ||
|
r32371 | |||
patch = mpatch_fold(bins, cpygetitem, 0, len); | ||||
if (!patch) { /* error already set or memory error */ | ||||
|
r41367 | if (!PyErr_Occurred()) { | ||
|
r32371 | PyErr_NoMemory(); | ||
|
r41367 | } | ||
|
r40027 | result = NULL; | ||
goto cleanup; | ||||
|
r32371 | } | ||
|
r40027 | outlen = mpatch_calcsize(buffer.len, patch); | ||
|
r32371 | if (outlen < 0) { | ||
r = (int)outlen; | ||||
result = NULL; | ||||
goto cleanup; | ||||
} | ||||
result = PyBytes_FromStringAndSize(NULL, outlen); | ||||
if (!result) { | ||||
result = NULL; | ||||
goto cleanup; | ||||
} | ||||
out = PyBytes_AsString(result); | ||||
|
r36381 | /* clang-format off */ | ||
{ | ||||
Py_BEGIN_ALLOW_THREADS | ||||
|
r40027 | r = mpatch_apply(out, buffer.buf, buffer.len, patch); | ||
|
r36381 | Py_END_ALLOW_THREADS | ||
} | ||||
/* clang-format on */ | ||||
|
r35959 | if (r < 0) { | ||
|
r32371 | Py_DECREF(result); | ||
result = NULL; | ||||
} | ||||
cleanup: | ||||
mpatch_lfree(patch); | ||||
|
r40027 | PyBuffer_Release(&buffer); | ||
|
r41367 | if (!result && !PyErr_Occurred()) { | ||
|
r32371 | setpyerr(r); | ||
|
r41367 | } | ||
|
r32371 | return result; | ||
} | ||||
/* calculate size of a patched file directly */ | ||||
|
r36245 | static PyObject *patchedsize(PyObject *self, PyObject *args) | ||
|
r32371 | { | ||
long orig, start, end, len, outlen = 0, last = 0, pos = 0; | ||||
Py_ssize_t patchlen; | ||||
char *bin; | ||||
|
r41367 | if (!PyArg_ParseTuple(args, PY23("ls#", "ly#"), &orig, &bin, | ||
&patchlen)) { | ||||
|
r32371 | return NULL; | ||
|
r41367 | } | ||
|
r32371 | |||
while (pos >= 0 && pos < patchlen) { | ||||
start = getbe32(bin + pos); | ||||
end = getbe32(bin + pos + 4); | ||||
len = getbe32(bin + pos + 8); | ||||
|
r41367 | if (start > end) { | ||
|
r32371 | break; /* sanity check */ | ||
|
r41367 | } | ||
|
r32371 | pos += 12 + len; | ||
outlen += start - last; | ||||
last = end; | ||||
outlen += len; | ||||
} | ||||
if (pos != patchlen) { | ||||
|
r41367 | if (!PyErr_Occurred()) { | ||
|
r36245 | PyErr_SetString(mpatch_Error, | ||
"patch cannot be decoded"); | ||||
|
r41367 | } | ||
|
r32371 | return NULL; | ||
} | ||||
outlen += orig - last; | ||||
return Py_BuildValue("l", outlen); | ||||
} | ||||
static PyMethodDef methods[] = { | ||||
|
r36245 | {"patches", patches, METH_VARARGS, "apply a series of patches\n"}, | ||
{"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"}, | ||||
{NULL, NULL}, | ||||
|
r32371 | }; | ||
static const int version = 1; | ||||
static struct PyModuleDef mpatch_module = { | ||||
|
r36245 | PyModuleDef_HEAD_INIT, "mpatch", mpatch_doc, -1, methods, | ||
|
r32371 | }; | ||
PyMODINIT_FUNC PyInit_mpatch(void) | ||||
{ | ||||
PyObject *m; | ||||
m = PyModule_Create(&mpatch_module); | ||||
if (m == NULL) | ||||
return NULL; | ||||
|
r36245 | mpatch_Error = | ||
PyErr_NewException("mercurial.cext.mpatch.mpatchError", NULL, NULL); | ||||
|
r32371 | Py_INCREF(mpatch_Error); | ||
PyModule_AddObject(m, "mpatchError", mpatch_Error); | ||||
PyModule_AddIntConstant(m, "version", version); | ||||
return m; | ||||
} | ||||