ryanmce created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers.
REVISION SUMMARY Implement a `mercurial.cext.xdiff` module that exposes the xdiff algorithm. `xdiff.blocks` should be a drop-in replacement for `bdiff.blocks`. In theory we can change the pure C version of `bdiff.c` directly. However that means we lose bdiff entirely. It seems more flexible to have both at the same time so they can be easily switched via Python code. Hence the Python module approach. TEST PLAN `make local`. And test from `hg debugshell`: In [1]: from mercurial.cext import bdiff, xdiff In [2]: bdiff.blocks('b\nc\nd\n', 'a\nb\nc\ne\nf\n') Out[2]: [(0, 2, 1, 3), (3, 3, 5, 5)] In [3]: xdiff.blocks('b\nc\nd\n', 'a\nb\nc\ne\nf\n') Out[3]: [(0, 2, 1, 3), (3, 3, 5, 5)] In [4]: bdiff.blocks('a\nb', '') Out[4]: [(2, 2, 0, 0)] In [5]: xdiff.blocks('a\nb', '') Out[5]: [(2, 2, 0, 0)] In [6]: bdiff.blocks('a\nb', 'a\nb\n') Out[6]: [(0, 1, 0, 1), (2, 2, 2, 2)] In [7]: xdiff.blocks('a\nb', 'a\nb\n') Out[7]: [(0, 1, 0, 1), (2, 2, 2, 2)] In [8]: xdiff.blocks('', '') Out[8]: [(0, 0, 0, 0)] In [9]: bdiff.blocks('', '') Out[9]: [(0, 0, 0, 0)] In [10]: xdiff.blocks('', '\n') Out[10]: [(0, 0, 1, 1)] REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D2576 AFFECTED FILES contrib/check-code.py mercurial/thirdparty/xdiff.c setup.py CHANGE DETAILS diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -220,7 +220,7 @@ py2exe.Distribution # silence unused import warning py2exeloaded = True # import py2exe's patched Distribution class - from distutils.core import Distribution + from distutils.core import Distribution # noqa: F811 except ImportError: py2exeloaded = False @@ -874,6 +874,28 @@ extra_compile_args=osutil_cflags, extra_link_args=osutil_ldflags, depends=common_depends), + Extension('mercurial.cext.xdiff', + sources=[ + 'mercurial/thirdparty/xdiff/xdiffi.c', + 'mercurial/thirdparty/xdiff/xemit.c', + 'mercurial/thirdparty/xdiff/xmerge.c', + 'mercurial/thirdparty/xdiff/xprepare.c', + 'mercurial/thirdparty/xdiff/xutils.c', + 'mercurial/thirdparty/xdiff.c', + ], + include_dirs=common_include_dirs + [ + 'mercurial/thirdparty/xdiff', + ], + depends=common_depends + [ + 'mercurial/thirdparty/xdiff/xdiff.h', + 'mercurial/thirdparty/xdiff/xdiffi.h', + 'mercurial/thirdparty/xdiff/xemit.h', + 'mercurial/thirdparty/xdiff/xinclude.h', + 'mercurial/thirdparty/xdiff/xmacros.h', + 'mercurial/thirdparty/xdiff/xprepare.h', + 'mercurial/thirdparty/xdiff/xtypes.h', + 'mercurial/thirdparty/xdiff/xutils.h', + ]), Extension('hgext.fsmonitor.pywatchman.bser', ['hgext/fsmonitor/pywatchman/bser.c']), ] diff --git a/mercurial/thirdparty/xdiff.c b/mercurial/thirdparty/xdiff.c new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/xdiff.c @@ -0,0 +1,96 @@ +/* + xdiff.c: simple Python wrapper for xdiff library + + Copyright (c) 2018 Facebook, Inc. + + This software may be used and distributed according to the terms of the + GNU General Public License version 2 or any later version. +*/ + +#include "xdiff.h" +#include "Python.h" + +static int hunk_consumer(long a1, long a2, long b1, long b2, void *priv) +{ + PyObject *rl = (PyObject *)priv; + PyObject *m = Py_BuildValue("llll", a1, a2, b1, b2); + if (!m) + return -1; + if (PyList_Append(rl, m) != 0) { + Py_DECREF(m); + return -1; + } + return 0; +} + +static PyObject *blocks(PyObject *self, PyObject *args) +{ + char *sa, *sb; + int na, nb; + + if (!PyArg_ParseTuple(args, "s#s#", &sa, &na, &sb, &nb)) + return NULL; + + mmfile_t a = {sa, na}, b = {sb, nb}; + + PyObject *rl = PyList_New(0); + if (!rl) + return PyErr_NoMemory(); + + xpparam_t xpp = { + 0, /* flags */ + NULL, /* anchors */ + 0, /* anchors_nr */ + }; + xdemitconf_t xecfg = { + 0, /* ctxlen */ + 0, /* interhunkctxlen */ + XDL_EMIT_BDIFFHUNK, /* flags */ + NULL, /* find_func */ + NULL, /* find_func_priv */ + hunk_consumer, /* hunk_consume_func */ + }; + xdemitcb_t ecb = { + rl, /* priv */ + NULL, /* outf */ + }; + + if (xdl_diff(&a, &b, &xpp, &xecfg, &ecb) != 0) { + Py_DECREF(rl); + return PyErr_NoMemory(); + } + + return rl; +} + +static char xdiff_doc[] = "xdiff wrapper"; + +static PyMethodDef methods[] = { + {"blocks", blocks, METH_VARARGS, + "(a: str, b: str) -> List[(a1, a2, b1, b2)].\n" + "Yield matched blocks. (a1, a2, b1, b2) are line numbers.\n"}, + {NULL, NULL}, +}; + +static const int version = 1; + +#ifdef IS_PY3K +static struct PyModuleDef bdiff_module = { + PyModuleDef_HEAD_INIT, "bdiff", xdiff_doc, -1, methods, +}; + +PyMODINIT_FUNC PyInit_xdiff(void) +{ + PyObject *m; + m = PyModule_Create(&xdiff_module); + PyModule_AddIntConstant(m, "version", version); + return m; +} +#else +PyMODINIT_FUNC initxdiff(void) +{ + PyObject *m; + m = Py_InitModule3("xdiff", methods, xdiff_doc); + PyModule_AddIntConstant(m, "version", version); +} +#endif diff --git a/contrib/check-code.py b/contrib/check-code.py --- a/contrib/check-code.py +++ b/contrib/check-code.py @@ -249,7 +249,8 @@ (r"[^_]_\([ \t\n]*(?:'[^']+'[ \t\n+]*)+%", "don't use % inside _()"), (r'(\w|\)),\w', "missing whitespace after ,"), (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"), - (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"), +# false positive on multi-line keyword arguments +# (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"), (r'\w\s=\s\s+\w', "gratuitous whitespace after ="), (( # a line ending with a colon, potentially with trailing comments To: ryanmce, #hg-reviewers Cc: mercurial-devel _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel