Commit 9dc28bd4 authored by Mark Florisson's avatar Mark Florisson

Allow arbitrary strides and zero- and one-sized dimensions

parent 65706856
...@@ -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,101 +152,50 @@ __pyx_fail: ...@@ -150,101 +152,50 @@ __pyx_fail:
////////// MemviewSliceInit ////////// ////////// MemviewSliceInit //////////
static int __Pyx_ValidateAndInit_memviewslice( static int
int *axes_specs, __pyx_check_strides(Py_buffer *buf, int dim, int ndim, int spec)
int c_or_f_flag,
int buf_flags,
int ndim,
__Pyx_TypeInfo *dtype,
__Pyx_BufFmt_StackElem stack[],
__Pyx_memviewslice *memviewslice,
PyObject *original_obj)
{ {
struct __pyx_memoryview_obj *memview, *new_memview; if (buf->shape[dim] <= 1)
__Pyx_RefNannyDeclarations return 1;
Py_buffer *buf;
int i, spec = 0, retval = -1;
__Pyx_BufFmt_Context ctx;
int from_memoryview = __pyx_memoryview_check(original_obj);
__Pyx_RefNannySetupContext("ValidateAndInit_memviewslice", 0);
if (from_memoryview && __pyx_typeinfo_cmp(dtype, ((struct __pyx_memoryview_obj *)
original_obj)->typeinfo)) {
/* We have a matching dtype, skip format parsing */
memview = (struct __pyx_memoryview_obj *) original_obj;
new_memview = NULL;
} else {
memview = (struct __pyx_memoryview_obj *) __pyx_memoryview_new(
original_obj, buf_flags, 0, dtype);
new_memview = memview;
if (unlikely(!memview))
goto fail;
}
buf = &memview->view;
if (buf->ndim != ndim) {
PyErr_Format(PyExc_ValueError,
"Buffer has wrong number of dimensions (expected %d, got %d)",
ndim, buf->ndim);
goto fail;
}
if (new_memview) {
__Pyx_BufFmt_Init(&ctx, stack, dtype);
if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail;
}
if ((unsigned)buf->itemsize != dtype->size) {
PyErr_Format(PyExc_ValueError,
"Item size of buffer (%" CYTHON_FORMAT_SSIZE_T "u byte%s) "
"does not match size of '%s' (%" CYTHON_FORMAT_SSIZE_T "u byte%s)",
buf->itemsize,
(buf->itemsize > 1) ? "s" : "",
dtype->name,
dtype->size,
(dtype->size > 1) ? "s" : "");
goto fail;
}
for (i = 0; i < ndim; i++) {
spec = axes_specs[i];
if (buf->strides) { if (buf->strides) {
if (spec & __Pyx_MEMVIEW_CONTIG) { if (spec & __Pyx_MEMVIEW_CONTIG) {
if (spec & (__Pyx_MEMVIEW_PTR|__Pyx_MEMVIEW_FULL)) { if (spec & (__Pyx_MEMVIEW_PTR|__Pyx_MEMVIEW_FULL)) {
if (buf->strides[i] != sizeof(void *)) { if (buf->strides[dim] != sizeof(void *)) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly contiguous in dimension %d.", i); "Buffer is not indirectly contiguous "
"in dimension %d.", dim);
goto fail; goto fail;
} }
} else if (buf->strides[i] != buf->itemsize) { } else if (buf->strides[dim] != buf->itemsize) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"Buffer and memoryview are not contiguous in the same dimension."); "Buffer and memoryview are not contiguous "
"in the same dimension.");
goto fail; goto fail;
} }
} }
if (spec & __Pyx_MEMVIEW_FOLLOW) { if (spec & __Pyx_MEMVIEW_FOLLOW) {
Py_ssize_t stride = buf->strides[i]; Py_ssize_t stride = buf->strides[dim];
if (stride < 0) if (stride < 0)
stride = -stride; stride = -stride;
if (stride < buf->itemsize) { if (stride < buf->itemsize) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"Buffer and memoryview are not contiguous in the same dimension."); "Buffer and memoryview are not contiguous "
"in the same dimension.");
goto fail; goto fail;
} }
} }
} else { } else {
if (spec & __Pyx_MEMVIEW_CONTIG && i != ndim - 1) { if (spec & __Pyx_MEMVIEW_CONTIG && dim != ndim - 1) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"C-contiguous buffer is not contiguous in " "C-contiguous buffer is not contiguous in "
"dimension %d", i); "dimension %d", dim);
goto fail; goto fail;
} else if (spec & (__Pyx_MEMVIEW_PTR)) { } else if (spec & (__Pyx_MEMVIEW_PTR)) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"C-contiguous buffer is not indirect in " "C-contiguous buffer is not indirect in "
"dimension %d", i); "dimension %d", dim);
goto fail; goto fail;
} else if (buf->suboffsets) { } else if (buf->suboffsets) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
...@@ -253,30 +204,51 @@ static int __Pyx_ValidateAndInit_memviewslice( ...@@ -253,30 +204,51 @@ static int __Pyx_ValidateAndInit_memviewslice(
} }
} }
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 /* Todo: without PyBUF_INDIRECT we may not have suboffset information, i.e., the
ptr may not be set to NULL but may be uninitialized? */ ptr may not be set to NULL but may be uninitialized? */
if (spec & __Pyx_MEMVIEW_DIRECT) { if (spec & __Pyx_MEMVIEW_DIRECT) {
if (buf->suboffsets && buf->suboffsets[i] >= 0) { if (buf->suboffsets && buf->suboffsets[dim] >= 0) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"Buffer not compatible with direct access in dimension %d.", i); "Buffer not compatible with direct access "
"in dimension %d.", dim);
goto fail; goto fail;
} }
} }
if (spec & __Pyx_MEMVIEW_PTR) { if (spec & __Pyx_MEMVIEW_PTR) {
if (!buf->suboffsets || (buf->suboffsets && buf->suboffsets[i] < 0)) { if (!buf->suboffsets || (buf->suboffsets && buf->suboffsets[dim] < 0)) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"Buffer is not indirectly accessisble in dimension %d.", i); "Buffer is not indirectly accessisble "
"in dimension %d.", dim);
goto fail; goto fail;
} }
} }
}
if (buf->strides) { 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) { if (c_or_f_flag & __Pyx_IS_F_CONTIG) {
Py_ssize_t stride = 1; Py_ssize_t stride = 1;
for (i=0; i<ndim; i++) { for (i = 0; i < ndim; i++) {
if (stride * buf->itemsize != buf->strides[i]) { if (stride * buf->itemsize != buf->strides[i] &&
buf->shape[i] > 1)
{
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"Buffer not fortran contiguous."); "Buffer not fortran contiguous.");
goto fail; goto fail;
...@@ -285,8 +257,9 @@ static int __Pyx_ValidateAndInit_memviewslice( ...@@ -285,8 +257,9 @@ static int __Pyx_ValidateAndInit_memviewslice(
} }
} else if (c_or_f_flag & __Pyx_IS_C_CONTIG) { } else if (c_or_f_flag & __Pyx_IS_C_CONTIG) {
Py_ssize_t stride = 1; Py_ssize_t stride = 1;
for (i = ndim-1; i>-1; i--) { for (i = ndim - 1; i >- 1; i--) {
if(stride * buf->itemsize != buf->strides[i]) { if (stride * buf->itemsize != buf->strides[i] &&
buf->shape[i] > 1) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"Buffer not C contiguous."); "Buffer not C contiguous.");
goto fail; goto fail;
...@@ -294,8 +267,83 @@ static int __Pyx_ValidateAndInit_memviewslice( ...@@ -294,8 +267,83 @@ static int __Pyx_ValidateAndInit_memviewslice(
stride = stride * buf->shape[i]; stride = stride * buf->shape[i];
} }
} }
return 1;
fail:
return 0;
}
static int __Pyx_ValidateAndInit_memviewslice(
int *axes_specs,
int c_or_f_flag,
int buf_flags,
int ndim,
__Pyx_TypeInfo *dtype,
__Pyx_BufFmt_StackElem stack[],
__Pyx_memviewslice *memviewslice,
PyObject *original_obj)
{
struct __pyx_memoryview_obj *memview, *new_memview;
__Pyx_RefNannyDeclarations
Py_buffer *buf;
int i, spec = 0, retval = -1;
__Pyx_BufFmt_Context ctx;
int from_memoryview = __pyx_memoryview_check(original_obj);
__Pyx_RefNannySetupContext("ValidateAndInit_memviewslice", 0);
if (from_memoryview && __pyx_typeinfo_cmp(dtype, ((struct __pyx_memoryview_obj *)
original_obj)->typeinfo)) {
/* We have a matching dtype, skip format parsing */
memview = (struct __pyx_memoryview_obj *) original_obj;
new_memview = NULL;
} else {
memview = (struct __pyx_memoryview_obj *) __pyx_memoryview_new(
original_obj, buf_flags, 0, dtype);
new_memview = memview;
if (unlikely(!memview))
goto fail;
}
buf = &memview->view;
if (buf->ndim != ndim) {
PyErr_Format(PyExc_ValueError,
"Buffer has wrong number of dimensions (expected %d, got %d)",
ndim, buf->ndim);
goto fail;
}
if (new_memview) {
__Pyx_BufFmt_Init(&ctx, stack, dtype);
if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail;
} }
if ((unsigned) buf->itemsize != dtype->size) {
PyErr_Format(PyExc_ValueError,
"Item size of buffer (%" CYTHON_FORMAT_SSIZE_T "u byte%s) "
"does not match size of '%s' (%" CYTHON_FORMAT_SSIZE_T "u byte%s)",
buf->itemsize,
(buf->itemsize > 1) ? "s" : "",
dtype->name,
dtype->size,
(dtype->size > 1) ? "s" : "");
goto fail;
}
/* Check axes */
for (i = 0; i < ndim; i++) {
spec = axes_specs[i];
if (!__pyx_check_strides(buf, i, ndim, spec))
goto fail;
if (!__pyx_check_suboffsets(buf, i, ndim, spec))
goto fail;
}
/* Check contiguity */
if (buf->strides && !__pyx_verify_contig(buf, ndim, c_or_f_flag))
goto fail;
/* 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