Explorar o código

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

Fixes: #173
rdb %!s(int64=8) %!d(string=hai) anos
pai
achega
dfa47e55ce

+ 5 - 7
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<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;
 

+ 164 - 87
panda/src/express/pointerToArray_ext.I

@@ -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 {

+ 29 - 10
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<PointerToArray<LMatrix3f> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags);
+INLINE int Extension<PointerToArray<LMatrix3f> >::
+__getbuffer__(PyObject *self, Py_buffer *view, int flags);
+
 template<>
-  INLINE int Extension<PointerToArray<LMatrix3d> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags);
+INLINE int Extension<PointerToArray<LMatrix3d> >::
+__getbuffer__(PyObject *self, Py_buffer *view, int flags);
+
 template<>
-  INLINE int Extension<PointerToArray<UnalignedLMatrix4f> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags);
+INLINE int Extension<PointerToArray<UnalignedLMatrix4f> >::
+__getbuffer__(PyObject *self, Py_buffer *view, int flags);
+
 template<>
-  INLINE int Extension<PointerToArray<UnalignedLMatrix4d> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags);
+INLINE int Extension<PointerToArray<UnalignedLMatrix4d> >::
+__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 Element>
 class Extension<ConstPointerToArray<Element> > : public ExtensionBase<ConstPointerToArray<Element> > {
 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<ConstPointerToArray<LMatrix3f> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
+INLINE int Extension<ConstPointerToArray<LMatrix3f> >::
+__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
+
 template<>
-  INLINE int Extension<ConstPointerToArray<LMatrix3d> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
+INLINE int Extension<ConstPointerToArray<LMatrix3d> >::
+__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
+
 template<>
-  INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4f> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
+INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4f> >::
+__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
+
 template<>
-  INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4d> >::__getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
+INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4d> >::
+__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.