diff options
| author | Sebastian Berg <sebastian@sipsolutions.net> | 2013-01-03 16:42:48 +0100 |
|---|---|---|
| committer | Sebastian Berg <sebastian@sipsolutions.net> | 2013-01-03 17:40:00 +0100 |
| commit | 2be873f9590d1c68f8cf2357cd0a1c72bd5874ed (patch) | |
| tree | 573e2fadf7e7da2d1dd1332f8bc49d64d9cc824c /numpy | |
| parent | c74586282e4598e70b9b2473ee987785e2a61477 (diff) | |
| download | numpy-2be873f9590d1c68f8cf2357cd0a1c72bd5874ed.tar.gz | |
BUG: Fix CheckStrides and strides setter checks for available memory
This changes the logic of PyArray_CheckStrides to really check the
full extent the new array will have. It also changes the stride
setting to calculate the full real extent of the underlying array
without assuming (usually correctly) that the strides of the base
array are regular.
Add some tests for cases that previously failed.
This "closes Issue gh-2503"
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/src/multiarray/arrayobject.c | 26 | ||||
| -rw-r--r-- | numpy/core/src/multiarray/getset.c | 28 | ||||
| -rw-r--r-- | numpy/core/tests/test_multiarray.py | 16 |
3 files changed, 55 insertions, 15 deletions
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c index 78afcc77f..a7f3e8d4b 100644 --- a/numpy/core/src/multiarray/arrayobject.c +++ b/numpy/core/src/multiarray/arrayobject.c @@ -1464,20 +1464,34 @@ 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 min_offset = 0; + npy_intp max_offset = 0; 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; + if (dims[i] == 0) { + /* Empty array. Validate offset in any case */ + max_offset = 0; + min_offset = 0; + break; + } + max_axis_offset = newstrides[i] * (dims[i] - 1); + if (max_axis_offset > 0) { + max_offset += max_axis_offset; } + else { + min_offset += max_axis_offset; + } + } + if ((max_offset > end) || (min_offset < begin)) { + return NPY_FALSE; } return NPY_TRUE; } diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c index 95830a625..4cd31d8bf 100644 --- a/numpy/core/src/multiarray/getset.c +++ b/numpy/core/src/multiarray/getset.c @@ -102,7 +102,11 @@ array_strides_set(PyArrayObject *self, PyObject *obj) PyArrayObject *new; npy_intp numbytes = 0; npy_intp offset = 0; + npy_intp max_axis_offset; + npy_intp min_offset = 0; + npy_intp max_offset = 0; Py_ssize_t buf_len; + int i; char *buf; if (obj == NULL) { @@ -136,12 +140,28 @@ 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); + /* Find the true extent of the base array, similar to CheckStrides */ + for (i = 0; i < PyArray_NDIM(new); i++) { + if (PyArray_DIMS(new)[i] == 0) { + /* Since all arrays must be empty here, this works */ + max_offset = 0; + min_offset = 0; + break; + } + max_axis_offset = PyArray_STRIDES(new)[i] * (PyArray_DIMS(new)[i] - 1); + if (max_axis_offset > 0) { + max_offset += max_axis_offset; + } + else { + min_offset += max_axis_offset; + } + } + + offset = -min_offset + PyArray_BYTES(self) - PyArray_BYTES(new); + numbytes = max_offset - min_offset + PyArray_ITEMSIZE(new); } - if (!PyArray_CheckStrides(PyArray_DESCR(self)->elsize, PyArray_NDIM(self), numbytes, + if (!PyArray_CheckStrides(PyArray_ITEMSIZE(self), PyArray_NDIM(self), numbytes, offset, PyArray_DIMS(self), newstrides.ptr)) { PyErr_SetString(PyExc_ValueError, "strides is not "\ diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index dfa5bb2b2..f2cab2904 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,12 @@ 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)) def test_fill(self): for t in "?bhilqpBHILQPfdgFDGO": |
