Commit 7db58754 authored by Mark Florisson's avatar Mark Florisson

Merge pull request #212 from markflorisson88/master

Relax stride check for zero- and one-sized dimensions
parents 65706856 9dc28bd4
...@@ -106,10 +106,11 @@ static int __Pyx_init_memviewslice( ...@@ -106,10 +106,11 @@ static int __Pyx_init_memviewslice(
__Pyx_memviewslice *memviewslice, __Pyx_memviewslice *memviewslice,
int memview_is_new_reference); int memview_is_new_reference);
static CYTHON_INLINE int __pyx_add_acquisition_count_locked(__pyx_atomic_int *acquisition_count, static CYTHON_INLINE int __pyx_add_acquisition_count_locked(
PyThread_type_lock lock); __pyx_atomic_int *acquisition_count, PyThread_type_lock lock);
static CYTHON_INLINE int __pyx_sub_acquisition_count_locked(__pyx_atomic_int *acquisition_count, static CYTHON_INLINE int __pyx_sub_acquisition_count_locked(
PyThread_type_lock lock); __pyx_atomic_int *acquisition_count, PyThread_type_lock lock);
#define __pyx_get_slice_count_pointer(memview) (memview->acquisition_count_aligned_p) #define __pyx_get_slice_count_pointer(memview) (memview->acquisition_count_aligned_p)
#define __pyx_get_slice_count(memview) (*__pyx_get_slice_count_pointer(memview)) #define __pyx_get_slice_count(memview) (*__pyx_get_slice_count_pointer(memview))
#define __PYX_INC_MEMVIEW(slice, have_gil) __Pyx_INC_MEMVIEW(slice, have_gil, __LINE__) #define __PYX_INC_MEMVIEW(slice, have_gil) __Pyx_INC_MEMVIEW(slice, have_gil, __LINE__)
...@@ -117,7 +118,8 @@ static CYTHON_INLINE int __pyx_sub_acquisition_count_locked(__pyx_atomic_int *ac ...@@ -117,7 +118,8 @@ static CYTHON_INLINE int __pyx_sub_acquisition_count_locked(__pyx_atomic_int *ac
static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *, int, int); static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *, int, int);
static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *, int, int); static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *, int, int);
/////////////// MemviewSliceIndex.proto /////////////// /////////////// MemviewSliceIndex.proto ///////////////
static CYTHON_INLINE char *__pyx_memviewslice_index_full(const char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset); static CYTHON_INLINE char *__pyx_memviewslice_index_full(
const char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset);
/////////////// ObjectToMemviewSlice /////////////// /////////////// ObjectToMemviewSlice ///////////////
...@@ -150,6 +152,127 @@ __pyx_fail: ...@@ -150,6 +152,127 @@ __pyx_fail:
////////// MemviewSliceInit ////////// ////////// MemviewSliceInit //////////
static int
__pyx_check_strides(Py_buffer *buf, int dim, int ndim, int spec)
{
if (buf->shape[dim] <= 1)
return 1;
if (buf->strides) {
if (spec & __Pyx_MEMVIEW_CONTIG) {
if (spec & (__Pyx_MEMVIEW_PTR|__Pyx_MEMVIEW_FULL)) {
if (buf->strides[dim] != sizeof(void *)) {
PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly contiguous "
"in dimension %d.", dim);
goto fail;
}
} else if (buf->strides[dim] != buf->itemsize) {
PyErr_SetString(PyExc_ValueError,
"Buffer and memoryview are not contiguous "
"in the same dimension.");
goto fail;
}
}
if (spec & __Pyx_MEMVIEW_FOLLOW) {
Py_ssize_t stride = buf->strides[dim];
if (stride < 0)
stride = -stride;
if (stride < buf->itemsize) {
PyErr_SetString(PyExc_ValueError,
"Buffer and memoryview are not contiguous "
"in the same dimension.");
goto fail;
}
}
} else {
if (spec & __Pyx_MEMVIEW_CONTIG && dim != ndim - 1) {
PyErr_Format(PyExc_ValueError,
"C-contiguous buffer is not contiguous in "
"dimension %d", dim);
goto fail;
} else if (spec & (__Pyx_MEMVIEW_PTR)) {
PyErr_Format(PyExc_ValueError,
"C-contiguous buffer is not indirect in "
"dimension %d", dim);
goto fail;
} else if (buf->suboffsets) {
PyErr_SetString(PyExc_ValueError,
"Buffer exposes suboffsets but no strides");
goto fail;
}
}
return 1;
fail:
return 0;
}
static int
__pyx_check_suboffsets(Py_buffer *buf, int dim, int ndim, int spec)
{
/* Todo: without PyBUF_INDIRECT we may not have suboffset information, i.e., the
ptr may not be set to NULL but may be uninitialized? */
if (spec & __Pyx_MEMVIEW_DIRECT) {
if (buf->suboffsets && buf->suboffsets[dim] >= 0) {
PyErr_Format(PyExc_ValueError,
"Buffer not compatible with direct access "
"in dimension %d.", dim);
goto fail;
}
}
if (spec & __Pyx_MEMVIEW_PTR) {
if (!buf->suboffsets || (buf->suboffsets && buf->suboffsets[dim] < 0)) {
PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly accessisble "
"in dimension %d.", dim);
goto fail;
}
}
return 1;
fail:
return 0;
}
static int
__pyx_verify_contig(Py_buffer *buf, int ndim, int c_or_f_flag)
{
int i;
if (c_or_f_flag & __Pyx_IS_F_CONTIG) {
Py_ssize_t stride = 1;
for (i = 0; i < ndim; i++) {
if (stride * buf->itemsize != buf->strides[i] &&
buf->shape[i] > 1)
{
PyErr_SetString(PyExc_ValueError,
"Buffer not fortran contiguous.");
goto fail;
}
stride = stride * buf->shape[i];
}
} else if (c_or_f_flag & __Pyx_IS_C_CONTIG) {
Py_ssize_t stride = 1;
for (i = ndim - 1; i >- 1; i--) {
if (stride * buf->itemsize != buf->strides[i] &&
buf->shape[i] > 1) {
PyErr_SetString(PyExc_ValueError,
"Buffer not C contiguous.");
goto fail;
}
stride = stride * buf->shape[i];
}
}
return 1;
fail:
return 0;
}
static int __Pyx_ValidateAndInit_memviewslice( static int __Pyx_ValidateAndInit_memviewslice(
int *axes_specs, int *axes_specs,
int c_or_f_flag, int c_or_f_flag,
...@@ -195,7 +318,7 @@ static int __Pyx_ValidateAndInit_memviewslice( ...@@ -195,7 +318,7 @@ static int __Pyx_ValidateAndInit_memviewslice(
if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail; if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail;
} }
if ((unsigned)buf->itemsize != dtype->size) { if ((unsigned) buf->itemsize != dtype->size) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"Item size of buffer (%" CYTHON_FORMAT_SSIZE_T "u byte%s) " "Item size of buffer (%" CYTHON_FORMAT_SSIZE_T "u byte%s) "
"does not match size of '%s' (%" CYTHON_FORMAT_SSIZE_T "u byte%s)", "does not match size of '%s' (%" CYTHON_FORMAT_SSIZE_T "u byte%s)",
...@@ -207,95 +330,20 @@ static int __Pyx_ValidateAndInit_memviewslice( ...@@ -207,95 +330,20 @@ static int __Pyx_ValidateAndInit_memviewslice(
goto fail; goto fail;
} }
/* Check axes */
for (i = 0; i < ndim; i++) { for (i = 0; i < ndim; i++) {
spec = axes_specs[i]; spec = axes_specs[i];
if (!__pyx_check_strides(buf, i, ndim, spec))
if (buf->strides) { goto fail;
if (spec & __Pyx_MEMVIEW_CONTIG) { if (!__pyx_check_suboffsets(buf, i, ndim, spec))
if (spec & (__Pyx_MEMVIEW_PTR|__Pyx_MEMVIEW_FULL)) { goto fail;
if (buf->strides[i] != sizeof(void *)) {
PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly contiguous in dimension %d.", i);
goto fail;
}
} else if (buf->strides[i] != buf->itemsize) {
PyErr_SetString(PyExc_ValueError,
"Buffer and memoryview are not contiguous in the same dimension.");
goto fail;
}
}
if (spec & __Pyx_MEMVIEW_FOLLOW) {
Py_ssize_t stride = buf->strides[i];
if (stride < 0)
stride = -stride;
if (stride < buf->itemsize) {
PyErr_SetString(PyExc_ValueError,
"Buffer and memoryview are not contiguous in the same dimension.");
goto fail;
}
}
} else {
if (spec & __Pyx_MEMVIEW_CONTIG && i != ndim - 1) {
PyErr_Format(PyExc_ValueError,
"C-contiguous buffer is not contiguous in "
"dimension %d", i);
goto fail;
} else if (spec & (__Pyx_MEMVIEW_PTR)) {
PyErr_Format(PyExc_ValueError,
"C-contiguous buffer is not indirect in "
"dimension %d", i);
goto fail;
} else if (buf->suboffsets) {
PyErr_SetString(PyExc_ValueError,
"Buffer exposes suboffsets but no strides");
goto fail;
}
}
/* Todo: without PyBUF_INDIRECT we may not have suboffset information, i.e., the
ptr may not be set to NULL but may be uninitialized? */
if (spec & __Pyx_MEMVIEW_DIRECT) {
if (buf->suboffsets && buf->suboffsets[i] >= 0) {
PyErr_Format(PyExc_ValueError,
"Buffer not compatible with direct access in dimension %d.", i);
goto fail;
}
}
if (spec & __Pyx_MEMVIEW_PTR) {
if (!buf->suboffsets || (buf->suboffsets && buf->suboffsets[i] < 0)) {
PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly accessisble in dimension %d.", i);
goto fail;
}
}
} }
if (buf->strides) { /* Check contiguity */
if (c_or_f_flag & __Pyx_IS_F_CONTIG) { if (buf->strides && !__pyx_verify_contig(buf, ndim, c_or_f_flag))
Py_ssize_t stride = 1; goto fail;
for (i=0; i<ndim; i++) {
if (stride * buf->itemsize != buf->strides[i]) {
PyErr_SetString(PyExc_ValueError,
"Buffer not fortran contiguous.");
goto fail;
}
stride = stride * buf->shape[i];
}
} else if (c_or_f_flag & __Pyx_IS_C_CONTIG) {
Py_ssize_t stride = 1;
for (i = ndim-1; i>-1; i--) {
if(stride * buf->itemsize != buf->strides[i]) {
PyErr_SetString(PyExc_ValueError,
"Buffer not C contiguous.");
goto fail;
}
stride = stride * buf->shape[i];
}
}
}
/* Initialize */
if (unlikely(__Pyx_init_memviewslice(memview, ndim, memviewslice, if (unlikely(__Pyx_init_memviewslice(memview, ndim, memviewslice,
new_memview != NULL) == -1)) { new_memview != NULL) == -1)) {
goto fail; goto fail;
......
# tag: numpy
# mode: run
"""
Test accepting NumPy arrays with arbitrary strides for zero- or one-sized
dimensions.
Thanks to Nathaniel Smith and Sebastian Berg.
See also:
Mailing list threads:
http://thread.gmane.org/gmane.comp.python.cython.devel/14762
http://thread.gmane.org/gmane.comp.python.cython.devel/14634
Detailed discussion of the difference between numpy/cython's current
definition of "contiguity", and the correct definition:
http://thread.gmane.org/gmane.comp.python.cython.devel/14634/focus=14640
The PR implementing NPY_RELAXED_STRIDES_CHECKING:
https://github.com/numpy/numpy/pull/3162
Another test case:
https://github.com/numpy/numpy/issues/2956
"""
import numpy as np
def test_one_sized(array):
"""
>>> a = np.ascontiguousarray(np.arange(10, dtype=np.double)[::100])
>>> test_one_sized(a)[0]
1.0
>>> a = np.arange(10, dtype=np.double)[::100]
>>> test_one_sized(a)[0]
1.0
"""
cdef double[::1] a = array
a[0] += 1.
return array
def test_zero_sized(array):
"""
>>> a = np.ascontiguousarray(np.arange(10, dtype=np.double)[100:200:10])
>>> a = test_zero_sized(a)
>>> a = np.arange(10, dtype=np.double)[100:200:10]
>>> a = test_zero_sized(a)
"""
cdef double[::1] a = array
return a
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment