[Python-checkins] cpython: Issue #22445: PyBuffer_IsContiguous() now implements precise contiguity

stefan.krah python-checkins at python.org
Sun Feb 1 14:55:34 CET 2015


https://hg.python.org/cpython/rev/369300948f3f
changeset:   94423:369300948f3f
user:        Stefan Krah <skrah at bytereef.org>
date:        Sun Feb 01 14:53:54 2015 +0100
summary:
  Issue #22445: PyBuffer_IsContiguous() now implements precise contiguity
tests, compatible with NumPy's NPY_RELAXED_STRIDES_CHECKING compilation
flag.  Previously the function reported false negatives for corner cases.

files:
  Lib/test/test_buffer.py |  13 ++++++
  Misc/NEWS               |   4 ++
  Modules/_testbuffer.c   |  55 +++++++++++++++++++---------
  Objects/abstract.c      |  49 +++++++++++++++++++------
  4 files changed, 91 insertions(+), 30 deletions(-)


diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -1007,6 +1007,7 @@
         # shape, strides, offset
         structure = (
             ([], [], 0),
+            ([1,3,1], [], 0),
             ([12], [], 0),
             ([12], [-1], 11),
             ([6], [2], 0),
@@ -1078,6 +1079,18 @@
         self.assertRaises(BufferError, ndarray, ex, getbuf=PyBUF_ANY_CONTIGUOUS)
         nd = ndarray(ex, getbuf=PyBUF_SIMPLE)
 
+        # Issue #22445: New precise contiguity definition.
+        for shape in [1,12,1], [7,0,7]:
+            for order in 0, ND_FORTRAN:
+                ex = ndarray(items, shape=shape, flags=order|ND_WRITABLE)
+                self.assertTrue(is_contiguous(ex, 'F'))
+                self.assertTrue(is_contiguous(ex, 'C'))
+
+                for flags in requests:
+                    nd = ndarray(ex, getbuf=flags)
+                    self.assertTrue(is_contiguous(nd, 'F'))
+                    self.assertTrue(is_contiguous(nd, 'C'))
+
     def test_ndarray_exceptions(self):
         nd = ndarray([9], [1])
         ndm = ndarray([9], [1], flags=ND_VAREXPORT)
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1531,6 +1531,10 @@
 C API
 -----
 
+- Issue #22445: PyBuffer_IsContiguous() now implements precise contiguity
+  tests, compatible with NumPy's NPY_RELAXED_STRIDES_CHECKING compilation
+  flag.  Previously the function reported false negatives for corner cases.
+
 - Issue #22079: PyType_Ready() now checks that statically allocated type has
   no dynamically allocated bases.
 
diff --git a/Modules/_testbuffer.c b/Modules/_testbuffer.c
--- a/Modules/_testbuffer.c
+++ b/Modules/_testbuffer.c
@@ -1510,6 +1510,19 @@
         view->shape = NULL;
     }
 
+    /* Ascertain that the new buffer has the same contiguity as the exporter */
+    if (ND_C_CONTIGUOUS(baseflags) != PyBuffer_IsContiguous(view, 'C') ||
+        /* skip cast to 1-d */
+        (view->format != NULL && view->shape != NULL &&
+         ND_FORTRAN_CONTIGUOUS(baseflags) != PyBuffer_IsContiguous(view, 'F')) ||
+        /* cast to 1-d */
+        (view->format == NULL && view->shape == NULL &&
+         !PyBuffer_IsContiguous(view, 'F'))) {
+        PyErr_SetString(PyExc_BufferError,
+            "ndarray: contiguity mismatch in getbuf()");
+            return -1;
+    }
+
     view->obj = (PyObject *)self;
     Py_INCREF(view->obj);
     self->head->exports++;
@@ -2206,6 +2219,8 @@
     for (i = 0; i < base->ndim; i++)
         base->suboffsets[i] = -1;
 
+    nd->head->flags &= ~(ND_C|ND_FORTRAN);
+
     Py_RETURN_NONE;
 }
 
