Skip to content

Throw errors when indexing into empty array_view objects #5247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 69 additions & 7 deletions src/numpy_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@

#include <Python.h>
#include <numpy/ndarrayobject.h>
#include <stdexcept>

#if defined(__GNUC__) || defined(__clang__)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define unlikely(x) (x)
#endif

namespace numpy
{
Expand Down Expand Up @@ -235,27 +242,39 @@ class array_view_accessors<AV, T, 1>
T &operator()(npy_intp i)
{
AVC *self = static_cast<AVC *>(this);
if (unlikely(self->m_data == NULL)) {
throw std::out_of_range("indexing into an array_view with no data");
}

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

const T &operator()(npy_intp i) const
{
const AVC *self = static_cast<const AVC *>(this);
if (unlikely(self->m_data == NULL)) {
throw std::out_of_range("indexing into an array_view with no data");
}

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

T &operator[](npy_intp i)
{
AVC *self = static_cast<AVC *>(this);
if (unlikely(self->m_data == NULL)) {
throw std::out_of_range("indexing into an array_view with no data");
}

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

const T &operator[](npy_intp i) const
{
const AVC *self = static_cast<const AVC *>(this);
if (unlikely(self->m_data == NULL)) {
throw std::out_of_range("indexing into an array_view with no data");
}

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

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

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

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

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

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

return sub_t(self->m_arr,
self->m_data + self->m_strides[0] * i,
Expand Down Expand Up @@ -381,9 +418,20 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>

array_view(PyArrayObject *arr, char *data, npy_intp *shape, npy_intp *strides)
{
bool empty = (ND == 0);
for (size_t i = 0; i < ND; i++) {
if (shape[i] == 0) {
empty = true;
}
}

m_arr = arr;
Py_XINCREF(arr);
m_data = data;
if (empty) {
m_data = NULL;
} else {
m_data = data;
}
m_shape = shape;
m_strides = strides;
}
Expand Down Expand Up @@ -446,10 +494,10 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
m_data = NULL;
m_shape = zeros;
m_strides = zeros;
if (PyArray_NDIM(tmp) == 0 && ND == 0) {
m_arr = tmp;
return 1;
}
if (PyArray_NDIM(tmp) == 0 && ND == 0) {
m_arr = tmp;
return 1;
}
}
if (PyArray_NDIM(tmp) != ND) {
PyErr_Format(PyExc_ValueError,
Expand All @@ -465,7 +513,17 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
m_arr = tmp;
m_shape = PyArray_DIMS(m_arr);
m_strides = PyArray_STRIDES(m_arr);
m_data = (char *)PyArray_BYTES(tmp);
bool empty = (ND == 0);
for (size_t i = 0; i < ND; i++) {
if (m_shape[i] == 0) {
empty = true;
}
}
if (empty) {
m_data = NULL;
} else {
m_data = (char *)PyArray_BYTES(tmp);
}
}

return 1;
Expand All @@ -481,7 +539,11 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>

size_t size() const
{
return (size_t)dim(0);
if (m_data == NULL) {
return 0;
} else {
return (size_t)dim(0);
}
}

bool empty() const
Expand Down
14 changes: 11 additions & 3 deletions src/py_exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,31 @@ class exception : public std::exception
} \
catch (const std::bad_alloc) \
{ \
PyErr_Format(PyExc_MemoryError, "In %s: Out of memory", (name)); \
PyErr_Format(PyExc_MemoryError, "In %s: Out of memory", (name)); \
{ \
cleanup; \
} \
return (errorcode); \
} \
catch (const std::overflow_error &e) \
{ \
PyErr_Format(PyExc_OverflowError, "In %s: %s", (name), e.what()); \
PyErr_Format(PyExc_OverflowError, "In %s: %s", (name), e.what()); \
{ \
cleanup; \
} \
return (errorcode); \
} \
catch (const std::out_of_range &e) \
{ \
PyErr_Format(PyExc_IndexError, "In %s: %s", (name), e.what()); \
{ \
cleanup; \
} \
return (errorcode); \
} \
catch (char const *e) \
{ \
PyErr_Format(PyExc_RuntimeError, "In %s: %s", (name), e); \
PyErr_Format(PyExc_RuntimeError, "In %s: %s", (name), e); \
{ \
cleanup; \
} \
Expand Down