mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
a5e1dbef14
commit
363af44a4a
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue