]> granicus.if.org Git - python/commitdiff
Backport from py3k: Implement the new buffer interface from pep3118
authorThomas Heller <theller@ctypes.org>
Thu, 5 Jun 2008 17:51:15 +0000 (17:51 +0000)
committerThomas Heller <theller@ctypes.org>
Thu, 5 Jun 2008 17:51:15 +0000 (17:51 +0000)
for ctypes instances.  Closes issue #2404.

Lib/ctypes/test/test_pep3118.py [new file with mode: 0644]
Modules/_ctypes/_ctypes.c
Modules/_ctypes/callproc.c
Modules/_ctypes/ctypes.h
Modules/_ctypes/stgdict.c

diff --git a/Lib/ctypes/test/test_pep3118.py b/Lib/ctypes/test/test_pep3118.py
new file mode 100644 (file)
index 0000000..cf628d8
--- /dev/null
@@ -0,0 +1,191 @@
+import unittest
+from ctypes import *
+import re, struct, sys
+
+if sys.byteorder == "little":
+    THIS_ENDIAN = "<"
+    OTHER_ENDIAN = ">"
+else:
+    THIS_ENDIAN = ">"
+    OTHER_ENDIAN = "<"
+
+class memoryview(object):
+    # This class creates a memoryview - like object from data returned
+    # by the private _ctypes._buffer_info() function, just enough for
+    # these tests.
+    #
+    # It can be removed when the py3k memoryview object is backported.
+    def __init__(self, ob):
+        from _ctypes import _buffer_info
+        self.format, self.ndim, self.shape = _buffer_info(ob)
+        if self.shape == ():
+            self.shape = None
+            self.itemsize = sizeof(ob)
+        else:
+            size = sizeof(ob)
+            for dim in self.shape:
+                size /= dim
+            self.itemsize = size
+        self.strides = None
+        self.readonly = False
+        self.size = sizeof(ob)
+
+def normalize(format):
+    # Remove current endian specifier and white space from a format
+    # string
+    format = format.replace(OTHER_ENDIAN, THIS_ENDIAN)
+    return re.sub(r"\s", "", format)
+
+class Test(unittest.TestCase):
+
+    def test_native_types(self):
+        for tp, fmt, shape, itemtp in native_types:
+            ob = tp()
+            v = memoryview(ob)
+            try:
+                self.failUnlessEqual(normalize(v.format), normalize(fmt))
+                self.failUnlessEqual(v.size, sizeof(ob))
+                self.failUnlessEqual(v.itemsize, sizeof(itemtp))
+                self.failUnlessEqual(v.shape, shape)
+                # ctypes object always have a non-strided memory block
+                self.failUnlessEqual(v.strides, None)
+                # they are always read/write
+                self.failIf(v.readonly)
+
+                if v.shape:
+                    n = 1
+                    for dim in v.shape:
+                        n = n * dim
+                    self.failUnlessEqual(v.itemsize * n, v.size)
+            except:
+                # so that we can see the failing type
+                print(tp)
+                raise
+
+    def test_endian_types(self):
+        for tp, fmt, shape, itemtp in endian_types:
+            ob = tp()
+            v = memoryview(ob)
+            try:
+                self.failUnlessEqual(v.format, fmt)
+                self.failUnlessEqual(v.size, sizeof(ob))
+                self.failUnlessEqual(v.itemsize, sizeof(itemtp))
+                self.failUnlessEqual(v.shape, shape)
+                # ctypes object always have a non-strided memory block
+                self.failUnlessEqual(v.strides, None)
+                # they are always read/write
+                self.failIf(v.readonly)
+
+                if v.shape:
+                    n = 1
+                    for dim in v.shape:
+                        n = n * dim
+                    self.failUnlessEqual(v.itemsize * n, v.size)
+            except:
+                # so that we can see the failing type
+                print(tp)
+                raise
+
+# define some structure classes
+
+class Point(Structure):
+    _fields_ = [("x", c_long), ("y", c_long)]
+
+class PackedPoint(Structure):
+    _pack_ = 2
+    _fields_ = [("x", c_long), ("y", c_long)]
+
+class Point2(Structure):
+    pass
+Point2._fields_ = [("x", c_long), ("y", c_long)]
+
+class EmptyStruct(Structure):
+    _fields_ = []
+
+class aUnion(Union):
+    _fields_ = [("a", c_int)]
+
+################################################################
+#
+# This table contains format strings as they look on little endian
+# machines.  The test replaces '<' with '>' on big endian machines.
+#
+native_types = [
+    # type                      format                  shape           calc itemsize
+
+    ## simple types
+
+    (c_char,                    "<c",                   None,           c_char),
+    (c_byte,                    "<b",                   None,           c_byte),
+    (c_ubyte,                   "<B",                   None,           c_ubyte),
+    (c_short,                   "<h",                   None,           c_short),
+    (c_ushort,                  "<H",                   None,           c_ushort),
+
+    # c_int and c_uint may be aliases to c_long
+    #(c_int,                     "<i",                   None,           c_int),
+    #(c_uint,                    "<I",                   None,           c_uint),
+
+    (c_long,                    "<l",                   None,           c_long),
+    (c_ulong,                   "<L",                   None,           c_ulong),
+
+    # c_longlong and c_ulonglong are aliases on 64-bit platforms
+    #(c_longlong,                "<q",                   None,           c_longlong),
+    #(c_ulonglong,               "<Q",                   None,           c_ulonglong),
+
+    (c_float,                   "<f",                   None,           c_float),
+    (c_double,                  "<d",                   None,           c_double),
+    # c_longdouble may be an alias to c_double
+
+    (c_bool,                    "<?",                   None,           c_bool),
+    (py_object,                 "<O",                   None,           py_object),
+
+    ## pointers
+
+    (POINTER(c_byte),           "&<b",                  None,           POINTER(c_byte)),
+    (POINTER(POINTER(c_long)),  "&&<l",                 None,           POINTER(POINTER(c_long))),
+
+    ## arrays and pointers
+
+    (c_double * 4,              "(4)<d",                (4,),           c_double),
+    (c_float * 4 * 3 * 2,       "(2,3,4)<f",            (2,3,4),        c_float),
+    (POINTER(c_short) * 2,      "(2)&<h",               (2,),           POINTER(c_short)),
+    (POINTER(c_short) * 2 * 3,  "(3,2)&<h",             (3,2,),         POINTER(c_short)),
+    (POINTER(c_short * 2),      "&(2)<h",               None,           POINTER(c_short)),
+
+    ## structures and unions
+
+    (Point,                     "T{<l:x:<l:y:}",        None,           Point),
+    # packed structures do not implement the pep
+    (PackedPoint,               "B",                    None,           PackedPoint),
+    (Point2,                    "T{<l:x:<l:y:}",        None,           Point2),
+    (EmptyStruct,               "T{}",                  None,           EmptyStruct),
+    # the pep does't support unions
+    (aUnion,                    "B",                    None,           aUnion),
+
+    ## other
+
+    # function signatures are not implemented
+    (CFUNCTYPE(None),           "X{}",                  None,           CFUNCTYPE(None)),
+
+    ]
+
+class BEPoint(BigEndianStructure):
+    _fields_ = [("x", c_long), ("y", c_long)]
+
+class LEPoint(LittleEndianStructure):
+    _fields_ = [("x", c_long), ("y", c_long)]
+
+################################################################
+#
+# This table contains format strings as they really look, on both big
+# and little endian machines.
+#
+endian_types = [
+    (BEPoint,                   "T{>l:x:>l:y:}",        None,           BEPoint),
+    (LEPoint,                   "T{<l:x:<l:y:}",        None,           LEPoint),
+    (POINTER(BEPoint),          "&T{>l:x:>l:y:}",       None,           POINTER(BEPoint)),
+    (POINTER(LEPoint),          "&T{<l:x:<l:y:}",       None,           POINTER(LEPoint)),
+    ]
+
+if __name__ == "__main__":
+    unittest.main()
index 740b7f6e15f1865a3012a84da3bf0eb9f83b2c39..30e981a7f942b3fb91d038d9e3e913901cd00136 100644 (file)
@@ -293,6 +293,36 @@ PyDict_GetItemProxy(PyObject *dict, PyObject *key)
 }
 
 /******************************************************************/
+/*
+  Allocate a memory block for a pep3118 format string, copy prefix (if
+  non-null) and suffix into it.  Returns NULL on failure, with the error
+  indicator set.  If called with a suffix of NULL the error indicator must
+  already be set.
+ */
+char *
+alloc_format_string(const char *prefix, const char *suffix)
+{
+       size_t len;
+       char *result;
+
+       if (suffix == NULL) {
+               assert(PyErr_Occurred());
+               return NULL;
+       }
+       len = strlen(suffix);
+       if (prefix)
+               len += strlen(prefix);
+       result = PyMem_Malloc(len + 1);
+       if (result == NULL)
+               return NULL;
+       if (prefix)
+               strcpy(result, prefix);
+       else
+               result[0] = '\0';
+       strcat(result, suffix);
+       return result;
+}
+
 /*
   StructType_Type - a meta type/class.  Creating a new class using this one as
   __metaclass__ will call the contructor StructUnionType_new.  It replaces the
@@ -874,6 +904,16 @@ PointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
        }
 
+       if (proto) {
+               StgDictObject *itemdict = PyType_stgdict(proto);
+               assert(itemdict);
+               stgdict->format = alloc_format_string("&", itemdict->format);
+               if (stgdict->format == NULL) {
+                       Py_DECREF((PyObject *)stgdict);
+                       return NULL;
+               }
+       }
+
        /* create the new instance (which is a class,
           since we are a metatype!) */
        result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
@@ -1244,9 +1284,10 @@ ArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
        StgDictObject *itemdict;
        PyObject *proto;
        PyObject *typedict;
-       int length;
+       long length;
 
        Py_ssize_t itemsize, itemalign;
+       char buf[32];
 
        typedict = PyTuple_GetItem(args, 2);
        if (!typedict)
@@ -1281,6 +1322,28 @@ ArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
        }
 
+       assert(itemdict->format);
+       if (itemdict->format[0] == '(') {
+               sprintf(buf, "(%ld,", length);
+               stgdict->format = alloc_format_string(buf, itemdict->format+1);
+       } else {
+               sprintf(buf, "(%ld)", length);
+               stgdict->format = alloc_format_string(buf, itemdict->format);
+       }
+       if (stgdict->format == NULL) {
+               Py_DECREF((PyObject *)stgdict);
+               return NULL;
+       }
+       stgdict->ndim = itemdict->ndim + 1;
+       stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t *) * stgdict->ndim);
+       if (stgdict->shape == NULL) {
+               Py_DECREF((PyObject *)stgdict);
+               return NULL;
+       }
+       stgdict->shape[0] = length;
+       memmove(&stgdict->shape[1], itemdict->shape,
+               sizeof(Py_ssize_t) * (stgdict->ndim - 1));
+
        itemsize = itemdict->size;
        if (length * itemsize < 0) {
                PyErr_SetString(PyExc_OverflowError,
@@ -1768,6 +1831,8 @@ SimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
        PyTypeObject *result;
        StgDictObject *stgdict;
        PyObject *proto;
+       const char *proto_str;
+       Py_ssize_t proto_len;
        PyMethodDef *ml;
        struct fielddesc *fmt;
 
@@ -1778,17 +1843,34 @@ SimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
 
        proto = PyObject_GetAttrString((PyObject *)result, "_type_"); /* new ref */
-       if (!proto
-           || !PyBytes_Check(proto)
-           || 1 != strlen(PyBytes_AS_STRING(proto))
-           || !strchr(SIMPLE_TYPE_CHARS, PyBytes_AS_STRING(proto)[0])) {
+       if (!proto) {
+               PyErr_SetString(PyExc_AttributeError,
+                               "class must define a '_type_' attribute");
+  error:
+               Py_XDECREF(proto);
+               Py_XDECREF(result);
+               return NULL;
+       }
+       if (PyString_Check(proto)) {
+               proto_str = PyBytes_AS_STRING(proto);
+               proto_len = PyBytes_GET_SIZE(proto);
+       } else {
+               PyErr_SetString(PyExc_TypeError,
+                       "class must define a '_type_' string attribute");
+               goto error;
+       }
+       if (proto_len != 1) {
+               PyErr_SetString(PyExc_ValueError,
+                               "class must define a '_type_' attribute "
+                               "which must be a string of length 1");
+               goto error;
+       }
+       if (!strchr(SIMPLE_TYPE_CHARS, *proto_str)) {
                PyErr_Format(PyExc_AttributeError,
                             "class must define a '_type_' attribute which must be\n"
                             "a single character string containing one of '%s'.",
                             SIMPLE_TYPE_CHARS);
-               Py_XDECREF(proto);
-               Py_DECREF(result);
-               return NULL;
+               goto error;
        }
        fmt = getentry(PyBytes_AS_STRING(proto));
        if (fmt == NULL) {
@@ -1810,6 +1892,16 @@ SimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
        stgdict->size = fmt->pffi_type->size;
        stgdict->setfunc = fmt->setfunc;
        stgdict->getfunc = fmt->getfunc;
+#ifdef WORDS_BIGENDIAN
+       stgdict->format = alloc_format_string(">", proto_str);
+#else
+       stgdict->format = alloc_format_string("<", proto_str);
+#endif
+       if (stgdict->format == NULL) {
+               Py_DECREF(result);
+               Py_DECREF((PyObject *)stgdict);
+               return NULL;
+       }
 
        stgdict->paramfunc = SimpleType_paramfunc;
 /*
@@ -1895,22 +1987,32 @@ SimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
        if (type == &SimpleType_Type && fmt->setfunc_swapped && fmt->getfunc_swapped) {
                PyObject *swapped = CreateSwappedType(type, args, kwds,
                                                      proto, fmt);
+               StgDictObject *sw_dict;
                if (swapped == NULL) {
                        Py_DECREF(result);
                        return NULL;
                }
+               sw_dict = PyType_stgdict(swapped);
 #ifdef WORDS_BIGENDIAN
                PyObject_SetAttrString((PyObject *)result, "__ctype_le__", swapped);
                PyObject_SetAttrString((PyObject *)result, "__ctype_be__", (PyObject *)result);
                PyObject_SetAttrString(swapped, "__ctype_be__", (PyObject *)result);
                PyObject_SetAttrString(swapped, "__ctype_le__", swapped);
+               /* We are creating the type for the OTHER endian */
+               sw_dict->format = alloc_format_string("<", stgdict->format+1);
 #else
                PyObject_SetAttrString((PyObject *)result, "__ctype_be__", swapped);
                PyObject_SetAttrString((PyObject *)result, "__ctype_le__", (PyObject *)result);
                PyObject_SetAttrString(swapped, "__ctype_le__", (PyObject *)result);
                PyObject_SetAttrString(swapped, "__ctype_be__", swapped);
+               /* We are creating the type for the OTHER endian */
+               sw_dict->format = alloc_format_string(">", stgdict->format+1);
 #endif
                Py_DECREF(swapped);
+               if (PyErr_Occurred()) {
+                       Py_DECREF(result);
+                       return NULL;
+               }
        };
 
        return (PyObject *)result;
@@ -2166,6 +2268,13 @@ CFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
 
        stgdict->paramfunc = CFuncPtrType_paramfunc;
+       /* We do NOT expose the function signature in the format string.  It
+          is impossible, generally, because the only requirement for the
+          argtypes items is that they have a .from_param method - we do not
+          know the types of the arguments (although, in practice, most
+          argtypes would be a ctypes type).
+       */
+       stgdict->format = alloc_format_string(NULL, "X{}");
        stgdict->flags |= TYPEFLAG_ISPOINTER;
 
        /* create the new instance (which is a class,
@@ -2386,15 +2495,34 @@ static PyMemberDef CData_members[] = {
        { NULL },
 };
 
-static Py_ssize_t CData_GetBuffer(PyObject *_self, Py_ssize_t seg, void **pptr)
+static int CData_NewGetBuffer(PyObject *_self, Py_buffer *view, int flags)
 {
        CDataObject *self = (CDataObject *)_self;
-       if (seg != 0) {
-               /* Hm. Must this set an exception? */
+       StgDictObject *dict = PyObject_stgdict(_self);
+       Py_ssize_t i;
+
+       if (view == NULL) return 0;
+       if (((flags & PyBUF_LOCK) == PyBUF_LOCK)) {
+               PyErr_SetString(PyExc_BufferError,
+                               "Cannot lock this object.");
                return -1;
        }
-       *pptr = self->b_ptr;
-       return self->b_size;
+
+       view->buf = self->b_ptr;
+       view->len = self->b_size;
+       view->readonly = 0;
+       /* use default format character if not set */
+       view->format = dict->format ? dict->format : "B";
+       view->ndim = dict->ndim;
+       view->shape = dict->shape;
+       view->itemsize = self->b_size;
+       for (i = 0; i < view->ndim; ++i) {
+               view->itemsize /= dict->shape[i];
+       }
+       view->strides = NULL;
+       view->suboffsets = NULL;
+       view->internal = NULL;
+       return 0;
 }
 
 static Py_ssize_t CData_GetSegcount(PyObject *_self, Py_ssize_t *lenp)
@@ -2404,11 +2532,24 @@ static Py_ssize_t CData_GetSegcount(PyObject *_self, Py_ssize_t *lenp)
        return 1;
 }
 
+static Py_ssize_t CData_GetBuffer(PyObject *_self, Py_ssize_t seg, void **pptr)
+{
+       CDataObject *self = (CDataObject *)_self;
+       if (seg != 0) {
+               /* Hm. Must this set an exception? */
+               return -1;
+       }
+       *pptr = self->b_ptr;
+       return self->b_size;
+}
+
 static PyBufferProcs CData_as_buffer = {
-       CData_GetBuffer,
-       CData_GetBuffer,
-       CData_GetSegcount,
-       NULL,
+       (readbufferproc)CData_GetBuffer,
+       (writebufferproc)CData_GetBuffer,
+       (segcountproc)CData_GetSegcount,
+       (charbufferproc)NULL,
+       (getbufferproc)CData_NewGetBuffer,
+       (releasebufferproc)NULL,
 };
 
 /*
@@ -2497,7 +2638,7 @@ PyTypeObject CData_Type = {
        0,                                      /* tp_getattro */
        0,                                      /* tp_setattro */
        &CData_as_buffer,                       /* tp_as_buffer */
-       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_BASETYPE, /* tp_flags */
        "XXX to be provided",                   /* tp_doc */
        (traverseproc)CData_traverse,           /* tp_traverse */
        (inquiry)CData_clear,                   /* tp_clear */
@@ -3824,7 +3965,7 @@ PyTypeObject CFuncPtr_Type = {
        0,                                      /* tp_getattro */
        0,                                      /* tp_setattro */
        &CData_as_buffer,                       /* tp_as_buffer */
-       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_BASETYPE, /* tp_flags */
        "Function Pointer",                     /* tp_doc */
        (traverseproc)CFuncPtr_traverse,        /* tp_traverse */
        (inquiry)CFuncPtr_clear,                /* tp_clear */
@@ -3967,7 +4108,7 @@ static PyTypeObject Struct_Type = {
        0,                                      /* tp_getattro */
        0,                                      /* tp_setattro */
        &CData_as_buffer,                       /* tp_as_buffer */
-       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_BASETYPE, /* tp_flags */
        "Structure base class",                 /* tp_doc */
        (traverseproc)CData_traverse,           /* tp_traverse */
        (inquiry)CData_clear,                   /* tp_clear */
@@ -4009,7 +4150,7 @@ static PyTypeObject Union_Type = {
        0,                                      /* tp_getattro */
        0,                                      /* tp_setattro */
        &CData_as_buffer,                       /* tp_as_buffer */
-       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_BASETYPE, /* tp_flags */
        "Union base class",                     /* tp_doc */
        (traverseproc)CData_traverse,           /* tp_traverse */
        (inquiry)CData_clear,                   /* tp_clear */
@@ -4406,7 +4547,7 @@ PyTypeObject Array_Type = {
        0,                                      /* tp_getattro */
        0,                                      /* tp_setattro */
        &CData_as_buffer,                       /* tp_as_buffer */
-       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_BASETYPE, /* tp_flags */
        "XXX to be provided",                   /* tp_doc */
        (traverseproc)CData_traverse,           /* tp_traverse */
        (inquiry)CData_clear,                   /* tp_clear */
@@ -4643,7 +4784,7 @@ static PyTypeObject Simple_Type = {
        0,                                      /* tp_getattro */
        0,                                      /* tp_setattro */
        &CData_as_buffer,                       /* tp_as_buffer */
-       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_BASETYPE, /* tp_flags */
        "XXX to be provided",                   /* tp_doc */
        (traverseproc)CData_traverse,           /* tp_traverse */
        (inquiry)CData_clear,                   /* tp_clear */
@@ -5043,7 +5184,7 @@ PyTypeObject Pointer_Type = {
        0,                                      /* tp_getattro */
        0,                                      /* tp_setattro */
        &CData_as_buffer,                       /* tp_as_buffer */
-       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_BASETYPE, /* tp_flags */
        "XXX to be provided",                   /* tp_doc */
        (traverseproc)CData_traverse,           /* tp_traverse */
        (inquiry)CData_clear,                   /* tp_clear */
index 95b2709e7982c2bba9c9abc4c0a535c4c2b54c4e..052017652f9ff2318205989b2c790e5a636b08d1 100644 (file)
@@ -1666,10 +1666,37 @@ pointer(PyObject *self, PyObject *arg)
        return result;
 }
 
+static PyObject *
+buffer_info(PyObject *self, PyObject *arg)
+{
+       StgDictObject *dict = PyType_stgdict(arg);
+       PyObject *shape;
+       Py_ssize_t i;
+
+       if (dict == NULL)
+               dict = PyObject_stgdict(arg);
+       if (dict == NULL) {
+               PyErr_SetString(PyExc_TypeError,
+                               "not a ctypes type or object");
+               return NULL;
+       }
+       shape = PyTuple_New(dict->ndim);
+       for (i = 0; i < (int)dict->ndim; ++i)
+               PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(dict->shape[i]));
+
+       if (PyErr_Occurred()) {
+               Py_DECREF(shape);
+               return NULL;
+       }
+       return Py_BuildValue("siN", dict->format, dict->ndim, shape);
+}
+
 PyMethodDef module_methods[] = {
        {"POINTER", POINTER, METH_O },
        {"pointer", pointer, METH_O },
        {"_unpickle", unpickle, METH_VARARGS },
+       {"_buffer_info", buffer_info, METH_O,
+        "Return buffer interface information (for testing only)"},
        {"resize", resize, METH_VARARGS, "Resize the memory buffer of a ctypes instance"},
 #ifdef CTYPES_UNICODE
        {"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc},
index 1a104cfb2c2fceb17949c2fd991ca5ccb6022608..d068ea5eecdecb67bfac4235b9b8a077611e8487 100644 (file)
@@ -235,6 +235,14 @@ typedef struct {
        PyObject *restype;      /* CDataObject or NULL */
        PyObject *checker;
        int flags;              /* calling convention and such */
+
+       /* pep3118 fields, pointers neeed PyMem_Free */
+       char *format;
+       int ndim;
+       Py_ssize_t *shape;
+/*     Py_ssize_t *strides;    */ /* unused in ctypes */
+/*     Py_ssize_t *suboffsets; */ /* unused in ctypes */
+
 } StgDictObject;
 
 /****************************************************************
@@ -415,6 +423,7 @@ extern void *MallocClosure(void);
 extern void _AddTraceback(char *, char *, int);
 
 extern PyObject *CData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr);
+extern char *alloc_format_string(const char *prefix, const char *suffix);
 
 /* XXX better name needed! */
 extern int IsSimpleSubType(PyObject *obj);
index f7f19cf5ce17a3b019cf11e922b150e77af4a636..cd38ecdbe7294b24fd2dc33baaf36c17168a39d7 100644 (file)
@@ -6,6 +6,7 @@
 #include <ffi.h>
 #ifdef MS_WIN32
 #include <windows.h>
+#include <malloc.h>
 #endif
 #include "ctypes.h"
 
@@ -24,6 +25,9 @@ StgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds)
 {
        if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0)
                return -1;
+       self->format = NULL;
+       self->ndim = 0;
+       self->shape = NULL;
        return 0;
 }
 
@@ -42,6 +46,8 @@ static void
 StgDict_dealloc(StgDictObject *self)
 {
        StgDict_clear(self);
+       PyMem_Free(self->format);
+       PyMem_Free(self->shape);
        PyMem_Free(self->ffi_type_pointer.elements);
        PyDict_Type.tp_dealloc((PyObject *)self);
 }
@@ -54,6 +60,10 @@ StgDict_clone(StgDictObject *dst, StgDictObject *src)
 
        StgDict_clear(dst);
        PyMem_Free(dst->ffi_type_pointer.elements);
+       PyMem_Free(dst->format);
+       dst->format = NULL;
+       PyMem_Free(dst->shape);
+       dst->shape = NULL;
        dst->ffi_type_pointer.elements = NULL;
 
        d = (char *)dst;
@@ -68,6 +78,20 @@ StgDict_clone(StgDictObject *dst, StgDictObject *src)
        Py_XINCREF(dst->restype);
        Py_XINCREF(dst->checker);
 
+       if (src->format) {
+               dst->format = PyMem_Malloc(strlen(src->format) + 1);
+               if (dst->format == NULL)
+                       return -1;
+               strcpy(dst->format, src->format);
+       }
+       if (src->shape) {
+               dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src->ndim);
+               if (dst->shape == NULL)
+                       return -1;
+               memcpy(dst->shape, src->shape,
+                      sizeof(Py_ssize_t) * src->ndim);
+       }
+
        if (src->ffi_type_pointer.elements == NULL)
                return 0;
        size = sizeof(ffi_type *) * (src->length + 1);
@@ -349,6 +373,11 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct)
                return -1;
        }
 
+       if (stgdict->format) {
+               PyMem_Free(stgdict->format);
+               stgdict->format = NULL;
+       }
+
        if (stgdict->ffi_type_pointer.elements)
                PyMem_Free(stgdict->ffi_type_pointer.elements);
 
@@ -387,6 +416,15 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct)
                ffi_ofs = 0;
        }
 
+       if (isStruct && !isPacked) {
+               stgdict->format = alloc_format_string(NULL, "T{");
+       } else {
+               /* PEP3118 doesn't support union, or packed structures (well,
+                  only standard packing, but we dont support the pep for
+                  that). Use 'B' for bytes. */
+               stgdict->format = alloc_format_string(NULL, "B");
+       }
+
 #define realdict ((PyObject *)&stgdict->dict)
        for (i = 0; i < len; ++i) {
                PyObject *name = NULL, *desc = NULL;
@@ -451,6 +489,24 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct)
                        }
                } else
                        bitsize = 0;
+               if (isStruct && !isPacked) {
+                       char *fieldfmt = dict->format ? dict->format : "B";
+                       char *fieldname = PyString_AsString(name);
+                       char *ptr;
+                       Py_ssize_t len = strlen(fieldname) + strlen(fieldfmt);
+                       char *buf = alloca(len + 2 + 1);
+
+                       sprintf(buf, "%s:%s:", fieldfmt, fieldname);
+
+                       ptr = stgdict->format;
+                       stgdict->format = alloc_format_string(stgdict->format, buf);
+                       PyMem_Free(ptr);
+
+                       if (stgdict->format == NULL) {
+                               Py_DECREF(pair);
+                               return -1;
+                       }
+               }
                if (isStruct) {
                        prop = CField_FromDesc(desc, i,
                                               &field_size, bitsize, &bitofs,
@@ -481,6 +537,13 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct)
                Py_DECREF(prop);
        }
 #undef realdict
+
+       if (isStruct && !isPacked) {
+               stgdict->format = alloc_format_string(stgdict->format, "}");
+               if (stgdict->format == NULL)
+                       return -1;
+       }
+
        if (!isStruct)
                size = union_size;