Skip to content

Commit e2c0520

Browse files
Luke Sewellrdbisme
authored andcommitted
Fix segfaulting issue with reduce iterator
1 parent 927f255 commit e2c0520

File tree

2 files changed

+65
-45
lines changed

2 files changed

+65
-45
lines changed

bottleneck/include/iterators.h

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -98,37 +98,59 @@ static inline void init_iter_all(iter *it, PyArrayObject *a, int ravel, int anyo
9898
it->ndim_m2 = -1;
9999
it->length = 1;
100100
it->astride = 0;
101-
} else if (C_CONTIGUOUS(a) && !F_CONTIGUOUS(a)) {
102-
/* The &&! in the next two else ifs is to deal with relaxed
103-
* stride checking introduced in numpy 1.12.0; see gh #161 */
104-
it->ndim_m2 = -1;
105-
it->axis = ndim - 1;
106-
it->length = PyArray_SIZE(a);
107-
it->astride = 0;
108-
for (i = ndim - 1; i > -1; i--) {
109-
/* protect against length zero strides such as in
110-
* np.ones((2, 2))[..., np.newaxis] */
111-
if (strides[i] == 0) {
112-
continue;
101+
} else {
102+
/* If strides aren't in descending order, some of the assumptions for C_CONTIGUOUS don't hold */
103+
int strides_descending = 1;
104+
for (i = 1; i < ndim; i++) {
105+
if (strides[i] > strides[i-1]) {
106+
strides_descending = 0;
107+
break;
113108
}
114-
it->astride = strides[i];
115-
break;
116109
}
117-
} else if (F_CONTIGUOUS(a) && !C_CONTIGUOUS(a)) {
118-
if (anyorder || !ravel) {
110+
111+
if (strides_descending && C_CONTIGUOUS(a) && !F_CONTIGUOUS(a)) {
112+
113+
/* The &&! in the next two else ifs is to deal with relaxed
114+
* stride checking introduced in numpy 1.12.0; see gh #161 */
119115
it->ndim_m2 = -1;
116+
it->axis = ndim - 1;
120117
it->length = PyArray_SIZE(a);
121118
it->astride = 0;
122-
for (i = 0; i < ndim; i++) {
119+
for (i = ndim - 1; i > -1; i--) {
123120
/* protect against length zero strides such as in
124-
* np.ones((2, 2), order='F')[np.newaxis, ...] */
121+
* np.ones((2, 2))[..., np.newaxis] */
125122
if (strides[i] == 0) {
126123
continue;
127124
}
128125
it->astride = strides[i];
129126
break;
130127
}
131-
} else {
128+
} else if (F_CONTIGUOUS(a) && !C_CONTIGUOUS(a)) {
129+
if (anyorder || !ravel) {
130+
it->ndim_m2 = -1;
131+
it->length = PyArray_SIZE(a);
132+
it->astride = 0;
133+
for (i = 0; i < ndim; i++) {
134+
/* protect against length zero strides such as in
135+
* np.ones((2, 2), order='F')[np.newaxis, ...] */
136+
if (strides[i] == 0) {
137+
continue;
138+
}
139+
it->astride = strides[i];
140+
break;
141+
}
142+
} else {
143+
it->ndim_m2 = -1;
144+
if (anyorder) {
145+
a = (PyArrayObject *)PyArray_Ravel(a, NPY_ANYORDER);
146+
} else {
147+
a = (PyArrayObject *)PyArray_Ravel(a, NPY_CORDER);
148+
}
149+
it->a_ravel = a;
150+
it->length = PyArray_DIM(a, 0);
151+
it->astride = PyArray_STRIDE(a, 0);
152+
}
153+
} else if (ravel) {
132154
it->ndim_m2 = -1;
133155
if (anyorder) {
134156
a = (PyArrayObject *)PyArray_Ravel(a, NPY_ANYORDER);
@@ -138,34 +160,24 @@ static inline void init_iter_all(iter *it, PyArrayObject *a, int ravel, int anyo
138160
it->a_ravel = a;
139161
it->length = PyArray_DIM(a, 0);
140162
it->astride = PyArray_STRIDE(a, 0);
141-
}
142-
} else if (ravel) {
143-
it->ndim_m2 = -1;
144-
if (anyorder) {
145-
a = (PyArrayObject *)PyArray_Ravel(a, NPY_ANYORDER);
146163
} else {
147-
a = (PyArrayObject *)PyArray_Ravel(a, NPY_CORDER);
148-
}
149-
it->a_ravel = a;
150-
it->length = PyArray_DIM(a, 0);
151-
it->astride = PyArray_STRIDE(a, 0);
152-
} else {
153-
it->ndim_m2 = ndim - 2;
154-
it->astride = strides[0];
155-
for (i = 1; i < ndim; i++) {
156-
if (strides[i] < it->astride) {
157-
it->astride = strides[i];
158-
it->axis = i;
164+
it->ndim_m2 = ndim - 2;
165+
it->astride = strides[0];
166+
for (i = 1; i < ndim; i++) {
167+
if (strides[i] < it->astride) {
168+
it->astride = strides[i];
169+
it->axis = i;
170+
}
159171
}
160-
}
161-
it->length = shape[it->axis];
162-
for (i = 0; i < ndim; i++) {
163-
if (i != it->axis) {
164-
it->indices[j] = 0;
165-
it->astrides[j] = strides[i];
166-
it->shape[j] = shape[i];
167-
it->nits *= shape[i];
168-
j++;
172+
it->length = shape[it->axis];
173+
for (i = 0; i < ndim; i++) {
174+
if (i != it->axis) {
175+
it->indices[j] = 0;
176+
it->astrides[j] = strides[i];
177+
it->shape[j] = shape[i];
178+
it->nits *= shape[i];
179+
j++;
180+
}
169181
}
170182
}
171183
}

bottleneck/tests/reduce_test.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,11 @@ def test_ddof_nans(func, dtype):
236236
for axis in [None, 0, 1, -1]:
237237
result = func(array, axis=axis, ddof=3)
238238
assert np.isnan(result)
239+
240+
241+
@pytest.mark.parametrize("dtype", DTYPES)
242+
@pytest.mark.parametrize("func", (bn.nanmean, bn.nanmax), ids=lambda x: x.__name__)
243+
def test_reduce_with_unordered_strides(func, dtype) -> None:
244+
array = np.zeros((1, 500, 2), dtype=dtype).transpose((1,2,0))
245+
result = func(array)
246+
assert result == 0

0 commit comments

Comments
 (0)