summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Krah <skrah@bytereef.org>2015-02-01 13:53:54 (GMT)
committerStefan Krah <skrah@bytereef.org>2015-02-01 13:53:54 (GMT)
commit363af44a4abff02ece61e456d55824f298448992 (patch)
tree1ecc31d3808152ca2f09ded5594cb715dc32a4c2
parenta5e1dbef146b9d1aece4e3def828d2b216a07e41 (diff)
downloadcpython-363af44a4abff02ece61e456d55824f298448992.zip
cpython-363af44a4abff02ece61e456d55824f298448992.tar.gz
cpython-363af44a4abff02ece61e456d55824f298448992.tar.bz2
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.
-rw-r--r--Lib/test/test_buffer.py13
-rw-r--r--Misc/NEWS4
-rw-r--r--Modules/_testbuffer.c53
-rw-r--r--Objects/abstract.c49
4 files changed, 90 insertions, 29 deletions
diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py
index 1cdc771..0976fa9 100644
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -1007,6 +1007,7 @@ class TestBufferProtocol(unittest.TestCase):
# shape, strides, offset
structure = (
([], [], 0),
+ ([1,3,1], [], 0),
([12], [], 0),
([12], [-1], 11),
([6], [2], 0),
@@ -1078,6 +1079,18 @@ class TestBufferProtocol(unittest.TestCase):
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
index b725c5d..9552e29 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1531,6 +1531,10 @@ Build
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
index 0c6ef16..a563a04 100644
--- a/Modules/_testbuffer.c
+++ b/Modules/_testbuffer.c
@@ -1510,6 +1510,19 @@ ndarray_getbuf(NDArrayObject *self, Py_buffer *view, int flags)
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 @@ ndarray_add_suboffsets(PyObject *self, PyObject *dummy)
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 @@ arraycmp(const Py_ssize_t *a1, const Py_ssize_t *a2, const Py_ssize_t *shape,
{
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 @@ is_contiguous(PyObject *self, PyObject *args)
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;
+ }
+ 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);
}
- ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False;
Py_INCREF(ret);
-
-release:
- PyBuffer_Release(&view);
return ret;
}
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 9ab60f2..92d1ac8 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -367,16 +367,35 @@ _IsFortranContiguous(const Py_buffer *view)
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 @@ _IsCContiguous(const Py_buffer *view)
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;