Lenard Lindstrom avatar Lenard Lindstrom committed 841b902

Centralize common array struct interface operations in the _view module

Add a C level api to _view: Pg_GetArrayInterface(PyObject *, PyObject **, PyArrayInterface **), a function to retrieve a PyArrayInterface C struct from an object, and Pg_ArrayStructAsDict(PyArrayInterface *), a function to convert a PyStructInterface value into a Python array interface dict instance. Also add a Python level _view module method, get_array_interface(obj), for unit testing of array structs.

Alter existing modules to use the new _view api, which replaces multiple instances of the static get_array_interface() C function.

Comments (0)

Files changed (6)

     PyObject *weakrefs;           /* There can be reference cycles   */
 } PgViewObject;
 
+static int Pg_GetArrayInterface(PyObject *, PyObject **, PyArrayInterface **);
+static PyObject *Pg_ArrayStructAsDict(PyArrayInterface *);
+
 /**
  * Helper functions.
  */
     return (PyObject *)self;
 }
 
+static PyObject *
+_shape_as_tuple(PyArrayInterface *inter_p)
+{
+    PyObject *shapeobj = PyTuple_New((Py_ssize_t)inter_p->nd);
+    PyObject *lengthobj;
+    Py_ssize_t i;
+
+    if (!shapeobj) {
+        return 0;
+    }
+    for (i = 0; i < inter_p->nd; ++i) {
+        lengthobj = PyInt_FromLong((long)inter_p->shape[i]);
+        if (!lengthobj) {
+            Py_DECREF(shapeobj);
+            return 0;
+        }
+        PyTuple_SET_ITEM(shapeobj, i, lengthobj);
+    }
+    return shapeobj;
+}
+
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+#define MY_ENDIAN '<'
+#define OTHER_ENDIAN '>'
+#else
+#define MY_ENDIAN '>'
+#define OTHER_ENDIAN '<'
+#endif
+
+static PyObject *
+_typekind_as_str(PyArrayInterface *inter_p)
+{
+    return Text_FromFormat("%c%c%i", 
+                           inter_p->flags & PAI_NOTSWAPPED ?
+                           MY_ENDIAN : OTHER_ENDIAN,
+                           inter_p->typekind, inter_p->itemsize);
+}
+
+#undef MY_ENDIAN
+#undef OTHER_ENDIAN
+
+static PyObject *_strides_as_tuple(PyArrayInterface *inter_p)
+{
+    PyObject *stridesobj = PyTuple_New((Py_ssize_t)inter_p->nd);
+    PyObject *lengthobj;
+    Py_ssize_t i;
+
+    if (!stridesobj) {
+        return 0;
+    }
+    for (i = 0; i < inter_p->nd; ++i) {
+        lengthobj = PyInt_FromLong((long)inter_p->strides[i]);
+        if (!lengthobj) {
+            Py_DECREF(stridesobj);
+            return 0;
+        }
+        PyTuple_SET_ITEM(stridesobj, i, lengthobj);
+    }
+    return stridesobj;
+}
+
+static PyObject *_data_as_tuple(PyArrayInterface *inter_p)
+{
+    long readonly = (inter_p->flags & PAI_WRITEABLE) == 0;
+
+    return Py_BuildValue("NN",
+                         PyLong_FromVoidPtr(inter_p->data),
+                         PyBool_FromLong(readonly));
+}
+
 /**
  * Creates a new PgViewObject.
  */
 };
 
 
+/**** Module methods ***/
+
+static PyObject *
+get_array_interface(PyObject *self, PyObject *arg)
+{
+    PyObject *cobj;
+    PyArrayInterface *inter_p;
+    PyObject *dictobj;
+
+    if (Pg_GetArrayInterface(arg, &cobj, &inter_p)) {
+        return 0;
+    }
+    dictobj = Pg_ArrayStructAsDict(inter_p);
+    Py_DECREF(cobj);
+    return dictobj;
+}
+
+static PyMethodDef _view_methods[] = {
+    { "get_array_interface", get_array_interface, METH_O,
+      "return an array struct interface as an interface dictionary" },
+    {0, 0, 0, 0}
+};
+
 /**** Public C api ***/
 
 static PyObject *
     return parent;
 }
 
+static int
+Pg_GetArrayInterface(PyObject *obj,
+                     PyObject **cobj_p,
+                     PyArrayInterface **inter_p)
+{
+    PyObject *cobj = PyObject_GetAttrString(obj, "__array_struct__");
+    PyArrayInterface *inter = NULL;
+
+    if (cobj == NULL) {
+        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+                PyErr_Clear();
+                PyErr_SetString(PyExc_ValueError,
+                                "no C-struct array interface");
+        }
+        return -1;
+    }
+
+#if PG_HAVE_COBJECT
+    if (PyCObject_Check(cobj)) {
+        inter = (PyArrayInterface *)PyCObject_AsVoidPtr(cobj);
+    }
+#endif
+#if PG_HAVE_CAPSULE
+    if (PyCapsule_IsValid(cobj, NULL)) {
+        inter = (PyArrayInterface *)PyCapsule_GetPointer(cobj, NULL);
+    }
+#endif
+    if (inter == NULL ||   /* conditional or */
+        inter->two != 2  ) {
+        Py_DECREF(cobj);
+        PyErr_SetString(PyExc_ValueError, "invalid array interface");
+        return -1;
+    }
+
+    *cobj_p = cobj;
+    *inter_p = inter;
+    return 0;
+}
+
+static PyObject *
+Pg_ArrayStructAsDict(PyArrayInterface *inter_p)
+{
+    PyObject *dictobj = Py_BuildValue("{sisNsNsNsN}",
+                                      "version", (int)3,
+                                      "typestr", _typekind_as_str(inter_p),
+                                      "shape", _shape_as_tuple(inter_p),
+                                      "strides", _strides_as_tuple(inter_p),
+                                      "data", _data_as_tuple(inter_p));
+
+    if (!dictobj) {
+        return 0;
+    }
+    if (inter_p->flags & PAI_ARR_HAS_DESCR) {
+        if (!inter_p->descr) {
+            Py_DECREF(dictobj);
+            PyErr_SetString(PyExc_ValueError,
+                            "Array struct has descr flag set"
+                            " but no descriptor");
+            return 0;
+        }
+        if (PyDict_SetItemString(dictobj, "descr", inter_p->descr)) {
+            return 0;
+        }
+    }
+    return dictobj;
+}
+
 /*DOC*/ static char _view_doc[] =
 /*DOC*/    "exports View, a generic wrapper object for an array "
 /*DOC*/    "struct capsule";
         "_view",
         _view_doc,
         -1,
-        NULL, NULL, NULL, NULL, NULL
+        _view_methods,
+        NULL, NULL, NULL, NULL
     };
 #endif
 
 #if PY3
     module = PyModule_Create(&_module);
 #else
-    module = Py_InitModule3(MODPREFIX "_view", NULL, _view_doc);
+    module = Py_InitModule3(MODPREFIX "_view", _view_methods, _view_doc);
 #endif
 
     Py_INCREF(&PgView_Type);
         DECREF_MOD(module);
         MODINIT_ERROR;
     }
-#if PYGAMEAPI_VIEW_NUMSLOTS != 4
+#if PYGAMEAPI_VIEW_NUMSLOTS != 6
 #error export slot count mismatch
 #endif
     c_api[0] = &PgView_Type;
     c_api[1] = PgView_New;
     c_api[2] = PgView_GetCapsule;
     c_api[3] = PgView_GetParent;
+    c_api[4] = Pg_GetArrayInterface;
+    c_api[5] = Pg_ArrayStructAsDict;
     apiobj = encapsulate_api(c_api, "_view");
     if (apiobj == NULL) {
         DECREF_MOD(module);
 #include "pgcompat.h"
 #include "doc/mixer_doc.h"
 #include "mixer.h"
-#include "pgarrinter.h"
+#include "pgview.h"
 
 #if !defined(EXPORT_BUFFER)
 #define EXPORT_BUFFER HAVE_NEW_BUFPROTO
     return size;
 }
 
