Commits

Armin Rigo committed 232154d

Rename: _ffi_backend => _cffi_backend

  • Participants
  • Parent commits 68247c4

Comments (0)

Files changed (7)

File c/_cffi_backend.c

+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "structmember.h"
+
+#ifdef MS_WIN32
+#include <windows.h>
+#include "misc_win32.h"
+#else
+#include <stddef.h>
+#include <stdint.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <ffi.h>
+#include <sys/mman.h>
+#endif
+
+#include "malloc_closure.h"
+
+/* Define USE__THREAD if gcc on your platform supports "__thread"
+   global variables. */
+#if !defined(MS_WIN32) && !defined(X86_DARWIN) && !defined(POWERPC_DARWIN)
+# define USE__THREAD
+#endif
+
+/************************************************************/
+
+/* base type flag: exactly one of the following: */
+#define CT_PRIMITIVE_SIGNED   1    /* signed integer */
+#define CT_PRIMITIVE_UNSIGNED 2    /* unsigned integer */
+#define CT_PRIMITIVE_CHAR     4    /* char (and, later, wchar_t) */
+#define CT_PRIMITIVE_FLOAT    8    /* float, double */
+#define CT_POINTER           16    /* pointer, excluding ptr-to-func */
+#define CT_ARRAY             32    /* array */
+#define CT_STRUCT            64    /* struct */
+#define CT_UNION            128    /* union */
+#define CT_FUNCTIONPTR      256    /* pointer to function */
+#define CT_VOID             512    /* void */
+
+/* other flags that may also be set in addition to the base flag: */
+#define CT_CAST_ANYTHING         1024    /* 'char' and 'void' only */
+#define CT_PRIMITIVE_FITS_LONG   2048
+#define CT_IS_OPAQUE             4096
+#define CT_IS_ENUM               8192
+#define CT_PRIMITIVE_ANY  (CT_PRIMITIVE_SIGNED |        \
+                           CT_PRIMITIVE_UNSIGNED |      \
+                           CT_PRIMITIVE_CHAR |          \
+                           CT_PRIMITIVE_FLOAT)
+
+typedef struct _ctypedescr {
+    PyObject_VAR_HEAD
+
+    struct _ctypedescr *ct_itemdescr;  /* ptrs and arrays: the item type */
+    PyObject *ct_stuff;                /* structs: dict of the fields
+                                          arrays: ctypedescr of the ptr type
+                                          function: tuple(ctres, ctargs...)
+                                          enum: pair {"name":x},{x:"name"} */
+    void *ct_extra;                    /* structs: first field (not a ref!)
+                                          function types: cif_description
+                                          primitives: prebuilt "cif" object */
+
+    PyObject *ct_weakreflist;    /* weakref support */
+
+    Py_ssize_t ct_size;     /* size of instances, or -1 if unknown */
+    Py_ssize_t ct_length;   /* length of arrays, or -1 if unknown;
+                               or alignment of primitive and struct types */
+    int ct_flags;           /* CT_xxx flags */
+
+    int ct_name_position;   /* index in ct_name of where to put a var name */
+    char ct_name[1];        /* string, e.g. "int *" for pointers to ints */
+} CTypeDescrObject;
+
+typedef struct {
+    PyObject_HEAD
+    CTypeDescrObject *c_type;
+    char *c_data;
+} CDataObject;
+
+typedef struct cfieldobject_s {
+    PyObject_HEAD
+    CTypeDescrObject *cf_type;
+    Py_ssize_t cf_offset;
+    short cf_bitshift, cf_bitsize;
+    struct cfieldobject_s *cf_next;
+} CFieldObject;
+
+static PyTypeObject CTypeDescr_Type;
+static PyTypeObject CField_Type;
+static PyTypeObject CData_Type;
+static PyTypeObject CDataOwning_Type;
+
+#define CTypeDescr_Check(ob)  (Py_TYPE(ob) == &CTypeDescr_Type)
+#define CData_Check(ob)       (Py_TYPE(ob) == &CData_Type ||            \
+                               Py_TYPE(ob) == &CDataOwning_Type)
+#define CDataOwn_Check(ob)    (Py_TYPE(ob) == &CDataOwning_Type)
+
+typedef union {
+    unsigned char m_char;
+    unsigned short m_short;
+    unsigned int m_int;
+    unsigned long m_long;
+    unsigned long long m_longlong;
+    float m_float;
+    double m_double;
+} union_alignment;
+
+typedef struct {
+    CDataObject head;
+    union_alignment alignment;
+} CDataObject_casted_primitive;
+
+typedef struct {
+    CDataObject head;
+    PyObject *weakreflist;
+} CDataObject_own_base;
+
+typedef struct {
+    CDataObject_own_base head;
+    union_alignment alignment;
+} CDataObject_own_nolength;
+
+typedef struct {
+    CDataObject_own_base head;
+    Py_ssize_t length;
+    union_alignment alignment;
+} CDataObject_own_length;
+
+typedef struct {
+    ffi_cif cif;
+    /* the following information is used when doing the call:
+       - a buffer of size 'exchange_size' is malloced
+       - the arguments are converted from Python objects to raw data
+       - the i'th raw data is stored at 'buffer + exchange_offset_arg[1+i]'
+       - the call is done
+       - the result is read back from 'buffer + exchange_offset_arg[0]' */
+    Py_ssize_t exchange_size;
+    Py_ssize_t exchange_offset_arg[1];
+} cif_description_t;
+
+
+/* whenever running Python code, the errno is saved in this thread-local
+   variable */
+#ifndef MS_WIN32
+# ifdef USE__THREAD
+static __thread int cffi_saved_errno = 0;
+static void save_errno(void) { cffi_saved_errno = errno; }
+static void restore_errno(void) { errno = cffi_saved_errno; }
+static void init_errno(void) { }
+# else
+#  include "misc_thread.h"
+# endif
+#endif
+
+/************************************************************/
+
+static CTypeDescrObject *
+ctypedescr_new(int name_size)
+{
+    CTypeDescrObject *ct = PyObject_GC_NewVar(CTypeDescrObject,
+                                              &CTypeDescr_Type,
+                                              name_size);
+    if (ct == NULL)
+        return NULL;
+
+    ct->ct_itemdescr = NULL;
+    ct->ct_stuff = NULL;
+    ct->ct_weakreflist = NULL;
+    PyObject_GC_Track(ct);
+    return ct;
+}
+
+static CTypeDescrObject *
+ctypedescr_new_on_top(CTypeDescrObject *ct_base, const char *extra_text,
+                      int extra_position)
+{
+    int base_name_len = strlen(ct_base->ct_name);
+    int extra_name_len = strlen(extra_text);
+    CTypeDescrObject *ct = ctypedescr_new(base_name_len + extra_name_len + 1);
+    char *p;
+    if (ct == NULL)
+        return NULL;
+
+    Py_INCREF(ct_base);
+    ct->ct_itemdescr = ct_base;
+    ct->ct_name_position = ct_base->ct_name_position + extra_position;
+
+    p = ct->ct_name;
+    memcpy(p, ct_base->ct_name, ct_base->ct_name_position);
+    p += ct_base->ct_name_position;
+    memcpy(p, extra_text, extra_name_len);
+    p += extra_name_len;
+    memcpy(p, ct_base->ct_name + ct_base->ct_name_position,
+           base_name_len - ct_base->ct_name_position + 1);
+
+    return ct;
+}
+
+static PyObject *
+ctypedescr_repr(CTypeDescrObject *ct)
+{
+    return PyString_FromFormat("<ctype '%s'>", ct->ct_name);
+}
+
+static void
+ctypedescr_dealloc(CTypeDescrObject *ct)
+{
+    PyObject_GC_UnTrack(ct);
+    if (ct->ct_weakreflist != NULL)
+        PyObject_ClearWeakRefs((PyObject *) ct);
+    Py_XDECREF(ct->ct_itemdescr);
+    Py_XDECREF(ct->ct_stuff);
+    if (ct->ct_flags & CT_FUNCTIONPTR)
+        PyObject_Free(ct->ct_extra);
+    Py_TYPE(ct)->tp_free((PyObject *)ct);
+}
+
+static int
+ctypedescr_traverse(CTypeDescrObject *ct, visitproc visit, void *arg)
+{
+    Py_VISIT(ct->ct_itemdescr);
+    Py_VISIT(ct->ct_stuff);
+    return 0;
+}
+
+static int
+ctypedescr_clear(CTypeDescrObject *ct)
+{
+    Py_CLEAR(ct->ct_itemdescr);
+    Py_CLEAR(ct->ct_stuff);
+    return 0;
+}
+
+static PyTypeObject CTypeDescr_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CTypeDescr",
+    offsetof(CTypeDescrObject, ct_name),
+    sizeof(char),
+    (destructor)ctypedescr_dealloc,             /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    (reprfunc)ctypedescr_repr,                  /* tp_repr */
+    0,                                          /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    0,                                          /* tp_as_mapping */
+    0,                                          /* tp_hash */
+    0,                                          /* tp_call */
+    0,                                          /* tp_str */
+    PyObject_GenericGetAttr,                    /* tp_getattro */
+    0,                                          /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,    /* tp_flags */
+    0,                                          /* tp_doc */
+    (traverseproc)ctypedescr_traverse,          /* tp_traverse */
+    (inquiry)ctypedescr_clear,                  /* tp_clear */
+    0,                                          /* tp_richcompare */
+    offsetof(CTypeDescrObject, ct_weakreflist), /* tp_weaklistoffset */
+};
+
+/************************************************************/
+
+static char *
+get_field_name(CTypeDescrObject *ct, CFieldObject *cf)
+{
+    Py_ssize_t i = 0;
+    PyObject *d_key, *d_value;
+    while (PyDict_Next(ct->ct_stuff, &i, &d_key, &d_value)) {
+        if (d_value == (PyObject *)cf)
+            return PyString_AsString(d_key);
+    }
+    return NULL;
+}
+
+static void
+cfield_dealloc(CFieldObject *cf)
+{
+    Py_DECREF(cf->cf_type);
+    PyObject_Del(cf);
+}
+
+static int
+cfield_traverse(CFieldObject *cf, visitproc visit, void *arg)
+{
+    Py_VISIT(cf->cf_type);
+    return 0;
+}
+
+#undef OFF
+#define OFF(x) offsetof(CFieldObject, x)
+
+static PyMemberDef cfield_members[] = {
+    {"type", T_OBJECT, OFF(cf_type), RO},
+    {"offset", T_PYSSIZET, OFF(cf_offset), RO},
+    {"bitshift", T_SHORT, OFF(cf_bitshift), RO},
+    {"bitsize", T_SHORT, OFF(cf_bitsize), RO},
+    {NULL}      /* Sentinel */
+};
+#undef OFF
+
+static PyTypeObject CField_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CField",
+    sizeof(CFieldObject),
+    0,
+    (destructor)cfield_dealloc,                 /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    0,                                          /* tp_repr */
+    0,                                          /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    0,                                          /* tp_as_mapping */
+    0,                                          /* tp_hash */
+    0,                                          /* tp_call */
+    0,                                          /* tp_str */
+    PyObject_GenericGetAttr,                    /* tp_getattro */
+    0,                                          /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
+    0,                                          /* tp_doc */
+    (traverseproc)cfield_traverse,              /* tp_traverse */
+    0,                                          /* tp_clear */
+    0,                                          /* tp_richcompare */
+    0,                                          /* tp_weaklistoffset */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    0,                                          /* tp_methods */
+    cfield_members,                             /* tp_members */
+};
+
+/************************************************************/
+
+static PY_LONG_LONG
+_my_PyLong_AsLongLong(PyObject *ob)
+{
+    /* (possibly) convert and cast a Python object to a long long.
+       Like PyLong_AsLongLong(), this version accepts a Python int too, and
+       does convertions from other types of objects.  The difference is that
+       this version refuses floats. */
+    if (PyInt_Check(ob)) {
+        return PyInt_AS_LONG(ob);
+    }
+    else if (PyLong_Check(ob)) {
+        return PyLong_AsLongLong(ob);
+    }
+    else {
+        PyObject *io;
+        PY_LONG_LONG res;
+        PyNumberMethods *nb = ob->ob_type->tp_as_number;
+
+        if (PyFloat_Check(ob) ||
+                nb == NULL || nb->nb_int == NULL) {
+            PyErr_SetString(PyExc_TypeError, "an integer is required");
+            return -1;
+        }
+        io = (*nb->nb_int) (ob);
+        if (io == NULL)
+            return -1;
+
+        if (PyInt_Check(io) || PyLong_Check(io)) {
+            res = _my_PyLong_AsLongLong(io);
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "integer conversion failed");
+            res = -1;
+        }
+        Py_DECREF(io);
+        return res;
+    }
+}
+
+static unsigned PY_LONG_LONG
+_my_PyLong_AsUnsignedLongLong(PyObject *ob, int strict)
+{
+    /* (possibly) convert and cast a Python object to an unsigned long long.
+       Like PyLong_AsLongLong(), this version accepts a Python int too, and
+       does convertions from other types of objects.  If 'strict', complains
+       with OverflowError and refuses floats.  If '!strict', rounds floats
+       and masks the result. */
+    if (PyInt_Check(ob)) {
+        long value1 = PyInt_AS_LONG(ob);
+        if (strict && value1 < 0)
+            goto negative;
+        return (unsigned PY_LONG_LONG)(PY_LONG_LONG)value1;
+    }
+    else if (PyLong_Check(ob)) {
+        if (strict) {
+            if (_PyLong_Sign(ob) < 0)
+                goto negative;
+            return PyLong_AsUnsignedLongLong(ob);
+        }
+        else {
+            return PyLong_AsUnsignedLongLongMask(ob);
+        }
+    }
+    else {
+        PyObject *io;
+        unsigned PY_LONG_LONG res;
+        PyNumberMethods *nb = ob->ob_type->tp_as_number;
+
+        if ((strict && PyFloat_Check(ob)) ||
+                nb == NULL || nb->nb_int == NULL) {
+            PyErr_SetString(PyExc_TypeError, "an integer is required");
+            return (unsigned PY_LONG_LONG)-1;
+        }
+        io = (*nb->nb_int) (ob);
+        if (io == NULL)
+            return (unsigned PY_LONG_LONG)-1;
+
+        if (PyInt_Check(io) || PyLong_Check(io)) {
+            res = _my_PyLong_AsUnsignedLongLong(io, strict);
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "integer conversion failed");
+            res = (unsigned PY_LONG_LONG)-1;
+        }
+        Py_DECREF(io);
+        return res;
+    }
+
+ negative:
+    PyErr_SetString(PyExc_OverflowError,
+                    "can't convert negative number to unsigned");
+    return (unsigned PY_LONG_LONG)-1;
+}
+
+static PY_LONG_LONG
+read_raw_signed_data(char *target, int size)
+{
+    if (size == sizeof(signed char))
+        return *((signed char*)target);
+    else if (size == sizeof(short))
+        return *((short*)target);
+    else if (size == sizeof(int))
+        return *((int*)target);
+    else if (size == sizeof(long))
+        return *((long*)target);
+    else if (size == sizeof(PY_LONG_LONG))
+        return *((PY_LONG_LONG*)target);
+    else {
+        Py_FatalError("read_raw_signed_data: bad integer size");
+        return 0;
+    }
+}
+
+static unsigned PY_LONG_LONG
+read_raw_unsigned_data(char *target, int size)
+{
+    if (size == sizeof(unsigned char))
+        return *((unsigned char*)target);
+    else if (size == sizeof(unsigned short))
+        return *((unsigned short*)target);
+    else if (size == sizeof(unsigned int))
+        return *((unsigned int*)target);
+    else if (size == sizeof(unsigned long))
+        return *((unsigned long*)target);
+    else if (size == sizeof(unsigned PY_LONG_LONG))
+        return *((unsigned PY_LONG_LONG*)target);
+    else {
+        Py_FatalError("read_raw_unsigned_data: bad integer size");
+        return 0;
+    }
+}
+
+static void
+write_raw_integer_data(char *target, unsigned PY_LONG_LONG source, int size)
+{
+    if (size == sizeof(unsigned char))
+        *((unsigned char*)target) = (unsigned char)source;
+    else if (size == sizeof(unsigned short))
+        *((unsigned short*)target) = (unsigned short)source;
+    else if (size == sizeof(unsigned int))
+        *((unsigned int*)target) = (unsigned int)source;
+    else if (size == sizeof(unsigned long))
+        *((unsigned long*)target) = (unsigned long)source;
+    else if (size == sizeof(unsigned PY_LONG_LONG))
+        *((unsigned PY_LONG_LONG*)target) = source;
+    else
+        Py_FatalError("write_raw_integer_data: bad integer size");
+}
+
+static double
+read_raw_float_data(char *target, int size)
+{
+    if (size == sizeof(float))
+        return *((float*)target);
+    else if (size == sizeof(double))
+        return *((double*)target);
+    else {
+        Py_FatalError("read_raw_float_data: bad float size");
+        return 0;
+    }
+}
+
+static void
+write_raw_float_data(char *target, double source, int size)
+{
+    if (size == sizeof(float))
+        *((float*)target) = (float)source;
+    else if (size == sizeof(double))
+        *((double*)target) = source;
+    else
+        Py_FatalError("write_raw_float_data: bad float size");
+}
+
+static PyObject *
+new_simple_cdata(char *data, CTypeDescrObject *ct)
+{
+    CDataObject *cd = PyObject_New(CDataObject, &CData_Type);
+    if (cd == NULL)
+        return NULL;
+    Py_INCREF(ct);
+    cd->c_data = data;
+    cd->c_type = ct;
+    return (PyObject *)cd;
+}
+
+static PyObject *convert_enum_string_to_int(CTypeDescrObject *ct, PyObject *ob)
+{
+    PyObject *d_value;
+
+    if (PyString_AS_STRING(ob)[0] == '#') {
+        char *number = PyString_AS_STRING(ob) + 1;   /* strip initial '#' */
+        PyObject *ob2 = PyString_FromString(number);
+        if (ob2 == NULL)
+            return NULL;
+
+        d_value = PyNumber_Long(ob2);
+        Py_DECREF(ob2);
+    }
+    else {
+        d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 0), ob);
+        if (d_value == NULL) {
+            PyErr_Format(PyExc_ValueError,
+                         "'%s' is not an enumerator for %s",
+                         PyString_AS_STRING(ob),
+                         ct->ct_name);
+            return NULL;
+        }
+        Py_INCREF(d_value);
+    }
+    return d_value;
+}
+
+static PyObject *
+convert_to_object(char *data, CTypeDescrObject *ct)
+{
+    if (!(ct->ct_flags & CT_PRIMITIVE_ANY)) {
+        /* non-primitive types (check done just for performance) */
+        if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) {
+            char *ptrdata = *(char **)data;
+            return new_simple_cdata(ptrdata, ct);
+        }
+        else if (ct->ct_flags & CT_IS_OPAQUE) {
+            PyErr_Format(PyExc_TypeError, "cannot return a cdata '%s'",
+                         ct->ct_name);
+            return NULL;
+        }
+        else if (ct->ct_flags & (CT_ARRAY|CT_STRUCT|CT_UNION)) {
+            return new_simple_cdata(data, ct);
+        }
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_SIGNED) {
+        PY_LONG_LONG value = read_raw_signed_data(data, ct->ct_size);
+
+        if (ct->ct_flags & CT_IS_ENUM) {
+            PyObject *d_value, *d_key = PyInt_FromLong((int)value);
+            if (d_key == NULL)
+                return NULL;
+
+            d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key);
+            Py_DECREF(d_key);
+            if (d_value != NULL)
+                Py_INCREF(d_value);
+            else
+                d_value = PyString_FromFormat("#%d", (int)value);
+            return d_value;
+        }
+        else if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG)
+            return PyInt_FromLong((long)value);
+        else
+            return PyLong_FromLongLong(value);
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_UNSIGNED) {
+        unsigned PY_LONG_LONG value =read_raw_unsigned_data(data, ct->ct_size);
+
+        if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG)
+            return PyInt_FromLong((long)value);
+        else
+            return PyLong_FromUnsignedLongLong(value);
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
+        double value = read_raw_float_data(data, ct->ct_size);
+        return PyFloat_FromDouble(value);
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
+        return PyString_FromStringAndSize(data, 1);
+    }
+
+    PyErr_Format(PyExc_SystemError,
+                 "convert_to_object: '%s'", ct->ct_name);
+    return NULL;
+}
+
+static PyObject *
+convert_to_object_bitfield(char *data, CFieldObject *cf)
+{
+    CTypeDescrObject *ct = cf->cf_type;
+
+    if (ct->ct_flags & CT_PRIMITIVE_SIGNED) {
+        unsigned PY_LONG_LONG value, valuemask, shiftforsign;
+        PY_LONG_LONG result;
+
+        value = (unsigned PY_LONG_LONG)read_raw_signed_data(data, ct->ct_size);
+        valuemask = (1ULL << cf->cf_bitsize) - 1ULL;
+        shiftforsign = 1ULL << (cf->cf_bitsize - 1);
+        value = ((value >> cf->cf_bitshift) + shiftforsign) & valuemask;
+        result = ((PY_LONG_LONG)value) - (PY_LONG_LONG)shiftforsign;
+
+        if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG)
+            return PyInt_FromLong((long)result);
+        else
+            return PyLong_FromLongLong(result);
+    }
+    else {
+        unsigned PY_LONG_LONG value, valuemask;
+
+        value = read_raw_unsigned_data(data, ct->ct_size);
+        valuemask = (1ULL << cf->cf_bitsize) - 1ULL;
+        value = (value >> cf->cf_bitshift) & valuemask;
+
+        if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG)
+            return PyInt_FromLong((long)value);
+        else
+            return PyLong_FromUnsignedLongLong(value);
+    }
+}
+
+static int _convert_overflow(PyObject *init, const char *ct_name)
+{
+    PyObject *s;
+    if (PyErr_Occurred())   /* already an exception pending */
+        return -1;
+    s = PyObject_Str(init);
+    if (s == NULL)
+        return -1;
+    PyErr_Format(PyExc_OverflowError, "integer %s does not fit '%s'",
+                 PyString_AS_STRING(s), ct_name);
+    Py_DECREF(s);
+    return -1;
+}
+
+static int _convert_to_char(PyObject *init)
+{
+    if (PyString_Check(init) && PyString_GET_SIZE(init) == 1) {
+        return (unsigned char)(PyString_AS_STRING(init)[0]);
+    }
+    if (CData_Check(init) &&
+           (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR)) {
+        return (unsigned char)(((CDataObject *)init)->c_data[0]);
+    }
+    PyErr_Format(PyExc_TypeError,
+                 "initializer for ctype 'char' must be a string of length 1, "
+                 "not %.200s", Py_TYPE(init)->tp_name);
+    return -1;
+}
+
+static int _convert_error(PyObject *init, const char *ct_name,
+                          const char *expected)
+{
+    if (CData_Check(init))
+        PyErr_Format(PyExc_TypeError,
+                     "initializer for ctype '%s' must be a %s, "
+                     "not cdata '%s'",
+                     ct_name, expected,
+                     ((CDataObject *)init)->c_type->ct_name);
+    else
+        PyErr_Format(PyExc_TypeError,
+                     "initializer for ctype '%s' must be a %s, "
+                     "not %.200s",
+                     ct_name, expected, Py_TYPE(init)->tp_name);
+    return -1;
+}
+
+static int    /* forward */
+convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init);
+static int    /* forward */
+convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init);
+
+static int
+convert_field_from_object(char *data, CFieldObject *cf, PyObject *value)
+{
+    if (cf->cf_bitshift >= 0)
+        return convert_from_object_bitfield(data, cf, value);
+    else
+        return convert_from_object(data, cf->cf_type, value);
+}
+
+static int
+convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
+{
+    const char *expected;
+    char buf[sizeof(PY_LONG_LONG)];
+
+    if (ct->ct_flags & CT_ARRAY) {
+        CTypeDescrObject *ctitem = ct->ct_itemdescr;
+
+        if (PyList_Check(init) || PyTuple_Check(init)) {
+            PyObject **items;
+            Py_ssize_t i, n;
+            n = PySequence_Fast_GET_SIZE(init);
+            if (ct->ct_length >= 0 && n > ct->ct_length) {
+                PyErr_Format(PyExc_IndexError,
+                             "too many initializers for '%s' (got %zd)",
+                             ct->ct_name, n);
+                return -1;
+            }
+            items = PySequence_Fast_ITEMS(init);
+            for (i=0; i<n; i++) {
+                if (convert_from_object(data, ctitem, items[i]) < 0)
+                    return -1;
+                data += ctitem->ct_size;
+            }
+            return 0;
+        }
+        else if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) {
+            char *srcdata;
+            Py_ssize_t n;
+            if (!PyString_Check(init)) {
+                expected = "str or list or tuple";
+                goto cannot_convert;
+            }
+            n = PyString_GET_SIZE(init);
+            if (ct->ct_length >= 0 && n > ct->ct_length) {
+                PyErr_Format(PyExc_IndexError,
+                             "initializer string is too long for '%s' "
+                             "(got %zd characters)", ct->ct_name, n);
+                return -1;
+            }
+            if (n != ct->ct_length)
+                n++;
+            srcdata = PyString_AS_STRING(init);
+            memcpy(data, srcdata, n);
+            return 0;
+        }
+        else {
+            expected = "list or tuple";
+            goto cannot_convert;
+        }
+    }
+    if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) {
+        char *ptrdata;
+        CTypeDescrObject *ctinit;
+
+        expected = "compatible pointer";
+        if (!CData_Check(init))
+            goto cannot_convert;
+        ctinit = ((CDataObject *)init)->c_type;
+        if (!(ctinit->ct_flags & (CT_POINTER|CT_FUNCTIONPTR))) {
+            if (ctinit->ct_flags & CT_ARRAY)
+                ctinit = (CTypeDescrObject *)ctinit->ct_stuff;
+            else
+                goto cannot_convert;
+        }
+        if (ctinit != ct) {
+            if (((ct->ct_flags & CT_POINTER) &&
+                 (ct->ct_itemdescr->ct_flags & CT_CAST_ANYTHING)) ||
+                ((ctinit->ct_flags & CT_POINTER) &&
+                 (ctinit->ct_itemdescr->ct_flags & CT_CAST_ANYTHING)))
+                ;   /* accept void* or char* as either source or target */
+            else
+                goto cannot_convert;
+        }
+        ptrdata = ((CDataObject *)init)->c_data;
+
+        *(char **)data = ptrdata;
+        return 0;
+    }
+    if (ct->ct_flags & CT_PRIMITIVE_SIGNED) {
+        PY_LONG_LONG value = _my_PyLong_AsLongLong(init);
+
+        if (value == -1 && PyErr_Occurred()) {
+            if (!(ct->ct_flags & CT_IS_ENUM))
+                return -1;
+            else {
+                PyObject *ob;
+                PyErr_Clear();
+                if (!PyString_Check(init)) {
+                    expected = "str or int";
+                    goto cannot_convert;
+                }
+
+                ob = convert_enum_string_to_int(ct, init);
+                if (ob == NULL)
+                    return -1;
+                value = PyLong_AsLongLong(ob);
+                Py_DECREF(ob);
+                if (value == -1 && PyErr_Occurred())
+                    return -1;
+            }
+        }
+        write_raw_integer_data(buf, value, ct->ct_size);
+        if (value != read_raw_signed_data(buf, ct->ct_size))
+            goto overflow;
+        write_raw_integer_data(data, value, ct->ct_size);
+        return 0;
+    }
+    if (ct->ct_flags & CT_PRIMITIVE_UNSIGNED) {
+        unsigned PY_LONG_LONG value = _my_PyLong_AsUnsignedLongLong(init, 1);
+        if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
+            return -1;
+        write_raw_integer_data(buf, value, ct->ct_size);
+        if (value != read_raw_unsigned_data(buf, ct->ct_size))
+            goto overflow;
+        write_raw_integer_data(data, value, ct->ct_size);
+        return 0;
+    }
+    if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
+        double value = PyFloat_AsDouble(init);
+        write_raw_float_data(data, value, ct->ct_size);
+        return 0;
+    }
+    if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
+        int res = _convert_to_char(init);
+        if (res < 0)
+            return -1;
+        data[0] = res;
+        return 0;
+    }
+    if (ct->ct_flags & CT_STRUCT) {
+
+        if (CData_Check(init)) {
+            if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) {
+                memcpy(data, ((CDataObject *)init)->c_data, ct->ct_size);
+                return 0;
+            }
+        }
+        if (PyList_Check(init) || PyTuple_Check(init)) {
+            PyObject **items = PySequence_Fast_ITEMS(init);
+            Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init);
+            CFieldObject *cf = (CFieldObject *)ct->ct_extra;
+
+            for (i=0; i<n; i++) {
+                if (cf == NULL) {
+                    PyErr_Format(PyExc_ValueError,
+                                 "too many initializers for '%s' (got %zd)",
+                                 ct->ct_name, n);
+                    return -1;
+                }
+                if (convert_field_from_object(data + cf->cf_offset,
+                                              cf, items[i]) < 0)
+                    return -1;
+                cf = cf->cf_next;
+            }
+            return 0;
+        }
+        if (PyDict_Check(init)) {
+            PyObject *d_key, *d_value;
+            Py_ssize_t i = 0;
+            CFieldObject *cf;
+
+            while (PyDict_Next(init, &i, &d_key, &d_value)) {
+                cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, d_key);
+                if (cf == NULL) {
+                    PyErr_SetObject(PyExc_KeyError, d_key);
+                    return -1;
+                }
+                if (convert_field_from_object(data + cf->cf_offset,
+                                              cf, d_value) < 0)
+                    return -1;
+            }
+            return 0;
+        }
+        expected = "list or tuple or dict or struct-cdata";
+        goto cannot_convert;
+    }
+    if (ct->ct_flags & CT_UNION) {
+
+        if (CData_Check(init)) {
+            if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) {
+                memcpy(data, ((CDataObject *)init)->c_data, ct->ct_size);
+                return 0;
+            }
+        }
+        CFieldObject *cf = (CFieldObject *)ct->ct_extra;   /* first field */
+        if (cf == NULL) {
+            PyErr_SetString(PyExc_ValueError, "empty union");
+            return -1;
+        }
+        return convert_field_from_object(data, cf, init);
+    }
+    PyErr_Format(PyExc_SystemError,
+                 "convert_from_object: '%s'", ct->ct_name);
+    return -1;
+
+ overflow:
+    return _convert_overflow(init, ct->ct_name);
+
+ cannot_convert:
+    return _convert_error(init, ct->ct_name, expected);
+}
+
+static int
+convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init)
+{
+    CTypeDescrObject *ct = cf->cf_type;
+    PY_LONG_LONG fmin, fmax, value = PyLong_AsLongLong(init);
+    unsigned PY_LONG_LONG rawfielddata, rawvalue, rawmask;
+    if (value == -1 && PyErr_Occurred())
+        return -1;
+
+    if (ct->ct_flags & CT_PRIMITIVE_SIGNED) {
+        fmin = -(1LL << (cf->cf_bitsize-1));
+        fmax = (1LL << (cf->cf_bitsize-1)) - 1LL;
+        if (fmax == 0)
+            fmax = 1;    /* special case to let "int x:1" receive "1" */
+    }
+    else {
+        fmin = 0LL;
+        fmax = (PY_LONG_LONG)((1ULL << cf->cf_bitsize) - 1ULL);
+    }
+    if (value < fmin || value > fmax) {
+        /* phew, PyErr_Format does not support "%lld" in Python 2.6 */
+        PyObject *svalue = NULL, *sfmin = NULL, *sfmax = NULL;
+        PyObject *lfmin = NULL, *lfmax = NULL;
+        svalue = PyObject_Str(init);
+        if (svalue == NULL) goto skip;
+        lfmin = PyLong_FromLongLong(fmin);
+        if (lfmin == NULL) goto skip;
+        sfmin = PyObject_Str(lfmin);
+        if (sfmin == NULL) goto skip;
+        lfmax = PyLong_FromLongLong(fmax);
+        if (lfmax == NULL) goto skip;
+        sfmax = PyObject_Str(lfmax);
+        if (sfmax == NULL) goto skip;
+        PyErr_Format(PyExc_OverflowError,
+                     "value %s outside the range allowed by the "
+                     "bit field width: %s <= x <= %s",
+                     PyString_AS_STRING(svalue),
+                     PyString_AS_STRING(sfmin),
+                     PyString_AS_STRING(sfmax));
+       skip:
+        Py_XDECREF(svalue);
+        Py_XDECREF(sfmin);
+        Py_XDECREF(sfmax);
+        Py_XDECREF(lfmin);
+        Py_XDECREF(lfmax);
+        return -1;
+    }
+
+    rawmask = ((1ULL << cf->cf_bitsize) - 1ULL) << cf->cf_bitshift;
+    rawvalue = ((unsigned PY_LONG_LONG)value) << cf->cf_bitshift;
+    rawfielddata = read_raw_unsigned_data(data, ct->ct_size);
+    rawfielddata = (rawfielddata & ~rawmask) | (rawvalue & rawmask);
+    write_raw_integer_data(data, rawfielddata, ct->ct_size);
+    return 0;
+}
+
+static Py_ssize_t
+get_array_length(CDataObject *cd)
+{
+    if (cd->c_type->ct_length < 0)
+        return ((CDataObject_own_length *)cd)->length;
+    else
+        return cd->c_type->ct_length;
+}
+
+static int
+get_alignment(CTypeDescrObject *ct)
+{
+    int align;
+ retry:
+    if ((ct->ct_flags & (CT_PRIMITIVE_ANY|CT_STRUCT|CT_UNION)) &&
+        !(ct->ct_flags & CT_IS_OPAQUE)) {
+        align = ct->ct_length;
+    }
+    else if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) {
+        struct aligncheck_ptr { char x; char *y; };
+        align = offsetof(struct aligncheck_ptr, y);
+    }
+    else if (ct->ct_flags & CT_ARRAY) {
+        ct = ct->ct_itemdescr;
+        goto retry;
+    }
+    else {
+        PyErr_Format(PyExc_TypeError, "ctype '%s' is of unknown alignment",
+                     ct->ct_name);
+        return -1;
+    }
+
+    if ((align < 1) || (align & (align-1))) {
+        PyErr_Format(PyExc_SystemError,
+                     "found for ctype '%s' bogus alignment '%d'",
+                     ct->ct_name, align);
+        return -1;
+    }
+    return align;
+}
+
+static void cdata_dealloc(CDataObject *cd)
+{
+    Py_DECREF(cd->c_type);
+    PyObject_Del(cd);
+}
+
+static void cdataowning_dealloc(CDataObject_own_base *cdb)
+{
+    if (cdb->weakreflist != NULL)
+        PyObject_ClearWeakRefs((PyObject *) cdb);
+
+    if (cdb->head.c_type->ct_flags & CT_FUNCTIONPTR) {
+        /* a callback */
+        ffi_closure *closure = (ffi_closure *)cdb->head.c_data;
+        PyObject *args = (PyObject *)(closure->user_data);
+        Py_XDECREF(args);
+        cffi_closure_free(closure);
+    }
+    cdata_dealloc(&cdb->head);
+}
+
+static int cdata_traverse(CDataObject *cd, visitproc visit, void *arg)
+{
+    Py_VISIT(cd->c_type);
+    return 0;
+}
+
+static PyObject *cdata_repr(CDataObject *cd)
+{
+    char *p;
+    PyObject *result, *s = NULL;
+
+    if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) {
+        PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+        if (o == NULL)
+            return NULL;
+        s = PyObject_Repr(o);
+        Py_DECREF(o);
+        if (s == NULL)
+            return NULL;
+        p = PyString_AS_STRING(s);
+    }
+    else {
+        if (cd->c_data != NULL) {
+            s = PyString_FromFormat("%p", cd->c_data);
+            if (s == NULL)
+                return NULL;
+            p = PyString_AS_STRING(s);
+        }
+        else
+            p = "NULL";
+    }
+    result = PyString_FromFormat("<cdata '%s' %s>", cd->c_type->ct_name, p);
+    Py_XDECREF(s);
+    return result;
+}
+
+static PyObject *cdata_str(CDataObject *cd)
+{
+    if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) {
+        return PyString_FromStringAndSize(cd->c_data, 1);
+    }
+    else if (cd->c_type->ct_itemdescr != NULL &&
+             cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) {
+        Py_ssize_t length;
+
+        if (cd->c_type->ct_flags & CT_ARRAY) {
+            const char *start = cd->c_data;
+            const char *end;
+            length = get_array_length(cd);
+            end = (const char *)memchr(start, 0, length);
+            if (end != NULL)
+                length = end - start;
+        }
+        else {
+            if (cd->c_data == NULL) {
+                PyObject *s = cdata_repr(cd);
+                if (s != NULL) {
+                    PyErr_Format(PyExc_RuntimeError,
+                                 "cannot use str() on %s",
+                                 PyString_AS_STRING(s));
+                    Py_DECREF(s);
+                }
+                return NULL;
+            }
+            length = strlen(cd->c_data);
+        }
+
+        return PyString_FromStringAndSize(cd->c_data, length);
+    }
+    else if (cd->c_type->ct_flags & CT_IS_ENUM)
+        return convert_to_object(cd->c_data, cd->c_type);
+    else
+        return cdata_repr(cd);
+}
+
+static PyObject *cdataowning_repr(CDataObject *cd)
+{
+    Py_ssize_t size;
+    if (cd->c_type->ct_flags & CT_POINTER)
+        size = cd->c_type->ct_itemdescr->ct_size;
+    else if (cd->c_type->ct_flags & CT_ARRAY)
+        size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
+    else if (cd->c_type->ct_flags & CT_FUNCTIONPTR)
+        goto callback_repr;
+    else
+        size = cd->c_type->ct_size;
+
+    return PyString_FromFormat("<cdata '%s' owning %zd bytes>",
+                               cd->c_type->ct_name, size);
+
+ callback_repr:
+    {
+        PyObject *s, *res;
+        PyObject *args = (PyObject *)((ffi_closure *)cd->c_data)->user_data;
+        if (args == NULL)
+            return cdata_repr(cd);
+
+        s = PyObject_Repr(PyTuple_GET_ITEM(args, 1));
+        if (s == NULL)
+            return NULL;
+        res = PyString_FromFormat("<cdata '%s' calling %s>",
+                                  cd->c_type->ct_name, PyString_AsString(s));
+        Py_DECREF(s);
+        return res;
+    }
+}
+
+static int cdata_nonzero(CDataObject *cd)
+{
+    return cd->c_data != NULL;
+}
+
+static PyObject *cdata_int(CDataObject *cd)
+{
+    if ((cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG))
+                             == (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG)) {
+        /* this case is to handle enums, but also serves as a slight
+           performance improvement for some other primitive types */
+        long value = (long)read_raw_signed_data(cd->c_data,
+                                                cd->c_type->ct_size);
+        return PyInt_FromLong(value);
+    }
+    if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) {
+        return convert_to_object(cd->c_data, cd->c_type);
+    }
+    else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) {
+        return PyInt_FromLong((unsigned char)cd->c_data[0]);
+    }
+    else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
+        PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+        PyObject *r = o ? PyNumber_Int(o) : NULL;
+        Py_XDECREF(o);
+        return r;
+    }
+    PyErr_Format(PyExc_TypeError, "int() not supported on cdata '%s'",
+                 cd->c_type->ct_name);
+    return NULL;
+}
+
+static PyObject *cdata_long(CDataObject *cd)
+{
+    PyObject *res = cdata_int(cd);
+    if (res != NULL && PyInt_CheckExact(res)) {
+        PyObject *o = PyLong_FromLong(PyInt_AS_LONG(res));
+        Py_DECREF(res);
+        res = o;
+    }
+    return res;
+}
+
+static PyObject *cdata_float(CDataObject *cd)
+{
+    if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
+        return convert_to_object(cd->c_data, cd->c_type);
+    }
+    PyErr_Format(PyExc_TypeError, "float() not supported on cdata '%s'",
+                 cd->c_type->ct_name);
+    return NULL;
+}
+
+static PyObject *cdata_richcompare(PyObject *v, PyObject *w, int op)
+{
+    int res;
+    PyObject *pyres;
+    char *v_cdata, *w_cdata;
+
+    assert(CData_Check(v));
+    if (!CData_Check(w))
+        goto Unimplemented;
+
+    v_cdata = ((CDataObject *)v)->c_data;
+    w_cdata = ((CDataObject *)w)->c_data;
+    if ((op != Py_EQ && op != Py_NE) &&
+        ((((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY) ||
+         (((CDataObject *)w)->c_type->ct_flags & CT_PRIMITIVE_ANY)))
+        goto Error;
+
+    switch (op) {
+    case Py_EQ: res = (v_cdata == w_cdata); break;
+    case Py_NE: res = (v_cdata != w_cdata); break;
+    case Py_LT: res = (v_cdata <  w_cdata); break;
+    case Py_LE: res = (v_cdata <= w_cdata); break;
+    case Py_GT: res = (v_cdata >  w_cdata); break;
+    case Py_GE: res = (v_cdata >= w_cdata); break;
+    default: res = -1;
+    }
+    pyres = res ? Py_True : Py_False;
+ done:
+    Py_INCREF(pyres);
+    return pyres;
+
+ Unimplemented:
+    pyres = Py_NotImplemented;
+    goto done;
+
+ Error:
+    PyErr_SetString(PyExc_TypeError,
+                    "cannot do comparison on a primitive cdata");
+    return NULL;
+}
+
+static long cdata_hash(CDataObject *cd)
+{
+    long h = _Py_HashPointer(cd->c_type) ^ _Py_HashPointer(cd->c_data);
+    if (h == -1)
+        h = -2;
+    return h;
+}
+
+static Py_ssize_t
+cdata_length(CDataObject *cd)
+{
+    if (cd->c_type->ct_flags & CT_ARRAY) {
+        return get_array_length(cd);
+    }
+    PyErr_Format(PyExc_TypeError, "cdata of type '%s' has no len()",
+                 cd->c_type->ct_name);
+    return -1;
+}
+
+static char *
+_cdata_get_indexed_ptr(CDataObject *cd, PyObject *key)
+{
+    Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
+    if (i == -1 && PyErr_Occurred())
+        return NULL;
+
+    if (cd->c_type->ct_flags & CT_POINTER) {
+        if (CDataOwn_Check(cd) && i != 0) {
+            PyErr_Format(PyExc_IndexError,
+                         "cdata '%s' can only be indexed by 0",
+                         cd->c_type->ct_name);
+            return NULL;
+        }
+    }
+    else if (cd->c_type->ct_flags & CT_ARRAY) {
+        if (i < 0) {
+            PyErr_SetString(PyExc_IndexError,
+                            "negative index not supported");
+            return NULL;
+        }
+        if (i >= get_array_length(cd)) {
+            PyErr_Format(PyExc_IndexError,
+                         "index too large for cdata '%s' (expected %zd < %zd)",
+                         cd->c_type->ct_name,
+                         i, get_array_length(cd));
+            return NULL;
+        }
+    }
+    else {
+        PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+    return cd->c_data + i * cd->c_type->ct_itemdescr->ct_size;
+}
+
+static PyObject *
+cdata_subscript(CDataObject *cd, PyObject *key)
+{
+    char *c = _cdata_get_indexed_ptr(cd, key);
+    CTypeDescrObject *ctitem = cd->c_type->ct_itemdescr;
+    /* use 'mp_subscript' instead of 'sq_item' because we don't want
+       negative indexes to be corrected automatically */
+    if (c == NULL)
+        return NULL;
+    return convert_to_object(c, ctitem);
+}
+
+static int
+cdata_ass_sub(CDataObject *cd, PyObject *key, PyObject *v)
+{
+    char *c = _cdata_get_indexed_ptr(cd, key);
+    CTypeDescrObject *ctitem = cd->c_type->ct_itemdescr;
+    /* use 'mp_ass_subscript' instead of 'sq_ass_item' because we don't want
+       negative indexes to be corrected automatically */
+    if (c == NULL)
+        return -1;
+    return convert_from_object(c, ctitem, v);
+}
+
+static PyObject *
+_cdata_add_or_sub(PyObject *v, PyObject *w, int sign)
+{
+    Py_ssize_t i;
+    CDataObject *cd;
+    CTypeDescrObject *ctptr;
+
+    if (!CData_Check(v))
+        goto not_implemented;
+
+    i = PyNumber_AsSsize_t(w, PyExc_OverflowError);
+    if (i == -1 && PyErr_Occurred())
+        return NULL;
+    i *= sign;
+
+    cd = (CDataObject *)v;
+    if (cd->c_type->ct_flags & CT_POINTER)
+        ctptr = cd->c_type;
+    else if (cd->c_type->ct_flags & CT_ARRAY) {
+        ctptr = (CTypeDescrObject *)cd->c_type->ct_stuff;
+    }
+    else {
+        PyErr_Format(PyExc_TypeError, "cannot add a cdata '%s' and a number",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+    if (ctptr->ct_itemdescr->ct_size < 0) {
+        PyErr_Format(PyExc_TypeError,
+                     "ctype '%s' points to items of unknown size",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+    return new_simple_cdata(cd->c_data + i * ctptr->ct_itemdescr->ct_size,
+                            ctptr);
+
+ not_implemented:
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+}
+
+static PyObject *
+cdata_add(PyObject *v, PyObject *w)
+{
+    return _cdata_add_or_sub(v, w, +1);
+}
+
+static PyObject *
+cdata_sub(PyObject *v, PyObject *w)
+{
+    if (CData_Check(v) && CData_Check(w)) {
+        CDataObject *cdv = (CDataObject *)v;
+        CDataObject *cdw = (CDataObject *)w;
+        CTypeDescrObject *ct = cdw->c_type;
+        Py_ssize_t diff;
+
+        if (ct->ct_flags & CT_ARRAY)     /* ptr_to_T - array_of_T: ok */
+            ct = (CTypeDescrObject *)ct->ct_stuff;
+
+        if (ct != cdv->c_type || !(ct->ct_flags & CT_POINTER) ||
+                (ct->ct_itemdescr->ct_size <= 0)) {
+            PyErr_Format(PyExc_TypeError,
+                         "cannot subtract cdata '%s' and cdata '%s'",
+                         cdv->c_type->ct_name, ct->ct_name);
+            return NULL;
+        }
+        diff = (cdv->c_data - cdw->c_data) / ct->ct_itemdescr->ct_size;
+        return PyInt_FromSsize_t(diff);
+    }
+
+    return _cdata_add_or_sub(v, w, -1);
+}
+
+static PyObject *
+cdata_getattro(CDataObject *cd, PyObject *attr)
+{
+    CFieldObject *cf;
+    CTypeDescrObject *ct = cd->c_type;
+
+    if (ct->ct_flags & CT_POINTER)
+        ct = ct->ct_itemdescr;
+
+    if ((ct->ct_flags & (CT_STRUCT|CT_UNION)) && ct->ct_stuff != NULL) {
+        cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
+        if (cf != NULL) {
+            /* read the field 'cf' */
+            char *data = cd->c_data + cf->cf_offset;
+            if (cf->cf_bitshift >= 0)
+                return convert_to_object_bitfield(data, cf);
+            else
+                return convert_to_object(data, cf->cf_type);
+        }
+    }
+    return PyObject_GenericGetAttr((PyObject *)cd, attr);
+}
+
+static int
+cdata_setattro(CDataObject *cd, PyObject *attr, PyObject *value)
+{
+    CFieldObject *cf;
+    CTypeDescrObject *ct = cd->c_type;
+
+    if (ct->ct_flags & CT_POINTER)
+        ct = ct->ct_itemdescr;
+
+    if ((ct->ct_flags & (CT_STRUCT|CT_UNION)) && ct->ct_stuff != NULL) {
+        cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
+        if (cf != NULL) {
+            /* write the field 'cf' */
+            char *data = cd->c_data + cf->cf_offset;
+            if (value != NULL) {
+                return convert_field_from_object(data, cf, value);
+            }
+            else {
+                PyErr_SetString(PyExc_AttributeError,
+                                "cannot delete struct field");
+                return -1;
+            }
+        }
+    }
+    return PyObject_GenericSetAttr((PyObject *)cd, attr, value);
+}
+
+static cif_description_t *
+fb_prepare_cif(PyObject *fargs, CTypeDescrObject *fresult);    /* forward */
+
+static PyObject*
+cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
+{
+    char *buffer;
+    void** buffer_array;
+    cif_description_t *cif_descr;
+    Py_ssize_t i, nargs, nargs_declared;
+    PyObject *signature, *res, *fvarargs;
+    CTypeDescrObject *fresult;
+    char *resultdata;
+    char *errormsg;
+
+    if (!(cd->c_type->ct_flags & CT_FUNCTIONPTR)) {
+        PyErr_Format(PyExc_TypeError, "cdata '%s' is not callable",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+    if (kwds != NULL && PyDict_Size(kwds) != 0) {
+        PyErr_SetString(PyExc_TypeError,
+                "a cdata function cannot be called with keyword arguments");
+        return NULL;
+    }
+    signature = cd->c_type->ct_stuff;
+    nargs = PyTuple_Size(args);
+    if (nargs < 0)
+        return NULL;
+    nargs_declared = PyTuple_GET_SIZE(signature) - 1;
+    fresult = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 0);
+    fvarargs = NULL;
+    buffer = NULL;
+
+    cif_descr = (cif_description_t *)cd->c_type->ct_extra;
+
+    if (cif_descr != NULL) {
+        /* regular case: this function does not take '...' arguments */
+        if (nargs != nargs_declared) {
+            errormsg = "%s expects %zd arguments, got %zd";
+            goto bad_number_of_arguments;
+        }
+    }
+    else {
+        /* call of a variadic function */
+        if (nargs < nargs_declared) {
+            errormsg = "%s expects at least %zd arguments, got %zd";
+            goto bad_number_of_arguments;
+        }
+        fvarargs = PyTuple_New(nargs);
+        if (fvarargs == NULL)
+            goto error;
+        for (i = 0; i < nargs_declared; i++) {
+            PyObject *o = PyTuple_GET_ITEM(signature, 1 + i);
+            Py_INCREF(o);
+            PyTuple_SET_ITEM(fvarargs, i, o);
+        }
+        for (i = nargs_declared; i < nargs; i++) {
+            PyObject *obj = PyTuple_GET_ITEM(args, i);
+            CTypeDescrObject *ct;
+
+            if (CData_Check(obj)) {
+                ct = ((CDataObject *)obj)->c_type;
+                if (ct->ct_flags & CT_ARRAY)
+                    ct = (CTypeDescrObject *)ct->ct_stuff;
+                Py_INCREF(ct);
+            }
+            else {
+                PyErr_Format(PyExc_TypeError,
+                             "argument %zd passed in the variadic part "
+                             "needs to be a cdata object (got %.200s)",
+                             i + 1, Py_TYPE(obj)->tp_name);
+                goto error;
+            }
+            PyTuple_SET_ITEM(fvarargs, i, (PyObject *)ct);
+        }
+        cif_descr = fb_prepare_cif(fvarargs, fresult);
+        if (cif_descr == NULL)
+            goto error;
+    }
+
+    buffer = PyObject_Malloc(cif_descr->exchange_size);
+    if (buffer == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    buffer_array = (void **)buffer;
+
+    for (i=0; i<nargs; i++) {
+        CTypeDescrObject *argtype;
+        char *data = buffer + cif_descr->exchange_offset_arg[1 + i];
+        PyObject *obj = PyTuple_GET_ITEM(args, i);
+
+        buffer_array[i] = data;
+
+        if (i < nargs_declared)
+            argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 1 + i);
+        else
+            argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i);
+
+        if ((argtype->ct_flags & CT_POINTER) &&
+            (argtype->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) &&
+            PyString_Check(obj)) {
+            /* special case: Python string -> cdata 'char *' */
+            *(char **)data = PyString_AS_STRING(obj);
+        }
+        else if (convert_from_object(data, argtype, obj) < 0)
+            goto error;
+    }
+
+    resultdata = buffer + cif_descr->exchange_offset_arg[0];
+
+    restore_errno();
+    ffi_call(&cif_descr->cif, (void (*)(void))(cd->c_data),
+             resultdata, buffer_array);
+    save_errno();
+
+    if (fresult->ct_flags & CT_VOID) {
+        res = Py_None;
+        Py_INCREF(res);
+    }
+    else {
+        res = convert_to_object(resultdata, fresult);
+    }
+    PyObject_Free(buffer);
+ done:
+    if (fvarargs != NULL) {
+        Py_DECREF(fvarargs);
+        if (cif_descr != NULL)  /* but only if fvarargs != NULL, if variadic */
+            PyObject_Free(cif_descr);
+    }
+    return res;
+
+ bad_number_of_arguments:
+    {
+        PyObject *s = cdata_repr(cd);
+        PyErr_Format(PyExc_TypeError, errormsg,
+                     PyString_AsString(s), nargs_declared, nargs);
+        goto error;
+    }
+
+ error:
+    if (buffer)
+        PyObject_Free(buffer);
+    res = NULL;
+    goto done;
+}
+
+static PyObject *cdata_iter(CDataObject *);
+
+static PyNumberMethods CData_as_number = {
+    (binaryfunc)cdata_add,      /*nb_add*/
+    (binaryfunc)cdata_sub,      /*nb_subtract*/
+    0,                          /*nb_multiply*/
+    0,                          /*nb_divide*/
+    0,                          /*nb_remainder*/
+    0,                          /*nb_divmod*/
+    0,                          /*nb_power*/
+    0,                          /*nb_negative*/
+    0,                          /*nb_positive*/
+    0,                          /*nb_absolute*/
+    (inquiry)cdata_nonzero,     /*nb_nonzero*/
+    0,                          /*nb_invert*/
+    0,                          /*nb_lshift*/
+    0,                          /*nb_rshift*/
+    0,                          /*nb_and*/
+    0,                          /*nb_xor*/
+    0,                          /*nb_or*/
+    0,                          /*nb_coerce*/
+    (unaryfunc)cdata_int,       /*nb_int*/
+    (unaryfunc)cdata_long,      /*nb_long*/
+    (unaryfunc)cdata_float,     /*nb_float*/
+    0,                          /*nb_oct*/
+    0,                          /*nb_hex*/
+};
+
+static PyMappingMethods CData_as_mapping = {
+    (lenfunc)cdata_length, /*mp_length*/
+    (binaryfunc)cdata_subscript, /*mp_subscript*/
+    (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
+};
+
+static PyTypeObject CData_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CData",
+    sizeof(CDataObject),
+    0,
+    (destructor)cdata_dealloc,                  /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    (reprfunc)cdata_repr,                       /* tp_repr */
+    &CData_as_number,                           /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    &CData_as_mapping,                          /* tp_as_mapping */
+    (hashfunc)cdata_hash,                       /* tp_hash */
+    (ternaryfunc)cdata_call,                    /* tp_call */
+    (reprfunc)cdata_str,                        /* tp_str */
+    (getattrofunc)cdata_getattro,               /* tp_getattro */
+    (setattrofunc)cdata_setattro,               /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
+    0,                                          /* tp_doc */
+    (traverseproc)cdata_traverse,               /* tp_traverse */
+    0,                                          /* tp_clear */
+    cdata_richcompare,                          /* tp_richcompare */
+    0,                                          /* tp_weaklistoffset */
+    (getiterfunc)cdata_iter,                    /* tp_iter */
+};
+
+static PyTypeObject CDataOwning_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CDataOwn",
+    sizeof(CDataObject_own_base),
+    0,
+    (destructor)cdataowning_dealloc,            /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    (reprfunc)cdataowning_repr,                 /* tp_repr */
+    0,                                          /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    0,                                          /* tp_as_mapping */
+    0,                                          /* tp_hash */
+    0,                                          /* tp_call */
+    0,                                          /* tp_str */
+    0,                                          /* tp_getattro */
+    0,                                          /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
+    0,                                          /* tp_doc */
+    0,                                          /* tp_traverse */
+    0,                                          /* tp_clear */
+    0,                                          /* tp_richcompare */
+    offsetof(CDataObject_own_base, weakreflist),/* tp_weaklistoffset */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    0,                                          /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    &CData_Type,                                /* tp_base */
+};
+
+/************************************************************/
+
+typedef struct {
+    PyObject_HEAD
+    char *di_next, *di_stop;
+    CDataObject *di_object;
+    CTypeDescrObject *di_itemtype;
+} CDataIterObject;
+
+static PyObject *
+cdataiter_next(CDataIterObject *it)
+{
+    char *result = it->di_next;
+    if (result != it->di_stop) {
+        it->di_next = result + it->di_itemtype->ct_size;
+        return convert_to_object(result, it->di_itemtype);
+    }
+    return NULL;
+}
+
+static void
+cdataiter_dealloc(CDataIterObject *it)
+{
+    Py_DECREF(it->di_object);
+    PyObject_Del(it);
+}
+
+static PyTypeObject CDataIter_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CDataIter",              /* tp_name */
+    sizeof(CDataIterObject),                /* tp_basicsize */
+    0,                                      /* tp_itemsize */
+    /* methods */
+    (destructor)cdataiter_dealloc,          /* tp_dealloc */
+    0,                                      /* tp_print */
+    0,                                      /* tp_getattr */
+    0,                                      /* tp_setattr */
+    0,                                      /* tp_compare */
+    0,                                      /* tp_repr */
+    0,                                      /* tp_as_number */
+    0,                                      /* tp_as_sequence */
+    0,                                      /* tp_as_mapping */
+    0,                                      /* tp_hash */
+    0,                                      /* tp_call */
+    0,                                      /* tp_str */
+    PyObject_GenericGetAttr,                /* tp_getattro */
+    0,                                      /* tp_setattro */
+    0,                                      /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT,                     /* tp_flags */
+    0,                                      /* tp_doc */
+    0,                                      /* tp_traverse */
+    0,                                      /* tp_clear */
+    0,                                      /* tp_richcompare */
+    0,                                      /* tp_weaklistoffset */
+    PyObject_SelfIter,                      /* tp_iter */
+    (iternextfunc)cdataiter_next,           /* tp_iternext */
+};
+
+static PyObject *
+cdata_iter(CDataObject *cd)
+{
+    CDataIterObject *it;
+
+    if (!(cd->c_type->ct_flags & CT_ARRAY)) {
+        PyErr_Format(PyExc_TypeError, "cdata '%s' does not support iteration",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+
+    it = PyObject_New(CDataIterObject, &CDataIter_Type);
+    if (it == NULL)
+        return NULL;
+
+    Py_INCREF(cd);
+    it->di_object = cd;
+    it->di_itemtype = cd->c_type->ct_itemdescr;
+    it->di_next = cd->c_data;
+    it->di_stop = cd->c_data + get_array_length(cd) * it->di_itemtype->ct_size;
+    return (PyObject *)it;
+}
+
+/************************************************************/
+
+static PyObject *b_newp(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct, *ctitem;
+    CDataObject_own_base *cdb;
+    PyObject *init = Py_None;
+    Py_ssize_t dataoffset, datasize, explicitlength;
+    if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init))
+        return NULL;
+
+    explicitlength = -1;
+    if (ct->ct_flags & CT_POINTER) {
+        dataoffset = offsetof(CDataObject_own_nolength, alignment);
+        ctitem = ct->ct_itemdescr;
+        datasize = ctitem->ct_size;
+        if (datasize < 0) {
+            PyErr_Format(PyExc_TypeError,
+                         "cannot instantiate ctype '%s' of unknown size",
+                         ctitem->ct_name);
+            return NULL;
+        }
+        if (ctitem->ct_flags & CT_PRIMITIVE_CHAR)
+            datasize += sizeof(char);  /* forcefully add a null character */
+    }
+    else if (ct->ct_flags & CT_ARRAY) {
+        dataoffset = offsetof(CDataObject_own_nolength, alignment);
+        datasize = ct->ct_size;
+        if (datasize < 0) {
+            if (PyList_Check(init) || PyTuple_Check(init)) {
+                explicitlength = PySequence_Fast_GET_SIZE(init);
+            }
+            else if (PyString_Check(init)) {
+                /* from a string, we add the null terminator */
+                explicitlength = PyString_GET_SIZE(init) + 1;
+            }
+            else {
+                explicitlength = PyNumber_AsSsize_t(init, PyExc_OverflowError);
+                if (explicitlength < 0) {
+                    if (!PyErr_Occurred())
+                        PyErr_SetString(PyExc_ValueError,
+                                        "negative array length");
+                    return NULL;
+                }
+                init = Py_None;
+            }
+            ctitem = ct->ct_itemdescr;
+            dataoffset = offsetof(CDataObject_own_length, alignment);
+            datasize = explicitlength * ctitem->ct_size;
+            if (explicitlength > 0 &&
+                    (datasize / explicitlength) != ctitem->ct_size) {
+                PyErr_SetString(PyExc_OverflowError,
+                                "array size would overflow a Py_ssize_t");
+                return NULL;
+            }
+        }
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError, "expected a pointer or array ctype");
+        return NULL;
+    }
+
+    cdb = (CDataObject_own_base *)PyObject_Malloc(dataoffset + datasize);
+    if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
+        return NULL;
+
+    Py_INCREF(ct);
+    cdb->head.c_type = ct;
+    cdb->head.c_data = ((char *)cdb) + dataoffset;
+    cdb->weakreflist = NULL;
+    if (explicitlength >= 0)
+        ((CDataObject_own_length*)cdb)->length = explicitlength;
+
+    memset(cdb->head.c_data, 0, datasize);
+    if (init != Py_None) {
+        if (convert_from_object(cdb->head.c_data,
+              (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) {
+            Py_DECREF(cdb);
+            return NULL;
+        }
+    }
+    return (PyObject *)cdb;
+}
+
+static CDataObject *_new_casted_primitive(CTypeDescrObject *ct)
+{
+    int dataoffset = offsetof(CDataObject_casted_primitive, alignment);
+    CDataObject *cd = (CDataObject *)PyObject_Malloc(dataoffset + ct->ct_size);
+    if (PyObject_Init((PyObject *)cd, &CData_Type) == NULL)
+        return NULL;
+    Py_INCREF(ct);
+    cd->c_type = ct;
+    cd->c_data = ((char*)cd) + dataoffset;
+    return cd;
+}
+
+static CDataObject *cast_to_integer_or_char(CTypeDescrObject *ct, PyObject *ob)
+{
+    unsigned PY_LONG_LONG value;
+    CDataObject *cd;
+
+    if (CData_Check(ob) &&
+        ((CDataObject *)ob)->c_type->ct_flags &
+                                 (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) {
+        value = (Py_intptr_t)((CDataObject *)ob)->c_data;
+    }
+    else if (PyString_Check(ob)) {
+        if (ct->ct_flags & CT_IS_ENUM) {
+            ob = convert_enum_string_to_int(ct, ob);
+            if (ob == NULL)
+                return NULL;
+            cd = cast_to_integer_or_char(ct, ob);
+            Py_DECREF(ob);
+            return cd;
+        }
+        else {
+            if (PyString_GET_SIZE(ob) != 1) {
+                PyErr_Format(PyExc_TypeError,
+                      "cannot cast string of length %zd to ctype '%s'",
+                             PyString_GET_SIZE(ob), ct->ct_name);
+                return NULL;
+            }
+            value = (unsigned char)PyString_AS_STRING(ob)[0];
+        }
+    }
+    else {
+        value = _my_PyLong_AsUnsignedLongLong(ob, 0);
+        if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
+            return NULL;
+    }
+    cd = _new_casted_primitive(ct);
+    if (cd != NULL)
+        write_raw_integer_data(cd->c_data, value, ct->ct_size);
+    return cd;
+}
+
+static PyObject *b_cast(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    CDataObject *cd;
+    PyObject *ob;
+    if (!PyArg_ParseTuple(args, "O!O:cast", &CTypeDescr_Type, &ct, &ob))
+        return NULL;
+
+    if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) {
+        /* cast to a pointer or to a funcptr */
+        unsigned PY_LONG_LONG value;
+
+        if (CData_Check(ob)) {
+            CDataObject *cdsrc = (CDataObject *)ob;
+            if (cdsrc->c_type->ct_flags &
+                    (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) {
+                return new_simple_cdata(cdsrc->c_data, ct);
+            }
+        }
+        value = _my_PyLong_AsUnsignedLongLong(ob, 0);
+        if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
+            return NULL;
+        return new_simple_cdata((char *)(Py_intptr_t)value, ct);
+    }
+    else if (ct->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED
+                             |CT_PRIMITIVE_CHAR)) {
+        /* cast to an integer type or a char */
+        return (PyObject *)cast_to_integer_or_char(ct, ob);
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
+        /* cast to a float */
+        double value;
+        PyObject *io;
+
+        if (CData_Check(ob)) {
+            CDataObject *cdsrc = (CDataObject *)ob;
+
+            if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY))
+                goto cannot_cast;
+            io = convert_to_object(cdsrc->c_data, cdsrc->c_type);
+            if (io == NULL)
+                return NULL;
+        }
+        else {
+            io = ob;
+            Py_INCREF(io);
+        }
+
+        if (PyString_Check(io)) {
+            if (PyString_GET_SIZE(io) != 1) {
+                Py_DECREF(io);
+                goto cannot_cast;
+            }
+            value = (unsigned char)PyString_AS_STRING(io)[0];
+        }
+        else {
+            value = PyFloat_AsDouble(io);
+        }
+        Py_DECREF(io);
+        if (value == -1.0 && PyErr_Occurred())
+            return NULL;
+
+        cd = _new_casted_primitive(ct);
+        if (cd != NULL)
+            write_raw_float_data(cd->c_data, value, ct->ct_size);
+        return (PyObject *)cd;
+    }
+    else {
+        PyErr_Format(PyExc_TypeError, "cannot cast to ctype '%s'",
+                     ct->ct_name);
+        return NULL;
+    }
+
+ cannot_cast:
+    if (CData_Check(ob))
+        PyErr_Format(PyExc_TypeError, "cannot cast ctype '%s' to ctype '%s'",
+                     ((CDataObject *)ob)->c_type->ct_name, ct->ct_name);
+    else
+        PyErr_Format(PyExc_TypeError,
+                     "cannot cast %.200s object to ctype '%s'",
+                     Py_TYPE(ob)->tp_name, ct->ct_name);
+    return NULL;
+}
+
+/************************************************************/
+
+typedef struct {
+    PyObject_HEAD
+    void *dl_handle;
+    char *dl_name;
+} DynLibObject;
+
+static void dl_dealloc(DynLibObject *dlobj)
+{
+    dlclose(dlobj->dl_handle);
+    free(dlobj->dl_name);
+    PyObject_Del(dlobj);
+}
+
+static PyObject *dl_repr(DynLibObject *dlobj)
+{
+    return PyString_FromFormat("<clibrary '%s'>", dlobj->dl_name);
+}
+
+static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    char *funcname;
+    void *funcptr;
+
+    if (!PyArg_ParseTuple(args, "O!s:load_function",
+                          &CTypeDescr_Type, &ct, &funcname))
+        return NULL;
+
+    if (!(ct->ct_flags & CT_FUNCTIONPTR)) {
+        PyErr_Format(PyExc_TypeError, "function cdata expected, got '%s'",
+                     ct->ct_name);
+        return NULL;
+    }
+    funcptr = dlsym(dlobj->dl_handle, funcname);
+    if (funcptr == NULL) {
+        PyErr_Format(PyExc_KeyError, "function '%s' not found in library '%s'",
+                     funcname, dlobj->dl_name);
+        return NULL;
+    }
+
+    return new_simple_cdata(funcptr, ct);
+}
+
+static PyObject *dl_read_variable(DynLibObject *dlobj, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    char *varname;
+    char *data;
+
+    if (!PyArg_ParseTuple(args, "O!s:read_variable",
+                          &CTypeDescr_Type, &ct, &varname))
+        return NULL;
+
+    data = dlsym(dlobj->dl_handle, varname);
+    if (data == NULL) {
+        PyErr_Format(PyExc_KeyError, "variable '%s' not found in library '%s'",
+                     varname, dlobj->dl_name);
+        return NULL;
+    }
+    return convert_to_object(data, ct);
+}
+
+static PyObject *dl_write_variable(DynLibObject *dlobj, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    PyObject *value;
+    char *varname;
+    char *data;
+
+    if (!PyArg_ParseTuple(args, "O!sO:read_variable",
+                          &CTypeDescr_Type, &ct, &varname, &value))
+        return NULL;
+
+    data = dlsym(dlobj->dl_handle, varname);
+    if (data == NULL) {
+        PyErr_Format(PyExc_KeyError, "variable '%s' not found in library '%s'",
+                     varname, dlobj->dl_name);
+        return NULL;
+    }
+    if (convert_from_object(data, ct, value) < 0)
+        return NULL;
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef dl_methods[] = {
+    {"load_function",   (PyCFunction)dl_load_function,  METH_VARARGS},
+    {"read_variable",   (PyCFunction)dl_read_variable,  METH_VARARGS},
+    {"write_variable",  (PyCFunction)dl_write_variable, METH_VARARGS},
+    {NULL,              NULL}           /* sentinel */
+};
+
+static PyTypeObject dl_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.Library",            /* tp_name */
+    sizeof(DynLibObject),               /* tp_basicsize */
+    0,                                  /* tp_itemsize */
+    /* methods */
+    (destructor)dl_dealloc,             /* tp_dealloc */
+    0,                                  /* tp_print */
+    0,                                  /* tp_getattr */
+    0,                                  /* tp_setattr */
+    0,                                  /* tp_compare */
+    (reprfunc)dl_repr,                  /* tp_repr */
+    0,                                  /* tp_as_number */
+    0,                                  /* tp_as_sequence */
+    0,                                  /* tp_as_mapping */
+    0,                                  /* tp_hash */
+    0,                                  /* tp_call */
+    0,                                  /* tp_str */
+    PyObject_GenericGetAttr,            /* tp_getattro */
+    0,                                  /* tp_setattro */
+    0,                                  /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT,                 /* tp_flags */
+    0,                                  /* tp_doc */
+    0,                                  /* tp_traverse */
+    0,                                  /* tp_clear */
+    0,                                  /* tp_richcompare */
+    0,                                  /* tp_weaklistoffset */
+    0,                                  /* tp_iter */
+    0,                                  /* tp_iternext */
+    dl_methods,                         /* tp_methods */
+};
+
+static PyObject *b_load_library(PyObject *self, PyObject *args)
+{
+    char *filename;
+    void *handle;
+    DynLibObject *dlobj;
+
+    if (!PyArg_ParseTuple(args, "et:load_library",
+                          Py_FileSystemDefaultEncoding, &filename))
+        return NULL;
+
+    handle = dlopen(filename, RTLD_LAZY);
+    if (handle == NULL) {
+        PyErr_Format(PyExc_OSError, "cannot load library: %s", filename);
+        return NULL;
+    }
+
+    dlobj = PyObject_New(DynLibObject, &dl_type);
+    if (dlobj == NULL) {
+        dlclose(handle);
+        return NULL;
+    }
+    dlobj->dl_handle = handle;
+    dlobj->dl_name = strdup(filename);
+    return (PyObject *)dlobj;
+}
+
+/************************************************************/
+
+static PyObject *b_nonstandard_integer_types(PyObject *self, PyObject *noarg)
+{
+#define UNSIGNED   0x1000
+    static const struct descr_s { const char *name; int size; } types[] = {
+        { "int8_t",        1 },
+        { "uint8_t",       1 | UNSIGNED },
+        { "int16_t",       2 },
+        { "uint16_t",      2 | UNSIGNED },
+        { "int32_t",       4 },
+        { "uint32_t",      4 | UNSIGNED },
+        { "int64_t",       8 },
+        { "uint64_t",      8 | UNSIGNED },
+
+        { "intptr_t",      sizeof(intptr_t) },
+        { "uintptr_t",     sizeof(uintptr_t) | UNSIGNED },
+        { "ptrdiff_t",     sizeof(ptrdiff_t) },
+        { "size_t",        sizeof(size_t) | UNSIGNED },
+        { "ssize_t",       sizeof(ssize_t) },
+        /*{ "wchar_t",       sizeof(wchar_t) | UNSIGNED },*/
+        { NULL }
+    };
+#undef UNSIGNED
+    const struct descr_s *ptypes;
+    PyObject *d;
+
+    d = PyDict_New();
+    if (d == NULL)
+        return NULL;
+
+    for (ptypes=types; ptypes->name; ptypes++) {
+        int err;
+        PyObject *obj = PyInt_FromLong(ptypes->size);
+        if (obj == NULL)
+            goto error;
+        err = PyDict_SetItemString(d, ptypes->name, obj);
+        Py_DECREF(obj);
+        if (err != 0)
+            goto error;
+    }
+    return d;
+
+ error:
+    Py_DECREF(d);
+    return NULL;
+}
+
+static PyObject *b_new_primitive_type(PyObject *self, PyObject *args)
+{
+#define ENUM_PRIMITIVE_TYPES                                    \
+       EPTYPE(c, char, CT_PRIMITIVE_CHAR | CT_CAST_ANYTHING)    \
+       EPTYPE(s, short, CT_PRIMITIVE_SIGNED )                   \
+       EPTYPE(i, int, CT_PRIMITIVE_SIGNED )                     \
+       EPTYPE(l, long, CT_PRIMITIVE_SIGNED )                    \
+       EPTYPE(ll, long long, CT_PRIMITIVE_SIGNED )              \
+       EPTYPE(sc, signed char, CT_PRIMITIVE_SIGNED )            \
+       EPTYPE(uc, unsigned char, CT_PRIMITIVE_UNSIGNED )        \
+       EPTYPE(us, unsigned short, CT_PRIMITIVE_UNSIGNED )       \
+       EPTYPE(ui, unsigned int, CT_PRIMITIVE_UNSIGNED )         \