From ed134d5ada340613988184906e77f07e6ea2d241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Thu, 15 Oct 2015 07:44:37 +0300 Subject: [PATCH 1/4] Throw errors when indexing into empty array_view objects Should catch issues like #5185 while not adding a big perf penalty --- src/numpy_cpp.h | 65 ++++++++++++++++++++++++++++++++++++++++++--- src/py_exceptions.h | 14 +++++++--- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h index 0bc2d5c03854..7480503cf462 100644 --- a/src/numpy_cpp.h +++ b/src/numpy_cpp.h @@ -27,6 +27,13 @@ #include #include +#include + +#if defined(__GNUC__) || defined(__clang__) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define unlikely(x) (x) +#endif namespace numpy { @@ -235,6 +242,9 @@ class array_view_accessors T &operator()(npy_intp i) { AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + throw std::out_of_range("indexing into an array_view with no data"); + } return *reinterpret_cast(self->m_data + self->m_strides[0] * i); } @@ -242,6 +252,9 @@ class array_view_accessors const T &operator()(npy_intp i) const { const AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + throw std::out_of_range("indexing into an array_view with no data"); + } return *reinterpret_cast(self->m_data + self->m_strides[0] * i); } @@ -249,6 +262,9 @@ class array_view_accessors T &operator[](npy_intp i) { AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + throw std::out_of_range("indexing into an array_view with no data"); + } return *reinterpret_cast(self->m_data + self->m_strides[0] * i); } @@ -256,6 +272,9 @@ class array_view_accessors const T &operator[](npy_intp i) const { const AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + throw std::out_of_range("indexing into an array_view with no data"); + } return *reinterpret_cast(self->m_data + self->m_strides[0] * i); } @@ -271,6 +290,9 @@ class array_view_accessors T &operator()(npy_intp i, npy_intp j) { AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + throw std::out_of_range("indexing into an array_view with no data"); + } return *reinterpret_cast(self->m_data + self->m_strides[0] * i + self->m_strides[1] * j); @@ -279,6 +301,9 @@ class array_view_accessors const T &operator()(npy_intp i, npy_intp j) const { const AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + throw std::out_of_range("indexing into an array_view with no data"); + } return *reinterpret_cast(self->m_data + self->m_strides[0] * i + self->m_strides[1] * j); @@ -287,6 +312,9 @@ class array_view_accessors sub_t operator[](npy_intp i) const { const AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + 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, @@ -305,6 +333,9 @@ class array_view_accessors T &operator()(npy_intp i, npy_intp j, npy_intp k) { AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + throw std::out_of_range("indexing into an array_view with no data"); + } return *reinterpret_cast(self->m_data + self->m_strides[0] * i + self->m_strides[1] * j + self->m_strides[2] * k); @@ -313,6 +344,9 @@ class array_view_accessors const T &operator()(npy_intp i, npy_intp j, npy_intp k) const { const AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + throw std::out_of_range("indexing into an array_view with no data"); + } return *reinterpret_cast(self->m_data + self->m_strides[0] * i + self->m_strides[1] * j + self->m_strides[2] * k); @@ -321,6 +355,9 @@ class array_view_accessors sub_t operator[](npy_intp i) const { const AVC *self = static_cast(this); + if (unlikely(self->m_empty)) { + 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, @@ -349,6 +386,9 @@ class array_view : public detail::array_view_accessors npy_intp *m_shape; npy_intp *m_strides; char *m_data; + // Flag for a limited kind of bounds checking, + // not the same as the empty() method which checks the outer dimension + bool m_empty; public: typedef T value_type; @@ -361,6 +401,7 @@ class array_view : public detail::array_view_accessors { m_shape = zeros; m_strides = zeros; + m_empty = true; } 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 m_data = other.m_data; m_shape = other.m_shape; m_strides = other.m_strides; + m_empty = other.m_empty; } array_view(PyArrayObject *arr, char *data, npy_intp *shape, npy_intp *strides) @@ -386,6 +428,12 @@ class array_view : public detail::array_view_accessors m_data = data; m_shape = shape; m_strides = strides; + m_empty = (ND == 0); + for (size_t i = 0; i < ND; i++) { + if (shape[i] == 0) { + m_empty = true; + } + } } 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 m_data = other.m_data; m_shape = other.m_shape; m_strides = other.m_strides; + m_empty = other.m_empty; } return *this; } @@ -430,6 +479,7 @@ class array_view : public detail::array_view_accessors m_data = NULL; m_shape = zeros; m_strides = zeros; + m_empty = true; } else { if (contiguous) { tmp = (PyArrayObject *)PyArray_ContiguousFromAny(arr, type_num_of::value, 0, ND); @@ -446,10 +496,11 @@ class array_view : public detail::array_view_accessors 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; + m_empty = true; + return 1; + } } if (PyArray_NDIM(tmp) != ND) { PyErr_Format(PyExc_ValueError, @@ -466,6 +517,12 @@ class array_view : public detail::array_view_accessors m_shape = PyArray_DIMS(m_arr); m_strides = PyArray_STRIDES(m_arr); m_data = (char *)PyArray_BYTES(tmp); + m_empty = (ND == 0); + for (size_t i = 0; i < ND; i++) { + if (m_shape[i] == 0) { + m_empty = true; + } + } } return 1; diff --git a/src/py_exceptions.h b/src/py_exceptions.h index 82dd94dedcab..765017bf3c0b 100644 --- a/src/py_exceptions.h +++ b/src/py_exceptions.h @@ -32,7 +32,7 @@ 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; \ } \ @@ -40,7 +40,15 @@ class exception : public std::exception } \ 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; \ } \ @@ -48,7 +56,7 @@ class exception : public std::exception } \ catch (char const *e) \ { \ - PyErr_Format(PyExc_RuntimeError, "In %s: %s", (name), e); \ + PyErr_Format(PyExc_RuntimeError, "In %s: %s", (name), e); \ { \ cleanup; \ } \ From aa38c5b0e338a708db573e47632019bb0bfe3248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Thu, 15 Oct 2015 09:28:28 +0300 Subject: [PATCH 2/4] Use null data instead of a separate flag to mark empty array views --- src/numpy_cpp.h | 57 +++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h index 7480503cf462..44c7a3566d68 100644 --- a/src/numpy_cpp.h +++ b/src/numpy_cpp.h @@ -242,7 +242,7 @@ class array_view_accessors T &operator()(npy_intp i) { AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -252,7 +252,7 @@ class array_view_accessors const T &operator()(npy_intp i) const { const AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -262,7 +262,7 @@ class array_view_accessors T &operator[](npy_intp i) { AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -272,7 +272,7 @@ class array_view_accessors const T &operator[](npy_intp i) const { const AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -290,7 +290,7 @@ class array_view_accessors T &operator()(npy_intp i, npy_intp j) { AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -301,7 +301,7 @@ class array_view_accessors const T &operator()(npy_intp i, npy_intp j) const { const AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -312,7 +312,7 @@ class array_view_accessors sub_t operator[](npy_intp i) const { const AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -333,7 +333,7 @@ class array_view_accessors T &operator()(npy_intp i, npy_intp j, npy_intp k) { AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -344,7 +344,7 @@ class array_view_accessors const T &operator()(npy_intp i, npy_intp j, npy_intp k) const { const AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -355,7 +355,7 @@ class array_view_accessors sub_t operator[](npy_intp i) const { const AVC *self = static_cast(this); - if (unlikely(self->m_empty)) { + if (unlikely(self->m_data == NULL)) { throw std::out_of_range("indexing into an array_view with no data"); } @@ -386,9 +386,6 @@ class array_view : public detail::array_view_accessors npy_intp *m_shape; npy_intp *m_strides; char *m_data; - // Flag for a limited kind of bounds checking, - // not the same as the empty() method which checks the outer dimension - bool m_empty; public: typedef T value_type; @@ -401,7 +398,6 @@ class array_view : public detail::array_view_accessors { m_shape = zeros; m_strides = zeros; - m_empty = true; } array_view(PyObject *arr, bool contiguous = false) : m_arr(NULL), m_data(NULL) @@ -418,22 +414,26 @@ class array_view : public detail::array_view_accessors m_data = other.m_data; m_shape = other.m_shape; m_strides = other.m_strides; - m_empty = other.m_empty; } array_view(PyArrayObject *arr, char *data, npy_intp *shape, npy_intp *strides) { - m_arr = arr; - Py_XINCREF(arr); - m_data = data; - m_shape = shape; - m_strides = strides; - m_empty = (ND == 0); + bool empty = (ND == 0); for (size_t i = 0; i < ND; i++) { if (shape[i] == 0) { - m_empty = true; + empty = true; } } + + m_arr = arr; + Py_XINCREF(arr); + if (empty) { + m_data = NULL; + } else { + m_data = data; + } + m_shape = shape; + m_strides = strides; } array_view(npy_intp shape[ND]) : m_arr(NULL), m_shape(NULL), m_strides(NULL), m_data(NULL) @@ -464,7 +464,6 @@ class array_view : public detail::array_view_accessors m_data = other.m_data; m_shape = other.m_shape; m_strides = other.m_strides; - m_empty = other.m_empty; } return *this; } @@ -479,7 +478,6 @@ class array_view : public detail::array_view_accessors m_data = NULL; m_shape = zeros; m_strides = zeros; - m_empty = true; } else { if (contiguous) { tmp = (PyArrayObject *)PyArray_ContiguousFromAny(arr, type_num_of::value, 0, ND); @@ -498,7 +496,6 @@ class array_view : public detail::array_view_accessors m_strides = zeros; if (PyArray_NDIM(tmp) == 0 && ND == 0) { m_arr = tmp; - m_empty = true; return 1; } } @@ -516,13 +513,17 @@ class array_view : public detail::array_view_accessors m_arr = tmp; m_shape = PyArray_DIMS(m_arr); m_strides = PyArray_STRIDES(m_arr); - m_data = (char *)PyArray_BYTES(tmp); - m_empty = (ND == 0); + bool empty = (ND == 0); for (size_t i = 0; i < ND; i++) { if (m_shape[i] == 0) { - m_empty = true; + empty = true; } } + if (empty) { + m_data = NULL; + } else { + m_data = (char *)PyArray_BYTES(tmp); + } } return 1; From cae71f6a13f1c35e40fd8ecf1bda34cb5a7bd116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Thu, 15 Oct 2015 15:28:32 +0300 Subject: [PATCH 3/4] Make array_view.size return 0 for empty arrays --- src/numpy_cpp.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h index 44c7a3566d68..ff1b0033db58 100644 --- a/src/numpy_cpp.h +++ b/src/numpy_cpp.h @@ -539,7 +539,17 @@ class array_view : public detail::array_view_accessors size_t size() const { - return (size_t)dim(0); + bool empty = (ND == 0); + for (size_t i = 0; i < ND; i++) { + if (m_shape[i] == 0) { + empty = true; + } + } + if (empty) { + return 0; + } else { + return (size_t)dim(0); + } } bool empty() const From 8698b5ee9363c4b9bd37537a4b0e6882bdf16cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Thu, 15 Oct 2015 18:41:55 +0300 Subject: [PATCH 4/4] Use m_data==NULL as the condition in size() --- src/numpy_cpp.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h index ff1b0033db58..1629688792bd 100644 --- a/src/numpy_cpp.h +++ b/src/numpy_cpp.h @@ -539,13 +539,7 @@ class array_view : public detail::array_view_accessors size_t size() const { - bool empty = (ND == 0); - for (size_t i = 0; i < ND; i++) { - if (m_shape[i] == 0) { - empty = true; - } - } - if (empty) { + if (m_data == NULL) { return 0; } else { return (size_t)dim(0); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy