[PATCH 3 of 3 v3] mpatch: implement cffi version of mpatch
Maciej Fijalkowski
fijall at gmail.com
Mon Jul 4 09:16:25 UTC 2016
# HG changeset patch
# User Maciej Fijalkowski <fijall at gmail.com>
# Date 1466664974 -7200
# Thu Jun 23 08:56:14 2016 +0200
# Node ID 5c1eec7c6b0d6363f5d09fada4180ee2470f17c6
# Parent 2752064613b03fb4c8dd636b173f5bfb64f4cfba
mpatch: implement cffi version of mpatch
This is a first go at working, but not 100% efficient version of mpatch
for PyPy that uses cffi. Additional improvements might follow, but it
already makes mercurial far more usable under pypy.
diff -r 2752064613b0 -r 5c1eec7c6b0d mercurial/__init__.py
--- a/mercurial/__init__.py Tue Jun 07 15:35:58 2016 +0200
+++ b/mercurial/__init__.py Thu Jun 23 08:56:14 2016 +0200
@@ -19,6 +19,7 @@
__all__ = []
modulepolicy = policy.policy
+policynoc = policy.policynoc
# Modules that have both Python and C implementations. See also the
# set of .py files under mercurial/pure/.
diff -r 2752064613b0 -r 5c1eec7c6b0d mercurial/build_mpatch_cffi.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/build_mpatch_cffi.py Thu Jun 23 08:56:14 2016 +0200
@@ -0,0 +1,35 @@
+
+from __future__ import absolute_import
+
+import os
+import cffi
+
+ffi = cffi.FFI()
+mpatch_c = os.path.join(os.path.dirname(__file__), 'mpatch.c')
+ffi.set_source("_mpatch_cffi", open(mpatch_c).read(),
+ include_dirs=["mercurial"])
+ffi.cdef("""
+
+struct mpatch_frag {
+ int start, end, len;
+ const char *data;
+};
+
+struct mpatch_flist {
+ struct mpatch_frag *base, *head, *tail;
+};
+
+extern "Python" struct mpatch_flist* cffi_get_next_item(void*, ssize_t);
+
+int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist** res);
+ssize_t mpatch_calcsize(size_t len, struct mpatch_flist *l);
+void mpatch_lfree(struct mpatch_flist *a);
+static int mpatch_apply(char *buf, const char *orig, size_t len,
+ struct mpatch_flist *l);
+struct mpatch_flist *mpatch_fold(void *bins,
+ struct mpatch_flist* (*get_next_item)(void*, ssize_t),
+ ssize_t start, ssize_t end);
+""")
+
+if __name__ == '__main__':
+ ffi.compile()
diff -r 2752064613b0 -r 5c1eec7c6b0d mercurial/mpatch_module.c
--- a/mercurial/mpatch_module.c Tue Jun 07 15:35:58 2016 +0200
+++ b/mercurial/mpatch_module.c Thu Jun 23 08:56:14 2016 +0200
@@ -10,7 +10,7 @@
static PyObject *mpatch_Error;
-struct mpatch_flist *cpygetnextitem(void *bins, ssize_t pos)
+struct mpatch_flist *cpygetitem(void *bins, ssize_t pos)
{
const char *buffer;
struct mpatch_flist *res;
@@ -52,7 +52,7 @@
if (PyObject_AsCharBuffer(text, &in, &inlen))
return NULL;
- patch = mpatch_fold(bins, cpygetnextitem, 0, len);
+ patch = fold(bins, cpygetitem, 0, len);
if (!patch) {
return NULL;
}
diff -r 2752064613b0 -r 5c1eec7c6b0d mercurial/pure/mpatch.py
--- a/mercurial/pure/mpatch.py Tue Jun 07 15:35:58 2016 +0200
+++ b/mercurial/pure/mpatch.py Thu Jun 23 08:56:14 2016 +0200
@@ -9,8 +9,10 @@
import struct
-from . import pycompat
+from . import policy, pycompat
stringio = pycompat.stringio
+modulepolicy = policy.policy
+policynocffi = policy.policynocffi
class mpatchError(Exception):
"""error raised when a delta cannot be decoded
@@ -125,3 +127,43 @@
outlen += orig - last
return outlen
+
+if modulepolicy not in policynocffi:
+ try:
+ from _mpatch_cffi import ffi, lib
+ except ImportError:
+ if modulepolicy == 'cffi': # strict cffi import
+ raise
+ else:
+ @ffi.def_extern()
+ def cffi_get_next_item(arg, pos):
+ all, bins = ffi.from_handle(arg)
+ container = ffi.new("struct mpatch_flist*[1]")
+ to_pass = ffi.new("char[]", str(bins[pos]))
+ all.append(to_pass)
+ r = lib.mpatch_decode(to_pass, len(to_pass) - 1, container)
+ if r < 0:
+ return ffi.NULL
+ return container[0]
+
+ def patches(text, bins):
+ lgt = len(bins)
+ all = []
+ if not lgt:
+ return text
+ arg = (all, bins)
+ patch = lib.mpatch_fold(ffi.new_handle(arg),
+ lib.cffi_get_next_item, 0, lgt)
+ if not patch:
+ raise mpatchError("cannot decode chunk")
+ outlen = lib.mpatch_calcsize(len(text), patch)
+ if outlen < 0:
+ lib.mpatch_lfree(patch)
+ raise mpatchError("inconsistency detected")
+ buf = ffi.new("char[]", outlen)
+ if lib.mpatch_apply(buf, text, len(text), patch) < 0:
+ lib.mpatch_lfree(patch)
+ raise mpatchError("error applying patches")
+ res = ffi.buffer(buf, outlen)[:]
+ lib.mpatch_lfree(patch)
+ return res
diff -r 2752064613b0 -r 5c1eec7c6b0d setup.py
--- a/setup.py Tue Jun 07 15:35:58 2016 +0200
+++ b/setup.py Thu Jun 23 08:56:14 2016 +0200
@@ -262,7 +262,8 @@
class hgdist(Distribution):
- pure = ispypy
+ pure = False
+ cffi = ispypy
global_options = Distribution.global_options + \
[('pure', None, "use pure (slow) Python "
@@ -316,6 +317,10 @@
if self.distribution.pure:
self.distribution.ext_modules = []
+ elif self.distribution.cffi:
+ from mercurial import build_mpatch_cffi
+ exts = [build_mpatch_cffi.ffi.distutils_extension()]
+ self.distribution.ext_modules = exts
else:
h = os.path.join(get_python_inc(), 'Python.h')
if not os.path.exists(h):
More information about the Mercurial-devel
mailing list