diff options
author | seberg <sebastian@sipsolutions.net> | 2013-01-06 02:40:54 -0800 |
---|---|---|
committer | seberg <sebastian@sipsolutions.net> | 2013-01-06 02:40:54 -0800 |
commit | 161bfe73e006af7c71db373aa99bdecce32f9e2a (patch) | |
tree | c2ed9a6b59f1d62821e2f2c809624c10da2d8090 /numpy | |
parent | 22ad3acca6cb4c029e544f6a060a864a0191087e (diff) | |
parent | 1981d0640d6a598a043379be43d19dcf590823f1 (diff) | |
download | numpy-161bfe73e006af7c71db373aa99bdecce32f9e2a.tar.gz |
Merge pull request #2882 from seberg/issue2503
BUG: Fix CheckStrides and strides setter checks for available memory
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/multiarray/arrayobject.c | 21 | ||||
-rw-r--r-- | numpy/core/src/multiarray/common.c | 30 | ||||
-rw-r--r-- | numpy/core/src/multiarray/common.h | 5 | ||||
-rw-r--r-- | numpy/core/src/multiarray/getset.c | 16 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 24 |
5 files changed, 77 insertions, 19 deletions
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c index 78afcc77f..10b750cdb 100644 --- a/numpy/core/src/multiarray/arrayobject.c +++ b/numpy/core/src/multiarray/arrayobject.c @@ -1464,20 +1464,23 @@ PyArray_CheckStrides(int elsize, int nd, npy_intp numbytes, npy_intp offset, npy_intp *dims, npy_intp *newstrides) { int i; - npy_intp byte_begin; - npy_intp begin; - npy_intp end; + npy_intp max_axis_offset; + npy_intp begin, end; + npy_intp lower_offset; + npy_intp upper_offset; if (numbytes == 0) { numbytes = PyArray_MultiplyList(dims, nd) * elsize; } + begin = -offset; - end = numbytes - offset - elsize; - for (i = 0; i < nd; i++) { - byte_begin = newstrides[i]*(dims[i] - 1); - if ((byte_begin < begin) || (byte_begin > end)) { - return NPY_FALSE; - } + end = numbytes - offset; + + offset_bounds_from_strides(elsize, nd, dims, newstrides, + &lower_offset, &upper_offset); + + if ((upper_offset > end) || (lower_offset < begin)) { + return NPY_FALSE; } return NPY_TRUE; } diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index 09b452f45..64ca352a5 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -674,3 +674,33 @@ _IsWriteable(PyArrayObject *ap) } return NPY_TRUE; } + + +NPY_NO_EXPORT void +offset_bounds_from_strides(const int itemsize, const int nd, + const npy_intp *dims, const npy_intp *strides, + npy_intp *lower_offset, npy_intp *upper_offset) { + npy_intp max_axis_offset; + npy_intp lower = 0; + npy_intp upper = 0; + int i; + + for (i = 0; i < nd; i++) { + if (dims[i] == 0) { + /* Empty array special case */ + *lower_offset = 0; + *upper_offset = 0; + return; + } + max_axis_offset = strides[i] * (dims[i] - 1); + if (max_axis_offset > 0) { + upper += max_axis_offset; + } + else { + lower += max_axis_offset; + } + } + upper += itemsize; + *lower_offset = lower; + *upper_offset = upper; +} diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h index b68d4286b..a474cf820 100644 --- a/numpy/core/src/multiarray/common.h +++ b/numpy/core/src/multiarray/common.h @@ -57,6 +57,11 @@ _IsAligned(PyArrayObject *ap); NPY_NO_EXPORT npy_bool _IsWriteable(PyArrayObject *ap); +NPY_NO_EXPORT void +offset_bounds_from_strides(const int itemsize, const int nd, + const npy_intp *dims, const npy_intp *strides, + npy_intp *lower_offset, npy_intp *upper_offset); + #include "ucsnarrow.h" #endif diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c index 7788663be..6ad4cc13a 100644 --- a/numpy/core/src/multiarray/getset.c +++ b/numpy/core/src/multiarray/getset.c @@ -102,6 +102,8 @@ array_strides_set(PyArrayObject *self, PyObject *obj) PyArrayObject *new; npy_intp numbytes = 0; npy_intp offset = 0; + npy_intp lower_offset = 0; + npy_intp upper_offset = 0; Py_ssize_t buf_len; char *buf; @@ -136,13 +138,17 @@ array_strides_set(PyArrayObject *self, PyObject *obj) } else { PyErr_Clear(); - numbytes = PyArray_MultiplyList(PyArray_DIMS(new), - PyArray_NDIM(new))*PyArray_DESCR(new)->elsize; - offset = PyArray_BYTES(self) - PyArray_BYTES(new); + offset_bounds_from_strides(PyArray_ITEMSIZE(new), PyArray_NDIM(new), + PyArray_DIMS(new), PyArray_STRIDES(new), + &lower_offset, &upper_offset); + + offset = PyArray_BYTES(self) - (PyArray_BYTES(new) + lower_offset); + numbytes = upper_offset - lower_offset; } - if (!PyArray_CheckStrides(PyArray_DESCR(self)->elsize, PyArray_NDIM(self), numbytes, - offset, + /* numbytes == 0 is special here, but the 0-size array case always works */ + if (!PyArray_CheckStrides(PyArray_ITEMSIZE(self), PyArray_NDIM(self), + numbytes, offset, PyArray_DIMS(self), newstrides.ptr)) { PyErr_SetString(PyExc_ValueError, "strides is not "\ "compatible with available memory"); diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index dfa5bb2b2..3d5bae220 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -97,16 +97,17 @@ class TestAttributes(TestCase): def test_stridesattr(self): x = self.one def make_array(size, offset, strides): - return ndarray([size], buffer=x, dtype=int, + return ndarray(size, buffer=x, dtype=int, offset=offset*x.itemsize, strides=strides*x.itemsize) assert_equal(make_array(4, 4, -1), array([4, 3, 2, 1])) self.assertRaises(ValueError, make_array, 4, 4, -2) self.assertRaises(ValueError, make_array, 4, 2, -1) self.assertRaises(ValueError, make_array, 8, 3, 1) - #self.assertRaises(ValueError, make_array, 8, 3, 0) - #self.assertRaises(ValueError, lambda: ndarray([1], strides=4)) - + assert_equal(make_array(8, 3, 0), np.array([3]*8)) + # Check behavior reported in gh-2503: + self.assertRaises(ValueError, make_array, (2, 3), 5, array([-2, -3])) + make_array(0, 0, 10) def test_set_stridesattr(self): x = self.one @@ -122,7 +123,20 @@ class TestAttributes(TestCase): self.assertRaises(ValueError, make_array, 4, 4, -2) self.assertRaises(ValueError, make_array, 4, 2, -1) self.assertRaises(RuntimeError, make_array, 8, 3, 1) - #self.assertRaises(ValueError, make_array, 8, 3, 0) + # Check that the true extent of the array is used. + # Test relies on as_strided base not exposing a buffer. + x = np.lib.stride_tricks.as_strided(arange(1), (10,10), (0,0)) + def set_strides(arr, strides): + arr.strides = strides + self.assertRaises(ValueError, set_strides, x, (10*x.itemsize, x.itemsize)) + + # Test for offset calculations: + x = np.lib.stride_tricks.as_strided(np.arange(10, dtype=np.int8)[-1], + shape=(10,), strides=(-1,)) + self.assertRaises(ValueError, set_strides, x[::-1], -1) + a = x[::-1] + a.strides = 1 + a[::2].strides = 2 def test_fill(self): for t in "?bhilqpBHILQPfdgFDGO": |