From dfa47e55ce0caa48435069752215beef86018f85 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 6 Oct 2017 18:23:37 +0200 Subject: [PATCH] express: fix PTA get_data in Python 3, fix CPTA construction Fixes: #173 --- panda/src/express/pointerToArray.h | 12 +- panda/src/express/pointerToArray_ext.I | 251 ++++++++++++++++--------- panda/src/express/pointerToArray_ext.h | 39 +++- 3 files changed, 198 insertions(+), 104 deletions(-) diff --git a/panda/src/express/pointerToArray.h b/panda/src/express/pointerToArray.h index d486f1b00f..b4458f17b8 100644 --- a/panda/src/express/pointerToArray.h +++ b/panda/src/express/pointerToArray.h @@ -105,9 +105,9 @@ PUBLISHED: INLINE void set_element(size_type n, const Element &value); EXTENSION(const Element &__getitem__(size_type n) const); EXTENSION(void __setitem__(size_type n, const Element &value)); - INLINE string get_data() const; - INLINE void set_data(const string &data); - INLINE string get_subdata(size_type n, size_type count) const; + EXTENSION(PyObject *get_data() const); + EXTENSION(void set_data(PyObject *data)); + EXTENSION(PyObject *get_subdata(size_type n, size_type count) const); INLINE void set_subdata(size_type n, size_type count, const string &data); INLINE int get_ref_count() const; INLINE int get_node_ref_count() const; @@ -255,14 +255,12 @@ PUBLISHED: INLINE ConstPointerToArray(const PointerToArray ©); INLINE ConstPointerToArray(const ConstPointerToArray ©); - EXTENSION(ConstPointerToArray(PyObject *self, PyObject *source)); - typedef TYPENAME pvector::size_type size_type; INLINE size_type size() const; INLINE const Element &get_element(size_type n) const; EXTENSION(const Element &__getitem__(size_type n) const); - INLINE string get_data() const; - INLINE string get_subdata(size_type n, size_type count) const; + EXTENSION(PyObject *get_data() const); + EXTENSION(PyObject *get_subdata(size_type n, size_type count) const); INLINE int get_ref_count() const; INLINE int get_node_ref_count() const; diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I index b7126e8363..6b8a9d54d0 100644 --- a/panda/src/express/pointerToArray_ext.I +++ b/panda/src/express/pointerToArray_ext.I @@ -79,93 +79,26 @@ INLINE void Extension >:: __init__(PyObject *self, PyObject *source) { #if PY_VERSION_HEX >= 0x02060000 if (PyObject_CheckBuffer(source)) { - // User passed a buffer object. - Py_buffer view; - if (PyObject_GetBuffer(source, &view, PyBUF_CONTIG_RO) == -1) { - PyErr_SetString(PyExc_TypeError, - "PointerToArray constructor requires a contiguous buffer"); - return; - } - - if (view.itemsize != 1 && view.itemsize != sizeof(Element)) { - PyErr_SetString(PyExc_TypeError, - "buffer.itemsize does not match PointerToArray element size"); - return; - } - - if (view.len % sizeof(Element) != 0) { - PyErr_Format(PyExc_ValueError, - "byte buffer is not a multiple of %zu bytes", - sizeof(Element)); - return; - } - - if (view.len > 0) { - this->_this->resize(view.len / sizeof(Element)); - memcpy(this->_this->p(), view.buf, view.len); - } - - PyBuffer_Release(&view); +#else + if (PyString_CheckExact(source)) { +#endif + // It's a byte sequence, or any object that exports the buffer protocol. + this->set_data(source); return; } -#endif - if (!PySequence_Check(source)) { + // Don't allow a unicode object even though it's a sequence. + if (!PySequence_Check(source) || PyUnicode_CheckExact(source)) { // If passed with a non-sequence, this isn't the right constructor. PyErr_SetString(PyExc_TypeError, "PointerToArray constructor requires a sequence or buffer object"); return; } - // If we were passed a Python string, then instead of storing it character- - // at-a-time, just load the whole string as a data buffer. Not sure if this - // case is still necessary - don't Python strbytes objects export the buffer - // protocol, as above? -#if PY_MAJOR_VERSION >= 3 - if (PyBytes_Check(source)) { - int size = PyBytes_Size(source); - if (size % sizeof(Element) != 0) { - PyErr_Format(PyExc_ValueError, - "bytes object is not a multiple of %zu bytes", - sizeof(Element)); - return; - } - - int num_elements = size / sizeof(Element); - this->_this->insert(this->_this->begin(), num_elements, Element()); - - // Hope there aren't any constructors or destructors involved here. - if (size != 0) { - const char *data = PyBytes_AsString(source); - memcpy(this->_this->p(), data, size); - } - return; - } -#else - if (PyString_CheckExact(source)) { - int size = PyString_Size(source); - if (size % sizeof(Element) != 0) { - PyErr_Format(PyExc_ValueError, - "str object is not a multiple of %zu bytes", - sizeof(Element)); - return; - } - - int num_elements = size / sizeof(Element); - this->_this->insert(this->_this->begin(), num_elements, Element()); - - // Hope there aren't any constructors or destructors involved here. - if (size != 0) { - const char *data = PyString_AsString(source); - memcpy(this->_this->p(), data, size); - } - return; - } -#endif - // Now construct the internal list by copying the elements one-at-a-time // from Python. - PyObject *push_back = PyObject_GetAttrString(self, "push_back"); + PyObject *dict = ((Dtool_PyInstDef *)self)->_My_Type->_PyType.tp_dict; + PyObject *push_back = PyDict_GetItemString(dict, "push_back"); if (push_back == NULL) { PyErr_BadArgument(); return; @@ -174,19 +107,20 @@ __init__(PyObject *self, PyObject *source) { // We need to initialize the this pointer before we can call push_back. ((Dtool_PyInstDef *)self)->_ptr_to_object = (void *)this->_this; - int size = PySequence_Size(source); - for (int i = 0; i < size; ++i) { + Py_ssize_t size = PySequence_Size(source); + this->_this->reserve(size); + for (Py_ssize_t i = 0; i < size; ++i) { PyObject *item = PySequence_GetItem(source, i); if (item == NULL) { return; } - PyObject *result = PyObject_CallFunctionObjArgs(push_back, item, NULL); + PyObject *result = PyObject_CallFunctionObjArgs(push_back, self, item, NULL); Py_DECREF(item); if (result == NULL) { // Unable to add item--probably it wasn't of the appropriate type. PyErr_Print(); PyErr_Format(PyExc_TypeError, - "Element %d in sequence passed to PointerToArray " + "Element %zd in sequence passed to PointerToArray " "constructor could not be added", i); return; } @@ -213,15 +147,110 @@ __setitem__(size_t n, const Element &value) { } /** - * This special constructor accepts a Python list of elements, or a Python - * string (or a bytes object, in Python 3). + * This returns the entire contents of the vector as a block of raw data in a + * string (or bytes object, in Python 3). + * + * @deprecated use memoryview(pta) or bytearray(pta) instead. */ template -INLINE void Extension >:: -__init__(PyObject *self, PyObject *source) { - PointerToArray array; - invoke_extension(&array).__init__(self, source); - *(this->_this) = MOVE(array); +INLINE PyObject *Extension >:: +get_data() const { +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size()); +#else + return PyString_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size()); +#endif +} + +/** + * This method exists mainly to access the data of the array easily from a + * high-level language such as Python. + * + * This replaces the entire contents of the vector from a block of raw data + * in a string (or bytes object, in Python 3). + */ +template +INLINE void Extension >:: +set_data(PyObject *data) { +#if PY_VERSION_HEX >= 0x02060000 + if (PyObject_CheckBuffer(data)) { + // User passed a buffer object. + Py_buffer view; + if (PyObject_GetBuffer(data, &view, PyBUF_CONTIG_RO) == -1) { + PyErr_SetString(PyExc_TypeError, + "PointerToArray.set_data() requires a contiguous buffer"); + return; + } + + if (view.itemsize != 1 && view.itemsize != sizeof(Element)) { + PyErr_SetString(PyExc_TypeError, + "buffer.itemsize does not match PointerToArray element size"); + return; + } + + if (view.len % sizeof(Element) != 0) { + PyErr_Format(PyExc_ValueError, + "byte buffer is not a multiple of %zu bytes", + sizeof(Element)); + return; + } + + if (view.len > 0) { + this->_this->resize(view.len / sizeof(Element)); + memcpy(this->_this->p(), view.buf, view.len); + } else { + this->_this->clear(); + } + + PyBuffer_Release(&view); + } else { + Dtool_Raise_TypeError("PointerToArray.set_data() requires a buffer object"); + } +#else + // In Python 2.5 we didn't have the new buffer protocol, only str. + if (PyString_CheckExact(data)) { + int size = PyString_Size(data); + if (size % sizeof(Element) != 0) { + PyErr_Format(PyExc_ValueError, + "str object is not a multiple of %zu bytes", + sizeof(Element)); + return; + } + + int num_elements = size / sizeof(Element); + this->_this->insert(this->_this->begin(), num_elements, Element()); + + // Hope there aren't any constructors or destructors involved here. + if (size != 0) { + const char *ptr = PyString_AsString(data); + memcpy(this->_this->p(), ptr, size); + } else { + this->_this->clear(); + } + } else { + Dtool_Raise_TypeError("PointerToArray.set_data() requires a str"); + } +#endif +} + +/** + * This returns the contents of a portion of the vector--from element (n) + * through element (n + count - 1)--as a block of raw data in a string (or + * bytes object, in Python 3). + * + * @deprecated use memoryview(pta) or bytearray(pta) instead. + */ +template +INLINE PyObject *Extension >:: +get_subdata(size_t n, size_t count) const { + n = min(n, this->_this->size()); + count = max(count, n); + count = min(count, this->_this->size() - n); +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); +#else + return PyString_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); +#endif } /** @@ -233,6 +262,42 @@ __getitem__(size_t n) const { return (*this->_this)[n]; } +/** + * This returns the entire contents of the vector as a block of raw data in a + * string (or bytes object, in Python 3). + * + * @deprecated use memoryview(pta) or bytearray(pta) instead. + */ +template +INLINE PyObject *Extension >:: +get_data() const { +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size()); +#else + return PyString_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size()); +#endif +} + +/** + * This returns the contents of a portion of the vector--from element (n) + * through element (n + count - 1)--as a block of raw data in a string (or + * bytes object, in Python 3). + * + * @deprecated use memoryview(pta) or bytearray(pta) instead. + */ +template +INLINE PyObject *Extension >:: +get_subdata(size_t n, size_t count) const { + n = min(n, this->_this->size()); + count = max(count, n); + count = min(count, this->_this->size() - n); +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); +#else + return PyString_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); +#endif +} + /** * This is used to implement the buffer protocol, in order to allow efficient * access to the array data through a Python multiview object. @@ -461,6 +526,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { #endif } +/** + * Specialization on __getbuffer__ for LMatrix3f. + */ template<> INLINE int Extension >:: __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { @@ -488,6 +556,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { #endif } +/** + * Specialization on __getbuffer__ for LMatrix3d. + */ template<> INLINE int Extension >:: __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { @@ -515,6 +586,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { #endif } +/** + * Specialization on __getbuffer__ for UnalignedLMatrix4f. + */ template<> INLINE int Extension >:: __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { @@ -542,6 +616,9 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { #endif } +/** + * Specialization on __getbuffer__ for UnalignedLMatrix4d. + */ template<> INLINE int Extension >:: __getbuffer__(PyObject *self, Py_buffer *view, int flags) const { diff --git a/panda/src/express/pointerToArray_ext.h b/panda/src/express/pointerToArray_ext.h index 9fea54cbc4..469b449fed 100644 --- a/panda/src/express/pointerToArray_ext.h +++ b/panda/src/express/pointerToArray_ext.h @@ -36,18 +36,29 @@ public: INLINE const Element &__getitem__(size_t n) const; INLINE void __setitem__(size_t n, const Element &value); + INLINE PyObject *get_data() const; + INLINE void set_data(PyObject *data); + INLINE PyObject *get_subdata(size_t n, size_t count) const; + INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags); INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const; }; template<> - INLINE int Extension >::__getbuffer__(PyObject *self, Py_buffer *view, int flags); +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags); + template<> - INLINE int Extension >::__getbuffer__(PyObject *self, Py_buffer *view, int flags); +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags); + template<> - INLINE int Extension >::__getbuffer__(PyObject *self, Py_buffer *view, int flags); +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags); + template<> - INLINE int Extension >::__getbuffer__(PyObject *self, Py_buffer *view, int flags); +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags); /** * This class defines the extension methods for ConstPointerToArray, which are @@ -59,22 +70,30 @@ template<> template class Extension > : public ExtensionBase > { public: - INLINE void __init__(PyObject *self, PyObject *source); - INLINE const Element &__getitem__(size_t n) const; + INLINE PyObject *get_data() const; + INLINE PyObject *get_subdata(size_t n, size_t count) const; + INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const; INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const; }; template<> - INLINE int Extension >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; + template<> - INLINE int Extension >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; + template<> - INLINE int Extension >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; + template<> - INLINE int Extension >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; +INLINE int Extension >:: +__getbuffer__(PyObject *self, Py_buffer *view, int flags) const; #ifdef _MSC_VER // Ugh... MSVC needs this because they still don't have a decent linker.