Permalink
Browse files

express: fix PTA get_data in Python 3, fix CPTA construction

Fixes: #173
  • Loading branch information...
rdb committed Oct 6, 2017
1 parent 5ac3ccb commit dfa47e55ce0caa48435069752215beef86018f85
Showing with 198 additions and 104 deletions.
  1. +5 −7 panda/src/express/pointerToArray.h
  2. +164 −87 panda/src/express/pointerToArray_ext.I
  3. +29 −10 panda/src/express/pointerToArray_ext.h
@@ -105,9 +105,9 @@ class PointerToArray : public PointerToArrayBase<Element> {
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 @@ class ConstPointerToArray : public PointerToArrayBase<Element> {
INLINE ConstPointerToArray(const PointerToArray<Element> &copy);
INLINE ConstPointerToArray(const ConstPointerToArray<Element> &copy);
EXTENSION(ConstPointerToArray(PyObject *self, PyObject *source));
typedef TYPENAME pvector<Element>::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;
@@ -79,93 +79,26 @@ INLINE void Extension<PointerToArray<Element> >::
__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<class Element>
INLINE void Extension<ConstPointerToArray<Element> >::
__init__(PyObject *self, PyObject *source) {
PointerToArray<Element> array;
invoke_extension(&array).__init__(self, source);
*(this->_this) = MOVE(array);
INLINE PyObject *Extension<PointerToArray<Element> >::
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<class Element>
INLINE void Extension<PointerToArray<Element> >::
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<class Element>
INLINE PyObject *Extension<PointerToArray<Element> >::
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<class Element>
INLINE PyObject *Extension<ConstPointerToArray<Element> >::
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<class Element>
INLINE PyObject *Extension<ConstPointerToArray<Element> >::
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<ConstPointerToArray<LMatrix3f> >::
__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<ConstPointerToArray<LMatrix3d> >::
__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<ConstPointerToArray<UnalignedLMatrix4f> >::
__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<ConstPointerToArray<UnalignedLMatrix4d> >::
__getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
Oops, something went wrong.

0 comments on commit dfa47e5

Please sign in to comment.