Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ed134d5

Browse files
committed
Throw errors when indexing into empty array_view objects
Should catch issues like #5185 while not adding a big perf penalty
1 parent 333136b commit ed134d5

File tree

2 files changed

+72
-7
lines changed

2 files changed

+72
-7
lines changed

src/numpy_cpp.h

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@
2727

2828
#include <Python.h>
2929
#include <numpy/ndarrayobject.h>
30+
#include <stdexcept>
31+
32+
#if defined(__GNUC__) || defined(__clang__)
33+
#define unlikely(x) __builtin_expect(!!(x), 0)
34+
#else
35+
#define unlikely(x) (x)
36+
#endif
3037

3138
namespace numpy
3239
{
@@ -235,27 +242,39 @@ class array_view_accessors<AV, T, 1>
235242
T &operator()(npy_intp i)
236243
{
237244
AVC *self = static_cast<AVC *>(this);
245+
if (unlikely(self->m_empty)) {
246+
throw std::out_of_range("indexing into an array_view with no data");
247+
}
238248

239249
return *reinterpret_cast<T *>(self->m_data + self->m_strides[0] * i);
240250
}
241251

242252
const T &operator()(npy_intp i) const
243253
{
244254
const AVC *self = static_cast<const AVC *>(this);
255+
if (unlikely(self->m_empty)) {
256+
throw std::out_of_range("indexing into an array_view with no data");
257+
}
245258

246259
return *reinterpret_cast<const T *>(self->m_data + self->m_strides[0] * i);
247260
}
248261

249262
T &operator[](npy_intp i)
250263
{
251264
AVC *self = static_cast<AVC *>(this);
265+
if (unlikely(self->m_empty)) {
266+
throw std::out_of_range("indexing into an array_view with no data");
267+
}
252268

253269
return *reinterpret_cast<T *>(self->m_data + self->m_strides[0] * i);
254270
}
255271

256272
const T &operator[](npy_intp i) const
257273
{
258274
const AVC *self = static_cast<const AVC *>(this);
275+
if (unlikely(self->m_empty)) {
276+
throw std::out_of_range("indexing into an array_view with no data");
277+
}
259278

260279
return *reinterpret_cast<const T *>(self->m_data + self->m_strides[0] * i);
261280
}
@@ -271,6 +290,9 @@ class array_view_accessors<AV, T, 2>
271290
T &operator()(npy_intp i, npy_intp j)
272291
{
273292
AVC *self = static_cast<AVC *>(this);
293+
if (unlikely(self->m_empty)) {
294+
throw std::out_of_range("indexing into an array_view with no data");
295+
}
274296

275297
return *reinterpret_cast<T *>(self->m_data + self->m_strides[0] * i +
276298
self->m_strides[1] * j);
@@ -279,6 +301,9 @@ class array_view_accessors<AV, T, 2>
279301
const T &operator()(npy_intp i, npy_intp j) const
280302
{
281303
const AVC *self = static_cast<const AVC *>(this);
304+
if (unlikely(self->m_empty)) {
305+
throw std::out_of_range("indexing into an array_view with no data");
306+
}
282307

283308
return *reinterpret_cast<const T *>(self->m_data + self->m_strides[0] * i +
284309
self->m_strides[1] * j);
@@ -287,6 +312,9 @@ class array_view_accessors<AV, T, 2>
287312
sub_t operator[](npy_intp i) const
288313
{
289314
const AVC *self = static_cast<const AVC *>(this);
315+
if (unlikely(self->m_empty)) {
316+
throw std::out_of_range("indexing into an array_view with no data");
317+
}
290318

291319
return sub_t(self->m_arr,
292320
self->m_data + self->m_strides[0] * i,
@@ -305,6 +333,9 @@ class array_view_accessors<AV, T, 3>
305333
T &operator()(npy_intp i, npy_intp j, npy_intp k)
306334
{
307335
AVC *self = static_cast<AVC *>(this);
336+
if (unlikely(self->m_empty)) {
337+
throw std::out_of_range("indexing into an array_view with no data");
338+
}
308339

309340
return *reinterpret_cast<T *>(self->m_data + self->m_strides[0] * i +
310341
self->m_strides[1] * j + self->m_strides[2] * k);
@@ -313,6 +344,9 @@ class array_view_accessors<AV, T, 3>
313344
const T &operator()(npy_intp i, npy_intp j, npy_intp k) const
314345
{
315346
const AVC *self = static_cast<const AVC *>(this);
347+
if (unlikely(self->m_empty)) {
348+
throw std::out_of_range("indexing into an array_view with no data");
349+
}
316350

317351
return *reinterpret_cast<const T *>(self->m_data + self->m_strides[0] * i +
318352
self->m_strides[1] * j + self->m_strides[2] * k);
@@ -321,6 +355,9 @@ class array_view_accessors<AV, T, 3>
321355
sub_t operator[](npy_intp i) const
322356
{
323357
const AVC *self = static_cast<const AVC *>(this);
358+
if (unlikely(self->m_empty)) {
359+
throw std::out_of_range("indexing into an array_view with no data");
360+
}
324361

325362
return sub_t(self->m_arr,
326363
self->m_data + self->m_strides[0] * i,
@@ -349,6 +386,9 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
349386
npy_intp *m_shape;
350387
npy_intp *m_strides;
351388
char *m_data;
389+
// Flag for a limited kind of bounds checking,
390+
// not the same as the empty() method which checks the outer dimension
391+
bool m_empty;
352392

353393
public:
354394
typedef T value_type;
@@ -361,6 +401,7 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
361401
{
362402
m_shape = zeros;
363403
m_strides = zeros;
404+
m_empty = true;
364405
}
365406

366407
array_view(PyObject *arr, bool contiguous = false) : m_arr(NULL), m_data(NULL)
@@ -377,6 +418,7 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
377418
m_data = other.m_data;
378419
m_shape = other.m_shape;
379420
m_strides = other.m_strides;
421+
m_empty = other.m_empty;
380422
}
381423

382424
array_view(PyArrayObject *arr, char *data, npy_intp *shape, npy_intp *strides)
@@ -386,6 +428,12 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
386428
m_data = data;
387429
m_shape = shape;
388430
m_strides = strides;
431+
m_empty = (ND == 0);
432+
for (size_t i = 0; i < ND; i++) {
433+
if (shape[i] == 0) {
434+
m_empty = true;
435+
}
436+
}
389437
}
390438

391439
array_view(npy_intp shape[ND]) : m_arr(NULL), m_shape(NULL), m_strides(NULL), m_data(NULL)
@@ -416,6 +464,7 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
416464
m_data = other.m_data;
417465
m_shape = other.m_shape;
418466
m_strides = other.m_strides;
467+
m_empty = other.m_empty;
419468
}
420469
return *this;
421470
}
@@ -430,6 +479,7 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
430479
m_data = NULL;
431480
m_shape = zeros;
432481
m_strides = zeros;
482+
m_empty = true;
433483
} else {
434484
if (contiguous) {
435485
tmp = (PyArrayObject *)PyArray_ContiguousFromAny(arr, type_num_of<T>::value, 0, ND);
@@ -446,10 +496,11 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
446496
m_data = NULL;
447497
m_shape = zeros;
448498
m_strides = zeros;
449-
if (PyArray_NDIM(tmp) == 0 && ND == 0) {
450-
m_arr = tmp;
451-
return 1;
452-
}
499+
if (PyArray_NDIM(tmp) == 0 && ND == 0) {
500+
m_arr = tmp;
501+
m_empty = true;
502+
return 1;
503+
}
453504
}
454505
if (PyArray_NDIM(tmp) != ND) {
455506
PyErr_Format(PyExc_ValueError,
@@ -466,6 +517,12 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
466517
m_shape = PyArray_DIMS(m_arr);
467518
m_strides = PyArray_STRIDES(m_arr);
468519
m_data = (char *)PyArray_BYTES(tmp);
520+
m_empty = (ND == 0);
521+
for (size_t i = 0; i < ND; i++) {
522+
if (m_shape[i] == 0) {
523+
m_empty = true;
524+
}
525+
}
469526
}
470527

471528
return 1;

src/py_exceptions.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,31 @@ class exception : public std::exception
3232
} \
3333
catch (const std::bad_alloc) \
3434
{ \
35-
PyErr_Format(PyExc_MemoryError, "In %s: Out of memory", (name)); \
35+
PyErr_Format(PyExc_MemoryError, "In %s: Out of memory", (name)); \
3636
{ \
3737
cleanup; \
3838
} \
3939
return (errorcode); \
4040
} \
4141
catch (const std::overflow_error &e) \
4242
{ \
43-
PyErr_Format(PyExc_OverflowError, "In %s: %s", (name), e.what()); \
43+
PyErr_Format(PyExc_OverflowError, "In %s: %s", (name), e.what()); \
44+
{ \
45+
cleanup; \
46+
} \
47+
return (errorcode); \
48+
} \
49+
catch (const std::out_of_range &e) \
50+
{ \
51+
PyErr_Format(PyExc_IndexError, "In %s: %s", (name), e.what()); \
4452
{ \
4553
cleanup; \
4654
} \
4755
return (errorcode); \
4856
} \
4957
catch (char const *e) \
5058
{ \
51-
PyErr_Format(PyExc_RuntimeError, "In %s: %s", (name), e); \
59+
PyErr_Format(PyExc_RuntimeError, "In %s: %s", (name), e); \
5260
{ \
5361
cleanup; \
5462
} \

0 commit comments

Comments
 (0)