-static int
-_get_array_interface(PyObject *obj,
-                     PyObject **cobj_p,
-                     PyArrayInterface **inter_p)
-{
-    PyObject *cobj = PyObject_GetAttrString(obj, "__array_struct__");
-    PyArrayInterface *inter = NULL;
-
-    if (cobj == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_Clear();
-                PyErr_SetString(PyExc_ValueError,
-                                "no C-struct array interface");
-        }
-        return -1;
-    }
-
-#if PG_HAVE_COBJECT
-    if (PyCObject_Check(cobj)) {
-        inter = (PyArrayInterface *)PyCObject_AsVoidPtr(cobj);
-    }
-#endif
-#if PG_HAVE_CAPSULE
-    if (PyCapsule_IsValid(cobj, NULL)) {
-        inter = (PyArrayInterface *)PyCapsule_GetPointer(cobj, NULL);
-    }
-#endif
-    if (inter == NULL ||   /* conditional or */
-        inter->two != 2  ) {
-        Py_DECREF(cobj);
-        PyErr_SetString(PyExc_ValueError, "invalid array interface");
-        return -1;
-    }
-
-    *cobj_p = cobj;
-    *inter_p = inter;
-    return 0;
-}
-
 static PG_sample_format_t
 _format_inter_to_audio(PyArrayInterface *inter)
 {
         PG_sample_format_t array_format;
         int rcode;
 
-        if (_get_array_interface(array, &cobj, &inter)) {
+        if (Pg_GetArrayInterface(array, &cobj, &inter)) {
             return -1;
         }
         if (!(inter->flags & PAI_CONTIGUOUS)) {
     if (PyErr_Occurred ()) {
         MODINIT_ERROR;
     }
+    import_pygame_view ();
+    if (PyErr_Occurred ()) {
+        MODINIT_ERROR;
+    }
 
     /* type preparation */
     if (PyType_Ready (&PySound_Type) < 0) {
 /* array structure interface version 3 declarations */
 
-const int PAI_CONTIGUOUS = 0x01;
-const int PAI_FORTRAN = 0x02;
-const int PAI_ALIGNED = 0x100;
-const int PAI_NOTSWAPPED = 0x200;
-const int PAI_WRITEABLE = 0x400;
-const int PAI_ARR_HAS_DESCR = 0x800;
+#if !defined(PG_ARRAYINTER_HEADER)
+#define PG_ARRAYINTER_HEADER
+
+static const int PAI_CONTIGUOUS = 0x01;
+static const int PAI_FORTRAN = 0x02;
+static const int PAI_ALIGNED = 0x100;
+static const int PAI_NOTSWAPPED = 0x200;
+static const int PAI_WRITEABLE = 0x400;
+static const int PAI_ARR_HAS_DESCR = 0x800;
 
 typedef struct {
     int two;              /* contains the integer 2 -- simple sanity check */
     void *data;           /* A pointer to the first element of the array */
     PyObject *descr;      /* NULL or a data-description */
 } PyArrayInterface;
+
+#endif
  */
 #if !defined(PG_VIEW_HEADER)
 
+#include "pgarrinter.h"
+
 typedef void (*PgView_Destructor)(PyObject *view);
 
-#define PYGAMEAPI_VIEW_NUMSLOTS 4
+#define PYGAMEAPI_VIEW_NUMSLOTS 6
 #define PYGAMEAPI_VIEW_FIRSTSLOT 0
 
 #if !(defined(PYGAMEAPI_VIEW_INTERNAL) || defined(NO_PYGAME_C_API))
                                    PyObject *parent,
                                    PgView_Destructor dest);
 typedef PyObject *(*_pgview_get_t)(PyObject *view);
+typedef int *(*_pg_getarrayinterface_t)(PyObject *obj,
+                                        PyObject **cobj_p,
+                                        PyArrayInterface **inter_p);
+typedef PyObject *(*_pg_arraystructasdict_t)(PyArrayInterface *inter_p);
 
 #define PgView_Type (*(PyTypeObject*)PgVIEW_C_API[0])
 #define PgView_New (*(_pgview_new_t)PgVIEW_C_API[1])
 #define PgView_GetCapsule (*(_pgview_get_t)PgVIEW_C_API[2])
 #define PgView_GetParent (*(_pgview_get_t)PgVIEW_C_API[3])
+#define Pg_GetArrayInterface (*(_pg_getarrayinterface_t)PgVIEW_C_API[4])
+#define Pg_ArrayStructAsDict (*(_pg_arraystructasdict_t)PgVIEW_C_API[5])
 #define PgView_Check(x) ((x)->ob_type == (PgView_Type))
 #define import_pygame_view() \
     _IMPORT_PYGAME_MODULE(_view, VIEW, PgVIEW_C_API)
 #include "pgcompat.h"
 #include "doc/pixelcopy_doc.h"
 #include "pgarrinter.h"
+#include "pgview.h"
 #include <SDL_byteorder.h>
 
 #if !defined(DOC_PYGAMEBLITARRAY)
 }
 
 static int
-_get_array_interface(PyObject *obj,
-                     PyObject **cobj_p,
-                     PyArrayInterface **inter_p)
-{
-    PyObject *cobj = PyObject_GetAttrString(obj, "__array_struct__");
-    PyArrayInterface *inter = NULL;
-
-    if (cobj == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_Clear();
-                PyErr_SetString(PyExc_ValueError,
-                                "no C-struct array interface");
-        }
-        return -1;
-    }
-
-#if PG_HAVE_COBJECT
-    if (PyCObject_Check(cobj)) {
-        inter = (PyArrayInterface *)PyCObject_AsVoidPtr(cobj);
-    }
-#endif
-#if PG_HAVE_CAPSULE
-    if (PyCapsule_IsValid(cobj, NULL)) {
-        inter = (PyArrayInterface *)PyCapsule_GetPointer(cobj, NULL);
-    }
-#endif
-    if (inter == NULL ||   /* conditional or */
-        inter->two != 2  ) {
-        Py_DECREF(cobj);
-        PyErr_SetString(PyExc_ValueError, "invalid array interface");
-        return -1;
-    }
-
-    *cobj_p = cobj;
-    *inter_p = inter;
-    return 0;
-}
-
-static int
 _copy_mapped(PyArrayInterface *inter, SDL_Surface *surf)
 {
     int pixelsize = surf->format->BytesPerPixel;
     surf = PySurface_AsSurface(surfobj);
     format = surf->format;
     
-    if (_get_array_interface(arrayobj, &cobj, &inter)) {
+    if (Pg_GetArrayInterface(arrayobj, &cobj, &inter)) {
         return 0;
     }
 
     }
     surf = PySurface_AsSurface(surfobj);
     
-    if (_get_array_interface(arrayobj, &cobj, &inter)) {
+    if (Pg_GetArrayInterface(arrayobj, &cobj, &inter)) {
         PySurface_Unlock(surfobj);
         return 0;
     }
 
     /* Determine array shapes and check validity
      */
-    if (_get_array_interface(tar_array, &tar_cobj, &tar_inter)) {
+    if (Pg_GetArrayInterface(tar_array, &tar_cobj, &tar_inter)) {
         goto fail;
     }
     if (!(tar_inter->flags & PAI_WRITEABLE)) {
                      (int)PIXELCOPY_MAX_DIM);
         goto fail;
     }
-    if (_get_array_interface(src_array, &src_cobj, &src_inter)) {
+    if (Pg_GetArrayInterface(src_array, &src_cobj, &src_inter)) {
         goto fail;
     }
     if (src_inter->typekind != 'u' && src_inter->typekind != 'i') {
     int sizex, sizey, bitsperpixel;
     Uint32 rmask, gmask, bmask;
 
-    if (_get_array_interface(arg, &capsule, &inter)) {
+    if (Pg_GetArrayInterface(arg, &capsule, &inter)) {
         return 0;
     }
     
     if (PyErr_Occurred()) {
         MODINIT_ERROR;
     }
+    import_pygame_view();
+    if (PyErr_Occurred()) {
+        MODINIT_ERROR;
+    }
 
 #if PY3
     module = PyModule_Create(&_module);

test/_view_test.py

         self.assertEqual(r[:2], '*<')
         self.assertEqual(r[-2:], '>*')
 
+
+class GetArrayInterfaceTest(unittest.TestCase):
+
+    def test_get_array_interface(self):
+        surf = pygame.Surface((7, 11), 0, 32)
+        d = pygame._view.get_array_interface(surf.get_view("2"))
+        self.assertEqual(len(d), 5)
+        self.assertEqual(d['version'], 3)
+        if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN:
+            byteorder = '<'
+        else:
+            byteorder = '>'
+        self.assertEqual(d['typestr'], byteorder + 'u4')
+        self.assertEqual(d['shape'], (7, 11))
+        self.assertEqual(d['strides'], (4, 28))
+        self.assertEqual(d['data'], (surf._pixels_address, False))
+
 if __name__ == '__main__':
     unittest.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.