@@ -2469,13 +2484,12 @@
 {
     Py_ssize_t i;
 
-    if (ndim == 1 && shape && shape[0] == 1) {
-        /* This is for comparing strides: For example, the array
-           [175], shape=[1], strides=[-5] is considered contiguous. */
-        return 1;
-    }
 
     for (i = 0; i < ndim; i++) {
+        if (shape && shape[i] <= 1) {
+            /* strides can differ if the dimension is less than 2 */
+            continue;
+        }
         if (a1[i] != a2[i]) {
             return 0;
         }
@@ -2555,30 +2569,35 @@
     PyObject *obj;
     PyObject *order;
     PyObject *ret = NULL;
-    Py_buffer view;
+    Py_buffer view, *base;
     char ord;
 
     if (!PyArg_ParseTuple(args, "OO", &obj, &order)) {
         return NULL;
     }
 
-    if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) {
-        PyErr_SetString(PyExc_TypeError,
-            "is_contiguous: object does not implement the buffer "
-            "protocol");
+    ord = get_ascii_order(order);
+    if (ord == CHAR_MAX) {
         return NULL;
     }
 
-    ord = get_ascii_order(order);
-    if (ord == CHAR_MAX) {
-        goto release;
+    if (NDArray_Check(obj)) {
+        /* Skip the buffer protocol to check simple etc. buffers directly. */
+        base = &((NDArrayObject *)obj)->head->base;
+        ret = PyBuffer_IsContiguous(base, ord) ? Py_True : Py_False;
     }
-
-    ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False;
+    else {
+        if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) {
+            PyErr_SetString(PyExc_TypeError,
+                "is_contiguous: object does not implement the buffer "
+                "protocol");
+            return NULL;
+        }
+        ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False;
+        PyBuffer_Release(&view);
+    }
+
     Py_INCREF(ret);
-
-release:
-    PyBuffer_Release(&view);
     return ret;
 }
 
diff --git a/Objects/abstract.c b/Objects/abstract.c
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -367,16 +367,35 @@
     Py_ssize_t sd, dim;
     int i;
 
-    if (view->ndim == 0) return 1;
-    if (view->strides == NULL) return (view->ndim == 1);
+    /* 1) len = product(shape) * itemsize
+       2) itemsize > 0
+       3) len = 0 <==> exists i: shape[i] = 0 */
+    if (view->len == 0) return 1;
+    if (view->strides == NULL) {  /* C-contiguous by definition */
+        /* Trivially F-contiguous */
+        if (view->ndim <= 1) return 1;
+
+        /* ndim > 1 implies shape != NULL */
+        assert(view->shape != NULL);
+
+        /* Effectively 1-d */
+        sd = 0;
+        for (i=0; i<view->ndim; i++) {
+            if (view->shape[i] > 1) sd += 1;
+        }
+        return sd <= 1;
+    }
+
+    /* strides != NULL implies both of these */
+    assert(view->ndim > 0);
+    assert(view->shape != NULL);
 
     sd = view->itemsize;
-    if (view->ndim == 1) return (view->shape[0] == 1 ||
-                               sd == view->strides[0]);
     for (i=0; i<view->ndim; i++) {
         dim = view->shape[i];
-        if (dim == 0) return 1;
-        if (view->strides[i] != sd) return 0;
+        if (dim > 1 && view->strides[i] != sd) {
+            return 0;
+        }
         sd *= dim;
     }
     return 1;
@@ -388,16 +407,22 @@
     Py_ssize_t sd, dim;
     int i;
 
-    if (view->ndim == 0) return 1;
-    if (view->strides == NULL) return 1;
+    /* 1) len = product(shape) * itemsize
+       2) itemsize > 0
+       3) len = 0 <==> exists i: shape[i] = 0 */
+    if (view->len == 0) return 1;
+    if (view->strides == NULL) return 1; /* C-contiguous by definition */
+
+    /* strides != NULL implies both of these */
+    assert(view->ndim > 0);
+    assert(view->shape != NULL);
 
     sd = view->itemsize;
-    if (view->ndim == 1) return (view->shape[0] == 1 ||
-                               sd == view->strides[0]);
     for (i=view->ndim-1; i>=0; i--) {
         dim = view->shape[i];
-        if (dim == 0) return 1;
-        if (view->strides[i] != sd) return 0;
+        if (dim > 1 && view->strides[i] != sd) {
+            return 0;
+        }
         sd *= dim;
     }
     return 1;

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list