Patchwork D2587: cext: accept arguments as Py_buffer

login
register
mail settings
Submitter phabricator
Date March 3, 2018, 4:28 p.m.
Message ID <differential-rev-PHID-DREV-or2eswa2p2z3u3b5lrmg-req@phab.mercurial-scm.org>
Download mbox | patch
Permalink /patch/28788/
State Superseded
Headers show

Comments

phabricator - March 3, 2018, 4:28 p.m.
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The s*/y* value formatters receive a Py_buffer instead of a char *.
  This value format is more flexible in the types that it allows.
  
  We change bdiff() to accept any object that conforms to the buffer
  protocol. We validate the buffers are contiguous and have a single
  dimension.
  
  This allows memoryview instances to be handled by the function, so
  we revert a recent change to cast arguments to bytes before calling
  this function.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2587

AFFECTED FILES
  mercurial/cext/bdiff.c
  mercurial/mdiff.py

CHANGE DETAILS




To: indygreg, #hg-reviewers
Cc: mercurial-devel

Patch

diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py
--- a/mercurial/mdiff.py
+++ b/mercurial/mdiff.py
@@ -30,17 +30,9 @@ 
 fixws = bdiff.fixws
 patches = mpatch.patches
 patchedsize = mpatch.patchedsize
-_textdiff = bdiff.bdiff
+textdiff = bdiff.bdiff
 splitnewlines = bdiff.splitnewlines
 
-# On Python 3, util.buffer() creates a memoryview, which appears not
-# supporting the buffer protocol
-if pycompat.ispy3:
-    def textdiff(a, b):
-        return _textdiff(bytes(a), bytes(b))
-else:
-    textdiff = _textdiff
-
 class diffopts(object):
     '''context is the number of context lines
     text treats all files as text
diff --git a/mercurial/cext/bdiff.c b/mercurial/cext/bdiff.c
--- a/mercurial/cext/bdiff.c
+++ b/mercurial/cext/bdiff.c
@@ -60,7 +60,8 @@ 
 
 static PyObject *bdiff(PyObject *self, PyObject *args)
 {
-	char *sa, *sb, *rb, *ia, *ib;
+	Py_buffer ba, bb;
+	char *rb, *ia, *ib;
 	PyObject *result = NULL;
 	struct bdiff_line *al = NULL, *bl = NULL;
 	struct bdiff_hunk l, *h;
@@ -70,25 +71,40 @@ 
 
 	l.next = NULL;
 
-	if (!PyArg_ParseTuple(args, PY23("s#s#:bdiff", "y#y#:bdiff"), &sa, &la,
-	                      &sb, &lb))
+	if (!PyArg_ParseTuple(args, PY23("s*s*:bdiff", "y*y*:bdiff"), &ba, &bb))
 		return NULL;
 
+	if (!PyBuffer_IsContiguous(&ba, 'C') || ba.ndim > 1) {
+		PyErr_SetString(PyExc_ValueError, "bdiff input not contiguous");
+		goto cleanup;
+	}
+
+	if (!PyBuffer_IsContiguous(&bb, 'C') || bb.ndim > 1) {
+		PyErr_SetString(PyExc_ValueError, "bdiff input not contiguous");
+		goto cleanup;
+	}
+
+	la = ba.len;
+	lb = bb.len;
+
 	if (la > UINT_MAX || lb > UINT_MAX) {
 		PyErr_SetString(PyExc_ValueError, "bdiff inputs too large");
-		return NULL;
+		goto cleanup;
 	}
 
 	_save = PyEval_SaveThread();
 
 	lmax = la > lb ? lb : la;
-	for (ia = sa, ib = sb; li < lmax && *ia == *ib; ++li, ++ia, ++ib)
+	for (ia = ba.buf, ib = bb.buf;
+	     li < lmax && *ia == *ib;
+	     ++li, ++ia, ++ib) {
 		if (*ia == '\n')
 			lcommon = li + 1;
+	}
 	/* we can almost add: if (li == lmax) lcommon = li; */
 
-	an = bdiff_splitlines(sa + lcommon, la - lcommon, &al);
-	bn = bdiff_splitlines(sb + lcommon, lb - lcommon, &bl);
+	an = bdiff_splitlines(ba.buf + lcommon, la - lcommon, &al);
+	bn = bdiff_splitlines(bb.buf + lcommon, lb - lcommon, &bl);
 	if (!al || !bl) {
 		PyErr_NoMemory();
 		goto cleanup;
@@ -137,6 +153,8 @@ 
 cleanup:
 	if (_save)
 		PyEval_RestoreThread(_save);
+	PyBuffer_Release(&ba);
+	PyBuffer_Release(&bb);
 	if (al) {
 		free(al);
 	}