Snap for 5590969 from 260e434a9ad462004b6e02ba9bfc6f6c5a889281 to sdk-release

Change-Id: Iddb9ba4ef288866a6cd065d6f40ca6f4b43358b3
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..370a25d
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,8 @@
+This package has been mostly done by Armin Rigo with help from
+Maciej FijaƂkowski. The idea is heavily based (although not directly
+copied) from LuaJIT ffi by Mike Pall.
+
+
+Other contributors:
+
+  Google Inc.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..29225ee
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+
+Except when otherwise stated (look for LICENSE files in directories or
+information at the beginning of each file) all software and
+documentation is licensed as follows: 
+
+    The MIT License
+
+    Permission is hereby granted, free of charge, to any person 
+    obtaining a copy of this software and associated documentation 
+    files (the "Software"), to deal in the Software without 
+    restriction, including without limitation the rights to use, 
+    copy, modify, merge, publish, distribute, sublicense, and/or 
+    sell copies of the Software, and to permit persons to whom the 
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included 
+    in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+    DEALINGS IN THE SOFTWARE.
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..e9fdd6a
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+recursive-include cffi *.py *.h
+recursive-include c *.c *.h *.asm *.py win64.obj
+recursive-include testing *.py *.c *.h
+recursive-include doc *.py *.rst Makefile *.bat
+recursive-include demo py.cleanup *.py embedding_test.c manual.c
+include AUTHORS LICENSE setup.py setup_base.py
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..512e1f3
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "cffi"
+description:
+    "Foreign Function Interface for Python calling C code."
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://bitbucket.org/cffi/cffi"
+  }
+  url {
+    type: HG
+    value: "https://bitbucket.org/cffi/cffi/src"
+  }
+  version: "1.12.2"
+  last_upgrade_date { year: 2019 month: 2 day: 26 }
+  license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+LICENSE
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3e7862d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+CFFI
+====
+
+Foreign Function Interface for Python calling C code.
+Please see the [Documentation](http://cffi.readthedocs.org/) or uncompiled
+in the doc/ subdirectory.
+
+Download
+--------
+
+[Download page](https://bitbucket.org/cffi/cffi/downloads)
+
+Contact
+-------
+
+[Mailing list](https://groups.google.com/forum/#!forum/python-cffi)
+
+Testing/development tips
+------------------------
+
+To run tests under CPython, run::
+
+    pip install pytest     # if you don't have py.test already
+    pip install pycparser
+    python setup.py build_ext -f -i
+    py.test c/ testing/
+
+If you run in another directory (either the tests or another program),
+you should use the environment variable ``PYTHONPATH=/path`` to point
+to the location that contains the ``_cffi_backend.so`` just compiled.
diff --git a/c/.libs_cffi_backend/libffi-45372312.so.6.0.4 b/c/.libs_cffi_backend/libffi-45372312.so.6.0.4
new file mode 100644
index 0000000..59e65c0
--- /dev/null
+++ b/c/.libs_cffi_backend/libffi-45372312.so.6.0.4
Binary files differ
diff --git a/c/Android.bp b/c/Android.bp
new file mode 100644
index 0000000..baced02
--- /dev/null
+++ b/c/Android.bp
@@ -0,0 +1,53 @@
+python_library {
+    name: "py-cffi-backend",
+    host_supported: true,
+    srcs: [
+        "_dummy_file_cffi_backend.py",
+    ],
+    version: {
+        py2: {
+	    enabled: true,
+	},
+	py3: {
+	    enabled: true,
+	},
+    },
+    data: [
+        ":py-cffi-backend-files"
+    ],
+}
+
+filegroup {
+    name: "py-cffi-backend-files",
+    srcs: [
+        "_cffi_backend.so",
+    ],
+}
+
+python_library {
+    name: "py-cffi-backend-libffi",
+    host_supported: true,
+    srcs: [
+        "_dummy_file_libffi.py",
+    ],
+    version: {
+        py2: {
+	    enabled: true,
+	},
+	py3: {
+	    enabled: true,
+	},
+    },
+    data: [
+        ":py-cffi-backend-libffi-files"
+    ],
+}
+
+filegroup {
+    name: "py-cffi-backend-libffi-files",
+    srcs: [
+        ".libs_cffi_backend/libffi-45372312.so.6.0.4",
+    ],
+}
+
+
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
new file mode 100644
index 0000000..c39866a
--- /dev/null
+++ b/c/_cffi_backend.c
@@ -0,0 +1,7686 @@
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "structmember.h"
+
+#define CFFI_VERSION  "1.12.2"
+
+#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
+
+/* this block of #ifs should be kept exactly identical between
+   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py */
+#if defined(_MSC_VER)
+# include <malloc.h>   /* for alloca() */
+# if _MSC_VER < 1600   /* MSVC < 2010 */
+   typedef __int8 int8_t;
+   typedef __int16 int16_t;
+   typedef __int32 int32_t;
+   typedef __int64 int64_t;
+   typedef unsigned __int8 uint8_t;
+   typedef unsigned __int16 uint16_t;
+   typedef unsigned __int32 uint32_t;
+   typedef unsigned __int64 uint64_t;
+   typedef __int8 int_least8_t;
+   typedef __int16 int_least16_t;
+   typedef __int32 int_least32_t;
+   typedef __int64 int_least64_t;
+   typedef unsigned __int8 uint_least8_t;
+   typedef unsigned __int16 uint_least16_t;
+   typedef unsigned __int32 uint_least32_t;
+   typedef unsigned __int64 uint_least64_t;
+   typedef __int8 int_fast8_t;
+   typedef __int16 int_fast16_t;
+   typedef __int32 int_fast32_t;
+   typedef __int64 int_fast64_t;
+   typedef unsigned __int8 uint_fast8_t;
+   typedef unsigned __int16 uint_fast16_t;
+   typedef unsigned __int32 uint_fast32_t;
+   typedef unsigned __int64 uint_fast64_t;
+   typedef __int64 intmax_t;
+   typedef unsigned __int64 uintmax_t;
+# else
+#  include <stdint.h>
+# endif
+# if _MSC_VER < 1800   /* MSVC < 2013 */
+   typedef unsigned char _Bool;
+# endif
+#else
+# include <stdint.h>
+# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
+#  include <alloca.h>
+# endif
+#endif
+
+
+/* Define the following macro ONLY if you trust libffi's version of
+ * ffi_closure_alloc() more than the code in malloc_closure.h.
+ * IMPORTANT: DO NOT ENABLE THIS ON LINUX, unless you understand exactly
+ * why I recommend against it and decide that you trust it more than my
+ * analysis below.
+ *
+ * There are two versions of this code: one inside libffi itself, and
+ * one inside malloc_closure.h here.  Both should be fine as long as the
+ * Linux distribution does _not_ enable extra security features.  If it
+ * does, then the code in malloc_closure.h will cleanly crash because
+ * there is no reasonable way to obtain a read-write-execute memory
+ * page.  On the other hand, the code in libffi will appear to
+ * work---but will actually randomly crash after a fork() if the child
+ * does not immediately call exec().  This second crash is of the kind
+ * that can be turned into an attack vector by a motivated attacker.
+ * So, _enabling_ extra security features _opens_ an attack vector.
+ * That sounds like a horribly bad idea to me, and is the reason for why
+ * I prefer CFFI crashing cleanly.
+ *
+ * Currently, we use libffi's ffi_closure_alloc() only on NetBSD.  It is
+ * known that on the NetBSD kernel, a different strategy is used which
+ * should not be open to the fork() bug.
+ */
+#ifdef __NetBSD__
+# define CFFI_TRUST_LIBFFI
+#endif
+
+#ifndef CFFI_TRUST_LIBFFI
+# include "malloc_closure.h"
+#endif
+
+
+#if PY_MAJOR_VERSION >= 3
+# define STR_OR_BYTES "bytes"
+# define PyText_Type PyUnicode_Type
+# define PyText_Check PyUnicode_Check
+# define PyTextAny_Check PyUnicode_Check
+# define PyText_FromFormat PyUnicode_FromFormat
+# define PyText_AsUTF8 _PyUnicode_AsString   /* PyUnicode_AsUTF8 in Py3.3 */
+# define PyText_AS_UTF8 _PyUnicode_AsString
+# if PY_VERSION_HEX >= 0x03030000
+#  define PyText_GetSize PyUnicode_GetLength
+# else
+#  define PyText_GetSize PyUnicode_GetSize
+# endif
+# define PyText_FromString PyUnicode_FromString
+# define PyText_FromStringAndSize PyUnicode_FromStringAndSize
+# define PyText_InternInPlace PyUnicode_InternInPlace
+# define PyText_InternFromString PyUnicode_InternFromString
+# define PyIntOrLong_Check PyLong_Check
+#else
+# define STR_OR_BYTES "str"
+# define PyText_Type PyString_Type
+# define PyText_Check PyString_Check
+# define PyTextAny_Check(op) (PyString_Check(op) || PyUnicode_Check(op))
+# define PyText_FromFormat PyString_FromFormat
+# define PyText_AsUTF8 PyString_AsString
+# define PyText_AS_UTF8 PyString_AS_STRING
+# define PyText_GetSize PyString_Size
+# define PyText_FromString PyString_FromString
+# define PyText_FromStringAndSize PyString_FromStringAndSize
+# define PyText_InternInPlace PyString_InternInPlace
+# define PyText_InternFromString PyString_InternFromString
+# define PyIntOrLong_Check(op) (PyInt_Check(op) || PyLong_Check(op))
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+# define PyInt_FromLong PyLong_FromLong
+# define PyInt_FromSsize_t PyLong_FromSsize_t
+# define PyInt_AsSsize_t PyLong_AsSsize_t
+# define PyInt_AsLong PyLong_AsLong
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+/* This is the default on Python3 and constant has been removed. */
+# define Py_TPFLAGS_CHECKTYPES 0
+#endif
+
+#if PY_MAJOR_VERSION < 3
+# undef PyCapsule_GetPointer
+# undef PyCapsule_New
+# define PyCapsule_GetPointer(capsule, name) \
+    (PyCObject_AsVoidPtr(capsule))
+# define PyCapsule_New(pointer, name, destructor) \
+    (PyCObject_FromVoidPtr(pointer, destructor))
+#endif
+
+/************************************************************/
+
+/* base type flag: exactly one of the following: */
+#define CT_PRIMITIVE_SIGNED   0x001   /* signed integer */
+#define CT_PRIMITIVE_UNSIGNED 0x002   /* unsigned integer */
+#define CT_PRIMITIVE_CHAR     0x004   /* char, wchar_t, charN_t */
+#define CT_PRIMITIVE_FLOAT    0x008   /* float, double, long double */
+#define CT_POINTER            0x010   /* pointer, excluding ptr-to-func */
+#define CT_ARRAY              0x020   /* array */
+#define CT_STRUCT             0x040   /* struct */
+#define CT_UNION              0x080   /* union */
+#define CT_FUNCTIONPTR        0x100   /* pointer to function */
+#define CT_VOID               0x200   /* void */
+#define CT_PRIMITIVE_COMPLEX  0x400   /* float _Complex, double _Complex */
+
+/* other flags that may also be set in addition to the base flag: */
+#define CT_IS_VOIDCHAR_PTR     0x00001000
+#define CT_PRIMITIVE_FITS_LONG 0x00002000
+#define CT_IS_OPAQUE           0x00004000
+#define CT_IS_ENUM             0x00008000
+#define CT_IS_PTR_TO_OWNED     0x00010000 /* only owned if CDataOwning_Type */
+#define CT_CUSTOM_FIELD_POS    0x00020000
+#define CT_IS_LONGDOUBLE       0x00040000
+#define CT_IS_BOOL             0x00080000
+#define CT_IS_FILE             0x00100000
+#define CT_IS_VOID_PTR         0x00200000
+#define CT_WITH_VAR_ARRAY      0x00400000
+/* unused                      0x00800000 */
+#define CT_LAZY_FIELD_LIST     0x01000000
+#define CT_WITH_PACKED_CHANGE  0x02000000
+#define CT_IS_SIGNED_WCHAR     0x04000000
+#define CT_PRIMITIVE_ANY  (CT_PRIMITIVE_SIGNED |        \
+                           CT_PRIMITIVE_UNSIGNED |      \
+                           CT_PRIMITIVE_CHAR |          \
+                           CT_PRIMITIVE_FLOAT |         \
+                           CT_PRIMITIVE_COMPLEX)
+
+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(abi, ctres, ctargs..)
+                                          enum: pair {"name":x},{x:"name"}
+                                          ptrs: lazily, ctypedescr of array */
+    void *ct_extra;                    /* structs: first field (not a ref!)
+                                          function types: cif_description
+                                          primitives: prebuilt "cif" object */
+
+    PyObject *ct_weakreflist;    /* weakref support */
+
+    PyObject *ct_unique_key;    /* key in unique_cache (a string, but not
+                                   human-readable) */
+
+    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;
+                               always -1 for pointers */
+    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;
+    PyObject *c_weakreflist;
+} CDataObject;
+
+typedef struct cfieldobject_s {
+    PyObject_HEAD
+    CTypeDescrObject *cf_type;
+    Py_ssize_t cf_offset;
+    short cf_bitshift;   /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */
+    short cf_bitsize;
+    unsigned char cf_flags;   /* BF_... */
+    struct cfieldobject_s *cf_next;
+} CFieldObject;
+#define BS_REGULAR            (-1) /* a regular field, not with bitshift */
+#define BS_EMPTY_ARRAY        (-2) /* a field declared 'type[0]' or 'type[]' */
+#define BF_IGNORE_IN_CTOR     0x01 /* union field not in the first place */
+
+static PyTypeObject CTypeDescr_Type;
+static PyTypeObject CField_Type;
+static PyTypeObject CData_Type;
+static PyTypeObject CDataOwning_Type;
+static PyTypeObject CDataOwningGC_Type;
+static PyTypeObject CDataGCP_Type;
+
+#define CTypeDescr_Check(ob)  (Py_TYPE(ob) == &CTypeDescr_Type)
+#define CData_Check(ob)       (Py_TYPE(ob) == &CData_Type ||            \
+                               Py_TYPE(ob) == &CDataOwning_Type ||      \
+                               Py_TYPE(ob) == &CDataOwningGC_Type ||    \
+                               Py_TYPE(ob) == &CDataGCP_Type)
+#define CDataOwn_Check(ob)    (Py_TYPE(ob) == &CDataOwning_Type ||      \
+                               Py_TYPE(ob) == &CDataOwningGC_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;
+    long double m_longdouble;
+} union_alignment;
+
+typedef struct {
+    CDataObject head;
+    union_alignment alignment;
+} CDataObject_casted_primitive;
+
+typedef struct {
+    CDataObject head;
+    union_alignment alignment;
+} CDataObject_own_nolength;
+
+typedef struct {
+    CDataObject head;
+    Py_ssize_t length;
+    union_alignment alignment;
+} CDataObject_own_length;
+
+typedef struct {
+    CDataObject head;
+    PyObject *structobj;
+} CDataObject_own_structptr;
+
+typedef struct {
+    CDataObject head;
+    Py_ssize_t length;     /* same as CDataObject_own_length up to here */
+    Py_buffer *bufferview;
+} CDataObject_owngc_frombuf;
+
+typedef struct {
+    CDataObject head;
+    Py_ssize_t length;     /* same as CDataObject_own_length up to here */
+    PyObject *origobj;
+    PyObject *destructor;
+} CDataObject_gcp;
+
+typedef struct {
+    CDataObject head;
+    ffi_closure *closure;
+} CDataObject_closure;
+
+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;
+
+#define ADD_WRAPAROUND(x, y)  ((Py_ssize_t)(((size_t)(x)) + ((size_t)(y))))
+#define MUL_WRAPAROUND(x, y)  ((Py_ssize_t)(((size_t)(x)) * ((size_t)(y))))
+
+
+/* whenever running Python code, the errno is saved in this thread-local
+   variable */
+#ifndef MS_WIN32
+# include "misc_thread_posix.h"
+#endif
+
+#include "minibuffer.h"
+
+#if PY_MAJOR_VERSION >= 3
+# include "file_emulator.h"
+#endif
+
+#ifdef PyUnicode_KIND     /* Python >= 3.3 */
+# include "wchar_helper_3.h"
+#else
+# include "wchar_helper.h"
+#endif
+
+#include "../cffi/_cffi_errors.h"
+
+typedef struct _cffi_allocator_s {
+    PyObject *ca_alloc, *ca_free;
+    int ca_dont_clear;
+} cffi_allocator_t;
+static const cffi_allocator_t default_allocator = { NULL, NULL, 0 };
+static PyObject *FFIError;
+static PyObject *unique_cache;
+
+/************************************************************/
+
+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;
+    ct->ct_unique_key = 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 PyText_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);
+
+    if (ct->ct_unique_key != NULL) {
+        /* revive dead object temporarily for DelItem */
+        Py_REFCNT(ct) = 43;
+        PyDict_DelItem(unique_cache, ct->ct_unique_key);
+        assert(Py_REFCNT(ct) == 42);
+        Py_REFCNT(ct) = 0;
+        Py_DECREF(ct->ct_unique_key);
+    }
+    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 PyObject *nosuchattr(const char *attr)
+{
+    PyErr_SetString(PyExc_AttributeError, attr);
+    return NULL;
+}
+
+static PyObject *ctypeget_kind(CTypeDescrObject *ct, void *context)
+{
+    char *result;
+    if (ct->ct_flags & CT_PRIMITIVE_ANY) {
+        if (ct->ct_flags & CT_IS_ENUM)
+            result = "enum";
+        else
+            result = "primitive";
+    }
+    else if (ct->ct_flags & CT_POINTER) {
+        result = "pointer";
+    }
+    else if (ct->ct_flags & CT_ARRAY) {
+        result = "array";
+    }
+    else if (ct->ct_flags & CT_VOID) {
+        result = "void";
+    }
+    else if (ct->ct_flags & CT_STRUCT) {
+        result = "struct";
+    }
+    else if (ct->ct_flags & CT_UNION) {
+        result = "union";
+    }
+    else if (ct->ct_flags & CT_FUNCTIONPTR) {
+        result = "function";
+    }
+    else
+        result = "?";
+
+    return PyText_FromString(result);
+}
+
+static PyObject *ctypeget_cname(CTypeDescrObject *ct, void *context)
+{
+    return PyText_FromString(ct->ct_name);
+}
+
+static PyObject *ctypeget_item(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & (CT_POINTER | CT_ARRAY)) {
+        Py_INCREF(ct->ct_itemdescr);
+        return (PyObject *)ct->ct_itemdescr;
+    }
+    return nosuchattr("item");
+}
+
+static PyObject *ctypeget_length(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_ARRAY) {
+        if (ct->ct_length >= 0) {
+            return PyInt_FromSsize_t(ct->ct_length);
+        }
+        else {
+            Py_INCREF(Py_None);
+            return Py_None;
+        }
+    }
+    return nosuchattr("length");
+}
+
+static PyObject *
+get_field_name(CTypeDescrObject *ct, CFieldObject *cf);   /* forward */
+
+/* returns 0 if the struct ctype is opaque, 1 if it is not, or -1 if
+   an exception occurs */
+#define force_lazy_struct(ct)                                           \
+    ((ct)->ct_stuff != NULL ? 1 : do_realize_lazy_struct(ct))
+
+static int do_realize_lazy_struct(CTypeDescrObject *ct);
+/* forward, implemented in realize_c_type.c */
+
+static PyObject *ctypeget_fields(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & (CT_STRUCT | CT_UNION)) {
+        if (!(ct->ct_flags & CT_IS_OPAQUE)) {
+            CFieldObject *cf;
+            PyObject *res;
+            if (force_lazy_struct(ct) < 0)
+                return NULL;
+            res = PyList_New(0);
+            if (res == NULL)
+                return NULL;
+            for (cf = (CFieldObject *)ct->ct_extra;
+                 cf != NULL; cf = cf->cf_next) {
+                PyObject *o = PyTuple_Pack(2, get_field_name(ct, cf),
+                                           (PyObject *)cf);
+                int err = (o != NULL) ? PyList_Append(res, o) : -1;
+                Py_XDECREF(o);
+                if (err < 0) {
+                    Py_DECREF(res);
+                    return NULL;
+                }
+            }
+            return res;
+        }
+        else {
+            Py_INCREF(Py_None);
+            return Py_None;
+        }
+    }
+    return nosuchattr("fields");
+}
+
+static PyObject *ctypeget_args(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *t = ct->ct_stuff;
+        return PyTuple_GetSlice(t, 2, PyTuple_GET_SIZE(t));
+    }
+    return nosuchattr("args");
+}
+
+static PyObject *ctypeget_result(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1);
+        Py_XINCREF(res);
+        return res;
+    }
+    return nosuchattr("result");
+}
+
+static PyObject *ctypeget_ellipsis(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *res = ct->ct_extra ? Py_False : Py_True;
+        Py_INCREF(res);
+        return res;
+    }
+    return nosuchattr("ellipsis");
+}
+
+static PyObject *ctypeget_abi(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0);
+        Py_XINCREF(res);
+        return res;
+    }
+    return nosuchattr("abi");
+}
+
+static PyObject *ctypeget_elements(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_IS_ENUM) {
+        PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1);
+        if (res) res = PyDict_Copy(res);
+        return res;
+    }
+    return nosuchattr("elements");
+}
+
+static PyObject *ctypeget_relements(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_IS_ENUM) {
+        PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0);
+        if (res) res = PyDict_Copy(res);
+        return res;
+    }
+    return nosuchattr("relements");
+}
+
+static PyGetSetDef ctypedescr_getsets[] = {
+    {"kind", (getter)ctypeget_kind, NULL, "kind"},
+    {"cname", (getter)ctypeget_cname, NULL, "C name"},
+    {"item", (getter)ctypeget_item, NULL, "pointer to, or array of"},
+    {"length", (getter)ctypeget_length, NULL, "array length or None"},
+    {"fields", (getter)ctypeget_fields, NULL, "struct or union fields"},
+    {"args", (getter)ctypeget_args, NULL, "function argument types"},
+    {"result", (getter)ctypeget_result, NULL, "function result type"},
+    {"ellipsis", (getter)ctypeget_ellipsis, NULL, "function has '...'"},
+    {"abi", (getter)ctypeget_abi, NULL, "function ABI"},
+    {"elements", (getter)ctypeget_elements, NULL, "enum elements"},
+    {"relements", (getter)ctypeget_relements, NULL, "enum elements, reverse"},
+    {NULL}                        /* sentinel */
+};
+
+static PyObject *
+ctypedescr_dir(PyObject *ct, PyObject *noarg)
+{
+    int err;
+    struct PyGetSetDef *gsdef;
+    PyObject *res = PyList_New(0);
+    if (res == NULL)
+        return NULL;
+
+    for (gsdef = ctypedescr_getsets; gsdef->name; gsdef++) {
+        PyObject *x = PyObject_GetAttrString(ct, gsdef->name);
+        if (x == NULL) {
+            PyErr_Clear();
+        }
+        else {
+            Py_DECREF(x);
+            x = PyText_FromString(gsdef->name);
+            err = (x != NULL) ? PyList_Append(res, x) : -1;
+            Py_XDECREF(x);
+            if (err < 0) {
+                Py_DECREF(res);
+                return NULL;
+            }
+        }
+    }
+    return res;
+}
+
+static PyMethodDef ctypedescr_methods[] = {
+    {"__dir__",   ctypedescr_dir,  METH_NOARGS},
+    {NULL,        NULL}           /* sentinel */
+};
+
+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 */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    ctypedescr_methods,                         /* tp_methods */
+    0,                                          /* tp_members */
+    ctypedescr_getsets,                         /* tp_getset */
+};
+
+/************************************************************/
+
+static PyObject *
+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 d_key;
+    }
+    Py_FatalError("_cffi_backend: get_field_name()");
+    return NULL;
+}
+
+static void
+cfield_dealloc(CFieldObject *cf)
+{
+    Py_DECREF(cf->cf_type);
+    PyObject_Del(cf);
+}
+
+#undef OFF
+#define OFF(x) offsetof(CFieldObject, x)
+
+static PyMemberDef cfield_members[] = {
+    {"type", T_OBJECT, OFF(cf_type), READONLY},
+    {"offset", T_PYSSIZET, OFF(cf_offset), READONLY},
+    {"bitshift", T_SHORT, OFF(cf_bitshift), READONLY},
+    {"bitsize", T_SHORT, OFF(cf_bitsize), READONLY},
+    {"flags", T_UBYTE, OFF(cf_flags), READONLY},
+    {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 */
+    0,                                          /* 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 int
+CDataObject_Or_PyFloat_Check(PyObject *ob)
+{
+    return (PyFloat_Check(ob) ||
+            (CData_Check(ob) &&
+             (((CDataObject *)ob)->c_type->ct_flags & CT_PRIMITIVE_FLOAT)));
+}
+
+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 PY_MAJOR_VERSION < 3
+    if (PyInt_Check(ob)) {
+        return PyInt_AS_LONG(ob);
+    }
+    else 
+#endif
+    if (PyLong_Check(ob)) {
+        return PyLong_AsLongLong(ob);
+    }
+    else {
+        PyObject *io;
+        PY_LONG_LONG res;
+        PyNumberMethods *nb = ob->ob_type->tp_as_number;
+
+        if (CDataObject_Or_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 (PyIntOrLong_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 PY_MAJOR_VERSION < 3
+    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
+#endif
+    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 && CDataObject_Or_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 (PyIntOrLong_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;
+}
+
+#define _read_raw_data(type)                    \
+    do {                                        \
+        if (size == sizeof(type)) {             \
+            type r;                             \
+            memcpy(&r, target, sizeof(type));   \
+            return r;                           \
+        }                                       \
+    } while(0)
+
+static PY_LONG_LONG
+read_raw_signed_data(char *target, int size)
+{
+    _read_raw_data(signed char);
+    _read_raw_data(short);
+    _read_raw_data(int);
+    _read_raw_data(long);
+    _read_raw_data(PY_LONG_LONG);
+    Py_FatalError("read_raw_signed_data: bad integer size");
+    return 0;
+}
+
+static unsigned PY_LONG_LONG
+read_raw_unsigned_data(char *target, int size)
+{
+    _read_raw_data(unsigned char);
+    _read_raw_data(unsigned short);
+    _read_raw_data(unsigned int);
+    _read_raw_data(unsigned long);
+    _read_raw_data(unsigned PY_LONG_LONG);
+    Py_FatalError("read_raw_unsigned_data: bad integer size");
+    return 0;
+}
+
+#ifdef __GNUC__
+/* This is a workaround for what I think is a GCC bug on several
+   platforms.  See issue #378. */
+__attribute__((noinline))
+#endif
+void _cffi_memcpy(char *target, const void *src, size_t size)
+{
+    memcpy(target, src, size);
+}
+
+#define _write_raw_data(type)                           \
+    do {                                                \
+        if (size == sizeof(type)) {                     \
+            type r = (type)source;                      \
+            _cffi_memcpy(target, &r, sizeof(type));           \
+            return;                                     \
+        }                                               \
+    } while(0)
+
+static void
+write_raw_integer_data(char *target, unsigned PY_LONG_LONG source, int size)
+{
+    _write_raw_data(unsigned char);
+    _write_raw_data(unsigned short);
+    _write_raw_data(unsigned int);
+    _write_raw_data(unsigned long);
+    _write_raw_data(unsigned PY_LONG_LONG);
+    Py_FatalError("write_raw_integer_data: bad integer size");
+}
+
+static double
+read_raw_float_data(char *target, int size)
+{
+    _read_raw_data(float);
+    _read_raw_data(double);
+    Py_FatalError("read_raw_float_data: bad float size");
+    return 0;
+}
+
+static long double
+read_raw_longdouble_data(char *target)
+{
+    int size = sizeof(long double);
+    _read_raw_data(long double);
+    Py_FatalError("read_raw_longdouble_data: bad long double size");
+    return 0;
+}
+
+static Py_complex
+read_raw_complex_data(char *target, int size)
+{
+    Py_complex r = {0.0, 0.0};
+    if (size == 2*sizeof(float)) {
+        float real_part, imag_part;
+        memcpy(&real_part, target + 0,             sizeof(float));
+        memcpy(&imag_part, target + sizeof(float), sizeof(float));
+        r.real = real_part;
+        r.imag = imag_part;
+        return r;
+    }
+    if (size == 2*sizeof(double)) {
+        memcpy(&r, target, 2*sizeof(double));
+        return r;
+    }
+    Py_FatalError("read_raw_complex_data: bad complex size");
+    return r;
+}
+
+static void
+write_raw_float_data(char *target, double source, int size)
+{
+    _write_raw_data(float);
+    _write_raw_data(double);
+    Py_FatalError("write_raw_float_data: bad float size");
+}
+
+static void
+write_raw_longdouble_data(char *target, long double source)
+{
+    int size = sizeof(long double);
+    _write_raw_data(long double);
+}
+
+#define _write_raw_complex_data(type)                      \
+    do {                                                   \
+        if (size == 2*sizeof(type)) {                      \
+            type r = (type)source.real;                    \
+            type i = (type)source.imag;                    \
+            _cffi_memcpy(target, &r, sizeof(type));              \
+            _cffi_memcpy(target+sizeof(type), &i, sizeof(type)); \
+            return;                                        \
+        }                                                  \
+    } while(0)
+
+static void
+write_raw_complex_data(char *target, Py_complex source, int size)
+{
+    _write_raw_complex_data(float);
+    _write_raw_complex_data(double);
+    Py_FatalError("write_raw_complex_data: bad complex 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;
+    cd->c_weakreflist = NULL;
+    return (PyObject *)cd;
+}
+
+static PyObject *
+new_sized_cdata(char *data, CTypeDescrObject *ct, Py_ssize_t length)
+{
+    CDataObject_own_length *scd;
+
+    scd = (CDataObject_own_length *)PyObject_Malloc(
+        offsetof(CDataObject_own_length, alignment));
+    if (PyObject_Init((PyObject *)scd, &CData_Type) == NULL)
+        return NULL;
+    Py_INCREF(ct);
+    scd->head.c_type = ct;
+    scd->head.c_data = data;
+    scd->head.c_weakreflist = NULL;
+    scd->length = length;
+    return (PyObject *)scd;
+}
+
+static CDataObject *_new_casted_primitive(CTypeDescrObject *ct);  /*forward*/
+
+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;
+            /*READ(data, sizeof(char *))*/
+            return new_simple_cdata(ptrdata, ct);
+        }
+        else if (ct->ct_flags & CT_IS_OPAQUE) {
+            PyErr_Format(PyExc_TypeError, "cdata '%s' is opaque",
+                         ct->ct_name);
+            return NULL;
+        }
+        else if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
+            return new_simple_cdata(data, ct);
+        }
+        else if (ct->ct_flags & CT_ARRAY) {
+            if (ct->ct_length < 0) {
+                /* we can't return a <cdata 'int[]'> here, because we don't
+                   know the length to give it.  As a compromize, returns
+                   <cdata 'int *'> in this case. */
+                ct = (CTypeDescrObject *)ct->ct_stuff;
+            }
+            return new_simple_cdata(data, ct);
+        }
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_SIGNED) {
+        PY_LONG_LONG value;
+        /*READ(data, ct->ct_size)*/
+        value = read_raw_signed_data(data, ct->ct_size);
+        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(data, ct->ct_size)*/
+        value = read_raw_unsigned_data(data, ct->ct_size);
+
+        if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) {
+            if (ct->ct_flags & CT_IS_BOOL) {
+                PyObject *x;
+                switch ((int)value) {
+                case 0: x = Py_False; break;
+                case 1: x = Py_True; break;
+                default:
+                    PyErr_Format(PyExc_ValueError,
+                                 "got a _Bool of value %d, expected 0 or 1",
+                                 (int)value);
+                    return NULL;
+                }
+                Py_INCREF(x);
+                return x;
+            }
+            return PyInt_FromLong((long)value);
+        }
+        else
+            return PyLong_FromUnsignedLongLong(value);
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
+        /*READ(data, ct->ct_size)*/
+        if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) {
+            double value = read_raw_float_data(data, ct->ct_size);
+            return PyFloat_FromDouble(value);
+        }
+        else {
+            long double value = read_raw_longdouble_data(data);
+            CDataObject *cd = _new_casted_primitive(ct);
+            if (cd != NULL)
+                write_raw_longdouble_data(cd->c_data, value);
+            return (PyObject *)cd;
+        }
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
+        /*READ(data, ct->ct_size)*/
+        switch (ct->ct_size) {
+        case sizeof(char):
+            return PyBytes_FromStringAndSize(data, 1);
+        case 2:
+            return _my_PyUnicode_FromChar16((cffi_char16_t *)data, 1);
+        case 4:
+            return _my_PyUnicode_FromChar32((cffi_char32_t *)data, 1);
+        }
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) {
+        Py_complex value = read_raw_complex_data(data, ct->ct_size);
+        return PyComplex_FromCComplex(value);
+    }
+
+    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;
+    /*READ(data, ct->ct_size)*/
+
+    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'",
+                 PyText_AS_UTF8(s), ct_name);
+    Py_DECREF(s);
+    return -1;
+}
+
+static int _convert_to_char(PyObject *init)
+{
+    if (PyBytes_Check(init) && PyBytes_GET_SIZE(init) == 1) {
+        return (unsigned char)(PyBytes_AS_STRING(init)[0]);
+    }
+    if (CData_Check(init) &&
+           (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) &&
+           (((CDataObject *)init)->c_type->ct_size == sizeof(char))) {
+        char *data = ((CDataObject *)init)->c_data;
+        /*READ(data, 1)*/
+        return *(unsigned char *)data;
+    }
+    PyErr_Format(PyExc_TypeError,
+                 "initializer for ctype 'char' must be a "STR_OR_BYTES
+                 " of length 1, not %.200s", Py_TYPE(init)->tp_name);
+    return -1;
+}
+
+static cffi_char16_t _convert_to_char16_t(PyObject *init)
+{
+    char err_got[80];
+    err_got[0] = 0;
+
+    if (PyUnicode_Check(init)) {
+        cffi_char16_t ordinal;
+        if (_my_PyUnicode_AsSingleChar16(init, &ordinal, err_got) == 0)
+            return ordinal;
+    }
+    if (CData_Check(init) &&
+           (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) &&
+           (((CDataObject *)init)->c_type->ct_size == 2)) {
+        char *data = ((CDataObject *)init)->c_data;
+        /*READ(data, 2)*/
+        return *(cffi_char16_t *)data;
+    }
+    PyErr_Format(PyExc_TypeError,
+                 "initializer for ctype 'char16_t' must be a unicode string "
+                 "of length 1, not %.200s",
+                 err_got[0] == 0 ? Py_TYPE(init)->tp_name : err_got);
+    return (cffi_char16_t)-1;
+}
+
+static cffi_char32_t _convert_to_char32_t(PyObject *init)
+{
+    char err_got[80];
+    err_got[0] = 0;
+
+    if (PyUnicode_Check(init)) {
+        cffi_char32_t ordinal;
+        if (_my_PyUnicode_AsSingleChar32(init, &ordinal, err_got) == 0)
+            return ordinal;
+    }
+    if (CData_Check(init) &&
+           (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) &&
+           (((CDataObject *)init)->c_type->ct_size == 4)) {
+        char *data = ((CDataObject *)init)->c_data;
+        /*READ(data, 4)*/
+        return *(cffi_char32_t *)data;
+    }
+    PyErr_Format(PyExc_TypeError,
+                 "initializer for ctype 'char32_t' must be a unicode string "
+                 "of length 1, not %.200s",
+                 err_got[0] == 0 ? Py_TYPE(init)->tp_name : err_got);
+    return (cffi_char32_t)-1;
+}
+
+static int _convert_error(PyObject *init, CTypeDescrObject *ct,
+                          const char *expected)
+{
+    if (CData_Check(init)) {
+        CTypeDescrObject *ct2 = ((CDataObject *)init)->c_type;
+        if (strcmp(ct->ct_name, ct2->ct_name) != 0)
+            PyErr_Format(PyExc_TypeError,
+                         "initializer for ctype '%s' must be a %s, "
+                         "not cdata '%s'",
+                         ct->ct_name, expected, ct2->ct_name);
+        else if (ct != ct2) {
+            /* in case we'd give the error message "initializer for
+               ctype 'A' must be a pointer to same type, not cdata
+               'B'", but with A=B, then give instead a different error
+               message to try to clear up the confusion */
+            PyErr_Format(PyExc_TypeError,
+                         "initializer for ctype '%s' appears indeed to be '%s',"
+                         " but the types are different (check that you are not"
+                         " e.g. mixing up different ffi instances)",
+                         ct->ct_name, ct2->ct_name);
+        }
+        else
+        {
+            PyErr_Format(PyExc_SystemError,
+                         "initializer for ctype '%s' is correct, but we get "
+                         "an internal mismatch--please report a bug",
+                         ct->ct_name);
+        }
+    }
+    else
+        PyErr_Format(PyExc_TypeError,
+                     "initializer for ctype '%s' must be a %s, "
+                     "not %.200s",
+                     ct->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 Py_ssize_t
+get_new_array_length(CTypeDescrObject *ctitem, PyObject **pvalue)
+{
+    PyObject *value = *pvalue;
+
+    if (PyList_Check(value) || PyTuple_Check(value)) {
+        return PySequence_Fast_GET_SIZE(value);
+    }
+    else if (PyBytes_Check(value)) {
+        /* from a string, we add the null terminator */
+        return PyBytes_GET_SIZE(value) + 1;
+    }
+    else if (PyUnicode_Check(value)) {
+        /* from a unicode, we add the null terminator */
+        int length;
+        if (ctitem->ct_size == 2)
+            length = _my_PyUnicode_SizeAsChar16(value);
+        else
+            length = _my_PyUnicode_SizeAsChar32(value);
+        return length + 1;
+    }
+    else {
+        Py_ssize_t explicitlength;
+        explicitlength = PyNumber_AsSsize_t(value, PyExc_OverflowError);
+        if (explicitlength < 0) {
+            if (PyErr_Occurred()) {
+                if (PyErr_ExceptionMatches(PyExc_TypeError))
+                    PyErr_Format(PyExc_TypeError,
+                        "expected new array length or list/tuple/str, "
+                        "not %.200s", Py_TYPE(value)->tp_name);
+            }
+            else
+                PyErr_SetString(PyExc_ValueError, "negative array length");
+            return -1;
+        }
+        *pvalue = Py_None;
+        return explicitlength;
+    }
+}
+
+static int
+convert_field_from_object(char *data, CFieldObject *cf, PyObject *value)
+{
+    data += cf->cf_offset;
+    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_vfield_from_object(char *data, CFieldObject *cf, PyObject *value,
+                           Py_ssize_t *optvarsize)
+{
+    /* a special case for var-sized C99 arrays */
+    if ((cf->cf_type->ct_flags & CT_ARRAY) && cf->cf_type->ct_size < 0) {
+        Py_ssize_t varsizelength = get_new_array_length(
+                                      cf->cf_type->ct_itemdescr, &value);
+        if (varsizelength < 0)
+            return -1;
+        if (optvarsize != NULL) {
+            /* in this mode, the only purpose of this function is to compute
+               the real size of the structure from a var-sized C99 array */
+            Py_ssize_t size, itemsize;
+            assert(data == NULL);
+            itemsize = cf->cf_type->ct_itemdescr->ct_size;
+            size = ADD_WRAPAROUND(cf->cf_offset,
+                                  MUL_WRAPAROUND(itemsize, varsizelength));
+            if (size < 0 ||
+                ((size - cf->cf_offset) / itemsize) != varsizelength) {
+                PyErr_SetString(PyExc_OverflowError,
+                                "array size would overflow a Py_ssize_t");
+                return -1;
+            }
+            if (size > *optvarsize)
+                *optvarsize = size;
+            return 0;
+        }
+        /* if 'value' was only an integer, get_new_array_length() returns
+           it and convert 'value' to be None.  Detect if this was the case,
+           and if so, stop here, leaving the content uninitialized
+           (it should be zero-initialized from somewhere else). */
+        if (value == Py_None)
+            return 0;
+    }
+    if (optvarsize == NULL)
+        return convert_field_from_object(data, cf, value);
+    else
+        return 0;
+}
+
+static int
+must_be_array_of_zero_or_one(const char *data, Py_ssize_t n)
+{
+    Py_ssize_t i;
+    for (i = 0; i < n; i++) {
+        if (((unsigned char)data[i]) > 1) {
+            PyErr_SetString(PyExc_ValueError,
+                "an array of _Bool can only contain \\x00 or \\x01");
+            return -1;
+        }
+    }
+    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
+convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
+{
+    /* used by convert_from_object(), and also to decode lists/tuples/unicodes
+       passed as function arguments.  'ct' is an CT_ARRAY in the first case
+       and a CT_POINTER in the second case. */
+    const char *expected;
+    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) ||
+             ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED))
+              && (ctitem->ct_size == sizeof(char)))) {
+        if (ctitem->ct_size == sizeof(char)) {
+            char *srcdata;
+            Py_ssize_t n;
+            if (!PyBytes_Check(init)) {
+                expected = STR_OR_BYTES" or list or tuple";
+                goto cannot_convert;
+            }
+            n = PyBytes_GET_SIZE(init);
+            if (ct->ct_length >= 0 && n > ct->ct_length) {
+                PyErr_Format(PyExc_IndexError,
+                             "initializer "STR_OR_BYTES" is too long for '%s' "
+                             "(got %zd characters)", ct->ct_name, n);
+                return -1;
+            }
+            if (n != ct->ct_length)
+                n++;
+            srcdata = PyBytes_AS_STRING(init);
+            if (ctitem->ct_flags & CT_IS_BOOL)
+                if (must_be_array_of_zero_or_one(srcdata, n) < 0)
+                    return -1;
+            memcpy(data, srcdata, n);
+            return 0;
+        }
+        else {
+            Py_ssize_t n;
+            if (!PyUnicode_Check(init)) {
+                expected = "unicode or list or tuple";
+                goto cannot_convert;
+            }
+
+            if (ctitem->ct_size == 4)
+                n = _my_PyUnicode_SizeAsChar32(init);
+            else
+                n = _my_PyUnicode_SizeAsChar16(init);
+
+            if (ct->ct_length >= 0 && n > ct->ct_length) {
+                PyErr_Format(PyExc_IndexError,
+                             "initializer unicode is too long for '%s' "
+                             "(got %zd characters)", ct->ct_name, n);
+                return -1;
+            }
+            if (n != ct->ct_length)
+                n++;
+            if (ctitem->ct_size == 4)
+                return _my_PyUnicode_AsChar32(init, (cffi_char32_t *)data, n);
+            else
+                return _my_PyUnicode_AsChar16(init, (cffi_char16_t *)data, n);
+        }
+    }
+    else {
+        expected = "list or tuple";
+        goto cannot_convert;
+    }
+
+ cannot_convert:
+    if ((ct->ct_flags & CT_ARRAY) && CData_Check(init))
+    {
+        CDataObject *cd = (CDataObject *)init;
+        if (cd->c_type == ct)
+        {
+            Py_ssize_t n = get_array_length(cd);
+            memcpy(data, cd->c_data, n * ctitem->ct_size);
+            return 0;
+        }
+    }
+    return _convert_error(init, ct, expected);
+}
+
+static int
+convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init,
+                           Py_ssize_t *optvarsize)
+{
+    /* does not accept 'init' being already a CData */
+    const char *expected;
+
+    if (force_lazy_struct(ct) <= 0) {
+        if (!PyErr_Occurred())
+            PyErr_Format(PyExc_TypeError, "'%s' is opaque", ct->ct_name);
+        return -1;
+    }
+
+    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++) {
+            while (cf != NULL && (cf->cf_flags & BF_IGNORE_IN_CTOR))
+                cf = cf->cf_next;
+            if (cf == NULL) {
+                PyErr_Format(PyExc_ValueError,
+                             "too many initializers for '%s' (got %zd)",
+                             ct->ct_name, n);
+                return -1;
+            }
+            if (convert_vfield_from_object(data, cf, items[i], optvarsize) < 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_vfield_from_object(data, cf, d_value, optvarsize) < 0)
+                return -1;
+        }
+        return 0;
+    }
+    expected = optvarsize == NULL ? "list or tuple or dict or struct-cdata"
+                                  : "list or tuple or dict";
+    return _convert_error(init, ct, expected);
+}
+
+#ifdef __GNUC__
+# if __GNUC__ >= 4
+/* Don't go inlining this huge function.  Needed because occasionally
+   it gets inlined in places where is causes a warning: call to
+   __builtin___memcpy_chk will always overflow destination buffer
+   (which is places where the 'ct' should never represent such a large
+   primitive type anyway). */
+__attribute__((noinline))
+# endif
+#endif
+static int
+convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
+{
+    const char *expected;
+    char buf[sizeof(PY_LONG_LONG)];
+
+    /*if (ct->ct_size > 0)*/
+        /*WRITE(data, ct->ct_size)*/
+
+    if (ct->ct_flags & CT_ARRAY) {
+        return convert_array_from_object(data, ct, init);
+    }
+    if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) {
+        char *ptrdata;
+        CTypeDescrObject *ctinit;
+
+        if (!CData_Check(init)) {
+            expected = "cdata pointer";
+            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 {
+                expected = "pointer or array";
+                goto cannot_convert;
+            }
+        }
+        if (ctinit != ct) {
+            int combined_flags = ct->ct_flags | ctinit->ct_flags;
+            if (combined_flags & CT_IS_VOID_PTR)
+                ;   /* accept "void *" as either source or target */
+            else if (combined_flags & CT_IS_VOIDCHAR_PTR) {
+                /* for backward compatibility, accept "char *" as either
+                   source of target.  This is not what C does, though,
+                   so emit a warning that will eventually turn into an
+                   error.  The warning is turned off if both types are
+                   pointers to single bytes. */
+                char *msg = (ct->ct_flags & CT_IS_VOIDCHAR_PTR ?
+                    "implicit cast to 'char *' from a different pointer type: "
+                    "will be forbidden in the future (check that the types "
+                    "are as you expect; use an explicit ffi.cast() if they "
+                    "are correct)" :
+                    "implicit cast from 'char *' to a different pointer type: "
+                    "will be forbidden in the future (check that the types "
+                    "are as you expect; use an explicit ffi.cast() if they "
+                    "are correct)");
+                if ((ct->ct_flags & ctinit->ct_flags & CT_POINTER) &&
+                    ct->ct_itemdescr->ct_size == 1 &&
+                    ctinit->ct_itemdescr->ct_size == 1) {
+                    /* no warning */
+                }
+                else if (PyErr_WarnEx(PyExc_UserWarning, msg, 1))
+                    return -1;
+            }
+            else {
+                expected = "pointer to same type";
+                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())
+            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;
+        if (ct->ct_flags & CT_IS_BOOL) {
+            if (value > 1ULL)      /* value != 0 && value != 1 */
+                goto overflow;
+        }
+        else {
+            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;
+        if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+                CData_Check(init) &&
+                (((CDataObject *)init)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            long double lvalue;
+            char *initdata = ((CDataObject *)init)->c_data;
+            /*READ(initdata, sizeof(long double))*/
+            lvalue = read_raw_longdouble_data(initdata);
+            write_raw_longdouble_data(data, lvalue);
+            return 0;
+        }
+        value = PyFloat_AsDouble(init);
+        if (value == -1.0 && PyErr_Occurred())
+            return -1;
+        if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+            write_raw_float_data(data, value, ct->ct_size);
+        else
+            write_raw_longdouble_data(data, (long double)value);
+        return 0;
+    }
+    if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
+        switch (ct->ct_size) {
+        case sizeof(char): {
+            int res = _convert_to_char(init);
+            if (res < 0)
+                return -1;
+            data[0] = res;
+            return 0;
+        }
+        case 2: {
+            cffi_char16_t res = _convert_to_char16_t(init);
+            if (res == (cffi_char16_t)-1 && PyErr_Occurred())
+                return -1;
+            *(cffi_char16_t *)data = res;
+            return 0;
+        }
+        case 4: {
+            cffi_char32_t res = _convert_to_char32_t(init);
+            if (res == (cffi_char32_t)-1 && PyErr_Occurred())
+                return -1;
+            *(cffi_char32_t *)data = res;
+            return 0;
+        }
+        }
+    }
+    if (ct->ct_flags & (CT_STRUCT|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;
+            }
+        }
+        return convert_struct_from_object(data, ct, init, NULL);
+    }
+    if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) {
+        Py_complex value = PyComplex_AsCComplex(init);
+        if (PyErr_Occurred())
+            return -1;
+        write_raw_complex_data(data, value, ct->ct_size);
+        return 0;
+    }
+    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, 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",
+                     PyText_AS_UTF8(svalue),
+                     PyText_AS_UTF8(sfmin),
+                     PyText_AS_UTF8(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;
+    /*WRITE(data, ct->ct_size)*/
+    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 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;
+        if (align == -1 && (ct->ct_flags & CT_LAZY_FIELD_LIST)) {
+            force_lazy_struct(ct);
+            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_ValueError, "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)
+{
+    if (cd->c_weakreflist != NULL)
+        PyObject_ClearWeakRefs((PyObject *) cd);
+
+    Py_DECREF(cd->c_type);
+#ifndef CFFI_MEM_LEAK     /* never release anything, tests only */
+    Py_TYPE(cd)->tp_free((PyObject *)cd);
+#endif
+}
+
+static void cdataowning_dealloc(CDataObject *cd)
+{
+    assert(!(cd->c_type->ct_flags & (CT_IS_VOID_PTR | CT_FUNCTIONPTR)));
+
+    if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+        Py_DECREF(((CDataObject_own_structptr *)cd)->structobj);
+    }
+#if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK)
+    if (cd->c_type->ct_flags & (CT_PRIMITIVE_ANY | CT_STRUCT | CT_UNION)) {
+        assert(cd->c_type->ct_size >= 0);
+        memset(cd->c_data, 0xDD, cd->c_type->ct_size);
+    }
+    else if (cd->c_type->ct_flags & CT_ARRAY) {
+        Py_ssize_t x = get_array_length(cd);
+        assert(x >= 0);
+        x *= cd->c_type->ct_itemdescr->ct_size;
+        assert(x >= 0);
+        memset(cd->c_data, 0xDD, x);
+    }
+#endif
+    cdata_dealloc(cd);
+}
+
+static void cdataowninggc_dealloc(CDataObject *cd)
+{
+    assert(!(cd->c_type->ct_flags & (CT_IS_PTR_TO_OWNED |
+                                     CT_PRIMITIVE_ANY |
+                                     CT_STRUCT | CT_UNION)));
+    PyObject_GC_UnTrack(cd);
+
+    if (cd->c_type->ct_flags & CT_IS_VOID_PTR) {        /* a handle */
+        PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
+        Py_DECREF(x);
+    }
+    else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {   /* a callback */
+        ffi_closure *closure = ((CDataObject_closure *)cd)->closure;
+        PyObject *args = (PyObject *)(closure->user_data);
+        Py_XDECREF(args);
+#ifdef CFFI_TRUST_LIBFFI
+        ffi_closure_free(closure);
+#else
+        cffi_closure_free(closure);
+#endif
+    }
+    else if (cd->c_type->ct_flags & CT_ARRAY) {         /* from_buffer */
+        Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
+        PyBuffer_Release(view);
+        PyObject_Free(view);
+    }
+    cdata_dealloc(cd);
+}
+
+static int cdataowninggc_traverse(CDataObject *cd, visitproc visit, void *arg)
+{
+    if (cd->c_type->ct_flags & CT_IS_VOID_PTR) {        /* a handle */
+        PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
+        Py_VISIT(x);
+    }
+    else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {   /* a callback */
+        ffi_closure *closure = ((CDataObject_closure *)cd)->closure;
+        PyObject *args = (PyObject *)(closure->user_data);
+        Py_VISIT(args);
+    }
+    else if (cd->c_type->ct_flags & CT_ARRAY) {         /* from_buffer */
+        Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
+        Py_VISIT(view->obj);
+    }
+    return 0;
+}
+
+static int cdataowninggc_clear(CDataObject *cd)
+{
+    if (cd->c_type->ct_flags & CT_IS_VOID_PTR) {        /* a handle */
+        CDataObject_own_structptr *cd1 = (CDataObject_own_structptr *)cd;
+        PyObject *x = cd1->structobj;
+        Py_INCREF(Py_None);
+        cd1->structobj = Py_None;
+        Py_DECREF(x);
+    }
+    else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {   /* a callback */
+        ffi_closure *closure = ((CDataObject_closure *)cd)->closure;
+        PyObject *args = (PyObject *)(closure->user_data);
+        closure->user_data = NULL;
+        Py_XDECREF(args);
+    }
+    else if (cd->c_type->ct_flags & CT_ARRAY) {         /* from_buffer */
+        Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
+        PyBuffer_Release(view);
+    }
+    return 0;
+}
+
+/* forward */
+static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb,
+                                      char *objdescr, PyObject *obj,
+                                      char *extra_error_line);
+
+
+static void gcp_finalize(PyObject *destructor, PyObject *origobj)
+{
+    /* NOTE: this decrements the reference count of the two arguments */
+
+    if (destructor != NULL) {
+        PyObject *result;
+        PyObject *error_type, *error_value, *error_traceback;
+
+        /* Save the current exception */
+        PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+        result = PyObject_CallFunctionObjArgs(destructor, origobj, NULL);
+        if (result != NULL) {
+            Py_DECREF(result);
+        }
+        else {
+            PyObject *t, *v, *tb;
+            PyErr_Fetch(&t, &v, &tb);
+            /* Don't use error capture here, because it is very much
+             * like errors at __del__(), and these ones are not captured
+             * either */
+            /* ecap = _cffi_start_error_capture(); */
+            _my_PyErr_WriteUnraisable(t, v, tb, "From callback for ffi.gc ",
+                                      origobj, NULL);
+            /* _cffi_stop_error_capture(ecap); */
+        }
+        Py_DECREF(destructor);
+
+        /* Restore the saved exception */
+        PyErr_Restore(error_type, error_value, error_traceback);
+    }
+    Py_XDECREF(origobj);
+}
+
+static void cdatagcp_finalize(CDataObject_gcp *cd)
+{
+    PyObject *destructor = cd->destructor;
+    PyObject *origobj = cd->origobj;
+    cd->destructor = NULL;
+    cd->origobj = NULL;
+    gcp_finalize(destructor, origobj);
+}
+
+static void cdatagcp_dealloc(CDataObject_gcp *cd)
+{
+    PyObject *destructor = cd->destructor;
+    PyObject *origobj = cd->origobj;
+    cdata_dealloc((CDataObject *)cd);
+
+    gcp_finalize(destructor, origobj);
+}
+
+static int cdatagcp_traverse(CDataObject_gcp *cd, visitproc visit, void *arg)
+{
+    Py_VISIT(cd->destructor);
+    Py_VISIT(cd->origobj);
+    return 0;
+}
+
+static PyObject *cdata_float(CDataObject *cd);  /*forward*/
+
+static PyObject *convert_cdata_to_enum_string(CDataObject *cd, int both)
+{
+    PyObject *d_key, *d_value;
+    CTypeDescrObject *ct = cd->c_type;
+
+    assert(ct->ct_flags & CT_IS_ENUM);
+    d_key = convert_to_object(cd->c_data, ct);
+    if (d_key == NULL)
+        return NULL;
+
+    d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key);
+    if (d_value != NULL) {
+        if (both) {
+            PyObject *o = PyObject_Str(d_key);
+            if (o == NULL)
+                d_value = NULL;
+            else {
+                d_value = PyText_FromFormat("%s: %s",
+                                            PyText_AS_UTF8(o),
+                                            PyText_AS_UTF8(d_value));
+                Py_DECREF(o);
+            }
+        }
+        else
+            Py_INCREF(d_value);
+    }
+    else
+        d_value = PyObject_Str(d_key);
+    Py_DECREF(d_key);
+    return d_value;
+}
+
+static PyObject *cdata_repr(CDataObject *cd)
+{
+    char *extra;
+    PyObject *result, *s;
+
+    if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) {
+        if (cd->c_type->ct_flags & CT_IS_ENUM) {
+            s = convert_cdata_to_enum_string(cd, 1);
+        }
+        else if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) {
+            long double lvalue;
+            char buffer[128];   /* big enough */
+            /*READ(cd->c_data, sizeof(long double)*/
+            lvalue = read_raw_longdouble_data(cd->c_data);
+            sprintf(buffer, "%LE", lvalue);
+            s = PyText_FromString(buffer);
+        }
+        else {
+            PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+            if (o == NULL)
+                return NULL;
+            s = PyObject_Repr(o);
+            Py_DECREF(o);
+        }
+    }
+    else if ((cd->c_type->ct_flags & CT_ARRAY) && cd->c_type->ct_length < 0) {
+        s = PyText_FromFormat("sliced length %zd", get_array_length(cd));
+    }
+    else {
+        if (cd->c_data != NULL) {
+            s = PyText_FromFormat("%p", cd->c_data);
+        }
+        else
+            s = PyText_FromString("NULL");
+    }
+    if (s == NULL)
+        return NULL;
+    /* it's slightly confusing to get "<cdata 'struct foo' 0x...>" because the
+       struct foo is not owned.  Trying to make it clearer, write in this
+       case "<cdata 'struct foo &' 0x...>". */
+    if (cd->c_type->ct_flags & (CT_STRUCT|CT_UNION))
+        extra = " &";
+    else
+        extra = "";
+    result = PyText_FromFormat("<cdata '%s%s' %s>",
+                               cd->c_type->ct_name, extra,
+                               PyText_AsUTF8(s));
+    Py_DECREF(s);
+    return result;
+}
+
+static PyObject *_cdata_repr2(CDataObject *cd, char *text, PyObject *x)
+{
+    PyObject *res, *s = PyObject_Repr(x);
+    if (s == NULL)
+        return NULL;
+    res = PyText_FromFormat("<cdata '%s' %s %s>",
+                            cd->c_type->ct_name, text, PyText_AsUTF8(s));
+    Py_DECREF(s);
+    return res;
+}
+
+static Py_ssize_t _cdata_var_byte_size(CDataObject *cd)
+{
+    /* If 'cd' is a 'struct foo' or 'struct foo *' allocated with
+       ffi.new(), and if the struct foo contains a varsize array,
+       then return the real allocated size.  Otherwise, return -1. */
+    if (!CDataOwn_Check(cd))
+        return -1;
+
+    if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+        cd = (CDataObject *)((CDataObject_own_structptr *)cd)->structobj;
+    }
+    if (cd->c_type->ct_flags & CT_WITH_VAR_ARRAY) {
+        return ((CDataObject_own_length *)cd)->length;
+    }
+    return -1;
+}
+
+static PyObject *cdataowning_repr(CDataObject *cd)
+{
+    Py_ssize_t size = _cdata_var_byte_size(cd);
+    if (size < 0) {
+        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
+            size = cd->c_type->ct_size;
+    }
+    return PyText_FromFormat("<cdata '%s' owning %zd bytes>",
+                             cd->c_type->ct_name, size);
+}
+
+static PyObject *cdataowninggc_repr(CDataObject *cd)
+{
+    if (cd->c_type->ct_flags & CT_IS_VOID_PTR) {        /* a handle */
+        PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
+        return _cdata_repr2(cd, "handle to", x);
+    }
+    else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {   /* a callback */
+        ffi_closure *closure = ((CDataObject_closure *)cd)->closure;
+        PyObject *args = (PyObject *)closure->user_data;
+        if (args == NULL)
+            return cdata_repr(cd);
+        else
+            return _cdata_repr2(cd, "calling", PyTuple_GET_ITEM(args, 1));
+    }
+    else if (cd->c_type->ct_flags & CT_ARRAY) {         /* from_buffer */
+        Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
+        Py_ssize_t buflen = get_array_length(cd);
+        return PyText_FromFormat(
+            "<cdata '%s' buffer len %zd from '%.200s' object>",
+            cd->c_type->ct_name,
+            buflen,
+            view->obj ? Py_TYPE(view->obj)->tp_name : "(null)");
+    }
+    return cdataowning_repr(cd);
+}
+
+static int cdata_nonzero(CDataObject *cd)
+{
+    if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) {
+        if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED |
+                                    CT_PRIMITIVE_UNSIGNED |
+                                    CT_PRIMITIVE_CHAR))
+            return read_raw_unsigned_data(cd->c_data, cd->c_type->ct_size) != 0;
+
+        if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
+            if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE)
+                return read_raw_longdouble_data(cd->c_data) != 0.0;
+            return read_raw_float_data(cd->c_data, cd->c_type->ct_size) != 0.0;
+        }
+        if (cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) {
+            Py_complex value = read_raw_complex_data(cd->c_data,
+                                                     cd->c_type->ct_size);
+            return value.real != 0.0 || value.imag != 0.0;
+        }
+    }
+    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;
+        /*READ(cd->c_data, cd->c_type->ct_size)*/
+        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) {
+        /*READ(cd->c_data, cd->c_type->ct_size)*/
+        switch (cd->c_type->ct_size) {
+        case sizeof(char):
+            return PyInt_FromLong((unsigned char)cd->c_data[0]);
+        case 2:
+            return PyInt_FromLong((long)*(cffi_char16_t *)cd->c_data);
+        case 4:
+            if (cd->c_type->ct_flags & CT_IS_SIGNED_WCHAR)
+                return PyInt_FromLong((long)*(int32_t *)cd->c_data);
+            else if (sizeof(long) > 4)
+                return PyInt_FromLong(*(uint32_t *)cd->c_data);
+            else
+                return PyLong_FromUnsignedLong(*(uint32_t *)cd->c_data);
+        }
+    }
+    else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
+        PyObject *o = cdata_float(cd);
+#if PY_MAJOR_VERSION < 3
+        PyObject *r = o ? PyNumber_Int(o) : NULL;
+#else
+        PyObject *r = o ? PyNumber_Long(o) : NULL;
+#endif
+        Py_XDECREF(o);
+        return r;
+    }
+    PyErr_Format(PyExc_TypeError, "int() not supported on cdata '%s'",
+                 cd->c_type->ct_name);
+    return NULL;
+}
+
+#if PY_MAJOR_VERSION < 3
+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;
+}
+#endif
+
+static PyObject *cdata_float(CDataObject *cd)
+{
+    if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
+        double value;
+        /*READ(cd->c_data, cd->c_type->ct_size)*/
+        if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            value = read_raw_float_data(cd->c_data, cd->c_type->ct_size);
+        }
+        else {
+            value = (double)read_raw_longdouble_data(cd->c_data);
+        }
+        return PyFloat_FromDouble(value);
+    }
+    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 v_is_ptr, w_is_ptr;
+    PyObject *pyres;
+
+    assert(CData_Check(v));
+
+    /* Comparisons involving a primitive cdata work differently than
+     * comparisons involving a struct/array/pointer.
+     *
+     * If v or w is a struct/array/pointer, then the other must be too
+     * (otherwise we return NotImplemented and leave the case to
+     * Python).  If both are, then we compare the addresses.
+     *
+     * If v and/or w is a primitive cdata, then we convert the cdata(s)
+     * to regular Python objects and redo the comparison there.
+     */
+
+    v_is_ptr = !(((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY);
+    w_is_ptr = CData_Check(w) &&
+                  !(((CDataObject *)w)->c_type->ct_flags & CT_PRIMITIVE_ANY);
+
+    if (v_is_ptr && w_is_ptr) {
+        int res;
+        char *v_cdata = ((CDataObject *)v)->c_data;
+        char *w_cdata = ((CDataObject *)w)->c_data;
+
+        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;
+    }
+    else if (v_is_ptr || w_is_ptr) {
+        pyres = Py_NotImplemented;
+    }
+    else {
+        PyObject *aa[2];
+        int i;
+
+        aa[0] = v; Py_INCREF(v);
+        aa[1] = w; Py_INCREF(w);
+        pyres = NULL;
+
+        for (i = 0; i < 2; i++) {
+            v = aa[i];
+            if (!CData_Check(v))
+                continue;
+            w = convert_to_object(((CDataObject *)v)->c_data,
+                                  ((CDataObject *)v)->c_type);
+            if (w == NULL)
+                goto error;
+            if (CData_Check(w)) {
+                Py_DECREF(w);
+                PyErr_Format(PyExc_NotImplementedError,
+                             "cannot use <cdata '%s'> in a comparison",
+                             ((CDataObject *)v)->c_type->ct_name);
+                goto error;
+            }
+            aa[i] = w;
+            Py_DECREF(v);
+        }
+        pyres = PyObject_RichCompare(aa[0], aa[1], op);
+     error:
+        Py_DECREF(aa[1]);
+        Py_DECREF(aa[0]);
+        return pyres;
+    }
+
+    Py_INCREF(pyres);
+    return pyres;
+}
+
+static long cdata_hash(CDataObject *v)
+{
+    if (((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY) {
+        PyObject *vv = convert_to_object(((CDataObject *)v)->c_data,
+                                         ((CDataObject *)v)->c_type);
+        if (vv == NULL)
+            return -1;
+        if (!CData_Check(vv)) {
+            long hash = PyObject_Hash(vv);
+            Py_DECREF(vv);
+            return hash;
+        }
+        Py_DECREF(vv);
+    }
+    return _Py_HashPointer(v->c_data);
+}
+
+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)) {
+            if (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_data == NULL) {
+                PyErr_Format(PyExc_RuntimeError,
+                             "cannot dereference null pointer from cdata '%s'",
+                             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");
+            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 *
+new_array_type(CTypeDescrObject *ctptr, Py_ssize_t length);   /* forward */
+
+static CTypeDescrObject *
+_cdata_getslicearg(CDataObject *cd, PySliceObject *slice, Py_ssize_t bounds[])
+{
+    Py_ssize_t start, stop;
+    CTypeDescrObject *ct;
+
+    start = PyInt_AsSsize_t(slice->start);
+    if (start == -1 && PyErr_Occurred()) {
+        if (slice->start == Py_None)
+            PyErr_SetString(PyExc_IndexError, "slice start must be specified");
+        return NULL;
+    }
+    stop = PyInt_AsSsize_t(slice->stop);
+    if (stop == -1 && PyErr_Occurred()) {
+        if (slice->stop == Py_None)
+            PyErr_SetString(PyExc_IndexError, "slice stop must be specified");
+        return NULL;
+    }
+    if (slice->step != Py_None) {
+        PyErr_SetString(PyExc_IndexError, "slice with step not supported");
+        return NULL;
+    }
+    if (start > stop) {
+        PyErr_SetString(PyExc_IndexError, "slice start > stop");
+        return NULL;
+    }
+
+    ct = cd->c_type;
+    if (ct->ct_flags & CT_ARRAY) {
+        if (start < 0) {
+            PyErr_SetString(PyExc_IndexError,
+                            "negative index");
+            return NULL;
+        }
+        if (stop > get_array_length(cd)) {
+            PyErr_Format(PyExc_IndexError,
+                         "index too large (expected %zd <= %zd)",
+                         stop, get_array_length(cd));
+            return NULL;
+        }
+        ct = (CTypeDescrObject *)ct->ct_stuff;
+    }
+    else if (!(ct->ct_flags & CT_POINTER)) {
+        PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed",
+                     ct->ct_name);
+        return NULL;
+    }
+
+    bounds[0] = start;
+    bounds[1] = stop - start;
+    return ct;
+}
+
+static PyObject *
+cdata_slice(CDataObject *cd, PySliceObject *slice)
+{
+    char *cdata;
+    Py_ssize_t bounds[2];
+    CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds);
+    if (ct == NULL)
+        return NULL;
+
+    if (ct->ct_stuff == NULL) {
+        ct->ct_stuff = new_array_type(ct, -1);
+        if (ct->ct_stuff == NULL)
+            return NULL;
+    }
+    ct = (CTypeDescrObject *)ct->ct_stuff;
+
+    cdata = cd->c_data + ct->ct_itemdescr->ct_size * bounds[0];
+    return new_sized_cdata(cdata, ct, bounds[1]);
+}
+
+static int
+cdata_ass_slice(CDataObject *cd, PySliceObject *slice, PyObject *v)
+{
+    Py_ssize_t bounds[2], i, length, itemsize;
+    PyObject *it, *item;
+    PyObject *(*iternext)(PyObject *);
+    char *cdata;
+    int err;
+    CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds);
+    if (ct == NULL)
+        return -1;
+    ct = ct->ct_itemdescr;
+    itemsize = ct->ct_size;
+    cdata = cd->c_data + itemsize * bounds[0];
+    length = bounds[1];
+
+    if (CData_Check(v)) {
+        CTypeDescrObject *ctv = ((CDataObject *)v)->c_type;
+        if ((ctv->ct_flags & CT_ARRAY) && (ctv->ct_itemdescr == ct) &&
+            (get_array_length((CDataObject *)v) == length)) {
+            /* fast path: copying from exactly the correct type */
+            memmove(cdata, ((CDataObject *)v)->c_data, itemsize * length);
+            return 0;
+        }
+    }
+
+    /* A fast path for <char[]>[0:N] = b"somestring" or bytearray, which
+       also adds support for Python 3: otherwise, you get integers while
+       enumerating the string, and you can't set them to characters :-/
+    */
+    if ((ct->ct_flags & CT_PRIMITIVE_CHAR) && itemsize == sizeof(char)) {
+        char *src;
+        Py_ssize_t srclen;
+        if (PyBytes_Check(v)) {
+            srclen = PyBytes_GET_SIZE(v);
+            src = PyBytes_AS_STRING(v);
+        }
+        else if (PyByteArray_Check(v)) {
+            srclen = PyByteArray_GET_SIZE(v);
+            src = PyByteArray_AS_STRING(v);
+        }
+        else
+            goto other_types;
+
+        if (srclen != length) {
+            PyErr_Format(PyExc_ValueError,
+                         "need a string of length %zd, got %zd",
+                         length, srclen);
+            return -1;
+        }
+        memcpy(cdata, src, length);
+        return 0;
+    }
+   other_types:
+
+    it = PyObject_GetIter(v);
+    if (it == NULL)
+        return -1;
+    iternext = *it->ob_type->tp_iternext;
+
+    for (i = 0; i < length; i++) {
+        item = iternext(it);
+        if (item == NULL) {
+            if (!PyErr_Occurred())
+                PyErr_Format(PyExc_ValueError,
+                             "need %zd values to unpack, got %zd",
+                             length, i);
+            goto error;
+        }
+        err = convert_from_object(cdata, ct, item);
+        Py_DECREF(item);
+        if (err < 0)
+            goto error;
+
+        cdata += itemsize;
+    }
+    item = iternext(it);
+    if (item != NULL) {
+        Py_DECREF(item);
+        PyErr_Format(PyExc_ValueError,
+                     "got more than %zd values to unpack", length);
+    }
+ error:
+    Py_DECREF(it);
+    return PyErr_Occurred() ? -1 : 0;
+}
+
+static PyObject *
+cdataowning_subscript(CDataObject *cd, PyObject *key)
+{
+    char *c;
+    if (PySlice_Check(key))
+        return cdata_slice(cd, (PySliceObject *)key);
+
+    c = _cdata_get_indexed_ptr(cd, key);
+    /* use 'mp_subscript' instead of 'sq_item' because we don't want
+       negative indexes to be corrected automatically */
+    if (c == NULL && PyErr_Occurred())
+        return NULL;
+
+    if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+        PyObject *res = ((CDataObject_own_structptr *)cd)->structobj;
+        Py_INCREF(res);
+        return res;
+    }
+    else {
+        return convert_to_object(c, cd->c_type->ct_itemdescr);
+    }
+}
+
+static PyObject *
+cdata_subscript(CDataObject *cd, PyObject *key)
+{
+    char *c;
+    if (PySlice_Check(key))
+        return cdata_slice(cd, (PySliceObject *)key);
+
+    c = _cdata_get_indexed_ptr(cd, key);
+    /* use 'mp_subscript' instead of 'sq_item' because we don't want
+       negative indexes to be corrected automatically */
+    if (c == NULL && PyErr_Occurred())
+        return NULL;
+    return convert_to_object(c, cd->c_type->ct_itemdescr);
+}
+
+static int
+cdata_ass_sub(CDataObject *cd, PyObject *key, PyObject *v)
+{
+    char *c;
+    CTypeDescrObject *ctitem;
+    if (PySlice_Check(key))
+        return cdata_ass_slice(cd, (PySliceObject *)key, v);
+
+    c = _cdata_get_indexed_ptr(cd, key);
+    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 && PyErr_Occurred())
+        return -1;
+    if (v == NULL) {
+        PyErr_SetString(PyExc_TypeError,
+                        "'del x[n]' not supported for cdata objects");
+        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, itemsize;
+    CDataObject *cd;
+    CTypeDescrObject *ctptr;
+
+    if (!CData_Check(v)) {
+        PyObject *swap;
+        assert(CData_Check(w));
+        if (sign != 1)
+            goto not_implemented;
+        swap = v;
+        v = w;
+        w = swap;
+    }
+
+    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;
+    }
+    itemsize = ctptr->ct_itemdescr->ct_size;
+    if (itemsize < 0) {
+        if (ctptr->ct_flags & CT_IS_VOID_PTR) {
+            itemsize = 1;
+        }
+        else {
+            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 * itemsize, 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, itemsize;
+
+        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 &&
+                 !(ct->ct_flags & CT_IS_VOID_PTR))) {
+            PyErr_Format(PyExc_TypeError,
+                         "cannot subtract cdata '%s' and cdata '%s'",
+                         cdv->c_type->ct_name, ct->ct_name);
+            return NULL;
+        }
+        itemsize = ct->ct_itemdescr->ct_size;
+        diff = cdv->c_data - cdw->c_data;
+        if (itemsize > 1) {
+            if (diff % itemsize) {
+                PyErr_SetString(PyExc_ValueError,
+                     "pointer subtraction: the distance between the two "
+                     "pointers is not a multiple of the item size");
+                return NULL;
+            }
+            diff = diff / itemsize;
+        }
+#if PY_MAJOR_VERSION < 3
+        return PyInt_FromSsize_t(diff);
+#else
+        return PyLong_FromSsize_t(diff);
+#endif
+    }
+
+    return _cdata_add_or_sub(v, w, -1);
+}
+
+static void
+_cdata_attr_errmsg(char *errmsg, CDataObject *cd, PyObject *attr)
+{
+    const char *text;
+    if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+        return;
+    PyErr_Clear();
+    text = PyText_AsUTF8(attr);
+    if (text == NULL)
+        return;
+    PyErr_Format(PyExc_AttributeError, errmsg, cd->c_type->ct_name, text);
+}
+
+static PyObject *
+cdata_getattro(CDataObject *cd, PyObject *attr)
+{
+    CFieldObject *cf;
+    CTypeDescrObject *ct = cd->c_type;
+    char *errmsg = "cdata '%s' has no attribute '%s'";
+    PyObject *x;
+
+    if (ct->ct_flags & CT_POINTER)
+        ct = ct->ct_itemdescr;
+
+    if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
+        switch (force_lazy_struct(ct)) {
+        case 1:
+            cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
+            if (cf != NULL) {
+                /* read the field 'cf' */
+                char *data = cd->c_data + cf->cf_offset;
+                Py_ssize_t array_len, size;
+
+                if (cf->cf_bitshift == BS_REGULAR) {
+                    return convert_to_object(data, cf->cf_type);
+                }
+                else if (cf->cf_bitshift != BS_EMPTY_ARRAY) {
+                    return convert_to_object_bitfield(data, cf);
+                }
+
+                /* variable-length array: */
+                /* if reading variable length array from variable length
+                   struct, calculate array type from allocated length */
+                size = _cdata_var_byte_size(cd) - cf->cf_offset;
+                if (size >= 0) {
+                    array_len = size / cf->cf_type->ct_itemdescr->ct_size;
+                    return new_sized_cdata(data, cf->cf_type, array_len);
+                }
+                return new_simple_cdata(data,
+                    (CTypeDescrObject *)cf->cf_type->ct_stuff);
+            }
+            errmsg = "cdata '%s' has no field '%s'";
+            break;
+        case -1:
+            return NULL;
+        default:
+            errmsg = "cdata '%s' points to an opaque type: cannot read fields";
+            break;
+        }
+    }
+    x = PyObject_GenericGetAttr((PyObject *)cd, attr);
+    if (x == NULL)
+        _cdata_attr_errmsg(errmsg, cd, attr);
+    return x;
+}
+
+static int
+cdata_setattro(CDataObject *cd, PyObject *attr, PyObject *value)
+{
+    CFieldObject *cf;
+    CTypeDescrObject *ct = cd->c_type;
+    char *errmsg = "cdata '%s' has no attribute '%s'";
+    int x;
+
+    if (ct->ct_flags & CT_POINTER)
+        ct = ct->ct_itemdescr;
+
+    if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
+        switch (force_lazy_struct(ct)) {
+        case 1:
+            cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
+            if (cf != NULL) {
+                /* write the field 'cf' */
+                if (value != NULL) {
+                    return convert_field_from_object(cd->c_data, cf, value);
+                }
+                else {
+                    PyErr_SetString(PyExc_AttributeError,
+                                    "cannot delete struct field");
+                    return -1;
+                }
+            }
+            errmsg = "cdata '%s' has no field '%s'";
+            break;
+        case -1:
+            return -1;
+        default:
+            errmsg = "cdata '%s' points to an opaque type: cannot write fields";
+            break;
+        }
+    }
+    x = PyObject_GenericSetAttr((PyObject *)cd, attr, value);
+    if (x < 0)
+        _cdata_attr_errmsg(errmsg, cd, attr);
+    return x;
+}
+
+static PyObject *
+convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/
+
+static cif_description_t *
+fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, ffi_abi);      /*forward*/
+
+static PyObject *new_primitive_type(const char *name);             /*forward*/
+
+static CTypeDescrObject *_get_ct_int(void)
+{
+    static CTypeDescrObject *ct_int = NULL;
+    if (ct_int == NULL) {
+        ct_int = (CTypeDescrObject *)new_primitive_type("int");
+    }
+    return ct_int;
+}
+
+static Py_ssize_t
+_prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init,
+                               char **output_data)
+{
+    /* 'ctptr' is here a pointer type 'ITEM *'.  Accept as argument an
+       initializer for an array 'ITEM[]'.  This includes the case of
+       passing a Python byte string to a 'char *' argument.
+
+       This function returns -1 if an error occurred,
+       0 if conversion succeeded (into *output_data),
+       or N > 0 if conversion would require N bytes of storage.
+    */
+    Py_ssize_t length, datasize;
+    CTypeDescrObject *ctitem;
+
+    if (CData_Check(init))
+        goto convert_default;
+
+    ctitem = ctptr->ct_itemdescr;
+    /* XXX some code duplication, how to avoid it? */
+    if (PyBytes_Check(init)) {
+        /* from a string: just returning the string here is fine.
+           We assume that the C code won't modify the 'char *' data. */
+        if ((ctptr->ct_flags & CT_IS_VOIDCHAR_PTR) ||
+            ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED))
+             && (ctitem->ct_size == sizeof(char)))) {
+#if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK)
+            length = PyBytes_GET_SIZE(init) + 1;
+#else
+            *output_data = PyBytes_AS_STRING(init);
+            if (ctitem->ct_flags & CT_IS_BOOL)
+                if (must_be_array_of_zero_or_one(*output_data,
+                                                 PyBytes_GET_SIZE(init)) < 0)
+                    return -1;
+            return 0;
+#endif
+        }
+        else
+            goto convert_default;
+    }
+    else if (PyList_Check(init) || PyTuple_Check(init)) {
+        length = PySequence_Fast_GET_SIZE(init);
+    }
+    else if (PyUnicode_Check(init)) {
+        /* from a unicode, we add the null terminator */
+        if (ctitem->ct_size == 2)
+            length = _my_PyUnicode_SizeAsChar16(init);
+        else
+            length = _my_PyUnicode_SizeAsChar32(init);
+        length += 1;
+    }
+    else if ((ctitem->ct_flags & CT_IS_FILE) && PyFile_Check(init)) {
+        *output_data = (char *)PyFile_AsFile(init);
+        if (*output_data == NULL && PyErr_Occurred())
+            return -1;
+        return 0;
+    }
+    else {
+        /* refuse to receive just an integer (and interpret it
+           as the array size) */
+        goto convert_default;
+    }
+
+    if (ctitem->ct_size <= 0)
+        goto convert_default;
+    datasize = MUL_WRAPAROUND(length, ctitem->ct_size);
+    if ((datasize / ctitem->ct_size) != length) {
+        PyErr_SetString(PyExc_OverflowError,
+                        "array size would overflow a Py_ssize_t");
+        return -1;
+    }
+    if (datasize <= 0)
+        datasize = 1;
+    return datasize;
+
+ convert_default:
+    return convert_from_object((char *)output_data, ctptr, init);
+}
+
+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 = NULL, *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) - 2;
+    fresult = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 1);
+    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";
+          bad_number_of_arguments:
+            PyErr_Format(PyExc_TypeError, errormsg,
+                         cd->c_type->ct_name, nargs_declared, nargs);
+            goto error;
+        }
+    }
+    else {
+        /* call of a variadic function */
+        ffi_abi fabi;
+        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, 2 + 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_PRIMITIVE_CHAR | CT_PRIMITIVE_UNSIGNED |
+                                    CT_PRIMITIVE_SIGNED)) {
+                    if (ct->ct_size < (Py_ssize_t)sizeof(int)) {
+                        ct = _get_ct_int();
+                        if (ct == NULL)
+                            goto error;
+                    }
+                }
+                else 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);
+        }
+#if PY_MAJOR_VERSION < 3
+        fabi = PyInt_AS_LONG(PyTuple_GET_ITEM(signature, 0));
+#else
+        fabi = PyLong_AS_LONG(PyTuple_GET_ITEM(signature, 0));
+#endif
+        cif_descr = fb_prepare_cif(fvarargs, fresult, fabi);
+        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, 2 + i);
+        else
+            argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i);
+
+        if (argtype->ct_flags & CT_POINTER) {
+            char *tmpbuf;
+            Py_ssize_t datasize = _prepare_pointer_call_argument(
+                                            argtype, obj, (char **)data);
+            if (datasize == 0)
+                ;    /* successfully filled '*data' */
+            else if (datasize < 0)
+                goto error;
+            else {
+                tmpbuf = alloca(datasize);
+                memset(tmpbuf, 0, datasize);
+                *(char **)data = tmpbuf;
+                if (convert_array_from_object(tmpbuf, argtype, obj) < 0)
+                    goto error;
+            }
+        }
+        else if (convert_from_object(data, argtype, obj) < 0)
+            goto error;
+    }
+
+    resultdata = buffer + cif_descr->exchange_offset_arg[0];
+    /*READ(cd->c_data, sizeof(void(*)(void)))*/
+
+    Py_BEGIN_ALLOW_THREADS
+    restore_errno();
+    ffi_call(&cif_descr->cif, (void (*)(void))(cd->c_data),
+             resultdata, buffer_array);
+    save_errno();
+    Py_END_ALLOW_THREADS
+
+    if (fresult->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED |
+                             CT_PRIMITIVE_UNSIGNED)) {
+#ifdef WORDS_BIGENDIAN
+        /* For results of precisely these types, libffi has a strange
+           rule that they will be returned as a whole 'ffi_arg' if they
+           are smaller.  The difference only matters on big-endian. */
+        if (fresult->ct_size < sizeof(ffi_arg))
+            resultdata += (sizeof(ffi_arg) - fresult->ct_size);
+#endif
+        res = convert_to_object(resultdata, fresult);
+    }
+    else if (fresult->ct_flags & CT_VOID) {
+        res = Py_None;
+        Py_INCREF(res);
+    }
+    else if (fresult->ct_flags & CT_STRUCT) {
+        res = convert_struct_to_owning_object(resultdata, fresult);
+    }
+    else {
+        res = convert_to_object(resultdata, fresult);
+    }
+    /* fall-through */
+
+ error:
+    if (buffer)
+        PyObject_Free(buffer);
+    if (fvarargs != NULL) {
+        Py_DECREF(fvarargs);
+        if (cif_descr != NULL)  /* but only if fvarargs != NULL, if variadic */
+            PyObject_Free(cif_descr);
+    }
+    return res;
+}
+
+static PyObject *cdata_dir(PyObject *cd, PyObject *noarg)
+{
+    CTypeDescrObject *ct = ((CDataObject *)cd)->c_type;
+
+    /* replace the type 'pointer-to-t' with just 't' */
+    if (ct->ct_flags & CT_POINTER) {
+        ct = ct->ct_itemdescr;
+    }
+    if ((ct->ct_flags & (CT_STRUCT | CT_UNION)) &&
+        !(ct->ct_flags & CT_IS_OPAQUE)) {
+
+        /* for non-opaque structs or unions */
+        if (force_lazy_struct(ct) < 0)
+            return NULL;
+        return PyDict_Keys(ct->ct_stuff);
+    }
+    else {
+        return PyList_New(0);   /* empty list for the other cases */
+    }
+}
+
+static PyObject *cdata_complex(PyObject *cd_, PyObject *noarg)
+{
+    CDataObject *cd = (CDataObject *)cd_;
+
+    if (cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) {
+        Py_complex value = read_raw_complex_data(cd->c_data, cd->c_type->ct_size);
+        PyObject *op = PyComplex_FromCComplex(value);
+        return op;
+    }
+    /* <cdata 'float'> or <cdata 'int'> cannot be directly converted by
+       calling complex(), just like <cdata 'int'> cannot be directly
+       converted by calling float() */
+
+    PyErr_Format(PyExc_TypeError, "complex() not supported on cdata '%s'",
+                 cd->c_type->ct_name);
+    return NULL;
+}
+
+static int explicit_release_case(PyObject *cd)
+{
+    CTypeDescrObject *ct = ((CDataObject *)cd)->c_type;
+    if (Py_TYPE(cd) == &CDataOwning_Type) {
+        if ((ct->ct_flags & (CT_POINTER | CT_ARRAY)) != 0)   /* ffi.new() */
+            return 0;
+    }
+    else if (Py_TYPE(cd) == &CDataOwningGC_Type) {
+        if (ct->ct_flags & CT_ARRAY)      /* ffi.from_buffer() */
+            return 1;
+    }
+    else if (Py_TYPE(cd) == &CDataGCP_Type) {
+        return 2;    /* ffi.gc() */
+    }
+    PyErr_SetString(PyExc_ValueError,
+        "only 'cdata' object from ffi.new(), ffi.gc(), ffi.from_buffer() "
+        "or ffi.new_allocator()() can be used with the 'with' keyword or "
+        "ffi.release()");
+    return -1;
+}
+
+static PyObject *cdata_enter(PyObject *cd, PyObject *noarg)
+{
+    if (explicit_release_case(cd) < 0)   /* only to check the ctype */
+        return NULL;
+    Py_INCREF(cd);
+    return cd;
+}
+
+static PyObject *cdata_exit(PyObject *cd, PyObject *args)
+{
+    /* 'args' ignored */
+    CTypeDescrObject *ct;
+    Py_buffer *view;
+    switch (explicit_release_case(cd))
+    {
+        case 0:    /* ffi.new() */
+            /* no effect on CPython: raw memory is allocated with the
+               same malloc() as the object itself, so it can't be
+               released independently.  If we use a custom allocator,
+               then it's implemented with ffi.gc(). */
+            ct = ((CDataObject *)cd)->c_type;
+            if (ct->ct_flags & CT_IS_PTR_TO_OWNED) {
+                PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
+                if (Py_TYPE(x) == &CDataGCP_Type) {
+                    /* this is a special case for
+                       ffi.new_allocator()("struct-or-union") */
+                    cdatagcp_finalize((CDataObject_gcp *)x);
+                }
+            }
+            break;
+
+        case 1:    /* ffi.from_buffer() */
+            view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
+            PyBuffer_Release(view);
+            break;
+
+        case 2:    /* ffi.gc() or ffi.new_allocator()("not-struct-nor-union") */
+            /* call the destructor immediately */
+            cdatagcp_finalize((CDataObject_gcp *)cd);
+            break;
+
+        default:
+            return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *cdata_iter(CDataObject *);
+
+static PyNumberMethods CData_as_number = {
+    (binaryfunc)cdata_add,      /*nb_add*/
+    (binaryfunc)cdata_sub,      /*nb_subtract*/
+    0,                          /*nb_multiply*/
+#if PY_MAJOR_VERSION < 3
+    0,                          /*nb_divide*/
+#endif
+    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*/
+#if PY_MAJOR_VERSION < 3
+    0,                          /*nb_coerce*/
+#endif
+    (unaryfunc)cdata_int,       /*nb_int*/
+#if PY_MAJOR_VERSION < 3
+    (unaryfunc)cdata_long,      /*nb_long*/
+#else
+    0,
+#endif
+    (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 PyMappingMethods CDataOwn_as_mapping = {
+    (lenfunc)cdata_length, /*mp_length*/
+    (binaryfunc)cdataowning_subscript, /*mp_subscript*/
+    (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
+};
+
+static PyMethodDef cdata_methods[] = {
+    {"__dir__",     cdata_dir,      METH_NOARGS},
+    {"__complex__", cdata_complex,  METH_NOARGS},
+    {"__enter__",   cdata_enter,    METH_NOARGS},
+    {"__exit__",    cdata_exit,     METH_VARARGS},
+    {NULL,          NULL}           /* sentinel */
+};
+
+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 */
+    0,                                          /* 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 */
+    0,                                          /* tp_traverse */
+    0,                                          /* tp_clear */
+    cdata_richcompare,                          /* tp_richcompare */
+    offsetof(CDataObject, c_weakreflist),       /* tp_weaklistoffset */
+    (getiterfunc)cdata_iter,                    /* tp_iter */
+    0,                                          /* tp_iternext */
+    cdata_methods,                              /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    0,                                          /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    0,                                          /* tp_dictoffset */
+    0,                                          /* tp_init */
+    0,                                          /* tp_alloc */
+    0,                                          /* tp_new */
+    PyObject_Del,                               /* tp_free */
+};
+
+static PyTypeObject CDataOwning_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CDataOwn",
+    sizeof(CDataObject),
+    0,
+    (destructor)cdataowning_dealloc,            /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    (reprfunc)cdataowning_repr,                 /* tp_repr */
+    0,  /* inherited */                         /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    &CDataOwn_as_mapping,                       /* tp_as_mapping */
+    0,  /* inherited */                         /* tp_hash */
+    0,  /* inherited */                         /* tp_call */
+    0,                                          /* tp_str */
+    0,  /* inherited */                         /* tp_getattro */
+    0,  /* inherited */                         /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
+    0,                                          /* tp_doc */
+    0,                                          /* tp_traverse */
+    0,                                          /* tp_clear */
+    0,  /* inherited */                         /* tp_richcompare */
+    0,  /* inherited */                         /* tp_weaklistoffset */
+    0,  /* inherited */                         /* tp_iter */
+    0,                                          /* tp_iternext */
+    0,  /* inherited */                         /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    &CData_Type,                                /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    0,                                          /* tp_dictoffset */
+    0,                                          /* tp_init */
+    0,                                          /* tp_alloc */
+    0,                                          /* tp_new */
+    free,                                       /* tp_free */
+};
+
+static PyTypeObject CDataOwningGC_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CDataOwnGC",
+    sizeof(CDataObject_owngc_frombuf),
+    0,
+    (destructor)cdataowninggc_dealloc,          /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    (reprfunc)cdataowninggc_repr,               /* tp_repr */
+    0,  /* inherited */                         /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    0,  /* inherited */                         /* tp_as_mapping */
+    0,  /* inherited */                         /* tp_hash */
+    0,  /* inherited */                         /* tp_call */
+    0,                                          /* tp_str */
+    0,  /* inherited */                         /* tp_getattro */
+    0,  /* inherited */                         /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES  /* tp_flags */
+                       | Py_TPFLAGS_HAVE_GC,
+    0,                                          /* tp_doc */
+    (traverseproc)cdataowninggc_traverse,       /* tp_traverse */
+    (inquiry)cdataowninggc_clear,               /* tp_clear */
+    0,  /* inherited */                         /* tp_richcompare */
+    0,  /* inherited */                         /* tp_weaklistoffset */
+    0,  /* inherited */                         /* tp_iter */
+    0,                                          /* tp_iternext */
+    0,  /* inherited */                         /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    &CDataOwning_Type,                          /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    0,                                          /* tp_dictoffset */
+    0,                                          /* tp_init */
+    0,                                          /* tp_alloc */
+    0,                                          /* tp_new */
+    PyObject_GC_Del,                            /* tp_free */
+};
+
+static PyTypeObject CDataGCP_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.CDataGCP",
+    sizeof(CDataObject_gcp),
+    0,
+    (destructor)cdatagcp_dealloc,               /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    0,  /* inherited */                         /* tp_repr */
+    0,  /* inherited */                         /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    0,  /* inherited */                         /* tp_as_mapping */
+    0,  /* inherited */                         /* tp_hash */
+    0,  /* inherited */                         /* tp_call */
+    0,                                          /* tp_str */
+    0,  /* inherited */                         /* tp_getattro */
+    0,  /* inherited */                         /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES  /* tp_flags */
+#ifdef Py_TPFLAGS_HAVE_FINALIZE
+                       | Py_TPFLAGS_HAVE_FINALIZE
+#endif
+                       | Py_TPFLAGS_HAVE_GC,
+    0,                                          /* tp_doc */
+    (traverseproc)cdatagcp_traverse,            /* tp_traverse */
+    0,                                          /* tp_clear */
+    0,  /* inherited */                         /* tp_richcompare */
+    0,  /* inherited */                         /* tp_weaklistoffset */
+    0,  /* inherited */                         /* tp_iter */
+    0,                                          /* tp_iternext */
+    0,  /* inherited */                         /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    &CData_Type,                                /* tp_base */
+#ifdef Py_TPFLAGS_HAVE_FINALIZE  /* CPython >= 3.4 */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    0,                                          /* tp_dictoffset */
+    0,                                          /* tp_init */
+    0,                                          /* tp_alloc */
+    0,                                          /* tp_new */
+    0,  /* inherited */                         /* tp_free */
+    0,                                          /* tp_is_gc */
+    0,                                          /* tp_bases */
+    0,                                          /* tp_mro */
+    0,                                          /* tp_cache */
+    0,                                          /* tp_subclasses */
+    0,                                          /* tp_weaklist */
+    0,                                          /* tp_del */
+    0,                                          /* version_tag */
+    (destructor)cdatagcp_finalize,              /* tp_finalize */
+#endif
+};
+
+/************************************************************/
+
+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 CDataObject *allocate_owning_object(Py_ssize_t size,
+                                           CTypeDescrObject *ct,
+                                           int dont_clear)
+{
+    /* note: objects with &CDataOwning_Type are always allocated with
+       either a plain malloc() or calloc(), and freed with free(). */
+    CDataObject *cd;
+    if (dont_clear)
+        cd = malloc(size);
+    else
+        cd = calloc(size, 1);
+    if (PyObject_Init((PyObject *)cd, &CDataOwning_Type) == NULL)
+        return NULL;
+
+    Py_INCREF(ct);
+    cd->c_type = ct;
+    cd->c_weakreflist = NULL;
+    return cd;
+}
+
+static PyObject *
+convert_struct_to_owning_object(char *data, CTypeDescrObject *ct)
+{
+    /* also accepts unions, for the API mode */
+    CDataObject *cd;
+    Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment);
+    Py_ssize_t datasize = ct->ct_size;
+
+    if (datasize < 0) {
+        PyErr_SetString(PyExc_TypeError,
+                        "return type is an opaque structure or union");
+        return NULL;
+    }
+    if (ct->ct_flags & CT_WITH_VAR_ARRAY) {
+        PyErr_SetString(PyExc_TypeError,
+                  "return type is a struct/union with a varsize array member");
+        return NULL;
+    }
+    cd = allocate_owning_object(dataoffset + datasize, ct, /*dont_clear=*/1);
+    if (cd == NULL)
+        return NULL;
+    cd->c_data = ((char *)cd) + dataoffset;
+
+    memcpy(cd->c_data, data, datasize);
+    return (PyObject *)cd;
+}
+
+static CDataObject *allocate_gcp_object(CDataObject *origobj,
+                                        CTypeDescrObject *ct,
+                                        PyObject *destructor)
+{
+    CDataObject_gcp *cd = PyObject_GC_New(CDataObject_gcp, &CDataGCP_Type);
+    if (cd == NULL)
+        return NULL;
+
+    Py_XINCREF(destructor);
+    Py_INCREF(origobj);
+    Py_INCREF(ct);
+    cd->head.c_data = origobj->c_data;
+    cd->head.c_type = ct;
+    cd->head.c_weakreflist = NULL;
+    cd->origobj = (PyObject *)origobj;
+    cd->destructor = destructor;
+
+    PyObject_GC_Track(cd);
+    return (CDataObject *)cd;
+}
+
+static CDataObject *allocate_with_allocator(Py_ssize_t basesize,
+                                            Py_ssize_t datasize,
+                                            CTypeDescrObject *ct,
+                                            const cffi_allocator_t *allocator)
+{
+    CDataObject *cd;
+
+    if (allocator->ca_alloc == NULL) {
+        cd = allocate_owning_object(basesize + datasize, ct,
+                                    allocator->ca_dont_clear);
+        if (cd == NULL)
+            return NULL;
+        cd->c_data = ((char *)cd) + basesize;
+    }
+    else {
+        PyObject *res = PyObject_CallFunction(allocator->ca_alloc, "n", datasize);
+        if (res == NULL)
+            return NULL;
+
+        if (!CData_Check(res)) {
+            PyErr_Format(PyExc_TypeError,
+                         "alloc() must return a cdata object (got %.200s)",
+                         Py_TYPE(res)->tp_name);
+            Py_DECREF(res);
+            return NULL;
+        }
+        cd = (CDataObject *)res;
+        if (!(cd->c_type->ct_flags & (CT_POINTER|CT_ARRAY))) {
+            PyErr_Format(PyExc_TypeError,
+                         "alloc() must return a cdata pointer, not '%s'",
+                         cd->c_type->ct_name);
+            Py_DECREF(res);
+            return NULL;
+        }
+        if (!cd->c_data) {
+            PyErr_SetString(PyExc_MemoryError, "alloc() returned NULL");
+            Py_DECREF(res);
+            return NULL;
+        }
+
+        cd = allocate_gcp_object(cd, ct, allocator->ca_free);
+        Py_DECREF(res);
+        if (!allocator->ca_dont_clear)
+            memset(cd->c_data, 0, datasize);
+    }
+    return cd;
+}
+
+static PyObject *direct_newp(CTypeDescrObject *ct, PyObject *init,
+                             const cffi_allocator_t *allocator)
+{
+    CTypeDescrObject *ctitem;
+    CDataObject *cd;
+    Py_ssize_t dataoffset, datasize, explicitlength;
+
+    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 *= 2;   /* forcefully add another character: a null */
+
+        if (ctitem->ct_flags & (CT_STRUCT | CT_UNION)) {
+            if (force_lazy_struct(ctitem) < 0)   /* for CT_WITH_VAR_ARRAY */
+                return NULL;
+
+            if (ctitem->ct_flags & CT_WITH_VAR_ARRAY) {
+                assert(ct->ct_flags & CT_IS_PTR_TO_OWNED);
+                dataoffset = offsetof(CDataObject_own_length, alignment);
+
+                if (init != Py_None) {
+                    Py_ssize_t optvarsize = datasize;
+                    if (convert_struct_from_object(NULL, ctitem, init,
+                                                   &optvarsize) < 0)
+                        return NULL;
+                    datasize = optvarsize;
+                }
+            }
+        }
+    }
+    else if (ct->ct_flags & CT_ARRAY) {
+        dataoffset = offsetof(CDataObject_own_nolength, alignment);
+        datasize = ct->ct_size;
+        if (datasize < 0) {
+            explicitlength = get_new_array_length(ct->ct_itemdescr, &init);
+            if (explicitlength < 0)
+                return NULL;
+            ctitem = ct->ct_itemdescr;
+            dataoffset = offsetof(CDataObject_own_length, alignment);
+            datasize = MUL_WRAPAROUND(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_Format(PyExc_TypeError,
+                     "expected a pointer or array ctype, got '%s'",
+                     ct->ct_name);
+        return NULL;
+    }
+
+    if (ct->ct_flags & CT_IS_PTR_TO_OWNED) {
+        /* common case of ptr-to-struct (or ptr-to-union): for this case
+           we build two objects instead of one, with the memory-owning
+           one being really the struct (or union) and the returned one
+           having a strong reference to it */
+        CDataObject *cds;
+
+        cds = allocate_with_allocator(dataoffset, datasize, ct->ct_itemdescr,
+                                      allocator);
+        if (cds == NULL)
+            return NULL;
+
+        cd = allocate_owning_object(sizeof(CDataObject_own_structptr), ct,
+                                    /*dont_clear=*/1);
+        if (cd == NULL) {
+            Py_DECREF(cds);
+            return NULL;
+        }
+        /* store the only reference to cds into cd */
+        ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds;
+        /* store information about the allocated size of the struct */
+        if (dataoffset == offsetof(CDataObject_own_length, alignment)) {
+            ((CDataObject_own_length *)cds)->length = datasize;
+        }
+        assert(explicitlength < 0);
+
+        cd->c_data = cds->c_data;
+    }
+    else {
+        cd = allocate_with_allocator(dataoffset, datasize, ct, allocator);
+        if (cd == NULL)
+            return NULL;
+
+        if (explicitlength >= 0)
+            ((CDataObject_own_length*)cd)->length = explicitlength;
+    }
+
+    if (init != Py_None) {
+        if (convert_from_object(cd->c_data,
+              (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) {
+            Py_DECREF(cd);
+            return NULL;
+        }
+    }
+    return (PyObject *)cd;
+}
+
+static PyObject *b_newp(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    PyObject *init = Py_None;
+    if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init))
+        return NULL;
+    return direct_newp(ct, init, &default_allocator);
+}
+
+static int
+_my_PyObject_AsBool(PyObject *ob)
+{
+    /* convert and cast a Python object to a boolean.  Accept an integer
+       or a float object, up to a CData 'long double'. */
+    PyObject *io;
+    PyNumberMethods *nb;
+    int res;
+
+#if PY_MAJOR_VERSION < 3
+    if (PyInt_Check(ob)) {
+        return PyInt_AS_LONG(ob) != 0;
+    }
+    else
+#endif
+    if (PyLong_Check(ob)) {
+        return _PyLong_Sign(ob) != 0;
+    }
+    else if (PyFloat_Check(ob)) {
+        return PyFloat_AS_DOUBLE(ob) != 0.0;
+    }
+    else if (CData_Check(ob)) {
+        CDataObject *cd = (CDataObject *)ob;
+        if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
+            /*READ(cd->c_data, cd->c_type->ct_size)*/
+            if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) {
+                /* 'long double' objects: return the answer directly */
+                return read_raw_longdouble_data(cd->c_data) != 0.0;
+            }
+            else {
+                /* 'float'/'double' objects: return the answer directly */
+                return read_raw_float_data(cd->c_data,
+                                           cd->c_type->ct_size) != 0.0;
+            }
+        }
+    }
+    nb = ob->ob_type->tp_as_number;
+    if (nb == NULL || (nb->nb_float == NULL && nb->nb_int == NULL)) {
+        PyErr_SetString(PyExc_TypeError, "integer/float expected");
+        return -1;
+    }
+    if (nb->nb_float && !CData_Check(ob))
+        io = (*nb->nb_float) (ob);
+    else
+        io = (*nb->nb_int) (ob);
+    if (io == NULL)
+        return -1;
+
+    if (PyIntOrLong_Check(io) || PyFloat_Check(io)) {
+        res = _my_PyObject_AsBool(io);
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError, "integer/float conversion failed");
+        res = -1;
+    }
+    Py_DECREF(io);
+    return res;
+}
+
+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;
+    cd->c_weakreflist = NULL;
+    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;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyString_Check(ob)) {
+        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];
+    }
+#endif
+    else if (PyUnicode_Check(ob)) {
+        char err_buf[80];
+        cffi_char32_t ordinal;
+        if (_my_PyUnicode_AsSingleChar32(ob, &ordinal, err_buf) < 0) {
+            PyErr_Format(PyExc_TypeError,
+                         "cannot cast %s to ctype '%s'", err_buf, ct->ct_name);
+            return NULL;
+        }
+        /* the types char16_t and char32_t are both unsigned.  However,
+           wchar_t might be signed.  In theory it does not matter,
+           because 'ordinal' comes from a regular Python unicode. */
+#ifdef HAVE_WCHAR_H
+        if (ct->ct_flags & CT_IS_SIGNED_WCHAR)
+            value = (wchar_t)ordinal;
+        else
+#endif
+            value = ordinal;
+    }
+    else if (PyBytes_Check(ob)) {
+        int res = _convert_to_char(ob);
+        if (res < 0)
+            return NULL;
+        value = (unsigned char)res;
+    }
+    else if (ct->ct_flags & CT_IS_BOOL) {
+        int res = _my_PyObject_AsBool(ob);
+        if (res < 0)
+            return NULL;
+        value = res;
+    }
+    else {
+        value = _my_PyLong_AsUnsignedLongLong(ob, 0);
+        if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
+            return NULL;
+    }
+    if (ct->ct_flags & CT_IS_BOOL)
+        value = !!value;
+    cd = _new_casted_primitive(ct);
+    if (cd != NULL)
+        write_raw_integer_data(cd->c_data, value, ct->ct_size);
+    return cd;
+}
+
+/* returns -1 if cannot cast, 0 if we don't get a value, 1 if we do */
+static int check_bytes_for_float_compatible(PyObject *io, double *out_value)
+{
+    if (PyBytes_Check(io)) {
+        if (PyBytes_GET_SIZE(io) != 1)
+            goto error;
+        *out_value = (unsigned char)PyBytes_AS_STRING(io)[0];
+        return 1;
+    }
+    else if (PyUnicode_Check(io)) {
+        char ignored[80];
+        cffi_char32_t ordinal;
+        if (_my_PyUnicode_AsSingleChar32(io, &ordinal, ignored) < 0)
+            goto error;
+        /* the signness of the 32-bit version of wide chars should not
+         * matter here, because 'ordinal' comes from a normal Python
+         * unicode string */
+        *out_value = ordinal;
+        return 1;
+    }
+    *out_value = 0;   /* silence a gcc warning if this function is inlined */
+    return 0;
+
+ error:
+    Py_DECREF(io);
+    *out_value = 0;   /* silence a gcc warning if this function is inlined */
+    return -1;
+}
+
+static PyObject *do_cast(CTypeDescrObject *ct, PyObject *ob)
+{
+    CDataObject *cd;
+
+    if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY) &&
+        ct->ct_size >= 0) {
+        /* cast to a pointer, to a funcptr, or to an array.
+           Note that casting to an array is an extension to the C language,
+           which seems to be necessary in order to sanely get a
+           <cdata 'int[3]'> at some address. */
+        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);
+            }
+        }
+        if ((ct->ct_flags & CT_POINTER) &&
+                (ct->ct_itemdescr->ct_flags & CT_IS_FILE) &&
+                PyFile_Check(ob)) {
+            FILE *f = PyFile_AsFile(ob);
+            if (f == NULL && PyErr_Occurred())
+                return NULL;
+            return new_simple_cdata((char *)f, 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;
+        int res;
+
+        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);
+        }
+
+        res = check_bytes_for_float_compatible(io, &value);
+        if (res == -1)
+            goto cannot_cast;
+        if (res == 0) {
+            if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+                 CData_Check(io) &&
+                 (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+                long double lvalue;
+                char *data = ((CDataObject *)io)->c_data;
+                /*READ(data, sizeof(long double)*/
+                lvalue = read_raw_longdouble_data(data);
+                Py_DECREF(io);
+                cd = _new_casted_primitive(ct);
+                if (cd != NULL)
+                    write_raw_longdouble_data(cd->c_data, lvalue);
+                return (PyObject *)cd;
+            }
+            value = PyFloat_AsDouble(io);
+        }
+        Py_DECREF(io);
+        if (value == -1.0 && PyErr_Occurred())
+            return NULL;
+
+        cd = _new_casted_primitive(ct);
+        if (cd != NULL) {
+            if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+                write_raw_float_data(cd->c_data, value, ct->ct_size);
+            else
+                write_raw_longdouble_data(cd->c_data, (long double)value);
+        }
+        return (PyObject *)cd;
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) {
+        /* cast to a complex */
+        Py_complex value;
+        PyObject *io;
+        int res;
+
+        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);
+        }
+
+        res = check_bytes_for_float_compatible(io, &value.real);
+        if (res == -1)
+            goto cannot_cast;
+        if (res == 1) {
+            // got it from string
+            value.imag = 0.0;
+        } else {
+            value = PyComplex_AsCComplex(io);
+        }
+        Py_DECREF(io);
+        if (PyErr_Occurred()) {
+            return NULL;
+        }
+        cd = _new_casted_primitive(ct);
+        if (cd != NULL) {
+            write_raw_complex_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;
+}
+
+static PyObject *b_cast(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    PyObject *ob;
+    if (!PyArg_ParseTuple(args, "O!O:cast", &CTypeDescr_Type, &ct, &ob))
+        return NULL;
+
+    return do_cast(ct, ob);
+}
+
+/************************************************************/
+
+typedef struct {
+    PyObject_HEAD
+    void *dl_handle;
+    char *dl_name;
+} DynLibObject;
+
+static void dl_dealloc(DynLibObject *dlobj)
+{
+    if (dlobj->dl_handle != NULL)
+        dlclose(dlobj->dl_handle);
+    free(dlobj->dl_name);
+    PyObject_Del(dlobj);
+}
+
+static PyObject *dl_repr(DynLibObject *dlobj)
+{
+    return PyText_FromFormat("<clibrary '%s'>", dlobj->dl_name);
+}
+
+static int dl_check_closed(DynLibObject *dlobj)
+{
+    if (dlobj->dl_handle == NULL)
+    {
+        PyErr_Format(PyExc_ValueError, "library '%s' has already been closed",
+                     dlobj->dl_name);
+        return -1;
+    }
+    return 0;
+}
+
+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 (dl_check_closed(dlobj) < 0)
+        return NULL;
+
+    if (!(ct->ct_flags & (CT_FUNCTIONPTR | CT_POINTER | CT_ARRAY))) {
+        PyErr_Format(PyExc_TypeError,
+                     "function or pointer or array cdata expected, got '%s'",
+                     ct->ct_name);
+        return NULL;
+    }
+    dlerror();   /* clear error condition */
+    funcptr = dlsym(dlobj->dl_handle, funcname);
+    if (funcptr == NULL) {
+        const char *error = dlerror();
+        PyErr_Format(PyExc_AttributeError,
+                     "function/symbol '%s' not found in library '%s': %s",
+                     funcname, dlobj->dl_name, error);
+        return NULL;
+    }
+
+    if ((ct->ct_flags & CT_ARRAY) && ct->ct_length < 0) {
+        ct = (CTypeDescrObject *)ct->ct_stuff;
+    }
+    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;
+
+    if (dl_check_closed(dlobj) < 0)
+        return NULL;
+
+    dlerror();   /* clear error condition */
+    data = dlsym(dlobj->dl_handle, varname);
+    if (data == NULL) {
+        const char *error = dlerror();
+        if (error != NULL) {
+            PyErr_Format(PyExc_KeyError,
+                         "variable '%s' not found in library '%s': %s",
+                         varname, dlobj->dl_name, error);
+            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:write_variable",
+                          &CTypeDescr_Type, &ct, &varname, &value))
+        return NULL;
+
+    if (dl_check_closed(dlobj) < 0)
+        return NULL;
+
+    dlerror();   /* clear error condition */
+    data = dlsym(dlobj->dl_handle, varname);
+    if (data == NULL) {
+        const char *error = dlerror();
+        PyErr_Format(PyExc_KeyError,
+                     "variable '%s' not found in library '%s': %s",
+                     varname, dlobj->dl_name, error);
+        return NULL;
+    }
+    if (convert_from_object(data, ct, value) < 0)
+        return NULL;
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *dl_close_lib(DynLibObject *dlobj, PyObject *no_args)
+{
+    if (dlobj->dl_handle != NULL)
+    {
+        dlclose(dlobj->dl_handle);
+        dlobj->dl_handle = 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},
+    {"close_lib",       (PyCFunction)dl_close_lib,      METH_NOARGS},
+    {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 void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
+                         PyObject **p_temp)
+{
+    /* Logic to call the correct version of dlopen().  Returns NULL in case of error.
+       Otherwise, '*p_printable_filename' will point to a printable char version of
+       the filename (maybe utf-8-encoded).  '*p_temp' will be set either to NULL or
+       to a temporary object that must be freed after looking at printable_filename.
+    */
+    void *handle;
+    char *filename_or_null;
+    int flags = 0;
+    *p_temp = NULL;
+    
+    if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) {
+        PyObject *dummy;
+        if (!PyArg_ParseTuple(args, "|Oi:load_library",
+                              &dummy, &flags))
+            return NULL;
+        filename_or_null = NULL;
+        *p_printable_filename = "<None>";
+    }
+    else
+    {
+        PyObject *s = PyTuple_GET_ITEM(args, 0);
+#ifdef MS_WIN32
+        Py_UNICODE *filenameW;
+        if (PyArg_ParseTuple(args, "u|i:load_library", &filenameW, &flags))
+        {
+#if PY_MAJOR_VERSION < 3
+            s = PyUnicode_AsUTF8String(s);
+            if (s == NULL)
+                return NULL;
+            *p_temp = s;
+#endif
+            *p_printable_filename = PyText_AsUTF8(s);
+            if (*p_printable_filename == NULL)
+                return NULL;
+
+            handle = dlopenW(filenameW);
+            goto got_handle;
+        }
+        PyErr_Clear();
+#endif
+        if (!PyArg_ParseTuple(args, "et|i:load_library",
+                     Py_FileSystemDefaultEncoding, &filename_or_null, &flags))
+            return NULL;
+#if PY_MAJOR_VERSION < 3
+        if (PyUnicode_Check(s))
+        {
+            s = PyUnicode_AsUTF8String(s);
+            if (s == NULL)
+                return NULL;
+            *p_temp = s;
+        }
+#endif
+        *p_printable_filename = PyText_AsUTF8(s);
+        if (*p_printable_filename == NULL)
+            return NULL;
+    }
+    if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0)
+        flags |= RTLD_NOW;
+
+    handle = dlopen(filename_or_null, flags);
+
+#ifdef MS_WIN32
+  got_handle:
+#endif
+    if (handle == NULL) {
+        const char *error = dlerror();
+        PyErr_Format(PyExc_OSError, "cannot load library '%s': %s",
+                     *p_printable_filename, error);
+        return NULL;
+    }
+    return handle;
+}
+
+static PyObject *b_load_library(PyObject *self, PyObject *args)
+{
+    const char *printable_filename;
+    PyObject *temp;
+    void *handle;
+    DynLibObject *dlobj = NULL;
+
+    handle = b_do_dlopen(args, &printable_filename, &temp);
+    if (handle == NULL)
+        goto error;
+
+    dlobj = PyObject_New(DynLibObject, &dl_type);
+    if (dlobj == NULL) {
+        dlclose(handle);
+        goto error;
+    }
+    dlobj->dl_handle = handle;
+    dlobj->dl_name = strdup(printable_filename);
+ 
+ error:
+    Py_XDECREF(temp);
+    return (PyObject *)dlobj;
+}
+
+/************************************************************/
+
+static PyObject *get_unique_type(CTypeDescrObject *x,
+                                 const void *unique_key[], long keylength)
+{
+    /* Replace the CTypeDescrObject 'x' with a standardized one.
+       This either just returns x, or x is decrefed and a new reference
+       to the already-existing equivalent is returned.
+
+       In this function, 'x' always contains a reference that must be
+       either decrefed or returned.
+
+       Keys:
+           void       ["void"]
+           primitive  [&static_struct]
+           pointer    [ctype]
+           array      [ctype, length]
+           funcptr    [ctresult, ellipsis+abi, num_args, ctargs...]
+    */
+    PyObject *key, *y;
+    void *pkey;
+
+    key = PyBytes_FromStringAndSize(NULL, keylength * sizeof(void *));
+    if (key == NULL)
+        goto error;
+
+    pkey = PyBytes_AS_STRING(key);
+    memcpy(pkey, unique_key, keylength * sizeof(void *));
+
+    y = PyDict_GetItem(unique_cache, key);
+    if (y != NULL) {
+        Py_DECREF(key);
+        Py_INCREF(y);
+        Py_DECREF(x);
+        return y;
+    }
+    if (PyDict_SetItem(unique_cache, key, (PyObject *)x) < 0) {
+        Py_DECREF(key);
+        goto error;
+    }
+    /* Haaaack for our reference count hack: gcmodule.c must not see this
+       dictionary.  The problem is that any PyDict_SetItem() notices that
+       'x' is tracked and re-tracks the unique_cache dictionary.  So here
+       we re-untrack it again... */
+    PyObject_GC_UnTrack(unique_cache);
+
+    assert(x->ct_unique_key == NULL);
+    x->ct_unique_key = key; /* the key will be freed in ctypedescr_dealloc() */
+    /* the 'value' in unique_cache doesn't count as 1, but don't use
+       Py_DECREF(x) here because it will confuse debug builds into thinking
+       there was an extra DECREF in total. */
+    ((PyObject *)x)->ob_refcnt--;
+    return (PyObject *)x;
+
+ error:
+    Py_DECREF(x);
+    return NULL;
+}
+
+/* according to the C standard, these types should be equivalent to the
+   _Complex types for the purposes of storage (not arguments in calls!) */
+typedef float cffi_float_complex_t[2];
+typedef double cffi_double_complex_t[2];
+
+static PyObject *new_primitive_type(const char *name)
+{
+#define ENUM_PRIMITIVE_TYPES                                    \
+       EPTYPE(c, char, CT_PRIMITIVE_CHAR)                       \
+       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 )         \
+       EPTYPE(ul, unsigned long, CT_PRIMITIVE_UNSIGNED )        \
+       EPTYPE(ull, unsigned long long, CT_PRIMITIVE_UNSIGNED )  \
+       EPTYPE(f, float, CT_PRIMITIVE_FLOAT )                    \
+       EPTYPE(d, double, CT_PRIMITIVE_FLOAT )                   \
+       EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE ) \
+       EPTYPE2(fc, "float _Complex", cffi_float_complex_t, CT_PRIMITIVE_COMPLEX ) \
+       EPTYPE2(dc, "double _Complex", cffi_double_complex_t, CT_PRIMITIVE_COMPLEX ) \
+       ENUM_PRIMITIVE_TYPES_WCHAR                               \
+       EPTYPE2(c16, "char16_t", cffi_char16_t, CT_PRIMITIVE_CHAR ) \
+       EPTYPE2(c32, "char32_t", cffi_char32_t, CT_PRIMITIVE_CHAR ) \
+       EPTYPE(b, _Bool, CT_PRIMITIVE_UNSIGNED | CT_IS_BOOL )    \
+     /* the following types are not primitive in the C sense */ \
+       EPTYPE(i8, int8_t, CT_PRIMITIVE_SIGNED)                  \
+       EPTYPE(u8, uint8_t, CT_PRIMITIVE_UNSIGNED)               \
+       EPTYPE(i16, int16_t, CT_PRIMITIVE_SIGNED)                \
+       EPTYPE(u16, uint16_t, CT_PRIMITIVE_UNSIGNED)             \
+       EPTYPE(i32, int32_t, CT_PRIMITIVE_SIGNED)                \
+       EPTYPE(u32, uint32_t, CT_PRIMITIVE_UNSIGNED)             \
+       EPTYPE(i64, int64_t, CT_PRIMITIVE_SIGNED)                \
+       EPTYPE(u64, uint64_t, CT_PRIMITIVE_UNSIGNED)             \
+       EPTYPE(il8, int_least8_t, CT_PRIMITIVE_SIGNED)           \
+       EPTYPE(ul8, uint_least8_t, CT_PRIMITIVE_UNSIGNED)        \
+       EPTYPE(il16, int_least16_t, CT_PRIMITIVE_SIGNED)         \
+       EPTYPE(ul16, uint_least16_t, CT_PRIMITIVE_UNSIGNED)      \
+       EPTYPE(il32, int_least32_t, CT_PRIMITIVE_SIGNED)         \
+       EPTYPE(ul32, uint_least32_t, CT_PRIMITIVE_UNSIGNED)      \
+       EPTYPE(il64, int_least64_t, CT_PRIMITIVE_SIGNED)         \
+       EPTYPE(ul64, uint_least64_t, CT_PRIMITIVE_UNSIGNED)      \
+       EPTYPE(if8, int_fast8_t, CT_PRIMITIVE_SIGNED)            \
+       EPTYPE(uf8, uint_fast8_t, CT_PRIMITIVE_UNSIGNED)         \
+       EPTYPE(if16, int_fast16_t, CT_PRIMITIVE_SIGNED)          \
+       EPTYPE(uf16, uint_fast16_t, CT_PRIMITIVE_UNSIGNED)       \
+       EPTYPE(if32, int_fast32_t, CT_PRIMITIVE_SIGNED)          \
+       EPTYPE(uf32, uint_fast32_t, CT_PRIMITIVE_UNSIGNED)       \
+       EPTYPE(if64, int_fast64_t, CT_PRIMITIVE_SIGNED)          \
+       EPTYPE(uf64, uint_fast64_t, CT_PRIMITIVE_UNSIGNED)       \
+       EPTYPE(ip, intptr_t, CT_PRIMITIVE_SIGNED)                \
+       EPTYPE(up, uintptr_t, CT_PRIMITIVE_UNSIGNED)             \
+       EPTYPE(im, intmax_t, CT_PRIMITIVE_SIGNED)                \
+       EPTYPE(um, uintmax_t, CT_PRIMITIVE_UNSIGNED)             \
+       EPTYPE(pd, ptrdiff_t, CT_PRIMITIVE_SIGNED)               \
+       EPTYPE(sz, size_t, CT_PRIMITIVE_UNSIGNED)                \
+       EPTYPE2(ssz, "ssize_t", Py_ssize_t, CT_PRIMITIVE_SIGNED)
+
+#ifdef HAVE_WCHAR_H
+# define ENUM_PRIMITIVE_TYPES_WCHAR                             \
+       EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR |                  \
+                           (((wchar_t)-1) > 0 ? 0 : CT_IS_SIGNED_WCHAR))
+#else
+# define ENUM_PRIMITIVE_TYPES_WCHAR   /* nothing */
+#endif
+
+#define EPTYPE(code, typename, flags)  EPTYPE2(code, #typename, typename, flags)
+
+#define EPTYPE2(code, export_name, typename, flags)     \
+    struct aligncheck_##code { char x; typename y; };
+    ENUM_PRIMITIVE_TYPES
+#undef EPTYPE2
+
+    CTypeDescrObject *td;
+    static const struct descr_s { const char *name; int size, align, flags; }
+    types[] = {
+#define EPTYPE2(code, export_name, typename, flags)     \
+        { export_name,                                  \
+          sizeof(typename),                             \
+          offsetof(struct aligncheck_##code, y),        \
+          flags                                         \
+        },
+    ENUM_PRIMITIVE_TYPES
+#undef EPTYPE2
+#undef EPTYPE
+#undef ENUM_PRIMITIVE_TYPES_WCHAR
+#undef ENUM_PRIMITIVE_TYPES
+        { NULL }
+    };
+    const struct descr_s *ptypes;
+    const void *unique_key[1];
+    int name_size;
+    ffi_type *ffitype;
+
+    for (ptypes=types; ; ptypes++) {
+        if (ptypes->name == NULL) {
+#ifndef HAVE_WCHAR_H
+            if (strcmp(name, "wchar_t"))
+                PyErr_SetString(PyExc_NotImplementedError, name);
+            else
+#endif
+            PyErr_SetString(PyExc_KeyError, name);
+            return NULL;
+        }
+        if (strcmp(name, ptypes->name) == 0)
+            break;
+    }
+
+    if (ptypes->flags & CT_PRIMITIVE_SIGNED) {
+        switch (ptypes->size) {
+        case 1: ffitype = &ffi_type_sint8; break;
+        case 2: ffitype = &ffi_type_sint16; break;
+        case 4: ffitype = &ffi_type_sint32; break;
+        case 8: ffitype = &ffi_type_sint64; break;
+        default: goto bad_ffi_type;
+        }
+    }
+    else if (ptypes->flags & CT_PRIMITIVE_FLOAT) {
+        if (strcmp(ptypes->name, "float") == 0)
+            ffitype = &ffi_type_float;
+        else if (strcmp(ptypes->name, "double") == 0)
+            ffitype = &ffi_type_double;
+        else if (strcmp(ptypes->name, "long double") == 0) {
+            /* assume that if sizeof(double) == sizeof(long double), then
+               the two types are equivalent for C.  libffi bugs on Win64
+               if a function's return type is ffi_type_longdouble... */
+            if (sizeof(double) == sizeof(long double))
+                ffitype = &ffi_type_double;
+            else
+                ffitype = &ffi_type_longdouble;
+        }
+        else
+            goto bad_ffi_type;
+    }
+    else if (ptypes->flags & CT_PRIMITIVE_COMPLEX) {
+        /* As of March 2017, still no libffi support for complex.
+           It fails silently if we try to use ffi_type_complex_float
+           or ffi_type_complex_double.  Better not use it at all.
+         */
+        ffitype = NULL;
+    }
+    else {
+        switch (ptypes->size) {
+        case 1: ffitype = &ffi_type_uint8; break;
+        case 2: ffitype = &ffi_type_uint16; break;
+        case 4: ffitype = &ffi_type_uint32; break;
+        case 8: ffitype = &ffi_type_uint64; break;
+        default: goto bad_ffi_type;
+        }
+    }
+
+    name_size = strlen(ptypes->name) + 1;
+    td = ctypedescr_new(name_size);
+    if (td == NULL)
+        return NULL;
+
+    memcpy(td->ct_name, name, name_size);
+    td->ct_size = ptypes->size;
+    td->ct_length = ptypes->align;
+    td->ct_extra = ffitype;
+    td->ct_flags = ptypes->flags;
+    if (td->ct_flags & (CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_CHAR)) {
+        if (td->ct_size <= (Py_ssize_t)sizeof(long))
+            td->ct_flags |= CT_PRIMITIVE_FITS_LONG;
+    }
+    else if (td->ct_flags & CT_PRIMITIVE_UNSIGNED) {
+        if (td->ct_size < (Py_ssize_t)sizeof(long))
+            td->ct_flags |= CT_PRIMITIVE_FITS_LONG;
+    }
+    td->ct_name_position = strlen(td->ct_name);
+    unique_key[0] = ptypes;
+    return get_unique_type(td, unique_key, 1);
+
+ bad_ffi_type:
+    PyErr_Format(PyExc_NotImplementedError,
+                 "primitive type '%s' has size %d; "
+                 "the supported sizes are 1, 2, 4, 8",
+                 name, (int)ptypes->size);
+    return NULL;
+}
+
+static PyObject *b_new_primitive_type(PyObject *self, PyObject *args)
+{
+    char *name;
+    if (!PyArg_ParseTuple(args, "s:new_primitive_type", &name))
+        return NULL;
+    return new_primitive_type(name);
+}
+
+static PyObject *new_pointer_type(CTypeDescrObject *ctitem)
+{
+    CTypeDescrObject *td;
+    const char *extra;
+    const void *unique_key[1];
+
+    if (ctitem->ct_flags & CT_ARRAY)
+        extra = "(*)";   /* obscure case: see test_array_add */
+    else
+        extra = " *";
+    td = ctypedescr_new_on_top(ctitem, extra, 2);
+    if (td == NULL)
+        return NULL;
+
+    td->ct_size = sizeof(void *);
+    td->ct_length = -1;
+    td->ct_flags = CT_POINTER;
+    if (ctitem->ct_flags & (CT_STRUCT|CT_UNION))
+        td->ct_flags |= CT_IS_PTR_TO_OWNED;
+    if (ctitem->ct_flags & CT_VOID)
+        td->ct_flags |= CT_IS_VOID_PTR;
+    if ((ctitem->ct_flags & CT_VOID) ||
+        ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) &&
+         ctitem->ct_size == sizeof(char)))
+        td->ct_flags |= CT_IS_VOIDCHAR_PTR;   /* 'void *' or 'char *' only */
+    unique_key[0] = ctitem;
+    return get_unique_type(td, unique_key, 1);
+}
+
+static PyObject *b_new_pointer_type(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ctitem;
+    if (!PyArg_ParseTuple(args, "O!:new_pointer_type",
+                          &CTypeDescr_Type, &ctitem))
+        return NULL;
+    return new_pointer_type(ctitem);
+}
+
+static PyObject *b_new_array_type(PyObject *self, PyObject *args)
+{
+    PyObject *lengthobj;
+    Py_ssize_t length;
+    CTypeDescrObject *ctptr;
+
+    if (!PyArg_ParseTuple(args, "O!O:new_array_type",
+                          &CTypeDescr_Type, &ctptr, &lengthobj))
+        return NULL;
+
+    if (lengthobj == Py_None) {
+        length = -1;
+    }
+    else {
+        length = PyNumber_AsSsize_t(lengthobj, PyExc_OverflowError);
+        if (length < 0) {
+            if (!PyErr_Occurred())
+                PyErr_SetString(PyExc_ValueError, "negative array length");
+            return NULL;
+        }
+    }
+    return new_array_type(ctptr, length);
+}
+
+static PyObject *
+new_array_type(CTypeDescrObject *ctptr, Py_ssize_t length)
+{
+    CTypeDescrObject *td, *ctitem;
+    char extra_text[32];
+    Py_ssize_t arraysize;
+    int flags = CT_ARRAY;
+    const void *unique_key[2];
+
+    if (!(ctptr->ct_flags & CT_POINTER)) {
+        PyErr_SetString(PyExc_TypeError, "first arg must be a pointer ctype");
+        return NULL;
+    }
+    ctitem = ctptr->ct_itemdescr;
+    if (ctitem->ct_size < 0) {
+        PyErr_Format(PyExc_ValueError, "array item of unknown size: '%s'",
+                     ctitem->ct_name);
+        return NULL;
+    }
+
+    if (length < 0) {
+        sprintf(extra_text, "[]");
+        length = -1;
+        arraysize = -1;
+    }
+    else {
+        sprintf(extra_text, "[%llu]", (unsigned PY_LONG_LONG)length);
+        arraysize = MUL_WRAPAROUND(length, ctitem->ct_size);
+        if (length > 0 && (arraysize / length) != ctitem->ct_size) {
+            PyErr_SetString(PyExc_OverflowError,
+                            "array size would overflow a Py_ssize_t");
+            return NULL;
+        }
+    }
+    td = ctypedescr_new_on_top(ctitem, extra_text, 0);
+    if (td == NULL)
+        return NULL;
+
+    Py_INCREF(ctptr);
+    td->ct_stuff = (PyObject *)ctptr;
+    td->ct_size = arraysize;
+    td->ct_length = length;
+    td->ct_flags = flags;
+    unique_key[0] = ctptr;
+    unique_key[1] = (void *)length;
+    return get_unique_type(td, unique_key, 2);
+}
+
+static PyObject *new_void_type(void)
+{
+    int name_size = strlen("void") + 1;
+    const void *unique_key[1];
+    CTypeDescrObject *td = ctypedescr_new(name_size);
+    if (td == NULL)
+        return NULL;
+
+    memcpy(td->ct_name, "void", name_size);
+    td->ct_size = -1;
+    td->ct_flags = CT_VOID | CT_IS_OPAQUE;
+    td->ct_name_position = strlen("void");
+    unique_key[0] = "void";
+    return get_unique_type(td, unique_key, 1);
+}
+
+static PyObject *b_new_void_type(PyObject *self, PyObject *args)
+{
+    return new_void_type();
+}
+
+static PyObject *new_struct_or_union_type(const char *name, int flag)
+{
+    int namelen = strlen(name);
+    CTypeDescrObject *td = ctypedescr_new(namelen + 1);
+    if (td == NULL)
+        return NULL;
+
+    td->ct_size = -1;
+    td->ct_length = -1;
+    td->ct_flags = flag | CT_IS_OPAQUE;
+    td->ct_extra = NULL;
+    memcpy(td->ct_name, name, namelen + 1);
+    td->ct_name_position = namelen;
+    return (PyObject *)td;
+}
+
+static PyObject *b_new_struct_type(PyObject *self, PyObject *args)
+{
+    char *name;
+    int flag;
+    if (!PyArg_ParseTuple(args, "s:new_struct_type", &name))
+        return NULL;
+
+    flag = CT_STRUCT;
+    if (strcmp(name, "struct _IO_FILE") == 0 || strcmp(name, "FILE") == 0)
+        flag |= CT_IS_FILE;
+    return new_struct_or_union_type(name, flag);
+}
+
+static PyObject *b_new_union_type(PyObject *self, PyObject *args)
+{
+    char *name;
+    if (!PyArg_ParseTuple(args, "s:new_union_type", &name))
+        return NULL;
+    return new_struct_or_union_type(name, CT_UNION);
+}
+
+static CFieldObject *
+_add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype,
+           Py_ssize_t offset, int bitshift, int fbitsize, int flags)
+{
+    int err;
+    Py_ssize_t prev_size;
+    CFieldObject *cf = PyObject_New(CFieldObject, &CField_Type);
+    if (cf == NULL)
+        return NULL;
+
+    Py_INCREF(ftype);
+    cf->cf_type = ftype;
+    cf->cf_offset = offset;
+    cf->cf_bitshift = bitshift;
+    cf->cf_bitsize = fbitsize;
+    cf->cf_flags = flags;
+
+    Py_INCREF(fname);
+    PyText_InternInPlace(&fname);
+    prev_size = PyDict_Size(interned_fields);
+    err = PyDict_SetItem(interned_fields, fname, (PyObject *)cf);
+    Py_DECREF(fname);
+    Py_DECREF(cf);
+    if (err < 0)
+        return NULL;
+
+    if (PyDict_Size(interned_fields) != prev_size + 1) {
+        PyErr_Format(PyExc_KeyError, "duplicate field name '%s'",
+                     PyText_AS_UTF8(fname));
+        return NULL;
+    }
+    return cf;   /* borrowed reference */
+}
+
+#define SF_MSVC_BITFIELDS     0x01
+#define SF_GCC_ARM_BITFIELDS  0x02
+#define SF_GCC_X86_BITFIELDS  0x10
+
+#define SF_GCC_BIG_ENDIAN     0x04
+#define SF_GCC_LITTLE_ENDIAN  0x40
+
+#define SF_PACKED             0x08
+#define SF_STD_FIELD_POS      0x80
+
+#ifdef MS_WIN32
+#  define SF_DEFAULT_PACKING     8
+#else
+#  define SF_DEFAULT_PACKING   0x40000000   /* a huge power of two */
+#endif
+
+static int complete_sflags(int sflags)
+{
+    /* add one of the SF_xxx_BITFIELDS flags if none is specified */
+    if (!(sflags & (SF_MSVC_BITFIELDS | SF_GCC_ARM_BITFIELDS |
+                    SF_GCC_X86_BITFIELDS))) {
+#ifdef MS_WIN32
+        sflags |= SF_MSVC_BITFIELDS;
+#else
+# if defined(__arm__) || defined(__aarch64__)
+        sflags |= SF_GCC_ARM_BITFIELDS;
+# else
+        sflags |= SF_GCC_X86_BITFIELDS;
+# endif
+#endif
+    }
+    /* add one of SF_GCC_xx_ENDIAN if none is specified */
+    if (!(sflags & (SF_GCC_BIG_ENDIAN | SF_GCC_LITTLE_ENDIAN))) {
+        int _check_endian = 1;
+        if (*(char *)&_check_endian == 0)
+            sflags |= SF_GCC_BIG_ENDIAN;
+        else
+            sflags |= SF_GCC_LITTLE_ENDIAN;
+    }
+    return sflags;
+}
+
+static int detect_custom_layout(CTypeDescrObject *ct, int sflags,
+                                Py_ssize_t cdef_value,
+                                Py_ssize_t compiler_value,
+                                const char *msg1, const char *txt,
+                                const char *msg2)
+{
+    if (compiler_value != cdef_value) {
+        if (sflags & SF_STD_FIELD_POS) {
+            PyErr_Format(FFIError,
+                         "%s: %s%s%s (cdef says %zd, but C compiler says %zd)."
+                         " fix it or use \"...;\" in the cdef for %s to "
+                         "make it flexible",
+                         ct->ct_name, msg1, txt, msg2,
+                         cdef_value, compiler_value,
+                         ct->ct_name);
+            return -1;
+        }
+        ct->ct_flags |= CT_CUSTOM_FIELD_POS;
+    }
+    return 0;
+}
+
+static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    PyObject *fields, *interned_fields, *ignored;
+    int is_union, alignment;
+    Py_ssize_t boffset, i, nb_fields, boffsetmax, alignedsize, boffsetorg;
+    Py_ssize_t totalsize = -1;
+    int totalalignment = -1;
+    CFieldObject **previous;
+    int prev_bitfield_size, prev_bitfield_free;
+    int sflags = 0, fflags;
+    int pack = 0;
+
+    if (!PyArg_ParseTuple(args, "O!O!|Oniii:complete_struct_or_union",
+                          &CTypeDescr_Type, &ct,
+                          &PyList_Type, &fields,
+                          &ignored, &totalsize, &totalalignment, &sflags,
+                          &pack))
+        return NULL;
+
+    sflags = complete_sflags(sflags);
+    if (sflags & SF_PACKED)
+        pack = 1;
+    else if (pack <= 0)
+        pack = SF_DEFAULT_PACKING;
+    else
+        sflags |= SF_PACKED;
+
+    if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) ==
+                        (CT_STRUCT|CT_IS_OPAQUE)) {
+        is_union = 0;
+    }
+    else if ((ct->ct_flags & (CT_UNION|CT_IS_OPAQUE)) ==
+                             (CT_UNION|CT_IS_OPAQUE)) {
+        is_union = 1;
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError,
+                  "first arg must be a non-initialized struct or union ctype");
+        return NULL;
+    }
+    ct->ct_flags &= ~(CT_CUSTOM_FIELD_POS | CT_WITH_PACKED_CHANGE);
+
+    alignment = 1;
+    boffset = 0;         /* this number is in *bits*, not bytes! */
+    boffsetmax = 0;      /* the maximum value of boffset, in bits too */
+    prev_bitfield_size = 0;
+    prev_bitfield_free = 0;
+    nb_fields = PyList_GET_SIZE(fields);
+    interned_fields = PyDict_New();
+    if (interned_fields == NULL)
+        return NULL;
+
+    previous = (CFieldObject **)&ct->ct_extra;
+
+    for (i=0; i<nb_fields; i++) {
+        PyObject *fname;
+        CTypeDescrObject *ftype;
+        int fbitsize = -1, falign, falignorg, do_align;
+        Py_ssize_t foffset = -1;
+
+        if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!|in:list item",
+                              &PyText_Type, &fname,
+                              &CTypeDescr_Type, &ftype,
+                              &fbitsize, &foffset))
+            goto error;
+
+        if (ftype->ct_size < 0) {
+            if ((ftype->ct_flags & CT_ARRAY) && fbitsize < 0
+                    && (i == nb_fields - 1 || foffset != -1)) {
+                ct->ct_flags |= CT_WITH_VAR_ARRAY;
+            }
+            else {
+                PyErr_Format(PyExc_TypeError,
+                             "field '%s.%s' has ctype '%s' of unknown size",
+                             ct->ct_name, PyText_AS_UTF8(fname),
+                             ftype->ct_name);
+                goto error;
+            }
+        }
+
+        if (is_union)
+            boffset = 0;   /* reset each field at offset 0 */
+
+        /* update the total alignment requirement, but skip it if the
+           field is an anonymous bitfield or if SF_PACKED */
+        falignorg = get_alignment(ftype);
+        if (falignorg < 0)
+            goto error;
+        falign = (pack < falignorg) ? pack : falignorg;
+
+        do_align = 1;
+        if (!(sflags & SF_GCC_ARM_BITFIELDS) && fbitsize >= 0) {
+            if (!(sflags & SF_MSVC_BITFIELDS)) {
+                /* GCC: anonymous bitfields (of any size) don't cause alignment */
+                do_align = PyText_GetSize(fname) > 0;
+            }
+            else {
+                /* MSVC: zero-sized bitfields don't cause alignment */
+                do_align = fbitsize > 0;
+            }
+        }
+        if (alignment < falign && do_align)
+            alignment = falign;
+
+        fflags = (is_union && i > 0) ? BF_IGNORE_IN_CTOR : 0;
+
+        if (fbitsize < 0) {
+            /* not a bitfield: common case */
+            int bs_flag;
+
+            if ((ftype->ct_flags & CT_ARRAY) && ftype->ct_length <= 0)
+                bs_flag = BS_EMPTY_ARRAY;
+            else
+                bs_flag = BS_REGULAR;
+
+            /* align this field to its own 'falign' by inserting padding */
+            boffsetorg = (boffset + falignorg*8-1) & ~(falignorg*8-1); /*bits!*/
+            boffset = (boffset + falign*8-1) & ~(falign*8-1); /* bits! */
+            if (boffsetorg != boffset) {
+                ct->ct_flags |= CT_WITH_PACKED_CHANGE;
+            }
+
+            if (foffset >= 0) {
+                /* a forced field position: ignore the offset just computed,
+                   except to know if we must set CT_CUSTOM_FIELD_POS */
+                if (detect_custom_layout(ct, sflags, boffset / 8, foffset,
+                                         "wrong offset for field '",
+                                         PyText_AS_UTF8(fname), "'") < 0)
+                    goto error;
+                boffset = foffset * 8;
+            }
+
+            if (PyText_GetSize(fname) == 0 &&
+                    ftype->ct_flags & (CT_STRUCT|CT_UNION)) {
+                /* a nested anonymous struct or union */
+                CFieldObject *cfsrc = (CFieldObject *)ftype->ct_extra;
+                for (; cfsrc != NULL; cfsrc = cfsrc->cf_next) {
+                    /* broken complexity in the call to get_field_name(),
+                       but we'll assume you never do that with nested
+                       anonymous structures with thousand of fields */
+                    *previous = _add_field(interned_fields,
+                                           get_field_name(ftype, cfsrc),
+                                           cfsrc->cf_type,
+                                           boffset / 8 + cfsrc->cf_offset,
+                                           cfsrc->cf_bitshift,
+                                           cfsrc->cf_bitsize,
+                                           cfsrc->cf_flags | fflags);
+                    if (*previous == NULL)
+                        goto error;
+                    previous = &(*previous)->cf_next;
+                }
+                /* always forbid such structures from being passed by value */
+                ct->ct_flags |= CT_CUSTOM_FIELD_POS;
+            }
+            else {
+                *previous = _add_field(interned_fields, fname, ftype,
+                                        boffset / 8, bs_flag, -1, fflags);
+                if (*previous == NULL)
+                    goto error;
+                previous = &(*previous)->cf_next;
+            }
+            if (ftype->ct_size >= 0)
+                boffset += ftype->ct_size * 8;
+            prev_bitfield_size = 0;
+        }
+        else {
+            /* this is the case of a bitfield */
+            Py_ssize_t field_offset_bytes;
+            int bits_already_occupied, bitshift;
+
+            if (foffset >= 0) {
+                PyErr_Format(PyExc_TypeError,
+                             "field '%s.%s' is a bitfield, "
+                             "but a fixed offset is specified",
+                             ct->ct_name, PyText_AS_UTF8(fname));
+                goto error;
+            }
+
+            if (!(ftype->ct_flags & (CT_PRIMITIVE_SIGNED |
+                                     CT_PRIMITIVE_UNSIGNED |
+                                     CT_PRIMITIVE_CHAR))) {
+                PyErr_Format(PyExc_TypeError,
+                        "field '%s.%s' declared as '%s' cannot be a bit field",
+                             ct->ct_name, PyText_AS_UTF8(fname),
+                             ftype->ct_name);
+                goto error;
+            }
+            if (fbitsize > 8 * ftype->ct_size) {
+                PyErr_Format(PyExc_TypeError,
+                             "bit field '%s.%s' is declared '%s:%d', which "
+                             "exceeds the width of the type",
+                             ct->ct_name, PyText_AS_UTF8(fname),
+                             ftype->ct_name, fbitsize);
+                goto error;
+            }
+
+            /* compute the starting position of the theoretical field
+               that covers a complete 'ftype', inside of which we will
+               locate the real bitfield */
+            field_offset_bytes = boffset / 8;
+            field_offset_bytes &= ~(falign - 1);
+
+            if (fbitsize == 0) {
+                if (PyText_GetSize(fname) > 0) {
+                    PyErr_Format(PyExc_TypeError,
+                                 "field '%s.%s' is declared with :0",
+                                 ct->ct_name, PyText_AS_UTF8(fname));
+                    goto error;
+                }
+                if (!(sflags & SF_MSVC_BITFIELDS)) {
+                    /* GCC's notion of "ftype :0;" */
+
+                    /* pad boffset to a value aligned for "ftype" */
+                    if (boffset > field_offset_bytes * 8) {
+                        field_offset_bytes += falign;
+                        assert(boffset < field_offset_bytes * 8);
+                    }
+                    boffset = field_offset_bytes * 8;
+                }
+                else {
+                    /* MSVC's notion of "ftype :0;" */
+
+                    /* Mostly ignored.  It seems they only serve as
+                       separator between other bitfields, to force them
+                       into separate words. */
+                }
+                prev_bitfield_size = 0;
+            }
+            else {
+                if (!(sflags & SF_MSVC_BITFIELDS)) {
+                    /* GCC's algorithm */
+
+                    /* Can the field start at the offset given by 'boffset'?  It
+                       can if it would entirely fit into an aligned ftype field. */
+                    bits_already_occupied = boffset - (field_offset_bytes * 8);
+
+                    if (bits_already_occupied + fbitsize > 8 * ftype->ct_size) {
+                        /* it would not fit, we need to start at the next
+                           allowed position */
+                        if ((sflags & SF_PACKED) &&
+                            (bits_already_occupied & 7)) {
+                            PyErr_Format(PyExc_NotImplementedError,
+                                "with 'packed', gcc would compile field "
+                                "'%s.%s' to reuse some bits in the previous "
+                                "field", ct->ct_name, PyText_AS_UTF8(fname));
+                            goto error;
+                        }
+                        field_offset_bytes += falign;
+                        assert(boffset < field_offset_bytes * 8);
+                        boffset = field_offset_bytes * 8;
+                        bitshift = 0;
+                    }
+                    else {
+                        bitshift = bits_already_occupied;
+                        assert(bitshift >= 0);
+                    }
+                    boffset += fbitsize;
+                }
+                else {
+                    /* MSVC's algorithm */
+
+                    /* A bitfield is considered as taking the full width
+                       of their declared type.  It can share some bits
+                       with the previous field only if it was also a
+                       bitfield and used a type of the same size. */
+                    if (prev_bitfield_size == ftype->ct_size &&
+                        prev_bitfield_free >= fbitsize) {
+                        /* yes: reuse */
+                        bitshift = 8 * prev_bitfield_size - prev_bitfield_free;
+                    }
+                    else {
+                        /* no: start a new full field */
+                        boffset = (boffset + falign*8-1) & ~(falign*8-1); /*align*/
+                        boffset += ftype->ct_size * 8;
+                        bitshift = 0;
+                        prev_bitfield_size = ftype->ct_size;
+                        prev_bitfield_free = 8 * prev_bitfield_size;
+                    }
+                    prev_bitfield_free -= fbitsize;
+                    field_offset_bytes = boffset / 8 - ftype->ct_size;
+                }
+
+                if (sflags & SF_GCC_BIG_ENDIAN)
+                    bitshift = 8 * ftype->ct_size - fbitsize - bitshift;
+
+                *previous = _add_field(interned_fields, fname, ftype,
+                                       field_offset_bytes, bitshift, fbitsize,
+                                       fflags);
+                if (*previous == NULL)
+                    goto error;
+                previous = &(*previous)->cf_next;
+            }
+        }
+
+        if (boffset > boffsetmax)
+            boffsetmax = boffset;
+    }
+    *previous = NULL;
+
+    /* Like C, if the size of this structure would be zero, we compute it
+       as 1 instead.  But for ctypes support, we allow the manually-
+       specified totalsize to be zero in this case. */
+    boffsetmax = (boffsetmax + 7) / 8;        /* bits -> bytes */
+    alignedsize = (boffsetmax + alignment - 1) & ~(alignment-1);
+    if (alignedsize == 0)
+        alignedsize = 1;
+
+    if (totalsize < 0) {
+        totalsize = alignedsize;
+    }
+    else {
+        if (detect_custom_layout(ct, sflags, alignedsize,
+                                 totalsize, "wrong total size", "", "") < 0)
+            goto error;
+        if (totalsize < boffsetmax) {
+            PyErr_Format(PyExc_TypeError,
+                         "%s cannot be of size %zd: there are fields at least "
+                         "up to %zd", ct->ct_name, totalsize, boffsetmax);
+            goto error;
+        }
+    }
+    if (totalalignment < 0) {
+        totalalignment = alignment;
+    }
+    else {
+        if (detect_custom_layout(ct, sflags, alignment, totalalignment,
+                                 "wrong total alignment", "", "") < 0)
+            goto error;
+    }
+
+    ct->ct_size = totalsize;
+    ct->ct_length = totalalignment;
+    ct->ct_stuff = interned_fields;
+    ct->ct_flags &= ~CT_IS_OPAQUE;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+
+ error:
+    ct->ct_extra = NULL;
+    Py_DECREF(interned_fields);
+    return NULL;
+}
+
+struct funcbuilder_s {
+    Py_ssize_t nb_bytes;
+    char *bufferp;
+    ffi_type **atypes;
+    ffi_type *rtype;
+    Py_ssize_t nargs;
+    CTypeDescrObject *fct;
+};
+
+static void *fb_alloc(struct funcbuilder_s *fb, Py_ssize_t size)
+{
+    if (fb->bufferp == NULL) {
+        fb->nb_bytes += size;
+        return NULL;
+    }
+    else {
+        char *result = fb->bufferp;
+        fb->bufferp += size;
+        return result;
+    }
+}
+
+#define SUPPORTED_IN_API_MODE                                            \
+        " are only supported as %s if the function is "                  \
+        "'API mode' and non-variadic (i.e. declared inside ffibuilder"   \
+        ".cdef()+ffibuilder.set_source() and not taking a final '...' "  \
+        "argument)"
+
+static ffi_type *fb_unsupported(CTypeDescrObject *ct, const char *place,
+                                const char *detail)
+{
+    PyErr_Format(PyExc_NotImplementedError,
+        "ctype '%s' not supported as %s.  %s.  "
+        "Such structs" SUPPORTED_IN_API_MODE,
+        ct->ct_name, place, detail, place);
+    return NULL;
+}
+
+static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct,
+                              int is_result_type)
+{
+    const char *place = is_result_type ? "return value" : "argument";
+
+    if (ct->ct_flags & (CT_PRIMITIVE_ANY & ~CT_PRIMITIVE_COMPLEX)) {
+        return (ffi_type *)ct->ct_extra;
+    }
+    else if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) {
+        return &ffi_type_pointer;
+    }
+    else if ((ct->ct_flags & CT_VOID) && is_result_type) {
+        return &ffi_type_void;
+    }
+
+    if (ct->ct_size <= 0) {
+        PyErr_Format(PyExc_TypeError,
+                     ct->ct_size < 0 ? "ctype '%s' has incomplete type"
+                                     : "ctype '%s' has size 0",
+                     ct->ct_name);
+        return NULL;
+    }
+    if (ct->ct_flags & CT_STRUCT) {
+        ffi_type *ffistruct, *ffifield;
+        ffi_type **elements;
+        Py_ssize_t i, n, nflat;
+        CFieldObject *cf;
+
+        /* We can't pass a struct that was completed by verify().
+           Issue: assume verify() is given "struct { long b; ...; }".
+           Then it will complete it in the same way whether it is actually
+           "struct { long a, b; }" or "struct { double a; long b; }".
+           But on 64-bit UNIX, these two structs are passed by value
+           differently: e.g. on x86-64, "b" ends up in register "rsi" in
+           the first case and "rdi" in the second case.
+
+           Another reason for CT_CUSTOM_FIELD_POS would be anonymous
+           nested structures: we lost the information about having it
+           here, so better safe (and forbid it) than sorry (and maybe
+           crash).  Note: it seems we only get in this case with
+           ffi.verify().
+        */
+        if (force_lazy_struct(ct) < 0)
+            return NULL;
+        if (ct->ct_flags & CT_CUSTOM_FIELD_POS) {
+            /* these NotImplementedErrors may be caught and ignored until
+               a real call is made to a function of this type */
+            return fb_unsupported(ct, place,
+                "It is a struct declared with \"...;\", but the C "
+                "calling convention may depend on the missing fields; "
+                "or, it contains anonymous struct/unions");
+        }
+        /* Another reason: __attribute__((packed)) is not supported by libffi.
+        */
+        if (ct->ct_flags & CT_WITH_PACKED_CHANGE) {
+            return fb_unsupported(ct, place,
+                "It is a 'packed' structure, with a different layout than "
+                "expected by libffi");
+        }
+
+        n = PyDict_Size(ct->ct_stuff);
+        nflat = 0;
+
+        /* walk the fields, expanding arrays into repetitions; first,
+           only count how many flattened fields there are */
+        cf = (CFieldObject *)ct->ct_extra;
+        for (i=0; i<n; i++) {
+            Py_ssize_t flat;
+            CTypeDescrObject *ct1;
+            assert(cf != NULL);
+            if (cf->cf_bitshift >= 0) {
+                return fb_unsupported(ct, place,
+                    "It is a struct with bit fields, which libffi does not "
+                    "support");
+            }
+            flat = 1;
+            ct1 = cf->cf_type;
+            while (ct1->ct_flags & CT_ARRAY) {
+                flat *= ct1->ct_length;
+                ct1 = ct1->ct_itemdescr;
+            }
+            if (flat <= 0) {
+                return fb_unsupported(ct, place,
+                    "It is a struct with a zero-length array, which libffi "
+                    "does not support");
+            }
+            nflat += flat;
+            cf = cf->cf_next;
+        }
+        assert(cf == NULL);
+
+        /* next, allocate and fill the flattened list */
+        elements = fb_alloc(fb, (nflat + 1) * sizeof(ffi_type*));
+        nflat = 0;
+        cf = (CFieldObject *)ct->ct_extra;
+        for (i=0; i<n; i++) {
+            Py_ssize_t j, flat = 1;
+            CTypeDescrObject *ct = cf->cf_type;
+            while (ct->ct_flags & CT_ARRAY) {
+                flat *= ct->ct_length;
+                ct = ct->ct_itemdescr;
+            }
+            ffifield = fb_fill_type(fb, ct, 0);
+            if (PyErr_Occurred())
+                return NULL;
+            if (elements != NULL) {
+                for (j=0; j<flat; j++)
+                    elements[nflat++] = ffifield;
+            }
+            cf = cf->cf_next;
+        }
+
+        /* finally, allocate the FFI_TYPE_STRUCT */
+        ffistruct = fb_alloc(fb, sizeof(ffi_type));
+        if (ffistruct != NULL) {
+            elements[nflat] = NULL;
+            ffistruct->size = ct->ct_size;
+            ffistruct->alignment = ct->ct_length;
+            ffistruct->type = FFI_TYPE_STRUCT;
+            ffistruct->elements = elements;
+        }
+        return ffistruct;
+    }
+    else if (ct->ct_flags & CT_UNION) {
+        PyErr_Format(PyExc_NotImplementedError,
+                     "ctype '%s' not supported as %s by libffi.  "
+                     "Unions" SUPPORTED_IN_API_MODE,
+                     ct->ct_name, place, place);
+        return NULL;
+    }
+    else {
+        char *extra = "";
+        if (ct->ct_flags & CT_PRIMITIVE_COMPLEX)
+            extra = " (the support for complex types inside libffi "
+                    "is mostly missing at this point, so CFFI only "
+                    "supports complex types as arguments or return "
+                    "value in API-mode functions)";
+
+        PyErr_Format(PyExc_NotImplementedError,
+                     "ctype '%s' (size %zd) not supported as %s%s",
+                     ct->ct_name, ct->ct_size, place, extra);
+        return NULL;
+    }
+}
+
+#define ALIGN_ARG(n)  ((n) + 7) & ~7
+
+static int fb_build(struct funcbuilder_s *fb, PyObject *fargs,
+                    CTypeDescrObject *fresult)
+{
+    Py_ssize_t i, nargs = PyTuple_GET_SIZE(fargs);
+    Py_ssize_t exchange_offset;
+    cif_description_t *cif_descr;
+
+    /* ffi buffer: start with a cif_description */
+    cif_descr = fb_alloc(fb, sizeof(cif_description_t) +
+                             nargs * sizeof(Py_ssize_t));
+
+    /* ffi buffer: next comes an array of 'ffi_type*', one per argument */
+    fb->atypes = fb_alloc(fb, nargs * sizeof(ffi_type*));
+    fb->nargs = nargs;
+
+    /* ffi buffer: next comes the result type */
+    fb->rtype = fb_fill_type(fb, fresult, 1);
+    if (PyErr_Occurred())
+        return -1;
+    if (cif_descr != NULL) {
+        /* exchange data size */
+        /* first, enough room for an array of 'nargs' pointers */
+        exchange_offset = nargs * sizeof(void*);
+        exchange_offset = ALIGN_ARG(exchange_offset);
+        cif_descr->exchange_offset_arg[0] = exchange_offset;
+        /* then enough room for the result --- which means at least
+           sizeof(ffi_arg), according to the ffi docs */
+        i = fb->rtype->size;
+        if (i < (Py_ssize_t)sizeof(ffi_arg))
+            i = sizeof(ffi_arg);
+        exchange_offset += i;
+    }
+    else
+        exchange_offset = 0;   /* not used */
+
+    /* loop over the arguments */
+    for (i=0; i<nargs; i++) {
+        CTypeDescrObject *farg;
+        ffi_type *atype;
+
+        farg = (CTypeDescrObject *)PyTuple_GET_ITEM(fargs, i);
+        /* convert arrays to pointers */
+        if (farg->ct_flags & CT_ARRAY)
+            farg = (CTypeDescrObject *)farg->ct_stuff;
+
+        /* ffi buffer: fill in the ffi for the i'th argument */
+        assert(farg != NULL);
+        atype = fb_fill_type(fb, farg, 0);
+        if (PyErr_Occurred())
+            return -1;
+
+        if (fb->atypes != NULL) {
+            fb->atypes[i] = atype;
+            /* exchange data size */
+            exchange_offset = ALIGN_ARG(exchange_offset);
+            cif_descr->exchange_offset_arg[1 + i] = exchange_offset;
+            exchange_offset += atype->size;
+        }
+    }
+
+    if (cif_descr != NULL) {
+        /* exchange data size */
+        /* we also align it to the next multiple of 8, in an attempt to
+           work around bugs(?) of libffi like #241 */
+        cif_descr->exchange_size = ALIGN_ARG(exchange_offset);
+    }
+    return 0;
+}
+
+#undef ALIGN_ARG
+
+static void fb_cat_name(struct funcbuilder_s *fb, const char *piece,
+                        int piecelen)
+{
+    if (fb->bufferp == NULL) {
+        fb->nb_bytes += piecelen;
+    }
+    else {
+        memcpy(fb->bufferp, piece, piecelen);
+        fb->bufferp += piecelen;
+    }
+}
+
+static int fb_build_name(struct funcbuilder_s *fb, const char *repl,
+                         CTypeDescrObject **pfargs, Py_ssize_t nargs,
+                         CTypeDescrObject *fresult, int ellipsis)
+{
+    Py_ssize_t i;
+    fb->nargs = nargs;
+
+    /* name: the function type name we build here is, like in C, made
+       as follows:
+
+         RESULT_TYPE_HEAD (*)(ARG_1_TYPE, ARG_2_TYPE, etc) RESULT_TYPE_TAIL
+    */
+    fb_cat_name(fb, fresult->ct_name, fresult->ct_name_position);
+    if (repl[0] != '(' &&
+        fresult->ct_name[fresult->ct_name_position - 1] != '*')
+        fb_cat_name(fb, " ", 1);   /* add a space */
+    fb_cat_name(fb, repl, strlen(repl));
+    if (fb->fct) {
+        i = strlen(repl) - 1;      /* between '(*' and ')' */
+        assert(repl[i] == ')');
+        fb->fct->ct_name_position = fresult->ct_name_position + i;
+    }
+    fb_cat_name(fb, "(", 1);
+
+    /* loop over the arguments */
+    for (i=0; i<nargs; i++) {
+        CTypeDescrObject *farg;
+
+        farg = pfargs[i];
+        if (!CTypeDescr_Check(farg)) {
+            PyErr_SetString(PyExc_TypeError, "expected a tuple of ctypes");
+            return -1;
+        }
+        /* name: concatenate the name of the i'th argument's type */
+        if (i > 0)
+            fb_cat_name(fb, ", ", 2);
+        fb_cat_name(fb, farg->ct_name, strlen(farg->ct_name));
+    }
+
+    /* name: add the '...' if needed */
+    if (ellipsis) {
+        if (nargs > 0)
+            fb_cat_name(fb, ", ", 2);
+        fb_cat_name(fb, "...", 3);
+    }
+
+    /* name: concatenate the tail of the result type */
+    fb_cat_name(fb, ")", 1);
+    fb_cat_name(fb, fresult->ct_name + fresult->ct_name_position,
+                strlen(fresult->ct_name) - fresult->ct_name_position + 1);
+    return 0;
+}
+
+static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb,
+                                          PyObject *fargs,
+                                          CTypeDescrObject *fresult,
+                                          int ellipsis, int fabi)
+{
+    CTypeDescrObject *fct, **pfargs;
+    Py_ssize_t nargs;
+    char *repl = "(*)";
+
+    fb->nb_bytes = 0;
+    fb->bufferp = NULL;
+    fb->fct = NULL;
+
+    pfargs = (CTypeDescrObject **)&PyTuple_GET_ITEM(fargs, 0);
+    nargs = PyTuple_GET_SIZE(fargs);
+#if defined(MS_WIN32) && !defined(_WIN64)
+    if (fabi == FFI_STDCALL)
+        repl = "(__stdcall *)";
+#endif
+
+    /* compute the total size needed for the name */
+    if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0)
+        return NULL;
+
+    /* allocate the function type */
+    fct = ctypedescr_new(fb->nb_bytes);
+    if (fct == NULL)
+        return NULL;
+    fb->fct = fct;
+
+    /* call again fb_build_name() to really build the ct_name */
+    fb->bufferp = fct->ct_name;
+    if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0)
+        goto error;
+    assert(fb->bufferp == fct->ct_name + fb->nb_bytes);
+
+    fct->ct_extra = NULL;
+    fct->ct_size = sizeof(void(*)(void));
+    fct->ct_flags = CT_FUNCTIONPTR;
+    return fct;
+
+ error:
+    Py_DECREF(fct);
+    return NULL;
+}
+
+static cif_description_t *fb_prepare_cif(PyObject *fargs,
+                                         CTypeDescrObject *fresult,
+                                         ffi_abi fabi)
+{
+    char *buffer;
+    cif_description_t *cif_descr;
+    struct funcbuilder_s funcbuffer;
+
+    funcbuffer.nb_bytes = 0;
+    funcbuffer.bufferp = NULL;
+
+    /* compute the total size needed in the buffer for libffi */
+    if (fb_build(&funcbuffer, fargs, fresult) < 0)
+        return NULL;
+
+    /* allocate the buffer */
+    buffer = PyObject_Malloc(funcbuffer.nb_bytes);
+    if (buffer == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    /* call again fb_build() to really build the libffi data structures */
+    funcbuffer.bufferp = buffer;
+    if (fb_build(&funcbuffer, fargs, fresult) < 0)
+        goto error;
+    assert(funcbuffer.bufferp == buffer + funcbuffer.nb_bytes);
+
+    cif_descr = (cif_description_t *)buffer;
+    if (ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs,
+                     funcbuffer.rtype, funcbuffer.atypes) != FFI_OK) {
+        PyErr_SetString(PyExc_SystemError,
+                        "libffi failed to build this function type");
+        goto error;
+    }
+    return cif_descr;
+
+ error:
+    PyObject_Free(buffer);
+    return NULL;
+}
+
+static PyObject *new_function_type(PyObject *fargs,   /* tuple */
+                                   CTypeDescrObject *fresult,
+                                   int ellipsis, int fabi)
+{
+    PyObject *fabiobj;
+    CTypeDescrObject *fct;
+    struct funcbuilder_s funcbuilder;
+    Py_ssize_t i;
+    const void **unique_key;
+
+    if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) ||
+        (fresult->ct_flags & CT_ARRAY)) {
+        char *msg;
+        if (fresult->ct_flags & CT_IS_OPAQUE)
+            msg = "result type '%s' is opaque";
+        else
+            msg = "invalid result type: '%s'";
+        PyErr_Format(PyExc_TypeError, msg, fresult->ct_name);
+        return NULL;
+    }
+
+    fct = fb_prepare_ctype(&funcbuilder, fargs, fresult, ellipsis, fabi);
+    if (fct == NULL)
+        return NULL;
+
+    if (!ellipsis) {
+        /* Functions with '...' varargs are stored without a cif_descr
+           at all.  The cif is computed on every call from the actual
+           types passed in.  For all other functions, the cif_descr
+           is computed here. */
+        cif_description_t *cif_descr;
+
+        cif_descr = fb_prepare_cif(fargs, fresult, fabi);
+        if (cif_descr == NULL) {
+            if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) {
+                PyErr_Clear();   /* will get the exception if we see an
+                                    actual call */
+            }
+            else
+                goto error;
+        }
+
+        fct->ct_extra = (char *)cif_descr;
+    }
+
+    /* build the signature, given by a tuple of ctype objects */
+    fct->ct_stuff = PyTuple_New(2 + funcbuilder.nargs);
+    if (fct->ct_stuff == NULL)
+        goto error;
+    fabiobj = PyInt_FromLong(fabi);
+    if (fabiobj == NULL)
+        goto error;
+    PyTuple_SET_ITEM(fct->ct_stuff, 0, fabiobj);
+
+    Py_INCREF(fresult);
+    PyTuple_SET_ITEM(fct->ct_stuff, 1, (PyObject *)fresult);
+    for (i=0; i<funcbuilder.nargs; i++) {
+        PyObject *o = PyTuple_GET_ITEM(fargs, i);
+        /* convert arrays into pointers */
+        if (((CTypeDescrObject *)o)->ct_flags & CT_ARRAY)
+            o = ((CTypeDescrObject *)o)->ct_stuff;
+        Py_INCREF(o);
+        PyTuple_SET_ITEM(fct->ct_stuff, 2 + i, o);
+    }
+
+    /* [ctresult, ellipsis+abi, num_args, ctargs...] */
+    unique_key = alloca((3 + funcbuilder.nargs) * sizeof(void *));
+    unique_key[0] = fresult;
+    unique_key[1] = (const void *)(Py_ssize_t)((fabi << 1) | !!ellipsis);
+    unique_key[2] = (const void *)(Py_ssize_t)(funcbuilder.nargs);
+    for (i=0; i<funcbuilder.nargs; i++)
+        unique_key[3 + i] = PyTuple_GET_ITEM(fct->ct_stuff, 2 + i);
+    return get_unique_type(fct, unique_key, 3 + funcbuilder.nargs);
+
+ error:
+    Py_DECREF(fct);
+    return NULL;
+}
+
+static PyObject *b_new_function_type(PyObject *self, PyObject *args)
+{
+    PyObject *fargs;
+    CTypeDescrObject *fresult;
+    int ellipsis = 0, fabi = FFI_DEFAULT_ABI;
+
+    if (!PyArg_ParseTuple(args, "O!O!|ii:new_function_type",
+                          &PyTuple_Type, &fargs,
+                          &CTypeDescr_Type, &fresult,
+                          &ellipsis,
+                          &fabi))
+        return NULL;
+
+    return new_function_type(fargs, fresult, ellipsis, fabi);
+}
+
+static int convert_from_object_fficallback(char *result,
+                                           CTypeDescrObject *ctype,
+                                           PyObject *pyobj,
+                                           int encode_result_for_libffi)
+{
+    /* work work work around a libffi irregularity: for integer return
+       types we have to fill at least a complete 'ffi_arg'-sized result
+       buffer. */
+    if (ctype->ct_size < (Py_ssize_t)sizeof(ffi_arg)) {
+        if (ctype->ct_flags & CT_VOID) {
+            if (pyobj == Py_None) {
+                return 0;
+            }
+            else {
+                PyErr_SetString(PyExc_TypeError,
+                    "callback with the return type 'void' must return None");
+                return -1;
+            }
+        }
+        if (!encode_result_for_libffi)
+            goto skip;
+        if (ctype->ct_flags & CT_PRIMITIVE_SIGNED) {
+            PY_LONG_LONG value;
+            /* It's probably fine to always zero-extend, but you never
+               know: maybe some code somewhere expects a negative
+               'short' result to be returned into EAX as a 32-bit
+               negative number.  Better safe than sorry.  This code
+               is about that case.  Let's ignore this for enums.
+            */
+            /* do a first conversion only to detect overflows.  This
+               conversion produces stuff that is otherwise ignored. */
+            if (convert_from_object(result, ctype, pyobj) < 0)
+                return -1;
+            /* manual inlining and tweaking of convert_from_object()
+               in order to write a whole 'ffi_arg'. */
+            value = _my_PyLong_AsLongLong(pyobj);
+            if (value == -1 && PyErr_Occurred())
+                return -1;
+            write_raw_integer_data(result, value, sizeof(ffi_arg));
+            return 0;
+        }
+        else if (ctype->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED |
+                                    CT_PRIMITIVE_UNSIGNED |
+                                    CT_POINTER | CT_FUNCTIONPTR)) {
+            /* zero extension: fill the '*result' with zeros, and (on big-
+               endian machines) correct the 'result' pointer to write to.
+               We also do that for pointers, even though we're normally not
+               in this branch because ctype->ct_size == sizeof(ffi_arg) for
+               pointers---except on some architectures like x32 (issue #372).
+             */
+            memset(result, 0, sizeof(ffi_arg));
+#ifdef WORDS_BIGENDIAN
+            result += (sizeof(ffi_arg) - ctype->ct_size);
+#endif
+        }
+    }
+ skip:
+    return convert_from_object(result, ctype, pyobj);
+}
+
+static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb,
+                                      char *objdescr, PyObject *obj,
+                                      char *extra_error_line)
+{
+    /* like PyErr_WriteUnraisable(), but write a full traceback */
+    PyObject *f;
+#if PY_MAJOR_VERSION >= 3
+    /* jump through hoops to ensure the tb is attached to v, on Python 3 */
+    PyErr_NormalizeException(&t, &v, &tb);
+    if (tb == NULL) {
+        tb = Py_None;
+        Py_INCREF(tb);
+    }
+    PyException_SetTraceback(v, tb);
+#endif
+    f = PySys_GetObject("stderr");
+    if (f != NULL) {
+        if (obj != NULL) {
+            PyFile_WriteString(objdescr, f);
+            PyFile_WriteObject(obj, f, 0);
+            PyFile_WriteString(":\n", f);
+        }
+        if (extra_error_line != NULL)
+            PyFile_WriteString(extra_error_line, f);
+        PyErr_Display(t, v, tb);
+    }
+    Py_XDECREF(t);
+    Py_XDECREF(v);
+    Py_XDECREF(tb);
+}
+
+static void general_invoke_callback(int decode_args_from_libffi,
+                                    void *result, char *args, void *userdata)
+{
+    PyObject *cb_args = (PyObject *)userdata;
+    CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0);
+    PyObject *signature = ct->ct_stuff;
+    PyObject *py_ob = PyTuple_GET_ITEM(cb_args, 1);
+    PyObject *py_args = NULL;
+    PyObject *py_res = NULL;
+    PyObject *py_rawerr;
+    PyObject *onerror_cb;
+    Py_ssize_t i, n;
+    char *extra_error_line = NULL;
+
+#define SIGNATURE(i)  ((CTypeDescrObject *)PyTuple_GET_ITEM(signature, i))
+
+    Py_INCREF(cb_args);
+
+    n = PyTuple_GET_SIZE(signature) - 2;
+    py_args = PyTuple_New(n);
+    if (py_args == NULL)
+        goto error;
+
+    for (i=0; i<n; i++) {
+        char *a_src;
+        PyObject *a;
+        CTypeDescrObject *a_ct = SIGNATURE(2 + i);
+
+        if (decode_args_from_libffi) {
+            a_src = ((void **)args)[i];
+        }
+        else {
+            a_src = args + i * 8;
+            if (a_ct->ct_flags & (CT_IS_LONGDOUBLE | CT_STRUCT | CT_UNION))
+                a_src = *(char **)a_src;
+        }
+        a = convert_to_object(a_src, a_ct);
+        if (a == NULL)
+            goto error;
+        PyTuple_SET_ITEM(py_args, i, a);
+    }
+
+    py_res = PyObject_Call(py_ob, py_args, NULL);
+    if (py_res == NULL)
+        goto error;
+    if (convert_from_object_fficallback(result, SIGNATURE(1), py_res,
+                                        decode_args_from_libffi) < 0) {
+        extra_error_line = "Trying to convert the result back to C:\n";
+        goto error;
+    }
+ done:
+    Py_XDECREF(py_args);
+    Py_XDECREF(py_res);
+    Py_DECREF(cb_args);
+    return;
+
+ error:
+    if (SIGNATURE(1)->ct_size > 0) {
+        py_rawerr = PyTuple_GET_ITEM(cb_args, 2);
+        memcpy(result, PyBytes_AS_STRING(py_rawerr),
+                       PyBytes_GET_SIZE(py_rawerr));
+    }
+    onerror_cb = PyTuple_GET_ITEM(cb_args, 3);
+    if (onerror_cb == Py_None) {
+        PyObject *ecap, *t, *v, *tb;
+        PyErr_Fetch(&t, &v, &tb);
+        ecap = _cffi_start_error_capture();
+        _my_PyErr_WriteUnraisable(t, v, tb, "From cffi callback ", py_ob,
+                                  extra_error_line);
+        _cffi_stop_error_capture(ecap);
+    }
+    else {
+        PyObject *exc1, *val1, *tb1, *res1, *exc2, *val2, *tb2;
+        PyErr_Fetch(&exc1, &val1, &tb1);
+        PyErr_NormalizeException(&exc1, &val1, &tb1);
+        res1 = PyObject_CallFunctionObjArgs(onerror_cb,
+                                            exc1 ? exc1 : Py_None,
+                                            val1 ? val1 : Py_None,
+                                            tb1  ? tb1  : Py_None,
+                                            NULL);
+        if (res1 != NULL) {
+            if (res1 != Py_None)
+                convert_from_object_fficallback(result, SIGNATURE(1), res1,
+                                                decode_args_from_libffi);
+            Py_DECREF(res1);
+        }
+        if (!PyErr_Occurred()) {
+            Py_XDECREF(exc1);
+            Py_XDECREF(val1);
+            Py_XDECREF(tb1);
+        }
+        else {
+            /* double exception! print a double-traceback... */
+            PyObject *ecap;
+            PyErr_Fetch(&exc2, &val2, &tb2);
+            ecap = _cffi_start_error_capture();
+            _my_PyErr_WriteUnraisable(exc1, val1, tb1,
+                                      "From cffi callback ", py_ob,
+                                      extra_error_line);
+            extra_error_line = ("\nDuring the call to 'onerror', "
+                                "another exception occurred:\n\n");
+            _my_PyErr_WriteUnraisable(exc2, val2, tb2,
+                                      NULL, NULL, extra_error_line);
+            _cffi_stop_error_capture(ecap);
+        }
+    }
+    goto done;
+
+#undef SIGNATURE
+}
+
+static void invoke_callback(ffi_cif *cif, void *result, void **args,
+                            void *userdata)
+{
+    save_errno();
+    {
+        PyGILState_STATE state = gil_ensure();
+        general_invoke_callback(1, result, (char *)args, userdata);
+        gil_release(state);
+    }
+    restore_errno();
+}
+
+static PyObject *prepare_callback_info_tuple(CTypeDescrObject *ct,
+                                             PyObject *ob,
+                                             PyObject *error_ob,
+                                             PyObject *onerror_ob,
+                                             int decode_args_from_libffi)
+{
+    CTypeDescrObject *ctresult;
+    PyObject *py_rawerr, *infotuple;
+    Py_ssize_t size;
+
+    if (!(ct->ct_flags & CT_FUNCTIONPTR)) {
+        PyErr_Format(PyExc_TypeError, "expected a function ctype, got '%s'",
+                     ct->ct_name);
+        return NULL;
+    }
+    if (!PyCallable_Check(ob)) {
+        PyErr_Format(PyExc_TypeError,
+                     "expected a callable object, not %.200s",
+                     Py_TYPE(ob)->tp_name);
+        return NULL;
+    }
+    if (onerror_ob != Py_None && !PyCallable_Check(onerror_ob)) {
+        PyErr_Format(PyExc_TypeError,
+                     "expected a callable object for 'onerror', not %.200s",
+                     Py_TYPE(onerror_ob)->tp_name);
+        return NULL;
+    }
+
+    ctresult = (CTypeDescrObject *)PyTuple_GET_ITEM(ct->ct_stuff, 1);
+    size = ctresult->ct_size;
+    if (size < (Py_ssize_t)sizeof(ffi_arg))
+        size = sizeof(ffi_arg);
+    py_rawerr = PyBytes_FromStringAndSize(NULL, size);
+    if (py_rawerr == NULL)
+        return NULL;
+    memset(PyBytes_AS_STRING(py_rawerr), 0, size);
+    if (error_ob != Py_None) {
+        if (convert_from_object_fficallback(
+                PyBytes_AS_STRING(py_rawerr), ctresult, error_ob,
+                decode_args_from_libffi) < 0) {
+            Py_DECREF(py_rawerr);
+            return NULL;
+        }
+    }
+    infotuple = Py_BuildValue("OOOO", ct, ob, py_rawerr, onerror_ob);
+    Py_DECREF(py_rawerr);
+
+#ifdef WITH_THREAD
+    /* We must setup the GIL here, in case the callback is invoked in
+       some other non-Pythonic thread.  This is the same as ctypes. */
+    PyEval_InitThreads();
+#endif
+
+    return infotuple;
+}
+
+static PyObject *b_callback(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    CDataObject_closure *cd;
+    PyObject *ob, *error_ob = Py_None, *onerror_ob = Py_None;
+    PyObject *infotuple;
+    cif_description_t *cif_descr;
+    ffi_closure *closure;
+    void *closure_exec;
+
+    if (!PyArg_ParseTuple(args, "O!O|OO:callback", &CTypeDescr_Type, &ct, &ob,
+                          &error_ob, &onerror_ob))
+        return NULL;
+
+    infotuple = prepare_callback_info_tuple(ct, ob, error_ob, onerror_ob, 1);
+    if (infotuple == NULL)
+        return NULL;
+
+#ifdef CFFI_TRUST_LIBFFI
+    closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec);
+#else
+    closure = cffi_closure_alloc();
+    closure_exec = closure;
+#endif
+    if (closure == NULL) {
+        Py_DECREF(infotuple);
+        return NULL;
+    }
+    cd = PyObject_GC_New(CDataObject_closure, &CDataOwningGC_Type);
+    if (cd == NULL)
+        goto error;
+    Py_INCREF(ct);
+    cd->head.c_type = ct;
+    cd->head.c_data = (char *)closure_exec;
+    cd->head.c_weakreflist = NULL;
+    cd->closure = closure;
+    PyObject_GC_Track(cd);
+
+    cif_descr = (cif_description_t *)ct->ct_extra;
+    if (cif_descr == NULL) {
+        PyErr_Format(PyExc_NotImplementedError,
+                     "%s: callback with unsupported argument or "
+                     "return type or with '...'", ct->ct_name);
+        goto error;
+    }
+#ifdef CFFI_TRUST_LIBFFI
+    if (ffi_prep_closure_loc(closure, &cif_descr->cif,
+                         invoke_callback, infotuple, closure_exec) != FFI_OK) {
+#else
+    if (ffi_prep_closure(closure, &cif_descr->cif,
+                         invoke_callback, infotuple) != FFI_OK) {
+#endif
+        PyErr_SetString(PyExc_SystemError,
+                        "libffi failed to build this callback");
+        goto error;
+    }
+    if (closure->user_data != infotuple) {
+        /* Issue #266.  Should not occur, but could, if we are using
+           at runtime a version of libffi compiled with a different
+           'ffi_closure' structure than the one we expect from ffi.h
+           (e.g. difference in details of the platform): a difference
+           in FFI_TRAMPOLINE_SIZE means that the 'user_data' field
+           ends up somewhere else, and so the test above fails.
+        */
+        PyErr_SetString(PyExc_SystemError,
+            "ffi_prep_closure(): bad user_data (it seems that the "
+            "version of the libffi library seen at runtime is "
+            "different from the 'ffi.h' file seen at compile-time)");
+        goto error;
+    }
+    return (PyObject *)cd;
+
+ error:
+    closure->user_data = NULL;
+    if (cd == NULL) {
+#ifdef CFFI_TRUST_LIBFFI
+        ffi_closure_free(closure);
+#else
+        cffi_closure_free(closure);
+#endif
+    }
+    else
+        Py_DECREF(cd);
+    Py_XDECREF(infotuple);
+    return NULL;
+}
+
+static PyObject *b_new_enum_type(PyObject *self, PyObject *args)
+{
+    char *ename;
+    PyObject *enumerators, *enumvalues;
+    PyObject *dict1 = NULL, *dict2 = NULL, *combined = NULL, *tmpkey = NULL;
+    int name_size;
+    CTypeDescrObject *td, *basetd;
+    Py_ssize_t i, n;
+
+    if (!PyArg_ParseTuple(args, "sO!O!O!:new_enum_type",
+                          &ename,
+                          &PyTuple_Type, &enumerators,
+                          &PyTuple_Type, &enumvalues,
+                          &CTypeDescr_Type, &basetd))
+        return NULL;
+
+    n = PyTuple_GET_SIZE(enumerators);
+    if (n != PyTuple_GET_SIZE(enumvalues)) {
+        PyErr_SetString(PyExc_ValueError,
+                        "tuple args must have the same size");
+        return NULL;
+    }
+
+    if (!(basetd->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED))) {
+        PyErr_SetString(PyExc_TypeError,
+                        "expected a primitive signed or unsigned base type");
+        return NULL;
+    }
+
+    dict1 = PyDict_New();
+    if (dict1 == NULL)
+        goto error;
+    dict2 = PyDict_New();
+    if (dict2 == NULL)
+        goto error;
+
+    for (i=n; --i >= 0; ) {
+        long long lvalue;
+        PyObject *value = PyTuple_GET_ITEM(enumvalues, i);
+        tmpkey = PyTuple_GET_ITEM(enumerators, i);
+        Py_INCREF(tmpkey);
+        if (!PyText_Check(tmpkey)) {
+#if PY_MAJOR_VERSION < 3
+            if (PyUnicode_Check(tmpkey)) {
+                const char *text = PyText_AsUTF8(tmpkey);
+                if (text == NULL)
+                    goto error;
+                Py_DECREF(tmpkey);
+                tmpkey = PyString_FromString(text);
+                if (tmpkey == NULL)
+                    goto error;
+            }
+            else
+#endif
+            {
+                PyErr_SetString(PyExc_TypeError,
+                                "enumerators must be a list of strings");
+                goto error;
+            }
+        }
+        if (convert_from_object((char*)&lvalue, basetd, value) < 0)
+            goto error;     /* out-of-range or badly typed 'value' */
+        if (PyDict_SetItem(dict1, tmpkey, value) < 0)
+            goto error;
+        if (PyDict_SetItem(dict2, value, tmpkey) < 0)
+            goto error;
+        Py_DECREF(tmpkey);
+        tmpkey = NULL;
+    }
+
+    combined = PyTuple_Pack(2, dict1, dict2);
+    if (combined == NULL)
+        goto error;
+
+    Py_CLEAR(dict2);
+    Py_CLEAR(dict1);
+
+    name_size = strlen(ename) + 1;
+    td = ctypedescr_new(name_size);
+    if (td == NULL)
+        goto error;
+
+    memcpy(td->ct_name, ename, name_size);
+    td->ct_stuff = combined;
+    td->ct_size = basetd->ct_size;
+    td->ct_length = basetd->ct_length;   /* alignment */
+    td->ct_extra = basetd->ct_extra;     /* ffi type  */
+    td->ct_flags = basetd->ct_flags | CT_IS_ENUM;
+    td->ct_name_position = name_size - 1;
+    return (PyObject *)td;
+
+ error:
+    Py_XDECREF(tmpkey);
+    Py_XDECREF(combined);
+    Py_XDECREF(dict2);
+    Py_XDECREF(dict1);
+    return NULL;
+}
+
+static PyObject *b_alignof(PyObject *self, PyObject *arg)
+{
+    int align;
+    if (!CTypeDescr_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "expected a 'ctype' object");
+        return NULL;
+    }
+    align = get_alignment((CTypeDescrObject *)arg);
+    if (align < 0)
+        return NULL;
+    return PyInt_FromLong(align);
+}
+
+static Py_ssize_t direct_sizeof_cdata(CDataObject *cd)
+{
+    Py_ssize_t size;
+    if (cd->c_type->ct_flags & CT_ARRAY)
+        size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
+    else {
+        size = -1;
+        if (cd->c_type->ct_flags & (CT_STRUCT | CT_UNION))
+            size = _cdata_var_byte_size(cd);
+        if (size < 0)
+            size = cd->c_type->ct_size;
+    }
+    return size;
+}
+
+static PyObject *b_sizeof(PyObject *self, PyObject *arg)
+{
+    Py_ssize_t size;
+
+    if (CData_Check(arg)) {
+        size = direct_sizeof_cdata((CDataObject *)arg);
+    }
+    else if (CTypeDescr_Check(arg)) {
+        size = ((CTypeDescrObject *)arg)->ct_size;
+        if (size < 0) {
+            PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown size",
+                         ((CTypeDescrObject *)arg)->ct_name);
+            return NULL;
+        }
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError,
+                        "expected a 'cdata' or 'ctype' object");
+        return NULL;
+    }
+    return PyInt_FromSsize_t(size);
+}
+
+static PyObject *b_typeof(PyObject *self, PyObject *arg)
+{
+    PyObject *res;
+
+    if (!CData_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object");
+        return NULL;
+    }
+    res = (PyObject *)((CDataObject *)arg)->c_type;
+    Py_INCREF(res);
+    return res;
+}
+
+static CTypeDescrObject *direct_typeoffsetof(CTypeDescrObject *ct,
+                                             PyObject *fieldname,
+                                             int following, Py_ssize_t *offset)
+{
+    /* Does not return a new reference! */
+    CTypeDescrObject *res;
+    CFieldObject *cf;
+
+    if (PyTextAny_Check(fieldname)) {
+        if (!following && (ct->ct_flags & CT_POINTER))
+            ct = ct->ct_itemdescr;
+        if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) {
+            PyErr_SetString(PyExc_TypeError,
+                            "with a field name argument, expected a "
+                            "struct or union ctype");
+            return NULL;
+        }
+        if (force_lazy_struct(ct) <= 0) {
+            if (!PyErr_Occurred())
+                PyErr_SetString(PyExc_TypeError, "struct/union is opaque");
+            return NULL;
+        }
+        cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname);
+        if (cf == NULL) {
+            PyErr_SetObject(PyExc_KeyError, fieldname);
+            return NULL;
+        }
+        if (cf->cf_bitshift >= 0) {
+            PyErr_SetString(PyExc_TypeError, "not supported for bitfields");
+            return NULL;
+        }
+        res = cf->cf_type;
+        *offset = cf->cf_offset;
+    }
+    else {
+        Py_ssize_t index = PyInt_AsSsize_t(fieldname);
+        if (index < 0 && PyErr_Occurred()) {
+            PyErr_SetString(PyExc_TypeError,
+                            "field name or array index expected");
+            return NULL;
+        }
+
+        if (!(ct->ct_flags & (CT_ARRAY|CT_POINTER)) ||
+                ct->ct_itemdescr->ct_size < 0) {
+            PyErr_SetString(PyExc_TypeError, "with an integer argument, "
+                                             "expected an array ctype or a "
+                                             "pointer to non-opaque");
+            return NULL;
+        }
+        res = ct->ct_itemdescr;
+        *offset = MUL_WRAPAROUND(index, ct->ct_itemdescr->ct_size);
+        if ((*offset / ct->ct_itemdescr->ct_size) != index) {
+            PyErr_SetString(PyExc_OverflowError,
+                            "array offset would overflow a Py_ssize_t");
+            return NULL;
+        }
+    }
+    return res;
+}
+
+static PyObject *b_typeoffsetof(PyObject *self, PyObject *args)
+{
+    PyObject *res, *fieldname;
+    CTypeDescrObject *ct;
+    Py_ssize_t offset;
+    int following = 0;
+
+    if (!PyArg_ParseTuple(args, "O!O|i:typeoffsetof",
+                          &CTypeDescr_Type, &ct, &fieldname, &following))
+        return NULL;
+
+    res = (PyObject *)direct_typeoffsetof(ct, fieldname, following, &offset);
+    if (res == NULL)
+        return NULL;
+
+    return Py_BuildValue("(On)", res, offset);
+}
+
+static PyObject *b_rawaddressof(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    CDataObject *cd;
+    Py_ssize_t offset;
+    int accepted_flags;
+
+    if (!PyArg_ParseTuple(args, "O!O!n:rawaddressof",
+                          &CTypeDescr_Type, &ct,
+                          &CData_Type, &cd,
+                          &offset))
+        return NULL;
+
+    accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER;
+    if ((cd->c_type->ct_flags & accepted_flags) == 0) {
+        PyErr_SetString(PyExc_TypeError,
+                        "expected a cdata struct/union/array/pointer object");
+        return NULL;
+    }
+    if ((ct->ct_flags & CT_POINTER) == 0) {
+        PyErr_SetString(PyExc_TypeError,
+                        "expected a pointer ctype");
+        return NULL;
+    }
+    return new_simple_cdata(cd->c_data + offset, ct);
+}
+
+static PyObject *b_getcname(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    char *replace_with, *p, *s;
+    Py_ssize_t namelen, replacelen;
+
+    if (!PyArg_ParseTuple(args, "O!s:getcname",
+                          &CTypeDescr_Type, &ct, &replace_with))
+        return NULL;
+
+    namelen = strlen(ct->ct_name);
+    replacelen = strlen(replace_with);
+    s = p = alloca(namelen + replacelen + 1);
+    memcpy(p, ct->ct_name, ct->ct_name_position);
+    p += ct->ct_name_position;
+    memcpy(p, replace_with, replacelen);
+    p += replacelen;
+    memcpy(p, ct->ct_name + ct->ct_name_position,
+           namelen - ct->ct_name_position);
+
+    return PyText_FromStringAndSize(s, namelen + replacelen);
+}
+
+static PyObject *b_string(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    CDataObject *cd;
+    Py_ssize_t maxlen = -1;
+    static char *keywords[] = {"cdata", "maxlen", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:string", keywords,
+                                     &CData_Type, &cd, &maxlen))
+        return NULL;
+
+    if (cd->c_type->ct_itemdescr != NULL &&
+        cd->c_type->ct_itemdescr->ct_flags & (CT_PRIMITIVE_CHAR |
+                                              CT_PRIMITIVE_SIGNED |
+                                              CT_PRIMITIVE_UNSIGNED) &&
+        !(cd->c_type->ct_itemdescr->ct_flags & CT_IS_BOOL)) {
+        Py_ssize_t length = maxlen;
+        if (cd->c_data == NULL) {
+            PyObject *s = cdata_repr(cd);
+            if (s != NULL) {
+                PyErr_Format(PyExc_RuntimeError,
+                             "cannot use string() on %s",
+                             PyText_AS_UTF8(s));
+                Py_DECREF(s);
+            }
+            return NULL;
+        }
+        if (length < 0 && cd->c_type->ct_flags & CT_ARRAY) {
+            length = get_array_length(cd);
+        }
+        if (cd->c_type->ct_itemdescr->ct_size == sizeof(char)) {
+            const char *start = cd->c_data;
+            if (length < 0) {
+                /*READ(start, 1)*/
+                length = strlen(start);
+                /*READ(start, length)*/
+            }
+            else {
+                const char *end;
+                /*READ(start, length)*/
+                end = (const char *)memchr(start, 0, length);
+                if (end != NULL)
+                    length = end - start;
+            }
+            return PyBytes_FromStringAndSize(start, length);
+        }
+        else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) {
+            switch (cd->c_type->ct_itemdescr->ct_size) {
+            case 2: {
+                const cffi_char16_t *start = (cffi_char16_t *)cd->c_data;
+                if (length < 0) {
+                    /*READ(start, 2)*/
+                    length = 0;
+                    while (start[length])
+                        length++;
+                    /*READ(start, 2 * length)*/
+                }
+                else {
+                    /*READ(start, 2 * length)*/
+                    maxlen = length;
+                    length = 0;
+                    while (length < maxlen && start[length])
+                        length++;
+                }
+                return _my_PyUnicode_FromChar16(start, length);
+            }
+            case 4: {
+                const cffi_char32_t *start = (cffi_char32_t *)cd->c_data;
+                if (length < 0) {
+                    /*READ(start, 4)*/
+                    length = 0;
+                    while (start[length])
+                        length++;
+                    /*READ(start, 4 * length)*/
+                }
+                else {
+                    /*READ(start, 4 * length)*/
+                    maxlen = length;
+                    length = 0;
+                    while (length < maxlen && start[length])
+                        length++;
+                }
+                return _my_PyUnicode_FromChar32(start, length);
+            }
+            }
+        }
+    }
+    else if (cd->c_type->ct_flags & CT_IS_ENUM) {
+        return convert_cdata_to_enum_string(cd, 0);
+    }
+    else if (cd->c_type->ct_flags & CT_IS_BOOL) {
+        /* fall through to TypeError */
+    }
+    else if (cd->c_type->ct_flags & (CT_PRIMITIVE_CHAR |
+                                     CT_PRIMITIVE_SIGNED |
+                                     CT_PRIMITIVE_UNSIGNED)) {
+        /*READ(cd->c_data, cd->c_type->ct_size)*/
+        if (cd->c_type->ct_size == sizeof(char))
+            return PyBytes_FromStringAndSize(cd->c_data, 1);
+        else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) {
+            switch (cd->c_type->ct_size) {
+            case 2:
+                return _my_PyUnicode_FromChar16((cffi_char16_t *)cd->c_data, 1);
+            case 4:
+                return _my_PyUnicode_FromChar32((cffi_char32_t *)cd->c_data, 1);
+            }
+        }
+    }
+    PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument",
+                 cd->c_type->ct_name);
+    return NULL;
+}
+
+static PyObject *b_unpack(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    CDataObject *cd;
+    CTypeDescrObject *ctitem;
+    Py_ssize_t i, length, itemsize;
+    PyObject *result;
+    char *src;
+    int casenum;
+    static char *keywords[] = {"cdata", "length", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!n:unpack", keywords,
+                                     &CData_Type, &cd, &length))
+        return NULL;
+
+    if (!(cd->c_type->ct_flags & (CT_ARRAY|CT_POINTER))) {
+        PyErr_Format(PyExc_TypeError,
+                     "expected a pointer or array, got '%s'",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+    if (length < 0) {
+        PyErr_SetString(PyExc_ValueError, "'length' cannot be negative");
+        return NULL;
+    }
+    if (cd->c_data == NULL) {
+        PyObject *s = cdata_repr(cd);
+        if (s != NULL) {
+            PyErr_Format(PyExc_RuntimeError,
+                         "cannot use unpack() on %s",
+                         PyText_AS_UTF8(s));
+            Py_DECREF(s);
+        }
+        return NULL;
+    }
+
+    /* byte- and unicode strings */
+    ctitem = cd->c_type->ct_itemdescr;
+    if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) {
+        switch (ctitem->ct_size) {
+        case sizeof(char):
+            return PyBytes_FromStringAndSize(cd->c_data, length);
+        case 2:
+            return _my_PyUnicode_FromChar16((cffi_char16_t *)cd->c_data,length);
+        case 4:
+            return _my_PyUnicode_FromChar32((cffi_char32_t *)cd->c_data,length);
+        }
+    }
+
+    /* else, the result is a list.  This implementation should be
+       equivalent to but much faster than '[p[i] for i in range(length)]'.
+       (Note that on PyPy, 'list(p[0:length])' should be equally fast,
+       but arguably, finding out that there *is* such an unexpected way
+       to write things down is the real problem.)
+    */
+    result = PyList_New(length);
+    if (result == NULL)
+        return NULL;
+
+    src = cd->c_data;
+    itemsize = ctitem->ct_size;
+    if (itemsize < 0) {
+        Py_DECREF(result);
+        PyErr_Format(PyExc_ValueError, "'%s' points to items of unknown size",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+
+    /* Determine some common fast-paths for the loop below.  The case -1
+       is the fall-back, which always gives the right answer. */
+
+#define ALIGNMENT_CHECK(align)                          \
+        (((align) & ((align) - 1)) == 0 &&              \
+         (((uintptr_t)src) & ((align) - 1)) == 0)
+
+    casenum = -1;
+
+    if ((ctitem->ct_flags & CT_PRIMITIVE_ANY) &&
+            ALIGNMENT_CHECK(ctitem->ct_length)) {
+        /* Source data is fully aligned; we can directly read without
+           memcpy().  The unaligned case is expected to be rare; in
+           this situation it is ok to fall back to the general
+           convert_to_object() in the loop.  For now we also use this
+           fall-back for types that are too large.
+        */
+        if (ctitem->ct_flags & CT_PRIMITIVE_SIGNED) {
+            if (itemsize == sizeof(long))             casenum = 3;
+            else if (itemsize == sizeof(int))         casenum = 2;
+            else if (itemsize == sizeof(short))       casenum = 1;
+            else if (itemsize == sizeof(signed char)) casenum = 0;
+        }
+        else if (ctitem->ct_flags & CT_PRIMITIVE_UNSIGNED) {
+            /* Note: we never pick case 6 if sizeof(int) == sizeof(long),
+               so that case 6 below can assume that the 'unsigned int' result
+               would always fit in a 'signed long'. */
+            if (ctitem->ct_flags & CT_IS_BOOL)           casenum = 11;
+            else if (itemsize == sizeof(unsigned long))  casenum = 7;
+            else if (itemsize == sizeof(unsigned int))   casenum = 6;
+            else if (itemsize == sizeof(unsigned short)) casenum = 5;
+            else if (itemsize == sizeof(unsigned char))  casenum = 4;
+        }
+        else if (ctitem->ct_flags & CT_PRIMITIVE_FLOAT) {
+            if      (itemsize == sizeof(double)) casenum = 9;
+            else if (itemsize == sizeof(float))  casenum = 8;
+        }
+    }
+    else if (ctitem->ct_flags & (CT_POINTER | CT_FUNCTIONPTR)) {
+        casenum = 10;    /* any pointer */
+    }
+#undef ALIGNMENT_CHECK
+
+    for (i = 0; i < length; i++) {
+        PyObject *x;
+        switch (casenum) {
+            /* general case */
+        default: x = convert_to_object(src, ctitem); break;
+
+            /* special cases for performance only */
+        case 0: x = PyInt_FromLong(*(signed char *)src); break;
+        case 1: x = PyInt_FromLong(*(short *)src); break;
+        case 2: x = PyInt_FromLong(*(int *)src); break;
+        case 3: x = PyInt_FromLong(*(long *)src); break;
+        case 4: x = PyInt_FromLong(*(unsigned char *)src); break;
+        case 5: x = PyInt_FromLong(*(unsigned short *)src); break;
+        case 6: x = PyInt_FromLong((long)*(unsigned int *)src); break;
+        case 7: x = PyLong_FromUnsignedLong(*(unsigned long *)src); break;
+        case 8: x = PyFloat_FromDouble(*(float *)src); break;
+        case 9: x = PyFloat_FromDouble(*(double *)src); break;
+        case 10: x = new_simple_cdata(*(char **)src, ctitem); break;
+        case 11:
+            switch (*(unsigned char *)src) {
+            case 0: x = Py_False; Py_INCREF(x); break;
+            case 1: x = Py_True;  Py_INCREF(x); break;
+            default: x = convert_to_object(src, ctitem); /* error */
+            }
+            break;
+        }
+        if (x == NULL) {
+            Py_DECREF(result);
+            return NULL;
+        }
+        PyList_SET_ITEM(result, i, x);
+        src += itemsize;
+    }
+    return result;
+}
+
+static PyObject *
+b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    /* this is the constructor of the type implemented in minibuffer.h */
+    CDataObject *cd;
+    Py_ssize_t size = -1;
+    static char *keywords[] = {"cdata", "size", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords,
+                                     &CData_Type, &cd, &size))
+        return NULL;
+
+    if (size < 0)
+        size = _cdata_var_byte_size(cd);
+
+    if (cd->c_type->ct_flags & CT_POINTER) {
+        if (size < 0)
+            size = cd->c_type->ct_itemdescr->ct_size;
+    }
+    else if (cd->c_type->ct_flags & CT_ARRAY) {
+        if (size < 0)
+            size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
+    }
+    else {
+        PyErr_Format(PyExc_TypeError,
+                     "expected a pointer or array cdata, got '%s'",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+    if (size < 0) {
+        PyErr_Format(PyExc_TypeError,
+                     "don't know the size pointed to by '%s'",
+                     cd->c_type->ct_name);
+        return NULL;
+    }
+    /*WRITE(cd->c_data, size)*/
+    return minibuffer_new(cd->c_data, size, (PyObject *)cd);
+}
+
+static PyObject *b_get_errno(PyObject *self, PyObject *noarg)
+{
+    int err;
+    restore_errno_only();
+    err = errno;
+    errno = 0;
+    return PyInt_FromLong(err);
+}
+
+static PyObject *b_set_errno(PyObject *self, PyObject *arg)
+{
+    long ival = PyInt_AsLong(arg);
+    if (ival == -1 && PyErr_Occurred())
+        return NULL;
+    else if (ival < INT_MIN || ival > INT_MAX) {
+        PyErr_SetString(PyExc_OverflowError, "errno value too large");
+        return NULL;
+    }
+    errno = (int)ival;
+    save_errno_only();
+    errno = 0;
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *newp_handle(CTypeDescrObject *ct_voidp, PyObject *x)
+{
+    CDataObject_own_structptr *cd;
+    cd = (CDataObject_own_structptr *)PyObject_GC_New(CDataObject_own_structptr,
+                                                      &CDataOwningGC_Type);
+    if (cd == NULL)
+        return NULL;
+    Py_INCREF(ct_voidp);
+    cd->head.c_type = ct_voidp;
+    cd->head.c_data = (char *)cd;
+    cd->head.c_weakreflist = NULL;
+    Py_INCREF(x);
+    cd->structobj = x;
+    PyObject_GC_Track(cd);
+    return (PyObject *)cd;
+}
+
+static PyObject *b_newp_handle(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    PyObject *x;
+    if (!PyArg_ParseTuple(args, "O!O", &CTypeDescr_Type, &ct, &x))
+        return NULL;
+
+    if (!(ct->ct_flags & CT_IS_VOID_PTR)) {
+        PyErr_Format(PyExc_TypeError, "needs 'void *', got '%s'", ct->ct_name);
+        return NULL;
+    }
+
+    return newp_handle(ct, x);
+}
+
+static PyObject *b_from_handle(PyObject *self, PyObject *arg)
+{
+    CTypeDescrObject *ct;
+    CDataObject_own_structptr *orgcd;
+    PyObject *x;
+    if (!CData_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object");
+        return NULL;
+    }
+    ct = ((CDataObject *)arg)->c_type;
+    if (!(ct->ct_flags & CT_IS_VOIDCHAR_PTR)) {
+        PyErr_Format(PyExc_TypeError,
+                     "expected a 'cdata' object with a 'void *' out of "
+                     "new_handle(), got '%s'", ct->ct_name);
+        return NULL;
+    }
+    orgcd = (CDataObject_own_structptr *)((CDataObject *)arg)->c_data;
+    if (!orgcd) {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "cannot use from_handle() on NULL pointer");
+        return NULL;
+    }
+    if (Py_REFCNT(orgcd) <= 0 || Py_TYPE(orgcd) != &CDataOwningGC_Type) {
+        Py_FatalError("ffi.from_handle() detected that the address passed "
+                      "points to garbage. If it is really the result of "
+                      "ffi.new_handle(), then the Python object has already "
+                      "been garbage collected");
+    }
+    x = orgcd->structobj;
+    Py_INCREF(x);
+    return x;
+}
+
+static int _my_PyObject_GetContiguousBuffer(PyObject *x, Py_buffer *view,
+                                            int writable_only)
+{
+#if PY_MAJOR_VERSION < 3
+    /* Some objects only support the buffer interface and CPython doesn't
+       translate it into the memoryview interface, mess.  Hack a very
+       minimal content for 'view'.  Don't care if the other fields are
+       uninitialized: we only call PyBuffer_Release(), which only reads
+       'view->obj'. */
+    PyBufferProcs *pb = x->ob_type->tp_as_buffer;
+    if (pb && !pb->bf_releasebuffer) {
+        /* we used to try all three in some vaguely sensible order,
+           i.e. first the write.  But trying to call the write on a
+           read-only buffer fails with TypeError.  So we use a less-
+           sensible order now.  See test_from_buffer_more_cases.
+
+           If 'writable_only', we only try bf_getwritebuffer.
+        */
+        readbufferproc proc = NULL;
+        if (!writable_only) {
+            proc = (readbufferproc)pb->bf_getreadbuffer;
+            if (!proc)
+                proc = (readbufferproc)pb->bf_getcharbuffer;
+        }
+        if (!proc)
+            proc = (readbufferproc)pb->bf_getwritebuffer;
+
+        if (proc && pb->bf_getsegcount) {
+            if ((*pb->bf_getsegcount)(x, NULL) != 1) {
+                PyErr_SetString(PyExc_TypeError,
+                                "expected a single-segment buffer object");
+                return -1;
+            }
+            view->len = (*proc)(x, 0, &view->buf);
+            if (view->len < 0)
+                return -1;
+            view->obj = x;
+            Py_INCREF(x);
+            return 0;
+        }
+    }
+#endif
+
+    if (PyObject_GetBuffer(x, view, writable_only ? PyBUF_WRITABLE
+                                                  : PyBUF_SIMPLE) < 0)
+        return -1;
+
+    if (!PyBuffer_IsContiguous(view, 'A')) {
+        PyBuffer_Release(view);
+        PyErr_SetString(PyExc_TypeError, "contiguous buffer expected");
+        return -1;
+    }
+    return 0;
+}
+
+static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x,
+                                    int require_writable)
+{
+    CDataObject *cd;
+    Py_buffer *view;
+    Py_ssize_t arraylength;
+
+    if (!(ct->ct_flags & CT_ARRAY)) {
+        PyErr_Format(PyExc_TypeError, "expected an array ctype, got '%s'",
+                     ct->ct_name);
+        return NULL;
+    }
+
+    /* PyPy 5.7 can obtain buffers for string (python 2)
+       or bytes (python 3). from_buffer(u"foo") is disallowed.
+     */
+    if (PyUnicode_Check(x)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "from_buffer() cannot return the address "
+                        "of a unicode object");
+        return NULL;
+    }
+
+    view = PyObject_Malloc(sizeof(Py_buffer));
+    if (view == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    if (_my_PyObject_GetContiguousBuffer(x, view, require_writable) < 0)
+        goto error1;
+
+    if (ct->ct_length >= 0) {
+        /* it's an array with a fixed length; make sure that the
+           buffer contains enough bytes. */
+        if (view->len < ct->ct_size) {
+            PyErr_Format(PyExc_ValueError,
+                "buffer is too small (%zd bytes) for '%s' (%zd bytes)",
+                view->len, ct->ct_name, ct->ct_size);
+            goto error2;
+        }
+        arraylength = ct->ct_length;
+    }
+    else {
+        /* it's an open 'array[]' */
+        if (ct->ct_itemdescr->ct_size == 1) {
+            /* fast path, performance only */
+            arraylength = view->len;
+        }
+        else if (ct->ct_itemdescr->ct_size > 0) {
+            /* give it as many items as fit the buffer.  Ignore a
+               partial last element. */
+            arraylength = view->len / ct->ct_itemdescr->ct_size;
+        }
+        else {
+            /* it's an array 'empty[]'.  Unsupported obscure case:
+               the problem is that setting the length of the result
+               to anything large (like SSIZE_T_MAX) is dangerous,
+               because if someone tries to loop over it, it will
+               turn effectively into an infinite loop. */
+            PyErr_Format(PyExc_ZeroDivisionError,
+                "from_buffer('%s', ..): the actual length of the array "
+                "cannot be computed", ct->ct_name);
+            goto error2;
+        }
+    }
+
+    cd = (CDataObject *)PyObject_GC_New(CDataObject_owngc_frombuf,
+                                        &CDataOwningGC_Type);
+    if (cd == NULL)
+        goto error2;
+
+    Py_INCREF(ct);
+    cd->c_type = ct;
+    cd->c_data = view->buf;
+    cd->c_weakreflist = NULL;
+    ((CDataObject_owngc_frombuf *)cd)->length = arraylength;
+    ((CDataObject_owngc_frombuf *)cd)->bufferview = view;
+    PyObject_GC_Track(cd);
+    return (PyObject *)cd;
+
+ error2:
+    PyBuffer_Release(view);
+ error1:
+    PyObject_Free(view);
+    return NULL;
+}
+
+static PyObject *b_from_buffer(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    PyObject *x;
+    int require_writable = 0;
+
+    if (!PyArg_ParseTuple(args, "O!O|i", &CTypeDescr_Type, &ct, &x,
+                          &require_writable))
+        return NULL;
+
+    return direct_from_buffer(ct, x, require_writable);
+}
+
+static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only)
+{
+    if (CData_Check(x)) {
+        CTypeDescrObject *ct = ((CDataObject *)x)->c_type;
+        if (!(ct->ct_flags & (CT_POINTER|CT_ARRAY))) {
+            PyErr_Format(PyExc_TypeError,
+                         "expected a pointer or array ctype, got '%s'",
+                         ct->ct_name);
+            return -1;
+        }
+        view->buf = ((CDataObject *)x)->c_data;
+        view->obj = NULL;
+        return 0;
+    }
+    else {
+        return _my_PyObject_GetContiguousBuffer(x, view, writable_only);
+    }
+}
+
+static PyObject *b_memmove(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *dest_obj, *src_obj;
+    Py_buffer dest_view, src_view;
+    Py_ssize_t n;
+    static char *keywords[] = {"dest", "src", "n", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOn", keywords,
+                                     &dest_obj, &src_obj, &n))
+        return NULL;
+    if (n < 0) {
+        PyErr_SetString(PyExc_ValueError, "negative size");
+        return NULL;
+    }
+
+    if (_fetch_as_buffer(src_obj, &src_view, 0) < 0) {
+        return NULL;
+    }
+    if (_fetch_as_buffer(dest_obj, &dest_view, 1) < 0) {
+        PyBuffer_Release(&src_view);
+        return NULL;
+    }
+
+    memmove(dest_view.buf, src_view.buf, n);
+
+    PyBuffer_Release(&dest_view);
+    PyBuffer_Release(&src_view);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *b__get_types(PyObject *self, PyObject *noarg)
+{
+    return PyTuple_Pack(2, (PyObject *)&CData_Type,
+                           (PyObject *)&CTypeDescr_Type);
+}
+
+/* forward, in commontypes.c */
+static PyObject *b__get_common_types(PyObject *self, PyObject *arg);
+
+static PyObject *b_gcp(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    CDataObject *cd;
+    CDataObject *origobj;
+    PyObject *destructor;
+    Py_ssize_t ignored;   /* for pypy */
+    static char *keywords[] = {"cdata", "destructor", "size", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O|n:gc", keywords,
+                                     &CData_Type, &origobj, &destructor,
+                                     &ignored))
+        return NULL;
+
+    if (destructor == Py_None) {
+	if (!PyObject_TypeCheck(origobj, &CDataGCP_Type)) {
+	    PyErr_SetString(PyExc_TypeError,
+			    "Can remove destructor only on a object "
+			    "previously returned by ffi.gc()");
+	    return NULL;
+	}
+	Py_CLEAR(((CDataObject_gcp *)origobj)->destructor);
+	Py_RETURN_NONE;
+    }
+
+    cd = allocate_gcp_object(origobj, origobj->c_type, destructor);
+    return (PyObject *)cd;
+}
+
+static PyObject *b_release(PyObject *self, PyObject *arg)
+{
+    if (!CData_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object");
+        return NULL;
+    }
+    return cdata_exit(arg, NULL);
+}
+
+/************************************************************/
+
+static char _testfunc0(char a, char b)
+{
+    return a + b;
+}
+static long _testfunc1(int a, long b)
+{
+    return (long)a + b;
+}
+static PY_LONG_LONG _testfunc2(PY_LONG_LONG a, PY_LONG_LONG b)
+{
+    return a + b;
+}
+static double _testfunc3(float a, double b)
+{
+    return a + b;
+}
+static float _testfunc4(float a, double b)
+{
+    return (float)(a + b);
+}
+static void _testfunc5(void)
+{
+    errno = errno + 15;
+}
+static int *_testfunc6(int *x)
+{
+    static int y;
+    y = *x - 1000;
+    return &y;
+}
+struct _testfunc7_s { unsigned char a1; short a2; };
+static short _testfunc7(struct _testfunc7_s inlined)
+{
+    return inlined.a1 + inlined.a2;
+}
+static int _testfunc9(int num, ...)
+{
+    va_list vargs;
+    int i, total = 0;
+    va_start(vargs, num);
+    for (i=0; i<num; i++) {
+        int value = va_arg(vargs, int);
+        if (value == 0)
+            value = -66666666;
+        total += value;
+    }
+    va_end(vargs);
+    return total;
+}
+
+static struct _testfunc7_s _testfunc10(int n)
+{
+    struct _testfunc7_s result;
+    result.a1 = n;
+    result.a2 = n * n;
+    return result;
+}
+
+struct _testfunc11_s { int a1, a2; };
+static struct _testfunc11_s _testfunc11(int n)
+{
+    struct _testfunc11_s result;
+    result.a1 = n;
+    result.a2 = n * n;
+    return result;
+}
+
+struct _testfunc12_s { double a1; };
+static struct _testfunc12_s _testfunc12(int n)
+{
+    struct _testfunc12_s result;
+    result.a1 = n;
+    return result;
+}
+
+struct _testfunc13_s { int a1, a2, a3; };
+static struct _testfunc13_s _testfunc13(int n)
+{
+    struct _testfunc13_s result;
+    result.a1 = n;
+    result.a2 = n * n;
+    result.a3 = n * n * n;
+    return result;
+}
+
+struct _testfunc14_s { float a1; };
+static struct _testfunc14_s _testfunc14(int n)
+{
+    struct _testfunc14_s result;
+    result.a1 = (float)n;
+    return result;
+}
+
+struct _testfunc15_s { float a1; int a2; };
+static struct _testfunc15_s _testfunc15(int n)
+{
+    struct _testfunc15_s result;
+    result.a1 = (float)n;
+    result.a2 = n * n;
+    return result;
+}
+
+struct _testfunc16_s { float a1, a2; };
+static struct _testfunc16_s _testfunc16(int n)
+{
+    struct _testfunc16_s result;
+    result.a1 = (float)n;
+    result.a2 = -(float)n;
+    return result;
+}
+
+struct _testfunc17_s { int a1; float a2; };
+static struct _testfunc17_s _testfunc17(int n)
+{
+    struct _testfunc17_s result;
+    result.a1 = n;
+    result.a2 = (float)n * (float)n;
+    return result;
+}
+
+static int _testfunc18(struct _testfunc17_s *ptr)
+{
+    return ptr->a1 + (int)ptr->a2;
+}
+
+static long double _testfunc19(long double x, int count)
+{
+    int i;
+    for (i=0; i<count; i++) {
+        x = 4*x - x*x;
+    }
+    return x;
+}
+
+static short _testfunc20(struct _testfunc7_s *ptr)
+{
+    return ptr->a1 + ptr->a2;
+}
+
+struct _testfunc21_s { int a, b, c, d, e, f, g, h, i, j; };
+static int _testfunc21(struct _testfunc21_s inlined)
+{
+    return ((inlined.a << 0) +
+            (inlined.b << 1) +
+            (inlined.c << 2) +
+            (inlined.d << 3) +
+            (inlined.e << 4) +
+            (inlined.f << 5) +
+            (inlined.g << 6) +
+            (inlined.h << 7) +
+            (inlined.i << 8) +
+            (inlined.j << 9));
+}
+
+struct _testfunc22_s { int a[10]; };
+static struct _testfunc22_s _testfunc22(struct _testfunc22_s s1,
+                                        struct _testfunc22_s s2)
+{
+    struct _testfunc22_s result;
+    int i;
+    for (i=0; i<10; i++)
+        result.a[i] = s1.a[i] - s2.a[i];
+    return result;
+}
+
+static int _testfunc23(char *p)
+{
+    if (p)
+        return 1000 * p[0];
+    return -42;
+}
+
+#if 0   /* libffi doesn't properly support complexes currently */
+        /* also, MSVC might not support _Complex... */
+        /* if this is enabled one day, remember to also add _Complex
+         * arguments in addition to return values. */
+static float _Complex _testfunc24(float a, float b)
+{
+    return a + I*2.0*b;
+}
+static double _Complex _testfunc25(double a, double b)
+{
+    return a + I*2.0*b;
+}
+#endif
+
+static PyObject *b__testfunc(PyObject *self, PyObject *args)
+{
+    /* for testing only */
+    int i;
+    void *f;
+    if (!PyArg_ParseTuple(args, "i:_testfunc", &i))
+        return NULL;
+    switch (i) {
+    case 0: f = &_testfunc0; break;
+    case 1: f = &_testfunc1; break;
+    case 2: f = &_testfunc2; break;
+    case 3: f = &_testfunc3; break;
+    case 4: f = &_testfunc4; break;
+    case 5: f = &_testfunc5; break;
+    case 6: f = &_testfunc6; break;
+    case 7: f = &_testfunc7; break;
+    case 8: f = stderr; break;
+    case 9: f = &_testfunc9; break;
+    case 10: f = &_testfunc10; break;
+    case 11: f = &_testfunc11; break;
+    case 12: f = &_testfunc12; break;
+    case 13: f = &_testfunc13; break;
+    case 14: f = &_testfunc14; break;
+    case 15: f = &_testfunc15; break;
+    case 16: f = &_testfunc16; break;
+    case 17: f = &_testfunc17; break;
+    case 18: f = &_testfunc18; break;
+    case 19: f = &_testfunc19; break;
+    case 20: f = &_testfunc20; break;
+    case 21: f = &_testfunc21; break;
+    case 22: f = &_testfunc22; break;
+    case 23: f = &_testfunc23; break;
+#if 0
+    case 24: f = &_testfunc24; break;
+    case 25: f = &_testfunc25; break;
+#endif
+    default:
+        PyErr_SetNone(PyExc_ValueError);
+        return NULL;
+    }
+    return PyLong_FromVoidPtr(f);
+}
+
+#if PY_MAJOR_VERSION < 3
+static Py_ssize_t _test_segcountproc(PyObject *o, Py_ssize_t *ignored)
+{
+    return 1;
+}
+static Py_ssize_t _test_getreadbuf(PyObject *o, Py_ssize_t i, void **r)
+{
+    static char buf[] = "RDB";
+    *r = buf;
+    return 3;
+}
+static Py_ssize_t _test_getwritebuf(PyObject *o, Py_ssize_t i, void **r)
+{
+    static char buf[] = "WRB";
+    *r = buf;
+    return 3;
+}
+static Py_ssize_t _test_getcharbuf(PyObject *o, Py_ssize_t i, char **r)
+{
+    static char buf[] = "CHB";
+    *r = buf;
+    return 3;
+}
+#endif
+static int _test_getbuf(PyObject *self, Py_buffer *view, int flags)
+{
+    static char buf[] = "GTB";
+    return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/0, flags);
+}
+static int _test_getbuf_ro(PyObject *self, Py_buffer *view, int flags)
+{
+    static char buf[] = "ROB";
+    return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/1, flags);
+}
+
+
+static PyObject *b__testbuff(PyObject *self, PyObject *args)
+{
+    /* for testing only */
+    int methods;
+    PyTypeObject *obj;
+    if (!PyArg_ParseTuple(args, "O!i|_testbuff", &PyType_Type, &obj, &methods))
+        return NULL;
+
+    assert(obj->tp_as_buffer != NULL);
+
+#if PY_MAJOR_VERSION < 3
+    obj->tp_as_buffer->bf_getsegcount = &_test_segcountproc;
+    obj->tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER;
+    obj->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
+    if (methods & 1)  obj->tp_as_buffer->bf_getreadbuffer  = &_test_getreadbuf;
+    if (methods & 2)  obj->tp_as_buffer->bf_getwritebuffer = &_test_getwritebuf;
+    if (methods & 4)  obj->tp_as_buffer->bf_getcharbuffer  = &_test_getcharbuf;
+#endif
+    if (methods & 8)  obj->tp_as_buffer->bf_getbuffer      = &_test_getbuf;
+    if (methods & 16) obj->tp_as_buffer->bf_getbuffer      = &_test_getbuf_ro;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *b_init_cffi_1_0_external_module(PyObject *, PyObject *);
+/* forward, see cffi1_module.c */
+
+
+static PyMethodDef FFIBackendMethods[] = {
+    {"load_library", b_load_library, METH_VARARGS},
+    {"new_primitive_type", b_new_primitive_type, METH_VARARGS},
+    {"new_pointer_type", b_new_pointer_type, METH_VARARGS},
+    {"new_array_type", b_new_array_type, METH_VARARGS},
+    {"new_void_type", b_new_void_type, METH_NOARGS},
+    {"new_struct_type", b_new_struct_type, METH_VARARGS},
+    {"new_union_type", b_new_union_type, METH_VARARGS},
+    {"complete_struct_or_union", b_complete_struct_or_union, METH_VARARGS},
+    {"new_function_type", b_new_function_type, METH_VARARGS},
+    {"new_enum_type", b_new_enum_type, METH_VARARGS},
+    {"newp", b_newp, METH_VARARGS},
+    {"cast", b_cast, METH_VARARGS},
+    {"callback", b_callback, METH_VARARGS},
+    {"alignof", b_alignof, METH_O},
+    {"sizeof", b_sizeof, METH_O},
+    {"typeof", b_typeof, METH_O},
+    {"typeoffsetof", b_typeoffsetof, METH_VARARGS},
+    {"rawaddressof", b_rawaddressof, METH_VARARGS},
+    {"getcname", b_getcname, METH_VARARGS},
+    {"string", (PyCFunction)b_string, METH_VARARGS | METH_KEYWORDS},
+    {"unpack", (PyCFunction)b_unpack, METH_VARARGS | METH_KEYWORDS},
+    {"get_errno", b_get_errno, METH_NOARGS},
+    {"set_errno", b_set_errno, METH_O},
+    {"newp_handle", b_newp_handle, METH_VARARGS},
+    {"from_handle", b_from_handle, METH_O},
+    {"from_buffer", b_from_buffer, METH_VARARGS},
+    {"memmove", (PyCFunction)b_memmove, METH_VARARGS | METH_KEYWORDS},
+    {"gcp", (PyCFunction)b_gcp, METH_VARARGS | METH_KEYWORDS},
+    {"release", b_release, METH_O},
+#ifdef MS_WIN32
+    {"getwinerror", (PyCFunction)b_getwinerror, METH_VARARGS | METH_KEYWORDS},
+#endif
+    {"_get_types", b__get_types, METH_NOARGS},
+    {"_get_common_types", b__get_common_types, METH_O},
+    {"_testfunc", b__testfunc, METH_VARARGS},
+    {"_testbuff", b__testbuff, METH_VARARGS},
+    {"_init_cffi_1_0_external_module", b_init_cffi_1_0_external_module, METH_O},
+    {NULL,     NULL}    /* Sentinel */
+};
+
+/************************************************************/
+/* Functions used by '_cffi_N.so', the generated modules    */
+
+#define _cffi_to_c_SIGNED_FN(RETURNTYPE, SIZE)                          \
+static RETURNTYPE _cffi_to_c_i##SIZE(PyObject *obj) {                   \
+    PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj);                      \
+    if ((tmp > (PY_LONG_LONG)((1ULL<<(SIZE-1)) - 1)) ||                 \
+        (tmp < (PY_LONG_LONG)(0ULL-(1ULL<<(SIZE-1)))))                  \
+        if (!PyErr_Occurred())                                          \
+            return (RETURNTYPE)_convert_overflow(obj, #SIZE "-bit int"); \
+    return (RETURNTYPE)tmp;                                             \
+}
+
+#define _cffi_to_c_UNSIGNED_FN(RETURNTYPE, SIZE)                        \
+static RETURNTYPE _cffi_to_c_u##SIZE(PyObject *obj) {                   \
+    unsigned PY_LONG_LONG tmp = _my_PyLong_AsUnsignedLongLong(obj, 1);  \
+    if (tmp > ~(((unsigned PY_LONG_LONG)-2) << (SIZE-1)))               \
+        if (!PyErr_Occurred())                                          \
+            return (RETURNTYPE)_convert_overflow(obj,                   \
+                                   #SIZE "-bit unsigned int");          \
+    return (RETURNTYPE)tmp;                                             \
+}
+
+_cffi_to_c_SIGNED_FN(int, 8)
+_cffi_to_c_SIGNED_FN(int, 16)
+_cffi_to_c_SIGNED_FN(int, 32)
+_cffi_to_c_SIGNED_FN(PY_LONG_LONG, 64)
+_cffi_to_c_UNSIGNED_FN(int, 8)
+_cffi_to_c_UNSIGNED_FN(int, 16)
+_cffi_to_c_UNSIGNED_FN(unsigned int, 32)
+_cffi_to_c_UNSIGNED_FN(unsigned PY_LONG_LONG, 64)
+
+static PyObject *_cffi_from_c_pointer(char *ptr, CTypeDescrObject *ct)
+{
+    return convert_to_object((char *)&ptr, ct);
+}
+
+static char *_cffi_to_c_pointer(PyObject *obj, CTypeDescrObject *ct)
+{
+    char *result;
+    if (convert_from_object((char *)&result, ct, obj) < 0) {
+        if ((ct->ct_flags & CT_POINTER) &&
+                (ct->ct_itemdescr->ct_flags & CT_IS_FILE) &&
+                PyFile_Check(obj)) {
+            PyErr_Clear();
+            return (char *)PyFile_AsFile(obj);
+        }
+        return NULL;
+    }
+    return result;
+}
+
+static long double _cffi_to_c_long_double(PyObject *obj)
+{
+    if (CData_Check(obj) &&
+            (((CDataObject *)obj)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+        char *data = ((CDataObject *)obj)->c_data;
+        /*READ(data, sizeof(long double))*/
+        return read_raw_longdouble_data(data);
+    }
+    else
+        return PyFloat_AsDouble(obj);
+}
+
+static _Bool _cffi_to_c__Bool(PyObject *obj)
+{
+    PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj);
+    if (tmp == 0)
+        return 0;
+    else if (tmp == 1)
+        return 1;
+    else if (PyErr_Occurred())
+        return (_Bool)-1;
+    else
+        return (_Bool)_convert_overflow(obj, "_Bool");
+}
+
+static PyObject *_cffi_get_struct_layout(Py_ssize_t nums[])
+{
+    PyObject *result;
+    int count = 0;
+    while (nums[count] >= 0)
+        count++;
+
+    result = PyList_New(count);
+    if (result == NULL)
+        return NULL;
+
+    while (--count >= 0) {
+        PyObject *o = PyInt_FromSsize_t(nums[count]);
+        if (o == NULL) {
+            Py_DECREF(result);
+            return NULL;
+        }
+        PyList_SET_ITEM(result, count, o);
+    }
+    return result;
+}
+
+static PyObject *_cffi_from_c_char(char x) {
+    return PyBytes_FromStringAndSize(&x, 1);
+}
+
+/* backward-compatibility hack: instead of _cffi_to_c_char16_t() and
+ * _cffi_to_c_char32_t(), we have _cffi_to_c_wchar_t() handling whatever
+ * size is wchar_t, and _cffi_to_c_wchar3216_t() handling the opposite.
+ */
+#ifdef HAVE_WCHAR_H
+typedef wchar_t cffi_wchar_t;
+#else
+typedef uint16_t cffi_wchar_t;   /* random pick... */
+#endif
+
+static cffi_wchar_t _cffi_to_c_wchar_t(PyObject *init)
+{
+    if (sizeof(cffi_wchar_t) == 2)
+        return (cffi_wchar_t)_convert_to_char16_t(init);
+    else
+        return (cffi_wchar_t)_convert_to_char32_t(init);
+}
+static PyObject *_cffi_from_c_wchar_t(cffi_wchar_t x) {
+    if (sizeof(cffi_wchar_t) == 2) {
+        cffi_char16_t input = x;
+        return _my_PyUnicode_FromChar16(&input, 1);
+    }
+    else {
+        cffi_char32_t input = x;
+        return _my_PyUnicode_FromChar32(&input, 1);
+    }
+}
+static int _cffi_to_c_wchar3216_t(PyObject *init)
+{
+    if (sizeof(cffi_wchar_t) == 4)
+        return (int)_convert_to_char16_t(init);
+    else
+        return (int)_convert_to_char32_t(init);
+}
+static PyObject *_cffi_from_c_wchar3216_t(int x) {
+    if (sizeof(cffi_wchar_t) == 4) {
+        cffi_char16_t input = x;
+        return _my_PyUnicode_FromChar16(&input, 1);
+    }
+    else {
+        cffi_char32_t input = x;
+        return _my_PyUnicode_FromChar32(&input, 1);
+    }
+}
+
+struct _cffi_externpy_s;      /* forward declaration */
+static void cffi_call_python(struct _cffi_externpy_s *, char *args);
+
+static void *cffi_exports[] = {
+    NULL,
+    _cffi_to_c_i8,
+    _cffi_to_c_u8,
+    _cffi_to_c_i16,
+    _cffi_to_c_u16,
+    _cffi_to_c_i32,
+    _cffi_to_c_u32,
+    _cffi_to_c_i64,
+    _cffi_to_c_u64,
+    _convert_to_char,
+    _cffi_from_c_pointer,
+    _cffi_to_c_pointer,
+    _cffi_get_struct_layout,
+    restore_errno,
+    save_errno,
+    _cffi_from_c_char,
+    convert_to_object,
+    convert_from_object,
+    convert_struct_to_owning_object,
+    _cffi_to_c_wchar_t,
+    _cffi_from_c_wchar_t,
+    _cffi_to_c_long_double,
+    _cffi_to_c__Bool,
+    _prepare_pointer_call_argument,
+    convert_array_from_object,
+    cffi_call_python,
+    _cffi_to_c_wchar3216_t,
+    _cffi_from_c_wchar3216_t,
+};
+
+static struct { const char *name; int value; } all_dlopen_flags[] = {
+    { "RTLD_LAZY",     RTLD_LAZY     },
+    { "RTLD_NOW",      RTLD_NOW      },
+    { "RTLD_GLOBAL",   RTLD_GLOBAL   },
+#ifdef RTLD_LOCAL
+    { "RTLD_LOCAL",    RTLD_LOCAL    },
+#else
+    { "RTLD_LOCAL",    0             },
+#endif
+#ifdef RTLD_NODELETE
+    { "RTLD_NODELETE", RTLD_NODELETE },
+#endif
+#ifdef RTLD_NOLOAD
+    { "RTLD_NOLOAD",   RTLD_NOLOAD   },
+#endif
+#ifdef RTLD_DEEPBIND
+    { "RTLD_DEEPBIND", RTLD_DEEPBIND },
+#endif
+    { NULL, 0 }
+};
+
+
+/************************************************************/
+
+#include "cffi1_module.c"
+
+/************************************************************/
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef FFIBackendModuleDef = {
+  PyModuleDef_HEAD_INIT,
+  "_cffi_backend",
+  NULL,
+  -1,
+  FFIBackendMethods,
+  NULL, NULL, NULL, NULL
+};
+#define INITERROR return NULL
+
+PyMODINIT_FUNC
+PyInit__cffi_backend(void)
+#else
+#define INITERROR return
+
+PyMODINIT_FUNC
+init_cffi_backend(void)
+#endif
+{
+    PyObject *m, *v;
+    int i;
+    static char init_done = 0;
+
+    v = PySys_GetObject("version");
+    if (v == NULL || !PyText_Check(v) ||
+            strncmp(PyText_AS_UTF8(v), PY_VERSION, 3) != 0) {
+        PyErr_Format(PyExc_ImportError,
+                     "this module was compiled for Python %c%c%c",
+                     PY_VERSION[0], PY_VERSION[1], PY_VERSION[2]);
+        INITERROR;
+    }
+
+#if PY_MAJOR_VERSION >= 3
+    m = PyModule_Create(&FFIBackendModuleDef);
+#else
+    m = Py_InitModule("_cffi_backend", FFIBackendMethods);
+#endif
+
+    if (m == NULL)
+        INITERROR;
+
+    if (unique_cache == NULL) {
+        unique_cache = PyDict_New();
+        if (unique_cache == NULL)
+            INITERROR;
+    }
+
+    if (PyType_Ready(&dl_type) < 0)
+        INITERROR;
+    if (PyType_Ready(&CTypeDescr_Type) < 0)
+        INITERROR;
+    if (PyType_Ready(&CField_Type) < 0)
+        INITERROR;
+    if (PyType_Ready(&CData_Type) < 0)
+        INITERROR;
+    if (PyType_Ready(&CDataOwning_Type) < 0)
+        INITERROR;
+    if (PyType_Ready(&CDataOwningGC_Type) < 0)
+        INITERROR;
+    if (PyType_Ready(&CDataGCP_Type) < 0)
+        INITERROR;
+    if (PyType_Ready(&CDataIter_Type) < 0)
+        INITERROR;
+    if (PyType_Ready(&MiniBuffer_Type) < 0)
+        INITERROR;
+
+    if (!init_done) {
+        v = PyText_FromString("_cffi_backend");
+        if (v == NULL || PyDict_SetItemString(CData_Type.tp_dict,
+                                              "__module__", v) < 0)
+            INITERROR;
+        v = PyText_FromString("<cdata>");
+        if (v == NULL || PyDict_SetItemString(CData_Type.tp_dict,
+                                              "__name__", v) < 0)
+            INITERROR;
+        init_done = 1;
+    }
+
+    /* this is for backward compatibility only */
+    v = PyCapsule_New((void *)cffi_exports, "cffi", NULL);
+    if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
+        INITERROR;
+
+    v = PyText_FromString(CFFI_VERSION);
+    if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0)
+        INITERROR;
+
+    if (PyModule_AddIntConstant(m, "FFI_DEFAULT_ABI", FFI_DEFAULT_ABI) < 0 ||
+#if defined(MS_WIN32) && !defined(_WIN64)
+        PyModule_AddIntConstant(m, "FFI_STDCALL", FFI_STDCALL) < 0 ||
+#endif
+        PyModule_AddIntConstant(m, "FFI_CDECL", FFI_DEFAULT_ABI) < 0 ||
+
+#ifdef MS_WIN32
+#  ifdef _WIN64
+        PyModule_AddIntConstant(m, "_WIN", 64) < 0 ||   /* win64 */
+#  else
+        PyModule_AddIntConstant(m, "_WIN", 32) < 0 ||   /* win32 */
+#  endif
+#endif
+        0)
+      INITERROR;
+
+    for (i = 0; all_dlopen_flags[i].name != NULL; i++) {
+        if (PyModule_AddIntConstant(m,
+                                    all_dlopen_flags[i].name,
+                                    all_dlopen_flags[i].value) < 0)
+            INITERROR;
+    }
+
+    Py_INCREF(&MiniBuffer_Type);
+    if (PyModule_AddObject(m, "buffer", (PyObject *)&MiniBuffer_Type) < 0)
+        INITERROR;
+
+    init_cffi_tls();
+    if (PyErr_Occurred())
+        INITERROR;
+    init_cffi_tls_zombie();
+    if (PyErr_Occurred())
+        INITERROR;
+
+    if (init_ffi_lib(m) < 0)
+        INITERROR;
+
+#if PY_MAJOR_VERSION >= 3
+    if (init_file_emulator() < 0)
+        INITERROR;
+    return m;
+#endif
+}
diff --git a/c/_cffi_backend.so b/c/_cffi_backend.so
new file mode 100644
index 0000000..7055ae6
--- /dev/null
+++ b/c/_cffi_backend.so
Binary files differ
diff --git a/c/_dummy_file_cffi_backend.py b/c/_dummy_file_cffi_backend.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/c/_dummy_file_cffi_backend.py
diff --git a/c/_dummy_file_libffi.py b/c/_dummy_file_libffi.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/c/_dummy_file_libffi.py
diff --git a/c/call_python.c b/c/call_python.c
new file mode 100644
index 0000000..8fdcb90
--- /dev/null
+++ b/c/call_python.c
@@ -0,0 +1,278 @@
+#if PY_VERSION_HEX >= 0x03080000
+# define Py_BUILD_CORE
+/* for access to the fields of PyInterpreterState */
+#  include "internal/pycore_pystate.h"
+# undef Py_BUILD_CORE
+#endif
+
+static PyObject *_get_interpstate_dict(void)
+{
+    /* Hack around to return a dict that is subinterpreter-local.
+       Does not return a new reference.  Returns NULL in case of
+       error, but without setting any exception.  (If called late
+       during shutdown, we *can't* set an exception!)
+    */
+    static PyObject *attr_name = NULL;
+    PyThreadState *tstate;
+    PyObject *d, *builtins;
+    int err;
+
+    tstate = PyThreadState_GET();
+    if (tstate == NULL) {
+        /* no thread state! */
+        return NULL;
+    }
+
+    builtins = tstate->interp->builtins;
+    if (builtins == NULL) {
+        /* subinterpreter was cleared already, or is being cleared right now,
+           to a point that is too much for us to continue */
+        return NULL;
+    }
+
+    /* from there on, we know the (sub-)interpreter is still valid */
+
+    if (attr_name == NULL) {
+        attr_name = PyText_InternFromString("__cffi_backend_extern_py");
+        if (attr_name == NULL)
+            goto error;
+    }
+
+    d = PyDict_GetItem(builtins, attr_name);
+    if (d == NULL) {
+        d = PyDict_New();
+        if (d == NULL)
+            goto error;
+        err = PyDict_SetItem(builtins, attr_name, d);
+        Py_DECREF(d);    /* if successful, there is one ref left in builtins */
+        if (err < 0)
+            goto error;
+    }
+    return d;
+
+ error:
+    PyErr_Clear();    /* typically a MemoryError */
+    return NULL;
+}
+
+static PyObject *_ffi_def_extern_decorator(PyObject *outer_args, PyObject *fn)
+{
+    const char *s;
+    PyObject *error, *onerror, *infotuple, *old1;
+    int index, err;
+    const struct _cffi_global_s *g;
+    struct _cffi_externpy_s *externpy;
+    CTypeDescrObject *ct;
+    FFIObject *ffi;
+    builder_c_t *types_builder;
+    PyObject *name = NULL;
+    PyObject *interpstate_dict;
+    PyObject *interpstate_key;
+
+    if (!PyArg_ParseTuple(outer_args, "OzOO", &ffi, &s, &error, &onerror))
+        return NULL;
+
+    if (s == NULL) {
+        name = PyObject_GetAttrString(fn, "__name__");
+        if (name == NULL)
+            return NULL;
+        s = PyText_AsUTF8(name);
+        if (s == NULL) {
+            Py_DECREF(name);
+            return NULL;
+        }
+    }
+
+    types_builder = &ffi->types_builder;
+    index = search_in_globals(&types_builder->ctx, s, strlen(s));
+    if (index < 0)
+        goto not_found;
+    g = &types_builder->ctx.globals[index];
+    if (_CFFI_GETOP(g->type_op) != _CFFI_OP_EXTERN_PYTHON)
+        goto not_found;
+    Py_XDECREF(name);
+
+    ct = realize_c_type(types_builder, types_builder->ctx.types,
+                        _CFFI_GETARG(g->type_op));
+    if (ct == NULL)
+        return NULL;
+
+    infotuple = prepare_callback_info_tuple(ct, fn, error, onerror, 0);
+    Py_DECREF(ct);
+    if (infotuple == NULL)
+        return NULL;
+
+    /* don't directly attach infotuple to externpy: in the presence of
+       subinterpreters, each time we switch to a different
+       subinterpreter and call the C function, it will notice the
+       change and look up infotuple from the interpstate_dict.
+    */
+    interpstate_dict = _get_interpstate_dict();
+    if (interpstate_dict == NULL) {
+        Py_DECREF(infotuple);
+        return PyErr_NoMemory();
+    }
+
+    externpy = (struct _cffi_externpy_s *)g->address;
+    interpstate_key = PyLong_FromVoidPtr((void *)externpy);
+    if (interpstate_key == NULL) {
+        Py_DECREF(infotuple);
+        return NULL;
+    }
+
+    err = PyDict_SetItem(interpstate_dict, interpstate_key, infotuple);
+    Py_DECREF(interpstate_key);
+    Py_DECREF(infotuple);    /* interpstate_dict owns the last ref */
+    if (err < 0)
+        return NULL;
+
+    /* force _update_cache_to_call_python() to be called the next time
+       the C function invokes cffi_call_python, to update the cache */
+    old1 = externpy->reserved1;
+    externpy->reserved1 = Py_None;   /* a non-NULL value */
+    Py_INCREF(Py_None);
+    Py_XDECREF(old1);
+
+    /* return the function object unmodified */
+    Py_INCREF(fn);
+    return fn;
+
+ not_found:
+    PyErr_Format(FFIError, "ffi.def_extern('%s'): no 'extern \"Python\"' "
+                 "function with this name", s);
+    Py_XDECREF(name);
+    return NULL;
+}
+
+
+static int _update_cache_to_call_python(struct _cffi_externpy_s *externpy)
+{
+    PyObject *interpstate_dict, *interpstate_key, *infotuple, *old1, *new1;
+    PyObject *old2;
+
+    interpstate_dict = _get_interpstate_dict();
+    if (interpstate_dict == NULL)
+        return 4;    /* oops, shutdown issue? */
+
+    interpstate_key = PyLong_FromVoidPtr((void *)externpy);
+    if (interpstate_key == NULL)
+        goto error;
+
+    infotuple = PyDict_GetItem(interpstate_dict, interpstate_key);
+    Py_DECREF(interpstate_key);
+    if (infotuple == NULL)
+        return 3;    /* no ffi.def_extern() from this subinterpreter */
+
+    new1 = PyThreadState_GET()->interp->modules;
+    Py_INCREF(new1);
+    Py_INCREF(infotuple);
+    old1 = (PyObject *)externpy->reserved1;
+    old2 = (PyObject *)externpy->reserved2;
+    externpy->reserved1 = new1;         /* holds a reference        */
+    externpy->reserved2 = infotuple;    /* holds a reference (issue #246) */
+    Py_XDECREF(old1);
+    Py_XDECREF(old2);
+
+    return 0;   /* no error */
+
+ error:
+    PyErr_Clear();
+    return 2;   /* out of memory? */
+}
+
+#if (defined(WITH_THREAD) && !defined(_MSC_VER) &&   \
+     !defined(__amd64__) && !defined(__x86_64__) &&   \
+     !defined(__i386__) && !defined(__i386))
+# if defined(HAVE_SYNC_SYNCHRONIZE)
+#   define read_barrier()  __sync_synchronize()
+# elif defined(_AIX)
+#   define read_barrier()  __lwsync()
+# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+#   include <mbarrier.h>
+#   define read_barrier()  __compiler_barrier()
+# elif defined(__hpux)
+#   define read_barrier()  _Asm_mf()
+# else
+#   define read_barrier()  /* missing */
+#   warning "no definition for read_barrier(), missing synchronization for\
+ multi-thread initialization in embedded mode"
+# endif
+#else
+# define read_barrier()  (void)0
+#endif
+
+static void cffi_call_python(struct _cffi_externpy_s *externpy, char *args)
+{
+    /* Invoked by the helpers generated from extern "Python" in the cdef.
+
+       'externpy' is a static structure that describes which of the
+       extern "Python" functions is called.  It has got fields 'name' and
+       'type_index' describing the function, and more reserved fields
+       that are initially zero.  These reserved fields are set up by
+       ffi.def_extern(), which invokes _ffi_def_extern_decorator() above.
+
+       'args' is a pointer to an array of 8-byte entries.  Each entry
+       contains an argument.  If an argument is less than 8 bytes, only
+       the part at the beginning of the entry is initialized.  If an
+       argument is 'long double' or a struct/union, then it is passed
+       by reference.
+
+       'args' is also used as the place to write the result to
+       (directly, even if more than 8 bytes).  In all cases, 'args' is
+       at least 8 bytes in size.
+    */
+    int err = 0;
+
+    /* This read barrier is needed for _embedding.h.  It is paired
+       with the write_barrier() there.  Without this barrier, we can
+       in theory see the following situation: the Python
+       initialization code already ran (in another thread), and the
+       '_cffi_call_python' function pointer directed execution here;
+       but any number of other data could still be seen as
+       uninitialized below.  For example, 'externpy' would still
+       contain NULLs even though it was correctly set up, or
+       'interpreter_lock' (the GIL inside CPython) would still be seen
+       as NULL, or 'autoInterpreterState' (used by
+       PyGILState_Ensure()) would be NULL or contain bogus fields.
+    */
+    read_barrier();
+
+    save_errno();
+
+    /* We need the infotuple here.  We could always go through
+       _update_cache_to_call_python(), but to avoid the extra dict
+       lookups, we cache in (reserved1, reserved2) the last seen pair
+       (interp->modules, infotuple).  The first item in this tuple is
+       a random PyObject that identifies the subinterpreter.
+    */
+    if (externpy->reserved1 == NULL) {
+        /* Not initialized!  We didn't call @ffi.def_extern() on this
+           externpy object from any subinterpreter at all. */
+        err = 1;
+    }
+    else {
+        PyGILState_STATE state = gil_ensure();
+        if (externpy->reserved1 != PyThreadState_GET()->interp->modules) {
+            /* Update the (reserved1, reserved2) cache.  This will fail
+               if we didn't call @ffi.def_extern() in this particular
+               subinterpreter. */
+            err = _update_cache_to_call_python(externpy);
+        }
+        if (!err) {
+            general_invoke_callback(0, args, args, externpy->reserved2);
+        }
+        gil_release(state);
+    }
+    if (err) {
+        static const char *msg[] = {
+            "no code was attached to it yet with @ffi.def_extern()",
+            "got internal exception (out of memory?)",
+            "@ffi.def_extern() was not called in the current subinterpreter",
+            "got internal exception (shutdown issue?)",
+        };
+        fprintf(stderr, "extern \"Python\": function %s() called, "
+                        "but %s.  Returning 0.\n", externpy->name, msg[err-1]);
+        memset(args, 0, externpy->size_of_result);
+    }
+    restore_errno();
+}
diff --git a/c/cdlopen.c b/c/cdlopen.c
new file mode 100644
index 0000000..ad33bbd
--- /dev/null
+++ b/c/cdlopen.c
@@ -0,0 +1,361 @@
+/* ffi.dlopen() interface with dlopen()/dlsym()/dlclose() */
+
+static void *cdlopen_fetch(PyObject *libname, void *libhandle,
+                           const char *symbol)
+{
+    void *address;
+
+    if (libhandle == NULL) {
+        PyErr_Format(FFIError, "library '%s' has been closed",
+                     PyText_AS_UTF8(libname));
+        return NULL;
+    }
+
+    dlerror();   /* clear error condition */
+    address = dlsym(libhandle, symbol);
+    if (address == NULL) {
+        const char *error = dlerror();
+        PyErr_Format(FFIError, "symbol '%s' not found in library '%s': %s",
+                     symbol, PyText_AS_UTF8(libname), error);
+    }
+    return address;
+}
+
+static void cdlopen_close_ignore_errors(void *libhandle)
+{
+    if (libhandle != NULL)
+        dlclose(libhandle);
+}
+
+static int cdlopen_close(PyObject *libname, void *libhandle)
+{
+    if (libhandle != NULL && dlclose(libhandle) != 0) {
+        const char *error = dlerror();
+        PyErr_Format(FFIError, "closing library '%s': %s",
+                     PyText_AS_UTF8(libname), error);
+        return -1;
+    }
+    return 0;
+}
+
+static PyObject *ffi_dlopen(PyObject *self, PyObject *args)
+{
+    const char *modname;
+    PyObject *temp, *result = NULL;
+    void *handle;
+
+    handle = b_do_dlopen(args, &modname, &temp);
+    if (handle != NULL)
+    {
+        result = (PyObject *)lib_internal_new((FFIObject *)self,
+                                              modname, handle);
+    }
+    Py_XDECREF(temp);
+    return result;
+}
+
+static PyObject *ffi_dlclose(PyObject *self, PyObject *args)
+{
+    LibObject *lib;
+    void *libhandle;
+    if (!PyArg_ParseTuple(args, "O!", &Lib_Type, &lib))
+        return NULL;
+
+    libhandle = lib->l_libhandle;
+    if (libhandle != NULL)
+    {
+        lib->l_libhandle = NULL;
+
+        /* Clear the dict to force further accesses to do cdlopen_fetch()
+           again, and fail because the library was closed. */
+        PyDict_Clear(lib->l_dict);
+
+        if (cdlopen_close(lib->l_libname, libhandle) < 0)
+            return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+
+static Py_ssize_t cdl_4bytes(char *src)
+{
+    /* read 4 bytes in little-endian order; return it as a signed integer */
+    signed char *ssrc = (signed char *)src;
+    unsigned char *usrc = (unsigned char *)src;
+    return (ssrc[0] << 24) | (usrc[1] << 16) | (usrc[2] << 8) | usrc[3];
+}
+
+static _cffi_opcode_t cdl_opcode(char *src)
+{
+    return (_cffi_opcode_t)cdl_4bytes(src);
+}
+
+typedef struct {
+    unsigned long long value;
+    int neg;
+} cdl_intconst_t;
+
+static int _cdl_realize_global_int(struct _cffi_getconst_s *gc)
+{
+    /* The 'address' field of 'struct _cffi_global_s' is set to point
+       to this function in case ffiobj_init() sees constant integers.
+       This fishes around after the 'ctx->globals' array, which is
+       initialized to contain another array, this time of
+       'cdl_intconst_t' structures.  We get the nth one and it tells
+       us what to return.
+    */
+    cdl_intconst_t *ic;
+    ic = (cdl_intconst_t *)(gc->ctx->globals + gc->ctx->num_globals);
+    ic += gc->gindex;
+    gc->value = ic->value;
+    return ic->neg;
+}
+
+static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    FFIObject *ffi;
+    static char *keywords[] = {"module_name", "_version", "_types",
+                               "_globals", "_struct_unions", "_enums",
+                               "_typenames", "_includes", NULL};
+    char *ffiname = "?", *types = NULL, *building = NULL;
+    Py_ssize_t version = -1;
+    Py_ssize_t types_len = 0;
+    PyObject *globals = NULL, *struct_unions = NULL, *enums = NULL;
+    PyObject *typenames = NULL, *includes = NULL;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds,
+                                     "|sns#O!O!O!O!O!:FFI", keywords,
+                                     &ffiname, &version, &types, &types_len,
+                                     &PyTuple_Type, &globals,
+                                     &PyTuple_Type, &struct_unions,
+                                     &PyTuple_Type, &enums,
+                                     &PyTuple_Type, &typenames,
+                                     &PyTuple_Type, &includes))
+        return -1;
+
+    ffi = (FFIObject *)self;
+    if (ffi->ctx_is_nonempty) {
+        PyErr_SetString(PyExc_ValueError,
+                        "cannot call FFI.__init__() more than once");
+        return -1;
+    }
+    ffi->ctx_is_nonempty = 1;
+
+    if (version == -1 && types_len == 0)
+        return 0;
+    if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) {
+        PyErr_Format(PyExc_ImportError,
+                     "cffi out-of-line Python module '%s' has unknown "
+                     "version %p", ffiname, (void *)version);
+        return -1;
+    }
+
+    if (types_len > 0) {
+        /* unpack a string of 4-byte entries into an array of _cffi_opcode_t */
+        _cffi_opcode_t *ntypes;
+        Py_ssize_t i, n = types_len / 4;
+
+        building = PyMem_Malloc(n * sizeof(_cffi_opcode_t));
+        if (building == NULL)
+            goto error;
+        ntypes = (_cffi_opcode_t *)building;
+
+        for (i = 0; i < n; i++) {
+            ntypes[i] = cdl_opcode(types);
+            types += 4;
+        }
+        ffi->types_builder.ctx.types = ntypes;
+        ffi->types_builder.ctx.num_types = n;
+        building = NULL;
+    }
+
+    if (globals != NULL) {
+        /* unpack a tuple alternating strings and ints, each two together
+           describing one global_s entry with no specified address or size.
+           The int is only used with integer constants. */
+        struct _cffi_global_s *nglobs;
+        cdl_intconst_t *nintconsts;
+        Py_ssize_t i, n = PyTuple_GET_SIZE(globals) / 2;
+
+        i = n * (sizeof(struct _cffi_global_s) + sizeof(cdl_intconst_t));
+        building = PyMem_Malloc(i);
+        if (building == NULL)
+            goto error;
+        memset(building, 0, i);
+        nglobs = (struct _cffi_global_s *)building;
+        nintconsts = (cdl_intconst_t *)(nglobs + n);
+
+        for (i = 0; i < n; i++) {
+            char *g = PyBytes_AS_STRING(PyTuple_GET_ITEM(globals, i * 2));
+            nglobs[i].type_op = cdl_opcode(g); g += 4;
+            nglobs[i].name = g;
+            if (_CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_CONSTANT_INT ||
+                _CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_ENUM) {
+                PyObject *o = PyTuple_GET_ITEM(globals, i * 2 + 1);
+                nglobs[i].address = &_cdl_realize_global_int;
+#if PY_MAJOR_VERSION < 3
+                if (PyInt_Check(o)) {
+                    nintconsts[i].neg = PyInt_AS_LONG(o) <= 0;
+                    nintconsts[i].value = (long long)PyInt_AS_LONG(o);
+                }
+                else
+#endif
+                {
+                    nintconsts[i].neg = PyObject_RichCompareBool(o, Py_False,
+                                                                 Py_LE);
+                    nintconsts[i].value = PyLong_AsUnsignedLongLongMask(o);
+                    if (PyErr_Occurred())
+                        goto error;
+                }
+            }
+        }
+        ffi->types_builder.ctx.globals = nglobs;
+        ffi->types_builder.ctx.num_globals = n;
+        building = NULL;
+    }
+
+    if (struct_unions != NULL) {
+        /* unpack a tuple of struct/unions, each described as a sub-tuple;
+           the item 0 of each sub-tuple describes the struct/union, and
+           the items 1..N-1 describe the fields, if any */
+        struct _cffi_struct_union_s *nstructs;
+        struct _cffi_field_s *nfields;
+        Py_ssize_t i, n = PyTuple_GET_SIZE(struct_unions);
+        Py_ssize_t nf = 0;   /* total number of fields */
+
+        for (i = 0; i < n; i++) {
+            nf += PyTuple_GET_SIZE(PyTuple_GET_ITEM(struct_unions, i)) - 1;
+        }
+        i = (n * sizeof(struct _cffi_struct_union_s) +
+             nf * sizeof(struct _cffi_field_s));
+        building = PyMem_Malloc(i);
+        if (building == NULL)
+            goto error;
+        memset(building, 0, i);
+        nstructs = (struct _cffi_struct_union_s *)building;
+        nfields = (struct _cffi_field_s *)(nstructs + n);
+        nf = 0;
+
+        for (i = 0; i < n; i++) {
+            /* 'desc' is the tuple of strings (desc_struct, desc_field_1, ..) */
+            PyObject *desc = PyTuple_GET_ITEM(struct_unions, i);
+            Py_ssize_t j, nf1 = PyTuple_GET_SIZE(desc) - 1;
+            char *s = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, 0));
+            /* 's' is the first string, describing the struct/union */
+            nstructs[i].type_index = cdl_4bytes(s); s += 4;
+            nstructs[i].flags = cdl_4bytes(s); s += 4;
+            nstructs[i].name = s;
+            if (nstructs[i].flags & (_CFFI_F_OPAQUE | _CFFI_F_EXTERNAL)) {
+                nstructs[i].size = (size_t)-1;
+                nstructs[i].alignment = -1;
+                nstructs[i].first_field_index = -1;
+                nstructs[i].num_fields = 0;
+                assert(nf1 == 0);
+            }
+            else {
+                nstructs[i].size = (size_t)-2;
+                nstructs[i].alignment = -2;
+                nstructs[i].first_field_index = nf;
+                nstructs[i].num_fields = nf1;
+            }
+            for (j = 0; j < nf1; j++) {
+                char *f = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, j + 1));
+                /* 'f' is one of the other strings beyond the first one,
+                   describing one field each */
+                nfields[nf].field_type_op = cdl_opcode(f); f += 4;
+                nfields[nf].field_offset = (size_t)-1;
+                if (_CFFI_GETOP(nfields[nf].field_type_op) != _CFFI_OP_NOOP) {
+                    nfields[nf].field_size = cdl_4bytes(f); f += 4;
+                }
+                else {
+                    nfields[nf].field_size = (size_t)-1;
+                }
+                nfields[nf].name = f;
+                nf++;
+            }
+        }
+        ffi->types_builder.ctx.struct_unions = nstructs;
+        ffi->types_builder.ctx.fields = nfields;
+        ffi->types_builder.ctx.num_struct_unions = n;
+        building = NULL;
+    }
+
+    if (enums != NULL) {
+        /* unpack a tuple of strings, each of which describes one enum_s
+           entry */
+        struct _cffi_enum_s *nenums;
+        Py_ssize_t i, n = PyTuple_GET_SIZE(enums);
+
+        i = n * sizeof(struct _cffi_enum_s);
+        building = PyMem_Malloc(i);
+        if (building == NULL)
+            goto error;
+        memset(building, 0, i);
+        nenums = (struct _cffi_enum_s *)building;
+
+        for (i = 0; i < n; i++) {
+            char *e = PyBytes_AS_STRING(PyTuple_GET_ITEM(enums, i));
+            /* 'e' is a string describing the enum */
+            nenums[i].type_index = cdl_4bytes(e); e += 4;
+            nenums[i].type_prim = cdl_4bytes(e); e += 4;
+            nenums[i].name = e; e += strlen(e) + 1;
+            nenums[i].enumerators = e;
+        }
+        ffi->types_builder.ctx.enums = nenums;
+        ffi->types_builder.ctx.num_enums = n;
+        building = NULL;
+    }
+
+    if (typenames != NULL) {
+        /* unpack a tuple of strings, each of which describes one typename_s
+           entry */
+        struct _cffi_typename_s *ntypenames;
+        Py_ssize_t i, n = PyTuple_GET_SIZE(typenames);
+
+        i = n * sizeof(struct _cffi_typename_s);
+        building = PyMem_Malloc(i);
+        if (building == NULL)
+            goto error;
+        memset(building, 0, i);
+        ntypenames = (struct _cffi_typename_s *)building;
+
+        for (i = 0; i < n; i++) {
+            char *t = PyBytes_AS_STRING(PyTuple_GET_ITEM(typenames, i));
+            /* 't' is a string describing the typename */
+            ntypenames[i].type_index = cdl_4bytes(t); t += 4;
+            ntypenames[i].name = t;
+        }
+        ffi->types_builder.ctx.typenames = ntypenames;
+        ffi->types_builder.ctx.num_typenames = n;
+        building = NULL;
+    }
+
+    if (includes != NULL) {
+        PyObject *included_libs;
+
+        included_libs = PyTuple_New(PyTuple_GET_SIZE(includes));
+        if (included_libs == NULL)
+            return -1;
+
+        Py_INCREF(includes);
+        ffi->types_builder.included_ffis = includes;
+        ffi->types_builder.included_libs = included_libs;
+    }
+
+    /* Above, we took directly some "char *" strings out of the strings,
+       typically from somewhere inside tuples.  Keep them alive by
+       incref'ing the whole input arguments. */
+    Py_INCREF(args);
+    Py_XINCREF(kwds);
+    ffi->types_builder._keepalive1 = args;
+    ffi->types_builder._keepalive2 = kwds;
+    return 0;
+
+ error:
+    if (building != NULL)
+        PyMem_Free(building);
+    if (!PyErr_Occurred())
+        PyErr_NoMemory();
+    return -1;
+}
diff --git a/c/cffi1_module.c b/c/cffi1_module.c
new file mode 100644
index 0000000..2b98e8e
--- /dev/null
+++ b/c/cffi1_module.c
@@ -0,0 +1,231 @@
+
+#include "parse_c_type.c"
+#include "realize_c_type.c"
+
+#define CFFI_VERSION_MIN            0x2601
+#define CFFI_VERSION_CHAR16CHAR32   0x2801
+#define CFFI_VERSION_MAX            0x28FF
+
+typedef struct FFIObject_s FFIObject;
+typedef struct LibObject_s LibObject;
+
+static PyTypeObject FFI_Type;   /* forward */
+static PyTypeObject Lib_Type;   /* forward */
+
+#include "ffi_obj.c"
+#include "cglob.c"
+#include "lib_obj.c"
+#include "cdlopen.c"
+#include "commontypes.c"
+#include "call_python.c"
+
+
+static int init_ffi_lib(PyObject *m)
+{
+    PyObject *x;
+    int i, res;
+    static char init_done = 0;
+
+    if (PyType_Ready(&FFI_Type) < 0)
+        return -1;
+    if (PyType_Ready(&Lib_Type) < 0)
+        return -1;
+
+    if (!init_done) {
+        if (init_global_types_dict(FFI_Type.tp_dict) < 0)
+            return -1;
+
+        FFIError = PyErr_NewException("ffi.error", NULL, NULL);
+        if (FFIError == NULL)
+            return -1;
+        if (PyDict_SetItemString(FFI_Type.tp_dict, "error", FFIError) < 0)
+            return -1;
+        if (PyDict_SetItemString(FFI_Type.tp_dict, "CType",
+                                 (PyObject *)&CTypeDescr_Type) < 0)
+            return -1;
+        if (PyDict_SetItemString(FFI_Type.tp_dict, "CData",
+                                 (PyObject *)&CData_Type) < 0)
+            return -1;
+        if (PyDict_SetItemString(FFI_Type.tp_dict, "buffer",
+                                 (PyObject *)&MiniBuffer_Type) < 0)
+            return -1;
+
+        for (i = 0; all_dlopen_flags[i].name != NULL; i++) {
+            x = PyInt_FromLong(all_dlopen_flags[i].value);
+            if (x == NULL)
+                return -1;
+            res = PyDict_SetItemString(FFI_Type.tp_dict,
+                                       all_dlopen_flags[i].name, x);
+            Py_DECREF(x);
+            if (res < 0)
+                return -1;
+        }
+        init_done = 1;
+    }
+
+    x = (PyObject *)&FFI_Type;
+    Py_INCREF(x);
+    if (PyModule_AddObject(m, "FFI", x) < 0)
+        return -1;
+    x = (PyObject *)&Lib_Type;
+    Py_INCREF(x);
+    if (PyModule_AddObject(m, "Lib", x) < 0)
+        return -1;
+
+    return 0;
+}
+
+static int make_included_tuples(char *module_name,
+                                const char *const *ctx_includes,
+                                PyObject **included_ffis,
+                                PyObject **included_libs)
+{
+    Py_ssize_t num = 0;
+    const char *const *p_include;
+
+    if (ctx_includes == NULL)
+        return 0;
+
+    for (p_include = ctx_includes; *p_include; p_include++) {
+        num++;
+    }
+    *included_ffis = PyTuple_New(num);
+    *included_libs = PyTuple_New(num);
+    if (*included_ffis == NULL || *included_libs == NULL)
+        goto error;
+
+    num = 0;
+    for (p_include = ctx_includes; *p_include; p_include++) {
+        PyObject *included_ffi, *included_lib;
+        PyObject *m = PyImport_ImportModule(*p_include);
+        if (m == NULL)
+            goto import_error;
+
+        included_ffi = PyObject_GetAttrString(m, "ffi");
+        PyTuple_SET_ITEM(*included_ffis, num, included_ffi);
+
+        included_lib = (included_ffi == NULL) ? NULL :
+                       PyObject_GetAttrString(m, "lib");
+        PyTuple_SET_ITEM(*included_libs, num, included_lib);
+
+        Py_DECREF(m);
+        if (included_lib == NULL)
+            goto import_error;
+
+        if (!FFIObject_Check(included_ffi) ||
+            !LibObject_Check(included_lib))
+            goto import_error;
+        num++;
+    }
+    return 0;
+
+ import_error:
+    PyErr_Format(PyExc_ImportError,
+                 "while loading %.200s: failed to import ffi, lib from %.200s",
+                 module_name, *p_include);
+ error:
+    Py_XDECREF(*included_ffis); *included_ffis = NULL;
+    Py_XDECREF(*included_libs); *included_libs = NULL;
+    return -1;
+}
+
+static PyObject *_my_Py_InitModule(char *module_name)
+{
+#if PY_MAJOR_VERSION >= 3
+    struct PyModuleDef *module_def, local_module_def = {
+        PyModuleDef_HEAD_INIT,
+        module_name,
+        NULL,
+        -1,
+        NULL, NULL, NULL, NULL, NULL
+    };
+    /* note: the 'module_def' is allocated dynamically and leaks,
+       but anyway the C extension module can never be unloaded */
+    module_def = PyMem_Malloc(sizeof(struct PyModuleDef));
+    if (module_def == NULL)
+        return PyErr_NoMemory();
+    *module_def = local_module_def;
+    return PyModule_Create(module_def);
+#else
+    return Py_InitModule(module_name, NULL);
+#endif
+}
+
+static PyObject *b_init_cffi_1_0_external_module(PyObject *self, PyObject *arg)
+{
+    PyObject *m, *modules_dict;
+    FFIObject *ffi;
+    LibObject *lib;
+    Py_ssize_t version, num_exports;
+    char *module_name, *exports, *module_name_with_lib;
+    void **raw;
+    const struct _cffi_type_context_s *ctx;
+
+    raw = (void **)PyLong_AsVoidPtr(arg);
+    if (raw == NULL)
+        return NULL;
+
+    module_name = (char *)raw[0];
+    version = (Py_ssize_t)raw[1];
+    exports = (char *)raw[2];
+    ctx = (const struct _cffi_type_context_s *)raw[3];
+
+    if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) {
+        if (!PyErr_Occurred())
+            PyErr_Format(PyExc_ImportError,
+                "cffi extension module '%s' uses an unknown version tag %p. "
+                "This module might need a more recent version of cffi "
+                "than the one currently installed, which is %s",
+                module_name, (void *)version, CFFI_VERSION);
+        return NULL;
+    }
+
+    /* initialize the exports array */
+    num_exports = 25;
+    if (ctx->flags & 1)    /* set to mean that 'extern "Python"' is used */
+        num_exports = 26;
+    if (version >= CFFI_VERSION_CHAR16CHAR32)
+        num_exports = 28;
+    memcpy(exports, (char *)cffi_exports, num_exports * sizeof(void *));
+
+    /* make the module object */
+    m = _my_Py_InitModule(module_name);
+    if (m == NULL)
+        return NULL;
+
+    /* build the FFI and Lib object inside this new module */
+    ffi = ffi_internal_new(&FFI_Type, ctx);
+    Py_XINCREF(ffi);    /* make the ffi object really immortal */
+    if (ffi == NULL || PyModule_AddObject(m, "ffi", (PyObject *)ffi) < 0)
+        return NULL;
+
+    lib = lib_internal_new(ffi, module_name, NULL);
+    if (lib == NULL || PyModule_AddObject(m, "lib", (PyObject *)lib) < 0)
+        return NULL;
+
+    if (make_included_tuples(module_name, ctx->includes,
+                             &ffi->types_builder.included_ffis,
+                             &lib->l_types_builder->included_libs) < 0)
+        return NULL;
+
+    /* add manually 'module_name.lib' in sys.modules:
+       see test_import_from_lib */
+    modules_dict = PySys_GetObject("modules");
+    if (!modules_dict)
+        return NULL;
+    module_name_with_lib = alloca(strlen(module_name) + 5);
+    strcpy(module_name_with_lib, module_name);
+    strcat(module_name_with_lib, ".lib");
+    if (PyDict_SetItemString(modules_dict, module_name_with_lib,
+                             (PyObject *)lib) < 0)
+        return NULL;
+
+#if PY_MAJOR_VERSION >= 3
+    /* add manually 'module_name' in sys.modules: it seems that 
+       Py_InitModule() is not enough to do that */
+    if (PyDict_SetItemString(modules_dict, module_name, m) < 0)
+        return NULL;
+#endif
+
+    return m;
+}
diff --git a/c/cglob.c b/c/cglob.c
new file mode 100644
index 0000000..9ee4025
--- /dev/null
+++ b/c/cglob.c
@@ -0,0 +1,113 @@
+
+typedef void *(*gs_fetch_addr_fn)(void);
+
+typedef struct {
+    PyObject_HEAD
+
+    PyObject         *gs_name;
+    CTypeDescrObject *gs_type;
+    char             *gs_data;
+    gs_fetch_addr_fn  gs_fetch_addr;
+
+} GlobSupportObject;
+
+static void glob_support_dealloc(GlobSupportObject *gs)
+{
+    Py_DECREF(gs->gs_name);
+    Py_DECREF(gs->gs_type);
+    PyObject_Del(gs);
+}
+
+static PyTypeObject GlobSupport_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "FFIGlobSupport",
+    sizeof(GlobSupportObject),
+    0,
+    (destructor)glob_support_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 */
+};
+
+#define GlobSupport_Check(ob)  (Py_TYPE(ob) == &GlobSupport_Type)
+
+static PyObject *make_global_var(PyObject *name, CTypeDescrObject *type,
+                                 char *addr, gs_fetch_addr_fn fetch_addr)
+{
+    GlobSupportObject *gs = PyObject_New(GlobSupportObject, &GlobSupport_Type);
+    if (gs == NULL)
+        return NULL;
+
+    Py_INCREF(name);
+    Py_INCREF(type);
+    gs->gs_name = name;
+    gs->gs_type = type;
+    gs->gs_data = addr;
+    gs->gs_fetch_addr = fetch_addr;
+    return (PyObject *)gs;
+}
+
+static void *fetch_global_var_addr(GlobSupportObject *gs)
+{
+    void *data;
+    if (gs->gs_data != NULL) {
+        data = gs->gs_data;
+    }
+    else {
+        Py_BEGIN_ALLOW_THREADS
+        restore_errno();
+        data = gs->gs_fetch_addr();
+        save_errno();
+        Py_END_ALLOW_THREADS
+    }
+    if (data == NULL) {
+        PyErr_Format(FFIError, "global variable '%s' is at address NULL",
+                     PyText_AS_UTF8(gs->gs_name));
+        return NULL;
+    }
+    return data;
+}
+
+static PyObject *read_global_var(GlobSupportObject *gs)
+{
+    void *data = fetch_global_var_addr(gs);
+    if (data == NULL)
+        return NULL;
+    return convert_to_object(data, gs->gs_type);
+}
+
+static int write_global_var(GlobSupportObject *gs, PyObject *obj)
+{
+    void *data = fetch_global_var_addr(gs);
+    if (data == NULL)
+        return -1;
+    return convert_from_object(data, gs->gs_type, obj);
+}
+
+static PyObject *cg_addressof_global_var(GlobSupportObject *gs)
+{
+    void *data;
+    PyObject *x, *ptrtype = new_pointer_type(gs->gs_type);
+    if (ptrtype == NULL)
+        return NULL;
+
+    data = fetch_global_var_addr(gs);
+    if (data != NULL)
+        x = new_simple_cdata(data, (CTypeDescrObject *)ptrtype);
+    else
+        x = NULL;
+    Py_DECREF(ptrtype);
+    return x;
+}
diff --git a/c/commontypes.c b/c/commontypes.c
new file mode 100644
index 0000000..a41c2fd
--- /dev/null
+++ b/c/commontypes.c
@@ -0,0 +1,216 @@
+/* This file must be kept in alphabetical order.  See test_commontypes.py */
+
+#define EQ(key, value)    key "\0" value   /* string concatenation */
+#ifdef _WIN64
+#  define W32_64(X,Y)  Y
+# else
+#  define W32_64(X,Y)  X
+# endif
+
+
+static const char *common_simple_types[] = {
+
+#ifdef MS_WIN32   /* Windows types */
+    EQ("ATOM", "WORD"),
+    EQ("BOOL", "int"),
+    EQ("BOOLEAN", "BYTE"),
+    EQ("BYTE", "unsigned char"),
+    EQ("CCHAR", "char"),
+    EQ("CHAR", "char"),
+    EQ("COLORREF", "DWORD"),
+    EQ("DWORD", "unsigned long"),
+    EQ("DWORD32", "unsigned int"),
+    EQ("DWORD64", "unsigned long long"),
+    EQ("DWORDLONG", "ULONGLONG"),
+    EQ("DWORD_PTR", "ULONG_PTR"),
+#endif
+
+    EQ("FILE", "struct _IO_FILE"),
+
+#ifdef MS_WIN32   /* more Windows types */
+    EQ("FLOAT", "float"),
+    EQ("HACCEL", "HANDLE"),
+    EQ("HALF_PTR", W32_64("short","int")),
+    EQ("HANDLE", "PVOID"),
+    EQ("HBITMAP", "HANDLE"),
+    EQ("HBRUSH", "HANDLE"),
+    EQ("HCOLORSPACE", "HANDLE"),
+    EQ("HCONV", "HANDLE"),
+    EQ("HCONVLIST", "HANDLE"),
+    EQ("HCURSOR", "HICON"),
+    EQ("HDC", "HANDLE"),
+    EQ("HDDEDATA", "HANDLE"),
+    EQ("HDESK", "HANDLE"),
+    EQ("HDROP", "HANDLE"),
+    EQ("HDWP", "HANDLE"),
+    EQ("HENHMETAFILE", "HANDLE"),
+    EQ("HFILE", "int"),
+    EQ("HFONT", "HANDLE"),
+    EQ("HGDIOBJ", "HANDLE"),
+    EQ("HGLOBAL", "HANDLE"),
+    EQ("HHOOK", "HANDLE"),
+    EQ("HICON", "HANDLE"),
+    EQ("HINSTANCE", "HANDLE"),
+    EQ("HKEY", "HANDLE"),
+    EQ("HKL", "HANDLE"),
+    EQ("HLOCAL", "HANDLE"),
+    EQ("HMENU", "HANDLE"),
+    EQ("HMETAFILE", "HANDLE"),
+    EQ("HMODULE", "HINSTANCE"),
+    EQ("HMONITOR", "HANDLE"),
+    EQ("HPALETTE", "HANDLE"),
+    EQ("HPEN", "HANDLE"),
+    EQ("HRESULT", "LONG"),
+    EQ("HRGN", "HANDLE"),
+    EQ("HRSRC", "HANDLE"),
+    EQ("HSZ", "HANDLE"),
+    EQ("HWND", "HANDLE"),
+    EQ("INT", "int"),
+    EQ("INT16", "short"),
+    EQ("INT32", "int"),
+    EQ("INT64", "long long"),
+    EQ("INT8", "signed char"),
+    EQ("INT_PTR", W32_64("int","long long")),
+    EQ("LANGID", "WORD"),
+    EQ("LCID", "DWORD"),
+    EQ("LCTYPE", "DWORD"),
+    EQ("LGRPID", "DWORD"),
+    EQ("LONG", "long"),
+    EQ("LONG32", "int"),
+    EQ("LONG64", "long long"),
+    EQ("LONGLONG", "long long"),
+    EQ("LONG_PTR", W32_64("long","long long")),
+    EQ("LPARAM", "LONG_PTR"),
+    EQ("LPBOOL", "BOOL *"),
+    EQ("LPBYTE", "BYTE *"),
+    EQ("LPCOLORREF", "DWORD *"),
+    EQ("LPCSTR", "const char *"),
+    EQ("LPCVOID", "const void *"),
+    EQ("LPCWSTR", "const WCHAR *"),
+    EQ("LPDWORD", "DWORD *"),
+    EQ("LPHANDLE", "HANDLE *"),
+    EQ("LPINT", "int *"),
+    EQ("LPLONG", "long *"),
+    EQ("LPSTR", "CHAR *"),
+    EQ("LPVOID", "void *"),
+    EQ("LPWORD", "WORD *"),
+    EQ("LPWSTR", "WCHAR *"),
+    EQ("LRESULT", "LONG_PTR"),
+    EQ("PBOOL", "BOOL *"),
+    EQ("PBOOLEAN", "BOOLEAN *"),
+    EQ("PBYTE", "BYTE *"),
+    EQ("PCHAR", "CHAR *"),
+    EQ("PCSTR", "const CHAR *"),
+    EQ("PCWSTR", "const WCHAR *"),
+    EQ("PDWORD", "DWORD *"),
+    EQ("PDWORD32", "DWORD32 *"),
+    EQ("PDWORD64", "DWORD64 *"),
+    EQ("PDWORDLONG", "DWORDLONG *"),
+    EQ("PDWORD_PTR", "DWORD_PTR *"),
+    EQ("PFLOAT", "FLOAT *"),
+    EQ("PHALF_PTR", "HALF_PTR *"),
+    EQ("PHANDLE", "HANDLE *"),
+    EQ("PHKEY", "HKEY *"),
+    EQ("PINT", "int *"),
+    EQ("PINT16", "INT16 *"),
+    EQ("PINT32", "INT32 *"),
+    EQ("PINT64", "INT64 *"),
+    EQ("PINT8", "INT8 *"),
+    EQ("PINT_PTR", "INT_PTR *"),
+    EQ("PLCID", "PDWORD"),
+    EQ("PLONG", "LONG *"),
+    EQ("PLONG32", "LONG32 *"),
+    EQ("PLONG64", "LONG64 *"),
+    EQ("PLONGLONG", "LONGLONG *"),
+    EQ("PLONG_PTR", "LONG_PTR *"),
+    EQ("PSHORT", "SHORT *"),
+    EQ("PSIZE_T", "SIZE_T *"),
+    EQ("PSSIZE_T", "SSIZE_T *"),
+    EQ("PSTR", "CHAR *"),
+    EQ("PUCHAR", "UCHAR *"),
+    EQ("PUHALF_PTR", "UHALF_PTR *"),
+    EQ("PUINT", "UINT *"),
+    EQ("PUINT16", "UINT16 *"),
+    EQ("PUINT32", "UINT32 *"),
+    EQ("PUINT64", "UINT64 *"),
+    EQ("PUINT8", "UINT8 *"),
+    EQ("PUINT_PTR", "UINT_PTR *"),
+    EQ("PULONG", "ULONG *"),
+    EQ("PULONG32", "ULONG32 *"),
+    EQ("PULONG64", "ULONG64 *"),
+    EQ("PULONGLONG", "ULONGLONG *"),
+    EQ("PULONG_PTR", "ULONG_PTR *"),
+    EQ("PUSHORT", "USHORT *"),
+    EQ("PVOID", "void *"),
+    EQ("PWCHAR", "WCHAR *"),
+    EQ("PWORD", "WORD *"),
+    EQ("PWSTR", "WCHAR *"),
+    EQ("QWORD", "unsigned long long"),
+    EQ("SC_HANDLE", "HANDLE"),
+    EQ("SC_LOCK", "LPVOID"),
+    EQ("SERVICE_STATUS_HANDLE", "HANDLE"),
+    EQ("SHORT", "short"),
+    EQ("SIZE_T", "ULONG_PTR"),
+    EQ("SSIZE_T", "LONG_PTR"),
+    EQ("UCHAR", "unsigned char"),
+    EQ("UHALF_PTR", W32_64("unsigned short","unsigned int")),
+    EQ("UINT", "unsigned int"),
+    EQ("UINT16", "unsigned short"),
+    EQ("UINT32", "unsigned int"),
+    EQ("UINT64", "unsigned long long"),
+    EQ("UINT8", "unsigned char"),
+    EQ("UINT_PTR", W32_64("unsigned int","unsigned long long")),
+    EQ("ULONG", "unsigned long"),
+    EQ("ULONG32", "unsigned int"),
+    EQ("ULONG64", "unsigned long long"),
+    EQ("ULONGLONG", "unsigned long long"),
+    EQ("ULONG_PTR", W32_64("unsigned long","unsigned long long")),
+    EQ("USHORT", "unsigned short"),
+    EQ("USN", "LONGLONG"),
+    EQ("VOID", "void"),
+    EQ("WCHAR", "wchar_t"),
+    EQ("WINSTA", "HANDLE"),
+    EQ("WORD", "unsigned short"),
+    EQ("WPARAM", "UINT_PTR"),
+#endif
+
+    EQ("bool", "_Bool"),
+};
+
+
+#undef EQ
+#undef W32_64
+
+#define num_common_simple_types    \
+    (sizeof(common_simple_types) / sizeof(common_simple_types[0]))
+
+
+static const char *get_common_type(const char *search, size_t search_len)
+{
+    const char *entry;
+    int index = search_sorted(common_simple_types, sizeof(const char *),
+                              num_common_simple_types, search, search_len);
+    if (index < 0)
+        return NULL;
+
+    entry = common_simple_types[index];
+    return entry + strlen(entry) + 1;
+}
+
+static PyObject *b__get_common_types(PyObject *self, PyObject *arg)
+{
+    int err;
+    size_t i;
+    for (i = 0; i < num_common_simple_types; i++) {
+        const char *s = common_simple_types[i];
+        PyObject *o = PyText_FromString(s + strlen(s) + 1);
+        if (o == NULL)
+            return NULL;
+        err = PyDict_SetItemString(arg, s, o);
+        Py_DECREF(o);
+        if (err < 0)
+            return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
diff --git a/c/ffi_obj.c b/c/ffi_obj.c
new file mode 100644
index 0000000..1e8cc6f
--- /dev/null
+++ b/c/ffi_obj.c
@@ -0,0 +1,1221 @@
+
+/* An FFI object has methods like ffi.new().  It is also a container
+   for the type declarations (typedefs and structs) that you can use,
+   say in ffi.new().
+
+   CTypeDescrObjects are internally stored in the dict 'types_dict'.
+   The types_dict is lazily filled with CTypeDescrObjects made from
+   reading a _cffi_type_context_s structure.
+
+   In "modern" mode, the FFI instance is made by the C extension
+   module originally created by recompile().  The _cffi_type_context_s
+   structure comes from global data in the C extension module.
+
+   In "compatibility" mode, an FFI instance is created explicitly by
+   the user, and its _cffi_type_context_s is initially empty.  You
+   need to call ffi.cdef() to add more information to it.
+*/
+
+#define FFI_COMPLEXITY_OUTPUT   1200     /* xxx should grow as needed */
+
+#define FFIObject_Check(op) PyObject_TypeCheck(op, &FFI_Type)
+#define LibObject_Check(ob)  ((Py_TYPE(ob) == &Lib_Type))
+
+struct FFIObject_s {
+    PyObject_HEAD
+    PyObject *gc_wrefs, *gc_wrefs_freelist;
+    PyObject *init_once_cache;
+    struct _cffi_parse_info_s info;
+    char ctx_is_static, ctx_is_nonempty;
+    builder_c_t types_builder;
+};
+
+static FFIObject *ffi_internal_new(PyTypeObject *ffitype,
+                                 const struct _cffi_type_context_s *static_ctx)
+{
+    static _cffi_opcode_t internal_output[FFI_COMPLEXITY_OUTPUT];
+
+    FFIObject *ffi;
+    if (static_ctx != NULL) {
+        ffi = (FFIObject *)PyObject_GC_New(FFIObject, ffitype);
+        /* we don't call PyObject_GC_Track() here: from _cffi_init_module()
+           it is not needed, because in this case the ffi object is immortal */
+    }
+    else {
+        ffi = (FFIObject *)ffitype->tp_alloc(ffitype, 0);
+    }
+    if (ffi == NULL)
+        return NULL;
+
+    if (init_builder_c(&ffi->types_builder, static_ctx) < 0) {
+        Py_DECREF(ffi);
+        return NULL;
+    }
+    ffi->gc_wrefs = NULL;
+    ffi->gc_wrefs_freelist = NULL;
+    ffi->init_once_cache = NULL;
+    ffi->info.ctx = &ffi->types_builder.ctx;
+    ffi->info.output = internal_output;
+    ffi->info.output_size = FFI_COMPLEXITY_OUTPUT;
+    ffi->ctx_is_static = (static_ctx != NULL);
+    ffi->ctx_is_nonempty = (static_ctx != NULL);
+    return ffi;
+}
+
+static void ffi_dealloc(FFIObject *ffi)
+{
+    PyObject_GC_UnTrack(ffi);
+    Py_XDECREF(ffi->gc_wrefs);
+    Py_XDECREF(ffi->gc_wrefs_freelist);
+    Py_XDECREF(ffi->init_once_cache);
+
+    free_builder_c(&ffi->types_builder, ffi->ctx_is_static);
+
+    Py_TYPE(ffi)->tp_free((PyObject *)ffi);
+}
+
+static int ffi_traverse(FFIObject *ffi, visitproc visit, void *arg)
+{
+    Py_VISIT(ffi->types_builder.types_dict);
+    Py_VISIT(ffi->types_builder.included_ffis);
+    Py_VISIT(ffi->types_builder.included_libs);
+    Py_VISIT(ffi->gc_wrefs);
+    return 0;
+}
+
+static PyObject *ffiobj_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    /* user-facing initialization code, for explicit FFI() calls */
+    return (PyObject *)ffi_internal_new(type, NULL);
+}
+
+/* forward, declared in cdlopen.c because it's mostly useful for this case */
+static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds);
+
+static PyObject *ffi_fetch_int_constant(FFIObject *ffi, const char *name,
+                                        int recursion)
+{
+    int index;
+
+    index = search_in_globals(&ffi->types_builder.ctx, name, strlen(name));
+    if (index >= 0) {
+        const struct _cffi_global_s *g;
+        g = &ffi->types_builder.ctx.globals[index];
+
+        switch (_CFFI_GETOP(g->type_op)) {
+        case _CFFI_OP_CONSTANT_INT:
+        case _CFFI_OP_ENUM:
+            return realize_global_int(&ffi->types_builder, index);
+
+        default:
+            PyErr_Format(FFIError,
+                         "function, global variable or non-integer constant "
+                         "'%.200s' must be fetched from its original 'lib' "
+                         "object", name);
+            return NULL;
+        }
+    }
+
+    if (ffi->types_builder.included_ffis != NULL) {
+        Py_ssize_t i;
+        PyObject *included_ffis = ffi->types_builder.included_ffis;
+
+        if (recursion > 100) {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "recursion overflow in ffi.include() delegations");
+            return NULL;
+        }
+
+        for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) {
+            FFIObject *ffi1;
+            PyObject *x;
+
+            ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i);
+            x = ffi_fetch_int_constant(ffi1, name, recursion + 1);
+            if (x != NULL || PyErr_Occurred())
+                return x;
+        }
+    }
+    return NULL;     /* no exception set, means "not found" */
+}
+
+#define ACCEPT_STRING   1
+#define ACCEPT_CTYPE    2
+#define ACCEPT_CDATA    4
+#define ACCEPT_ALL      (ACCEPT_STRING | ACCEPT_CTYPE | ACCEPT_CDATA)
+#define CONSIDER_FN_AS_FNPTR  8
+
+static CTypeDescrObject *_ffi_bad_type(FFIObject *ffi, const char *input_text)
+{
+    size_t length = strlen(input_text);
+    char *extra;
+
+    if (length > 500) {
+        extra = "";
+    }
+    else {
+        char *p;
+        size_t i, num_spaces = ffi->info.error_location;
+        extra = alloca(length + num_spaces + 4);
+        p = extra;
+        *p++ = '\n';
+        for (i = 0; i < length; i++) {
+            if (' ' <= input_text[i] && input_text[i] < 0x7f)
+                *p++ = input_text[i];
+            else if (input_text[i] == '\t' || input_text[i] == '\n')
+                *p++ = ' ';
+            else
+                *p++ = '?';
+        }
+        *p++ = '\n';
+        memset(p, ' ', num_spaces);
+        p += num_spaces;
+        *p++ = '^';
+        *p++ = 0;
+    }
+    PyErr_Format(FFIError, "%s%s", ffi->info.error_message, extra);
+    return NULL;
+}
+
+static CTypeDescrObject *_ffi_type(FFIObject *ffi, PyObject *arg,
+                                   int accept)
+{
+    /* Returns the CTypeDescrObject from the user-supplied 'arg'.
+       Does not return a new reference!
+    */
+    if ((accept & ACCEPT_STRING) && PyText_Check(arg)) {
+        PyObject *types_dict = ffi->types_builder.types_dict;
+        PyObject *x = PyDict_GetItem(types_dict, arg);
+
+        if (x == NULL) {
+            const char *input_text = PyText_AS_UTF8(arg);
+            int err, index = parse_c_type(&ffi->info, input_text);
+            if (index < 0)
+                return _ffi_bad_type(ffi, input_text);
+
+            x = realize_c_type_or_func(&ffi->types_builder,
+                                       ffi->info.output, index);
+            if (x == NULL)
+                return NULL;
+
+            /* Cache under the name given by 'arg', in addition to the
+               fact that the same ct is probably already cached under
+               its standardized name.  In a few cases, it is not, e.g.
+               if it is a primitive; for the purpose of this function,
+               the important point is the following line, which makes
+               sure that in any case the next _ffi_type() with the same
+               'arg' will succeed early, in PyDict_GetItem() above.
+            */
+            err = PyDict_SetItem(types_dict, arg, x);
+            Py_DECREF(x); /* we know it was written in types_dict (unless out
+                             of mem), so there is at least that ref left */
+            if (err < 0)
+                return NULL;
+        }
+
+        if (CTypeDescr_Check(x))
+            return (CTypeDescrObject *)x;
+        else if (accept & CONSIDER_FN_AS_FNPTR)
+            return unwrap_fn_as_fnptr(x);
+        else
+            return unexpected_fn_type(x);
+    }
+    else if ((accept & ACCEPT_CTYPE) && CTypeDescr_Check(arg)) {
+        return (CTypeDescrObject *)arg;
+    }
+    else if ((accept & ACCEPT_CDATA) && CData_Check(arg)) {
+        return ((CDataObject *)arg)->c_type;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyUnicode_Check(arg)) {
+        CTypeDescrObject *result;
+        arg = PyUnicode_AsASCIIString(arg);
+        if (arg == NULL)
+            return NULL;
+        result = _ffi_type(ffi, arg, accept);
+        Py_DECREF(arg);
+        return result;
+    }
+#endif
+    else {
+        const char *m1 = (accept & ACCEPT_STRING) ? "string" : "";
+        const char *m2 = (accept & ACCEPT_CTYPE) ? "ctype object" : "";
+        const char *m3 = (accept & ACCEPT_CDATA) ? "cdata object" : "";
+        const char *s12 = (*m1 && (*m2 || *m3)) ? " or " : "";
+        const char *s23 = (*m2 && *m3) ? " or " : "";
+        PyErr_Format(PyExc_TypeError, "expected a %s%s%s%s%s, got '%.200s'",
+                     m1, s12, m2, s23, m3,
+                     Py_TYPE(arg)->tp_name);
+        return NULL;
+    }
+}
+
+PyDoc_STRVAR(ffi_sizeof_doc,
+"Return the size in bytes of the argument.\n"
+"It can be a string naming a C type, or a 'cdata' instance.");
+
+static PyObject *ffi_sizeof(FFIObject *self, PyObject *arg)
+{
+    Py_ssize_t size;
+
+    if (CData_Check(arg)) {
+        size = direct_sizeof_cdata((CDataObject *)arg);
+    }
+    else {
+        CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL);
+        if (ct == NULL)
+            return NULL;
+        size = ct->ct_size;
+        if (size < 0) {
+            PyErr_Format(FFIError, "don't know the size of ctype '%s'",
+                         ct->ct_name);
+            return NULL;
+        }
+    }
+    return PyInt_FromSsize_t(size);
+}
+
+PyDoc_STRVAR(ffi_alignof_doc,
+"Return the natural alignment size in bytes of the argument.\n"
+"It can be a string naming a C type, or a 'cdata' instance.");
+
+static PyObject *ffi_alignof(FFIObject *self, PyObject *arg)
+{
+    int align;
+    CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL);
+    if (ct == NULL)
+        return NULL;
+
+    align = get_alignment(ct);
+    if (align < 0)
+        return NULL;
+    return PyInt_FromLong(align);
+}
+
+PyDoc_STRVAR(ffi_typeof_doc,
+"Parse the C type given as a string and return the\n"
+"corresponding <ctype> object.\n"
+"It can also be used on 'cdata' instance to get its C type.");
+
+static PyObject *_cpyextfunc_type_index(PyObject *x);  /* forward */
+
+static PyObject *ffi_typeof(FFIObject *self, PyObject *arg)
+{
+    PyObject *x = (PyObject *)_ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CDATA);
+    if (x != NULL) {
+        Py_INCREF(x);
+    }
+    else {
+        x = _cpyextfunc_type_index(arg);
+    }
+    return x;
+}
+
+PyDoc_STRVAR(ffi_new_doc,
+"Allocate an instance according to the specified C type and return a\n"
+"pointer to it.  The specified C type must be either a pointer or an\n"
+"array: ``new('X *')`` allocates an X and returns a pointer to it,\n"
+"whereas ``new('X[n]')`` allocates an array of n X'es and returns an\n"
+"array referencing it (which works mostly like a pointer, like in C).\n"
+"You can also use ``new('X[]', n)`` to allocate an array of a\n"
+"non-constant length n.\n"
+"\n"
+"The memory is initialized following the rules of declaring a global\n"
+"variable in C: by default it is zero-initialized, but an explicit\n"
+"initializer can be given which can be used to fill all or part of the\n"
+"memory.\n"
+"\n"
+"When the returned <cdata> object goes out of scope, the memory is\n"
+"freed.  In other words the returned <cdata> object has ownership of\n"
+"the value of type 'cdecl' that it points to.  This means that the raw\n"
+"data can be used as long as this object is kept alive, but must not be\n"
+"used for a longer time.  Be careful about that when copying the\n"
+"pointer to the memory somewhere else, e.g. into another structure.");
+
+static PyObject *_ffi_new(FFIObject *self, PyObject *args, PyObject *kwds,
+                          const cffi_allocator_t *allocator)
+{
+    CTypeDescrObject *ct;
+    PyObject *arg, *init = Py_None;
+    static char *keywords[] = {"cdecl", "init", NULL};
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:new", keywords,
+                                     &arg, &init))
+        return NULL;
+
+    ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE);
+    if (ct == NULL)
+        return NULL;
+
+    return direct_newp(ct, init, allocator);
+}
+
+static PyObject *ffi_new(FFIObject *self, PyObject *args, PyObject *kwds)
+{
+    return _ffi_new(self, args, kwds, &default_allocator);
+}
+
+static PyObject *_ffi_new_with_allocator(PyObject *allocator, PyObject *args,
+                                         PyObject *kwds)
+{
+    cffi_allocator_t alloc1;
+    PyObject *my_alloc, *my_free;
+    my_alloc = PyTuple_GET_ITEM(allocator, 1);
+    my_free  = PyTuple_GET_ITEM(allocator, 2);
+    alloc1.ca_alloc = (my_alloc == Py_None ? NULL : my_alloc);
+    alloc1.ca_free  = (my_free  == Py_None ? NULL : my_free);
+    alloc1.ca_dont_clear = (PyTuple_GET_ITEM(allocator, 3) == Py_False);
+
+    return _ffi_new((FFIObject *)PyTuple_GET_ITEM(allocator, 0),
+                    args, kwds, &alloc1);
+}
+
+PyDoc_STRVAR(ffi_new_allocator_doc,
+"Return a new allocator, i.e. a function that behaves like ffi.new()\n"
+"but uses the provided low-level 'alloc' and 'free' functions.\n"
+"\n"
+"'alloc' is called with the size as argument.  If it returns NULL, a\n"
+"MemoryError is raised.  'free' is called with the result of 'alloc'\n"
+"as argument.  Both can be either Python functions or directly C\n"
+"functions.  If 'free' is None, then no free function is called.\n"
+"If both 'alloc' and 'free' are None, the default is used.\n"
+"\n"
+"If 'should_clear_after_alloc' is set to False, then the memory\n"
+"returned by 'alloc' is assumed to be already cleared (or you are\n"
+"fine with garbage); otherwise CFFI will clear it.");
+
+static PyObject *ffi_new_allocator(FFIObject *self, PyObject *args,
+                                   PyObject *kwds)
+{
+    PyObject *allocator, *result;
+    PyObject *my_alloc = Py_None, *my_free = Py_None;
+    int should_clear_after_alloc = 1;
+    static char *keywords[] = {"alloc", "free", "should_clear_after_alloc",
+                               NULL};
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:new_allocator", keywords,
+                                     &my_alloc, &my_free,
+                                     &should_clear_after_alloc))
+        return NULL;
+
+    if (my_alloc == Py_None && my_free != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "cannot pass 'free' without 'alloc'");
+        return NULL;
+    }
+
+    allocator = PyTuple_Pack(4,
+                             (PyObject *)self,
+                             my_alloc,
+                             my_free,
+                             PyBool_FromLong(should_clear_after_alloc));
+    if (allocator == NULL)
+        return NULL;
+
+    {
+        static PyMethodDef md = {"allocator",
+                                 (PyCFunction)_ffi_new_with_allocator,
+                                 METH_VARARGS | METH_KEYWORDS};
+        result = PyCFunction_New(&md, allocator);
+    }
+    Py_DECREF(allocator);
+    return result;
+}
+
+PyDoc_STRVAR(ffi_cast_doc,
+"Similar to a C cast: returns an instance of the named C\n"
+"type initialized with the given 'source'.  The source is\n"
+"casted between integers or pointers of any type.");
+
+static PyObject *ffi_cast(FFIObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    PyObject *ob, *arg;
+    if (!PyArg_ParseTuple(args, "OO:cast", &arg, &ob))
+        return NULL;
+
+    ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE);
+    if (ct == NULL)
+        return NULL;
+
+    return do_cast(ct, ob);
+}
+
+PyDoc_STRVAR(ffi_string_doc,
+"Return a Python string (or unicode string) from the 'cdata'.  If\n"
+"'cdata' is a pointer or array of characters or bytes, returns the\n"
+"null-terminated string.  The returned string extends until the first\n"
+"null character, or at most 'maxlen' characters.  If 'cdata' is an\n"
+"array then 'maxlen' defaults to its length.\n"
+"\n"
+"If 'cdata' is a pointer or array of wchar_t, returns a unicode string\n"
+"following the same rules.\n"
+"\n"
+"If 'cdata' is a single character or byte or a wchar_t, returns it as a\n"
+"string or unicode string.\n"
+"\n"
+"If 'cdata' is an enum, returns the value of the enumerator as a\n"
+"string, or 'NUMBER' if the value is out of range.");
+
+#define ffi_string  b_string     /* ffi_string() => b_string()
+                                    from _cffi_backend.c */
+
+PyDoc_STRVAR(ffi_unpack_doc,
+"Unpack an array of C data of the given length,\n"
+"returning a Python string/unicode/list.\n"
+"\n"
+"If 'cdata' is a pointer to 'char', returns a byte string.\n"
+"It does not stop at the first null.  This is equivalent to:\n"
+"ffi.buffer(cdata, length)[:]\n"
+"\n"
+"If 'cdata' is a pointer to 'wchar_t', returns a unicode string.\n"
+"'length' is measured in wchar_t's; it is not the size in bytes.\n"
+"\n"
+"If 'cdata' is a pointer to anything else, returns a list of\n"
+"'length' items.  This is a faster equivalent to:\n"
+"[cdata[i] for i in range(length)]");
+
+#define ffi_unpack  b_unpack     /* ffi_unpack() => b_unpack()
+                                    from _cffi_backend.c */
+
+
+PyDoc_STRVAR(ffi_offsetof_doc,
+"Return the offset of the named field inside the given structure or\n"
+"array, which must be given as a C type name.  You can give several\n"
+"field names in case of nested structures.  You can also give numeric\n"
+"values which correspond to array items, in case of an array type.");
+
+static PyObject *ffi_offsetof(FFIObject *self, PyObject *args)
+{
+    PyObject *arg;
+    CTypeDescrObject *ct;
+    Py_ssize_t i, offset;
+
+    if (PyTuple_Size(args) < 2) {
+        PyErr_SetString(PyExc_TypeError,
+                        "offsetof() expects at least 2 arguments");
+        return NULL;
+    }
+
+    arg = PyTuple_GET_ITEM(args, 0);
+    ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE);
+    if (ct == NULL)
+        return NULL;
+
+    offset = 0;
+    for (i = 1; i < PyTuple_GET_SIZE(args); i++) {
+        Py_ssize_t ofs1;
+        ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i), i > 1, &ofs1);
+        if (ct == NULL)
+            return NULL;
+        offset += ofs1;
+    }
+    return PyInt_FromSsize_t(offset);
+}
+
+PyDoc_STRVAR(ffi_addressof_doc,
+"Limited equivalent to the '&' operator in C:\n"
+"\n"
+"1. ffi.addressof(<cdata 'struct-or-union'>) returns a cdata that is a\n"
+"pointer to this struct or union.\n"
+"\n"
+"2. ffi.addressof(<cdata>, field-or-index...) returns the address of a\n"
+"field or array item inside the given structure or array, recursively\n"
+"in case of nested structures or arrays.\n"
+"\n"
+"3. ffi.addressof(<library>, \"name\") returns the address of the named\n"
+"function or global variable.");
+
+static PyObject *address_of_global_var(PyObject *args);  /* forward */
+
+static PyObject *ffi_addressof(FFIObject *self, PyObject *args)
+{
+    PyObject *arg, *z, *result;
+    CTypeDescrObject *ct;
+    Py_ssize_t i, offset = 0;
+    int accepted_flags;
+
+    if (PyTuple_Size(args) < 1) {
+        PyErr_SetString(PyExc_TypeError,
+                        "addressof() expects at least 1 argument");
+        return NULL;
+    }
+
+    arg = PyTuple_GET_ITEM(args, 0);
+    if (LibObject_Check(arg)) {
+        /* case 3 in the docstring */
+        return address_of_global_var(args);
+    }
+
+    ct = _ffi_type(self, arg, ACCEPT_CDATA);
+    if (ct == NULL)
+        return NULL;
+
+    if (PyTuple_GET_SIZE(args) == 1) {
+        /* case 1 in the docstring */
+        accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY;
+        if ((ct->ct_flags & accepted_flags) == 0) {
+            PyErr_SetString(PyExc_TypeError,
+                            "expected a cdata struct/union/array object");
+            return NULL;
+        }
+    }
+    else {
+        /* case 2 in the docstring */
+        accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER;
+        if ((ct->ct_flags & accepted_flags) == 0) {
+            PyErr_SetString(PyExc_TypeError,
+                        "expected a cdata struct/union/array/pointer object");
+            return NULL;
+        }
+        for (i = 1; i < PyTuple_GET_SIZE(args); i++) {
+            Py_ssize_t ofs1;
+            ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i),
+                                     i > 1, &ofs1);
+            if (ct == NULL)
+                return NULL;
+            offset += ofs1;
+        }
+    }
+
+    z = new_pointer_type(ct);
+    if (z == NULL)
+        return NULL;
+
+    result = new_simple_cdata(((CDataObject *)arg)->c_data + offset,
+                              (CTypeDescrObject *)z);
+    Py_DECREF(z);
+    return result;
+}
+
+static PyObject *_combine_type_name_l(CTypeDescrObject *ct,
+                                      size_t extra_text_len)
+{
+    size_t base_name_len;
+    PyObject *result;
+    char *p;
+
+    base_name_len = strlen(ct->ct_name);
+    result = PyBytes_FromStringAndSize(NULL, base_name_len + extra_text_len);
+    if (result == NULL)
+        return NULL;
+
+    p = PyBytes_AS_STRING(result);
+    memcpy(p, ct->ct_name, ct->ct_name_position);
+    p += ct->ct_name_position;
+    p += extra_text_len;
+    memcpy(p, ct->ct_name + ct->ct_name_position,
+           base_name_len - ct->ct_name_position);
+    return result;
+}
+
+PyDoc_STRVAR(ffi_getctype_doc,
+"Return a string giving the C type 'cdecl', which may be itself a\n"
+"string or a <ctype> object.  If 'replace_with' is given, it gives\n"
+"extra text to append (or insert for more complicated C types), like a\n"
+"variable name, or '*' to get actually the C type 'pointer-to-cdecl'.");
+
+static PyObject *ffi_getctype(FFIObject *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *c_decl, *res;
+    char *p, *replace_with = "";
+    int add_paren, add_space;
+    CTypeDescrObject *ct;
+    size_t replace_with_len;
+    static char *keywords[] = {"cdecl", "replace_with", NULL};
+#if PY_MAJOR_VERSION >= 3
+    PyObject *u;
+#endif
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s:getctype", keywords,
+                                     &c_decl, &replace_with))
+        return NULL;
+
+    ct = _ffi_type(self, c_decl, ACCEPT_STRING|ACCEPT_CTYPE);
+    if (ct == NULL)
+        return NULL;
+
+    while (replace_with[0] != 0 && isspace(replace_with[0]))
+        replace_with++;
+    replace_with_len = strlen(replace_with);
+    while (replace_with_len > 0 && isspace(replace_with[replace_with_len - 1]))
+        replace_with_len--;
+
+    add_paren = (replace_with[0] == '*' &&
+                 ((ct->ct_flags & CT_ARRAY) != 0));
+    add_space = (!add_paren && replace_with_len > 0 &&
+                 replace_with[0] != '[' && replace_with[0] != '(');
+
+    res = _combine_type_name_l(ct, replace_with_len + add_space + 2*add_paren);
+    if (res == NULL)
+        return NULL;
+
+    p = PyBytes_AS_STRING(res) + ct->ct_name_position;
+    if (add_paren)
+        *p++ = '(';
+    if (add_space)
+        *p++ = ' ';
+    memcpy(p, replace_with, replace_with_len);
+    if (add_paren)
+        p[replace_with_len] = ')';
+
+#if PY_MAJOR_VERSION >= 3
+    /* bytes -> unicode string */
+    u = PyUnicode_DecodeLatin1(PyBytes_AS_STRING(res),
+                               PyBytes_GET_SIZE(res),
+                               NULL);
+    Py_DECREF(res);
+    res = u;
+#endif
+
+    return res;
+}
+
+PyDoc_STRVAR(ffi_new_handle_doc,
+"Return a non-NULL cdata of type 'void *' that contains an opaque\n"
+"reference to the argument, which can be any Python object.  To cast it\n"
+"back to the original object, use from_handle().  You must keep alive\n"
+"the cdata object returned by new_handle()!");
+
+static PyObject *ffi_new_handle(FFIObject *self, PyObject *arg)
+{
+    /* g_ct_voidp is equal to <ctype 'void *'> */
+    return newp_handle(g_ct_voidp, arg);
+}
+
+PyDoc_STRVAR(ffi_from_handle_doc,
+"Cast a 'void *' back to a Python object.  Must be used *only* on the\n"
+"pointers returned by new_handle(), and *only* as long as the exact\n"
+"cdata object returned by new_handle() is still alive (somewhere else\n"
+"in the program).  Failure to follow these rules will crash.");
+
+#define ffi_from_handle  b_from_handle   /* ffi_from_handle => b_from_handle
+                                            from _cffi_backend.c */
+
+PyDoc_STRVAR(ffi_from_buffer_doc,
+"Return a <cdata 'char[]'> that points to the data of the given Python\n"
+"object, which must support the buffer interface.  Note that this is\n"
+"not meant to be used on the built-in types str or unicode\n"
+"(you can build 'char[]' arrays explicitly) but only on objects\n"
+"containing large quantities of raw data in some other format, like\n"
+"'array.array' or numpy arrays.");
+
+static PyObject *ffi_from_buffer(FFIObject *self, PyObject *args,
+                                 PyObject *kwds)
+{
+    PyObject *cdecl1, *python_buf = NULL;
+    CTypeDescrObject *ct;
+    int require_writable = 0;
+    static char *keywords[] = {"cdecl", "python_buffer",
+                               "require_writable", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oi:from_buffer", keywords,
+                                     &cdecl1, &python_buf, &require_writable))
+        return NULL;
+
+    if (python_buf == NULL) {
+        python_buf = cdecl1;
+        ct = g_ct_chararray;
+    }
+    else {
+        ct = _ffi_type(self, cdecl1, ACCEPT_STRING|ACCEPT_CTYPE);
+        if (ct == NULL)
+            return NULL;
+    }
+    return direct_from_buffer(ct, python_buf, require_writable);
+}
+
+PyDoc_STRVAR(ffi_gc_doc,
+"Return a new cdata object that points to the same data.\n"
+"Later, when this new cdata object is garbage-collected,\n"
+"'destructor(old_cdata_object)' will be called.\n"
+"\n"
+"The optional 'size' gives an estimate of the size, used to\n"
+"trigger the garbage collection more eagerly.  So far only used\n"
+"on PyPy.  It tells the GC that the returned object keeps alive\n"
+"roughly 'size' bytes of external memory.");
+
+#define ffi_gc  b_gcp     /* ffi_gc() => b_gcp()
+                             from _cffi_backend.c */
+
+PyDoc_STRVAR(ffi_def_extern_doc,
+"A decorator.  Attaches the decorated Python function to the C code\n"
+"generated for the 'extern \"Python\"' function of the same name.\n"
+"Calling the C function will then invoke the Python function.\n"
+"\n"
+"Optional arguments: 'name' is the name of the C function, if\n"
+"different from the Python function; and 'error' and 'onerror'\n"
+"handle what occurs if the Python function raises an exception\n"
+"(see the docs for details).");
+
+/* forward; see call_python.c */
+static PyObject *_ffi_def_extern_decorator(PyObject *, PyObject *);
+
+static PyObject *ffi_def_extern(FFIObject *self, PyObject *args,
+                                PyObject *kwds)
+{
+    static PyMethodDef md = {"def_extern_decorator",
+                             (PyCFunction)_ffi_def_extern_decorator, METH_O};
+    PyObject *name = Py_None, *error = Py_None;
+    PyObject *res, *onerror = Py_None;
+    static char *keywords[] = {"name", "error", "onerror", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", keywords,
+                                     &name, &error, &onerror))
+        return NULL;
+
+    args = Py_BuildValue("(OOOO)", (PyObject *)self, name, error, onerror);
+    if (args == NULL)
+        return NULL;
+
+    res = PyCFunction_New(&md, args);
+    Py_DECREF(args);
+    return res;
+}
+
+PyDoc_STRVAR(ffi_callback_doc,
+"Return a callback object or a decorator making such a callback object.\n"
+"'cdecl' must name a C function pointer type.  The callback invokes the\n"
+"specified 'python_callable' (which may be provided either directly or\n"
+"via a decorator).  Important: the callback object must be manually\n"
+"kept alive for as long as the callback may be invoked from the C code.");
+
+static PyObject *_ffi_callback_decorator(PyObject *outer_args, PyObject *fn)
+{
+    PyObject *res, *old;
+
+    old = PyTuple_GET_ITEM(outer_args, 1);
+    PyTuple_SET_ITEM(outer_args, 1, fn);
+    res = b_callback(NULL, outer_args);
+    PyTuple_SET_ITEM(outer_args, 1, old);
+    return res;
+}
+
+static PyObject *ffi_callback(FFIObject *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *c_decl, *python_callable = Py_None, *error = Py_None;
+    PyObject *res, *onerror = Py_None;
+    static char *keywords[] = {"cdecl", "python_callable", "error",
+                               "onerror", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", keywords,
+                                     &c_decl, &python_callable, &error,
+                                     &onerror))
+        return NULL;
+
+    c_decl = (PyObject *)_ffi_type(self, c_decl, ACCEPT_STRING | ACCEPT_CTYPE |
+                                                 CONSIDER_FN_AS_FNPTR);
+    if (c_decl == NULL)
+        return NULL;
+
+    args = Py_BuildValue("(OOOO)", c_decl, python_callable, error, onerror);
+    if (args == NULL)
+        return NULL;
+
+    if (python_callable != Py_None) {
+        res = b_callback(NULL, args);
+    }
+    else {
+        static PyMethodDef md = {"callback_decorator",
+                                 (PyCFunction)_ffi_callback_decorator, METH_O};
+        res = PyCFunction_New(&md, args);
+    }
+    Py_DECREF(args);
+    return res;
+}
+
+#ifdef MS_WIN32
+PyDoc_STRVAR(ffi_getwinerror_doc,
+"Return either the GetLastError() or the error number given by the\n"
+"optional 'code' argument, as a tuple '(code, message)'.");
+
+#define ffi_getwinerror  b_getwinerror  /* ffi_getwinerror() => b_getwinerror()
+                                           from misc_win32.h */
+#endif
+
+PyDoc_STRVAR(ffi_errno_doc, "the value of 'errno' from/to the C calls");
+
+static PyObject *ffi_get_errno(PyObject *self, void *closure)
+{
+    /* xxx maybe think about how to make the saved errno local
+       to an ffi instance */
+    return b_get_errno(NULL, NULL);
+}
+
+static int ffi_set_errno(PyObject *self, PyObject *newval, void *closure)
+{
+    PyObject *x = b_set_errno(NULL, newval);
+    if (x == NULL)
+        return -1;
+    Py_DECREF(x);
+    return 0;
+}
+
+PyDoc_STRVAR(ffi_dlopen_doc,
+"Load and return a dynamic library identified by 'name'.  The standard\n"
+"C library can be loaded by passing None.\n"
+"\n"
+"Note that functions and types declared with 'ffi.cdef()' are not\n"
+"linked to a particular library, just like C headers.  In the library\n"
+"we only look for the actual (untyped) symbols at the time of their\n"
+"first access.");
+
+PyDoc_STRVAR(ffi_dlclose_doc,
+"Close a library obtained with ffi.dlopen().  After this call, access to\n"
+"functions or variables from the library will fail (possibly with a\n"
+"segmentation fault).");
+
+static PyObject *ffi_dlopen(PyObject *self, PyObject *args);  /* forward */
+static PyObject *ffi_dlclose(PyObject *self, PyObject *args);  /* forward */
+
+PyDoc_STRVAR(ffi_int_const_doc,
+"Get the value of an integer constant.\n"
+"\n"
+"'ffi.integer_const(\"xxx\")' is equivalent to 'lib.xxx' if xxx names an\n"
+"integer constant.  The point of this function is limited to use cases\n"
+"where you have an 'ffi' object but not any associated 'lib' object.");
+
+static PyObject *ffi_int_const(FFIObject *self, PyObject *args, PyObject *kwds)
+{
+    char *name;
+    PyObject *x;
+    static char *keywords[] = {"name", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", keywords, &name))
+        return NULL;
+
+    x = ffi_fetch_int_constant(self, name, 0);
+
+    if (x == NULL && !PyErr_Occurred()) {
+        PyErr_Format(PyExc_AttributeError,
+                     "integer constant '%.200s' not found", name);
+    }
+    return x;
+}
+
+PyDoc_STRVAR(ffi_list_types_doc,
+"Returns the user type names known to this FFI instance.\n"
+"This returns a tuple containing three lists of names:\n"
+"(typedef_names, names_of_structs, names_of_unions)");
+
+static PyObject *ffi_list_types(FFIObject *self, PyObject *noargs)
+{
+    Py_ssize_t i, n1 = self->types_builder.ctx.num_typenames;
+    Py_ssize_t n23 = self->types_builder.ctx.num_struct_unions;
+    PyObject *o, *lst[3] = {NULL, NULL, NULL}, *result = NULL;
+
+    lst[0] = PyList_New(n1);
+    if (lst[0] == NULL)
+        goto error;
+    lst[1] = PyList_New(0);
+    if (lst[1] == NULL)
+        goto error;
+    lst[2] = PyList_New(0);
+    if (lst[2] == NULL)
+        goto error;
+
+    for (i = 0; i < n1; i++) {
+        o = PyText_FromString(self->types_builder.ctx.typenames[i].name);
+        if (o == NULL)
+            goto error;
+        PyList_SET_ITEM(lst[0], i, o);
+    }
+
+    for (i = 0; i < n23; i++) {
+        const struct _cffi_struct_union_s *s;
+        int err, index;
+
+        s = &self->types_builder.ctx.struct_unions[i];
+        if (s->name[0] == '$')
+            continue;
+
+        o = PyText_FromString(s->name);
+        if (o == NULL)
+            goto error;
+        index = (s->flags & _CFFI_F_UNION) ? 2 : 1;
+        err = PyList_Append(lst[index], o);
+        Py_DECREF(o);
+        if (err < 0)
+            goto error;
+    }
+    result = PyTuple_Pack(3, lst[0], lst[1], lst[2]);
+    /* fall-through */
+ error:
+    Py_XDECREF(lst[2]);
+    Py_XDECREF(lst[1]);
+    Py_XDECREF(lst[0]);
+    return result;
+}
+
+PyDoc_STRVAR(ffi_memmove_doc,
+"ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.\n"
+"\n"
+"Like the C function memmove(), the memory areas may overlap;\n"
+"apart from that it behaves like the C function memcpy().\n"
+"\n"
+"'src' can be any cdata ptr or array, or any Python buffer object.\n"
+"'dest' can be any cdata ptr or array, or a writable Python buffer\n"
+"object.  The size to copy, 'n', is always measured in bytes.\n"
+"\n"
+"Unlike other methods, this one supports all Python buffer including\n"
+"byte strings and bytearrays---but it still does not support\n"
+"non-contiguous buffers.");
+
+#define ffi_memmove  b_memmove     /* ffi_memmove() => b_memmove()
+                                      from _cffi_backend.c */
+
+PyDoc_STRVAR(ffi_init_once_doc,
+"init_once(function, tag): run function() once.  More precisely,\n"
+"'function()' is called the first time we see a given 'tag'.\n"
+"\n"
+"The return value of function() is remembered and returned by the current\n"
+"and all future init_once() with the same tag.  If init_once() is called\n"
+"from multiple threads in parallel, all calls block until the execution\n"
+"of function() is done.  If function() raises an exception, it is\n"
+"propagated and nothing is cached.");
+
+#if PY_MAJOR_VERSION < 3
+/* PyCapsule_New is redefined to be PyCObject_FromVoidPtr in _cffi_backend,
+   which gives 2.6 compatibility; but the destructor signature is different */
+static void _free_init_once_lock(void *lock)
+{
+    PyThread_free_lock((PyThread_type_lock)lock);
+}
+#else
+static void _free_init_once_lock(PyObject *capsule)
+{
+    PyThread_type_lock lock;
+    lock = PyCapsule_GetPointer(capsule, "cffi_init_once_lock");
+    if (lock != NULL)
+        PyThread_free_lock(lock);
+}
+#endif
+
+static PyObject *ffi_init_once(FFIObject *self, PyObject *args, PyObject *kwds)
+{
+    static char *keywords[] = {"func", "tag", NULL};
+    PyObject *cache, *func, *tag, *tup, *res, *x, *lockobj;
+    PyThread_type_lock lock;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", keywords, &func, &tag))
+        return NULL;
+
+    /* a lot of fun with reference counting and error checking
+       in this function */
+
+    /* atomically get or create a new dict (no GIL release) */
+    cache = self->init_once_cache;
+    if (cache == NULL) {
+        cache = PyDict_New();
+        if (cache == NULL)
+            return NULL;
+        self->init_once_cache = cache;
+    }
+
+    /* get the tuple from cache[tag], or make a new one: (False, lock) */
+    tup = PyDict_GetItem(cache, tag);
+    if (tup == NULL) {
+        lock = PyThread_allocate_lock();
+        if (lock == NULL)
+            return NULL;
+        x = PyCapsule_New(lock, "cffi_init_once_lock", _free_init_once_lock);
+        if (x == NULL) {
+            PyThread_free_lock(lock);
+            return NULL;
+        }
+        tup = PyTuple_Pack(2, Py_False, x);
+        Py_DECREF(x);
+        if (tup == NULL)
+            return NULL;
+        x = tup;
+
+        /* Possible corner case if 'tag' is an object overriding __eq__
+           in pure Python: the GIL may be released when we are running it.
+           We really need to call dict.setdefault(). */
+        tup = PyObject_CallMethod(cache, "setdefault", "OO", tag, x);
+        Py_DECREF(x);
+        if (tup == NULL)
+            return NULL;
+
+        Py_DECREF(tup);   /* there is still a ref inside the dict */
+    }
+
+    res = PyTuple_GET_ITEM(tup, 1);
+    Py_INCREF(res);
+
+    if (PyTuple_GET_ITEM(tup, 0) == Py_True) {
+        /* tup == (True, result): return the result. */
+        return res;
+    }
+
+    /* tup == (False, lock) */
+    lockobj = res;
+    lock = (PyThread_type_lock)PyCapsule_GetPointer(lockobj,
+                                                    "cffi_init_once_lock");
+    if (lock == NULL) {
+        Py_DECREF(lockobj);
+        return NULL;
+    }
+
+    Py_BEGIN_ALLOW_THREADS
+    PyThread_acquire_lock(lock, WAIT_LOCK);
+    Py_END_ALLOW_THREADS
+
+    x = PyDict_GetItem(cache, tag);
+    if (x != NULL && PyTuple_GET_ITEM(x, 0) == Py_True) {
+        /* the real result was put in the dict while we were waiting
+           for PyThread_acquire_lock() above */
+        res = PyTuple_GET_ITEM(x, 1);
+        Py_INCREF(res);
+    }
+    else {
+        res = PyObject_CallFunction(func, "");
+        if (res != NULL) {
+            tup = PyTuple_Pack(2, Py_True, res);
+            if (tup == NULL || PyDict_SetItem(cache, tag, tup) < 0) {
+                Py_XDECREF(tup);
+                Py_DECREF(res);
+                res = NULL;
+            }
+        }
+    }
+
+    PyThread_release_lock(lock);
+    Py_DECREF(lockobj);
+    return res;
+}
+
+PyDoc_STRVAR(ffi_release_doc,
+"Release now the resources held by a 'cdata' object from ffi.new(),\n"
+"ffi.gc() or ffi.from_buffer().  The cdata object must not be used\n"
+"afterwards.\n"
+"\n"
+"'ffi.release(cdata)' is equivalent to 'cdata.__exit__()'.\n"
+"\n"
+"Note that on CPython this method has no effect (so far) on objects\n"
+"returned by ffi.new(), because the memory is allocated inline with the\n"
+"cdata object and cannot be freed independently.  It might be fixed in\n"
+"future releases of cffi.");
+
+#define ffi_release  b_release     /* ffi_release() => b_release()
+                                      from _cffi_backend.c */
+
+
+#define METH_VKW  (METH_VARARGS | METH_KEYWORDS)
+static PyMethodDef ffi_methods[] = {
+ {"addressof",  (PyCFunction)ffi_addressof,  METH_VARARGS, ffi_addressof_doc},
+ {"alignof",    (PyCFunction)ffi_alignof,    METH_O,       ffi_alignof_doc},
+ {"def_extern", (PyCFunction)ffi_def_extern, METH_VKW,     ffi_def_extern_doc},
+ {"callback",   (PyCFunction)ffi_callback,   METH_VKW,     ffi_callback_doc},
+ {"cast",       (PyCFunction)ffi_cast,       METH_VARARGS, ffi_cast_doc},
+ {"dlclose",    (PyCFunction)ffi_dlclose,    METH_VARARGS, ffi_dlclose_doc},
+ {"dlopen",     (PyCFunction)ffi_dlopen,     METH_VARARGS, ffi_dlopen_doc},
+ {"from_buffer",(PyCFunction)ffi_from_buffer,METH_VKW,     ffi_from_buffer_doc},
+ {"from_handle",(PyCFunction)ffi_from_handle,METH_O,       ffi_from_handle_doc},
+ {"gc",         (PyCFunction)ffi_gc,         METH_VKW,     ffi_gc_doc},
+ {"getctype",   (PyCFunction)ffi_getctype,   METH_VKW,     ffi_getctype_doc},
+#ifdef MS_WIN32
+ {"getwinerror",(PyCFunction)ffi_getwinerror,METH_VKW,     ffi_getwinerror_doc},
+#endif
+ {"init_once",  (PyCFunction)ffi_init_once,  METH_VKW,     ffi_init_once_doc},
+ {"integer_const",(PyCFunction)ffi_int_const,METH_VKW,     ffi_int_const_doc},
+ {"list_types", (PyCFunction)ffi_list_types, METH_NOARGS,  ffi_list_types_doc},
+ {"memmove",    (PyCFunction)ffi_memmove,    METH_VKW,     ffi_memmove_doc},
+ {"new",        (PyCFunction)ffi_new,        METH_VKW,     ffi_new_doc},
+{"new_allocator",(PyCFunction)ffi_new_allocator,METH_VKW,ffi_new_allocator_doc},
+ {"new_handle", (PyCFunction)ffi_new_handle, METH_O,       ffi_new_handle_doc},
+ {"offsetof",   (PyCFunction)ffi_offsetof,   METH_VARARGS, ffi_offsetof_doc},
+ {"release",    (PyCFunction)ffi_release,    METH_O,       ffi_release_doc},
+ {"sizeof",     (PyCFunction)ffi_sizeof,     METH_O,       ffi_sizeof_doc},
+ {"string",     (PyCFunction)ffi_string,     METH_VKW,     ffi_string_doc},
+ {"typeof",     (PyCFunction)ffi_typeof,     METH_O,       ffi_typeof_doc},
+ {"unpack",     (PyCFunction)ffi_unpack,     METH_VKW,     ffi_unpack_doc},
+ {NULL}
+};
+
+static PyGetSetDef ffi_getsets[] = {
+    {"errno",  ffi_get_errno,  ffi_set_errno,  ffi_errno_doc},
+    {NULL}
+};
+
+static PyTypeObject FFI_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "CompiledFFI",
+    sizeof(FFIObject),
+    0,
+    (destructor)ffi_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 | Py_TPFLAGS_HAVE_GC |
+        Py_TPFLAGS_BASETYPE,                    /* tp_flags */
+    0,                                          /* tp_doc */
+    (traverseproc)ffi_traverse,                 /* tp_traverse */
+    0,                                          /* tp_clear */
+    0,                                          /* tp_richcompare */
+    0,                                          /* tp_weaklistoffset */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    ffi_methods,                                /* tp_methods */
+    0,                                          /* tp_members */
+    ffi_getsets,                                /* tp_getset */
+    0,                                          /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    0,                                          /* tp_dictoffset */
+    ffiobj_init,                                /* tp_init */
+    0,                                          /* tp_alloc */
+    ffiobj_new,                                 /* tp_new */
+    PyObject_GC_Del,                            /* tp_free */
+};
+
+
+static PyObject *
+_fetch_external_struct_or_union(const struct _cffi_struct_union_s *s,
+                                PyObject *included_ffis, int recursion)
+{
+    Py_ssize_t i;
+
+    if (included_ffis == NULL)
+        return NULL;
+
+    if (recursion > 100) {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "recursion overflow in ffi.include() delegations");
+        return NULL;
+    }
+
+    for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) {
+        FFIObject *ffi1;
+        const struct _cffi_struct_union_s *s1;
+        int sindex;
+        PyObject *x;
+
+        ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i);
+        sindex = search_in_struct_unions(&ffi1->types_builder.ctx, s->name,
+                                         strlen(s->name));
+        if (sindex < 0)  /* not found at all */
+            continue;
+        s1 = &ffi1->types_builder.ctx.struct_unions[sindex];
+        if ((s1->flags & (_CFFI_F_EXTERNAL | _CFFI_F_UNION))
+                == (s->flags & _CFFI_F_UNION)) {
+            /* s1 is not external, and the same kind (struct or union) as s */
+            return _realize_c_struct_or_union(&ffi1->types_builder, sindex);
+        }
+        /* not found, look more recursively */
+        x = _fetch_external_struct_or_union(
+                s, ffi1->types_builder.included_ffis, recursion + 1);
+        if (x != NULL || PyErr_Occurred())
+            return x;   /* either found, or got an error */
+    }
+    return NULL;   /* not found at all, leave without an error */
+}
diff --git a/c/file_emulator.h b/c/file_emulator.h
new file mode 100644
index 0000000..82a34c0
--- /dev/null
+++ b/c/file_emulator.h
@@ -0,0 +1,93 @@
+
+/* Emulation of PyFile_Check() and PyFile_AsFile() for Python 3. */
+
+static PyObject *PyIOBase_TypeObj;
+
+static int init_file_emulator(void)
+{
+    if (PyIOBase_TypeObj == NULL) {
+        PyObject *io = PyImport_ImportModule("_io");
+        if (io == NULL)
+            return -1;
+        PyIOBase_TypeObj = PyObject_GetAttrString(io, "_IOBase");
+        if (PyIOBase_TypeObj == NULL)
+            return -1;
+    }
+    return 0;
+}
+
+
+#define PyFile_Check(p)  PyObject_IsInstance(p, PyIOBase_TypeObj)
+
+
+static void _close_file_capsule(PyObject *ob_capsule)
+{
+    FILE *f = (FILE *)PyCapsule_GetPointer(ob_capsule, "FILE");
+    if (f != NULL)
+        fclose(f);
+}
+
+
+static FILE *PyFile_AsFile(PyObject *ob_file)
+{
+    PyObject *ob, *ob_capsule = NULL, *ob_mode = NULL;
+    FILE *f;
+    int fd;
+    const char *mode;
+
+    ob = PyObject_CallMethod(ob_file, "flush", NULL);
+    if (ob == NULL)
+        goto fail;
+    Py_DECREF(ob);
+
+    ob_capsule = PyObject_GetAttrString(ob_file, "__cffi_FILE");
+    if (ob_capsule == NULL) {
+        PyErr_Clear();
+
+        fd = PyObject_AsFileDescriptor(ob_file);
+        if (fd < 0)
+            goto fail;
+
+        ob_mode = PyObject_GetAttrString(ob_file, "mode");
+        if (ob_mode == NULL)
+            goto fail;
+        mode = PyText_AsUTF8(ob_mode);
+        if (mode == NULL)
+            goto fail;
+
+        fd = dup(fd);
+        if (fd < 0) {
+            PyErr_SetFromErrno(PyExc_OSError);
+            goto fail;
+        }
+
+        f = fdopen(fd, mode);
+        if (f == NULL) {
+            close(fd);
+            PyErr_SetFromErrno(PyExc_OSError);
+            goto fail;
+        }
+        setbuf(f, NULL);    /* non-buffered */
+        Py_DECREF(ob_mode);
+        ob_mode = NULL;
+
+        ob_capsule = PyCapsule_New(f, "FILE", _close_file_capsule);
+        if (ob_capsule == NULL) {
+            fclose(f);
+            goto fail;
+        }
+
+        if (PyObject_SetAttrString(ob_file, "__cffi_FILE", ob_capsule) < 0)
+            goto fail;
+    }
+    else {
+        f = PyCapsule_GetPointer(ob_capsule, "FILE");
+    }
+    Py_DECREF(ob_capsule);   /* assumes still at least one reference */
+    return f;
+
+ fail:
+    Py_XDECREF(ob_mode);
+    Py_XDECREF(ob_capsule);
+    return NULL;
+}
diff --git a/c/lib_obj.c b/c/lib_obj.c
new file mode 100644
index 0000000..7cd40ec
--- /dev/null
+++ b/c/lib_obj.c
@@ -0,0 +1,712 @@
+
+/* A Lib object is what is in the "lib" attribute of a C extension
+   module originally created by recompile().
+
+   A Lib object is special in the sense that it has a custom
+   __getattr__ which returns C globals, functions and constants.  The
+   original idea was to raise AttributeError for anything else, even
+   attrs like '__class__', but it breaks various things; now, standard
+   attrs are returned, but in the unlikely case where a user cdef()s
+   the same name, then the standard attr is hidden (and the various
+   things like introspection might break).
+
+   A Lib object has got a reference to the _cffi_type_context_s
+   structure, which is used to create lazily the objects returned by
+   __getattr__.
+*/
+
+struct CPyExtFunc_s {
+    PyMethodDef md;
+    void *direct_fn;
+    int type_index;
+    char doc[1];
+};
+
+struct LibObject_s {
+    PyObject_HEAD
+    builder_c_t *l_types_builder; /* same as the one on the ffi object */
+    PyObject *l_dict;           /* content, built lazily */
+    PyObject *l_libname;        /* some string that gives the name of the lib */
+    FFIObject *l_ffi;           /* reference back to the ffi object */
+    void *l_libhandle;          /* the dlopen()ed handle, if any */
+};
+
+static struct CPyExtFunc_s *_cpyextfunc_get(PyObject *x)
+{
+    PyObject *y;
+    LibObject *lo;
+    PyCFunctionObject *fo;
+
+    if (!PyCFunction_Check(x))
+        return NULL;
+    y = PyCFunction_GET_SELF(x);
+    if (!LibObject_Check(y))
+        return NULL;
+
+    fo = (PyCFunctionObject *)x;
+    lo = (LibObject *)y;
+    if (lo->l_libname != fo->m_module)
+        return NULL;
+
+    return (struct CPyExtFunc_s *)(fo->m_ml);
+}
+
+static PyObject *_cpyextfunc_type(LibObject *lib, struct CPyExtFunc_s *exf)
+{
+    PyObject *tuple, *result;
+    tuple = realize_c_type_or_func(lib->l_types_builder,
+                                   lib->l_types_builder->ctx.types,
+                                   exf->type_index);
+    if (tuple == NULL)
+        return NULL;
+
+    /* 'tuple' is a tuple of length 1 containing the real CT_FUNCTIONPTR
+       object */
+    result = PyTuple_GetItem(tuple, 0);
+    Py_XINCREF(result);
+    Py_DECREF(tuple);
+    return result;
+}
+
+static PyObject *_cpyextfunc_type_index(PyObject *x)
+{
+    struct CPyExtFunc_s *exf;
+    LibObject *lib;
+
+    assert(PyErr_Occurred());
+    exf = _cpyextfunc_get(x);
+    if (exf == NULL)
+        return NULL;    /* still the same exception is set */
+
+    PyErr_Clear();
+
+    lib = (LibObject *)PyCFunction_GET_SELF(x);
+    return _cpyextfunc_type(lib, exf);
+}
+
+static void cdlopen_close_ignore_errors(void *libhandle);  /* forward */
+static void *cdlopen_fetch(PyObject *libname, void *libhandle,
+                           const char *symbol);
+
+static void lib_dealloc(LibObject *lib)
+{
+    PyObject_GC_UnTrack(lib);
+    cdlopen_close_ignore_errors(lib->l_libhandle);
+    Py_DECREF(lib->l_dict);
+    Py_DECREF(lib->l_libname);
+    Py_DECREF(lib->l_ffi);
+    PyObject_GC_Del(lib);
+}
+
+static int lib_traverse(LibObject *lib, visitproc visit, void *arg)
+{
+    Py_VISIT(lib->l_dict);
+    Py_VISIT(lib->l_libname);
+    Py_VISIT(lib->l_ffi);
+    return 0;
+}
+
+static PyObject *lib_repr(LibObject *lib)
+{
+    return PyText_FromFormat("<Lib object for '%.200s'>",
+                             PyText_AS_UTF8(lib->l_libname));
+}
+
+static PyObject *lib_build_cpython_func(LibObject *lib,
+                                        const struct _cffi_global_s *g,
+                                        const char *s, int flags)
+{
+    /* First make sure the argument types and return type are really
+       built.  The C extension code can then assume that they are,
+       by calling _cffi_type().
+    */
+    PyObject *result = NULL;
+    CTypeDescrObject **pfargs = NULL;
+    CTypeDescrObject *fresult;
+    Py_ssize_t nargs = 0;
+    struct CPyExtFunc_s *xfunc;
+    int i, type_index = _CFFI_GETARG(g->type_op);
+    _cffi_opcode_t *opcodes = lib->l_types_builder->ctx.types;
+    static const char *const format = ";\n\nCFFI C function from %s.lib";
+    const char *libname = PyText_AS_UTF8(lib->l_libname);
+    struct funcbuilder_s funcbuilder;
+
+    /* return type: */
+    fresult = realize_c_func_return_type(lib->l_types_builder, opcodes,
+                                       type_index);
+    if (fresult == NULL)
+        goto error;
+
+    /* argument types: */
+    /* note that if the arguments are already built, they have a
+       pointer in the 'opcodes' array, and GETOP() returns a
+       random even value.  But OP_FUNCTION_END is odd, so the
+       condition below still works correctly. */
+    i = type_index + 1;
+    while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END)
+        i++;
+    pfargs = alloca(sizeof(CTypeDescrObject *) * (i - type_index - 1));
+    i = type_index + 1;
+    while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) {
+        CTypeDescrObject *ct = realize_c_type(lib->l_types_builder, opcodes, i);
+        if (ct == NULL)
+            goto error;
+        pfargs[nargs++] = ct;
+        i++;
+    }
+
+    memset(&funcbuilder, 0, sizeof(funcbuilder));
+    if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0)
+        goto error;
+
+    /* The few bytes of memory we allocate here appear to leak, but
+       this is not a real leak.  Indeed, CPython never unloads its C
+       extension modules.  There is only one PyMem_Malloc() per real
+       C function in a CFFI C extension module.  That means that this
+       PyMem_Malloc() could also have been written with a static
+       global variable generated for each CPYTHON_BLTN defined in the
+       C extension, and the effect would be the same (but a bit more
+       complicated).
+    */
+    xfunc = PyMem_Malloc(sizeof(struct CPyExtFunc_s) +
+                         funcbuilder.nb_bytes +
+                         strlen(format) + strlen(libname));
+    if (xfunc == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+    memset((char *)xfunc, 0, sizeof(struct CPyExtFunc_s));
+    assert(g->address);
+    xfunc->md.ml_meth = (PyCFunction)g->address;
+    xfunc->md.ml_flags = flags;
+    xfunc->md.ml_name = g->name;
+    xfunc->md.ml_doc = xfunc->doc;
+    xfunc->direct_fn = g->size_or_direct_fn;
+    xfunc->type_index = type_index;
+
+    /* build the docstring */
+    funcbuilder.bufferp = xfunc->doc;
+    if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0)
+        goto error;
+    sprintf(funcbuilder.bufferp - 1, format, libname);
+    /* done building the docstring */
+
+    result = PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname);
+    /* fall-through */
+ error:
+    Py_XDECREF(fresult);
+    while (nargs > 0) {
+        --nargs;
+        Py_DECREF(pfargs[nargs]);
+    }
+    return result;
+}
+
+static PyObject *lib_build_and_cache_attr(LibObject *lib, PyObject *name,
+                                          int recursion)
+{
+    /* does not return a new reference! */
+    PyObject *x;
+    int index;
+    const struct _cffi_global_s *g;
+    CTypeDescrObject *ct;
+    builder_c_t *types_builder = lib->l_types_builder;
+    const char *s = PyText_AsUTF8(name);
+    if (s == NULL)
+        return NULL;
+
+    index = search_in_globals(&types_builder->ctx, s, strlen(s));
+    if (index < 0) {
+
+        if (types_builder->included_libs != NULL) {
+            Py_ssize_t i;
+            PyObject *included_ffis = types_builder->included_ffis;
+            PyObject *included_libs = types_builder->included_libs;
+
+            if (recursion > 100) {
+                PyErr_SetString(PyExc_RuntimeError,
+                    "recursion overflow in ffi.include() delegations");
+                return NULL;
+            }
+
+            for (i = 0; i < PyTuple_GET_SIZE(included_libs); i++) {
+                LibObject *lib1;
+
+                lib1 = (LibObject *)PyTuple_GET_ITEM(included_libs, i);
+                if (lib1 != NULL) {
+                    x = PyDict_GetItem(lib1->l_dict, name);
+                    if (x != NULL) {
+                        Py_INCREF(x);
+                        goto found;
+                    }
+                    x = lib_build_and_cache_attr(lib1, name, recursion + 1);
+                    if (x != NULL) {
+                        Py_INCREF(x);
+                        goto found;
+                    }
+                }
+                else {
+                    FFIObject *ffi1;
+
+                    ffi1 = (FFIObject *)PyTuple_GetItem(included_ffis, i);
+                    if (ffi1 == NULL)
+                        return NULL;
+                    x = ffi_fetch_int_constant(ffi1, s, recursion + 1);
+                    if (x != NULL)
+                        goto found;
+                }
+                if (PyErr_Occurred())
+                    return NULL;
+            }
+        }
+
+        if (recursion > 0)
+            return NULL;  /* no error set, continue looking elsewhere */
+
+        PyErr_Format(PyExc_AttributeError,
+                     "cffi library '%.200s' has no function, constant "
+                     "or global variable named '%.200s'",
+                     PyText_AS_UTF8(lib->l_libname), s);
+        return NULL;
+    }
+
+    g = &types_builder->ctx.globals[index];
+
+    switch (_CFFI_GETOP(g->type_op)) {
+
+    case _CFFI_OP_CPYTHON_BLTN_V:
+        x = lib_build_cpython_func(lib, g, s, METH_VARARGS);
+        break;
+
+    case _CFFI_OP_CPYTHON_BLTN_N:
+        x = lib_build_cpython_func(lib, g, s, METH_NOARGS);
+        break;
+
+    case _CFFI_OP_CPYTHON_BLTN_O:
+        x = lib_build_cpython_func(lib, g, s, METH_O);
+        break;
+
+    case _CFFI_OP_CONSTANT_INT:
+    case _CFFI_OP_ENUM:
+    {
+        /* a constant integer whose value, in an "unsigned long long",
+           is obtained by calling the function at g->address */
+        x = realize_global_int(types_builder, index);
+        break;
+    }
+
+    case _CFFI_OP_CONSTANT:
+    case _CFFI_OP_DLOPEN_CONST:
+    {
+        /* a constant which is not of integer type */
+        char *data;
+        ct = realize_c_type(types_builder, types_builder->ctx.types,
+                            _CFFI_GETARG(g->type_op));
+        if (ct == NULL)
+            return NULL;
+
+        if (ct->ct_size <= 0) {
+            PyErr_Format(FFIError, "constant '%s' is of type '%s', "
+                         "whose size is not known", s, ct->ct_name);
+            return NULL;
+        }
+        if (g->address == NULL) {
+            /* for dlopen() style */
+            assert(_CFFI_GETOP(g->type_op) == _CFFI_OP_DLOPEN_CONST);
+            data = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s);
+            if (data == NULL)
+                return NULL;
+        }
+        else {
+            /* The few bytes of memory we allocate here appear to leak, but
+               this is not a real leak.  Indeed, CPython never unloads its C
+               extension modules.  There is only one PyMem_Malloc() per real
+               non-integer C constant in a CFFI C extension module.  That
+               means that this PyMem_Malloc() could also have been written
+               with a static global variable generated for each OP_CONSTANT
+               defined in the C extension, and the effect would be the same
+               (but a bit more complicated).
+
+               Note that we used to do alloca(), but see issue #198.  We
+               could still do alloca(), or explicit PyMem_Free(), in some
+               cases; but there is no point and it only makes the remaining
+               less-common cases more suspicious.
+            */
+            assert(_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT);
+            data = PyMem_Malloc(ct->ct_size);
+            if (data == NULL) {
+                PyErr_NoMemory();
+                return NULL;
+            }
+            ((void(*)(char*))g->address)(data);
+        }
+        x = convert_to_object(data, ct);
+        Py_DECREF(ct);
+        break;
+    }
+
+    case _CFFI_OP_GLOBAL_VAR:
+    {
+        /* global variable of the exact type specified here
+           (nowadays, only used by the ABI mode or backward
+           compatibility; see _CFFI_OP_GLOBAL_VAR_F for the API mode)
+         */
+        Py_ssize_t g_size = (Py_ssize_t)g->size_or_direct_fn;
+        ct = realize_c_type(types_builder, types_builder->ctx.types,
+                            _CFFI_GETARG(g->type_op));
+        if (ct == NULL)
+            return NULL;
+        if (g_size != ct->ct_size && g_size != 0 && ct->ct_size > 0) {
+            PyErr_Format(FFIError,
+                         "global variable '%.200s' should be %zd bytes "
+                         "according to the cdef, but is actually %zd",
+                         s, ct->ct_size, g_size);
+            x = NULL;
+        }
+        else {
+            void *address = g->address;
+            if (address == NULL) {
+                /* for dlopen() style */
+                address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s);
+                if (address == NULL)
+                    return NULL;
+            }
+            x = make_global_var(name, ct, address, NULL);
+        }
+        Py_DECREF(ct);
+        break;
+    }
+
+    case _CFFI_OP_GLOBAL_VAR_F:
+        ct = realize_c_type(types_builder, types_builder->ctx.types,
+                            _CFFI_GETARG(g->type_op));
+        if (ct == NULL)
+            return NULL;
+        x = make_global_var(name, ct, NULL, (gs_fetch_addr_fn)g->address);
+        Py_DECREF(ct);
+        break;
+
+    case _CFFI_OP_DLOPEN_FUNC:
+    {
+        /* For dlopen(): the function of the given 'name'.  We use
+           dlsym() to get the address of something in the dynamic
+           library, which we interpret as being exactly a function of
+           the specified type.
+        */
+        PyObject *ct1;
+        void *address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s);
+        if (address == NULL)
+            return NULL;
+
+        ct1 = realize_c_type_or_func(types_builder,
+                                     types_builder->ctx.types,
+                                     _CFFI_GETARG(g->type_op));
+        if (ct1 == NULL)
+            return NULL;
+
+        assert(!CTypeDescr_Check(ct1));   /* must be a function */
+        x = new_simple_cdata(address, unwrap_fn_as_fnptr(ct1));
+
+        Py_DECREF(ct1);
+        break;
+    }
+
+    case _CFFI_OP_EXTERN_PYTHON:
+        /* for reading 'lib.bar' where bar is declared with extern "Python" */
+        ct = realize_c_type(types_builder, types_builder->ctx.types,
+                            _CFFI_GETARG(g->type_op));
+        if (ct == NULL)
+            return NULL;
+        x = convert_to_object((char *)&g->size_or_direct_fn, ct);
+        Py_DECREF(ct);
+        break;
+
+    default:
+        PyErr_Format(PyExc_NotImplementedError, "in lib_build_attr: op=%d",
+                     (int)_CFFI_GETOP(g->type_op));
+        return NULL;
+    }
+
+ found:
+    if (x != NULL) {
+        int err = PyDict_SetItem(lib->l_dict, name, x);
+        Py_DECREF(x);
+        if (err < 0)     /* else there is still one ref left in the dict */
+            return NULL;
+    }
+    return x;
+}
+
+#define LIB_GET_OR_CACHE_ADDR(x, lib, name, error)      \
+    do {                                                \
+        x = PyDict_GetItem(lib->l_dict, name);          \
+        if (x == NULL) {                                \
+            x = lib_build_and_cache_attr(lib, name, 0); \
+            if (x == NULL) {                            \
+                error;                                  \
+            }                                           \
+        }                                               \
+    } while (0)
+
+static PyObject *_lib_dir1(LibObject *lib, int ignore_global_vars)
+{
+    const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals;
+    int i, count = 0, total = lib->l_types_builder->ctx.num_globals;
+    PyObject *s, *lst = PyList_New(total);
+    if (lst == NULL)
+        return NULL;
+
+    for (i = 0; i < total; i++) {
+        if (ignore_global_vars) {
+            int op = _CFFI_GETOP(g[i].type_op);
+            if (op == _CFFI_OP_GLOBAL_VAR || op == _CFFI_OP_GLOBAL_VAR_F)
+                continue;
+        }
+        s = PyText_FromString(g[i].name);
+        if (s == NULL)
+            goto error;
+        PyList_SET_ITEM(lst, count, s);
+        count++;
+    }
+    if (PyList_SetSlice(lst, count, total, NULL) < 0)
+        goto error;
+    return lst;
+
+ error:
+    Py_DECREF(lst);
+    return NULL;
+}
+
+static PyObject *_lib_dict(LibObject *lib)
+{
+    const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals;
+    int i, total = lib->l_types_builder->ctx.num_globals;
+    PyObject *name, *x, *d = PyDict_New();
+    if (d == NULL)
+        return NULL;
+
+    for (i = 0; i < total; i++) {
+        name = PyText_FromString(g[i].name);
+        if (name == NULL)
+            goto error;
+
+        LIB_GET_OR_CACHE_ADDR(x, lib, name, goto error);
+
+        if (PyDict_SetItem(d, name, x) < 0)
+            goto error;
+        Py_DECREF(name);
+    }
+    return d;
+
+ error:
+    Py_XDECREF(name);
+    Py_DECREF(d);
+    return NULL;
+}
+
+static PyObject *lib_getattr(LibObject *lib, PyObject *name)
+{
+    const char *p;
+    PyObject *x;
+    LIB_GET_OR_CACHE_ADDR(x, lib, name, goto missing);
+
+    if (GlobSupport_Check(x)) {
+        return read_global_var((GlobSupportObject *)x);
+    }
+    Py_INCREF(x);
+    return x;
+
+ missing:
+    /*** ATTRIBUTEERROR IS SET HERE ***/
+    p = PyText_AsUTF8(name);
+    if (p == NULL)
+        return NULL;
+    if (strcmp(p, "__all__") == 0) {
+        PyErr_Clear();
+        return _lib_dir1(lib, 1);
+    }
+    if (strcmp(p, "__dict__") == 0) {
+        PyErr_Clear();
+        return _lib_dict(lib);
+    }
+    if (strcmp(p, "__class__") == 0) {
+        PyErr_Clear();
+        x = (PyObject *)&PyModule_Type;
+        /* ^^^ used to be Py_TYPE(lib).  But HAAAAAACK!  That makes
+           help() behave correctly.  I couldn't find a more reasonable
+           way.  Urgh. */
+        Py_INCREF(x);
+        return x;
+    }
+    /* this hack is for Python 3.5, and also to give a more 
+       module-like behavior */
+    if (strcmp(p, "__name__") == 0) {
+        PyErr_Clear();
+        return PyText_FromFormat("%s.lib", PyText_AS_UTF8(lib->l_libname));
+    }
+#if PY_MAJOR_VERSION >= 3
+    if (strcmp(p, "__loader__") == 0 || strcmp(p, "__spec__") == 0) {
+        /* some more module-like behavior hacks */
+        PyErr_Clear();
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+#endif
+    return NULL;
+}
+
+static int lib_setattr(LibObject *lib, PyObject *name, PyObject *val)
+{
+    PyObject *x;
+    LIB_GET_OR_CACHE_ADDR(x, lib, name, return -1);
+
+    if (val == NULL) {
+        PyErr_SetString(PyExc_AttributeError, "C attribute cannot be deleted");
+        return -1;
+    }
+
+    if (GlobSupport_Check(x)) {
+        return write_global_var((GlobSupportObject *)x, val);
+    }
+
+    PyErr_Format(PyExc_AttributeError,
+                 "cannot write to function or constant '%.200s'",
+                 PyText_Check(name) ? PyText_AS_UTF8(name) : "?");
+    return -1;
+}
+
+static PyObject *lib_dir(PyObject *self, PyObject *noarg)
+{
+    return _lib_dir1((LibObject *)self, 0);
+}
+
+static PyMethodDef lib_methods[] = {
+    {"__dir__",   lib_dir,  METH_NOARGS},
+    {NULL,        NULL}           /* sentinel */
+};
+
+static PyTypeObject Lib_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "CompiledLib",
+    sizeof(LibObject),
+    0,
+    (destructor)lib_dealloc,                    /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    (reprfunc)lib_repr,                         /* tp_repr */
+    0,                                          /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    0,                                          /* tp_as_mapping */
+    0,                                          /* tp_hash */
+    0,                                          /* tp_call */
+    0,                                          /* tp_str */
+    (getattrofunc)lib_getattr,                  /* tp_getattro */
+    (setattrofunc)lib_setattr,                  /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,    /* tp_flags */
+    0,                                          /* tp_doc */
+    (traverseproc)lib_traverse,                 /* tp_traverse */
+    0,                                          /* tp_clear */
+    0,                                          /* tp_richcompare */
+    0,                                          /* tp_weaklistoffset */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    lib_methods,                                /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    0,                                          /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    offsetof(LibObject, l_dict),                /* tp_dictoffset */
+};
+
+static LibObject *lib_internal_new(FFIObject *ffi, const char *module_name,
+                                   void *dlopen_libhandle)
+{
+    LibObject *lib;
+    PyObject *libname, *dict;
+
+    libname = PyText_FromString(module_name);
+    if (libname == NULL)
+        goto err1;
+
+    dict = PyDict_New();
+    if (dict == NULL)
+        goto err2;
+
+    lib = (LibObject *)PyType_GenericAlloc(&Lib_Type, 0);
+    if (lib == NULL)
+        goto err3;
+
+    lib->l_types_builder = &ffi->types_builder;
+    lib->l_dict = dict;
+    lib->l_libname = libname;
+    Py_INCREF(ffi);
+    lib->l_ffi = ffi;
+    lib->l_libhandle = dlopen_libhandle;
+    return lib;
+
+ err3:
+    Py_DECREF(dict);
+ err2:
+    Py_DECREF(libname);
+ err1:
+    cdlopen_close_ignore_errors(dlopen_libhandle);
+    return NULL;
+}
+
+static PyObject *address_of_global_var(PyObject *args)
+{
+    LibObject *lib;
+    PyObject *x, *o_varname;
+    char *varname;
+
+    if (!PyArg_ParseTuple(args, "O!s", &Lib_Type, &lib, &varname))
+        return NULL;
+
+    /* rebuild a string from 'varname', to do typechecks and to force
+       a unicode back to a plain string (on python 2) */
+    o_varname = PyText_FromString(varname);
+    if (o_varname == NULL)
+        return NULL;
+
+    LIB_GET_OR_CACHE_ADDR(x, lib, o_varname, goto error);
+    Py_DECREF(o_varname);
+    if (GlobSupport_Check(x)) {
+        return cg_addressof_global_var((GlobSupportObject *)x);
+    }
+    else {
+        struct CPyExtFunc_s *exf = _cpyextfunc_get(x);
+        if (exf != NULL) {  /* an OP_CPYTHON_BLTN: '&func' returns a cdata */
+            PyObject *ct;
+            if (exf->direct_fn == NULL) {
+                Py_INCREF(x);    /* backward compatibility */
+                return x;
+            }
+            ct = _cpyextfunc_type(lib, exf);
+            if (ct == NULL)
+                return NULL;
+            x = new_simple_cdata(exf->direct_fn, (CTypeDescrObject *)ct);
+            Py_DECREF(ct);
+            return x;
+        }
+        if (CData_Check(x) &&  /* a constant functionptr cdata: 'f == &f' */
+                (((CDataObject *)x)->c_type->ct_flags & CT_FUNCTIONPTR) != 0) {
+            Py_INCREF(x);
+            return x;
+        }
+        else {
+            PyErr_Format(PyExc_AttributeError,
+                         "cannot take the address of the constant '%.200s'",
+                         varname);
+            return NULL;
+        }
+    }
+
+ error:
+    Py_DECREF(o_varname);
+    return NULL;
+}
diff --git a/c/libffi_msvc/LICENSE b/c/libffi_msvc/LICENSE
new file mode 100644
index 0000000..f591795
--- /dev/null
+++ b/c/libffi_msvc/LICENSE
@@ -0,0 +1,20 @@
+libffi - Copyright (c) 1996-2003  Red Hat, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+``Software''), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/c/libffi_msvc/README b/c/libffi_msvc/README
new file mode 100644
index 0000000..97a12cf
--- /dev/null
+++ b/c/libffi_msvc/README
@@ -0,0 +1,502 @@
+This directory contains the libffi package, which is not part of GCC but
+shipped with GCC as convenience.
+
+Copied without changes from CPython 2.7 head (e04e1f253ed8).
+
+Status
+======
+
+libffi-2.00 has not been released yet! This is a development snapshot!
+
+libffi-1.20 was released on October 5, 1998. Check the libffi web
+page for updates: <URL:http://sources.redhat.com/libffi/>.
+
+
+What is libffi?
+===============
+
+Compilers for high level languages generate code that follow certain
+conventions. These conventions are necessary, in part, for separate
+compilation to work. One such convention is the "calling
+convention". The "calling convention" is essentially a set of
+assumptions made by the compiler about where function arguments will
+be found on entry to a function. A "calling convention" also specifies
+where the return value for a function is found.
+
+Some programs may not know at the time of compilation what arguments
+are to be passed to a function. For instance, an interpreter may be
+told at run-time about the number and types of arguments used to call
+a given function. Libffi can be used in such programs to provide a
+bridge from the interpreter program to compiled code.
+
+The libffi library provides a portable, high level programming
+interface to various calling conventions. This allows a programmer to
+call any function specified by a call interface description at run
+time.  
+
+Ffi stands for Foreign Function Interface. A foreign function
+interface is the popular name for the interface that allows code
+written in one language to call code written in another language. The
+libffi library really only provides the lowest, machine dependent
+layer of a fully featured foreign function interface. A layer must
+exist above libffi that handles type conversions for values passed
+between the two languages.
+
+
+Supported Platforms and Prerequisites
+=====================================
+
+Libffi has been ported to:
+
+	SunOS 4.1.3 & Solaris 2.x (SPARC-V8, SPARC-V9)
+
+	Irix 5.3 & 6.2 (System V/o32 & n32)
+
+	Intel x86 - Linux (System V ABI)
+
+	Alpha - Linux and OSF/1
+
+	m68k - Linux (System V ABI)
+
+	PowerPC - Linux (System V ABI, Darwin, AIX)
+
+	ARM - Linux (System V ABI)
+
+Libffi has been tested with the egcs 1.0.2 gcc compiler. Chances are
+that other versions will work.  Libffi has also been built and tested
+with the SGI compiler tools.
+
+On PowerPC, the tests failed (see the note below).
+
+You must use GNU make to build libffi. SGI's make will not work.
+Sun's probably won't either.
+	
+If you port libffi to another platform, please let me know! I assume
+that some will be easy (x86 NetBSD), and others will be more difficult
+(HP).
+
+
+Installing libffi
+=================
+
+[Note: before actually performing any of these installation steps,
+ you may wish to read the "Platform Specific Notes" below.]
+
+First you must configure the distribution for your particular
+system. Go to the directory you wish to build libffi in and run the
+"configure" program found in the root directory of the libffi source
+distribution.
+
+You may want to tell configure where to install the libffi library and
+header files. To do that, use the --prefix configure switch.  Libffi
+will install under /usr/local by default. 
+
+If you want to enable extra run-time debugging checks use the the
+--enable-debug configure switch. This is useful when your program dies
+mysteriously while using libffi. 
+
+Another useful configure switch is --enable-purify-safety. Using this
+will add some extra code which will suppress certain warnings when you
+are using Purify with libffi. Only use this switch when using 
+Purify, as it will slow down the library.
+
+Configure has many other options. Use "configure --help" to see them all.
+
+Once configure has finished, type "make". Note that you must be using
+GNU make. SGI's make will not work.  Sun's probably won't either.
+You can ftp GNU make from prep.ai.mit.edu:/pub/gnu.
+
+To ensure that libffi is working as advertised, type "make test".
+
+To install the library and header files, type "make install".
+
+
+Using libffi
+============
+
+	The Basics
+	----------
+
+Libffi assumes that you have a pointer to the function you wish to
+call and that you know the number and types of arguments to pass it,
+as well as the return type of the function.
+
+The first thing you must do is create an ffi_cif object that matches
+the signature of the function you wish to call. The cif in ffi_cif
+stands for Call InterFace. To prepare a call interface object, use the
+following function:
+
+ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi,
+			unsigned int nargs, 
+			ffi_type *rtype, ffi_type **atypes);
+
+	CIF is a pointer to the call interface object you wish
+		to initialize.
+
+	ABI is an enum that specifies the calling convention 
+		to use for the call. FFI_DEFAULT_ABI defaults
+		to the system's native calling convention. Other
+		ABI's may be used with care. They are system
+		specific.
+
+	NARGS is the number of arguments this function accepts.	
+		libffi does not yet support vararg functions.
+
+	RTYPE is a pointer to an ffi_type structure that represents
+		the return type of the function. Ffi_type objects
+		describe the types of values. libffi provides
+		ffi_type objects for many of the native C types:
+		signed int, unsigned int, signed char, unsigned char,
+		etc. There is also a pointer ffi_type object and
+		a void ffi_type. Use &ffi_type_void for functions that 
+		don't return values.
+
+	ATYPES is a vector of ffi_type pointers. ARGS must be NARGS long.
+		If NARGS is 0, this is ignored.
+
+
+ffi_prep_cif will return a status code that you are responsible 
+for checking. It will be one of the following:
+
+	FFI_OK - All is good.
+
+	FFI_BAD_TYPEDEF - One of the ffi_type objects that ffi_prep_cif
+		came across is bad.
+
+
+Before making the call, the VALUES vector should be initialized 
+with pointers to the appropriate argument values.
+
+To call the the function using the initialized ffi_cif, use the
+ffi_call function:
+
+void ffi_call(ffi_cif *cif, void *fn, void *rvalue, void **avalues);
+
+	CIF is a pointer to the ffi_cif initialized specifically
+		for this function.
+
+	FN is a pointer to the function you want to call.
+
+	RVALUE is a pointer to a chunk of memory that is to hold the
+		result of the function call. Currently, it must be
+		at least one word in size (except for the n32 version
+		under Irix 6.x, which must be a pointer to an 8 byte 
+		aligned value (a long long). It must also be at least 
+		word aligned (depending on the return type, and the
+		system's alignment requirements). If RTYPE is 
+		&ffi_type_void, this is ignored. If RVALUE is NULL, 
+		the return value is discarded.
+
+	AVALUES is a vector of void* that point to the memory locations
+		holding the argument values for a call.
+		If NARGS is 0, this is ignored.
+
+
+If you are expecting a return value from FN it will have been stored
+at RVALUE.
+
+
+
+	An Example
+	----------
+
+Here is a trivial example that calls puts() a few times.
+
+    #include <stdio.h>
+    #include <ffi.h>
+    
+    int main()
+    {
+      ffi_cif cif;
+      ffi_type *args[1];
+      void *values[1];
+      char *s;
+      int rc;
+      
+      /* Initialize the argument info vectors */    
+      args[0] = &ffi_type_uint;
+      values[0] = &s;
+      
+      /* Initialize the cif */
+      if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, 
+    		       &ffi_type_uint, args) == FFI_OK)
+        {
+          s = "Hello World!";
+          ffi_call(&cif, puts, &rc, values);
+          /* rc now holds the result of the call to puts */
+          
+          /* values holds a pointer to the function's arg, so to 
+	     call puts() again all we need to do is change the 
+             value of s */
+          s = "This is cool!";
+          ffi_call(&cif, puts, &rc, values);
+        }
+      
+      return 0;
+    }
+
+
+
+	Aggregate Types
+	---------------
+
+Although libffi has no special support for unions or bit-fields, it is
+perfectly happy passing structures back and forth. You must first
+describe the structure to libffi by creating a new ffi_type object
+for it. Here is the definition of ffi_type:
+
+    typedef struct _ffi_type
+    {
+      unsigned size;
+      short alignment;
+      short type;
+      struct _ffi_type **elements;
+    } ffi_type;
+    
+All structures must have type set to FFI_TYPE_STRUCT.  You may set
+size and alignment to 0. These will be calculated and reset to the
+appropriate values by ffi_prep_cif().
+
+elements is a NULL terminated array of pointers to ffi_type objects
+that describe the type of the structure elements. These may, in turn,
+be structure elements.
+
+The following example initializes a ffi_type object representing the
+tm struct from Linux's time.h:
+
+				    struct tm {
+					int tm_sec;
+					int tm_min;
+					int tm_hour;
+					int tm_mday;
+					int tm_mon;
+					int tm_year;
+					int tm_wday;
+					int tm_yday;
+					int tm_isdst;
+					/* Those are for future use. */
+					long int __tm_gmtoff__;
+					__const char *__tm_zone__;
+				    };
+
+    {
+      ffi_type tm_type;
+      ffi_type *tm_type_elements[12];
+      int i;
+
+      tm_type.size = tm_type.alignment = 0;
+      tm_type.elements = &tm_type_elements;
+    
+      for (i = 0; i < 9; i++)
+          tm_type_elements[i] = &ffi_type_sint;
+
+      tm_type_elements[9] = &ffi_type_slong;
+      tm_type_elements[10] = &ffi_type_pointer;
+      tm_type_elements[11] = NULL;
+
+      /* tm_type can now be used to represent tm argument types and
+	 return types for ffi_prep_cif() */
+    }
+
+
+
+Platform Specific Notes
+=======================
+
+	Intel x86
+	---------
+
+There are no known problems with the x86 port.
+
+	Sun SPARC - SunOS 4.1.3 & Solaris 2.x
+	-------------------------------------
+
+You must use GNU Make to build libffi on Sun platforms.
+
+	MIPS - Irix 5.3 & 6.x
+	---------------------
+
+Irix 6.2 and better supports three different calling conventions: o32,
+n32 and n64. Currently, libffi only supports both o32 and n32 under
+Irix 6.x, but only o32 under Irix 5.3. Libffi will automatically be
+configured for whichever calling convention it was built for.
+
+By default, the configure script will try to build libffi with the GNU
+development tools. To build libffi with the SGI development tools, set
+the environment variable CC to either "cc -32" or "cc -n32" before
+running configure under Irix 6.x (depending on whether you want an o32
+or n32 library), or just "cc" for Irix 5.3.
+
+With the n32 calling convention, when returning structures smaller
+than 16 bytes, be sure to provide an RVALUE that is 8 byte aligned.
+Here's one way of forcing this:
+
+	double struct_storage[2];
+	my_small_struct *s = (my_small_struct *) struct_storage;  
+	/* Use s for RVALUE */
+
+If you don't do this you are liable to get spurious bus errors. 
+
+"long long" values are not supported yet.
+
+You must use GNU Make to build libffi on SGI platforms.
+
+	ARM - System V ABI
+	------------------
+
+The ARM port was performed on a NetWinder running ARM Linux ELF
+(2.0.31) and gcc 2.8.1.
+
+
+
+	PowerPC System V ABI
+	--------------------
+
+There are two `System V ABI's which libffi implements for PowerPC.
+They differ only in how small structures are returned from functions.
+
+In the FFI_SYSV version, structures that are 8 bytes or smaller are
+returned in registers.  This is what GCC does when it is configured
+for solaris, and is what the System V ABI I have (dated September
+1995) says.
+
+In the FFI_GCC_SYSV version, all structures are returned the same way:
+by passing a pointer as the first argument to the function.  This is
+what GCC does when it is configured for linux or a generic sysv
+target.
+
+EGCS 1.0.1 (and probably other versions of EGCS/GCC) also has a
+inconsistency with the SysV ABI: When a procedure is called with many
+floating-point arguments, some of them get put on the stack.  They are
+all supposed to be stored in double-precision format, even if they are
+only single-precision, but EGCS stores single-precision arguments as
+single-precision anyway.  This causes one test to fail (the `many
+arguments' test).
+
+
+What's With The Crazy Comments?
+===============================
+
+You might notice a number of cryptic comments in the code, delimited
+by /*@ and @*/. These are annotations read by the program LCLint, a
+tool for statically checking C programs. You can read all about it at
+<http://larch-www.lcs.mit.edu:8001/larch/lclint/index.html>.
+
+
+History
+=======
+
+1.20 Oct-5-98
+	Raffaele Sena produces ARM port.
+
+1.19 Oct-5-98
+	Fixed x86 long double and long long return support.
+	m68k bug fixes from Andreas Schwab.
+	Patch for DU assembler compatibility for the Alpha from Richard
+	Henderson.
+
+1.18 Apr-17-98
+	Bug fixes and MIPS configuration changes.
+
+1.17 Feb-24-98
+	Bug fixes and m68k port from Andreas Schwab. PowerPC port from
+	Geoffrey Keating. Various bug x86, Sparc and MIPS bug fixes.
+
+1.16 Feb-11-98
+	Richard Henderson produces Alpha port.
+
+1.15 Dec-4-97
+	Fixed an n32 ABI bug. New libtool, auto* support.
+
+1.14 May-13-97
+	libtool is now used to generate shared and static libraries.
+	Fixed a minor portability problem reported by Russ McManus
+	<mcmanr@eq.gs.com>.
+
+1.13 Dec-2-96
+	Added --enable-purify-safety to keep Purify from complaining
+	about certain low level code.
+	Sparc fix for calling functions with < 6 args.
+	Linux x86 a.out fix.
+
+1.12 Nov-22-96
+	Added missing ffi_type_void, needed for supporting void return 
+	types. Fixed test case for non MIPS machines. Cygnus Support 
+	is now Cygnus Solutions. 
+
+1.11 Oct-30-96
+	Added notes about GNU make.
+
+1.10 Oct-29-96
+	Added configuration fix for non GNU compilers.
+
+1.09 Oct-29-96
+	Added --enable-debug configure switch. Clean-ups based on LCLint 
+	feedback. ffi_mips.h is always installed. Many configuration 
+	fixes. Fixed ffitest.c for sparc builds.
+
+1.08 Oct-15-96
+	Fixed n32 problem. Many clean-ups.
+
+1.07 Oct-14-96
+	Gordon Irlam rewrites v8.S again. Bug fixes.
+
+1.06 Oct-14-96
+	Gordon Irlam improved the sparc port. 
+
+1.05 Oct-14-96
+	Interface changes based on feedback.
+
+1.04 Oct-11-96
+	Sparc port complete (modulo struct passing bug).
+
+1.03 Oct-10-96
+	Passing struct args, and returning struct values works for
+	all architectures/calling conventions. Expanded tests.
+
+1.02 Oct-9-96
+	Added SGI n32 support. Fixed bugs in both o32 and Linux support.
+	Added "make test".
+
+1.01 Oct-8-96
+	Fixed float passing bug in mips version. Restructured some
+	of the code. Builds cleanly with SGI tools.
+
+1.00 Oct-7-96
+	First release. No public announcement.
+
+
+Authors & Credits
+=================
+
+libffi was written by Anthony Green <green@cygnus.com>.
+
+Portions of libffi were derived from Gianni Mariani's free gencall
+library for Silicon Graphics machines.
+
+The closure mechanism was designed and implemented by Kresten Krab
+Thorup.
+
+The Sparc port was derived from code contributed by the fine folks at
+Visible Decisions Inc <http://www.vdi.com>. Further enhancements were
+made by Gordon Irlam at Cygnus Solutions <http://www.cygnus.com>.
+
+The Alpha port was written by Richard Henderson at Cygnus Solutions.
+
+Andreas Schwab ported libffi to m68k Linux and provided a number of
+bug fixes.
+
+Geoffrey Keating ported libffi to the PowerPC.
+
+Raffaele Sena ported libffi to the ARM.
+
+Jesper Skov and Andrew Haley both did more than their fair share of
+stepping through the code and tracking down bugs.
+
+Thanks also to Tom Tromey for bug fixes and configuration help.
+
+Thanks to Jim Blandy, who provided some useful feedback on the libffi
+interface.
+
+If you have a problem, or have found a bug, please send a note to
+green@cygnus.com.
diff --git a/c/libffi_msvc/README.ctypes b/c/libffi_msvc/README.ctypes
new file mode 100644
index 0000000..17e8a40
--- /dev/null
+++ b/c/libffi_msvc/README.ctypes
@@ -0,0 +1,7 @@
+The purpose is to hack the libffi sources so that they can be compiled
+with MSVC, and to extend them so that they have the features I need
+for ctypes.
+
+I retrieved the libffi sources from the gcc cvs repository on
+2004-01-27.  Then I did 'configure' in a 'build' subdirectory on a x86
+linux system, and copied the files I found useful.
diff --git a/c/libffi_msvc/ffi.c b/c/libffi_msvc/ffi.c
new file mode 100644
index 0000000..836f171
--- /dev/null
+++ b/c/libffi_msvc/ffi.c
@@ -0,0 +1,486 @@
+/* -----------------------------------------------------------------------
+   ffi.c - Copyright (c) 1996, 1998, 1999, 2001  Red Hat, Inc.
+           Copyright (c) 2002  Ranjit Mathew
+           Copyright (c) 2002  Bo Thorsen
+           Copyright (c) 2002  Roger Sayle
+   
+   x86 Foreign Function Interface 
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+   ----------------------------------------------------------------------- */
+
+#include <ffi.h>
+#include <ffi_common.h>
+
+#include <stdlib.h>
+
+/* ffi_prep_args is called by the assembly routine once stack space
+   has been allocated for the function's arguments */
+
+extern void Py_FatalError(const char *msg);
+
+/*@-exportheader@*/
+void ffi_prep_args(char *stack, extended_cif *ecif)
+/*@=exportheader@*/
+{
+  register unsigned int i;
+  register void **p_argv;
+  register char *argp;
+  register ffi_type **p_arg;
+
+  argp = stack;
+  if (ecif->cif->flags == FFI_TYPE_STRUCT)
+    {
+      *(void **) argp = ecif->rvalue;
+      argp += sizeof(void *);
+    }
+
+  p_argv = ecif->avalue;
+
+  for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types;
+       i != 0;
+       i--, p_arg++)
+    {
+      size_t z;
+
+      /* Align if necessary */
+      if ((sizeof(void *) - 1) & (size_t) argp)
+	argp = (char *) ALIGN(argp, sizeof(void *));
+
+      z = (*p_arg)->size;
+      if (z < sizeof(int))
+	{
+	  z = sizeof(int);
+	  switch ((*p_arg)->type)
+	    {
+	    case FFI_TYPE_SINT8:
+	      *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
+	      break;
+
+	    case FFI_TYPE_UINT8:
+	      *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
+	      break;
+
+	    case FFI_TYPE_SINT16:
+	      *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
+	      break;
+
+	    case FFI_TYPE_UINT16:
+	      *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
+	      break;
+
+	    case FFI_TYPE_SINT32:
+	      *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv);
+	      break;
+
+	    case FFI_TYPE_UINT32:
+	      *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
+	      break;
+
+	    case FFI_TYPE_STRUCT:
+	      *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
+	      break;
+
+	    default:
+	      FFI_ASSERT(0);
+	    }
+	}
+#ifdef _WIN64
+      else if (z > 8)
+        {
+          /* On Win64, if a single argument takes more than 8 bytes,
+             then it is always passed by reference. */
+          *(void **)argp = *p_argv;
+          z = 8;
+        }
+#endif
+      else
+	{
+	  memcpy(argp, *p_argv, z);
+	}
+      p_argv++;
+      argp += z;
+    }
+
+  if (argp - stack > (long)ecif->cif->bytes)
+    {
+      Py_FatalError("FFI BUG: not enough stack space for arguments");
+    }
+  return;
+}
+
+/* Perform machine dependent cif processing */
+ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
+{
+  /* Set the return type flag */
+  switch (cif->rtype->type)
+    {
+    case FFI_TYPE_VOID:
+    case FFI_TYPE_SINT64:
+    case FFI_TYPE_FLOAT:
+    case FFI_TYPE_DOUBLE:
+    case FFI_TYPE_LONGDOUBLE:
+      cif->flags = (unsigned) cif->rtype->type;
+      break;
+
+    case FFI_TYPE_STRUCT:
+      /* MSVC returns small structures in registers.  Put in cif->flags
+         the value FFI_TYPE_STRUCT only if the structure is big enough;
+         otherwise, put the 4- or 8-bytes integer type. */
+      if (cif->rtype->size <= 4)
+        cif->flags = FFI_TYPE_INT;
+      else if (cif->rtype->size <= 8)
+        cif->flags = FFI_TYPE_SINT64;
+      else
+        cif->flags = FFI_TYPE_STRUCT;
+      break;
+
+    case FFI_TYPE_UINT64:
+#ifdef _WIN64
+    case FFI_TYPE_POINTER:
+#endif
+      cif->flags = FFI_TYPE_SINT64;
+      break;
+
+    default:
+      cif->flags = FFI_TYPE_INT;
+      break;
+    }
+
+  return FFI_OK;
+}
+
+#ifdef _WIN32
+extern int
+ffi_call_x86(void (*)(char *, extended_cif *), 
+	     /*@out@*/ extended_cif *, 
+	     unsigned, unsigned, 
+	     /*@out@*/ unsigned *, 
+	     void (*fn)());
+#endif
+
+#ifdef _WIN64
+extern int
+ffi_call_AMD64(void (*)(char *, extended_cif *),
+		 /*@out@*/ extended_cif *,
+		 unsigned, unsigned,
+		 /*@out@*/ unsigned *,
+		 void (*fn)());
+#endif
+
+int
+ffi_call(/*@dependent@*/ ffi_cif *cif, 
+	 void (*fn)(), 
+	 /*@out@*/ void *rvalue, 
+	 /*@dependent@*/ void **avalue)
+{
+  extended_cif ecif;
+
+  ecif.cif = cif;
+  ecif.avalue = avalue;
+  
+  /* If the return value is a struct and we don't have a return	*/
+  /* value address then we need to make one		        */
+
+  if ((rvalue == NULL) && 
+      (cif->flags == FFI_TYPE_STRUCT))
+    {
+      /*@-sysunrecog@*/
+      ecif.rvalue = alloca(cif->rtype->size);
+      /*@=sysunrecog@*/
+    }
+  else
+    ecif.rvalue = rvalue;
+    
+  
+  switch (cif->abi) 
+    {
+#if !defined(_WIN64)
+    case FFI_SYSV:
+    case FFI_STDCALL:
+      return ffi_call_x86(ffi_prep_args, &ecif, cif->bytes, 
+			  cif->flags, ecif.rvalue, fn);
+      break;
+#else
+    case FFI_SYSV:
+      /*@-usedef@*/
+      return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes,
+			   cif->flags, ecif.rvalue, fn);
+      /*@=usedef@*/
+      break;
+#endif
+
+    default:
+      FFI_ASSERT(0);
+      break;
+    }
+  return -1; /* theller: Hrm. */
+}
+
+
+/** private members **/
+
+static void ffi_prep_incoming_args_SYSV (char *stack, void **ret,
+					  void** args, ffi_cif* cif);
+/* This function is jumped to by the trampoline */
+
+#ifdef _WIN64
+void *
+#else
+static void __fastcall
+#endif
+ffi_closure_SYSV (ffi_closure *closure, char *argp)
+{
+  // this is our return value storage
+  long double    res;
+
+  // our various things...
+  ffi_cif       *cif;
+  void         **arg_area;
+  unsigned short rtype;
+  void          *resp = (void*)&res;
+  void *args = argp + sizeof(void *);
+
+  cif         = closure->cif;
+  arg_area    = (void**) alloca (cif->nargs * sizeof (void*));  
+
+  /* this call will initialize ARG_AREA, such that each
+   * element in that array points to the corresponding 
+   * value on the stack; and if the function returns
+   * a structure, it will re-set RESP to point to the
+   * structure return address.  */
+
+  ffi_prep_incoming_args_SYSV(args, (void**)&resp, arg_area, cif);
+  
+  (closure->fun) (cif, resp, arg_area, closure->user_data);
+
+  rtype = cif->flags;
+
+#if defined(_WIN32) && !defined(_WIN64)
+#ifdef _MSC_VER
+  /* now, do a generic return based on the value of rtype */
+  if (rtype == FFI_TYPE_INT)
+    {
+	    _asm mov eax, resp ;
+	    _asm mov eax, [eax] ;
+    }
+  else if (rtype == FFI_TYPE_FLOAT)
+    {
+	    _asm mov eax, resp ;
+	    _asm fld DWORD PTR [eax] ;
+//      asm ("flds (%0)" : : "r" (resp) : "st" );
+    }
+  else if (rtype == FFI_TYPE_DOUBLE)
+    {
+	    _asm mov eax, resp ;
+	    _asm fld QWORD PTR [eax] ;
+//      asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" );
+    }
+  else if (rtype == FFI_TYPE_LONGDOUBLE)
+    {
+//      asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" );
+    }
+  else if (rtype == FFI_TYPE_SINT64)
+    {
+	    _asm mov edx, resp ;
+	    _asm mov eax, [edx] ;
+	    _asm mov edx, [edx + 4] ;
+//      asm ("movl 0(%0),%%eax;"
+//	   "movl 4(%0),%%edx" 
+//	   : : "r"(resp)
+//	   : "eax", "edx");
+    }
+#else
+  /* now, do a generic return based on the value of rtype */
+  if (rtype == FFI_TYPE_INT)
+    {
+      asm ("movl (%0),%%eax" : : "r" (resp) : "eax");
+    }
+  else if (rtype == FFI_TYPE_FLOAT)
+    {
+      asm ("flds (%0)" : : "r" (resp) : "st" );
+    }
+  else if (rtype == FFI_TYPE_DOUBLE)
+    {
+      asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" );
+    }
+  else if (rtype == FFI_TYPE_LONGDOUBLE)
+    {
+      asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" );
+    }
+  else if (rtype == FFI_TYPE_SINT64)
+    {
+      asm ("movl 0(%0),%%eax;"
+	   "movl 4(%0),%%edx" 
+	   : : "r"(resp)
+	   : "eax", "edx");
+    }
+#endif
+#endif
+
+#ifdef _WIN64
+  /* The result is returned in rax.  This does the right thing for
+     result types except for floats; we have to 'mov xmm0, rax' in the
+     caller to correct this.
+  */
+  return *(void **)resp;
+#endif
+}
+
+/*@-exportheader@*/
+static void 
+ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
+			    void **avalue, ffi_cif *cif)
+/*@=exportheader@*/
+{
+  register unsigned int i;
+  register void **p_argv;
+  register char *argp;
+  register ffi_type **p_arg;
+
+  argp = stack;
+
+  if ( cif->flags == FFI_TYPE_STRUCT ) {
+    *rvalue = *(void **) argp;
+    argp += 4;
+  }
+
+  p_argv = avalue;
+
+  for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++)
+    {
+      size_t z;
+
+      /* Align if necessary */
+      if ((sizeof(char *) - 1) & (size_t) argp) {
+	argp = (char *) ALIGN(argp, sizeof(char*));
+      }
+
+      z = (*p_arg)->size;
+
+      /* because we're little endian, this is what it turns into.   */
+
+#ifdef _WIN64
+      if (z > 8)
+        {
+          /* On Win64, if a single argument takes more than 8 bytes,
+             then it is always passed by reference. */
+          *p_argv = *((void**) argp);
+          z = 8;
+        }
+      else
+#endif
+      *p_argv = (void*) argp;
+
+      p_argv++;
+      argp += z;
+    }
+  
+  return;
+}
+
+/* the cif must already be prep'ed */
+extern void ffi_closure_OUTER();
+
+ffi_status
+ffi_prep_closure_loc (ffi_closure* closure,
+					  ffi_cif* cif,
+					  void (*fun)(ffi_cif*,void*,void**,void*),
+					  void *user_data,
+					  void *codeloc)
+{
+  short bytes;
+  char *tramp;
+#ifdef _WIN64
+  int mask = 0;
+#endif
+  FFI_ASSERT (cif->abi == FFI_SYSV);
+  
+  if (cif->abi == FFI_SYSV)
+    bytes = 0;
+#if !defined(_WIN64)
+  else if (cif->abi == FFI_STDCALL)
+    bytes = cif->bytes;
+#endif
+  else
+    return FFI_BAD_ABI;
+
+  tramp = &closure->tramp[0];
+
+#define BYTES(text) memcpy(tramp, text, sizeof(text)), tramp += sizeof(text)-1
+#define POINTER(x) *(void**)tramp = (void*)(x), tramp += sizeof(void*)
+#define SHORT(x) *(short*)tramp = x, tramp += sizeof(short)
+#define INT(x) *(int*)tramp = x, tramp += sizeof(int)
+
+#ifdef _WIN64
+  if (cif->nargs >= 1 &&
+      (cif->arg_types[0]->type == FFI_TYPE_FLOAT
+       || cif->arg_types[0]->type == FFI_TYPE_DOUBLE))
+    mask |= 1;
+  if (cif->nargs >= 2 &&
+      (cif->arg_types[1]->type == FFI_TYPE_FLOAT
+       || cif->arg_types[1]->type == FFI_TYPE_DOUBLE))
+    mask |= 2;
+  if (cif->nargs >= 3 &&
+      (cif->arg_types[2]->type == FFI_TYPE_FLOAT
+       || cif->arg_types[2]->type == FFI_TYPE_DOUBLE))
+    mask |= 4;
+  if (cif->nargs >= 4 &&
+      (cif->arg_types[3]->type == FFI_TYPE_FLOAT
+       || cif->arg_types[3]->type == FFI_TYPE_DOUBLE))
+    mask |= 8;
+
+  /* 41 BB ----         mov         r11d,mask */
+  BYTES("\x41\xBB"); INT(mask);
+
+  /* 48 B8 --------     mov         rax, closure			*/
+  BYTES("\x48\xB8"); POINTER(closure);
+
+  /* 49 BA --------     mov         r10, ffi_closure_OUTER */
+  BYTES("\x49\xBA"); POINTER(ffi_closure_OUTER);
+
+  /* 41 FF E2           jmp         r10 */
+  BYTES("\x41\xFF\xE2");
+
+#else
+
+  /* mov ecx, closure */
+  BYTES("\xb9"); POINTER(closure);
+
+  /* mov edx, esp */
+  BYTES("\x8b\xd4");
+
+  /* call ffi_closure_SYSV */
+  BYTES("\xe8"); POINTER((char*)&ffi_closure_SYSV - (tramp + 4));
+
+  /* ret bytes */
+  BYTES("\xc2");
+  SHORT(bytes);
+  
+#endif
+
+  if (tramp - &closure->tramp[0] > FFI_TRAMPOLINE_SIZE)
+    Py_FatalError("FFI_TRAMPOLINE_SIZE too small in " __FILE__);
+
+  closure->cif  = cif;
+  closure->user_data = user_data;
+  closure->fun  = fun;
+  return FFI_OK;
+}
diff --git a/c/libffi_msvc/ffi.h b/c/libffi_msvc/ffi.h
new file mode 100644
index 0000000..97cdb59
--- /dev/null
+++ b/c/libffi_msvc/ffi.h
@@ -0,0 +1,322 @@
+/* -----------------------------------------------------------------*-C-*-
+   libffi 2.00-beta - Copyright (c) 1996-2003  Red Hat, Inc.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+
+   ----------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------
+   The basic API is described in the README file.
+
+   The raw API is designed to bypass some of the argument packing
+   and unpacking on architectures for which it can be avoided.
+
+   The closure API allows interpreted functions to be packaged up
+   inside a C function pointer, so that they can be called as C functions,
+   with no understanding on the client side that they are interpreted.
+   It can also be used in other cases in which it is necessary to package
+   up a user specified parameter and a function pointer as a single
+   function pointer.
+
+   The closure API must be implemented in order to get its functionality,
+   e.g. for use by gij.  Routines are provided to emulate the raw API
+   if the underlying platform doesn't allow faster implementation.
+
+   More details on the raw and cloure API can be found in:
+
+   http://gcc.gnu.org/ml/java/1999-q3/msg00138.html
+
+   and
+
+   http://gcc.gnu.org/ml/java/1999-q3/msg00174.html
+   -------------------------------------------------------------------- */
+
+#ifndef LIBFFI_H
+#define LIBFFI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Specify which architecture libffi is configured for. */
+//XXX #define X86
+
+/* ---- System configuration information --------------------------------- */
+
+#include <ffitarget.h>
+
+#ifndef LIBFFI_ASM
+
+#include <stddef.h>
+#include <limits.h>
+
+/* LONG_LONG_MAX is not always defined (not if STRICT_ANSI, for example).
+   But we can find it either under the correct ANSI name, or under GNU
+   C's internal name.  */
+#ifdef LONG_LONG_MAX
+# define FFI_LONG_LONG_MAX LONG_LONG_MAX
+#else
+# ifdef LLONG_MAX
+#  define FFI_LONG_LONG_MAX LLONG_MAX
+# else
+#  ifdef __GNUC__
+#   define FFI_LONG_LONG_MAX __LONG_LONG_MAX__
+#  endif
+#  ifdef _MSC_VER
+#   define FFI_LONG_LONG_MAX _I64_MAX
+#  endif
+# endif
+#endif
+
+#if SCHAR_MAX == 127
+# define ffi_type_uchar                ffi_type_uint8
+# define ffi_type_schar                ffi_type_sint8
+#else
+ #error "char size not supported"
+#endif
+
+#if SHRT_MAX == 32767
+# define ffi_type_ushort       ffi_type_uint16
+# define ffi_type_sshort       ffi_type_sint16
+#elif SHRT_MAX == 2147483647
+# define ffi_type_ushort       ffi_type_uint32
+# define ffi_type_sshort       ffi_type_sint32
+#else
+ #error "short size not supported"
+#endif
+
+#if INT_MAX == 32767
+# define ffi_type_uint         ffi_type_uint16
+# define ffi_type_sint         ffi_type_sint16
+#elif INT_MAX == 2147483647
+# define ffi_type_uint         ffi_type_uint32
+# define ffi_type_sint         ffi_type_sint32
+#elif INT_MAX == 9223372036854775807
+# define ffi_type_uint         ffi_type_uint64
+# define ffi_type_sint         ffi_type_sint64
+#else
+ #error "int size not supported"
+#endif
+
+#define ffi_type_ulong         ffi_type_uint64
+#define ffi_type_slong         ffi_type_sint64
+#if LONG_MAX == 2147483647
+# if FFI_LONG_LONG_MAX != 9223372036854775807
+  #error "no 64-bit data type supported"
+# endif
+#elif LONG_MAX != 9223372036854775807
+ #error "long size not supported"
+#endif
+
+/* The closure code assumes that this works on pointers, i.e. a size_t	*/
+/* can hold a pointer.							*/
+
+typedef struct _ffi_type
+{
+  size_t size;
+  unsigned short alignment;
+  unsigned short type;
+  /*@null@*/ struct _ffi_type **elements;
+} ffi_type;
+
+/* These are defined in types.c */
+extern ffi_type ffi_type_void;
+extern ffi_type ffi_type_uint8;
+extern ffi_type ffi_type_sint8;
+extern ffi_type ffi_type_uint16;
+extern ffi_type ffi_type_sint16;
+extern ffi_type ffi_type_uint32;
+extern ffi_type ffi_type_sint32;
+extern ffi_type ffi_type_uint64;
+extern ffi_type ffi_type_sint64;
+extern ffi_type ffi_type_float;
+extern ffi_type ffi_type_double;
+extern ffi_type ffi_type_longdouble;
+extern ffi_type ffi_type_pointer;
+
+
+typedef enum {
+  FFI_OK = 0,
+  FFI_BAD_TYPEDEF,
+  FFI_BAD_ABI 
+} ffi_status;
+
+typedef unsigned FFI_TYPE;
+
+typedef struct {
+  ffi_abi abi;
+  unsigned nargs;
+  /*@dependent@*/ ffi_type **arg_types;
+  /*@dependent@*/ ffi_type *rtype;
+  unsigned bytes;
+  unsigned flags;
+#ifdef FFI_EXTRA_CIF_FIELDS
+  FFI_EXTRA_CIF_FIELDS;
+#endif
+} ffi_cif;
+
+/* ---- Definitions for the raw API -------------------------------------- */
+
+#ifdef _WIN64
+#define FFI_SIZEOF_ARG 8
+#else
+#define FFI_SIZEOF_ARG 4
+#endif
+
+typedef union {
+  ffi_sarg  sint;
+  ffi_arg   uint;
+  float	    flt;
+  char      data[FFI_SIZEOF_ARG];
+  void*     ptr;
+} ffi_raw;
+
+void ffi_raw_call (/*@dependent@*/ ffi_cif *cif, 
+		   void (*fn)(), 
+		   /*@out@*/ void *rvalue, 
+		   /*@dependent@*/ ffi_raw *avalue);
+
+void ffi_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw);
+void ffi_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args);
+size_t ffi_raw_size (ffi_cif *cif);
+
+/* This is analogous to the raw API, except it uses Java parameter	*/
+/* packing, even on 64-bit machines.  I.e. on 64-bit machines		*/
+/* longs and doubles are followed by an empty 64-bit word.		*/
+
+void ffi_java_raw_call (/*@dependent@*/ ffi_cif *cif, 
+		        void (*fn)(), 
+		        /*@out@*/ void *rvalue, 
+		        /*@dependent@*/ ffi_raw *avalue);
+
+void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw);
+void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args);
+size_t ffi_java_raw_size (ffi_cif *cif);
+
+/* ---- Definitions for closures ----------------------------------------- */
+
+#if FFI_CLOSURES
+
+typedef struct {
+  char tramp[FFI_TRAMPOLINE_SIZE];
+  ffi_cif   *cif;
+  void     (*fun)(ffi_cif*,void*,void**,void*);
+  void      *user_data;
+} ffi_closure;
+
+void ffi_closure_free(void *);
+void *ffi_closure_alloc (size_t size, void **code);
+
+ffi_status
+ffi_prep_closure_loc (ffi_closure*,
+		  ffi_cif *,
+		  void (*fun)(ffi_cif*,void*,void**,void*),
+		  void *user_data,
+		  void *codeloc);
+
+/* AR: for cffi we need the following API, and not the _loc version */
+#define ffi_prep_closure(a,b,c,d)  ffi_prep_closure_loc(a,b,c,d,a)
+
+typedef struct {
+  char tramp[FFI_TRAMPOLINE_SIZE];
+
+  ffi_cif   *cif;
+
+#if !FFI_NATIVE_RAW_API
+
+  /* if this is enabled, then a raw closure has the same layout 
+     as a regular closure.  We use this to install an intermediate 
+     handler to do the transaltion, void** -> ffi_raw*. */
+
+  void     (*translate_args)(ffi_cif*,void*,void**,void*);
+  void      *this_closure;
+
+#endif
+
+  void     (*fun)(ffi_cif*,void*,ffi_raw*,void*);
+  void      *user_data;
+
+} ffi_raw_closure;
+
+ffi_status
+ffi_prep_raw_closure (ffi_raw_closure*,
+		      ffi_cif *cif,
+		      void (*fun)(ffi_cif*,void*,ffi_raw*,void*),
+		      void *user_data);
+
+ffi_status
+ffi_prep_java_raw_closure (ffi_raw_closure*,
+		           ffi_cif *cif,
+		           void (*fun)(ffi_cif*,void*,ffi_raw*,void*),
+		           void *user_data);
+
+#endif /* FFI_CLOSURES */
+
+/* ---- Public interface definition -------------------------------------- */
+
+ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, 
+			ffi_abi abi,
+			unsigned int nargs, 
+			/*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type *rtype, 
+			/*@dependent@*/ ffi_type **atypes);
+
+int
+ffi_call(/*@dependent@*/ ffi_cif *cif, 
+	 void (*fn)(), 
+	 /*@out@*/ void *rvalue, 
+	 /*@dependent@*/ void **avalue);
+
+/* Useful for eliminating compiler warnings */
+#define FFI_FN(f) ((void (*)())f)
+
+/* ---- Definitions shared with assembly code ---------------------------- */
+
+#endif
+
+/* If these change, update src/mips/ffitarget.h. */
+#define FFI_TYPE_VOID       0    
+#define FFI_TYPE_INT        1
+#define FFI_TYPE_FLOAT      2    
+#define FFI_TYPE_DOUBLE     3
+#if 1
+#define FFI_TYPE_LONGDOUBLE 4
+#else
+#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE
+#endif
+#define FFI_TYPE_UINT8      5   
+#define FFI_TYPE_SINT8      6
+#define FFI_TYPE_UINT16     7 
+#define FFI_TYPE_SINT16     8
+#define FFI_TYPE_UINT32     9
+#define FFI_TYPE_SINT32     10
+#define FFI_TYPE_UINT64     11
+#define FFI_TYPE_SINT64     12
+#define FFI_TYPE_STRUCT     13
+#define FFI_TYPE_POINTER    14
+
+/* This should always refer to the last type code (for sanity checks) */
+#define FFI_TYPE_LAST       FFI_TYPE_POINTER
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/c/libffi_msvc/ffi_common.h b/c/libffi_msvc/ffi_common.h
new file mode 100644
index 0000000..43fb83b
--- /dev/null
+++ b/c/libffi_msvc/ffi_common.h
@@ -0,0 +1,77 @@
+/* -----------------------------------------------------------------------
+   ffi_common.h - Copyright (c) 1996  Red Hat, Inc.
+
+   Common internal definitions and macros. Only necessary for building
+   libffi.
+   ----------------------------------------------------------------------- */
+
+#ifndef FFI_COMMON_H
+#define FFI_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <fficonfig.h>
+#include <malloc.h>
+
+/* Check for the existence of memcpy. */
+#if STDC_HEADERS
+# include <string.h>
+#else
+# ifndef HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#if defined(FFI_DEBUG) 
+#include <stdio.h>
+#endif
+
+#ifdef FFI_DEBUG
+/*@exits@*/ void ffi_assert(/*@temp@*/ char *expr, /*@temp@*/ char *file, int line);
+void ffi_stop_here(void);
+void ffi_type_test(/*@temp@*/ /*@out@*/ ffi_type *a, /*@temp@*/ char *file, int line);
+
+#define FFI_ASSERT(x) ((x) ? (void)0 : ffi_assert(#x, __FILE__,__LINE__))
+#define FFI_ASSERT_AT(x, f, l) ((x) ? 0 : ffi_assert(#x, (f), (l)))
+#define FFI_ASSERT_VALID_TYPE(x) ffi_type_test (x, __FILE__, __LINE__)
+#else
+#define FFI_ASSERT(x) 
+#define FFI_ASSERT_AT(x, f, l)
+#define FFI_ASSERT_VALID_TYPE(x)
+#endif
+
+#define ALIGN(v, a)  (((((size_t) (v))-1) | ((a)-1))+1)
+
+/* Perform machine dependent cif processing */
+ffi_status ffi_prep_cif_machdep(ffi_cif *cif);
+
+/* Extended cif, used in callback from assembly routine */
+typedef struct
+{
+  /*@dependent@*/ ffi_cif *cif;
+  /*@dependent@*/ void *rvalue;
+  /*@dependent@*/ void **avalue;
+} extended_cif;
+
+/* Terse sized type definitions.  */
+typedef unsigned int UINT8  __attribute__((__mode__(__QI__)));
+typedef signed int   SINT8  __attribute__((__mode__(__QI__)));
+typedef unsigned int UINT16 __attribute__((__mode__(__HI__)));
+typedef signed int   SINT16 __attribute__((__mode__(__HI__)));
+typedef unsigned int UINT32 __attribute__((__mode__(__SI__)));
+typedef signed int   SINT32 __attribute__((__mode__(__SI__)));
+typedef unsigned int UINT64 __attribute__((__mode__(__DI__)));
+typedef signed int   SINT64 __attribute__((__mode__(__DI__)));
+
+typedef float FLOAT32;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/c/libffi_msvc/fficonfig.h b/c/libffi_msvc/fficonfig.h
new file mode 100644
index 0000000..c14f653
--- /dev/null
+++ b/c/libffi_msvc/fficonfig.h
@@ -0,0 +1,96 @@
+/* fficonfig.h.  Originally created by configure, now hand_maintained for MSVC. */
+
+/* fficonfig.h.  Generated automatically by configure.  */
+/* fficonfig.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define this for MSVC, but not for mingw32! */
+#ifdef _MSC_VER
+#define __attribute__(x) /* */
+#endif
+#define alloca _alloca
+
+/*----------------------------------------------------------------*/
+
+/* Define if using alloca.c.  */
+/* #undef C_ALLOCA */
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+   This function is required for alloca.c support on those systems.  */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define if you have alloca, as a function or macro.  */
+#define HAVE_ALLOCA 1
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix).  */
+/* #define HAVE_ALLOCA_H 1 */
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+/* #undef STACK_DIRECTION */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if you have the memcpy function.  */
+#define HAVE_MEMCPY 1
+
+/* Define if read-only mmap of a plain file works. */
+//#define HAVE_MMAP_FILE 1
+
+/* Define if mmap of /dev/zero works. */
+//#define HAVE_MMAP_DEV_ZERO 1
+
+/* Define if mmap with MAP_ANON(YMOUS) works. */
+//#define HAVE_MMAP_ANON 1
+
+/* The number of bytes in type double */
+#define SIZEOF_DOUBLE 8
+
+/* The number of bytes in type long double */
+#define SIZEOF_LONG_DOUBLE 12
+
+/* Define if you have the long double type and it is bigger than a double */
+#define HAVE_LONG_DOUBLE 1
+
+/* whether byteorder is bigendian */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define if the host machine stores words of multi-word integers in
+   big-endian order. */
+/* #undef HOST_WORDS_BIG_ENDIAN */
+
+/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */
+#define BYTEORDER 1234
+
+/* Define if your assembler and linker support unaligned PC relative relocs. */
+/* #undef HAVE_AS_SPARC_UA_PCREL */
+
+/* Define if your assembler supports .register. */
+/* #undef HAVE_AS_REGISTER_PSEUDO_OP */
+
+/* Define if .eh_frame sections should be read-only. */
+/* #undef HAVE_RO_EH_FRAME */
+
+/* Define to the flags needed for the .section .eh_frame directive. */
+/* #define EH_FRAME_FLAGS "aw" */
+
+/* Define to the flags needed for the .section .eh_frame directive. */
+/* #define EH_FRAME_FLAGS "aw" */
+
+/* Define this if you want extra debugging. */
+/* #undef FFI_DEBUG */
+
+/* Define this is you do not want support for aggregate types. */
+/* #undef FFI_NO_STRUCTS */
+
+/* Define this is you do not want support for the raw API. */
+/* #undef FFI_NO_RAW_API */
+
+/* Define this if you are using Purify and want to suppress spurious messages. */
+/* #undef USING_PURIFY */
+
diff --git a/c/libffi_msvc/ffitarget.h b/c/libffi_msvc/ffitarget.h
new file mode 100644
index 0000000..85f5ee8
--- /dev/null
+++ b/c/libffi_msvc/ffitarget.h
@@ -0,0 +1,85 @@
+/* -----------------------------------------------------------------*-C-*-
+   ffitarget.h - Copyright (c) 1996-2003  Red Hat, Inc.
+   Target configuration macros for x86 and x86-64.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+
+   ----------------------------------------------------------------------- */
+
+#ifndef LIBFFI_TARGET_H
+#define LIBFFI_TARGET_H
+
+/* ---- System specific configurations ----------------------------------- */
+
+#if defined (X86_64) && defined (__i386__)
+#undef X86_64
+#define X86
+#endif
+
+/* ---- Generic type definitions ----------------------------------------- */
+
+#ifndef LIBFFI_ASM
+#ifndef _WIN64
+typedef unsigned long          ffi_arg;
+#else
+typedef unsigned __int64       ffi_arg;
+#endif
+typedef signed long            ffi_sarg;
+
+typedef enum ffi_abi {
+  FFI_FIRST_ABI = 0,
+
+  /* ---- Intel x86 Win32 ---------- */
+  FFI_SYSV,
+#ifndef _WIN64
+  FFI_STDCALL,
+#endif
+  /* TODO: Add fastcall support for the sake of completeness */
+  FFI_DEFAULT_ABI = FFI_SYSV,
+
+  /* ---- Intel x86 and AMD x86-64 - */
+/* #if !defined(X86_WIN32) && (defined(__i386__) || defined(__x86_64__)) */
+/*   FFI_SYSV, */
+/*   FFI_UNIX64,*/   /* Unix variants all use the same ABI for x86-64  */
+/* #ifdef __i386__ */
+/*   FFI_DEFAULT_ABI = FFI_SYSV, */
+/* #else */
+/*   FFI_DEFAULT_ABI = FFI_UNIX64, */
+/* #endif */
+/* #endif */
+
+  FFI_LAST_ABI = FFI_DEFAULT_ABI + 1
+} ffi_abi;
+#endif
+
+/* ---- Definitions for closures ----------------------------------------- */
+
+#define FFI_CLOSURES 1
+
+#ifdef _WIN64
+#define FFI_TRAMPOLINE_SIZE 29
+#define FFI_NATIVE_RAW_API 0
+#else
+#define FFI_TRAMPOLINE_SIZE 15
+#define FFI_NATIVE_RAW_API 1	/* x86 has native raw api support */
+#endif
+
+#endif
+
diff --git a/c/libffi_msvc/prep_cif.c b/c/libffi_msvc/prep_cif.c
new file mode 100644
index 0000000..5dacfff
--- /dev/null
+++ b/c/libffi_msvc/prep_cif.c
@@ -0,0 +1,181 @@
+/* -----------------------------------------------------------------------
+   prep_cif.c - Copyright (c) 1996, 1998  Red Hat, Inc.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+   ----------------------------------------------------------------------- */
+
+#include <ffi.h>
+#include <ffi_common.h>
+#include <stdlib.h>
+
+
+/* Round up to FFI_SIZEOF_ARG. */
+
+#define STACK_ARG_SIZE(x) ALIGN(x, FFI_SIZEOF_ARG)
+
+/* Perform machine independent initialization of aggregate type
+   specifications. */
+
+static ffi_status initialize_aggregate(/*@out@*/ ffi_type *arg)
+{
+  ffi_type **ptr; 
+
+  FFI_ASSERT(arg != NULL);
+
+  /*@-usedef@*/
+
+  FFI_ASSERT(arg->elements != NULL);
+  FFI_ASSERT(arg->size == 0);
+  FFI_ASSERT(arg->alignment == 0);
+
+  ptr = &(arg->elements[0]);
+
+  while ((*ptr) != NULL)
+    {
+      if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK))
+	return FFI_BAD_TYPEDEF;
+      
+      /* Perform a sanity check on the argument type */
+      FFI_ASSERT_VALID_TYPE(*ptr);
+
+      arg->size = ALIGN(arg->size, (*ptr)->alignment);
+      arg->size += (*ptr)->size;
+
+      arg->alignment = (arg->alignment > (*ptr)->alignment) ? 
+	arg->alignment : (*ptr)->alignment;
+
+      ptr++;
+    }
+
+  /* Structure size includes tail padding.  This is important for
+     structures that fit in one register on ABIs like the PowerPC64
+     Linux ABI that right justify small structs in a register.
+     It's also needed for nested structure layout, for example
+     struct A { long a; char b; }; struct B { struct A x; char y; };
+     should find y at an offset of 2*sizeof(long) and result in a
+     total size of 3*sizeof(long).  */
+  arg->size = ALIGN (arg->size, arg->alignment);
+
+  if (arg->size == 0)
+    return FFI_BAD_TYPEDEF;
+  else
+    return FFI_OK;
+
+  /*@=usedef@*/
+}
+
+/* Perform machine independent ffi_cif preparation, then call
+   machine dependent routine. */
+
+ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, 
+			ffi_abi abi, unsigned int nargs, 
+			/*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type *rtype, 
+			/*@dependent@*/ ffi_type **atypes)
+{
+  unsigned bytes = 0;
+  unsigned int i;
+  ffi_type **ptr;
+
+  FFI_ASSERT(cif != NULL);
+  FFI_ASSERT((abi > FFI_FIRST_ABI) && (abi <= FFI_DEFAULT_ABI));
+
+  cif->abi = abi;
+  cif->arg_types = atypes;
+  cif->nargs = nargs;
+  cif->rtype = rtype;
+
+  cif->flags = 0;
+
+  /* Initialize the return type if necessary */
+  /*@-usedef@*/
+  if ((cif->rtype->size == 0) && (initialize_aggregate(cif->rtype) != FFI_OK))
+    return FFI_BAD_TYPEDEF;
+  /*@=usedef@*/
+
+  /* Perform a sanity check on the return type */
+  FFI_ASSERT_VALID_TYPE(cif->rtype);
+
+  /* x86-64 and s390 stack space allocation is handled in prep_machdep.  */
+#if !defined M68K && !defined __x86_64__ && !defined S390
+  /* Make space for the return structure pointer */
+  if (cif->rtype->type == FFI_TYPE_STRUCT
+#ifdef _WIN32
+      && (cif->rtype->size > 8)  /* MSVC returns small structs in registers */
+#endif
+#ifdef SPARC
+      && (cif->abi != FFI_V9 || cif->rtype->size > 32)
+#endif
+      )
+    bytes = STACK_ARG_SIZE(sizeof(void*));
+#endif
+
+  for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
+    {
+
+      /* Initialize any uninitialized aggregate type definitions */
+      if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK))
+	return FFI_BAD_TYPEDEF;
+
+      /* Perform a sanity check on the argument type, do this 
+	 check after the initialization.  */
+      FFI_ASSERT_VALID_TYPE(*ptr);
+
+#if !defined __x86_64__ && !defined S390
+#ifdef SPARC
+      if (((*ptr)->type == FFI_TYPE_STRUCT
+	   && ((*ptr)->size > 16 || cif->abi != FFI_V9))
+	  || ((*ptr)->type == FFI_TYPE_LONGDOUBLE
+	      && cif->abi != FFI_V9))
+	bytes += sizeof(void*);
+      else
+#endif
+	{
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+		/* Don't know if this is a libffi bug or not.  At least on
+		   Windows with MSVC, function call parameters are *not*
+		   aligned in the same way as structure fields are, they are
+		   only aligned in integer boundaries.
+
+		   This doesn't do any harm for cdecl functions and closures,
+		   since the caller cleans up the stack, but it is wrong for
+		   stdcall functions where the callee cleans.
+		*/
+
+	  /* Add any padding if necessary */
+	  if (((*ptr)->alignment - 1) & bytes)
+	    bytes = ALIGN(bytes, (*ptr)->alignment);
+	  
+#endif
+	  bytes += STACK_ARG_SIZE((*ptr)->size);
+	}
+#endif
+    }
+
+#ifdef _WIN64
+  /* Function call needs at least 40 bytes stack size, on win64 AMD64 */
+  if (bytes < 40)
+      bytes = 40;
+#endif
+
+  cif->bytes = bytes;
+
+  /* Perform machine dependent cif processing */
+  return ffi_prep_cif_machdep(cif);
+}
diff --git a/c/libffi_msvc/types.c b/c/libffi_msvc/types.c
new file mode 100644
index 0000000..4433ac2
--- /dev/null
+++ b/c/libffi_msvc/types.c
@@ -0,0 +1,104 @@
+/* -----------------------------------------------------------------------
+   types.c - Copyright (c) 1996, 1998  Red Hat, Inc.
+   
+   Predefined ffi_types needed by libffi.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+   ----------------------------------------------------------------------- */
+
+#include <ffi.h>
+#include <ffi_common.h>
+
+/* Type definitions */
+
+#define FFI_INTEGRAL_TYPEDEF(n, s, a, t) ffi_type ffi_type_##n = { s, a, t, NULL }
+#define FFI_AGGREGATE_TYPEDEF(n, e) ffi_type ffi_type_##n = { 0, 0, FFI_TYPE_STRUCT, e }
+
+/* Size and alignment are fake here. They must not be 0. */
+FFI_INTEGRAL_TYPEDEF(void, 1, 1, FFI_TYPE_VOID);
+
+FFI_INTEGRAL_TYPEDEF(uint8, 1, 1, FFI_TYPE_UINT8);
+FFI_INTEGRAL_TYPEDEF(sint8, 1, 1, FFI_TYPE_SINT8);
+FFI_INTEGRAL_TYPEDEF(uint16, 2, 2, FFI_TYPE_UINT16);
+FFI_INTEGRAL_TYPEDEF(sint16, 2, 2, FFI_TYPE_SINT16);
+FFI_INTEGRAL_TYPEDEF(uint32, 4, 4, FFI_TYPE_UINT32);
+FFI_INTEGRAL_TYPEDEF(sint32, 4, 4, FFI_TYPE_SINT32);
+FFI_INTEGRAL_TYPEDEF(float, 4, 4, FFI_TYPE_FLOAT);
+
+#if defined ALPHA || defined SPARC64 || defined X86_64 || defined S390X \
+    || defined IA64 || defined _WIN64
+
+FFI_INTEGRAL_TYPEDEF(pointer, 8, 8, FFI_TYPE_POINTER);
+
+#else
+
+FFI_INTEGRAL_TYPEDEF(pointer, 4, 4, FFI_TYPE_POINTER);
+
+#endif
+
+#if defined X86 || defined X86_WIN32 || defined ARM || defined M68K
+
+FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64);
+FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64);
+
+#elif defined SH
+
+FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64);
+FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64);
+
+#else
+
+FFI_INTEGRAL_TYPEDEF(uint64, 8, 8, FFI_TYPE_UINT64);
+FFI_INTEGRAL_TYPEDEF(sint64, 8, 8, FFI_TYPE_SINT64);
+
+#endif
+
+
+#if defined X86 || defined X86_WIN32 || defined M68K
+
+FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE);
+FFI_INTEGRAL_TYPEDEF(longdouble, 12, 4, FFI_TYPE_LONGDOUBLE);
+
+#elif defined ARM || defined SH || defined POWERPC_AIX || defined POWERPC_DARWIN
+
+FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE);
+FFI_INTEGRAL_TYPEDEF(longdouble, 8, 4, FFI_TYPE_LONGDOUBLE);
+
+#elif defined SPARC
+
+FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE);
+#ifdef SPARC64
+FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE);
+#else
+FFI_INTEGRAL_TYPEDEF(longdouble, 16, 8, FFI_TYPE_LONGDOUBLE);
+#endif
+
+#elif defined X86_64
+
+FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE);
+FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE);
+
+#else
+
+FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE);
+FFI_INTEGRAL_TYPEDEF(longdouble, 8, 8, FFI_TYPE_LONGDOUBLE);
+
+#endif
+
diff --git a/c/libffi_msvc/win32.c b/c/libffi_msvc/win32.c
new file mode 100644
index 0000000..d1149a8
--- /dev/null
+++ b/c/libffi_msvc/win32.c
@@ -0,0 +1,162 @@
+/* -----------------------------------------------------------------------
+   win32.S - Copyright (c) 1996, 1998, 2001, 2002  Red Hat, Inc.
+	     Copyright (c) 2001  John Beniton
+	     Copyright (c) 2002  Ranjit Mathew
+			
+ 
+   X86 Foreign Function Interface
+ 
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+ 
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+ 
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+   ----------------------------------------------------------------------- */
+
+/* theller: almost verbatim translation from gas syntax to MSVC inline
+   assembler code. */
+
+/* theller: ffi_call_x86 now returns an integer - the difference of the stack
+   pointer before and after the function call.  If everything is ok, zero is
+   returned.  If stdcall functions are passed the wrong number of arguments,
+   the difference will be nonzero. */
+
+#include <ffi.h>
+#include <ffi_common.h>
+
+__declspec(naked) int
+ffi_call_x86(void (* prepfunc)(char *, extended_cif *), /* 8 */
+	     extended_cif *ecif, /* 12 */
+	     unsigned bytes, /* 16 */
+	     unsigned flags, /* 20 */
+	     unsigned *rvalue, /* 24 */
+	     void (*fn)()) /* 28 */
+{
+	_asm {
+		push ebp
+		mov ebp, esp
+
+		push esi // NEW: this register must be preserved across function calls
+// XXX SAVE ESP NOW!
+		mov esi, esp		// save stack pointer before the call
+
+// Make room for all of the new args.
+		mov ecx, [ebp+16]
+		sub esp, ecx		// sub esp, bytes
+		
+		mov eax, esp
+
+// Place all of the ffi_prep_args in position
+		push [ebp + 12] // ecif
+		push eax
+		call [ebp + 8] // prepfunc
+
+// Return stack to previous state and call the function
+		add esp, 8
+// FIXME: Align the stack to a 128-bit boundary to avoid
+// potential performance hits.
+		call [ebp + 28]
+
+// Load ecif->cif->abi
+		mov ecx, [ebp + 12]
+		mov ecx, [ecx]ecif.cif
+		mov ecx, [ecx]ecif.cif.abi
+		
+		cmp ecx, FFI_STDCALL
+		je noclean
+// STDCALL: Remove the space we pushed for the args
+		mov ecx, [ebp + 16]
+		add esp, ecx
+// CDECL: Caller has already cleaned the stack
+noclean:
+// Check that esp has the same value as before!
+		sub esi, esp
+
+// Load %ecx with the return type code
+		mov ecx, [ebp + 20]
+
+// If the return value pointer is NULL, assume no return value.
+/*
+  Intel asm is weird. We have to explicitely specify 'DWORD PTR' in the nexr instruction,
+  otherwise only one BYTE will be compared (instead of a DWORD)!
+ */
+		cmp DWORD PTR [ebp + 24], 0
+		jne sc_retint
+
+// Even if there is no space for the return value, we are
+// obliged to handle floating-point values.
+		cmp ecx, FFI_TYPE_FLOAT
+		jne sc_noretval
+//        fstp  %st(0)
+		fstp st(0)
+
+		jmp sc_epilogue
+
+sc_retint:
+		cmp ecx, FFI_TYPE_INT
+		jne sc_retfloat
+//        # Load %ecx with the pointer to storage for the return value
+		mov ecx, [ebp + 24]
+		mov [ecx + 0], eax
+		jmp sc_epilogue
+
+sc_retfloat:
+		cmp ecx, FFI_TYPE_FLOAT
+		jne sc_retdouble
+// Load %ecx with the pointer to storage for the return value
+		mov ecx, [ebp+24]
+//        fstps (%ecx)
+		fstp DWORD PTR [ecx]
+		jmp sc_epilogue
+
+sc_retdouble:
+		cmp ecx, FFI_TYPE_DOUBLE
+		jne sc_retlongdouble
+//        movl  24(%ebp),%ecx
+		mov ecx, [ebp+24]
+		fstp QWORD PTR [ecx]
+		jmp sc_epilogue
+
+		jmp sc_retlongdouble // avoid warning about unused label
+sc_retlongdouble:
+		cmp ecx, FFI_TYPE_LONGDOUBLE
+		jne sc_retint64
+// Load %ecx with the pointer to storage for the return value
+		mov ecx, [ebp+24]
+//        fstpt (%ecx)
+		fstp QWORD PTR [ecx] /* XXX ??? */
+		jmp sc_epilogue
+
+sc_retint64:
+		cmp ecx, FFI_TYPE_SINT64
+		jne sc_retstruct
+// Load %ecx with the pointer to storage for the return value
+		mov ecx, [ebp+24]
+		mov [ecx+0], eax
+		mov [ecx+4], edx
+
+sc_retstruct:
+// Nothing to do!
+
+sc_noretval:
+sc_epilogue:
+		mov eax, esi
+		pop esi // NEW restore: must be preserved across function calls
+		mov esp, ebp
+		pop ebp
+		ret
+	}
+}
diff --git a/c/libffi_msvc/win64.asm b/c/libffi_msvc/win64.asm
new file mode 100644
index 0000000..301188b
--- /dev/null
+++ b/c/libffi_msvc/win64.asm
@@ -0,0 +1,156 @@
+PUBLIC	ffi_call_AMD64
+
+EXTRN	__chkstk:NEAR
+EXTRN	ffi_closure_SYSV:NEAR
+
+_TEXT	SEGMENT
+
+;;; ffi_closure_OUTER will be called with these registers set:
+;;;    rax points to 'closure'
+;;;    r11 contains a bit mask that specifies which of the
+;;;    first four parameters are float or double
+;;;
+;;; It must move the parameters passed in registers to their stack location,
+;;; call ffi_closure_SYSV for the actual work, then return the result.
+;;; 
+ffi_closure_OUTER PROC FRAME
+	;; save actual arguments to their stack space.
+	test	r11, 1
+	jne	first_is_float	
+	mov	QWORD PTR [rsp+8], rcx
+	jmp	second
+first_is_float:
+	movlpd	QWORD PTR [rsp+8], xmm0
+
+second:
+	test	r11, 2
+	jne	second_is_float	
+	mov	QWORD PTR [rsp+16], rdx
+	jmp	third
+second_is_float:
+	movlpd	QWORD PTR [rsp+16], xmm1
+
+third:
+	test	r11, 4
+	jne	third_is_float	
+	mov	QWORD PTR [rsp+24], r8
+	jmp	forth
+third_is_float:
+	movlpd	QWORD PTR [rsp+24], xmm2
+
+forth:
+	test	r11, 8
+	jne	forth_is_float	
+	mov	QWORD PTR [rsp+32], r9
+	jmp	done
+forth_is_float:
+	movlpd	QWORD PTR [rsp+32], xmm3
+
+done:
+.ALLOCSTACK 40
+	sub	rsp, 40
+.ENDPROLOG
+	mov	rcx, rax	; context is first parameter
+	mov	rdx, rsp	; stack is second parameter
+	add	rdx, 40		; correct our own area
+	mov	rax, ffi_closure_SYSV
+	call	rax		; call the real closure function
+	;; Here, code is missing that handles float return values
+	add	rsp, 40
+	movd	xmm0, rax	; In case the closure returned a float.
+	ret	0
+ffi_closure_OUTER ENDP
+
+
+;;; ffi_call_AMD64
+
+stack$ = 0
+prepfunc$ = 32
+ecif$ = 40
+bytes$ = 48
+flags$ = 56
+rvalue$ = 64
+fn$ = 72
+
+ffi_call_AMD64 PROC FRAME
+
+	mov	QWORD PTR [rsp+32], r9
+	mov	QWORD PTR [rsp+24], r8
+	mov	QWORD PTR [rsp+16], rdx
+	mov	QWORD PTR [rsp+8], rcx
+.PUSHREG rbp
+	push	rbp
+.ALLOCSTACK 48
+	sub	rsp, 48					; 00000030H
+.SETFRAME rbp, 32
+	lea	rbp, QWORD PTR [rsp+32]
+.ENDPROLOG
+
+	mov	eax, DWORD PTR bytes$[rbp]
+	add	rax, 15
+	and	rax, -16
+	call	__chkstk
+	sub	rsp, rax
+	lea	rax, QWORD PTR [rsp+32]
+	mov	QWORD PTR stack$[rbp], rax
+
+	mov	rdx, QWORD PTR ecif$[rbp]
+	mov	rcx, QWORD PTR stack$[rbp]
+	call	QWORD PTR prepfunc$[rbp]
+
+	mov	rsp, QWORD PTR stack$[rbp]
+
+	movlpd	xmm3, QWORD PTR [rsp+24]
+	movd	r9, xmm3
+
+	movlpd	xmm2, QWORD PTR [rsp+16]
+	movd	r8, xmm2
+
+	movlpd	xmm1, QWORD PTR [rsp+8]
+	movd	rdx, xmm1
+
+	movlpd	xmm0, QWORD PTR [rsp]
+	movd	rcx, xmm0
+
+	call	QWORD PTR fn$[rbp]
+ret_int$:
+ 	cmp	DWORD PTR flags$[rbp], 1 ; FFI_TYPE_INT
+ 	jne	ret_float$
+
+	mov	rcx, QWORD PTR rvalue$[rbp]
+	mov	DWORD PTR [rcx], eax
+	jmp	SHORT ret_nothing$
+
+ret_float$:
+ 	cmp	DWORD PTR flags$[rbp], 2 ; FFI_TYPE_FLOAT
+ 	jne	SHORT ret_double$
+
+ 	mov	rax, QWORD PTR rvalue$[rbp]
+ 	movlpd	QWORD PTR [rax], xmm0
+ 	jmp	SHORT ret_nothing$
+
+ret_double$:
+ 	cmp	DWORD PTR flags$[rbp], 3 ; FFI_TYPE_DOUBLE
+ 	jne	SHORT ret_int64$
+
+ 	mov	rax, QWORD PTR rvalue$[rbp]
+ 	movlpd	QWORD PTR [rax], xmm0
+ 	jmp	SHORT ret_nothing$
+
+ret_int64$:
+  	cmp	DWORD PTR flags$[rbp], 12 ; FFI_TYPE_SINT64
+  	jne	ret_nothing$
+
+ 	mov	rcx, QWORD PTR rvalue$[rbp]
+ 	mov	QWORD PTR [rcx], rax
+ 	jmp	SHORT ret_nothing$
+	
+ret_nothing$:
+	xor	eax, eax
+
+	lea	rsp, QWORD PTR [rbp+16]
+	pop	rbp
+	ret	0
+ffi_call_AMD64 ENDP
+_TEXT	ENDS
+END
diff --git a/c/libffi_msvc/win64.obj b/c/libffi_msvc/win64.obj
new file mode 100644
index 0000000..38d3cd1
--- /dev/null
+++ b/c/libffi_msvc/win64.obj
Binary files differ
diff --git a/c/malloc_closure.h b/c/malloc_closure.h
new file mode 100644
index 0000000..bebb93d
--- /dev/null
+++ b/c/malloc_closure.h
@@ -0,0 +1,176 @@
+/*
+ * This file is from CPython's Modules/_ctypes/malloc_closure.c
+ * and has received some edits.
+ */
+
+#include <ffi.h>
+#ifdef MS_WIN32
+#include <windows.h>
+#else
+#include <sys/mman.h>
+#include <unistd.h>
+# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#  define MAP_ANONYMOUS MAP_ANON
+# endif
+#endif
+
+/* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC.
+
+   This is, apparently, an undocumented change to ffi_prep_closure():
+   depending on the Linux kernel we're running on, we must give it a
+   mmap that is either PROT_READ|PROT_WRITE|PROT_EXEC or only
+   PROT_READ|PROT_WRITE.  In the latter case, just trying to obtain a
+   mmap with PROT_READ|PROT_WRITE|PROT_EXEC would kill our process(!),
+   but in that situation libffi is fine with only PROT_READ|PROT_WRITE.
+   There is nothing in the libffi API to know that, though, so we have
+   to guess by parsing /proc/self/status.  "Meh."
+ */
+#ifdef __linux__
+#include <stdlib.h>
+
+static int emutramp_enabled = -1;
+
+static int
+emutramp_enabled_check (void)
+{
+    char *buf = NULL;
+    size_t len = 0;
+    FILE *f;
+    int ret;
+    f = fopen ("/proc/self/status", "r");
+    if (f == NULL)
+        return 0;
+    ret = 0;
+
+    while (getline (&buf, &len, f) != -1)
+        if (!strncmp (buf, "PaX:", 4))
+            {
+                char emutramp;
+                if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
+                    ret = (emutramp == 'E');
+                break;
+            }
+    free (buf);
+    fclose (f);
+    return ret;
+}
+
+#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
+        : (emutramp_enabled = emutramp_enabled_check ()))
+#else
+#define is_emutramp_enabled() 0
+#endif
+
+
+/* 'allocate_num_pages' is dynamically adjusted starting from one
+   page.  It grows by a factor of PAGE_ALLOCATION_GROWTH_RATE.  This is
+   meant to handle both the common case of not needing a lot of pages,
+   and the rare case of needing many of them.  Systems in general have a
+   limit of how many mmap'd blocks can be open.
+*/
+
+#define PAGE_ALLOCATION_GROWTH_RATE  1.3
+
+static Py_ssize_t allocate_num_pages = 0;
+
+/* #define MALLOC_CLOSURE_DEBUG */ /* enable for some debugging output */
+
+/******************************************************************/
+
+union mmaped_block {
+    ffi_closure closure;
+    union mmaped_block *next;
+};
+
+static union mmaped_block *free_list = 0;
+static Py_ssize_t _pagesize = 0;
+
+static void more_core(void)
+{
+    union mmaped_block *item;
+    Py_ssize_t count, i;
+
+/* determine the pagesize */
+#ifdef MS_WIN32
+    if (!_pagesize) {
+        SYSTEM_INFO systeminfo;
+        GetSystemInfo(&systeminfo);
+        _pagesize = systeminfo.dwPageSize;
+    }
+#else
+    if (!_pagesize) {
+#ifdef _SC_PAGESIZE
+        _pagesize = sysconf(_SC_PAGESIZE);
+#else
+        _pagesize = getpagesize();
+#endif
+    }
+#endif
+    if (_pagesize <= 0)
+        _pagesize = 4096;
+
+    /* bump 'allocate_num_pages' */
+    allocate_num_pages = 1 + (
+        (Py_ssize_t)(allocate_num_pages * PAGE_ALLOCATION_GROWTH_RATE));
+
+    /* calculate the number of mmaped_blocks to allocate */
+    count = (allocate_num_pages * _pagesize) / sizeof(union mmaped_block);
+
+    /* allocate a memory block */
+#ifdef MS_WIN32
+    item = (union mmaped_block *)VirtualAlloc(NULL,
+                                           count * sizeof(union mmaped_block),
+                                           MEM_COMMIT,
+                                           PAGE_EXECUTE_READWRITE);
+    if (item == NULL)
+        return;
+#else
+    {
+    int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+    if (is_emutramp_enabled ())
+        prot &= ~PROT_EXEC;
+    item = (union mmaped_block *)mmap(NULL,
+                        allocate_num_pages * _pagesize,
+                        prot,
+                        MAP_PRIVATE | MAP_ANONYMOUS,
+                        -1,
+                        0);
+    if (item == (void *)MAP_FAILED)
+        return;
+    }
+#endif
+
+#ifdef MALLOC_CLOSURE_DEBUG
+    printf("block at %p allocated (%ld bytes), %ld mmaped_blocks\n",
+           item, (long)(allocate_num_pages * _pagesize), (long)count);
+#endif
+    /* put them into the free list */
+    for (i = 0; i < count; ++i) {
+        item->next = free_list;
+        free_list = item;
+        ++item;
+    }
+}
+
+/******************************************************************/
+
+/* put the item back into the free list */
+static void cffi_closure_free(ffi_closure *p)
+{
+    union mmaped_block *item = (union mmaped_block *)p;
+    item->next = free_list;
+    free_list = item;
+}
+
+/* return one item from the free list, allocating more if needed */
+static ffi_closure *cffi_closure_alloc(void)
+{
+    union mmaped_block *item;
+    if (!free_list)
+        more_core();
+    if (!free_list)
+        return NULL;
+    item = free_list;
+    free_list = item->next;
+    return &item->closure;
+}
diff --git a/c/minibuffer.h b/c/minibuffer.h
new file mode 100644
index 0000000..f3f5ca1
--- /dev/null
+++ b/c/minibuffer.h
@@ -0,0 +1,408 @@
+
+/* Implementation of a C object with the 'buffer' or 'memoryview'
+ * interface at C-level (as approriate for the version of Python we're
+ * compiling for), but only a minimal but *consistent* part of the
+ * 'buffer' interface at application level.
+ */
+
+typedef struct {
+    PyObject_HEAD
+    char      *mb_data;
+    Py_ssize_t mb_size;
+    PyObject  *mb_keepalive;
+    PyObject  *mb_weakreflist;    /* weakref support */
+} MiniBufferObj;
+
+static Py_ssize_t mb_length(MiniBufferObj *self)
+{
+    return self->mb_size;
+}
+
+static PyObject *mb_item(MiniBufferObj *self, Py_ssize_t idx)
+{
+    if (idx < 0 || idx >= self->mb_size ) {
+        PyErr_SetString(PyExc_IndexError, "buffer index out of range");
+        return NULL;
+    }
+    return PyBytes_FromStringAndSize(self->mb_data + idx, 1);
+}
+
+static PyObject *mb_slice(MiniBufferObj *self,
+                          Py_ssize_t left, Py_ssize_t right)
+{
+    Py_ssize_t size = self->mb_size;
+    if (left < 0)     left = 0;
+    if (right > size) right = size;
+    if (left > right) left = right;
+    return PyBytes_FromStringAndSize(self->mb_data + left, right - left);
+}
+
+static int mb_ass_item(MiniBufferObj *self, Py_ssize_t idx, PyObject *other)
+{
+    if (idx < 0 || idx >= self->mb_size) {
+        PyErr_SetString(PyExc_IndexError,
+                        "buffer assignment index out of range");
+        return -1;
+    }
+    if (PyBytes_Check(other) && PyBytes_GET_SIZE(other) == 1) {
+        self->mb_data[idx] = PyBytes_AS_STRING(other)[0];
+        return 0;
+    }
+    else {
+        PyErr_Format(PyExc_TypeError,
+                     "must assign a "STR_OR_BYTES
+                     " of length 1, not %.200s", Py_TYPE(other)->tp_name);
+        return -1;
+    }
+}
+
+/* forward: from _cffi_backend.c */
+static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only);
+
+static int mb_ass_slice(MiniBufferObj *self,
+                        Py_ssize_t left, Py_ssize_t right, PyObject *other)
+{
+    Py_ssize_t count;
+    Py_ssize_t size = self->mb_size;
+    Py_buffer src_view;
+
+    if (_fetch_as_buffer(other, &src_view, 0) < 0)
+        return -1;
+
+    if (left < 0)     left = 0;
+    if (right > size) right = size;
+    if (left > right) left = right;
+
+    count = right - left;
+    if (count != src_view.len) {
+        PyBuffer_Release(&src_view);
+        PyErr_SetString(PyExc_ValueError,
+                        "right operand length must match slice length");
+        return -1;
+    }
+    memcpy(self->mb_data + left, src_view.buf, count);
+    PyBuffer_Release(&src_view);
+    return 0;
+}
+
+#if PY_MAJOR_VERSION < 3
+static Py_ssize_t mb_getdata(MiniBufferObj *self, Py_ssize_t idx, void **pp)
+{
+    *pp = self->mb_data;
+    return self->mb_size;
+}
+
+static Py_ssize_t mb_getsegcount(MiniBufferObj *self, Py_ssize_t *lenp)
+{
+    if (lenp)
+        *lenp = self->mb_size;
+    return 1;
+}
+
+static PyObject *mb_str(MiniBufferObj *self)
+{
+    /* Python 2: we want str(buffer) to behave like buffer[:], because
+       that's what bytes(buffer) does on Python 3 and there is no way
+       we can prevent this. */
+    return PyString_FromStringAndSize(self->mb_data, self->mb_size);
+}
+#endif
+
+static int mb_getbuf(MiniBufferObj *self, Py_buffer *view, int flags)
+{
+    return PyBuffer_FillInfo(view, (PyObject *)self,
+                             self->mb_data, self->mb_size,
+                             /*readonly=*/0, flags);
+}
+
+static PySequenceMethods mb_as_sequence = {
+    (lenfunc)mb_length, /*sq_length*/
+    (binaryfunc)0, /*sq_concat*/
+    (ssizeargfunc)0, /*sq_repeat*/
+    (ssizeargfunc)mb_item, /*sq_item*/
+    (ssizessizeargfunc)mb_slice, /*sq_slice*/
+    (ssizeobjargproc)mb_ass_item, /*sq_ass_item*/
+    (ssizessizeobjargproc)mb_ass_slice, /*sq_ass_slice*/
+};
+
+static PyBufferProcs mb_as_buffer = {
+#if PY_MAJOR_VERSION < 3
+    (readbufferproc)mb_getdata,
+    (writebufferproc)mb_getdata,
+    (segcountproc)mb_getsegcount,
+    (charbufferproc)mb_getdata,
+#endif
+    (getbufferproc)mb_getbuf,
+    (releasebufferproc)0,
+};
+
+static void
+mb_dealloc(MiniBufferObj *ob)
+{
+    PyObject_GC_UnTrack(ob);
+    if (ob->mb_weakreflist != NULL)
+        PyObject_ClearWeakRefs((PyObject *)ob);
+    Py_XDECREF(ob->mb_keepalive);
+    Py_TYPE(ob)->tp_free((PyObject *)ob);
+}
+
+static int
+mb_traverse(MiniBufferObj *ob, visitproc visit, void *arg)
+{
+    Py_VISIT(ob->mb_keepalive);
+    return 0;
+}
+
+static int
+mb_clear(MiniBufferObj *ob)
+{
+    Py_CLEAR(ob->mb_keepalive);
+    return 0;
+}
+
+static PyObject *
+mb_richcompare(PyObject *self, PyObject *other, int op)
+{
+    Py_ssize_t self_size, other_size;
+    Py_buffer self_bytes, other_bytes;
+    PyObject *res;
+    Py_ssize_t minsize;
+    int cmp, rc;
+
+    /* Bytes can be compared to anything that supports the (binary)
+       buffer API.  Except that a comparison with Unicode is always an
+       error, even if the comparison is for equality. */
+    rc = PyObject_IsInstance(self, (PyObject*)&PyUnicode_Type);
+    if (!rc)
+        rc = PyObject_IsInstance(other, (PyObject*)&PyUnicode_Type);
+    if (rc < 0)
+        return NULL;
+    if (rc) {
+        Py_INCREF(Py_NotImplemented);
+        return Py_NotImplemented;
+    }
+
+    if (PyObject_GetBuffer(self, &self_bytes, PyBUF_SIMPLE) != 0) {
+        PyErr_Clear();
+        Py_INCREF(Py_NotImplemented);
+        return Py_NotImplemented;
+
+    }
+    self_size = self_bytes.len;
+
+    if (PyObject_GetBuffer(other, &other_bytes, PyBUF_SIMPLE) != 0) {
+        PyErr_Clear();
+        PyBuffer_Release(&self_bytes);
+        Py_INCREF(Py_NotImplemented);
+        return Py_NotImplemented;
+
+    }
+    other_size = other_bytes.len;
+
+    if (self_size != other_size && (op == Py_EQ || op == Py_NE)) {
+        /* Shortcut: if the lengths differ, the objects differ */
+        cmp = (op == Py_NE);
+    }
+    else {
+        minsize = self_size;
+        if (other_size < minsize)
+            minsize = other_size;
+
+        cmp = memcmp(self_bytes.buf, other_bytes.buf, minsize);
+        /* In ISO C, memcmp() guarantees to use unsigned bytes! */
+
+        if (cmp == 0) {
+            if (self_size < other_size)
+                cmp = -1;
+            else if (self_size > other_size)
+                cmp = 1;
+        }
+
+        switch (op) {
+        case Py_LT: cmp = cmp <  0; break;
+        case Py_LE: cmp = cmp <= 0; break;
+        case Py_EQ: cmp = cmp == 0; break;
+        case Py_NE: cmp = cmp != 0; break;
+        case Py_GT: cmp = cmp >  0; break;
+        case Py_GE: cmp = cmp >= 0; break;
+        }
+    }
+
+    res = cmp ? Py_True : Py_False;
+    PyBuffer_Release(&self_bytes);
+    PyBuffer_Release(&other_bytes);
+    Py_INCREF(res);
+    return res;
+}
+
+#if PY_MAJOR_VERSION >= 3
+/* pfffffffffffff pages of copy-paste from listobject.c */
+
+/* pfffffffffffff#2: the PySlice_GetIndicesEx() *macro* should not
+   be called, because C extension modules compiled with it differ
+   on ABI between 3.6.0, 3.6.1 and 3.6.2. */
+#if PY_VERSION_HEX < 0x03070000 && defined(PySlice_GetIndicesEx) && !defined(PYPY_VERSION)
+#undef PySlice_GetIndicesEx
+#endif
+
+static PyObject *mb_subscript(MiniBufferObj *self, PyObject *item)
+{
+    if (PyIndex_Check(item)) {
+        Py_ssize_t i;
+        i = PyNumber_AsSsize_t(item, PyExc_IndexError);
+        if (i == -1 && PyErr_Occurred())
+            return NULL;
+        if (i < 0)
+            i += self->mb_size;
+        return mb_item(self, i);
+    }
+    else if (PySlice_Check(item)) {
+        Py_ssize_t start, stop, step, slicelength;
+
+        if (PySlice_GetIndicesEx(item, self->mb_size,
+                         &start, &stop, &step, &slicelength) < 0)
+            return NULL;
+
+        if (step == 1)
+            return mb_slice(self, start, stop);
+        else {
+            PyErr_SetString(PyExc_TypeError,
+                            "buffer doesn't support slicing with step != 1");
+            return NULL;
+        }
+    }
+    else {
+        PyErr_Format(PyExc_TypeError,
+                     "buffer indices must be integers, not %.200s",
+                     item->ob_type->tp_name);
+        return NULL;
+    }
+}
+static int
+mb_ass_subscript(MiniBufferObj* self, PyObject* item, PyObject* value)
+{
+    if (PyIndex_Check(item)) {
+        Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
+        if (i == -1 && PyErr_Occurred())
+            return -1;
+        if (i < 0)
+            i += self->mb_size;
+        return mb_ass_item(self, i, value);
+    }
+    else if (PySlice_Check(item)) {
+        Py_ssize_t start, stop, step, slicelength;
+
+        if (PySlice_GetIndicesEx(item, self->mb_size,
+                         &start, &stop, &step, &slicelength) < 0) {
+            return -1;
+        }
+
+        if (step == 1)
+            return mb_ass_slice(self, start, stop, value);
+        else {
+            PyErr_SetString(PyExc_TypeError,
+                            "buffer doesn't support slicing with step != 1");
+            return -1;
+        }
+    }
+    else {
+        PyErr_Format(PyExc_TypeError,
+                     "buffer indices must be integers, not %.200s",
+                     item->ob_type->tp_name);
+        return -1;
+    }
+}
+
+static PyMappingMethods mb_as_mapping = {
+    (lenfunc)mb_length, /*mp_length*/
+    (binaryfunc)mb_subscript, /*mp_subscript*/
+    (objobjargproc)mb_ass_subscript, /*mp_ass_subscript*/
+};
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+# define MINIBUF_TPFLAGS 0
+#else
+# define MINIBUF_TPFLAGS (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER)
+#endif
+
+PyDoc_STRVAR(ffi_buffer_doc,
+"ffi.buffer(cdata[, byte_size]):\n"
+"Return a read-write buffer object that references the raw C data\n"
+"pointed to by the given 'cdata'.  The 'cdata' must be a pointer or an\n"
+"array.  Can be passed to functions expecting a buffer, or directly\n"
+"manipulated with:\n"
+"\n"
+"    buf[:]          get a copy of it in a regular string, or\n"
+"    buf[idx]        as a single character\n"
+"    buf[:] = ...\n"
+"    buf[idx] = ...  change the content");
+
+static PyObject *            /* forward, implemented in _cffi_backend.c */
+b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+
+
+static PyTypeObject MiniBuffer_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.buffer",
+    sizeof(MiniBufferObj),
+    0,
+    (destructor)mb_dealloc,                     /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    0,                                          /* tp_repr */
+    0,                                          /* tp_as_number */
+    &mb_as_sequence,                            /* tp_as_sequence */
+#if PY_MAJOR_VERSION < 3
+    0,                                          /* tp_as_mapping */
+#else
+    &mb_as_mapping,                             /* tp_as_mapping */
+#endif
+    0,                                          /* tp_hash */
+    0,                                          /* tp_call */
+#if PY_MAJOR_VERSION < 3
+    (reprfunc)mb_str,                           /* tp_str */
+#else
+    0,                                          /* tp_str */
+#endif
+    PyObject_GenericGetAttr,                    /* tp_getattro */
+    0,                                          /* tp_setattro */
+    &mb_as_buffer,                              /* tp_as_buffer */
+    (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+        MINIBUF_TPFLAGS),                       /* tp_flags */
+    ffi_buffer_doc,                             /* tp_doc */
+    (traverseproc)mb_traverse,                  /* tp_traverse */
+    (inquiry)mb_clear,                          /* tp_clear */
+    (richcmpfunc)mb_richcompare,                /* tp_richcompare */
+    offsetof(MiniBufferObj, mb_weakreflist),    /* tp_weaklistoffset */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    0,                                          /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    0,                                          /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    0,                                          /* tp_dictoffset */
+    0,                                          /* tp_init */
+    0,                                          /* tp_alloc */
+    b_buffer_new,                               /* tp_new */
+    0,                                          /* tp_free */
+};
+
+static PyObject *minibuffer_new(char *data, Py_ssize_t size,
+                                PyObject *keepalive)
+{
+    MiniBufferObj *ob = PyObject_GC_New(MiniBufferObj, &MiniBuffer_Type);
+    if (ob != NULL) {
+        ob->mb_data = data;
+        ob->mb_size = size;
+        ob->mb_keepalive = keepalive; Py_INCREF(keepalive);
+        ob->mb_weakreflist = NULL;
+        PyObject_GC_Track(ob);
+    }
+    return (PyObject *)ob;
+}
diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h
new file mode 100644
index 0000000..66e2835
--- /dev/null
+++ b/c/misc_thread_common.h
@@ -0,0 +1,371 @@
+#ifndef WITH_THREAD
+# error "xxx no-thread configuration not tested, please report if you need that"
+#endif
+#include "pythread.h"
+
+
+struct cffi_tls_s {
+    /* The current thread's ThreadCanaryObj.  This is only non-null in
+       case cffi builds the thread state here.  It remains null if this
+       thread had already a thread state provided by CPython. */
+    struct thread_canary_s *local_thread_canary;
+
+#ifndef USE__THREAD
+    /* The saved errno.  If the C compiler supports '__thread', then
+       we use that instead. */
+    int saved_errno;
+#endif
+
+#ifdef MS_WIN32
+    /* The saved lasterror, on Windows. */
+    int saved_lasterror;
+#endif
+};
+
+static struct cffi_tls_s *get_cffi_tls(void);   /* in misc_thread_posix.h 
+                                                   or misc_win32.h */
+
+
+/* We try to keep the PyThreadState around in a thread not started by
+ * Python but where cffi callbacks occur.  If we didn't do that, then
+ * the standard logic in PyGILState_Ensure() and PyGILState_Release()
+ * would create a new PyThreadState and completely free it for every
+ * single call.  For some applications, this is a huge slow-down.
+ *
+ * As shown by issue #362, it is quite messy to do.  The current
+ * solution is to keep the PyThreadState alive by incrementing its
+ * 'gilstate_counter'.  We detect thread shut-down, and we put the
+ * PyThreadState inside a list of zombies (we can't free it
+ * immediately because we don't have the GIL at that point in time).
+ * We also detect other pieces of code (notably Py_Finalize()) which
+ * clear and free PyThreadStates under our feet, using ThreadCanaryObj.
+ */
+
+#define TLS_ZOM_LOCK() PyThread_acquire_lock(cffi_zombie_lock, WAIT_LOCK)
+#define TLS_ZOM_UNLOCK() PyThread_release_lock(cffi_zombie_lock)
+static PyThread_type_lock cffi_zombie_lock = NULL;
+
+
+/* A 'canary' object is created in a thread when there is a callback
+   invoked, and that thread has no PyThreadState so far.  It is an
+   object of reference count equal to 1, which is stored in the
+   PyThreadState->dict.  Two things can occur then:
+
+   1. The PyThreadState can be forcefully cleared by Py_Finalize().
+      Then thread_canary_dealloc() is called, and we have to cancel
+      the hacks we did to keep the PyThreadState alive.
+
+   2. The thread finishes.  In that case, we put the canary in a list
+      of zombies, and at some convenient time later when we have the
+      GIL, we free all PyThreadStates in the zombie list.
+
+   Some more fun comes from the fact that thread_canary_dealloc() can
+   be called at a point where the canary is in the zombie list already.
+   Also, the various pieces are freed at specific points in time, and
+   we must make sure not to access already-freed structures:
+
+    - the struct cffi_tls_s is valid until the thread shuts down, and
+      then it is freed by cffi_thread_shutdown().
+
+    - the canary is a normal Python object, but we have a borrowed
+      reference to it from cffi_tls_s.local_thread_canary.
+ */
+
+typedef struct thread_canary_s {
+    PyObject_HEAD
+    struct thread_canary_s *zombie_prev, *zombie_next;
+    PyThreadState *tstate;
+    struct cffi_tls_s *tls;
+} ThreadCanaryObj;
+
+static PyTypeObject ThreadCanary_Type;    /* forward */
+static ThreadCanaryObj cffi_zombie_head;
+
+static void
+_thread_canary_detach_with_lock(ThreadCanaryObj *ob)
+{
+    /* must be called with both the GIL and TLS_ZOM_LOCK. */
+    ThreadCanaryObj *p, *n;
+    p = ob->zombie_prev;
+    n = ob->zombie_next;
+    p->zombie_next = n;
+    n->zombie_prev = p;
+    ob->zombie_prev = NULL;
+    ob->zombie_next = NULL;
+}
+
+static void
+thread_canary_dealloc(ThreadCanaryObj *ob)
+{
+    /* this ThreadCanaryObj is being freed: if it is in the zombie
+       chained list, remove it.  Thread-safety: 'zombie_next' amd
+       'local_thread_canary' accesses need to be protected with
+       the TLS_ZOM_LOCK.
+     */
+    TLS_ZOM_LOCK();
+    if (ob->zombie_next != NULL) {
+        //fprintf(stderr, "thread_canary_dealloc(%p): ZOMBIE\n", ob);
+        _thread_canary_detach_with_lock(ob);
+    }
+    else {
+        //fprintf(stderr, "thread_canary_dealloc(%p): not a zombie\n", ob);
+    }
+
+    if (ob->tls != NULL) {
+        //fprintf(stderr, "thread_canary_dealloc(%p): was local_thread_canary\n", ob);
+        assert(ob->tls->local_thread_canary == ob);
+        ob->tls->local_thread_canary = NULL;
+    }
+    TLS_ZOM_UNLOCK();
+
+    PyObject_Del((PyObject *)ob);
+}
+
+static void
+thread_canary_make_zombie(ThreadCanaryObj *ob)
+{
+    /* This must be called without the GIL, but with the TLS_ZOM_LOCK.
+       It must be called at most once for a given ThreadCanaryObj. */
+    ThreadCanaryObj *last;
+
+    //fprintf(stderr, "thread_canary_make_zombie(%p)\n", ob);
+    if (ob->zombie_next)
+        Py_FatalError("cffi: ThreadCanaryObj is already a zombie");
+    last = cffi_zombie_head.zombie_prev;
+    ob->zombie_next = &cffi_zombie_head;
+    ob->zombie_prev = last;
+    last->zombie_next = ob;
+    cffi_zombie_head.zombie_prev = ob;
+}
+
+static void
+thread_canary_free_zombies(void)
+{
+    /* This must be called with the GIL. */
+    if (cffi_zombie_head.zombie_next == &cffi_zombie_head)
+        return;    /* fast path */
+
+    while (1) {
+        ThreadCanaryObj *ob;
+        PyThreadState *tstate = NULL;
+
+        TLS_ZOM_LOCK();
+        ob = cffi_zombie_head.zombie_next;
+        if (ob != &cffi_zombie_head) {
+            tstate = ob->tstate;
+            //fprintf(stderr, "thread_canary_free_zombie(%p) tstate=%p\n", ob, tstate);
+            _thread_canary_detach_with_lock(ob);
+            if (tstate == NULL)
+                Py_FatalError("cffi: invalid ThreadCanaryObj->tstate");
+        }
+        TLS_ZOM_UNLOCK();
+
+        if (tstate == NULL)
+            break;
+        PyThreadState_Clear(tstate);  /* calls thread_canary_dealloc on 'ob',
+                                         but now ob->zombie_next == NULL. */
+        PyThreadState_Delete(tstate);
+        //fprintf(stderr, "thread_canary_free_zombie: cleared and deleted tstate=%p\n", tstate);
+    }
+    //fprintf(stderr, "thread_canary_free_zombie: end\n");
+}
+
+static void
+thread_canary_register(PyThreadState *tstate)
+{
+    /* called with the GIL; 'tstate' is the current PyThreadState. */
+    ThreadCanaryObj *canary;
+    PyObject *tdict;
+    struct cffi_tls_s *tls;
+    int err;
+
+    /* first free the zombies, if any */
+    thread_canary_free_zombies();
+
+    tls = get_cffi_tls();
+    if (tls == NULL)
+        goto ignore_error;
+
+    tdict = PyThreadState_GetDict();
+    if (tdict == NULL)
+        goto ignore_error;
+
+    canary = PyObject_New(ThreadCanaryObj, &ThreadCanary_Type);
+    //fprintf(stderr, "thread_canary_register(%p): tstate=%p tls=%p\n", canary, tstate, tls);
+    if (canary == NULL)
+        goto ignore_error;
+    canary->zombie_prev = NULL;
+    canary->zombie_next = NULL;
+    canary->tstate = tstate;
+    canary->tls = tls;
+
+    err = PyDict_SetItemString(tdict, "cffi.thread.canary", (PyObject *)canary);
+    Py_DECREF(canary);
+    if (err < 0)
+        goto ignore_error;
+
+    /* thread-safety: we have the GIL here, and 'tstate' is the one that
+       corresponds to our own thread.  We are allocating a new 'canary'
+       and setting it up for our own thread, both in 'tdict' (which owns
+       the reference) and in 'tls->local_thread_canary' (which doesn't). */
+    assert(Py_REFCNT(canary) == 1);
+    tls->local_thread_canary = canary;
+    tstate->gilstate_counter++;
+    /* ^^^ this means 'tstate' will never be automatically freed by
+           PyGILState_Release() */
+    return;
+
+ ignore_error:
+    PyErr_Clear();
+}
+
+static PyTypeObject ThreadCanary_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_cffi_backend.thread_canary",
+    sizeof(ThreadCanaryObj),
+    0,
+    (destructor)thread_canary_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 */
+    0,                                          /* tp_getattro */
+    0,                                          /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
+};
+
+static void init_cffi_tls_zombie(void)
+{
+    cffi_zombie_head.zombie_next = &cffi_zombie_head;
+    cffi_zombie_head.zombie_prev = &cffi_zombie_head;
+    cffi_zombie_lock = PyThread_allocate_lock();
+    if (cffi_zombie_lock == NULL)
+        PyErr_SetString(PyExc_SystemError, "can't allocate cffi_zombie_lock");
+}
+
+static void cffi_thread_shutdown(void *p)
+{
+    /* this function is called from misc_thread_posix or misc_win32
+       when a thread is about to end. */
+    struct cffi_tls_s *tls = (struct cffi_tls_s *)p;
+
+    /* thread-safety: this field 'local_thread_canary' can be reset
+       to NULL in parallel, protected by TLS_ZOM_LOCK. */
+    TLS_ZOM_LOCK();
+    if (tls->local_thread_canary != NULL) {
+        tls->local_thread_canary->tls = NULL;
+        thread_canary_make_zombie(tls->local_thread_canary);
+    }
+    TLS_ZOM_UNLOCK();
+    //fprintf(stderr, "thread_shutdown(%p)\n", tls);
+    free(tls);
+}
+
+/* USE__THREAD is defined by setup.py if it finds that it is
+   syntactically valid to use "__thread" with this C compiler. */
+#ifdef USE__THREAD
+
+static __thread int cffi_saved_errno = 0;
+static void save_errno_only(void) { cffi_saved_errno = errno; }
+static void restore_errno_only(void) { errno = cffi_saved_errno; }
+
+#else
+
+static void save_errno_only(void)
+{
+    int saved = errno;
+    struct cffi_tls_s *tls = get_cffi_tls();
+    if (tls != NULL)
+        tls->saved_errno = saved;
+}
+
+static void restore_errno_only(void)
+{
+    struct cffi_tls_s *tls = get_cffi_tls();
+    if (tls != NULL)
+        errno = tls->saved_errno;
+}
+
+#endif
+
+
+/* MESS.  We can't use PyThreadState_GET(), because that calls
+   PyThreadState_Get() which fails an assert if the result is NULL.
+   
+   * in Python 2.7 and <= 3.4, the variable _PyThreadState_Current
+     is directly available, so use that.
+
+   * in Python 3.5, the variable is available too, but it might be
+     the case that the headers don't define it (this changed in 3.5.1).
+     In case we're compiling with 3.5.x with x >= 1, we need to
+     manually define this variable.
+
+   * in Python >= 3.6 there is _PyThreadState_UncheckedGet().
+     It was added in 3.5.2 but should never be used in 3.5.x
+     because it is not available in 3.5.0 or 3.5.1.
+*/
+#if PY_VERSION_HEX >= 0x03050100 && PY_VERSION_HEX < 0x03060000
+PyAPI_DATA(void *volatile) _PyThreadState_Current;
+#endif
+
+static PyThreadState *get_current_ts(void)
+{
+#if PY_VERSION_HEX >= 0x03060000
+    return _PyThreadState_UncheckedGet();
+#elif defined(_Py_atomic_load_relaxed)
+    return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
+#else
+    return (PyThreadState*)_PyThreadState_Current;  /* assume atomic read */
+#endif
+}
+
+static PyGILState_STATE gil_ensure(void)
+{
+    /* Called at the start of a callback.  Replacement for
+       PyGILState_Ensure().
+    */
+    PyGILState_STATE result;
+    PyThreadState *ts = PyGILState_GetThisThreadState();
+
+    if (ts != NULL) {
+        ts->gilstate_counter++;
+        if (ts != get_current_ts()) {
+            /* common case: 'ts' is our non-current thread state and
+               we have to make it current and acquire the GIL */
+            PyEval_RestoreThread(ts);
+            return PyGILState_UNLOCKED;
+        }
+        else {
+            return PyGILState_LOCKED;
+        }
+    }
+    else {
+        /* no thread state here so far. */
+        result = PyGILState_Ensure();
+        assert(result == PyGILState_UNLOCKED);
+
+        ts = PyGILState_GetThisThreadState();
+        assert(ts != NULL);
+        assert(ts == get_current_ts());
+        assert(ts->gilstate_counter >= 1);
+
+        /* Use the ThreadCanary mechanism to keep 'ts' alive until the
+           thread really shuts down */
+        thread_canary_register(ts);
+
+        return result;
+    }
+}
+
+static void gil_release(PyGILState_STATE oldstate)
+{
+    PyGILState_Release(oldstate);
+}
diff --git a/c/misc_thread_posix.h b/c/misc_thread_posix.h
new file mode 100644
index 0000000..bcc0177
--- /dev/null
+++ b/c/misc_thread_posix.h
@@ -0,0 +1,49 @@
+/*
+  Logic for a better replacement of PyGILState_Ensure().
+
+  This version is ready to handle the case of a non-Python-started
+  thread in which we do a large number of calls to CFFI callbacks.  If
+  we were to rely on PyGILState_Ensure() for that, we would constantly
+  be creating and destroying PyThreadStates---it is slow, and
+  PyThreadState_Delete() will actually walk the list of all thread
+  states, making it O(n). :-(
+
+  This version only creates one PyThreadState object the first time we
+  see a given thread, and keep it alive until the thread is really
+  shut down, using a destructor on the tls key.
+*/
+
+#include <pthread.h>
+#include "misc_thread_common.h"
+
+
+static pthread_key_t cffi_tls_key;
+
+static void init_cffi_tls(void)
+{
+    if (pthread_key_create(&cffi_tls_key, &cffi_thread_shutdown) != 0)
+        PyErr_SetString(PyExc_OSError, "pthread_key_create() failed");
+}
+
+static struct cffi_tls_s *_make_cffi_tls(void)
+{
+    void *p = calloc(1, sizeof(struct cffi_tls_s));
+    if (p == NULL)
+        return NULL;
+    if (pthread_setspecific(cffi_tls_key, p) != 0) {
+        free(p);
+        return NULL;
+    }
+    return p;
+}
+
+static struct cffi_tls_s *get_cffi_tls(void)
+{
+    void *p = pthread_getspecific(cffi_tls_key);
+    if (p == NULL)
+        p = _make_cffi_tls();
+    return (struct cffi_tls_s *)p;
+}
+
+#define save_errno      save_errno_only
+#define restore_errno   restore_errno_only
diff --git a/c/misc_win32.h b/c/misc_win32.h
new file mode 100644
index 0000000..07b76c1
--- /dev/null
+++ b/c/misc_win32.h
@@ -0,0 +1,241 @@
+#include <malloc.h>   /* for alloca() */
+
+
+/************************************************************/
+/* errno and GetLastError support */
+
+#include "misc_thread_common.h"
+
+static DWORD cffi_tls_index = TLS_OUT_OF_INDEXES;
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,
+                    DWORD     reason_for_call,
+                    LPVOID    reserved)
+{
+    LPVOID p;
+
+    switch (reason_for_call) {
+
+    case DLL_THREAD_DETACH:
+        if (cffi_tls_index != TLS_OUT_OF_INDEXES) {
+            p = TlsGetValue(cffi_tls_index);
+            if (p != NULL) {
+                TlsSetValue(cffi_tls_index, NULL);
+                cffi_thread_shutdown(p);
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+    return TRUE;
+}
+
+static void init_cffi_tls(void)
+{
+    if (cffi_tls_index == TLS_OUT_OF_INDEXES) {
+        cffi_tls_index = TlsAlloc();
+        if (cffi_tls_index == TLS_OUT_OF_INDEXES)
+            PyErr_SetString(PyExc_WindowsError, "TlsAlloc() failed");
+    }
+}
+
+static struct cffi_tls_s *get_cffi_tls(void)
+{
+    LPVOID p = TlsGetValue(cffi_tls_index);
+
+    if (p == NULL) {
+        p = malloc(sizeof(struct cffi_tls_s));
+        if (p == NULL)
+            return NULL;
+        memset(p, 0, sizeof(struct cffi_tls_s));
+        TlsSetValue(cffi_tls_index, p);
+    }
+    return (struct cffi_tls_s *)p;
+}
+
+#ifdef USE__THREAD
+# error "unexpected USE__THREAD on Windows"
+#endif
+
+static void save_errno(void)
+{
+    int current_err = errno;
+    int current_lasterr = GetLastError();
+    struct cffi_tls_s *p = get_cffi_tls();
+    if (p != NULL) {
+        p->saved_errno = current_err;
+        p->saved_lasterror = current_lasterr;
+    }
+    /* else: cannot report the error */
+}
+
+static void restore_errno(void)
+{
+    struct cffi_tls_s *p = get_cffi_tls();
+    if (p != NULL) {
+        SetLastError(p->saved_lasterror);
+        errno = p->saved_errno;
+    }
+    /* else: cannot report the error */
+}
+
+/************************************************************/
+
+
+#if PY_MAJOR_VERSION >= 3
+static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    int err = -1;
+    int len;
+    WCHAR *s_buf = NULL; /* Free via LocalFree */
+    PyObject *v, *message;
+    static char *keywords[] = {"code", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err))
+        return NULL;
+
+    if (err == -1) {
+        struct cffi_tls_s *p = get_cffi_tls();
+        if (p == NULL)
+            return PyErr_NoMemory();
+        err = p->saved_lasterror;
+    }
+
+    len = FormatMessageW(
+        /* Error API error */
+        FORMAT_MESSAGE_ALLOCATE_BUFFER |
+        FORMAT_MESSAGE_FROM_SYSTEM |
+        FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL,           /* no message source */
+        err,
+        MAKELANGID(LANG_NEUTRAL,
+        SUBLANG_DEFAULT), /* Default language */
+        (LPWSTR) &s_buf,
+        0,              /* size not used */
+        NULL);          /* no args */
+    if (len==0) {
+        /* Only seen this in out of mem situations */
+        message = PyUnicode_FromFormat("Windows Error 0x%X", err);
+    } else {
+        /* remove trailing cr/lf and dots */
+        while (len > 0 && (s_buf[len-1] <= L' ' || s_buf[len-1] == L'.'))
+            s_buf[--len] = L'\0';
+        message = PyUnicode_FromWideChar(s_buf, len);
+    }
+    if (message != NULL)
+        v = Py_BuildValue("(iO)", err, message);
+    else
+        v = NULL;
+    LocalFree(s_buf);
+    return v;
+}
+#else
+static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    int err = -1;
+    int len;
+    char *s;
+    char *s_buf = NULL; /* Free via LocalFree */
+    char s_small_buf[40]; /* Room for "Windows Error 0xFFFFFFFFFFFFFFFF" */
+    PyObject *v;
+    static char *keywords[] = {"code", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err))
+        return NULL;
+
+    if (err == -1) {
+        struct cffi_tls_s *p = get_cffi_tls();
+        if (p == NULL)
+            return PyErr_NoMemory();
+        err = p->saved_lasterror;
+    }
+
+    len = FormatMessage(
+        /* Error API error */
+        FORMAT_MESSAGE_ALLOCATE_BUFFER |
+        FORMAT_MESSAGE_FROM_SYSTEM |
+        FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL,           /* no message source */
+        err,
+        MAKELANGID(LANG_NEUTRAL,
+        SUBLANG_DEFAULT), /* Default language */
+        (LPTSTR) &s_buf,
+        0,              /* size not used */
+        NULL);          /* no args */
+    if (len==0) {
+        /* Only seen this in out of mem situations */
+        sprintf(s_small_buf, "Windows Error 0x%X", err);
+        s = s_small_buf;
+        s_buf = NULL;
+    } else {
+        s = s_buf;
+        /* remove trailing cr/lf and dots */
+        while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.'))
+            s[--len] = '\0';
+    }
+    v = Py_BuildValue("(is)", err, s);
+    LocalFree(s_buf);
+    return v;
+}
+#endif
+
+
+/************************************************************/
+/* Emulate dlopen()&co. from the Windows API */
+
+#define RTLD_LAZY   0
+#define RTLD_NOW    0
+#define RTLD_GLOBAL 0
+#define RTLD_LOCAL  0
+
+static void *dlopen(const char *filename, int flag)
+{
+    return (void *)LoadLibraryA(filename);
+}
+
+static void *dlopenW(const wchar_t *filename)
+{
+    return (void *)LoadLibraryW(filename);
+}
+
+static void *dlsym(void *handle, const char *symbol)
+{
+    void *address = GetProcAddress((HMODULE)handle, symbol);
+#ifndef MS_WIN64
+    if (!address) {
+        /* If 'symbol' is not found, then try '_symbol@N' for N in
+           (0, 4, 8, 12, ..., 124).  Unlike ctypes, we try to do that
+           for any symbol, although in theory it should only be done
+           for __stdcall functions.
+        */
+        int i;
+        char *mangled_name = alloca(1 + strlen(symbol) + 1 + 3 + 1);
+        if (!mangled_name)
+            return NULL;
+        for (i = 0; i < 32; i++) {
+            sprintf(mangled_name, "_%s@%d", symbol, i * 4);
+            address = GetProcAddress((HMODULE)handle, mangled_name);
+            if (address)
+                break;
+        }
+    }
+#endif
+    return address;
+}
+
+static int dlclose(void *handle)
+{
+    return FreeLibrary((HMODULE)handle) ? 0 : -1;
+}
+
+static const char *dlerror(void)
+{
+    static char buf[32];
+    DWORD dw = GetLastError(); 
+    if (dw == 0)
+        return NULL;
+    sprintf(buf, "error 0x%x", (unsigned int)dw);
+    return buf;
+}
diff --git a/c/parse_c_type.c b/c/parse_c_type.c
new file mode 100644
index 0000000..698ef64
--- /dev/null
+++ b/c/parse_c_type.c
@@ -0,0 +1,847 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#define _CFFI_INTERNAL
+#include "../cffi/parse_c_type.h"
+
+
+enum token_e {
+    TOK_STAR='*',
+    TOK_OPEN_PAREN='(',
+    TOK_CLOSE_PAREN=')',
+    TOK_OPEN_BRACKET='[',
+    TOK_CLOSE_BRACKET=']',
+    TOK_COMMA=',',
+
+    TOK_START=256,
+    TOK_END,
+    TOK_ERROR,
+    TOK_IDENTIFIER,
+    TOK_INTEGER,
+    TOK_DOTDOTDOT,
+
+    /* keywords */
+    TOK__BOOL,
+    TOK_CHAR,
+    TOK__COMPLEX,
+    TOK_CONST,
+    TOK_DOUBLE,
+    TOK_ENUM,
+    TOK_FLOAT,
+    //TOK__IMAGINARY,
+    TOK_INT,
+    TOK_LONG,
+    TOK_SHORT,
+    TOK_SIGNED,
+    TOK_STRUCT,
+    TOK_UNION,
+    TOK_UNSIGNED,
+    TOK_VOID,
+    TOK_VOLATILE,
+
+    TOK_CDECL,
+    TOK_STDCALL,
+};
+
+typedef struct {
+    struct _cffi_parse_info_s *info;
+    const char *input, *p;
+    size_t size;              // the next token is at 'p' and of length 'size'
+    enum token_e kind;
+    _cffi_opcode_t *output;
+    size_t output_index;
+} token_t;
+
+static int is_space(char x)
+{
+    return (x == ' ' || x == '\f' || x == '\n' || x == '\r' ||
+            x == '\t' || x == '\v');
+}
+
+static int is_ident_first(char x)
+{
+    return (('A' <= x && x <= 'Z') || ('a' <= x && x <= 'z') || x == '_' ||
+            x == '$');   /* '$' in names is supported here, for the struct
+                            names invented by cparser */
+}
+
+static int is_digit(char x)
+{
+    return ('0' <= x && x <= '9');
+}
+
+static int is_hex_digit(char x)
+{
+    return (('0' <= x && x <= '9') ||
+            ('A' <= x && x <= 'F') ||
+            ('a' <= x && x <= 'f'));
+}
+
+static int is_ident_next(char x)
+{
+    return (is_ident_first(x) || is_digit(x));
+}
+
+static char get_following_char(token_t *tok)
+{
+    const char *p = tok->p + tok->size;
+    if (tok->kind == TOK_ERROR)
+        return 0;
+    while (is_space(*p))
+        p++;
+    return *p;
+}
+
+static int number_of_commas(token_t *tok)
+{
+    const char *p = tok->p;
+    int result = 0;
+    int nesting = 0;
+    while (1) {
+        switch (*p++) {
+        case ',': result += !nesting; break;
+        case '(': nesting++; break;
+        case ')': if ((--nesting) < 0) return result; break;
+        case 0:   return result;
+        default:  break;
+        }
+    }
+}
+
+static void next_token(token_t *tok)
+{
+    const char *p = tok->p + tok->size;
+    if (tok->kind == TOK_ERROR)
+        return;
+    while (!is_ident_first(*p)) {
+        if (is_space(*p)) {
+            p++;
+        }
+        else if (is_digit(*p)) {
+            tok->kind = TOK_INTEGER;
+            tok->p = p;
+            tok->size = 1;
+            if (p[1] == 'x' || p[1] == 'X')
+                tok->size = 2;
+            while (is_hex_digit(p[tok->size]))
+                tok->size++;
+            return;
+        }
+        else if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
+            tok->kind = TOK_DOTDOTDOT;
+            tok->p = p;
+            tok->size = 3;
+            return;
+        }
+        else if (*p) {
+            tok->kind = *p;
+            tok->p = p;
+            tok->size = 1;
+            return;
+        }
+        else {
+            tok->kind = TOK_END;
+            tok->p = p;
+            tok->size = 0;
+            return;
+        }
+    }
+    tok->kind = TOK_IDENTIFIER;
+    tok->p = p;
+    tok->size = 1;
+    while (is_ident_next(p[tok->size]))
+        tok->size++;
+
+    switch (*p) {
+    case '_':
+        if (tok->size == 5 && !memcmp(p, "_Bool", 5))  tok->kind = TOK__BOOL;
+        if (tok->size == 7 && !memcmp(p,"__cdecl",7))  tok->kind = TOK_CDECL;
+        if (tok->size == 9 && !memcmp(p,"__stdcall",9))tok->kind = TOK_STDCALL;
+        if (tok->size == 8 && !memcmp(p,"_Complex",8)) tok->kind = TOK__COMPLEX;
+        break;
+    case 'c':
+        if (tok->size == 4 && !memcmp(p, "char", 4))   tok->kind = TOK_CHAR;
+        if (tok->size == 5 && !memcmp(p, "const", 5))  tok->kind = TOK_CONST;
+        break;
+    case 'd':
+        if (tok->size == 6 && !memcmp(p, "double", 6)) tok->kind = TOK_DOUBLE;
+        break;
+    case 'e':
+        if (tok->size == 4 && !memcmp(p, "enum", 4))   tok->kind = TOK_ENUM;
+        break;
+    case 'f':
+        if (tok->size == 5 && !memcmp(p, "float", 5))  tok->kind = TOK_FLOAT;
+        break;
+    case 'i':
+        if (tok->size == 3 && !memcmp(p, "int", 3))    tok->kind = TOK_INT;
+        break;
+    case 'l':
+        if (tok->size == 4 && !memcmp(p, "long", 4))   tok->kind = TOK_LONG;
+        break;
+    case 's':
+        if (tok->size == 5 && !memcmp(p, "short", 5))  tok->kind = TOK_SHORT;
+        if (tok->size == 6 && !memcmp(p, "signed", 6)) tok->kind = TOK_SIGNED;
+        if (tok->size == 6 && !memcmp(p, "struct", 6)) tok->kind = TOK_STRUCT;
+        break;
+    case 'u':
+        if (tok->size == 5 && !memcmp(p, "union", 5))  tok->kind = TOK_UNION;
+        if (tok->size == 8 && !memcmp(p,"unsigned",8)) tok->kind = TOK_UNSIGNED;
+        break;
+    case 'v':
+        if (tok->size == 4 && !memcmp(p, "void", 4))   tok->kind = TOK_VOID;
+        if (tok->size == 8 && !memcmp(p,"volatile",8)) tok->kind = TOK_VOLATILE;
+        break;
+    }
+}
+
+static int parse_error(token_t *tok, const char *msg)
+{
+    if (tok->kind != TOK_ERROR) {
+        tok->kind = TOK_ERROR;
+        tok->info->error_location = tok->p - tok->input;
+        tok->info->error_message = msg;
+    }
+    return -1;
+}
+
+static int write_ds(token_t *tok, _cffi_opcode_t ds)
+{
+    size_t index = tok->output_index;
+    if (index >= tok->info->output_size) {
+        parse_error(tok, "internal type complexity limit reached");
+        return -1;
+    }
+    tok->output[index] = ds;
+    tok->output_index = index + 1;
+    return index;
+}
+
+#define MAX_SSIZE_T  (((size_t)-1) >> 1)
+
+static int parse_complete(token_t *tok);
+static const char *get_common_type(const char *search, size_t search_len);
+static int parse_common_type_replacement(token_t *tok, const char *replacement);
+
+static int parse_sequel(token_t *tok, int outer)
+{
+    /* Emit opcodes for the "sequel", which is the optional part of a
+       type declaration that follows the type name, i.e. everything
+       with '*', '[ ]', '( )'.  Returns the entry point index pointing
+       the innermost opcode (the one that corresponds to the complete
+       type).  The 'outer' argument is the index of the opcode outside
+       this "sequel".
+     */
+    int check_for_grouping, abi=0;
+    _cffi_opcode_t result, *p_current;
+
+ header:
+    switch (tok->kind) {
+    case TOK_STAR:
+        outer = write_ds(tok, _CFFI_OP(_CFFI_OP_POINTER, outer));
+        next_token(tok);
+        goto header;
+    case TOK_CONST:
+        /* ignored for now */
+        next_token(tok);
+        goto header;
+    case TOK_VOLATILE:
+        /* ignored for now */
+        next_token(tok);
+        goto header;
+    case TOK_CDECL:
+    case TOK_STDCALL:
+        /* must be in a function; checked below */
+        abi = tok->kind;
+        next_token(tok);
+        goto header;
+    default:
+        break;
+    }
+
+    check_for_grouping = 1;
+    if (tok->kind == TOK_IDENTIFIER) {
+        next_token(tok);    /* skip a potential variable name */
+        check_for_grouping = 0;
+    }
+
+    result = 0;
+    p_current = &result;
+
+    while (tok->kind == TOK_OPEN_PAREN) {
+        next_token(tok);
+
+        if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) {
+            abi = tok->kind;
+            next_token(tok);
+        }
+
+        if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR ||
+                                            tok->kind == TOK_CONST ||
+                                            tok->kind == TOK_VOLATILE ||
+                                            tok->kind == TOK_OPEN_BRACKET)) {
+            /* just parentheses for grouping.  Use a OP_NOOP to simplify */
+            int x;
+            assert(p_current == &result);
+            x = tok->output_index;
+            p_current = tok->output + x;
+
+            write_ds(tok, _CFFI_OP(_CFFI_OP_NOOP, 0));
+
+            x = parse_sequel(tok, x);
+            result = _CFFI_OP(_CFFI_GETOP(0), x);
+        }
+        else {
+            /* function type */
+            int arg_total, base_index, arg_next, flags=0;
+
+            if (abi == TOK_STDCALL) {
+                flags = 2;
+                /* note that an ellipsis below will overwrite this flags,
+                   which is the goal: variadic functions are always cdecl */
+            }
+            abi = 0;
+
+            if (tok->kind == TOK_VOID && get_following_char(tok) == ')') {
+                next_token(tok);
+            }
+
+            /* (over-)estimate 'arg_total'.  May return 1 when it is really 0 */
+            arg_total = number_of_commas(tok) + 1;
+
+            *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index);
+            p_current = tok->output + tok->output_index;
+
+            base_index = write_ds(tok, _CFFI_OP(_CFFI_OP_FUNCTION, 0));
+            if (base_index < 0)
+                return -1;
+            /* reserve (arg_total + 1) slots for the arguments and the
+               final FUNCTION_END */
+            for (arg_next = 0; arg_next <= arg_total; arg_next++)
+                if (write_ds(tok, _CFFI_OP(0, 0)) < 0)
+                    return -1;
+
+            arg_next = base_index + 1;
+
+            if (tok->kind != TOK_CLOSE_PAREN) {
+                while (1) {
+                    int arg;
+                    _cffi_opcode_t oarg;
+
+                    if (tok->kind == TOK_DOTDOTDOT) {
+                        flags = 1;   /* ellipsis */
+                        next_token(tok);
+                        break;
+                    }
+                    arg = parse_complete(tok);
+                    switch (_CFFI_GETOP(tok->output[arg])) {
+                    case _CFFI_OP_ARRAY:
+                    case _CFFI_OP_OPEN_ARRAY:
+                        arg = _CFFI_GETARG(tok->output[arg]);
+                        /* fall-through */
+                    case _CFFI_OP_FUNCTION:
+                        oarg = _CFFI_OP(_CFFI_OP_POINTER, arg);
+                        break;
+                    default:
+                        oarg = _CFFI_OP(_CFFI_OP_NOOP, arg);
+                        break;
+                    }
+                    assert(arg_next - base_index <= arg_total);
+                    tok->output[arg_next++] = oarg;
+                    if (tok->kind != TOK_COMMA)
+                        break;
+                    next_token(tok);
+                }
+            }
+            tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags);
+        }
+
+        if (tok->kind != TOK_CLOSE_PAREN)
+            return parse_error(tok, "expected ')'");
+        next_token(tok);
+    }
+
+    if (abi != 0)
+        return parse_error(tok, "expected '('");
+
+    while (tok->kind == TOK_OPEN_BRACKET) {
+        *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index);
+        p_current = tok->output + tok->output_index;
+
+        next_token(tok);
+        if (tok->kind != TOK_CLOSE_BRACKET) {
+            size_t length;
+            int gindex;
+            char *endptr;
+
+            switch (tok->kind) {
+
+            case TOK_INTEGER:
+                errno = 0;
+                if (sizeof(length) > sizeof(unsigned long)) {
+#ifdef MS_WIN32
+# ifdef _WIN64
+                    length = _strtoui64(tok->p, &endptr, 0);
+# else
+                    abort();  /* unreachable */
+# endif
+#else
+                    length = strtoull(tok->p, &endptr, 0);
+#endif
+                }
+                else
+                    length = strtoul(tok->p, &endptr, 0);
+                if (endptr != tok->p + tok->size)
+                    return parse_error(tok, "invalid number");
+                if (errno == ERANGE || length > MAX_SSIZE_T)
+                    return parse_error(tok, "number too large");
+                break;
+
+            case TOK_IDENTIFIER:
+                gindex = search_in_globals(tok->info->ctx, tok->p, tok->size);
+                if (gindex >= 0) {
+                    const struct _cffi_global_s *g;
+                    g = &tok->info->ctx->globals[gindex];
+                    if (_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT_INT ||
+                        _CFFI_GETOP(g->type_op) == _CFFI_OP_ENUM) {
+                        int neg;
+                        struct _cffi_getconst_s gc;
+                        gc.ctx = tok->info->ctx;
+                        gc.gindex = gindex;
+                        neg = ((int(*)(struct _cffi_getconst_s*))g->address)
+                            (&gc);
+                        if (neg == 0 && gc.value > MAX_SSIZE_T)
+                            return parse_error(tok,
+                                               "integer constant too large");
+                        if (neg == 0 || gc.value == 0) {
+                            length = (size_t)gc.value;
+                            break;
+                        }
+                        if (neg != 1)
+                            return parse_error(tok, "disagreement about"
+                                               " this constant's value");
+                    }
+                }
+                /* fall-through to the default case */
+            default:
+                return parse_error(tok, "expected a positive integer constant");
+            }
+
+            next_token(tok);
+
+            write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, 0));
+            write_ds(tok, (_cffi_opcode_t)length);
+        }
+        else
+            write_ds(tok, _CFFI_OP(_CFFI_OP_OPEN_ARRAY, 0));
+
+        if (tok->kind != TOK_CLOSE_BRACKET)
+            return parse_error(tok, "expected ']'");
+        next_token(tok);
+    }
+
+    *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), outer);
+    return _CFFI_GETARG(result);
+}
+
+static int search_sorted(const char *const *base,
+                         size_t item_size, int array_len,
+                         const char *search, size_t search_len)
+{
+    int left = 0, right = array_len;
+    const char *baseptr = (const char *)base;
+
+    while (left < right) {
+        int middle = (left + right) / 2;
+        const char *src = *(const char *const *)(baseptr + middle * item_size);
+        int diff = strncmp(src, search, search_len);
+        if (diff == 0 && src[search_len] == '\0')
+            return middle;
+        else if (diff >= 0)
+            right = middle;
+        else
+            left = middle + 1;
+    }
+    return -1;
+}
+
+#define MAKE_SEARCH_FUNC(FIELD)                                         \
+  static                                                                \
+  int search_in_##FIELD(const struct _cffi_type_context_s *ctx,         \
+                        const char *search, size_t search_len)          \
+  {                                                                     \
+      return search_sorted(&ctx->FIELD->name, sizeof(*ctx->FIELD),      \
+                           ctx->num_##FIELD, search, search_len);       \
+  }
+
+MAKE_SEARCH_FUNC(globals)
+MAKE_SEARCH_FUNC(struct_unions)
+MAKE_SEARCH_FUNC(typenames)
+MAKE_SEARCH_FUNC(enums)
+
+#undef MAKE_SEARCH_FUNC
+
+
+static
+int search_standard_typename(const char *p, size_t size)
+{
+    if (size < 6 || p[size-2] != '_' || p[size-1] != 't')
+        return -1;
+
+    switch (p[4]) {
+
+    case '1':
+        if (size == 8 && !memcmp(p, "uint16", 6)) return _CFFI_PRIM_UINT16;
+        if (size == 8 && !memcmp(p, "char16", 6)) return _CFFI_PRIM_CHAR16;
+        break;
+
+    case '2':
+        if (size == 7 && !memcmp(p, "int32", 5)) return _CFFI_PRIM_INT32;
+        break;
+
+    case '3':
+        if (size == 8 && !memcmp(p, "uint32", 6)) return _CFFI_PRIM_UINT32;
+        if (size == 8 && !memcmp(p, "char32", 6)) return _CFFI_PRIM_CHAR32;
+        break;
+
+    case '4':
+        if (size == 7 && !memcmp(p, "int64", 5)) return _CFFI_PRIM_INT64;
+        break;
+
+    case '6':
+        if (size == 8 && !memcmp(p, "uint64", 6)) return _CFFI_PRIM_UINT64;
+        if (size == 7 && !memcmp(p, "int16", 5)) return _CFFI_PRIM_INT16;
+        break;
+
+    case '8':
+        if (size == 7 && !memcmp(p, "uint8", 5)) return _CFFI_PRIM_UINT8;
+        break;
+
+    case 'a':
+        if (size == 8 && !memcmp(p, "intmax", 6)) return _CFFI_PRIM_INTMAX;
+        break;
+
+    case 'e':
+        if (size == 7 && !memcmp(p, "ssize", 5)) return _CFFI_PRIM_SSIZE;
+        break;
+
+    case 'f':
+        if (size == 11 && !memcmp(p, "int_fast8",   9)) return _CFFI_PRIM_INT_FAST8;
+        if (size == 12 && !memcmp(p, "int_fast16", 10)) return _CFFI_PRIM_INT_FAST16;
+        if (size == 12 && !memcmp(p, "int_fast32", 10)) return _CFFI_PRIM_INT_FAST32;
+        if (size == 12 && !memcmp(p, "int_fast64", 10)) return _CFFI_PRIM_INT_FAST64;
+        break;
+
+    case 'i':
+        if (size == 9 && !memcmp(p, "ptrdiff", 7)) return _CFFI_PRIM_PTRDIFF;
+        break;
+
+    case 'l':
+        if (size == 12 && !memcmp(p, "int_least8",  10)) return _CFFI_PRIM_INT_LEAST8;
+        if (size == 13 && !memcmp(p, "int_least16", 11)) return _CFFI_PRIM_INT_LEAST16;
+        if (size == 13 && !memcmp(p, "int_least32", 11)) return _CFFI_PRIM_INT_LEAST32;
+        if (size == 13 && !memcmp(p, "int_least64", 11)) return _CFFI_PRIM_INT_LEAST64;
+        break;
+
+    case 'm':
+        if (size == 9 && !memcmp(p, "uintmax", 7)) return _CFFI_PRIM_UINTMAX;
+        break;
+
+    case 'p':
+        if (size == 9 && !memcmp(p, "uintptr", 7)) return _CFFI_PRIM_UINTPTR;
+        break;
+
+    case 'r':
+        if (size == 7 && !memcmp(p, "wchar", 5)) return _CFFI_PRIM_WCHAR;
+        break;
+
+    case 't':
+        if (size == 8 && !memcmp(p, "intptr", 6)) return _CFFI_PRIM_INTPTR;
+        break;
+
+    case '_':
+        if (size == 6 && !memcmp(p, "size", 4)) return _CFFI_PRIM_SIZE;
+        if (size == 6 && !memcmp(p, "int8", 4)) return _CFFI_PRIM_INT8;
+        if (size >= 12) {
+            switch (p[10]) {
+            case '1':
+                if (size == 14 && !memcmp(p, "uint_least16", 12)) return _CFFI_PRIM_UINT_LEAST16;
+                break;
+            case '2':
+                if (size == 13 && !memcmp(p, "uint_fast32", 11)) return _CFFI_PRIM_UINT_FAST32;
+                break;
+            case '3':
+                if (size == 14 && !memcmp(p, "uint_least32", 12)) return _CFFI_PRIM_UINT_LEAST32;
+                break;
+            case '4':
+                if (size == 13 && !memcmp(p, "uint_fast64", 11)) return _CFFI_PRIM_UINT_FAST64;
+                break;
+            case '6':
+                if (size == 14 && !memcmp(p, "uint_least64", 12)) return _CFFI_PRIM_UINT_LEAST64;
+                if (size == 13 && !memcmp(p, "uint_fast16", 11)) return _CFFI_PRIM_UINT_FAST16;
+                break;
+            case '8':
+                if (size == 13 && !memcmp(p, "uint_least8", 11)) return _CFFI_PRIM_UINT_LEAST8;
+                break;
+            case '_':
+                if (size == 12 && !memcmp(p, "uint_fast8", 10)) return _CFFI_PRIM_UINT_FAST8;
+                break;
+            default:
+                break;
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+    return -1;
+}
+
+
+static int parse_complete(token_t *tok)
+{
+    unsigned int t0;
+    _cffi_opcode_t t1;
+    _cffi_opcode_t t1complex;
+    int modifiers_length, modifiers_sign;
+
+ qualifiers:
+    switch (tok->kind) {
+    case TOK_CONST:
+        /* ignored for now */
+        next_token(tok);
+        goto qualifiers;
+    case TOK_VOLATILE:
+        /* ignored for now */
+        next_token(tok);
+        goto qualifiers;
+    default:
+        ;
+    }
+
+    modifiers_length = 0;
+    modifiers_sign = 0;
+ modifiers:
+    switch (tok->kind) {
+
+    case TOK_SHORT:
+        if (modifiers_length != 0)
+            return parse_error(tok, "'short' after another 'short' or 'long'");
+        modifiers_length--;
+        next_token(tok);
+        goto modifiers;
+
+    case TOK_LONG:
+        if (modifiers_length < 0)
+            return parse_error(tok, "'long' after 'short'");
+        if (modifiers_length >= 2)
+            return parse_error(tok, "'long long long' is too long");
+        modifiers_length++;
+        next_token(tok);
+        goto modifiers;
+
+    case TOK_SIGNED:
+        if (modifiers_sign)
+            return parse_error(tok, "multiple 'signed' or 'unsigned'");
+        modifiers_sign++;
+        next_token(tok);
+        goto modifiers;
+
+    case TOK_UNSIGNED:
+        if (modifiers_sign)
+            return parse_error(tok, "multiple 'signed' or 'unsigned'");
+        modifiers_sign--;
+        next_token(tok);
+        goto modifiers;
+
+    default:
+        break;
+    }
+
+    t1complex = 0;
+
+    if (modifiers_length || modifiers_sign) {
+
+        switch (tok->kind) {
+
+        case TOK_VOID:
+        case TOK__BOOL:
+        case TOK_FLOAT:
+        case TOK_STRUCT:
+        case TOK_UNION:
+        case TOK_ENUM:
+        case TOK__COMPLEX:
+            return parse_error(tok, "invalid combination of types");
+
+        case TOK_DOUBLE:
+            if (modifiers_sign != 0 || modifiers_length != 1)
+                return parse_error(tok, "invalid combination of types");
+            next_token(tok);
+            t0 = _CFFI_PRIM_LONGDOUBLE;
+            break;
+
+        case TOK_CHAR:
+            if (modifiers_length != 0)
+                return parse_error(tok, "invalid combination of types");
+            modifiers_length = -2;
+            /* fall-through */
+        case TOK_INT:
+            next_token(tok);
+            /* fall-through */
+        default:
+            if (modifiers_sign >= 0)
+                switch (modifiers_length) {
+                case -2: t0 = _CFFI_PRIM_SCHAR; break;
+                case -1: t0 = _CFFI_PRIM_SHORT; break;
+                case 1:  t0 = _CFFI_PRIM_LONG; break;
+                case 2:  t0 = _CFFI_PRIM_LONGLONG; break;
+                default: t0 = _CFFI_PRIM_INT; break;
+                }
+            else
+                switch (modifiers_length) {
+                case -2: t0 = _CFFI_PRIM_UCHAR; break;
+                case -1: t0 = _CFFI_PRIM_USHORT; break;
+                case 1:  t0 = _CFFI_PRIM_ULONG; break;
+                case 2:  t0 = _CFFI_PRIM_ULONGLONG; break;
+                default: t0 = _CFFI_PRIM_UINT; break;
+                }
+        }
+        t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, t0);
+    }
+    else {
+        switch (tok->kind) {
+        case TOK_INT:
+            t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT);
+            break;
+        case TOK_CHAR:
+            t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_CHAR);
+            break;
+        case TOK_VOID:
+            t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_VOID);
+            break;
+        case TOK__BOOL:
+            t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_BOOL);
+            break;
+        case TOK_FLOAT:
+            t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOAT);
+            t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOATCOMPLEX);
+            break;
+        case TOK_DOUBLE:
+            t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLE);
+            t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLECOMPLEX);
+            break;
+        case TOK_IDENTIFIER:
+        {
+            const char *replacement;
+            int n = search_in_typenames(tok->info->ctx, tok->p, tok->size);
+            if (n >= 0) {
+                t1 = _CFFI_OP(_CFFI_OP_TYPENAME, n);
+                break;
+            }
+            n = search_standard_typename(tok->p, tok->size);
+            if (n >= 0) {
+                t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, n);
+                break;
+            }
+            replacement = get_common_type(tok->p, tok->size);
+            if (replacement != NULL) {
+                n = parse_common_type_replacement(tok, replacement);
+                if (n < 0)
+                    return parse_error(tok, "internal error, please report!");
+                t1 = _CFFI_OP(_CFFI_OP_NOOP, n);
+                break;
+            }
+            return parse_error(tok, "undefined type name");
+        }
+        case TOK_STRUCT:
+        case TOK_UNION:
+        {
+            int n, kind = tok->kind;
+            next_token(tok);
+            if (tok->kind != TOK_IDENTIFIER)
+                return parse_error(tok, "struct or union name expected");
+
+            n = search_in_struct_unions(tok->info->ctx, tok->p, tok->size);
+            if (n < 0) {
+                if (kind == TOK_STRUCT && tok->size == 8 &&
+                        !memcmp(tok->p, "_IO_FILE", 8))
+                    n = _CFFI__IO_FILE_STRUCT;
+                else
+                    return parse_error(tok, "undefined struct/union name");
+            }
+            else if (((tok->info->ctx->struct_unions[n].flags & _CFFI_F_UNION)
+                      != 0) ^ (kind == TOK_UNION))
+                return parse_error(tok, "wrong kind of tag: struct vs union");
+
+            t1 = _CFFI_OP(_CFFI_OP_STRUCT_UNION, n);
+            break;
+        }
+        case TOK_ENUM:
+        {
+            int n;
+            next_token(tok);
+            if (tok->kind != TOK_IDENTIFIER)
+                return parse_error(tok, "enum name expected");
+
+            n = search_in_enums(tok->info->ctx, tok->p, tok->size);
+            if (n < 0)
+                return parse_error(tok, "undefined enum name");
+
+            t1 = _CFFI_OP(_CFFI_OP_ENUM, n);
+            break;
+        }
+        default:
+            return parse_error(tok, "identifier expected");
+        }
+        next_token(tok);
+    }
+    if (tok->kind == TOK__COMPLEX)
+    {
+        if (t1complex == 0)
+            return parse_error(tok, "_Complex type combination unsupported");
+        t1 = t1complex;
+        next_token(tok);
+    }
+
+    return parse_sequel(tok, write_ds(tok, t1));
+}
+
+
+static
+int parse_c_type_from(struct _cffi_parse_info_s *info, size_t *output_index,
+                      const char *input)
+{
+    int result;
+    token_t token;
+
+    token.info = info;
+    token.kind = TOK_START;
+    token.input = input;
+    token.p = input;
+    token.size = 0;
+    token.output = info->output;
+    token.output_index = *output_index;
+
+    next_token(&token);
+    result = parse_complete(&token);
+
+    *output_index = token.output_index;
+    if (token.kind != TOK_END)
+        return parse_error(&token, "unexpected symbol");
+    return result;
+}
+
+static
+int parse_c_type(struct _cffi_parse_info_s *info, const char *input)
+{
+    size_t output_index = 0;
+    return parse_c_type_from(info, &output_index, input);
+}
+
+static
+int parse_common_type_replacement(token_t *tok, const char *replacement)
+{
+    return parse_c_type_from(tok->info, &tok->output_index, replacement);
+}
diff --git a/c/realize_c_type.c b/c/realize_c_type.c
new file mode 100644
index 0000000..082c488
--- /dev/null
+++ b/c/realize_c_type.c
@@ -0,0 +1,797 @@
+
+typedef struct {
+    struct _cffi_type_context_s ctx;   /* inlined substructure */
+    PyObject *types_dict;
+    PyObject *included_ffis;
+    PyObject *included_libs;
+    PyObject *_keepalive1;
+    PyObject *_keepalive2;
+} builder_c_t;
+
+
+static PyObject *all_primitives[_CFFI__NUM_PRIM];
+static CTypeDescrObject *g_ct_voidp, *g_ct_chararray;
+
+static PyObject *build_primitive_type(int num);   /* forward */
+
+#define primitive_in_range(num)   ((num) >= 0 && (num) < _CFFI__NUM_PRIM)
+#define get_primitive_type(num)                                 \
+    ((primitive_in_range(num) && all_primitives[num] != NULL) ? \
+        all_primitives[num] : build_primitive_type(num))
+
+static int init_global_types_dict(PyObject *ffi_type_dict)
+{
+    int err;
+    PyObject *ct_void, *ct_char, *ct2, *pnull;
+    /* XXX some leaks in case these functions fail, but well,
+       MemoryErrors during importing an extension module are kind
+       of bad anyway */
+
+    ct_void = get_primitive_type(_CFFI_PRIM_VOID);         // 'void'
+    if (ct_void == NULL)
+        return -1;
+
+    ct2 = new_pointer_type((CTypeDescrObject *)ct_void);   // 'void *'
+    if (ct2 == NULL)
+        return -1;
+    g_ct_voidp = (CTypeDescrObject *)ct2;
+
+    ct_char = get_primitive_type(_CFFI_PRIM_CHAR);         // 'char'
+    if (ct_char == NULL)
+        return -1;
+
+    ct2 = new_pointer_type((CTypeDescrObject *)ct_char);   // 'char *'
+    if (ct2 == NULL)
+        return -1;
+
+    ct2 = new_array_type((CTypeDescrObject *)ct2, -1);     // 'char[]'
+    if (ct2 == NULL)
+        return -1;
+    g_ct_chararray = (CTypeDescrObject *)ct2;
+
+    pnull = new_simple_cdata(NULL, g_ct_voidp);
+    if (pnull == NULL)
+        return -1;
+    err = PyDict_SetItemString(ffi_type_dict, "NULL", pnull);
+    Py_DECREF(pnull);
+    return err;
+}
+
+static void free_builder_c(builder_c_t *builder, int ctx_is_static)
+{
+    if (!ctx_is_static) {
+        size_t i;
+        const void *mem[] = {builder->ctx.types,
+                             builder->ctx.globals,
+                             builder->ctx.struct_unions,
+                             //builder->ctx.fields: allocated with struct_unions
+                             builder->ctx.enums,
+                             builder->ctx.typenames};
+        for (i = 0; i < sizeof(mem) / sizeof(*mem); i++) {
+            if (mem[i] != NULL)
+                PyMem_Free((void *)mem[i]);
+        }
+    }
+    Py_XDECREF(builder->included_ffis);
+    Py_XDECREF(builder->included_libs);
+    Py_XDECREF(builder->types_dict);
+    Py_XDECREF(builder->_keepalive1);
+    Py_XDECREF(builder->_keepalive2);
+}
+
+static int init_builder_c(builder_c_t *builder,
+                          const struct _cffi_type_context_s *ctx)
+{
+    PyObject *ldict = PyDict_New();
+    if (ldict == NULL)
+        return -1;
+
+    if (ctx)
+        builder->ctx = *ctx;
+    else
+        memset(&builder->ctx, 0, sizeof(builder->ctx));
+
+    builder->types_dict = ldict;
+    builder->included_ffis = NULL;
+    builder->included_libs = NULL;
+    builder->_keepalive1 = NULL;
+    builder->_keepalive2 = NULL;
+    return 0;
+}
+
+static PyObject *build_primitive_type(int num)
+{
+    /* XXX too many translations between here and new_primitive_type() */
+    static const char *primitive_name[] = {
+        NULL,
+        "_Bool",
+        "char",
+        "signed char",
+        "unsigned char",
+        "short",
+        "unsigned short",
+        "int",
+        "unsigned int",
+        "long",
+        "unsigned long",
+        "long long",
+        "unsigned long long",
+        "float",
+        "double",
+        "long double",
+        "wchar_t",
+        "int8_t",
+        "uint8_t",
+        "int16_t",
+        "uint16_t",
+        "int32_t",
+        "uint32_t",
+        "int64_t",
+        "uint64_t",
+        "intptr_t",
+        "uintptr_t",
+        "ptrdiff_t",
+        "size_t",
+        "ssize_t",
+        "int_least8_t",
+        "uint_least8_t",
+        "int_least16_t",
+        "uint_least16_t",
+        "int_least32_t",
+        "uint_least32_t",
+        "int_least64_t",
+        "uint_least64_t",
+        "int_fast8_t",
+        "uint_fast8_t",
+        "int_fast16_t",
+        "uint_fast16_t",
+        "int_fast32_t",
+        "uint_fast32_t",
+        "int_fast64_t",
+        "uint_fast64_t",
+        "intmax_t",
+        "uintmax_t",
+        "float _Complex",
+        "double _Complex",
+        "char16_t",
+        "char32_t",
+    };
+    PyObject *x;
+
+    assert(sizeof(primitive_name) == sizeof(*primitive_name) * _CFFI__NUM_PRIM);
+    if (num == _CFFI_PRIM_VOID) {
+        x = new_void_type();
+    }
+    else if (primitive_in_range(num) && primitive_name[num] != NULL) {
+        x = new_primitive_type(primitive_name[num]);
+    }
+    else if (num == _CFFI__UNKNOWN_PRIM) {
+        PyErr_SetString(FFIError, "primitive integer type with an unexpected "
+                        "size (or not an integer type at all)");
+        return NULL;
+    }
+    else if (num == _CFFI__UNKNOWN_FLOAT_PRIM) {
+        PyErr_SetString(FFIError, "primitive floating-point type with an "
+                        "unexpected size (or not a float type at all)");
+        return NULL;
+    }
+    else if (num == _CFFI__UNKNOWN_LONG_DOUBLE) {
+        PyErr_SetString(FFIError, "primitive floating-point type is "
+                        "'long double', not supported for now with "
+                        "the syntax 'typedef double... xxx;'");
+        return NULL;
+    }
+    else {
+        PyErr_Format(PyExc_NotImplementedError, "prim=%d", num);
+        return NULL;
+    }
+
+    all_primitives[num] = x;
+    return x;
+}
+
+static PyObject *realize_global_int(builder_c_t *builder, int gindex)
+{
+    int neg;
+    char got[64];
+    unsigned long long value;
+    struct _cffi_getconst_s gc;
+    const struct _cffi_global_s *g = &builder->ctx.globals[gindex];
+    gc.ctx = &builder->ctx;
+    gc.gindex = gindex;
+    /* note: we cast g->address to this function type; we do the same
+       in parse_c_type:parse_sequel() too.  Note that the called function
+       may be declared simply with "unsigned long long *" as argument,
+       which is fine as it is the first field in _cffi_getconst_s. */
+    assert(&gc.value == (unsigned long long *)&gc);
+    neg = ((int(*)(struct _cffi_getconst_s *))g->address)(&gc);
+    value = gc.value;
+
+    switch (neg) {
+
+    case 0:
+        if (value <= (unsigned long long)LONG_MAX)
+            return PyInt_FromLong((long)value);
+        else
+            return PyLong_FromUnsignedLongLong(value);
+
+    case 1:
+        if ((long long)value >= (long long)LONG_MIN)
+            return PyInt_FromLong((long)value);
+        else
+            return PyLong_FromLongLong((long long)value);
+
+    default:
+        break;
+    }
+    if (neg == 2)
+        sprintf(got, "%llu (0x%llx)", value, value);
+    else
+        sprintf(got, "%lld", (long long)value);
+    PyErr_Format(FFIError, "the C compiler says '%.200s' is equal to %s, "
+                           "but the cdef disagrees", g->name, got);
+    return NULL;
+}
+
+static CTypeDescrObject *
+unwrap_fn_as_fnptr(PyObject *x)
+{
+    assert(PyTuple_Check(x));
+    return (CTypeDescrObject *)PyTuple_GET_ITEM(x, 0);
+}
+
+static CTypeDescrObject *
+unexpected_fn_type(PyObject *x)
+{
+    CTypeDescrObject *ct = unwrap_fn_as_fnptr(x);
+    char *text1 = ct->ct_name;
+    char *text2 = text1 + ct->ct_name_position + 1;
+    assert(text2[-3] == '(');
+    text2[-3] = '\0';
+    PyErr_Format(FFIError, "the type '%s%s' is a function type, not a "
+                           "pointer-to-function type", text1, text2);
+    text2[-3] = '(';
+    return NULL;
+}
+
+static PyObject *
+realize_c_type_or_func(builder_c_t *builder,
+                       _cffi_opcode_t opcodes[], int index);  /* forward */
+
+
+/* Interpret an opcodes[] array.  If opcodes == ctx->types, store all
+   the intermediate types back in the opcodes[].  Returns a new
+   reference.
+*/
+static CTypeDescrObject *
+realize_c_type(builder_c_t *builder, _cffi_opcode_t opcodes[], int index)
+{
+    PyObject *x = realize_c_type_or_func(builder, opcodes, index);
+    if (x == NULL || CTypeDescr_Check(x))
+        return (CTypeDescrObject *)x;
+    else {
+        unexpected_fn_type(x);
+        Py_DECREF(x);
+        return NULL;
+    }
+}
+
+static void _realize_name(char *target, const char *prefix, const char *srcname)
+{
+    /* "xyz" => "struct xyz"
+       "$xyz" => "xyz"
+       "$1" => "struct $1"
+    */
+    if (srcname[0] == '$' && srcname[1] != '$' &&
+            !('0' <= srcname[1] && srcname[1] <= '9')) {
+        strcpy(target, &srcname[1]);
+    }
+    else {
+        strcpy(target, prefix);
+        strcat(target, srcname);
+    }
+}
+
+static void _unrealize_name(char *target, const char *srcname)
+{
+    /* reverse of _realize_name() */
+    if (strncmp(srcname, "struct ", 7) == 0) {
+        strcpy(target, &srcname[7]);
+    }
+    else if (strncmp(srcname, "union ", 6) == 0) {
+        strcpy(target, &srcname[6]);
+    }
+    else if (strncmp(srcname, "enum ", 5) == 0) {
+        strcpy(target, &srcname[5]);
+    }
+    else {
+        strcpy(target, "$");
+        strcat(target, srcname);
+    }
+}
+
+static PyObject *                                              /* forward */
+_fetch_external_struct_or_union(const struct _cffi_struct_union_s *s,
+                                PyObject *included_ffis, int recursion);
+
+static PyObject *
+_realize_c_struct_or_union(builder_c_t *builder, int sindex)
+{
+    PyObject *x;
+    _cffi_opcode_t op2;
+    const struct _cffi_struct_union_s *s;
+
+    if (sindex == _CFFI__IO_FILE_STRUCT) {
+        /* returns a single global cached opaque type */
+        static PyObject *file_struct = NULL;
+        if (file_struct == NULL)
+            file_struct = new_struct_or_union_type("FILE",
+                                                   CT_STRUCT | CT_IS_FILE);
+        Py_XINCREF(file_struct);
+        return file_struct;
+    }
+
+    s = &builder->ctx.struct_unions[sindex];
+    op2 = builder->ctx.types[s->type_index];
+    if ((((uintptr_t)op2) & 1) == 0) {
+        x = (PyObject *)op2;     /* found already in the "primary" slot */
+        Py_INCREF(x);
+    }
+    else {
+        CTypeDescrObject *ct = NULL;
+
+        if (!(s->flags & _CFFI_F_EXTERNAL)) {
+            int flags = (s->flags & _CFFI_F_UNION) ? CT_UNION : CT_STRUCT;
+            char *name = alloca(8 + strlen(s->name));
+            _realize_name(name,
+                          (s->flags & _CFFI_F_UNION) ? "union " : "struct ",
+                          s->name);
+            if (strcmp(name, "struct _IO_FILE") == 0)
+                x = _realize_c_struct_or_union(builder, _CFFI__IO_FILE_STRUCT);
+            else
+                x = new_struct_or_union_type(name, flags);
+            if (x == NULL)
+                return NULL;
+
+            if (!(s->flags & _CFFI_F_OPAQUE)) {
+                assert(s->first_field_index >= 0);
+                ct = (CTypeDescrObject *)x;
+                ct->ct_size = (Py_ssize_t)s->size;
+                ct->ct_length = s->alignment;   /* may be -1 */
+                ct->ct_flags &= ~CT_IS_OPAQUE;
+                ct->ct_flags |= CT_LAZY_FIELD_LIST;
+                ct->ct_extra = builder;
+            }
+            else
+                assert(s->first_field_index < 0);
+        }
+        else {
+            assert(s->first_field_index < 0);
+            x = _fetch_external_struct_or_union(s, builder->included_ffis, 0);
+            if (x == NULL) {
+                if (!PyErr_Occurred())
+                    PyErr_Format(FFIError, "'%s %.200s' should come from "
+                                 "ffi.include() but was not found",
+                                 (s->flags & _CFFI_F_UNION) ? "union"
+                                 : "struct", s->name);
+                return NULL;
+            }
+            if (!(s->flags & _CFFI_F_OPAQUE)) {
+                if (((CTypeDescrObject *)x)->ct_flags & CT_IS_OPAQUE) {
+                    const char *prefix = (s->flags & _CFFI_F_UNION) ? "union"
+                                         : "struct";
+                    PyErr_Format(PyExc_NotImplementedError,
+                                 "'%s %.200s' is opaque in the ffi.include(), "
+                                 "but no longer in the ffi doing the include "
+                                 "(workaround: don't use ffi.include() but "
+                                 "duplicate the declarations of everything "
+                                 "using %s %.200s)",
+                                 prefix, s->name, prefix, s->name);
+                    Py_DECREF(x);
+                    return NULL;
+                }
+            }
+        }
+
+        /* Update the "primary" OP_STRUCT_UNION slot */
+        assert((((uintptr_t)x) & 1) == 0);
+        assert(builder->ctx.types[s->type_index] == op2);
+        Py_INCREF(x);
+        builder->ctx.types[s->type_index] = x;
+
+        if (ct != NULL && s->size == (size_t)-2) {
+            /* oops, this struct is unnamed and we couldn't generate
+               a C expression to get its size.  We have to rely on
+               complete_struct_or_union() to compute it now. */
+            if (do_realize_lazy_struct(ct) < 0) {
+                builder->ctx.types[s->type_index] = op2;
+                return NULL;
+            }
+        }
+    }
+    return x;
+}
+
+static PyObject *
+realize_c_type_or_func(builder_c_t *builder,
+                        _cffi_opcode_t opcodes[], int index)
+{
+    PyObject *x, *y, *z;
+    _cffi_opcode_t op = opcodes[index];
+    Py_ssize_t length = -1;
+
+    if ((((uintptr_t)op) & 1) == 0) {
+        x = (PyObject *)op;
+        Py_INCREF(x);
+        return x;
+    }
+
+    switch (_CFFI_GETOP(op)) {
+
+    case _CFFI_OP_PRIMITIVE:
+        x = get_primitive_type(_CFFI_GETARG(op));
+        Py_XINCREF(x);
+        break;
+
+    case _CFFI_OP_POINTER:
+        y = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op));
+        if (y == NULL)
+            return NULL;
+        if (CTypeDescr_Check(y)) {
+            x = new_pointer_type((CTypeDescrObject *)y);
+        }
+        else {
+            assert(PyTuple_Check(y));   /* from _CFFI_OP_FUNCTION */
+            x = PyTuple_GET_ITEM(y, 0);
+            Py_INCREF(x);
+        }
+        Py_DECREF(y);
+        break;
+
+    case _CFFI_OP_ARRAY:
+        length = (Py_ssize_t)opcodes[index + 1];
+        /* fall-through */
+    case _CFFI_OP_OPEN_ARRAY:
+        y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op));
+        if (y == NULL)
+            return NULL;
+        z = new_pointer_type((CTypeDescrObject *)y);
+        Py_DECREF(y);
+        if (z == NULL)
+            return NULL;
+        x = new_array_type((CTypeDescrObject *)z, length);
+        Py_DECREF(z);
+        break;
+
+    case _CFFI_OP_STRUCT_UNION:
+        x = _realize_c_struct_or_union(builder, _CFFI_GETARG(op));
+        break;
+
+    case _CFFI_OP_ENUM:
+    {
+        const struct _cffi_enum_s *e;
+        _cffi_opcode_t op2;
+
+        e = &builder->ctx.enums[_CFFI_GETARG(op)];
+        op2 = builder->ctx.types[e->type_index];
+        if ((((uintptr_t)op2) & 1) == 0) {
+            x = (PyObject *)op2;
+            Py_INCREF(x);
+        }
+        else {
+            PyObject *enumerators = NULL, *enumvalues = NULL, *tmp;
+            Py_ssize_t i, j, n = 0;
+            const char *p;
+            int gindex;
+            PyObject *args;
+            PyObject *basetd = get_primitive_type(e->type_prim);
+            if (basetd == NULL)
+                return NULL;
+
+            if (*e->enumerators != '\0') {
+                n++;
+                for (p = e->enumerators; *p != '\0'; p++)
+                    n += (*p == ',');
+            }
+            enumerators = PyTuple_New(n);
+            if (enumerators == NULL)
+                return NULL;
+
+            enumvalues = PyTuple_New(n);
+            if (enumvalues == NULL) {
+                Py_DECREF(enumerators);
+                return NULL;
+            }
+
+            p = e->enumerators;
+            for (i = 0; i < n; i++) {
+                j = 0;
+                while (p[j] != ',' && p[j] != '\0')
+                    j++;
+                tmp = PyText_FromStringAndSize(p, j);
+                if (tmp == NULL)
+                    break;
+                PyTuple_SET_ITEM(enumerators, i, tmp);
+
+                gindex = search_in_globals(&builder->ctx, p, j);
+                assert(gindex >= 0);
+                assert(builder->ctx.globals[gindex].type_op ==
+                       _CFFI_OP(_CFFI_OP_ENUM, -1));
+
+                tmp = realize_global_int(builder, gindex);
+                if (tmp == NULL)
+                    break;
+                PyTuple_SET_ITEM(enumvalues, i, tmp);
+
+                p += j + 1;
+            }
+
+            args = NULL;
+            if (!PyErr_Occurred()) {
+                char *name = alloca(6 + strlen(e->name));
+                _realize_name(name, "enum ", e->name);
+                args = Py_BuildValue("(sOOO)", name, enumerators,
+                                     enumvalues, basetd);
+            }
+            Py_DECREF(enumerators);
+            Py_DECREF(enumvalues);
+            if (args == NULL)
+                return NULL;
+
+            x = b_new_enum_type(NULL, args);
+            Py_DECREF(args);
+            if (x == NULL)
+                return NULL;
+
+            /* Update the "primary" _CFFI_OP_ENUM slot, which
+               may be the same or a different slot than the "current" one */
+            assert((((uintptr_t)x) & 1) == 0);
+            assert(builder->ctx.types[e->type_index] == op2);
+            Py_INCREF(x);
+            builder->ctx.types[e->type_index] = x;
+
+            /* Done, leave without updating the "current" slot because
+               it may be done already above.  If not, never mind, the
+               next call to realize_c_type() will do it. */
+            return x;
+        }
+        break;
+    }
+
+    case _CFFI_OP_FUNCTION:
+    {
+        PyObject *fargs;
+        int i, base_index, num_args, ellipsis, abi;
+
+        y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op));
+        if (y == NULL)
+            return NULL;
+
+        base_index = index + 1;
+        num_args = 0;
+        /* note that if the arguments are already built, they have a
+           pointer in the 'opcodes' array, and GETOP() returns a
+           random even value.  But OP_FUNCTION_END is odd, so the
+           condition below still works correctly. */
+        while (_CFFI_GETOP(opcodes[base_index + num_args]) !=
+                   _CFFI_OP_FUNCTION_END)
+            num_args++;
+
+        ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 0x01;
+        abi      = _CFFI_GETARG(opcodes[base_index + num_args]) & 0xFE;
+        switch (abi) {
+        case 0:
+            abi = FFI_DEFAULT_ABI;
+            break;
+        case 2:
+#if defined(MS_WIN32) && !defined(_WIN64)
+            abi = FFI_STDCALL;
+#else
+            abi = FFI_DEFAULT_ABI;
+#endif
+            break;
+        default:
+            PyErr_Format(FFIError, "abi number %d not supported", abi);
+            Py_DECREF(y);
+            return NULL;
+        }
+
+        fargs = PyTuple_New(num_args);
+        if (fargs == NULL) {
+            Py_DECREF(y);
+            return NULL;
+        }
+
+        for (i = 0; i < num_args; i++) {
+            z = (PyObject *)realize_c_type(builder, opcodes, base_index + i);
+            if (z == NULL) {
+                Py_DECREF(fargs);
+                Py_DECREF(y);
+                return NULL;
+            }
+            PyTuple_SET_ITEM(fargs, i, z);
+        }
+
+        z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis, abi);
+        Py_DECREF(fargs);
+        Py_DECREF(y);
+        if (z == NULL)
+            return NULL;
+
+        x = PyTuple_Pack(1, z);   /* hack: hide the CT_FUNCTIONPTR.  it will
+                                     be revealed again by the OP_POINTER */
+        Py_DECREF(z);
+        break;
+    }
+
+    case _CFFI_OP_NOOP:
+        x = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op));
+        break;
+
+    case _CFFI_OP_TYPENAME:
+    {
+        /* essential: the TYPENAME opcode resolves the type index looked
+           up in the 'ctx->typenames' array, but it does so in 'ctx->types'
+           instead of in 'opcodes'! */
+        int type_index = builder->ctx.typenames[_CFFI_GETARG(op)].type_index;
+        x = realize_c_type_or_func(builder, builder->ctx.types, type_index);
+        break;
+    }
+
+    default:
+        PyErr_Format(PyExc_NotImplementedError, "op=%d", (int)_CFFI_GETOP(op));
+        return NULL;
+    }
+
+    if (x != NULL && opcodes == builder->ctx.types && opcodes[index] != x) {
+        assert((((uintptr_t)x) & 1) == 0);
+        assert((((uintptr_t)opcodes[index]) & 1) == 1);
+        Py_INCREF(x);
+        opcodes[index] = x;
+    }
+    return x;
+};
+
+static CTypeDescrObject *
+realize_c_func_return_type(builder_c_t *builder,
+                           _cffi_opcode_t opcodes[], int index)
+{
+    PyObject *x;
+    _cffi_opcode_t op = opcodes[index];
+
+    if ((((uintptr_t)op) & 1) == 0) {
+        /* already built: assert that it is a function and fish
+           for the return type */
+        x = (PyObject *)op;
+        assert(PyTuple_Check(x));   /* from _CFFI_OP_FUNCTION */
+        x = PyTuple_GET_ITEM(x, 0);
+        assert(CTypeDescr_Check(x));
+        assert(((CTypeDescrObject *)x)->ct_flags & CT_FUNCTIONPTR);
+        x = PyTuple_GET_ITEM(((CTypeDescrObject *)x)->ct_stuff, 1);
+        assert(CTypeDescr_Check(x));
+        Py_INCREF(x);
+        return (CTypeDescrObject *)x;
+    }
+    else {
+        assert(_CFFI_GETOP(op) == _CFFI_OP_FUNCTION);
+        return realize_c_type(builder, opcodes, _CFFI_GETARG(opcodes[index]));
+    }
+}
+
+static int do_realize_lazy_struct(CTypeDescrObject *ct)
+{
+    /* This is called by force_lazy_struct() in _cffi_backend.c */
+    assert(ct->ct_flags & (CT_STRUCT | CT_UNION));
+
+    if (ct->ct_flags & CT_LAZY_FIELD_LIST) {
+        builder_c_t *builder;
+        char *p;
+        int n, i, sflags;
+        const struct _cffi_struct_union_s *s;
+        const struct _cffi_field_s *fld;
+        PyObject *fields, *args, *res;
+
+        assert(!(ct->ct_flags & CT_IS_OPAQUE));
+
+        builder = ct->ct_extra;
+        assert(builder != NULL);
+
+        p = alloca(2 + strlen(ct->ct_name));
+        _unrealize_name(p, ct->ct_name);
+
+        n = search_in_struct_unions(&builder->ctx, p, strlen(p));
+        if (n < 0)
+            Py_FatalError("lost a struct/union!");
+
+        s = &builder->ctx.struct_unions[n];
+        fld = &builder->ctx.fields[s->first_field_index];
+
+        /* XXX painfully build all the Python objects that are the args
+           to b_complete_struct_or_union() */
+
+        fields = PyList_New(s->num_fields);
+        if (fields == NULL)
+            return -1;
+
+        for (i = 0; i < s->num_fields; i++, fld++) {
+            _cffi_opcode_t op = fld->field_type_op;
+            int fbitsize = -1;
+            PyObject *f;
+            CTypeDescrObject *ctf;
+
+            switch (_CFFI_GETOP(op)) {
+
+            case _CFFI_OP_BITFIELD:
+                assert(fld->field_size >= 0);
+                fbitsize = (int)fld->field_size;
+                /* fall-through */
+            case _CFFI_OP_NOOP:
+                ctf = realize_c_type(builder, builder->ctx.types,
+                                     _CFFI_GETARG(op));
+                break;
+
+            default:
+                Py_DECREF(fields);
+                PyErr_Format(PyExc_NotImplementedError, "field op=%d",
+                             (int)_CFFI_GETOP(op));
+                return -1;
+            }
+
+            if (ctf != NULL && fld->field_offset == (size_t)-1) {
+                /* unnamed struct, with field positions and sizes entirely
+                   determined by complete_struct_or_union() and not checked.
+                   Or, bitfields (field_size >= 0), similarly not checked. */
+                assert(fld->field_size == (size_t)-1 || fbitsize >= 0);
+            }
+            else if (ctf == NULL || detect_custom_layout(ct, SF_STD_FIELD_POS,
+                                     ctf->ct_size, fld->field_size,
+                                     "wrong size for field '",
+                                     fld->name, "'") < 0) {
+                Py_DECREF(fields);
+                return -1;
+            }
+
+            f = Py_BuildValue("(sOin)", fld->name, ctf,
+                              fbitsize, (Py_ssize_t)fld->field_offset);
+            if (f == NULL) {
+                Py_DECREF(fields);
+                return -1;
+            }
+            PyList_SET_ITEM(fields, i, f);
+        }
+
+        sflags = 0;
+        if (s->flags & _CFFI_F_CHECK_FIELDS)
+            sflags |= SF_STD_FIELD_POS;
+        if (s->flags & _CFFI_F_PACKED)
+            sflags |= SF_PACKED;
+
+        args = Py_BuildValue("(OOOnii)", ct, fields, Py_None,
+                             (Py_ssize_t)s->size,
+                             s->alignment,
+                             sflags);
+        Py_DECREF(fields);
+        if (args == NULL)
+            return -1;
+
+        ct->ct_extra = NULL;
+        ct->ct_flags |= CT_IS_OPAQUE;
+        res = b_complete_struct_or_union(NULL, args);
+        ct->ct_flags &= ~CT_IS_OPAQUE;
+        Py_DECREF(args);
+
+        if (res == NULL) {
+            ct->ct_extra = builder;
+            return -1;
+        }
+
+        assert(ct->ct_stuff != NULL);
+        ct->ct_flags &= ~CT_LAZY_FIELD_LIST;
+        Py_DECREF(res);
+        return 1;
+    }
+    else {
+        assert(ct->ct_flags & CT_IS_OPAQUE);
+        return 0;
+    }
+}
diff --git a/c/test_c.py b/c/test_c.py
new file mode 100644
index 0000000..da5f751
--- /dev/null
+++ b/c/test_c.py
@@ -0,0 +1,4256 @@
+import py
+def _setup_path():
+    import os, sys
+    if '__pypy__' in sys.builtin_module_names:
+        py.test.skip("_cffi_backend.c: not tested on top of pypy, "
+                     "use pypy/module/_cffi_backend/test/ instead.")
+    sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
+_setup_path()
+from _cffi_backend import *
+from _cffi_backend import _testfunc, _get_types, _get_common_types, __version__
+
+# ____________________________________________________________
+
+import sys
+assert __version__ == "1.12.2", ("This test_c.py file is for testing a version"
+                                 " of cffi that differs from the one that we"
+                                 " get from 'import _cffi_backend'")
+if sys.version_info < (3,):
+    type_or_class = "type"
+    mandatory_b_prefix = ''
+    mandatory_u_prefix = 'u'
+    bytechr = chr
+    bitem2bchr = lambda x: x
+    class U(object):
+        def __add__(self, other):
+            return eval('u'+repr(other).replace(r'\\u', r'\u')
+                                       .replace(r'\\U', r'\U'))
+    u = U()
+    str2bytes = str
+    strict_compare = False
+else:
+    type_or_class = "class"
+    long = int
+    unicode = str
+    unichr = chr
+    mandatory_b_prefix = 'b'
+    mandatory_u_prefix = ''
+    bytechr = lambda n: bytes([n])
+    bitem2bchr = bytechr
+    u = ""
+    str2bytes = lambda s: bytes(s, "ascii")
+    strict_compare = True
+
+def size_of_int():
+    BInt = new_primitive_type("int")
+    return sizeof(BInt)
+
+def size_of_long():
+    BLong = new_primitive_type("long")
+    return sizeof(BLong)
+
+def size_of_ptr():
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    return sizeof(BPtr)
+
+
+def find_and_load_library(name, flags=RTLD_NOW):
+    import ctypes.util
+    if name is None:
+        path = None
+    else:
+        path = ctypes.util.find_library(name)
+        if path is None and name == 'c':
+            assert sys.platform == 'win32'
+            assert sys.version_info >= (3,)
+            py.test.skip("dlopen(None) cannot work on Windows with Python 3")
+    return load_library(path, flags)
+
+def test_load_library():
+    x = find_and_load_library('c')
+    assert repr(x).startswith("<clibrary '")
+    x = find_and_load_library('c', RTLD_NOW | RTLD_GLOBAL)
+    assert repr(x).startswith("<clibrary '")
+    x = find_and_load_library('c', RTLD_LAZY)
+    assert repr(x).startswith("<clibrary '")
+
+def test_all_rtld_symbols():
+    import sys
+    FFI_DEFAULT_ABI        # these symbols must be defined
+    FFI_CDECL
+    RTLD_LAZY
+    RTLD_NOW
+    RTLD_GLOBAL
+    RTLD_LOCAL
+    if sys.platform.startswith("linux"):
+        RTLD_NODELETE
+        RTLD_NOLOAD
+        RTLD_DEEPBIND
+
+def test_new_primitive_type():
+    py.test.raises(KeyError, new_primitive_type, "foo")
+    p = new_primitive_type("signed char")
+    assert repr(p) == "<ctype 'signed char'>"
+
+def check_dir(p, expected):
+    got = [name for name in dir(p) if not name.startswith('_')]
+    assert got == sorted(expected)
+
+def test_inspect_primitive_type():
+    p = new_primitive_type("signed char")
+    assert p.kind == "primitive"
+    assert p.cname == "signed char"
+    check_dir(p, ['cname', 'kind'])
+
+def test_cast_to_signed_char():
+    p = new_primitive_type("signed char")
+    x = cast(p, -65 + 17*256)
+    assert repr(x) == "<cdata 'signed char' -65>"
+    assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class
+    assert int(x) == -65
+    x = cast(p, -66 + (1<<199)*256)
+    assert repr(x) == "<cdata 'signed char' -66>"
+    assert int(x) == -66
+    assert (x == cast(p, -66)) is True
+    assert (x != cast(p, -66)) is False
+    q = new_primitive_type("short")
+    assert (x == cast(q, -66)) is True
+    assert (x != cast(q, -66)) is False
+
+def test_sizeof_type():
+    py.test.raises(TypeError, sizeof, 42.5)
+    p = new_primitive_type("short")
+    assert sizeof(p) == 2
+
+def test_integer_types():
+    for name in ['signed char', 'short', 'int', 'long', 'long long']:
+        p = new_primitive_type(name)
+        size = sizeof(p)
+        min = -(1 << (8*size-1))
+        max = (1 << (8*size-1)) - 1
+        assert int(cast(p, min)) == min
+        assert int(cast(p, max)) == max
+        assert int(cast(p, min - 1)) == max
+        assert int(cast(p, max + 1)) == min
+        py.test.raises(TypeError, cast, p, None)
+        assert long(cast(p, min - 1)) == max
+        assert int(cast(p, b'\x08')) == 8
+        assert int(cast(p, u+'\x08')) == 8
+    for name in ['char', 'short', 'int', 'long', 'long long']:
+        p = new_primitive_type('unsigned ' + name)
+        size = sizeof(p)
+        max = (1 << (8*size)) - 1
+        assert int(cast(p, 0)) == 0
+        assert int(cast(p, max)) == max
+        assert int(cast(p, -1)) == max
+        assert int(cast(p, max + 1)) == 0
+        assert long(cast(p, -1)) == max
+        assert int(cast(p, b'\xFE')) == 254
+        assert int(cast(p, u+'\xFE')) == 254
+
+def test_no_float_on_int_types():
+    p = new_primitive_type('long')
+    py.test.raises(TypeError, float, cast(p, 42))
+    py.test.raises(TypeError, complex, cast(p, 42))
+
+def test_float_types():
+    INF = 1E200 * 1E200
+    for name in ["float", "double"]:
+        p = new_primitive_type(name)
+        assert bool(cast(p, 0)) is False      # since 1.7
+        assert bool(cast(p, -0.0)) is False   # since 1.7
+        assert bool(cast(p, 1e-42)) is True
+        assert bool(cast(p, -1e-42)) is True
+        assert bool(cast(p, INF))
+        assert bool(cast(p, -INF))
+        assert bool(cast(p, float("nan")))
+        assert int(cast(p, -150)) == -150
+        assert int(cast(p, 61.91)) == 61
+        assert long(cast(p, 61.91)) == 61
+        assert type(int(cast(p, 61.91))) is int
+        assert type(int(cast(p, 1E22))) is long
+        assert type(long(cast(p, 61.91))) is long
+        assert type(long(cast(p, 1E22))) is long
+        py.test.raises(OverflowError, int, cast(p, INF))
+        py.test.raises(OverflowError, int, cast(p, -INF))
+        assert float(cast(p, 1.25)) == 1.25
+        assert float(cast(p, INF)) == INF
+        assert float(cast(p, -INF)) == -INF
+        if name == "float":
+            assert float(cast(p, 1.1)) != 1.1     # rounding error
+            assert float(cast(p, 1E200)) == INF   # limited range
+
+        assert cast(p, -1.1) == cast(p, -1.1)
+        assert repr(float(cast(p, -0.0))) == '-0.0'
+        assert float(cast(p, b'\x09')) == 9.0
+        assert float(cast(p, u+'\x09')) == 9.0
+        assert float(cast(p, True)) == 1.0
+        py.test.raises(TypeError, cast, p, None)
+
+def test_complex_types():
+    INF = 1E200 * 1E200
+    for name in ["float", "double"]:
+        p = new_primitive_type(name + " _Complex")
+        assert bool(cast(p, 0)) is False
+        assert bool(cast(p, INF))
+        assert bool(cast(p, -INF))
+        assert bool(cast(p, 0j)) is False
+        assert bool(cast(p, INF*1j))
+        assert bool(cast(p, -INF*1j))
+        # "can't convert complex to float", like CPython's "float(0j)"
+        py.test.raises(TypeError, int, cast(p, -150))
+        py.test.raises(TypeError, long, cast(p, -150))
+        py.test.raises(TypeError, float, cast(p, -150))
+        assert complex(cast(p, 1.25)) == 1.25
+        assert complex(cast(p, 1.25j)) == 1.25j
+        assert complex(cast(p, complex(0,INF))) == complex(0,INF)
+        assert complex(cast(p, -INF)) == -INF
+        if name == "float":
+            assert complex(cast(p, 1.1j)) != 1.1j         # rounding error
+            assert complex(cast(p, 1E200+3j)) == INF+3j   # limited range
+            assert complex(cast(p, complex(3,1E200))) == complex(3,INF) # limited range
+
+        assert cast(p, -1.1j) == cast(p, -1.1j)
+        assert repr(complex(cast(p, -0.0)).real) == '-0.0'
+        #assert repr(complex(cast(p, -0j))) == '-0j'   # http://bugs.python.org/issue29602
+        assert complex(cast(p, b'\x09')) == 9.0 + 0j
+        assert complex(cast(p, u+'\x09')) == 9.0 + 0j
+        assert complex(cast(p, True)) == 1.0 + 0j
+        py.test.raises(TypeError, cast, p, None)
+        #
+        py.test.raises(TypeError, cast, new_primitive_type(name), 1+0j)
+        #
+        for basetype in ["char", "int", "uint64_t", "float",
+                         "double", "long double"]:
+            baseobj = cast(new_primitive_type(basetype), 65)
+            py.test.raises(TypeError, complex, baseobj)
+        #
+        BArray = new_array_type(new_pointer_type(p), 10)
+        x = newp(BArray, None)
+        x[5] = 12.34 + 56.78j
+        assert type(x[5]) is complex
+        assert abs(x[5] - (12.34 + 56.78j)) < 1e-5
+        assert (x[5] == 12.34 + 56.78j) == (name == "double")  # rounding error
+        #
+        class Foo:
+            def __complex__(self):
+                return 2 + 3j
+        assert complex(Foo()) == 2 + 3j
+        assert complex(cast(p, Foo())) == 2 + 3j
+    py.test.raises(TypeError, cast, new_primitive_type("int"), 1+0j)
+
+def test_character_type():
+    p = new_primitive_type("char")
+    assert bool(cast(p, 'A')) is True
+    assert bool(cast(p, '\x00')) is False    # since 1.7
+    assert cast(p, '\x00') == cast(p, -17*256)
+    assert int(cast(p, 'A')) == 65
+    assert long(cast(p, 'A')) == 65
+    assert type(int(cast(p, 'A'))) is int
+    assert type(long(cast(p, 'A'))) is long
+    assert str(cast(p, 'A')) == repr(cast(p, 'A'))
+    assert repr(cast(p, 'A')) == "<cdata 'char' %s'A'>" % mandatory_b_prefix
+    assert repr(cast(p, 255)) == r"<cdata 'char' %s'\xff'>" % mandatory_b_prefix
+    assert repr(cast(p, 0)) == r"<cdata 'char' %s'\x00'>" % mandatory_b_prefix
+
+def test_pointer_type():
+    p = new_primitive_type("int")
+    assert repr(p) == "<ctype 'int'>"
+    p = new_pointer_type(p)
+    assert repr(p) == "<ctype 'int *'>"
+    p = new_pointer_type(p)
+    assert repr(p) == "<ctype 'int * *'>"
+    p = new_pointer_type(p)
+    assert repr(p) == "<ctype 'int * * *'>"
+
+def test_inspect_pointer_type():
+    p1 = new_primitive_type("int")
+    p2 = new_pointer_type(p1)
+    assert p2.kind == "pointer"
+    assert p2.cname == "int *"
+    assert p2.item is p1
+    check_dir(p2, ['cname', 'kind', 'item'])
+    p3 = new_pointer_type(p2)
+    assert p3.item is p2
+
+def test_pointer_to_int():
+    BInt = new_primitive_type("int")
+    py.test.raises(TypeError, newp, BInt)
+    py.test.raises(TypeError, newp, BInt, None)
+    BPtr = new_pointer_type(BInt)
+    p = newp(BPtr)
+    assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+    p = newp(BPtr, None)
+    assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+    p = newp(BPtr, 5000)
+    assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+    q = cast(BPtr, p)
+    assert repr(q).startswith("<cdata 'int *' 0x")
+    assert p == q
+    assert hash(p) == hash(q)
+    e = py.test.raises(TypeError, newp, new_array_type(BPtr, None), None)
+    assert str(e.value) == (
+        "expected new array length or list/tuple/str, not NoneType")
+
+def test_pointer_bool():
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    p = cast(BPtr, 0)
+    assert bool(p) is False
+    p = cast(BPtr, 42)
+    assert bool(p) is True
+
+def test_pointer_to_pointer():
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    BPtrPtr = new_pointer_type(BPtr)
+    p = newp(BPtrPtr, None)
+    assert repr(p) == "<cdata 'int * *' owning %d bytes>" % size_of_ptr()
+
+def test_reading_pointer_to_int():
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    p = newp(BPtr, None)
+    assert p[0] == 0
+    p = newp(BPtr, 5000)
+    assert p[0] == 5000
+    py.test.raises(IndexError, "p[1]")
+    py.test.raises(IndexError, "p[-1]")
+
+def test_reading_pointer_to_float():
+    BFloat = new_primitive_type("float")
+    py.test.raises(TypeError, newp, BFloat, None)
+    BPtr = new_pointer_type(BFloat)
+    p = newp(BPtr, None)
+    assert p[0] == 0.0 and type(p[0]) is float
+    p = newp(BPtr, 1.25)
+    assert p[0] == 1.25 and type(p[0]) is float
+    p = newp(BPtr, 1.1)
+    assert p[0] != 1.1 and abs(p[0] - 1.1) < 1E-5   # rounding errors
+
+def test_cast_float_to_int():
+    for type in ["int", "unsigned int", "long", "unsigned long",
+                 "long long", "unsigned long long"]:
+        p = new_primitive_type(type)
+        assert int(cast(p, 4.2)) == 4
+        py.test.raises(TypeError, newp, new_pointer_type(p), 4.2)
+
+def test_newp_integer_types():
+    for name in ['signed char', 'short', 'int', 'long', 'long long']:
+        p = new_primitive_type(name)
+        pp = new_pointer_type(p)
+        size = sizeof(p)
+        min = -(1 << (8*size-1))
+        max = (1 << (8*size-1)) - 1
+        assert newp(pp, min)[0] == min
+        assert newp(pp, max)[0] == max
+        py.test.raises(OverflowError, newp, pp, min - 2 ** 32)
+        py.test.raises(OverflowError, newp, pp, min - 2 ** 64)
+        py.test.raises(OverflowError, newp, pp, max + 2 ** 32)
+        py.test.raises(OverflowError, newp, pp, max + 2 ** 64)
+        py.test.raises(OverflowError, newp, pp, min - 1)
+        py.test.raises(OverflowError, newp, pp, max + 1)
+        py.test.raises(OverflowError, newp, pp, min - 1 - 2 ** 32)
+        py.test.raises(OverflowError, newp, pp, min - 1 - 2 ** 64)
+        py.test.raises(OverflowError, newp, pp, max + 1)
+        py.test.raises(OverflowError, newp, pp, max + 1 + 2 ** 32)
+        py.test.raises(OverflowError, newp, pp, max + 1 + 2 ** 64)
+        py.test.raises(TypeError, newp, pp, 1.0)
+    for name in ['char', 'short', 'int', 'long', 'long long']:
+        p = new_primitive_type('unsigned ' + name)
+        pp = new_pointer_type(p)
+        size = sizeof(p)
+        max = (1 << (8*size)) - 1
+        assert newp(pp, 0)[0] == 0
+        assert newp(pp, max)[0] == max
+        py.test.raises(OverflowError, newp, pp, -1)
+        py.test.raises(OverflowError, newp, pp, max + 1)
+
+def test_reading_pointer_to_char():
+    BChar = new_primitive_type("char")
+    py.test.raises(TypeError, newp, BChar, None)
+    BPtr = new_pointer_type(BChar)
+    p = newp(BPtr, None)
+    assert p[0] == b'\x00'
+    p = newp(BPtr, b'A')
+    assert p[0] == b'A'
+    py.test.raises(TypeError, newp, BPtr, 65)
+    py.test.raises(TypeError, newp, BPtr, b"foo")
+    py.test.raises(TypeError, newp, BPtr, u+"foo")
+    c = cast(BChar, b'A')
+    assert str(c) == repr(c)
+    assert int(c) == ord(b'A')
+    py.test.raises(TypeError, cast, BChar, b'foo')
+    py.test.raises(TypeError, cast, BChar, u+'foo')
+    e = py.test.raises(TypeError, newp, new_array_type(BPtr, None), 12.3)
+    assert str(e.value) == (
+        "expected new array length or list/tuple/str, not float")
+
+def test_reading_pointer_to_pointer():
+    BVoidP = new_pointer_type(new_void_type())
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    BIntPtrPtr = new_pointer_type(BIntPtr)
+    q = newp(BIntPtr, 42)
+    assert q[0] == 42
+    p = newp(BIntPtrPtr, None)
+    assert p[0] is not None
+    assert p[0] == cast(BVoidP, 0)
+    assert p[0] == cast(BCharP, 0)
+    assert p[0] != None
+    assert repr(p[0]) == "<cdata 'int *' NULL>"
+    p[0] = q
+    assert p[0] != cast(BVoidP, 0)
+    assert p[0] != cast(BCharP, 0)
+    assert p[0][0] == 42
+    q[0] += 1
+    assert p[0][0] == 43
+    p = newp(BIntPtrPtr, q)
+    assert p[0][0] == 43
+
+def test_load_standard_library():
+    if sys.platform == "win32":
+        py.test.raises(OSError, find_and_load_library, None)
+        return
+    x = find_and_load_library(None)
+    BVoidP = new_pointer_type(new_void_type())
+    assert x.load_function(BVoidP, 'strcpy')
+    py.test.raises(AttributeError, x.load_function,
+                   BVoidP, 'xxx_this_function_does_not_exist')
+    # the next one is from 'libm', not 'libc', but we assume
+    # that it is already loaded too, so it should work
+    assert x.load_function(BVoidP, 'sqrt')
+    #
+    x.close_lib()
+    py.test.raises(ValueError, x.load_function, BVoidP, 'sqrt')
+    x.close_lib()
+
+def test_no_len_on_nonarray():
+    p = new_primitive_type("int")
+    py.test.raises(TypeError, len, cast(p, 42))
+
+def test_cmp_none():
+    p = new_primitive_type("int")
+    x = cast(p, 42)
+    assert (x == None) is False
+    assert (x != None) is True
+    assert (x == ["hello"]) is False
+    assert (x != ["hello"]) is True
+    y = cast(p, 0)
+    assert (y == None) is False
+
+def test_invalid_indexing():
+    p = new_primitive_type("int")
+    x = cast(p, 42)
+    py.test.raises(TypeError, "x[0]")
+
+def test_default_str():
+    BChar = new_primitive_type("char")
+    x = cast(BChar, 42)
+    assert str(x) == repr(x)
+    BInt = new_primitive_type("int")
+    x = cast(BInt, 42)
+    assert str(x) == repr(x)
+    BArray = new_array_type(new_pointer_type(BInt), 10)
+    x = newp(BArray, None)
+    assert str(x) == repr(x)
+
+def test_default_unicode():
+    BInt = new_primitive_type("int")
+    x = cast(BInt, 42)
+    assert unicode(x) == unicode(repr(x))
+    BArray = new_array_type(new_pointer_type(BInt), 10)
+    x = newp(BArray, None)
+    assert unicode(x) == unicode(repr(x))
+
+def test_cast_from_cdataint():
+    BInt = new_primitive_type("int")
+    x = cast(BInt, 0)
+    y = cast(new_pointer_type(BInt), x)
+    assert bool(y) is False
+    #
+    x = cast(BInt, 42)
+    y = cast(BInt, x)
+    assert int(y) == 42
+    y = cast(new_primitive_type("char"), x)
+    assert int(y) == 42
+    y = cast(new_primitive_type("float"), x)
+    assert float(y) == 42.0
+    #
+    z = cast(BInt, 42.5)
+    assert int(z) == 42
+    z = cast(BInt, y)
+    assert int(z) == 42
+
+def test_void_type():
+    p = new_void_type()
+    assert p.kind == "void"
+    assert p.cname == "void"
+    check_dir(p, ['kind', 'cname'])
+
+def test_array_type():
+    p = new_primitive_type("int")
+    assert repr(p) == "<ctype 'int'>"
+    #
+    py.test.raises(TypeError, new_array_type, new_pointer_type(p), "foo")
+    py.test.raises(ValueError, new_array_type, new_pointer_type(p), -42)
+    #
+    p1 = new_array_type(new_pointer_type(p), None)
+    assert repr(p1) == "<ctype 'int[]'>"
+    py.test.raises(ValueError, new_array_type, new_pointer_type(p1), 42)
+    #
+    p1 = new_array_type(new_pointer_type(p), 42)
+    p2 = new_array_type(new_pointer_type(p1), 25)
+    assert repr(p2) == "<ctype 'int[25][42]'>"
+    p2 = new_array_type(new_pointer_type(p1), None)
+    assert repr(p2) == "<ctype 'int[][42]'>"
+    #
+    py.test.raises(OverflowError,
+                   new_array_type, new_pointer_type(p), sys.maxsize+1)
+    py.test.raises(OverflowError,
+                   new_array_type, new_pointer_type(p), sys.maxsize // 3)
+
+def test_inspect_array_type():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), None)
+    assert p1.kind == "array"
+    assert p1.cname == "int[]"
+    assert p1.item is p
+    assert p1.length is None
+    check_dir(p1, ['cname', 'kind', 'item', 'length'])
+    p1 = new_array_type(new_pointer_type(p), 42)
+    assert p1.kind == "array"
+    assert p1.cname == "int[42]"
+    assert p1.item is p
+    assert p1.length == 42
+    check_dir(p1, ['cname', 'kind', 'item', 'length'])
+
+def test_array_instance():
+    LENGTH = 1423
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), LENGTH)
+    a = newp(p1, None)
+    assert repr(a) == "<cdata 'int[%d]' owning %d bytes>" % (
+        LENGTH, LENGTH * size_of_int())
+    assert len(a) == LENGTH
+    for i in range(LENGTH):
+        assert a[i] == 0
+    py.test.raises(IndexError, "a[LENGTH]")
+    py.test.raises(IndexError, "a[-1]")
+    for i in range(LENGTH):
+        a[i] = i * i + 1
+    for i in range(LENGTH):
+        assert a[i] == i * i + 1
+    e = py.test.raises(IndexError, "a[LENGTH+100] = 500")
+    assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value)
+    py.test.raises(TypeError, int, a)
+
+def test_array_of_unknown_length_instance():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), None)
+    py.test.raises(TypeError, newp, p1, None)
+    py.test.raises(ValueError, newp, p1, -42)
+    a = newp(p1, 42)
+    assert len(a) == 42
+    for i in range(42):
+        a[i] -= i
+    for i in range(42):
+        assert a[i] == -i
+    py.test.raises(IndexError, "a[42]")
+    py.test.raises(IndexError, "a[-1]")
+    py.test.raises(IndexError, "a[42] = 123")
+    py.test.raises(IndexError, "a[-1] = 456")
+
+def test_array_of_unknown_length_instance_with_initializer():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), None)
+    a = newp(p1, list(range(42)))
+    assert len(a) == 42
+    a = newp(p1, tuple(range(142)))
+    assert len(a) == 142
+
+def test_array_initializer():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), None)
+    a = newp(p1, list(range(100, 142)))
+    for i in range(42):
+        assert a[i] == 100 + i
+    #
+    p2 = new_array_type(new_pointer_type(p), 43)
+    a = newp(p2, tuple(range(100, 142)))
+    for i in range(42):
+        assert a[i] == 100 + i
+    assert a[42] == 0      # extra uninitialized item
+
+def test_array_add():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), 5)    # int[5]
+    p2 = new_array_type(new_pointer_type(p1), 3)   # int[3][5]
+    a = newp(p2, [list(range(n, n+5)) for n in [100, 200, 300]])
+    assert repr(a) == "<cdata 'int[3][5]' owning %d bytes>" % (
+        3*5*size_of_int(),)
+    assert repr(a + 0).startswith("<cdata 'int(*)[5]' 0x")
+    assert 0 + a == a + 0 != 1 + a == a + 1
+    assert repr(a[0]).startswith("<cdata 'int[5]' 0x")
+    assert repr((a + 0)[0]).startswith("<cdata 'int[5]' 0x")
+    assert repr(a[0] + 0).startswith("<cdata 'int *' 0x")
+    assert type(a[0][0]) is int
+    assert type((a[0] + 0)[0]) is int
+
+def test_array_sub():
+    BInt = new_primitive_type("int")
+    BArray = new_array_type(new_pointer_type(BInt), 5)   # int[5]
+    a = newp(BArray, None)
+    p = a + 1
+    assert p - a == 1
+    assert p - (a+0) == 1
+    assert a == (p - 1)
+    BPtr = new_pointer_type(new_primitive_type("short"))
+    q = newp(BPtr, None)
+    py.test.raises(TypeError, "p - q")
+    py.test.raises(TypeError, "q - p")
+    py.test.raises(TypeError, "a - q")
+    e = py.test.raises(TypeError, "q - a")
+    assert str(e.value) == "cannot subtract cdata 'short *' and cdata 'int *'"
+
+def test_ptr_sub_unaligned():
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    a = cast(BIntPtr, 1240)
+    for bi in range(1430, 1438):
+        b = cast(BIntPtr, bi)
+        if ((bi - 1240) % size_of_int()) == 0:
+            assert b - a == (bi - 1240) // size_of_int()
+            assert a - b == (1240 - bi) // size_of_int()
+        else:
+            py.test.raises(ValueError, "b - a")
+            py.test.raises(ValueError, "a - b")
+
+def test_cast_primitive_from_cdata():
+    p = new_primitive_type("int")
+    n = cast(p, cast(p, -42))
+    assert int(n) == -42
+    #
+    p = new_primitive_type("unsigned int")
+    n = cast(p, cast(p, 42))
+    assert int(n) == 42
+    #
+    p = new_primitive_type("long long")
+    n = cast(p, cast(p, -(1<<60)))
+    assert int(n) == -(1<<60)
+    #
+    p = new_primitive_type("unsigned long long")
+    n = cast(p, cast(p, 1<<63))
+    assert int(n) == 1<<63
+    #
+    p = new_primitive_type("float")
+    n = cast(p, cast(p, 42.5))
+    assert float(n) == 42.5
+    #
+    p = new_primitive_type("char")
+    n = cast(p, cast(p, "A"))
+    assert int(n) == ord("A")
+
+def test_new_primitive_from_cdata():
+    p = new_primitive_type("int")
+    p1 = new_pointer_type(p)
+    n = newp(p1, cast(p, -42))
+    assert n[0] == -42
+    #
+    p = new_primitive_type("unsigned int")
+    p1 = new_pointer_type(p)
+    n = newp(p1, cast(p, 42))
+    assert n[0] == 42
+    #
+    p = new_primitive_type("float")
+    p1 = new_pointer_type(p)
+    n = newp(p1, cast(p, 42.5))
+    assert n[0] == 42.5
+    #
+    p = new_primitive_type("char")
+    p1 = new_pointer_type(p)
+    n = newp(p1, cast(p, "A"))
+    assert n[0] == b"A"
+
+def test_cast_between_pointers():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BIntA = new_array_type(BIntP, None)
+    a = newp(BIntA, [40, 41, 42, 43, 44])
+    BShortP = new_pointer_type(new_primitive_type("short"))
+    b = cast(BShortP, a)
+    c = cast(BIntP, b)
+    assert c[3] == 43
+    BLongLong = new_primitive_type("long long")
+    d = cast(BLongLong, c)
+    e = cast(BIntP, d)
+    assert e[3] == 43
+    f = cast(BIntP, int(d))
+    assert f[3] == 43
+    #
+    b = cast(BShortP, 0)
+    assert not b
+    c = cast(BIntP, b)
+    assert not c
+    assert int(cast(BLongLong, c)) == 0
+
+def test_alignof():
+    BInt = new_primitive_type("int")
+    assert alignof(BInt) == sizeof(BInt)
+    BPtr = new_pointer_type(BInt)
+    assert alignof(BPtr) == sizeof(BPtr)
+    BArray = new_array_type(BPtr, None)
+    assert alignof(BArray) == alignof(BInt)
+
+def test_new_struct_type():
+    BStruct = new_struct_type("foo")
+    assert repr(BStruct) == "<ctype 'foo'>"
+    BStruct = new_struct_type("struct foo")
+    assert repr(BStruct) == "<ctype 'struct foo'>"
+    BPtr = new_pointer_type(BStruct)
+    assert repr(BPtr) == "<ctype 'struct foo *'>"
+    py.test.raises(ValueError, sizeof, BStruct)
+    py.test.raises(ValueError, alignof, BStruct)
+
+def test_new_union_type():
+    BUnion = new_union_type("union foo")
+    assert repr(BUnion) == "<ctype 'union foo'>"
+    BPtr = new_pointer_type(BUnion)
+    assert repr(BPtr) == "<ctype 'union foo *'>"
+
+def test_complete_struct():
+    BLong = new_primitive_type("long")
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("struct foo")
+    assert BStruct.kind == "struct"
+    assert BStruct.cname == "struct foo"
+    assert BStruct.fields is None
+    check_dir(BStruct, ['cname', 'kind', 'fields'])
+    #
+    complete_struct_or_union(BStruct, [('a1', BLong, -1),
+                                       ('a2', BChar, -1),
+                                       ('a3', BShort, -1)])
+    d = BStruct.fields
+    assert len(d) == 3
+    assert d[0][0] == 'a1'
+    assert d[0][1].type is BLong
+    assert d[0][1].offset == 0
+    assert d[0][1].bitshift == -1
+    assert d[0][1].bitsize == -1
+    assert d[1][0] == 'a2'
+    assert d[1][1].type is BChar
+    assert d[1][1].offset == sizeof(BLong)
+    assert d[1][1].bitshift == -1
+    assert d[1][1].bitsize == -1
+    assert d[2][0] == 'a3'
+    assert d[2][1].type is BShort
+    assert d[2][1].offset == sizeof(BLong) + sizeof(BShort)
+    assert d[2][1].bitshift == -1
+    assert d[2][1].bitsize == -1
+    assert sizeof(BStruct) == 2 * sizeof(BLong)
+    assert alignof(BStruct) == alignof(BLong)
+
+def test_complete_union():
+    BLong = new_primitive_type("long")
+    BChar = new_primitive_type("char")
+    BUnion = new_union_type("union foo")
+    assert BUnion.kind == "union"
+    assert BUnion.cname == "union foo"
+    assert BUnion.fields is None
+    complete_struct_or_union(BUnion, [('a1', BLong, -1),
+                                      ('a2', BChar, -1)])
+    d = BUnion.fields
+    assert len(d) == 2
+    assert d[0][0] == 'a1'
+    assert d[0][1].type is BLong
+    assert d[0][1].offset == 0
+    assert d[1][0] == 'a2'
+    assert d[1][1].type is BChar
+    assert d[1][1].offset == 0
+    assert sizeof(BUnion) == sizeof(BLong)
+    assert alignof(BUnion) == alignof(BLong)
+
+def test_struct_instance():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    p = cast(BStructPtr, 42)
+    e = py.test.raises(AttributeError, "p.a1")    # opaque
+    assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: "
+                            "cannot read fields")
+    e = py.test.raises(AttributeError, "p.a1 = 10")    # opaque
+    assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: "
+                            "cannot write fields")
+
+    complete_struct_or_union(BStruct, [('a1', BInt, -1),
+                                       ('a2', BInt, -1)])
+    p = newp(BStructPtr, None)
+    s = p[0]
+    assert s.a1 == 0
+    s.a2 = 123
+    assert s.a1 == 0
+    assert s.a2 == 123
+    py.test.raises(OverflowError, "s.a1 = sys.maxsize+1")
+    assert s.a1 == 0
+    e = py.test.raises(AttributeError, "p.foobar")
+    assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'"
+    e = py.test.raises(AttributeError, "p.foobar = 42")
+    assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'"
+    e = py.test.raises(AttributeError, "s.foobar")
+    assert str(e.value) == "cdata 'struct foo' has no field 'foobar'"
+    e = py.test.raises(AttributeError, "s.foobar = 42")
+    assert str(e.value) == "cdata 'struct foo' has no field 'foobar'"
+    j = cast(BInt, 42)
+    e = py.test.raises(AttributeError, "j.foobar")
+    assert str(e.value) == "cdata 'int' has no attribute 'foobar'"
+    e = py.test.raises(AttributeError, "j.foobar = 42")
+    assert str(e.value) == "cdata 'int' has no attribute 'foobar'"
+    j = cast(new_pointer_type(BInt), 42)
+    e = py.test.raises(AttributeError, "j.foobar")
+    assert str(e.value) == "cdata 'int *' has no attribute 'foobar'"
+    e = py.test.raises(AttributeError, "j.foobar = 42")
+    assert str(e.value) == "cdata 'int *' has no attribute 'foobar'"
+    pp = newp(new_pointer_type(BStructPtr), p)
+    e = py.test.raises(AttributeError, "pp.a1")
+    assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'"
+    e = py.test.raises(AttributeError, "pp.a1 = 42")
+    assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'"
+
+def test_union_instance():
+    BInt = new_primitive_type("int")
+    BUInt = new_primitive_type("unsigned int")
+    BUnion = new_union_type("union bar")
+    complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)])
+    p = newp(new_pointer_type(BUnion), [-42])
+    bigval = -42 + (1 << (8*size_of_int()))
+    assert p.a1 == -42
+    assert p.a2 == bigval
+    p = newp(new_pointer_type(BUnion), {'a2': bigval})
+    assert p.a1 == -42
+    assert p.a2 == bigval
+    py.test.raises(OverflowError, newp, new_pointer_type(BUnion),
+                   {'a1': bigval})
+    p = newp(new_pointer_type(BUnion), [])
+    assert p.a1 == p.a2 == 0
+
+def test_struct_pointer():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BInt, -1),
+                                       ('a2', BInt, -1)])
+    p = newp(BStructPtr, None)
+    assert p.a1 == 0      # read/write via the pointer (C equivalent: '->')
+    p.a2 = 123
+    assert p.a1 == 0
+    assert p.a2 == 123
+
+def test_struct_init_list():
+    BVoidP = new_pointer_type(new_void_type())
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BInt, -1),
+                                       ('a2', BInt, -1),
+                                       ('a3', BInt, -1),
+                                       ('p4', BIntPtr, -1)])
+    s = newp(BStructPtr, [123, 456])
+    assert s.a1 == 123
+    assert s.a2 == 456
+    assert s.a3 == 0
+    assert s.p4 == cast(BVoidP, 0)
+    assert s.p4 != 0
+    #
+    s = newp(BStructPtr, {'a2': 41122, 'a3': -123})
+    assert s.a1 == 0
+    assert s.a2 == 41122
+    assert s.a3 == -123
+    assert s.p4 == cast(BVoidP, 0)
+    #
+    py.test.raises(KeyError, newp, BStructPtr, {'foobar': 0})
+    #
+    p = newp(BIntPtr, 14141)
+    s = newp(BStructPtr, [12, 34, 56, p])
+    assert s.p4 == p
+    assert s.p4
+    #
+    s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)])
+    assert s.p4 == cast(BVoidP, 0)
+    assert not s.p4
+    #
+    py.test.raises(TypeError, newp, BStructPtr, [12, 34, 56, None])
+
+def test_array_in_struct():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    BArrayInt5 = new_array_type(new_pointer_type(BInt), 5)
+    complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)])
+    s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]])
+    assert s.a1[2] == 27
+    assert repr(s.a1).startswith("<cdata 'int[5]' 0x")
+
+def test_offsetof():
+    def offsetof(BType, fieldname):
+        return typeoffsetof(BType, fieldname)[1]
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    py.test.raises(TypeError, offsetof, BInt, "abc")
+    py.test.raises(TypeError, offsetof, BStruct, "abc")
+    complete_struct_or_union(BStruct, [('abc', BInt, -1), ('def', BInt, -1)])
+    assert offsetof(BStruct, 'abc') == 0
+    assert offsetof(BStruct, 'def') == size_of_int()
+    py.test.raises(KeyError, offsetof, BStruct, "ghi")
+    assert offsetof(new_pointer_type(BStruct), "def") == size_of_int()
+
+def test_function_type():
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt, BInt), BInt, False)
+    assert repr(BFunc) == "<ctype 'int(*)(int, int)'>"
+    BFunc2 = new_function_type((), BFunc, False)
+    assert repr(BFunc2) == "<ctype 'int(*(*)())(int, int)'>"
+
+def test_inspect_function_type():
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt, BInt), BInt, False)
+    assert BFunc.kind == "function"
+    assert BFunc.cname == "int(*)(int, int)"
+    assert BFunc.args == (BInt, BInt)
+    assert BFunc.result is BInt
+    assert BFunc.ellipsis is False
+    assert BFunc.abi == FFI_DEFAULT_ABI
+
+def test_function_type_taking_struct():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc = new_function_type((BStruct,), BShort, False)
+    assert repr(BFunc) == "<ctype 'short(*)(struct foo)'>"
+
+def test_function_void_result():
+    BVoid = new_void_type()
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt, BInt), BVoid, False)
+    assert repr(BFunc) == "<ctype 'void(*)(int, int)'>"
+
+def test_function_void_arg():
+    BVoid = new_void_type()
+    BInt = new_primitive_type("int")
+    py.test.raises(TypeError, new_function_type, (BVoid,), BInt, False)
+
+def test_call_function_0():
+    BSignedChar = new_primitive_type("signed char")
+    BFunc0 = new_function_type((BSignedChar, BSignedChar), BSignedChar, False)
+    f = cast(BFunc0, _testfunc(0))
+    assert f(40, 2) == 42
+    assert f(-100, -100) == -200 + 256
+    py.test.raises(OverflowError, f, 128, 0)
+    py.test.raises(OverflowError, f, 0, 128)
+
+def test_call_function_0_pretend_bool_result():
+    BSignedChar = new_primitive_type("signed char")
+    BBool = new_primitive_type("_Bool")
+    BFunc0 = new_function_type((BSignedChar, BSignedChar), BBool, False)
+    f = cast(BFunc0, _testfunc(0))
+    assert f(40, -39) is True
+    assert f(40, -40) is False
+    py.test.raises(ValueError, f, 40, 2)
+
+def test_call_function_1():
+    BInt = new_primitive_type("int")
+    BLong = new_primitive_type("long")
+    BFunc1 = new_function_type((BInt, BLong), BLong, False)
+    f = cast(BFunc1, _testfunc(1))
+    assert f(40, 2) == 42
+    assert f(-100, -100) == -200
+    int_max = (1 << (8*size_of_int()-1)) - 1
+    long_max = (1 << (8*size_of_long()-1)) - 1
+    if int_max == long_max:
+        assert f(int_max, 1) == - int_max - 1
+    else:
+        assert f(int_max, 1) == int_max + 1
+
+def test_call_function_2():
+    BLongLong = new_primitive_type("long long")
+    BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False)
+    f = cast(BFunc2, _testfunc(2))
+    longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1
+    assert f(longlong_max - 42, 42) == longlong_max
+    assert f(43, longlong_max - 42) == - longlong_max - 1
+
+def test_call_function_3():
+    BFloat = new_primitive_type("float")
+    BDouble = new_primitive_type("double")
+    BFunc3 = new_function_type((BFloat, BDouble), BDouble, False)
+    f = cast(BFunc3, _testfunc(3))
+    assert f(1.25, 5.1) == 1.25 + 5.1     # exact
+    res = f(1.3, 5.1)
+    assert res != 6.4 and abs(res - 6.4) < 1E-5    # inexact
+
+def test_call_function_4():
+    BFloat = new_primitive_type("float")
+    BDouble = new_primitive_type("double")
+    BFunc4 = new_function_type((BFloat, BDouble), BFloat, False)
+    f = cast(BFunc4, _testfunc(4))
+    res = f(1.25, 5.1)
+    assert res != 6.35 and abs(res - 6.35) < 1E-5    # inexact
+
+def test_call_function_5():
+    BVoid = new_void_type()
+    BFunc5 = new_function_type((), BVoid, False)
+    f = cast(BFunc5, _testfunc(5))
+    f()   # did not crash
+
+def test_call_function_6():
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    BFunc6 = new_function_type((BIntPtr,), BIntPtr, False)
+    f = cast(BFunc6, _testfunc(6))
+    x = newp(BIntPtr, 42)
+    res = f(x)
+    assert typeof(res) is BIntPtr
+    assert res[0] == 42 - 1000
+    #
+    BIntArray = new_array_type(BIntPtr, None)
+    BFunc6bis = new_function_type((BIntArray,), BIntPtr, False)
+    f = cast(BFunc6bis, _testfunc(6))
+    #
+    res = f([142])
+    assert typeof(res) is BIntPtr
+    assert res[0] == 142 - 1000
+    #
+    res = f((143,))
+    assert typeof(res) is BIntPtr
+    assert res[0] == 143 - 1000
+    #
+    x = newp(BIntArray, [242])
+    res = f(x)
+    assert typeof(res) is BIntPtr
+    assert res[0] == 242 - 1000
+    #
+    py.test.raises(TypeError, f, 123456)
+    py.test.raises(TypeError, f, "foo")
+    py.test.raises(TypeError, f, u+"bar")
+
+def test_call_function_7():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc7 = new_function_type((BStruct,), BShort, False)
+    f = cast(BFunc7, _testfunc(7))
+    res = f({'a1': b'A', 'a2': -4042})
+    assert res == -4042 + ord(b'A')
+    #
+    x = newp(BStructPtr, {'a1': b'A', 'a2': -4042})
+    res = f(x[0])
+    assert res == -4042 + ord(b'A')
+
+def test_call_function_20():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc20 = new_function_type((BStructPtr,), BShort, False)
+    f = cast(BFunc20, _testfunc(20))
+    x = newp(BStructPtr, {'a1': b'A', 'a2': -4042})
+    # can't pass a 'struct foo'
+    py.test.raises(TypeError, f, x[0])
+
+def test_call_function_21():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [('a', BInt, -1),
+                                       ('b', BInt, -1),
+                                       ('c', BInt, -1),
+                                       ('d', BInt, -1),
+                                       ('e', BInt, -1),
+                                       ('f', BInt, -1),
+                                       ('g', BInt, -1),
+                                       ('h', BInt, -1),
+                                       ('i', BInt, -1),
+                                       ('j', BInt, -1)])
+    BFunc21 = new_function_type((BStruct,), BInt, False)
+    f = cast(BFunc21, _testfunc(21))
+    res = f(list(range(13, 3, -1)))
+    lst = [(n << i) for (i, n) in enumerate(range(13, 3, -1))]
+    assert res == sum(lst)
+
+def test_call_function_22():
+    BInt = new_primitive_type("int")
+    BArray10 = new_array_type(new_pointer_type(BInt), 10)
+    BStruct = new_struct_type("struct foo")
+    BStructP = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BArray10, -1)])
+    BFunc22 = new_function_type((BStruct, BStruct), BStruct, False)
+    f = cast(BFunc22, _testfunc(22))
+    p1 = newp(BStructP, {'a': list(range(100, 110))})
+    p2 = newp(BStructP, {'a': list(range(1000, 1100, 10))})
+    res = f(p1[0], p2[0])
+    for i in range(10):
+        assert res.a[i] == p1.a[i] - p2.a[i]
+
+def test_call_function_23():
+    BVoid = new_void_type()          # declaring the function as int(void*)
+    BVoidP = new_pointer_type(BVoid)
+    BInt = new_primitive_type("int")
+    BFunc23 = new_function_type((BVoidP,), BInt, False)
+    f = cast(BFunc23, _testfunc(23))
+    res = f(b"foo")
+    assert res == 1000 * ord(b'f')
+    res = f(cast(BVoidP, 0))        # NULL
+    assert res == -42
+    py.test.raises(TypeError, f, None)
+    py.test.raises(TypeError, f, 0)
+    py.test.raises(TypeError, f, 0.0)
+
+def test_call_function_23_bis():
+    # declaring the function as int(unsigned char*)
+    BUChar = new_primitive_type("unsigned char")
+    BUCharP = new_pointer_type(BUChar)
+    BInt = new_primitive_type("int")
+    BFunc23 = new_function_type((BUCharP,), BInt, False)
+    f = cast(BFunc23, _testfunc(23))
+    res = f(b"foo")
+    assert res == 1000 * ord(b'f')
+
+def test_call_function_23_bool_array():
+    # declaring the function as int(_Bool*)
+    BBool = new_primitive_type("_Bool")
+    BBoolP = new_pointer_type(BBool)
+    BInt = new_primitive_type("int")
+    BFunc23 = new_function_type((BBoolP,), BInt, False)
+    f = cast(BFunc23, _testfunc(23))
+    res = f(b"\x01\x01")
+    assert res == 1000
+    py.test.raises(ValueError, f, b"\x02\x02")
+
+def test_cannot_pass_struct_with_array_of_length_0():
+    BInt = new_primitive_type("int")
+    BArray0 = new_array_type(new_pointer_type(BInt), 0)
+    BStruct = new_struct_type("struct foo")
+    BStructP = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BArray0)])
+    BFunc = new_function_type((BStruct,), BInt, False)
+    py.test.raises(NotImplementedError, cast(BFunc, 123), cast(BStructP, 123))
+    BFunc2 = new_function_type((BInt,), BStruct, False)
+    py.test.raises(NotImplementedError, cast(BFunc2, 123), 123)
+
+def test_call_function_9():
+    BInt = new_primitive_type("int")
+    BFunc9 = new_function_type((BInt,), BInt, True)    # vararg
+    f = cast(BFunc9, _testfunc(9))
+    assert f(0) == 0
+    assert f(1, cast(BInt, 42)) == 42
+    assert f(2, cast(BInt, 40), cast(BInt, 2)) == 42
+    py.test.raises(TypeError, f, 1, 42)
+    py.test.raises(TypeError, f, 2, None)
+    # promotion of chars and shorts to ints
+    BSChar = new_primitive_type("signed char")
+    BUChar = new_primitive_type("unsigned char")
+    BSShort = new_primitive_type("short")
+    assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192
+
+def test_call_function_24():
+    BFloat = new_primitive_type("float")
+    BFloatComplex = new_primitive_type("float _Complex")
+    BFunc3 = new_function_type((BFloat, BFloat), BFloatComplex, False)
+    if 0:   # libffi returning nonsense silently, so logic disabled for now
+        f = cast(BFunc3, _testfunc(24))
+        result = f(1.25, 5.1)
+        assert type(result) == complex
+        assert result.real == 1.25   # exact
+        assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact
+    else:
+        f = cast(BFunc3, _testfunc(9))
+        py.test.raises(NotImplementedError, f, 12.3, 34.5)
+
+def test_call_function_25():
+    BDouble = new_primitive_type("double")
+    BDoubleComplex = new_primitive_type("double _Complex")
+    BFunc3 = new_function_type((BDouble, BDouble), BDoubleComplex, False)
+    if 0:   # libffi returning nonsense silently, so logic disabled for now
+        f = cast(BFunc3, _testfunc(25))
+        result = f(1.25, 5.1)
+        assert type(result) == complex
+        assert result.real == 1.25   # exact
+        assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-10) # inexact
+    else:
+        f = cast(BFunc3, _testfunc(9))
+        py.test.raises(NotImplementedError, f, 12.3, 34.5)
+
+def test_cannot_call_with_a_autocompleted_struct():
+    BSChar = new_primitive_type("signed char")
+    BDouble = new_primitive_type("double")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('c', BDouble, -1, 8),
+                                       ('a', BSChar, -1, 2),
+                                       ('b', BSChar, -1, 0)])
+    BFunc = new_function_type((BStruct,), BDouble)   # internally not callable
+    dummy_func = cast(BFunc, 42)
+    e = py.test.raises(NotImplementedError, dummy_func, "?")
+    msg = ("ctype 'struct foo' not supported as argument.  It is a struct "
+           'declared with "...;", but the C calling convention may depend '
+           "on the missing fields; or, it contains anonymous struct/unions.  "
+           "Such structs are only supported as argument if the function is "
+           "'API mode' and non-variadic (i.e. declared inside ffibuilder."
+           "cdef()+ffibuilder.set_source() and not taking a final '...' "
+           "argument)")
+    assert str(e.value) == msg
+
+def test_new_charp():
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    x = newp(BCharA, 42)
+    assert len(x) == 42
+    x = newp(BCharA, b"foobar")
+    assert len(x) == 7
+
+def test_load_and_call_function():
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BLong = new_primitive_type("long")
+    BFunc = new_function_type((BCharP,), BLong, False)
+    ll = find_and_load_library('c')
+    strlen = ll.load_function(BFunc, "strlen")
+    input = newp(new_array_type(BCharP, None), b"foobar")
+    assert strlen(input) == 6
+    #
+    assert strlen(b"foobarbaz") == 9
+    #
+    BVoidP = new_pointer_type(new_void_type())
+    strlenaddr = ll.load_function(BVoidP, "strlen")
+    assert strlenaddr == cast(BVoidP, strlen)
+
+def test_read_variable():
+    ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard
+    ## https://bugs.pypy.org/issue1643
+    if not sys.platform.startswith("linux"):
+        py.test.skip("untested")
+    BVoidP = new_pointer_type(new_void_type())
+    ll = find_and_load_library('c')
+    stderr = ll.read_variable(BVoidP, "stderr")
+    assert stderr == cast(BVoidP, _testfunc(8))
+    #
+    ll.close_lib()
+    py.test.raises(ValueError, ll.read_variable, BVoidP, "stderr")
+
+def test_read_variable_as_unknown_length_array():
+    ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard
+    ## https://bugs.pypy.org/issue1643
+    if not sys.platform.startswith("linux"):
+        py.test.skip("untested")
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    BArray = new_array_type(BCharP, None)
+    ll = find_and_load_library('c')
+    stderr = ll.read_variable(BArray, "stderr")
+    assert repr(stderr).startswith("<cdata 'char *' 0x")
+    # ^^ and not 'char[]', which is basically not allowed and would crash
+
+def test_write_variable():
+    ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard
+    ## https://bugs.pypy.org/issue1643
+    if not sys.platform.startswith("linux"):
+        py.test.skip("untested")
+    BVoidP = new_pointer_type(new_void_type())
+    ll = find_and_load_library('c')
+    stderr = ll.read_variable(BVoidP, "stderr")
+    ll.write_variable(BVoidP, "stderr", cast(BVoidP, 0))
+    assert ll.read_variable(BVoidP, "stderr") is not None
+    assert not ll.read_variable(BVoidP, "stderr")
+    ll.write_variable(BVoidP, "stderr", stderr)
+    assert ll.read_variable(BVoidP, "stderr") == stderr
+    #
+    ll.close_lib()
+    py.test.raises(ValueError, ll.write_variable, BVoidP, "stderr", stderr)
+
+def test_callback():
+    BInt = new_primitive_type("int")
+    def make_callback():
+        def cb(n):
+            return n + 1
+        BFunc = new_function_type((BInt,), BInt, False)
+        return callback(BFunc, cb, 42)    # 'cb' and 'BFunc' go out of scope
+    f = make_callback()
+    assert f(-142) == -141
+    assert repr(f).startswith(
+        "<cdata 'int(*)(int)' calling <function ")
+    assert "cb at 0x" in repr(f)
+    e = py.test.raises(TypeError, f)
+    assert str(e.value) == "'int(*)(int)' expects 1 arguments, got 0"
+
+def test_callback_exception():
+    try:
+        import cStringIO
+    except ImportError:
+        import io as cStringIO    # Python 3
+    import linecache
+    def matches(istr, ipattern):
+        str, pattern = istr, ipattern
+        while '$' in pattern:
+            i = pattern.index('$')
+            assert str[:i] == pattern[:i]
+            j = str.find(pattern[i+1], i)
+            assert i + 1 <= j <= str.find('\n', i)
+            str = str[j:]
+            pattern = pattern[i+1:]
+        assert str == pattern
+        return True
+    def check_value(x):
+        if x == 10000:
+            raise ValueError(42)
+    def Zcb1(x):
+        check_value(x)
+        return x * 3
+    BShort = new_primitive_type("short")
+    BFunc = new_function_type((BShort,), BShort, False)
+    f = callback(BFunc, Zcb1, -42)
+    #
+    seen = []
+    oops_result = None
+    def oops(*args):
+        seen.append(args)
+        return oops_result
+    ff = callback(BFunc, Zcb1, -42, oops)
+    #
+    orig_stderr = sys.stderr
+    orig_getline = linecache.getline
+    try:
+        linecache.getline = lambda *args: 'LINE'    # hack: speed up PyPy tests
+        sys.stderr = cStringIO.StringIO()
+        assert f(100) == 300
+        assert sys.stderr.getvalue() == ''
+        assert f(10000) == -42
+        assert matches(sys.stderr.getvalue(), """\
+From cffi callback <function$Zcb1 at 0x$>:
+Traceback (most recent call last):
+  File "$", line $, in Zcb1
+    $
+  File "$", line $, in check_value
+    $
+ValueError: 42
+""")
+        sys.stderr = cStringIO.StringIO()
+        bigvalue = 20000
+        assert f(bigvalue) == -42
+        assert matches(sys.stderr.getvalue(), """\
+From cffi callback <function$Zcb1 at 0x$>:
+Trying to convert the result back to C:
+OverflowError: integer 60000 does not fit 'short'
+""")
+        sys.stderr = cStringIO.StringIO()
+        bigvalue = 20000
+        assert len(seen) == 0
+        assert ff(bigvalue) == -42
+        assert sys.stderr.getvalue() == ""
+        assert len(seen) == 1
+        exc, val, tb = seen[0]
+        assert exc is OverflowError
+        assert str(val) == "integer 60000 does not fit 'short'"
+        #
+        sys.stderr = cStringIO.StringIO()
+        bigvalue = 20000
+        del seen[:]
+        oops_result = 81
+        assert ff(bigvalue) == 81
+        oops_result = None
+        assert sys.stderr.getvalue() == ""
+        assert len(seen) == 1
+        exc, val, tb = seen[0]
+        assert exc is OverflowError
+        assert str(val) == "integer 60000 does not fit 'short'"
+        #
+        sys.stderr = cStringIO.StringIO()
+        bigvalue = 20000
+        del seen[:]
+        oops_result = "xy"     # not None and not an int!
+        assert ff(bigvalue) == -42
+        oops_result = None
+        assert matches(sys.stderr.getvalue(), """\
+From cffi callback <function$Zcb1 at 0x$>:
+Trying to convert the result back to C:
+OverflowError: integer 60000 does not fit 'short'
+
+During the call to 'onerror', another exception occurred:
+
+TypeError: $integer$
+""")
+        #
+        sys.stderr = cStringIO.StringIO()
+        seen = "not a list"    # this makes the oops() function crash
+        assert ff(bigvalue) == -42
+        assert matches(sys.stderr.getvalue(), """\
+From cffi callback <function$Zcb1 at 0x$>:
+Trying to convert the result back to C:
+OverflowError: integer 60000 does not fit 'short'
+
+During the call to 'onerror', another exception occurred:
+
+Traceback (most recent call last):
+  File "$", line $, in oops
+    $
+AttributeError: 'str' object has no attribute 'append'
+""")
+    finally:
+        sys.stderr = orig_stderr
+        linecache.getline = orig_getline
+
+def test_callback_return_type():
+    for rettype in ["signed char", "short", "int", "long", "long long",
+                    "unsigned char", "unsigned short", "unsigned int",
+                    "unsigned long", "unsigned long long"]:
+        BRet = new_primitive_type(rettype)
+        def cb(n):
+            return n + 1
+        BFunc = new_function_type((BRet,), BRet)
+        f = callback(BFunc, cb, 42)
+        assert f(41) == 42
+        if rettype.startswith("unsigned "):
+            min = 0
+            max = (1 << (8*sizeof(BRet))) - 1
+        else:
+            min = -(1 << (8*sizeof(BRet)-1))
+            max = (1 << (8*sizeof(BRet)-1)) - 1
+        assert f(min) == min + 1
+        assert f(max - 1) == max
+        assert f(max) == 42
+
+def test_a_lot_of_callbacks():
+    BIGNUM = 10000
+    if 'PY_DOT_PY' in globals(): BIGNUM = 100   # tests on py.py
+    #
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt,), BInt, False)
+    def make_callback(m):
+        def cb(n):
+            return n + m
+        return callback(BFunc, cb, 42)    # 'cb' and 'BFunc' go out of scope
+    #
+    flist = [make_callback(i) for i in range(BIGNUM)]
+    for i, f in enumerate(flist):
+        assert f(-142) == -142 + i
+
+def test_callback_receiving_tiny_struct():
+    BSChar = new_primitive_type("signed char")
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BSChar, -1),
+                                       ('b', BSChar, -1)])
+    def cb(s):
+        return s.a + 10 * s.b
+    BFunc = new_function_type((BStruct,), BInt)
+    f = callback(BFunc, cb)
+    p = newp(BStructPtr, [-2, -4])
+    n = f(p[0])
+    assert n == -42
+
+def test_callback_returning_tiny_struct():
+    BSChar = new_primitive_type("signed char")
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BSChar, -1),
+                                       ('b', BSChar, -1)])
+    def cb(n):
+        return newp(BStructPtr, [-n, -3*n])[0]
+    BFunc = new_function_type((BInt,), BStruct)
+    f = callback(BFunc, cb)
+    s = f(10)
+    assert typeof(s) is BStruct
+    assert repr(s) == "<cdata 'struct foo' owning 2 bytes>"
+    assert s.a == -10
+    assert s.b == -30
+
+def test_callback_receiving_struct():
+    BSChar = new_primitive_type("signed char")
+    BInt = new_primitive_type("int")
+    BDouble = new_primitive_type("double")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BSChar, -1),
+                                       ('b', BDouble, -1)])
+    def cb(s):
+        return s.a + int(s.b)
+    BFunc = new_function_type((BStruct,), BInt)
+    f = callback(BFunc, cb)
+    p = newp(BStructPtr, [-2, 44.444])
+    n = f(p[0])
+    assert n == 42
+
+def test_callback_returning_struct():
+    BSChar = new_primitive_type("signed char")
+    BInt = new_primitive_type("int")
+    BDouble = new_primitive_type("double")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BSChar, -1),
+                                       ('b', BDouble, -1)])
+    def cb(n):
+        return newp(BStructPtr, [-n, 1E-42])[0]
+    BFunc = new_function_type((BInt,), BStruct)
+    f = callback(BFunc, cb)
+    s = f(10)
+    assert typeof(s) is BStruct
+    assert repr(s) in ["<cdata 'struct foo' owning 12 bytes>",
+                       "<cdata 'struct foo' owning 16 bytes>"]
+    assert s.a == -10
+    assert s.b == 1E-42
+
+def test_callback_receiving_big_struct():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BInt, -1),
+                                       ('b', BInt, -1),
+                                       ('c', BInt, -1),
+                                       ('d', BInt, -1),
+                                       ('e', BInt, -1),
+                                       ('f', BInt, -1),
+                                       ('g', BInt, -1),
+                                       ('h', BInt, -1),
+                                       ('i', BInt, -1),
+                                       ('j', BInt, -1)])
+    def cb(s):
+        for i, name in enumerate("abcdefghij"):
+            assert getattr(s, name) == 13 - i
+        return 42
+    BFunc = new_function_type((BStruct,), BInt)
+    f = callback(BFunc, cb)
+    p = newp(BStructPtr, list(range(13, 3, -1)))
+    n = f(p[0])
+    assert n == 42
+
+def test_callback_returning_big_struct():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BInt, -1),
+                                       ('b', BInt, -1),
+                                       ('c', BInt, -1),
+                                       ('d', BInt, -1),
+                                       ('e', BInt, -1),
+                                       ('f', BInt, -1),
+                                       ('g', BInt, -1),
+                                       ('h', BInt, -1),
+                                       ('i', BInt, -1),
+                                       ('j', BInt, -1)])
+    def cb():
+        return newp(BStructPtr, list(range(13, 3, -1)))[0]
+    BFunc = new_function_type((), BStruct)
+    f = callback(BFunc, cb)
+    s = f()
+    assert typeof(s) is BStruct
+    assert repr(s) in ["<cdata 'struct foo' owning 40 bytes>",
+                       "<cdata 'struct foo' owning 80 bytes>"]
+    for i, name in enumerate("abcdefghij"):
+        assert getattr(s, name) == 13 - i
+
+def test_callback_returning_void():
+    BVoid = new_void_type()
+    BFunc = new_function_type((), BVoid, False)
+    def cb():
+        seen.append(42)
+    f = callback(BFunc, cb)
+    seen = []
+    f()
+    assert seen == [42]
+    py.test.raises(TypeError, callback, BFunc, cb, -42)
+
+def test_enum_type():
+    BUInt = new_primitive_type("unsigned int")
+    BEnum = new_enum_type("foo", (), (), BUInt)
+    assert repr(BEnum) == "<ctype 'foo'>"
+    assert BEnum.kind == "enum"
+    assert BEnum.cname == "foo"
+    assert BEnum.elements == {}
+    #
+    BInt = new_primitive_type("int")
+    BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt)
+    assert BEnum.kind == "enum"
+    assert BEnum.cname == "enum foo"
+    assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'}
+    # 'elements' is not the real dict, but merely a copy
+    BEnum.elements[2] = '??'
+    assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'}
+    #
+    BEnum = new_enum_type("enum bar", ('ab', 'cd'), (5, 5), BUInt)
+    assert BEnum.elements == {5: 'ab'}
+    assert BEnum.relements == {'ab': 5, 'cd': 5}
+
+def test_cast_to_enum():
+    BInt = new_primitive_type("int")
+    BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt)
+    assert sizeof(BEnum) == sizeof(BInt)
+    e = cast(BEnum, 0)
+    assert repr(e) == "<cdata 'enum foo' 0: def>"
+    assert repr(cast(BEnum, -42)) == "<cdata 'enum foo' -42>"
+    assert repr(cast(BEnum, -20)) == "<cdata 'enum foo' -20: ab>"
+    assert string(e) == 'def'
+    assert string(cast(BEnum, -20)) == 'ab'
+    assert int(cast(BEnum, 1)) == 1
+    assert int(cast(BEnum, 0)) == 0
+    assert int(cast(BEnum, -242 + 2**128)) == -242
+    assert string(cast(BEnum, -242 + 2**128)) == '-242'
+    #
+    BUInt = new_primitive_type("unsigned int")
+    BEnum = new_enum_type("enum bar", ('def', 'c', 'ab'), (0, 1, 20), BUInt)
+    e = cast(BEnum, -1)
+    assert repr(e) == "<cdata 'enum bar' 4294967295>"     # unsigned int
+    #
+    BLong = new_primitive_type("long")
+    BEnum = new_enum_type("enum baz", (), (), BLong)
+    assert sizeof(BEnum) == sizeof(BLong)
+    e = cast(BEnum, -1)
+    assert repr(e) == "<cdata 'enum baz' -1>"
+
+def test_enum_with_non_injective_mapping():
+    BInt = new_primitive_type("int")
+    BEnum = new_enum_type("enum foo", ('ab', 'cd'), (7, 7), BInt)
+    e = cast(BEnum, 7)
+    assert repr(e) == "<cdata 'enum foo' 7: ab>"
+    assert string(e) == 'ab'
+
+def test_enum_in_struct():
+    BInt = new_primitive_type("int")
+    BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt)
+    BStruct = new_struct_type("struct bar")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BEnum, -1)])
+    p = newp(BStructPtr, [-20])
+    assert p.a1 == -20
+    p = newp(BStructPtr, [12])
+    assert p.a1 == 12
+    e = py.test.raises(TypeError, newp, BStructPtr, [None])
+    msg = str(e.value)
+    assert ("an integer is required" in msg or  # CPython
+            "unsupported operand type for int(): 'NoneType'" in msg or  # old PyPys
+            "expected integer, got NoneType object" in msg) # newer PyPys
+    py.test.raises(TypeError, 'p.a1 = "def"')
+    if sys.version_info < (3,):
+        BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,), BInt)
+        assert string(cast(BEnum2, 5)) == 'abc'
+        assert type(string(cast(BEnum2, 5))) is str
+
+def test_enum_overflow():
+    max_uint = 2 ** (size_of_int()*8) - 1
+    max_int = max_uint // 2
+    max_ulong = 2 ** (size_of_long()*8) - 1
+    max_long = max_ulong // 2
+    for BPrimitive in [new_primitive_type("int"),
+                       new_primitive_type("unsigned int"),
+                       new_primitive_type("long"),
+                       new_primitive_type("unsigned long")]:
+        for x in [max_uint, max_int, max_ulong, max_long]:
+            for testcase in [x, x+1, -x-1, -x-2]:
+                if int(cast(BPrimitive, testcase)) == testcase:
+                    # fits
+                    BEnum = new_enum_type("foo", ("AA",), (testcase,),
+                                          BPrimitive)
+                    assert int(cast(BEnum, testcase)) == testcase
+                else:
+                    # overflows
+                    py.test.raises(OverflowError, new_enum_type,
+                                   "foo", ("AA",), (testcase,), BPrimitive)
+
+def test_callback_returning_enum():
+    BInt = new_primitive_type("int")
+    BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20), BInt)
+    def cb(n):
+        if n & 1:
+            return cast(BEnum, n)
+        else:
+            return n
+    BFunc = new_function_type((BInt,), BEnum)
+    f = callback(BFunc, cb)
+    assert f(0) == 0
+    assert f(1) == 1
+    assert f(-20) == -20
+    assert f(20) == 20
+    assert f(21) == 21
+
+def test_callback_returning_enum_unsigned():
+    BInt = new_primitive_type("int")
+    BUInt = new_primitive_type("unsigned int")
+    BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20), BUInt)
+    def cb(n):
+        if n & 1:
+            return cast(BEnum, n)
+        else:
+            return n
+    BFunc = new_function_type((BInt,), BEnum)
+    f = callback(BFunc, cb)
+    assert f(0) == 0
+    assert f(1) == 1
+    assert f(-21) == 2**32 - 21
+    assert f(20) == 20
+    assert f(21) == 21
+
+def test_callback_returning_char():
+    BInt = new_primitive_type("int")
+    BChar = new_primitive_type("char")
+    def cb(n):
+        return bytechr(n)
+    BFunc = new_function_type((BInt,), BChar)
+    f = callback(BFunc, cb)
+    assert f(0) == b'\x00'
+    assert f(255) == b'\xFF'
+
+def _hacked_pypy_uni4():
+    pyuni4 = {1: True, 2: False}[len(u+'\U00012345')]
+    return 'PY_DOT_PY' in globals() and not pyuni4
+
+def test_callback_returning_wchar_t():
+    BInt = new_primitive_type("int")
+    BWChar = new_primitive_type("wchar_t")
+    def cb(n):
+        if n == -1:
+            return u+'\U00012345'
+        if n == -2:
+            raise ValueError
+        return unichr(n)
+    BFunc = new_function_type((BInt,), BWChar)
+    f = callback(BFunc, cb)
+    assert f(0) == unichr(0)
+    assert f(255) == unichr(255)
+    assert f(0x1234) == u+'\u1234'
+    if sizeof(BWChar) == 4 and not _hacked_pypy_uni4():
+        assert f(-1) == u+'\U00012345'
+    assert f(-2) == u+'\x00'   # and an exception printed to stderr
+
+def test_struct_with_bitfields():
+    BLong = new_primitive_type("long")
+    BStruct = new_struct_type("struct foo")
+    LONGBITS = 8 * sizeof(BLong)
+    complete_struct_or_union(BStruct, [('a1', BLong, 1),
+                                       ('a2', BLong, 2),
+                                       ('a3', BLong, 3),
+                                       ('a4', BLong, LONGBITS - 5)])
+    d = BStruct.fields
+    assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0
+    assert d[3][1].offset == sizeof(BLong)
+    def f(m, r):
+        if sys.byteorder == 'little':
+            return r
+        else:
+            return LONGBITS - m - r
+    assert d[0][1].bitshift == f(1, 0)
+    assert d[0][1].bitsize == 1
+    assert d[1][1].bitshift == f(2, 1)
+    assert d[1][1].bitsize == 2
+    assert d[2][1].bitshift == f(3, 3)
+    assert d[2][1].bitsize == 3
+    assert d[3][1].bitshift == f(LONGBITS - 5, 0)
+    assert d[3][1].bitsize == LONGBITS - 5
+    assert sizeof(BStruct) == 2 * sizeof(BLong)
+    assert alignof(BStruct) == alignof(BLong)
+
+def test_bitfield_instance():
+    BInt = new_primitive_type("int")
+    BUnsignedInt = new_primitive_type("unsigned int")
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [('a1', BInt, 1),
+                                       ('a2', BUnsignedInt, 2),
+                                       ('a3', BInt, 3)])
+    p = newp(new_pointer_type(BStruct), None)
+    p.a1 = -1
+    assert p.a1 == -1
+    p.a1 = 0
+    py.test.raises(OverflowError, "p.a1 = 2")
+    assert p.a1 == 0
+    #
+    p.a1 = -1
+    p.a2 = 3
+    p.a3 = -4
+    py.test.raises(OverflowError, "p.a3 = 4")
+    e = py.test.raises(OverflowError, "p.a3 = -5")
+    assert str(e.value) == ("value -5 outside the range allowed by the "
+                            "bit field width: -4 <= x <= 3")
+    assert p.a1 == -1 and p.a2 == 3 and p.a3 == -4
+    #
+    # special case for convenience: "int x:1", while normally signed,
+    # allows also setting the value "1" (it still gets read back as -1)
+    p.a1 = 1
+    assert p.a1 == -1
+    e = py.test.raises(OverflowError, "p.a1 = -2")
+    assert str(e.value) == ("value -2 outside the range allowed by the "
+                            "bit field width: -1 <= x <= 1")
+
+def test_bitfield_instance_init():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [('a1', BInt, 1)])
+    p = newp(new_pointer_type(BStruct), [-1])
+    assert p.a1 == -1
+    p = newp(new_pointer_type(BStruct), {'a1': -1})
+    assert p.a1 == -1
+    #
+    BUnion = new_union_type("union bar")
+    complete_struct_or_union(BUnion, [('a1', BInt, 1)])
+    p = newp(new_pointer_type(BUnion), [-1])
+    assert p.a1 == -1
+
+def test_weakref():
+    import _weakref
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    rlist = [_weakref.ref(BInt),
+             _weakref.ref(newp(BPtr, 42)),
+             _weakref.ref(cast(BPtr, 42)),
+             _weakref.ref(cast(BInt, 42)),
+             _weakref.ref(buffer(newp(BPtr, 42))),
+             ]
+    for i in range(5):
+        import gc; gc.collect()
+        if [r() for r in rlist] == [None for r in rlist]:
+            break
+
+def test_no_inheritance():
+    BInt = new_primitive_type("int")
+    try:
+        class foo(type(BInt)): pass
+    except TypeError:
+        pass
+    else:
+        raise AssertionError
+    x = cast(BInt, 42)
+    try:
+        class foo(type(x)): pass
+    except TypeError:
+        pass
+    else:
+        raise AssertionError
+
+def test_assign_string():
+    BChar = new_primitive_type("char")
+    BArray1 = new_array_type(new_pointer_type(BChar), 5)
+    BArray2 = new_array_type(new_pointer_type(BArray1), 5)
+    a = newp(BArray2, [b"abc", b"de", b"ghij"])
+    assert string(a[1]) == b"de"
+    assert string(a[2]) == b"ghij"
+    a[2] = b"."
+    assert string(a[2]) == b"."
+    a[2] = b"12345"
+    assert string(a[2]) == b"12345"
+    e = py.test.raises(IndexError, 'a[2] = b"123456"')
+    assert 'char[5]' in str(e.value)
+    assert 'got 6 characters' in str(e.value)
+
+def test_add_error():
+    x = cast(new_primitive_type("int"), 42)
+    py.test.raises(TypeError, "x + 1")
+    py.test.raises(TypeError, "x - 1")
+
+def test_void_errors():
+    py.test.raises(ValueError, alignof, new_void_type())
+    py.test.raises(TypeError, newp, new_pointer_type(new_void_type()), None)
+
+def test_too_many_items():
+    BChar = new_primitive_type("char")
+    BArray = new_array_type(new_pointer_type(BChar), 5)
+    py.test.raises(IndexError, newp, BArray, tuple(b'123456'))
+    py.test.raises(IndexError, newp, BArray, list(b'123456'))
+    py.test.raises(IndexError, newp, BArray, b'123456')
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [])
+    py.test.raises(TypeError, newp, new_pointer_type(BStruct), b'')
+    py.test.raises(ValueError, newp, new_pointer_type(BStruct), [b'1'])
+
+def test_more_type_errors():
+    BInt = new_primitive_type("int")
+    BChar = new_primitive_type("char")
+    BArray = new_array_type(new_pointer_type(BChar), 5)
+    py.test.raises(TypeError, newp, BArray, 12.34)
+    BArray = new_array_type(new_pointer_type(BInt), 5)
+    py.test.raises(TypeError, newp, BArray, 12.34)
+    BFloat = new_primitive_type("float")
+    py.test.raises(TypeError, cast, BFloat, newp(BArray, None))
+
+def test_more_overflow_errors():
+    BUInt = new_primitive_type("unsigned int")
+    py.test.raises(OverflowError, newp, new_pointer_type(BUInt), -1)
+    py.test.raises(OverflowError, newp, new_pointer_type(BUInt), 2**32)
+
+def test_newp_copying():
+    """Test that we can do newp(<type>, <cdata of the given type>) for most
+    types, including same-type arrays.
+    """
+    BInt = new_primitive_type("int")
+    p = newp(new_pointer_type(BInt), cast(BInt, 42))
+    assert p[0] == 42
+    #
+    BUInt = new_primitive_type("unsigned int")
+    p = newp(new_pointer_type(BUInt), cast(BUInt, 42))
+    assert p[0] == 42
+    #
+    BChar = new_primitive_type("char")
+    p = newp(new_pointer_type(BChar), cast(BChar, '!'))
+    assert p[0] == b'!'
+    #
+    BFloat = new_primitive_type("float")
+    p = newp(new_pointer_type(BFloat), cast(BFloat, 12.25))
+    assert p[0] == 12.25
+    #
+    BStruct = new_struct_type("struct foo_s")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BInt, -1)])
+    s1 = newp(BStructPtr, [42])
+    p1 = newp(new_pointer_type(BStructPtr), s1)
+    assert p1[0] == s1
+    #
+    BArray = new_array_type(new_pointer_type(BInt), None)
+    a1 = newp(BArray, [1, 2, 3, 4])
+    py.test.raises(TypeError, newp, BArray, a1)
+    BArray6 = new_array_type(new_pointer_type(BInt), 6)
+    a1 = newp(BArray6, [10, 20, 30])
+    a2 = newp(BArray6, a1)
+    assert list(a2) == [10, 20, 30, 0, 0, 0]
+    #
+    s1 = newp(BStructPtr, [42])
+    s2 = newp(BStructPtr, s1[0])
+    assert s2.a1 == 42
+    #
+    BUnion = new_union_type("union foo_u")
+    BUnionPtr = new_pointer_type(BUnion)
+    complete_struct_or_union(BUnion, [('a1', BInt, -1)])
+    u1 = newp(BUnionPtr, [42])
+    u2 = newp(BUnionPtr, u1[0])
+    assert u2.a1 == 42
+    #
+    BFunc = new_function_type((BInt,), BUInt)
+    p1 = cast(BFunc, 42)
+    p2 = newp(new_pointer_type(BFunc), p1)
+    assert p2[0] == p1
+
+def test_string():
+    BChar = new_primitive_type("char")
+    assert string(cast(BChar, 42)) == b'*'
+    assert string(cast(BChar, 0)) == b'\x00'
+    BCharP = new_pointer_type(BChar)
+    BArray = new_array_type(BCharP, 10)
+    a = newp(BArray, b"hello")
+    assert len(a) == 10
+    assert string(a) == b"hello"
+    p = a + 2
+    assert string(p) == b"llo"
+    assert string(newp(new_array_type(BCharP, 4), b"abcd")) == b"abcd"
+    py.test.raises(RuntimeError, string, cast(BCharP, 0))
+    assert string(a, 4) == b"hell"
+    assert string(a, 5) == b"hello"
+    assert string(a, 6) == b"hello"
+
+def test_string_byte():
+    BByte = new_primitive_type("signed char")
+    assert string(cast(BByte, 42)) == b'*'
+    assert string(cast(BByte, 0)) == b'\x00'
+    BArray = new_array_type(new_pointer_type(BByte), None)
+    a = newp(BArray, [65, 66, 67])
+    assert type(string(a)) is bytes and string(a) == b'ABC'
+    #
+    BByte = new_primitive_type("unsigned char")
+    assert string(cast(BByte, 42)) == b'*'
+    assert string(cast(BByte, 0)) == b'\x00'
+    BArray = new_array_type(new_pointer_type(BByte), None)
+    a = newp(BArray, [65, 66, 67])
+    assert type(string(a)) is bytes and string(a) == b'ABC'
+    if 'PY_DOT_PY' not in globals() and sys.version_info < (3,):
+        assert string(a, 8).startswith(b'ABC')  # may contain additional garbage
+
+def test_string_wchar():
+    for typename in ["wchar_t", "char16_t", "char32_t"]:
+        _test_string_wchar_variant(typename)
+
+def _test_string_wchar_variant(typename):
+    BWChar = new_primitive_type(typename)
+    assert string(cast(BWChar, 42)) == u+'*'
+    assert string(cast(BWChar, 0x4253)) == u+'\u4253'
+    assert string(cast(BWChar, 0)) == u+'\x00'
+    BArray = new_array_type(new_pointer_type(BWChar), None)
+    a = newp(BArray, [u+'A', u+'B', u+'C'])
+    assert type(string(a)) is unicode and string(a) == u+'ABC'
+    if 'PY_DOT_PY' not in globals() and sys.version_info < (3,):
+        try:
+            # may contain additional garbage
+            assert string(a, 8).startswith(u+'ABC')
+        except ValueError:    # garbage contains values > 0x10FFFF
+            assert sizeof(BWChar) == 4
+
+def test_string_typeerror():
+    BShort = new_primitive_type("short")
+    BArray = new_array_type(new_pointer_type(BShort), None)
+    a = newp(BArray, [65, 66, 67])
+    py.test.raises(TypeError, string, a)
+
+def test_bug_convert_to_ptr():
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BDouble = new_primitive_type("double")
+    x = cast(BDouble, 42)
+    py.test.raises(TypeError, newp, new_pointer_type(BCharP), x)
+
+def test_set_struct_fields():
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharArray10 = new_array_type(BCharP, 10)
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)])
+    p = newp(BStructPtr, None)
+    assert string(p.a1) == b''
+    p.a1 = b'foo'
+    assert string(p.a1) == b'foo'
+    assert list(p.a1) == [b'f', b'o', b'o'] + [b'\x00'] * 7
+    p.a1 = [b'x', b'y']
+    assert string(p.a1) == b'xyo'
+
+def test_invalid_function_result_types():
+    BFunc = new_function_type((), new_void_type())
+    BArray = new_array_type(new_pointer_type(BFunc), 5)        # works
+    new_function_type((), BFunc)    # works
+    new_function_type((), new_primitive_type("int"))
+    new_function_type((), new_pointer_type(BFunc))
+    BUnion = new_union_type("union foo_u")
+    complete_struct_or_union(BUnion, [])
+    BFunc = new_function_type((), BUnion)
+    py.test.raises(NotImplementedError, cast(BFunc, 123))
+    py.test.raises(TypeError, new_function_type, (), BArray)
+
+def test_struct_return_in_func():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BFloat = new_primitive_type("float")
+    BDouble = new_primitive_type("double")
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo_s")
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc10 = new_function_type((BInt,), BStruct)
+    f = cast(BFunc10, _testfunc(10))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct foo_s' owning 4 bytes>"
+    assert s.a1 == bytechr(40)
+    assert s.a2 == 40 * 40
+    #
+    BStruct11 = new_struct_type("struct test11")
+    complete_struct_or_union(BStruct11, [('a1', BInt, -1),
+                                         ('a2', BInt, -1)])
+    BFunc11 = new_function_type((BInt,), BStruct11)
+    f = cast(BFunc11, _testfunc(11))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test11' owning 8 bytes>"
+    assert s.a1 == 40
+    assert s.a2 == 40 * 40
+    #
+    BStruct12 = new_struct_type("struct test12")
+    complete_struct_or_union(BStruct12, [('a1', BDouble, -1),
+                                         ])
+    BFunc12 = new_function_type((BInt,), BStruct12)
+    f = cast(BFunc12, _testfunc(12))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test12' owning 8 bytes>"
+    assert s.a1 == 40.0
+    #
+    BStruct13 = new_struct_type("struct test13")
+    complete_struct_or_union(BStruct13, [('a1', BInt, -1),
+                                         ('a2', BInt, -1),
+                                         ('a3', BInt, -1)])
+    BFunc13 = new_function_type((BInt,), BStruct13)
+    f = cast(BFunc13, _testfunc(13))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test13' owning 12 bytes>"
+    assert s.a1 == 40
+    assert s.a2 == 40 * 40
+    assert s.a3 == 40 * 40 * 40
+    #
+    BStruct14 = new_struct_type("struct test14")
+    complete_struct_or_union(BStruct14, [('a1', BFloat, -1),
+                                         ])
+    BFunc14 = new_function_type((BInt,), BStruct14)
+    f = cast(BFunc14, _testfunc(14))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test14' owning 4 bytes>"
+    assert s.a1 == 40.0
+    #
+    BStruct15 = new_struct_type("struct test15")
+    complete_struct_or_union(BStruct15, [('a1', BFloat, -1),
+                                         ('a2', BInt, -1)])
+    BFunc15 = new_function_type((BInt,), BStruct15)
+    f = cast(BFunc15, _testfunc(15))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test15' owning 8 bytes>"
+    assert s.a1 == 40.0
+    assert s.a2 == 40 * 40
+    #
+    BStruct16 = new_struct_type("struct test16")
+    complete_struct_or_union(BStruct16, [('a1', BFloat, -1),
+                                         ('a2', BFloat, -1)])
+    BFunc16 = new_function_type((BInt,), BStruct16)
+    f = cast(BFunc16, _testfunc(16))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test16' owning 8 bytes>"
+    assert s.a1 == 40.0
+    assert s.a2 == -40.0
+    #
+    BStruct17 = new_struct_type("struct test17")
+    complete_struct_or_union(BStruct17, [('a1', BInt, -1),
+                                         ('a2', BFloat, -1)])
+    BFunc17 = new_function_type((BInt,), BStruct17)
+    f = cast(BFunc17, _testfunc(17))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test17' owning 8 bytes>"
+    assert s.a1 == 40
+    assert s.a2 == 40.0 * 40.0
+    #
+    BStruct17Ptr = new_pointer_type(BStruct17)
+    BFunc18 = new_function_type((BStruct17Ptr,), BInt)
+    f = cast(BFunc18, _testfunc(18))
+    x = f([[40, 2.5]])
+    assert x == 42
+    x = f([{'a2': 43.1}])
+    assert x == 43
+
+def test_cast_with_functionptr():
+    BFunc = new_function_type((), new_void_type())
+    BFunc2 = new_function_type((), new_primitive_type("short"))
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BFunc, -1)])
+    newp(BStructPtr, [cast(BFunc, 0)])
+    newp(BStructPtr, [cast(BCharP, 0)])
+    py.test.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)])
+    py.test.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)])
+
+def test_wchar():
+    _test_wchar_variant("wchar_t")
+    if sys.platform.startswith("linux"):
+        BWChar = new_primitive_type("wchar_t")
+        assert sizeof(BWChar) == 4
+        # wchar_t is often signed on Linux, but not always (e.g. on ARM)
+        assert int(cast(BWChar, -1)) in (-1, 4294967295)
+
+def test_char16():
+    BChar16 = new_primitive_type("char16_t")
+    assert sizeof(BChar16) == 2
+    _test_wchar_variant("char16_t")
+    assert int(cast(BChar16, -1)) == 0xffff       # always unsigned
+
+def test_char32():
+    BChar32 = new_primitive_type("char32_t")
+    assert sizeof(BChar32) == 4
+    _test_wchar_variant("char32_t")
+    assert int(cast(BChar32, -1)) == 0xffffffff   # always unsigned
+
+def _test_wchar_variant(typename):
+    BWChar = new_primitive_type(typename)
+    BInt = new_primitive_type("int")
+    pyuni4 = {1: True, 2: False}[len(u+'\U00012345')]
+    wchar4 = {2: False, 4: True}[sizeof(BWChar)]
+    assert str(cast(BWChar, 0x45)) == "<cdata '%s' %s'E'>" % (
+        typename, mandatory_u_prefix)
+    assert str(cast(BWChar, 0x1234)) == "<cdata '%s' %s'\u1234'>" % (
+        typename, mandatory_u_prefix)
+    if not _hacked_pypy_uni4():
+        if wchar4:
+            x = cast(BWChar, 0x12345)
+            assert str(x) == "<cdata '%s' %s'\U00012345'>" % (
+                typename, mandatory_u_prefix)
+            assert int(x) == 0x12345
+        else:
+            x = cast(BWChar, 0x18345)
+            assert str(x) == "<cdata '%s' %s'\u8345'>" % (
+                typename, mandatory_u_prefix)
+            assert int(x) == 0x8345
+    #
+    BWCharP = new_pointer_type(BWChar)
+    BStruct = new_struct_type("struct foo_s")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BWChar, -1),
+                                       ('a2', BWCharP, -1)])
+    s = newp(BStructPtr)
+    s.a1 = u+'\x00'
+    assert s.a1 == u+'\x00'
+    py.test.raises(TypeError, "s.a1 = b'a'")
+    py.test.raises(TypeError, "s.a1 = bytechr(0xFF)")
+    s.a1 = u+'\u1234'
+    assert s.a1 == u+'\u1234'
+    if pyuni4:
+        if wchar4:
+            s.a1 = u+'\U00012345'
+            assert s.a1 == u+'\U00012345'
+    elif wchar4:
+        if not _hacked_pypy_uni4():
+            s.a1 = cast(BWChar, 0x12345)
+            assert s.a1 == u+'\ud808\udf45'
+            s.a1 = u+'\ud807\udf44'
+            assert s.a1 == u+'\U00011f44'
+    else:
+        py.test.raises(TypeError, "s.a1 = u+'\U00012345'")
+    #
+    BWCharArray = new_array_type(BWCharP, None)
+    a = newp(BWCharArray, u+'hello \u1234 world')
+    assert len(a) == 14   # including the final null
+    assert string(a) == u+'hello \u1234 world'
+    a[13] = u+'!'
+    assert string(a) == u+'hello \u1234 world!'
+    assert str(a) == repr(a)
+    assert a[6] == u+'\u1234'
+    a[6] = u+'-'
+    assert string(a) == u+'hello - world!'
+    assert str(a) == repr(a)
+    #
+    if wchar4 and not _hacked_pypy_uni4():
+        u1 = u+'\U00012345\U00012346\U00012347'
+        a = newp(BWCharArray, u1)
+        assert len(a) == 4
+        assert string(a) == u1
+        assert len(list(a)) == 4
+        expected = [u+'\U00012345', u+'\U00012346', u+'\U00012347', unichr(0)]
+        assert list(a) == expected
+        got = [a[i] for i in range(4)]
+        assert got == expected
+        py.test.raises(IndexError, 'a[4]')
+    #
+    w = cast(BWChar, 'a')
+    assert repr(w) == "<cdata '%s' %s'a'>" % (typename, mandatory_u_prefix)
+    assert str(w) == repr(w)
+    assert string(w) == u+'a'
+    assert int(w) == ord('a')
+    w = cast(BWChar, 0x1234)
+    assert repr(w) == "<cdata '%s' %s'\u1234'>" % (typename, mandatory_u_prefix)
+    assert str(w) == repr(w)
+    assert string(w) == u+'\u1234'
+    assert int(w) == 0x1234
+    w = cast(BWChar, u+'\u8234')
+    assert repr(w) == "<cdata '%s' %s'\u8234'>" % (typename, mandatory_u_prefix)
+    assert str(w) == repr(w)
+    assert string(w) == u+'\u8234'
+    assert int(w) == 0x8234
+    w = cast(BInt, u+'\u1234')
+    assert repr(w) == "<cdata 'int' 4660>"
+    if wchar4 and not _hacked_pypy_uni4():
+        w = cast(BWChar, u+'\U00012345')
+        assert repr(w) == "<cdata '%s' %s'\U00012345'>" % (
+            typename, mandatory_u_prefix)
+        assert str(w) == repr(w)
+        assert string(w) == u+'\U00012345'
+        assert int(w) == 0x12345
+        w = cast(BInt, u+'\U00012345')
+        assert repr(w) == "<cdata 'int' 74565>"
+    py.test.raises(TypeError, cast, BInt, u+'')
+    py.test.raises(TypeError, cast, BInt, u+'XX')
+    assert int(cast(BInt, u+'a')) == ord('a')
+    #
+    a = newp(BWCharArray, u+'hello - world')
+    p = cast(BWCharP, a)
+    assert string(p) == u+'hello - world'
+    p[6] = u+'\u2345'
+    assert string(p) == u+'hello \u2345 world'
+    #
+    s = newp(BStructPtr, [u+'\u1234', p])
+    assert s.a1 == u+'\u1234'
+    assert s.a2 == p
+    assert str(s.a2) == repr(s.a2)
+    assert string(s.a2) == u+'hello \u2345 world'
+    #
+    q = cast(BWCharP, 0)
+    assert str(q) == repr(q)
+    py.test.raises(RuntimeError, string, q)
+    #
+    def cb(p):
+        assert repr(p).startswith("<cdata '%s *' 0x" % typename)
+        return len(string(p))
+    BFunc = new_function_type((BWCharP,), BInt, False)
+    f = callback(BFunc, cb, -42)
+    assert f(u+'a\u1234b') == 3
+    #
+    if wchar4 and not pyuni4 and not _hacked_pypy_uni4():
+        # try out-of-range wchar_t values
+        x = cast(BWChar, 1114112)
+        py.test.raises(ValueError, string, x)
+        x = cast(BWChar, -1)
+        py.test.raises(ValueError, string, x)
+
+def test_wchar_variants_mix():
+    BWChar  = new_primitive_type("wchar_t")
+    BChar16 = new_primitive_type("char16_t")
+    BChar32 = new_primitive_type("char32_t")
+    assert int(cast(BChar32, cast(BChar16, -2))) == 0xfffe
+    assert int(cast(BWChar, cast(BChar16, -2))) == 0xfffe
+    assert int(cast(BChar16, cast(BChar32, 0x0001f345))) == 0xf345
+    assert int(cast(BChar16, cast(BWChar, 0x0001f345))) == 0xf345
+    #
+    BChar16A = new_array_type(new_pointer_type(BChar16), None)
+    BChar32A = new_array_type(new_pointer_type(BChar32), None)
+    x = cast(BChar32, 'A')
+    py.test.raises(TypeError, newp, BChar16A, [x])
+    x = cast(BChar16, 'A')
+    py.test.raises(TypeError, newp, BChar32A, [x])
+    #
+    a = newp(BChar16A, u+'\U00012345')
+    assert len(a) == 3
+    a = newp(BChar32A, u+'\U00012345')
+    assert len(a) == 2   # even if the Python unicode string above is 2 chars
+
+def test_keepalive_struct():
+    # exception to the no-keepalive rule: p=newp(BStructPtr) returns a
+    # pointer owning the memory, and p[0] returns a pointer to the
+    # struct that *also* owns the memory
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1),
+                                       ('a2', new_primitive_type("int"), -1),
+                                       ('a3', new_primitive_type("int"), -1)])
+    p = newp(BStructPtr)
+    assert repr(p) == "<cdata 'struct foo *' owning 12 bytes>"
+    q = p[0]
+    assert repr(q) == "<cdata 'struct foo' owning 12 bytes>"
+    q.a1 = 123456
+    assert p.a1 == 123456
+    r = cast(BStructPtr, p)
+    assert repr(r[0]).startswith("<cdata 'struct foo &' 0x")
+    del p
+    import gc; gc.collect()
+    assert q.a1 == 123456
+    assert repr(q) == "<cdata 'struct foo' owning 12 bytes>"
+    assert q.a1 == 123456
+
+def test_nokeepalive_struct():
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    BStructPtrPtr = new_pointer_type(BStructPtr)
+    complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)])
+    p = newp(BStructPtr)
+    pp = newp(BStructPtrPtr)
+    pp[0] = p
+    s = pp[0][0]
+    assert repr(s).startswith("<cdata 'struct foo &' 0x")
+
+def test_owning_repr():
+    BInt = new_primitive_type("int")
+    BArray = new_array_type(new_pointer_type(BInt), None)   # int[]
+    p = newp(BArray, 7)
+    assert repr(p) == "<cdata 'int[]' owning 28 bytes>"
+    assert sizeof(p) == 28
+    #
+    BArray = new_array_type(new_pointer_type(BInt), 7)   # int[7]
+    p = newp(BArray, None)
+    assert repr(p) == "<cdata 'int[7]' owning 28 bytes>"
+    assert sizeof(p) == 28
+
+def test_cannot_dereference_void():
+    BVoidP = new_pointer_type(new_void_type())
+    p = cast(BVoidP, 123456)
+    py.test.raises(TypeError, "p[0]")
+    p = cast(BVoidP, 0)
+    py.test.raises((TypeError, RuntimeError), "p[0]")
+
+def test_iter():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BArray = new_array_type(BIntP, None)   # int[]
+    p = newp(BArray, 7)
+    assert list(p) == list(iter(p)) == [0] * 7
+    #
+    py.test.raises(TypeError, iter, cast(BInt, 5))
+    py.test.raises(TypeError, iter, cast(BIntP, 123456))
+
+def test_cmp():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BVoidP = new_pointer_type(new_void_type())
+    p = newp(BIntP, 123)
+    q = cast(BInt, 124)
+    assert (p == q) is False
+    assert (p != q) is True
+    assert (q == p) is False
+    assert (q != p) is True
+    if strict_compare:
+        py.test.raises(TypeError, "p < q")
+        py.test.raises(TypeError, "p <= q")
+        py.test.raises(TypeError, "q < p")
+        py.test.raises(TypeError, "q <= p")
+        py.test.raises(TypeError, "p > q")
+        py.test.raises(TypeError, "p >= q")
+    r = cast(BVoidP, p)
+    assert (p <  r) is False
+    assert (p <= r) is True
+    assert (p == r) is True
+    assert (p != r) is False
+    assert (p >  r) is False
+    assert (p >= r) is True
+    s = newp(BIntP, 125)
+    assert (p == s) is False
+    assert (p != s) is True
+    assert (p < s) is (p <= s) is (s > p) is (s >= p)
+    assert (p > s) is (p >= s) is (s < p) is (s <= p)
+    assert (p < s) ^ (p > s)
+
+def test_buffer():
+    try:
+        import __builtin__
+    except ImportError:
+        import builtins as __builtin__
+    BShort = new_primitive_type("short")
+    s = newp(new_pointer_type(BShort), 100)
+    assert sizeof(s) == size_of_ptr()
+    assert sizeof(BShort) == 2
+    assert len(buffer(s)) == 2
+    #
+    BChar = new_primitive_type("char")
+    BCharArray = new_array_type(new_pointer_type(BChar), None)
+    c = newp(BCharArray, b"hi there")
+    #
+    buf = buffer(c)
+    assert repr(buf).startswith('<_cffi_backend.buffer object at 0x')
+    assert bytes(buf) == b"hi there\x00"
+    assert type(buf) is buffer
+    if sys.version_info < (3,):
+        assert str(buf) == "hi there\x00"
+        assert unicode(buf) == u+"hi there\x00"
+    else:
+        assert str(buf) == repr(buf)
+    # --mb_length--
+    assert len(buf) == len(b"hi there\x00")
+    # --mb_item--
+    for i in range(-12, 12):
+        try:
+            expected = b"hi there\x00"[i]
+        except IndexError:
+            py.test.raises(IndexError, "buf[i]")
+        else:
+            assert buf[i] == bitem2bchr(expected)
+    # --mb_slice--
+    assert buf[:] == b"hi there\x00"
+    for i in range(-12, 12):
+        assert buf[i:] == b"hi there\x00"[i:]
+        assert buf[:i] == b"hi there\x00"[:i]
+        for j in range(-12, 12):
+            assert buf[i:j] == b"hi there\x00"[i:j]
+    # --misc--
+    assert list(buf) == list(map(bitem2bchr, b"hi there\x00"))
+    # --mb_as_buffer--
+    if hasattr(__builtin__, 'buffer'):          # Python <= 2.7
+        py.test.raises(TypeError, __builtin__.buffer, c)
+        bf1 = __builtin__.buffer(buf)
+        assert len(bf1) == len(buf) and bf1[3] == "t"
+    if hasattr(__builtin__, 'memoryview'):      # Python >= 2.7
+        py.test.raises(TypeError, memoryview, c)
+        mv1 = memoryview(buf)
+        assert len(mv1) == len(buf) and mv1[3] in (b"t", ord(b"t"))
+    # --mb_ass_item--
+    expected = list(map(bitem2bchr, b"hi there\x00"))
+    for i in range(-12, 12):
+        try:
+            expected[i] = bytechr(i & 0xff)
+        except IndexError:
+            py.test.raises(IndexError, "buf[i] = bytechr(i & 0xff)")
+        else:
+            buf[i] = bytechr(i & 0xff)
+        assert list(buf) == expected
+    # --mb_ass_slice--
+    buf[:] = b"hi there\x00"
+    assert list(buf) == list(c) == list(map(bitem2bchr, b"hi there\x00"))
+    py.test.raises(ValueError, 'buf[:] = b"shorter"')
+    py.test.raises(ValueError, 'buf[:] = b"this is much too long!"')
+    buf[4:2] = b""   # no effect, but should work
+    assert buf[:] == b"hi there\x00"
+    buf[:2] = b"HI"
+    assert buf[:] == b"HI there\x00"
+    buf[:2] = b"hi"
+    expected = list(map(bitem2bchr, b"hi there\x00"))
+    x = 0
+    for i in range(-12, 12):
+        for j in range(-12, 12):
+            start = i if i >= 0 else i + len(buf)
+            stop  = j if j >= 0 else j + len(buf)
+            start = max(0, min(len(buf), start))
+            stop  = max(0, min(len(buf), stop))
+            sample = bytechr(x & 0xff) * (stop - start)
+            x += 1
+            buf[i:j] = sample
+            expected[i:j] = map(bitem2bchr, sample)
+            assert list(buf) == expected
+
+def test_getcname():
+    BUChar = new_primitive_type("unsigned char")
+    BArray = new_array_type(new_pointer_type(BUChar), 123)
+    assert getcname(BArray, "<-->") == "unsigned char<-->[123]"
+
+def test_errno():
+    BVoid = new_void_type()
+    BFunc5 = new_function_type((), BVoid)
+    f = cast(BFunc5, _testfunc(5))
+    set_errno(50)
+    f()
+    assert get_errno() == 65
+    f(); f()
+    assert get_errno() == 95
+
+def test_errno_callback():
+    if globals().get('PY_DOT_PY') == '2.5':
+        py.test.skip("cannot run this test on py.py with Python 2.5")
+    set_errno(95)
+    def cb():
+        e = get_errno()
+        set_errno(e - 6)
+    BVoid = new_void_type()
+    BFunc5 = new_function_type((), BVoid)
+    f = callback(BFunc5, cb)
+    f()
+    assert get_errno() == 89
+    f(); f()
+    assert get_errno() == 77
+
+def test_cast_to_array():
+    # not valid in C!  extension to get a non-owning <cdata 'int[3]'>
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BArray = new_array_type(BIntP, 3)
+    x = cast(BArray, 0)
+    assert repr(x) == "<cdata 'int[3]' NULL>"
+
+def test_cast_invalid():
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [])
+    p = cast(new_pointer_type(BStruct), 123456)
+    s = p[0]
+    py.test.raises(TypeError, cast, BStruct, s)
+
+def test_bug_float_convertion():
+    BDouble = new_primitive_type("double")
+    BDoubleP = new_pointer_type(BDouble)
+    py.test.raises(TypeError, newp, BDoubleP, "foobar")
+
+def test_bug_delitem():
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    x = newp(BCharP)
+    py.test.raises(TypeError, "del x[0]")
+
+def test_bug_delattr():
+    BLong = new_primitive_type("long")
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [('a1', BLong, -1)])
+    x = newp(new_pointer_type(BStruct))
+    py.test.raises(AttributeError, "del x.a1")
+
+def test_variable_length_struct():
+    py.test.skip("later")
+    BLong = new_primitive_type("long")
+    BArray = new_array_type(new_pointer_type(BLong), None)
+    BStruct = new_struct_type("struct foo")
+    BStructP = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BLong, -1),
+                                       ('a2', BArray, -1)])
+    assert sizeof(BStruct) == size_of_long()
+    assert alignof(BStruct) == alignof(BLong)
+    #
+    py.test.raises(TypeError, newp, BStructP, None)
+    x = newp(BStructP, 5)
+    assert sizeof(x) == 6 * size_of_long()
+    x[4] = 123
+    assert x[4] == 123
+    py.test.raises(IndexError, "x[5]")
+    assert len(x.a2) == 5
+    #
+    py.test.raises(TypeError, newp, BStructP, [123])
+    x = newp(BStructP, [123, 5])
+    assert x.a1 == 123
+    assert len(x.a2) == 5
+    assert list(x.a2) == [0] * 5
+    #
+    x = newp(BStructP, {'a2': 5})
+    assert x.a1 == 0
+    assert len(x.a2) == 5
+    assert list(x.a2) == [0] * 5
+    #
+    x = newp(BStructP, [123, (4, 5)])
+    assert x.a1 == 123
+    assert len(x.a2) == 2
+    assert list(x.a2) == [4, 5]
+    #
+    x = newp(BStructP, {'a2': (4, 5)})
+    assert x.a1 == 0
+    assert len(x.a2) == 2
+    assert list(x.a2) == [4, 5]
+
+def test_autocast_int():
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    BLongLong = new_primitive_type("long long")
+    BULongLong = new_primitive_type("unsigned long long")
+    BULongLongPtr = new_pointer_type(BULongLong)
+    x = newp(BIntPtr, cast(BInt, 42))
+    assert x[0] == 42
+    x = newp(BIntPtr, cast(BLongLong, 42))
+    assert x[0] == 42
+    x = newp(BIntPtr, cast(BULongLong, 42))
+    assert x[0] == 42
+    x = newp(BULongLongPtr, cast(BInt, 42))
+    assert x[0] == 42
+    py.test.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42))
+    x = cast(BInt, cast(BInt, 42))
+    assert int(x) == 42
+    x = cast(BInt, cast(BLongLong, 42))
+    assert int(x) == 42
+    x = cast(BInt, cast(BULongLong, 42))
+    assert int(x) == 42
+    x = cast(BULongLong, cast(BInt, 42))
+    assert int(x) == 42
+    x = cast(BULongLong, cast(BInt, -42))
+    assert int(x) == 2 ** 64 - 42
+    x = cast(BIntPtr, cast(BInt, 42))
+    assert int(cast(BInt, x)) == 42
+
+def test_autocast_float():
+    BFloat = new_primitive_type("float")
+    BDouble = new_primitive_type("float")
+    BFloatPtr = new_pointer_type(BFloat)
+    x = newp(BFloatPtr, cast(BDouble, 12.5))
+    assert x[0] == 12.5
+    x = cast(BFloat, cast(BDouble, 12.5))
+    assert float(x) == 12.5
+
+def test_longdouble():
+    py_py = 'PY_DOT_PY' in globals()
+    BInt = new_primitive_type("int")
+    BLongDouble = new_primitive_type("long double")
+    BLongDoublePtr = new_pointer_type(BLongDouble)
+    BLongDoubleArray = new_array_type(BLongDoublePtr, None)
+    a = newp(BLongDoubleArray, 1)
+    x = a[0]
+    if not py_py:
+        assert repr(x).startswith("<cdata 'long double' 0.0")
+    assert float(x) == 0.0
+    assert int(x) == 0
+    #
+    b = newp(BLongDoubleArray, [1.23])
+    x = b[0]
+    if not py_py:
+        assert repr(x).startswith("<cdata 'long double' 1.23")
+    assert float(x) == 1.23
+    assert int(x) == 1
+    #
+    BFunc19 = new_function_type((BLongDouble, BInt), BLongDouble)
+    f = cast(BFunc19, _testfunc(19))
+    start = lstart = 1.5
+    for i in range(107):
+        start = 4 * start - start * start
+        lstart = f(lstart, 1)
+    lother = f(1.5, 107)
+    if not py_py:
+        assert float(lstart) == float(lother)
+        assert repr(lstart) == repr(lother)
+        if sizeof(BLongDouble) > sizeof(new_primitive_type("double")):
+            assert float(lstart) != start
+            assert repr(lstart).startswith("<cdata 'long double' ")
+    #
+    c = newp(BLongDoubleArray, [lstart])
+    x = c[0]
+    assert float(f(lstart, 107)) == float(f(x, 107))
+
+def test_get_array_of_length_zero():
+    for length in [0, 5, 10]:
+        BLong = new_primitive_type("long")
+        BLongP = new_pointer_type(BLong)
+        BArray0 = new_array_type(BLongP, length)
+        BStruct = new_struct_type("struct foo")
+        BStructPtr = new_pointer_type(BStruct)
+        complete_struct_or_union(BStruct, [('a1', BArray0, -1)])
+        p = newp(BStructPtr, None)
+        if length == 0:
+            assert repr(p.a1).startswith("<cdata 'long *' 0x")
+        else:
+            assert repr(p.a1).startswith("<cdata 'long[%d]' 0x" % length)
+
+def test_nested_anonymous_struct():
+    BInt = new_primitive_type("int")
+    BChar = new_primitive_type("char")
+    BStruct = new_struct_type("struct foo")
+    BInnerStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BInnerStruct, [('a1', BInt, -1),
+                                            ('a2', BChar, -1)])
+    complete_struct_or_union(BStruct, [('', BInnerStruct, -1),
+                                       ('a3', BChar, -1)])
+    assert sizeof(BInnerStruct) == sizeof(BInt) * 2   # with alignment
+    assert sizeof(BStruct) == sizeof(BInt) * 3        # 'a3' is placed after
+    d = BStruct.fields
+    assert len(d) == 3
+    assert d[0][0] == 'a1'
+    assert d[0][1].type is BInt
+    assert d[0][1].offset == 0
+    assert d[0][1].bitshift == -1
+    assert d[0][1].bitsize == -1
+    assert d[1][0] == 'a2'
+    assert d[1][1].type is BChar
+    assert d[1][1].offset == sizeof(BInt)
+    assert d[1][1].bitshift == -1
+    assert d[1][1].bitsize == -1
+    assert d[2][0] == 'a3'
+    assert d[2][1].type is BChar
+    assert d[2][1].offset == sizeof(BInt) * 2
+    assert d[2][1].bitshift == -1
+    assert d[2][1].bitsize == -1
+
+def test_nested_anonymous_struct_2():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    BInnerUnion = new_union_type("union bar")
+    complete_struct_or_union(BInnerUnion, [('a1', BInt, -1),
+                                           ('a2', BInt, -1)])
+    complete_struct_or_union(BStruct, [('b1', BInt, -1),
+                                       ('', BInnerUnion, -1),
+                                       ('b2', BInt, -1)])
+    assert sizeof(BInnerUnion) == sizeof(BInt)
+    assert sizeof(BStruct) == sizeof(BInt) * 3
+    fields = [(name, fld.offset, fld.flags) for (name, fld) in BStruct.fields]
+    assert fields == [
+        ('b1', 0 * sizeof(BInt), 0),
+        ('a1', 1 * sizeof(BInt), 0),
+        ('a2', 1 * sizeof(BInt), 1),
+        ('b2', 2 * sizeof(BInt), 0),
+    ]
+
+def test_sizeof_union():
+    # a union has the largest alignment of its members, and a total size
+    # that is the largest of its items *possibly further aligned* if
+    # another smaller item has a larger alignment...
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    assert sizeof(BShort) == alignof(BShort) == 2
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [('a1', BChar),
+                                       ('a2', BChar),
+                                       ('a3', BChar)])
+    assert sizeof(BStruct) == 3 and alignof(BStruct) == 1
+    BUnion = new_union_type("union u")
+    complete_struct_or_union(BUnion, [('s', BStruct),
+                                      ('i', BShort)])
+    assert sizeof(BUnion) == 4
+    assert alignof(BUnion) == 2
+
+def test_unaligned_struct():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [('b', BInt, -1, 1)],
+                             None, 5, 1)
+
+def test_CData_CType():
+    CData, CType = _get_types()
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    nullchr = cast(BChar, 0)
+    chrref = newp(BCharP, None)
+    assert isinstance(nullchr, CData)
+    assert isinstance(chrref, CData)
+    assert not isinstance(BChar, CData)
+    assert not isinstance(nullchr, CType)
+    assert not isinstance(chrref, CType)
+    assert isinstance(BChar, CType)
+
+def test_no_cdata_float():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BUInt = new_primitive_type("unsigned int")
+    BUIntP = new_pointer_type(BUInt)
+    BFloat = new_primitive_type("float")
+    py.test.raises(TypeError, newp, BIntP, cast(BFloat, 0.0))
+    py.test.raises(TypeError, newp, BUIntP, cast(BFloat, 0.0))
+
+def test_bool():
+    BBool = new_primitive_type("_Bool")
+    BBoolP = new_pointer_type(BBool)
+    assert int(cast(BBool, False)) == 0
+    assert int(cast(BBool, True)) == 1
+    assert bool(cast(BBool, False)) is False    # since 1.7
+    assert bool(cast(BBool, True)) is True
+    assert int(cast(BBool, 3)) == 1
+    assert int(cast(BBool, long(3))) == 1
+    assert int(cast(BBool, long(10)**4000)) == 1
+    assert int(cast(BBool, -0.1)) == 1
+    assert int(cast(BBool, -0.0)) == 0
+    assert int(cast(BBool, '\x00')) == 0
+    assert int(cast(BBool, '\xff')) == 1
+    assert newp(BBoolP, False)[0] == 0
+    assert newp(BBoolP, True)[0] == 1
+    assert newp(BBoolP, 0)[0] == 0
+    assert newp(BBoolP, 1)[0] == 1
+    py.test.raises(TypeError, newp, BBoolP, 1.0)
+    py.test.raises(TypeError, newp, BBoolP, '\x00')
+    py.test.raises(OverflowError, newp, BBoolP, 2)
+    py.test.raises(OverflowError, newp, BBoolP, -1)
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    p = newp(BCharP, b'\x01')
+    q = cast(BBoolP, p)
+    assert q[0] is True
+    p = newp(BCharP, b'\x00')
+    q = cast(BBoolP, p)
+    assert q[0] is False
+    py.test.raises(TypeError, string, cast(BBool, False))
+    BDouble = new_primitive_type("double")
+    assert int(cast(BBool, cast(BDouble, 0.1))) == 1
+    assert int(cast(BBool, cast(BDouble, 0.0))) == 0
+    BBoolA = new_array_type(BBoolP, None)
+    p = newp(BBoolA, b'\x01\x00')
+    assert p[0] is True
+    assert p[1] is False
+
+def test_bool_forbidden_cases():
+    BBool = new_primitive_type("_Bool")
+    BBoolP = new_pointer_type(BBool)
+    BBoolA = new_array_type(BBoolP, None)
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    p = newp(BCharP, b'X')
+    q = cast(BBoolP, p)
+    py.test.raises(ValueError, "q[0]")
+    py.test.raises(TypeError, newp, BBoolP, b'\x00')
+    assert newp(BBoolP, 0)[0] is False
+    assert newp(BBoolP, 1)[0] is True
+    py.test.raises(OverflowError, newp, BBoolP, 2)
+    py.test.raises(OverflowError, newp, BBoolP, -1)
+    py.test.raises(ValueError, newp, BBoolA, b'\x00\x01\x02')
+    py.test.raises(OverflowError, newp, BBoolA, [0, 1, 2])
+    py.test.raises(TypeError, string, newp(BBoolP, 1))
+    py.test.raises(TypeError, string, newp(BBoolA, [1]))
+
+def test_typeoffsetof():
+    BChar = new_primitive_type("char")
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BChar, -1),
+                                       ('a3', BChar, -1)])
+    py.test.raises(TypeError, typeoffsetof, BStructPtr, None)
+    py.test.raises(TypeError, typeoffsetof, BStruct, None)
+    assert typeoffsetof(BStructPtr, 'a1') == (BChar, 0)
+    assert typeoffsetof(BStruct, 'a1') == (BChar, 0)
+    assert typeoffsetof(BStructPtr, 'a2') == (BChar, 1)
+    assert typeoffsetof(BStruct, 'a3') == (BChar, 2)
+    assert typeoffsetof(BStructPtr, 'a2', 0) == (BChar, 1)
+    assert typeoffsetof(BStruct, u+'a3') == (BChar, 2)
+    py.test.raises(TypeError, typeoffsetof, BStructPtr, 'a2', 1)
+    py.test.raises(KeyError, typeoffsetof, BStructPtr, 'a4')
+    py.test.raises(KeyError, typeoffsetof, BStruct, 'a5')
+    py.test.raises(TypeError, typeoffsetof, BStruct, 42)
+    py.test.raises(TypeError, typeoffsetof, BChar, 'a1')
+
+def test_typeoffsetof_array():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BArray = new_array_type(BIntP, None)
+    py.test.raises(TypeError, typeoffsetof, BArray, None)
+    py.test.raises(TypeError, typeoffsetof, BArray, 'a1')
+    assert typeoffsetof(BArray, 51) == (BInt, 51 * size_of_int())
+    assert typeoffsetof(BIntP, 51) == (BInt, 51 * size_of_int())
+    assert typeoffsetof(BArray, -51) == (BInt, -51 * size_of_int())
+    MAX = sys.maxsize // size_of_int()
+    assert typeoffsetof(BArray, MAX) == (BInt, MAX * size_of_int())
+    assert typeoffsetof(BIntP, MAX) == (BInt, MAX * size_of_int())
+    py.test.raises(OverflowError, typeoffsetof, BArray, MAX + 1)
+
+def test_typeoffsetof_no_bitfield():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct, [('a1', BInt, 4)])
+    py.test.raises(TypeError, typeoffsetof, BStruct, 'a1')
+
+def test_rawaddressof():
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BStruct = new_struct_type("struct foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BChar, -1),
+                                       ('a3', BChar, -1)])
+    p = newp(BStructPtr)
+    assert repr(p) == "<cdata 'struct foo *' owning 3 bytes>"
+    s = p[0]
+    assert repr(s) == "<cdata 'struct foo' owning 3 bytes>"
+    a = rawaddressof(BStructPtr, s, 0)
+    assert repr(a).startswith("<cdata 'struct foo *' 0x")
+    py.test.raises(TypeError, rawaddressof, BStruct, s, 0)
+    b = rawaddressof(BCharP, s, 0)
+    assert b == cast(BCharP, p)
+    c = rawaddressof(BStructPtr, a, 0)
+    assert c == a
+    py.test.raises(TypeError, rawaddressof, BStructPtr, cast(BChar, '?'), 0)
+    #
+    d = rawaddressof(BCharP, s, 1)
+    assert d == cast(BCharP, p) + 1
+    #
+    e = cast(BCharP, 109238)
+    f = rawaddressof(BCharP, e, 42)
+    assert f == e + 42
+    #
+    BCharA = new_array_type(BCharP, None)
+    e = newp(BCharA, 50)
+    f = rawaddressof(BCharP, e, 42)
+    assert f == e + 42
+
+def test_newp_signed_unsigned_char():
+    BCharArray = new_array_type(
+        new_pointer_type(new_primitive_type("char")), None)
+    p = newp(BCharArray, b"foo")
+    assert len(p) == 4
+    assert list(p) == [b"f", b"o", b"o", b"\x00"]
+    #
+    BUCharArray = new_array_type(
+        new_pointer_type(new_primitive_type("unsigned char")), None)
+    p = newp(BUCharArray, b"fo\xff")
+    assert len(p) == 4
+    assert list(p) == [ord("f"), ord("o"), 0xff, 0]
+    #
+    BSCharArray = new_array_type(
+        new_pointer_type(new_primitive_type("signed char")), None)
+    p = newp(BSCharArray, b"fo\xff")
+    assert len(p) == 4
+    assert list(p) == [ord("f"), ord("o"), -1, 0]
+
+def test_newp_from_bytearray_doesnt_work():
+    BCharArray = new_array_type(
+        new_pointer_type(new_primitive_type("char")), None)
+    py.test.raises(TypeError, newp, BCharArray, bytearray(b"foo"))
+    p = newp(BCharArray, 5)
+    buffer(p)[:] = bytearray(b"foo.\x00")
+    assert len(p) == 5
+    assert list(p) == [b"f", b"o", b"o", b".", b"\x00"]
+    p[1:3] = bytearray(b"XY")
+    assert list(p) == [b"f", b"X", b"Y", b".", b"\x00"]
+
+def test_string_assignment_to_byte_array():
+    BByteArray = new_array_type(
+        new_pointer_type(new_primitive_type("unsigned char")), None)
+    p = newp(BByteArray, 5)
+    p[0:3] = bytearray(b"XYZ")
+    assert list(p) == [ord("X"), ord("Y"), ord("Z"), 0, 0]
+
+# XXX hack
+if sys.version_info >= (3,):
+    try:
+        import posix, io
+        posix.fdopen = io.open
+    except ImportError:
+        pass   # win32
+
+def test_FILE():
+    if sys.platform == "win32":
+        py.test.skip("testing FILE not implemented")
+    #
+    BFILE = new_struct_type("struct _IO_FILE")
+    BFILEP = new_pointer_type(BFILE)
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BCharP, BFILEP), BInt, False)
+    BFunc2 = new_function_type((BFILEP, BCharP), BInt, True)
+    ll = find_and_load_library('c')
+    fputs = ll.load_function(BFunc, "fputs")
+    fscanf = ll.load_function(BFunc2, "fscanf")
+    #
+    import posix
+    fdr, fdw = posix.pipe()
+    fr1 = posix.fdopen(fdr, 'rb', 256)
+    fw1 = posix.fdopen(fdw, 'wb', 256)
+    #
+    fw1.write(b"X")
+    res = fputs(b"hello world\n", fw1)
+    assert res >= 0
+    fw1.flush()     # should not be needed
+    #
+    p = newp(new_array_type(BCharP, 100), None)
+    res = fscanf(fr1, b"%s\n", p)
+    assert res == 1
+    assert string(p) == b"Xhello"
+    fr1.close()
+    fw1.close()
+
+def test_FILE_only_for_FILE_arg():
+    if sys.platform == "win32":
+        py.test.skip("testing FILE not implemented")
+    #
+    B_NOT_FILE = new_struct_type("struct NOT_FILE")
+    B_NOT_FILEP = new_pointer_type(B_NOT_FILE)
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BCharP, B_NOT_FILEP), BInt, False)
+    ll = find_and_load_library('c')
+    fputs = ll.load_function(BFunc, "fputs")
+    #
+    import posix
+    fdr, fdw = posix.pipe()
+    fr1 = posix.fdopen(fdr, 'r')
+    fw1 = posix.fdopen(fdw, 'w')
+    #
+    e = py.test.raises(TypeError, fputs, b"hello world\n", fw1)
+    assert str(e.value).startswith(
+        "initializer for ctype 'struct NOT_FILE *' must "
+        "be a cdata pointer, not ")
+
+def test_FILE_object():
+    if sys.platform == "win32":
+        py.test.skip("testing FILE not implemented")
+    #
+    BFILE = new_struct_type("FILE")
+    BFILEP = new_pointer_type(BFILE)
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BCharP, BFILEP), BInt, False)
+    BFunc2 = new_function_type((BFILEP,), BInt, False)
+    ll = find_and_load_library('c')
+    fputs = ll.load_function(BFunc, "fputs")
+    fileno = ll.load_function(BFunc2, "fileno")
+    #
+    import posix
+    fdr, fdw = posix.pipe()
+    fw1 = posix.fdopen(fdw, 'wb', 256)
+    #
+    fw1p = cast(BFILEP, fw1)
+    fw1.write(b"X")
+    fw1.flush()
+    res = fputs(b"hello\n", fw1p)
+    assert res >= 0
+    res = fileno(fw1p)
+    assert (res == fdw) == (sys.version_info < (3,))
+    fw1.close()
+    #
+    data = posix.read(fdr, 256)
+    assert data == b"Xhello\n"
+    posix.close(fdr)
+
+def test_errno_saved():
+    set_errno(42)
+    # a random function that will reset errno to 0 (at least on non-windows)
+    import os; os.stat('.')
+    #
+    res = get_errno()
+    assert res == 42
+
+def test_GetLastError():
+    if sys.platform != "win32":
+        py.test.skip("GetLastError(): only for Windows")
+    #
+    lib = find_and_load_library('KERNEL32.DLL')
+    BInt = new_primitive_type("int")
+    BVoid = new_void_type()
+    BFunc1 = new_function_type((BInt,), BVoid, False)
+    BFunc2 = new_function_type((), BInt, False)
+    SetLastError = lib.load_function(BFunc1, "SetLastError")
+    GetLastError = lib.load_function(BFunc2, "GetLastError")
+    #
+    SetLastError(42)
+    # a random function that will reset the real GetLastError() to 0
+    import nt; nt.stat('.')
+    #
+    res = GetLastError()
+    assert res == 42
+    #
+    SetLastError(2)
+    code, message = getwinerror()
+    assert code == 2
+    assert message == "The system cannot find the file specified"
+    #
+    code, message = getwinerror(1155)
+    assert code == 1155
+    assert message == ("No application is associated with the "
+                       "specified file for this operation")
+
+def test_nonstandard_integer_types():
+    for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t',
+                     'uint32_t', 'int64_t', 'uint64_t', 'intptr_t',
+                     'uintptr_t', 'ptrdiff_t', 'size_t', 'ssize_t',
+                     'int_least8_t',  'uint_least8_t',
+                     'int_least16_t', 'uint_least16_t',
+                     'int_least32_t', 'uint_least32_t',
+                     'int_least64_t', 'uint_least64_t',
+                     'int_fast8_t',  'uint_fast8_t',
+                     'int_fast16_t', 'uint_fast16_t',
+                     'int_fast32_t', 'uint_fast32_t',
+                     'int_fast64_t', 'uint_fast64_t',
+                     'intmax_t', 'uintmax_t']:
+        new_primitive_type(typename)    # works
+
+def test_cannot_convert_unicode_to_charp():
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    BCharArray = new_array_type(BCharP, None)
+    py.test.raises(TypeError, newp, BCharArray, u+'foobar')
+
+def test_buffer_keepalive():
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    BCharArray = new_array_type(BCharP, None)
+    buflist = []
+    for i in range(20):
+        c = newp(BCharArray, str2bytes("hi there %d" % i))
+        buflist.append(buffer(c))
+    import gc; gc.collect()
+    for i in range(20):
+        buf = buflist[i]
+        assert buf[:] == str2bytes("hi there %d\x00" % i)
+
+def test_slice():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BIntArray = new_array_type(BIntP, None)
+    c = newp(BIntArray, 5)
+    assert len(c) == 5
+    assert repr(c) == "<cdata 'int[]' owning 20 bytes>"
+    d = c[1:4]
+    assert len(d) == 3
+    assert repr(d) == "<cdata 'int[]' sliced length 3>"
+    d[0] = 123
+    d[2] = 456
+    assert c[1] == 123
+    assert c[3] == 456
+    assert d[2] == 456
+    py.test.raises(IndexError, "d[3]")
+    py.test.raises(IndexError, "d[-1]")
+
+def test_slice_ptr():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BIntArray = new_array_type(BIntP, None)
+    c = newp(BIntArray, 5)
+    d = (c+1)[0:2]
+    assert len(d) == 2
+    assert repr(d) == "<cdata 'int[]' sliced length 2>"
+    d[1] += 50
+    assert c[2] == 50
+
+def test_slice_array_checkbounds():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BIntArray = new_array_type(BIntP, None)
+    c = newp(BIntArray, 5)
+    c[0:5]
+    assert len(c[5:5]) == 0
+    py.test.raises(IndexError, "c[-1:1]")
+    cp = c + 0
+    cp[-1:1]
+
+def test_nonstandard_slice():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BIntArray = new_array_type(BIntP, None)
+    c = newp(BIntArray, 5)
+    e = py.test.raises(IndexError, "c[:5]")
+    assert str(e.value) == "slice start must be specified"
+    e = py.test.raises(IndexError, "c[4:]")
+    assert str(e.value) == "slice stop must be specified"
+    e = py.test.raises(IndexError, "c[1:2:3]")
+    assert str(e.value) == "slice with step not supported"
+    e = py.test.raises(IndexError, "c[1:2:1]")
+    assert str(e.value) == "slice with step not supported"
+    e = py.test.raises(IndexError, "c[4:2]")
+    assert str(e.value) == "slice start > stop"
+    e = py.test.raises(IndexError, "c[6:6]")
+    assert str(e.value) == "index too large (expected 6 <= 5)"
+
+def test_setslice():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BIntArray = new_array_type(BIntP, None)
+    c = newp(BIntArray, 5)
+    c[1:3] = [100, 200]
+    assert list(c) == [0, 100, 200, 0, 0]
+    cp = c + 3
+    cp[-1:1] = [300, 400]
+    assert list(c) == [0, 100, 300, 400, 0]
+    cp[-1:1] = iter([500, 600])
+    assert list(c) == [0, 100, 500, 600, 0]
+    py.test.raises(ValueError, "cp[-1:1] = [1000]")
+    assert list(c) == [0, 100, 1000, 600, 0]
+    py.test.raises(ValueError, "cp[-1:1] = (700, 800, 900)")
+    assert list(c) == [0, 100, 700, 800, 0]
+
+def test_setslice_array():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BIntArray = new_array_type(BIntP, None)
+    c = newp(BIntArray, 5)
+    d = newp(BIntArray, [10, 20, 30])
+    c[1:4] = d
+    assert list(c) == [0, 10, 20, 30, 0]
+    #
+    BShortP = new_pointer_type(new_primitive_type("short"))
+    BShortArray = new_array_type(BShortP, None)
+    d = newp(BShortArray, [40, 50])
+    c[1:3] = d
+    assert list(c) == [0, 40, 50, 30, 0]
+
+def test_cdata_name_module_doc():
+    p = new_primitive_type("signed char")
+    x = cast(p, 17)
+    assert x.__module__ == '_cffi_backend'
+    assert x.__name__ == '<cdata>'
+    assert hasattr(x, '__doc__')
+
+def test_different_types_of_ptr_equality():
+    BVoidP = new_pointer_type(new_void_type())
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    x = cast(BVoidP, 12345)
+    assert x == cast(BIntP, 12345)
+    assert x != cast(BIntP, 12344)
+    assert hash(x) == hash(cast(BIntP, 12345))
+
+def test_new_handle():
+    import _weakref
+    BVoidP = new_pointer_type(new_void_type())
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    class mylist(list):
+        pass
+    o = mylist([2, 3, 4])
+    x = newp_handle(BVoidP, o)
+    assert repr(x) == "<cdata 'void *' handle to [2, 3, 4]>"
+    assert x
+    assert from_handle(x) is o
+    assert from_handle(cast(BCharP, x)) is o
+    wr = _weakref.ref(o)
+    del o
+    import gc; gc.collect()
+    assert wr() is not None
+    assert from_handle(x) == list((2, 3, 4))
+    assert from_handle(cast(BCharP, x)) == list((2, 3, 4))
+    del x
+    for i in range(3):
+        if wr() is not None:
+            import gc; gc.collect()
+    assert wr() is None
+    py.test.raises(RuntimeError, from_handle, cast(BCharP, 0))
+
+def test_new_handle_cycle():
+    import _weakref
+    BVoidP = new_pointer_type(new_void_type())
+    class A(object):
+        pass
+    o = A()
+    o.cycle = newp_handle(BVoidP, o)
+    wr = _weakref.ref(o)
+    del o
+    for i in range(3):
+        if wr() is not None:
+            import gc; gc.collect()
+    assert wr() is None
+
+def _test_bitfield_details(flag):
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BInt = new_primitive_type("int")
+    BUInt = new_primitive_type("unsigned int")
+    BStruct = new_struct_type("struct foo1")
+    complete_struct_or_union(BStruct, [('a', BChar, -1),
+                                       ('b1', BInt, 9),
+                                       ('b2', BUInt, 7),
+                                       ('c', BChar, -1)], -1, -1, -1, flag)
+    if not (flag & SF_MSVC_BITFIELDS):   # gcc, any variant
+        assert typeoffsetof(BStruct, 'c') == (BChar, 3)
+        assert sizeof(BStruct) == 4
+    else:               # msvc
+        assert typeoffsetof(BStruct, 'c') == (BChar, 8)
+        assert sizeof(BStruct) == 12
+    assert alignof(BStruct) == 4
+    #
+    p = newp(new_pointer_type(BStruct), None)
+    p.a = b'A'
+    p.b1 = -201
+    p.b2 = 99
+    p.c = b'\x9D'
+    raw = buffer(p)[:]
+    if sys.byteorder == 'little':
+        if flag & SF_MSVC_BITFIELDS:
+            assert raw == b'A\x00\x00\x007\xC7\x00\x00\x9D\x00\x00\x00'
+        elif flag & SF_GCC_LITTLE_ENDIAN:
+            assert raw == b'A7\xC7\x9D'
+        elif flag & SF_GCC_BIG_ENDIAN:
+            assert raw == b'A\xE3\x9B\x9D'
+        else:
+            raise AssertionError("bad flag")
+    else:
+        if flag & SF_MSVC_BITFIELDS:
+            assert raw == b'A\x00\x00\x00\x00\x00\xC77\x9D\x00\x00\x00'
+        elif flag & SF_GCC_LITTLE_ENDIAN:
+            assert raw == b'A\xC77\x9D'
+        elif flag & SF_GCC_BIG_ENDIAN:
+            assert raw == b'A\x9B\xE3\x9D'
+        else:
+            raise AssertionError("bad flag")
+    #
+    BStruct = new_struct_type("struct foo2")
+    complete_struct_or_union(BStruct, [('a', BChar, -1),
+                                       ('',  BShort, 9),
+                                       ('c', BChar, -1)], -1, -1, -1, flag)
+    assert typeoffsetof(BStruct, 'c') == (BChar, 4)
+    if flag & SF_MSVC_BITFIELDS:
+        assert sizeof(BStruct) == 6
+        assert alignof(BStruct) == 2
+    elif flag & SF_GCC_X86_BITFIELDS:
+        assert sizeof(BStruct) == 5
+        assert alignof(BStruct) == 1
+    elif flag & SF_GCC_ARM_BITFIELDS:
+        assert sizeof(BStruct) == 6
+        assert alignof(BStruct) == 2
+    else:
+        raise AssertionError("bad flag")
+    #
+    BStruct = new_struct_type("struct foo2")
+    complete_struct_or_union(BStruct, [('a', BChar, -1),
+                                       ('',  BInt, 0),
+                                       ('',  BInt, 0),
+                                       ('c', BChar, -1)], -1, -1, -1, flag)
+    if flag & SF_MSVC_BITFIELDS:
+        assert typeoffsetof(BStruct, 'c') == (BChar, 1)
+        assert sizeof(BStruct) == 2
+        assert alignof(BStruct) == 1
+    elif flag & SF_GCC_X86_BITFIELDS:
+        assert typeoffsetof(BStruct, 'c') == (BChar, 4)
+        assert sizeof(BStruct) == 5
+        assert alignof(BStruct) == 1
+    elif flag & SF_GCC_ARM_BITFIELDS:
+        assert typeoffsetof(BStruct, 'c') == (BChar, 4)
+        assert sizeof(BStruct) == 8
+        assert alignof(BStruct) == 4
+    else:
+        raise AssertionError("bad flag")
+
+
+SF_MSVC_BITFIELDS     = 0x01
+SF_GCC_ARM_BITFIELDS  = 0x02
+SF_GCC_X86_BITFIELDS  = 0x10
+
+SF_GCC_BIG_ENDIAN     = 0x04
+SF_GCC_LITTLE_ENDIAN  = 0x40
+
+SF_PACKED             = 0x08
+
+def test_bitfield_as_x86_gcc():
+    _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_LITTLE_ENDIAN)
+
+def test_bitfield_as_msvc():
+    _test_bitfield_details(flag=SF_MSVC_BITFIELDS|SF_GCC_LITTLE_ENDIAN)
+
+def test_bitfield_as_arm_gcc():
+    _test_bitfield_details(flag=SF_GCC_ARM_BITFIELDS|SF_GCC_LITTLE_ENDIAN)
+
+def test_bitfield_as_ppc_gcc():
+    # PowerPC uses the same format as X86, but is big-endian
+    _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN)
+
+
+def test_struct_array_no_length():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BArray = new_array_type(BIntP, None)
+    BStruct = new_struct_type("foo")
+    py.test.raises(TypeError, complete_struct_or_union,
+                   BStruct, [('x', BArray),
+                             ('y', BInt)])
+    #
+    BStruct = new_struct_type("foo")
+    complete_struct_or_union(BStruct, [('x', BInt),
+                                       ('y', BArray)])
+    assert sizeof(BStruct) == size_of_int()
+    d = BStruct.fields
+    assert len(d) == 2
+    assert d[0][0] == 'x'
+    assert d[0][1].type is BInt
+    assert d[0][1].offset == 0
+    assert d[0][1].bitshift == -1
+    assert d[0][1].bitsize == -1
+    assert d[1][0] == 'y'
+    assert d[1][1].type is BArray
+    assert d[1][1].offset == size_of_int()
+    assert d[1][1].bitshift == -2
+    assert d[1][1].bitsize == -1
+    #
+    p = newp(new_pointer_type(BStruct))
+    p.x = 42
+    assert p.x == 42
+    assert typeof(p.y) is BArray
+    assert len(p.y) == 0
+    assert p.y == cast(BIntP, p) + 1
+    #
+    p = newp(new_pointer_type(BStruct), [100])
+    assert p.x == 100
+    assert len(p.y) == 0
+    #
+    # Tests for
+    #    ffi.new("struct_with_var_array *", [field.., [the_array_items..]])
+    #    ffi.new("struct_with_var_array *", [field.., array_size])
+    plist = []
+    for i in range(20):
+        if i % 2 == 0:
+            p = newp(new_pointer_type(BStruct), [100, [200, i, 400]])
+        else:
+            p = newp(new_pointer_type(BStruct), [100, 3])
+            p.y[1] = i
+            p.y[0] = 200
+            assert p.y[2] == 0
+            p.y[2] = 400
+        assert len(p.y) == 3
+        assert len(p[0].y) == 3
+        assert len(buffer(p)) == sizeof(BInt) * 4
+        assert sizeof(p[0]) == sizeof(BInt) * 4
+        plist.append(p)
+    for i in range(20):
+        p = plist[i]
+        assert p.x == 100
+        assert p.y[0] == 200
+        assert p.y[1] == i
+        assert p.y[2] == 400
+        assert list(p.y) == [200, i, 400]
+    #
+    # the following assignment works, as it normally would, for any array field
+    p.y = [501, 601]
+    assert list(p.y) == [501, 601, 400]
+    p[0].y = [500, 600]
+    assert list(p[0].y) == [500, 600, 400]
+    assert repr(p) == "<cdata 'foo *' owning %d bytes>" % (
+        sizeof(BStruct) + 3 * sizeof(BInt),)
+    assert repr(p[0]) == "<cdata 'foo' owning %d bytes>" % (
+        sizeof(BStruct) + 3 * sizeof(BInt),)
+    assert sizeof(p[0]) == sizeof(BStruct) + 3 * sizeof(BInt)
+    #
+    # from a non-owning pointer, we can't get the length
+    q = cast(new_pointer_type(BStruct), p)
+    assert q.y[0] == 500
+    assert q[0].y[0] == 500
+    py.test.raises(TypeError, len, q.y)
+    py.test.raises(TypeError, len, q[0].y)
+    assert typeof(q.y) is BIntP
+    assert typeof(q[0].y) is BIntP
+    assert sizeof(q[0]) == sizeof(BStruct)
+    #
+    # error cases
+    py.test.raises(IndexError, "p.y[4]")
+    py.test.raises(TypeError, "p.y = cast(BIntP, 0)")
+    py.test.raises(TypeError, "p.y = 15")
+    py.test.raises(TypeError, "p.y = None")
+    #
+    # accepting this may be specified by the C99 standard,
+    # or a GCC strangeness...
+    BStruct2 = new_struct_type("bar")
+    complete_struct_or_union(BStruct2, [('f', BStruct),
+                                        ('n', BInt)])
+    p = newp(new_pointer_type(BStruct2), {'n': 42})
+    assert p.n == 42
+    #
+    # more error cases
+    py.test.raises(TypeError, newp, new_pointer_type(BStruct), [100, None])
+    BArray4 = new_array_type(BIntP, 4)
+    BStruct4 = new_struct_type("test4")
+    complete_struct_or_union(BStruct4, [('a', BArray4)])   # not varsized
+    py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [None])
+    py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [4])
+    p = newp(new_pointer_type(BStruct4), [[10, 20, 30]])
+    assert p.a[0] == 10
+    assert p.a[1] == 20
+    assert p.a[2] == 30
+    assert p.a[3] == 0
+
+def test_struct_array_no_length_explicit_position():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BArray = new_array_type(BIntP, None)
+    BStruct = new_struct_type("foo")
+    complete_struct_or_union(BStruct, [('x', BArray, -1, 0), # actually 3 items
+                                       ('y', BInt, -1, 12)])
+    p = newp(new_pointer_type(BStruct), [[10, 20], 30])
+    assert p.x[0] == 10
+    assert p.x[1] == 20
+    assert p.x[2] == 0
+    assert p.y == 30
+    p = newp(new_pointer_type(BStruct), {'x': [40], 'y': 50})
+    assert p.x[0] == 40
+    assert p.x[1] == 0
+    assert p.x[2] == 0
+    assert p.y == 50
+    p = newp(new_pointer_type(BStruct), {'y': 60})
+    assert p.x[0] == 0
+    assert p.x[1] == 0
+    assert p.x[2] == 0
+    assert p.y == 60
+    #
+    # This "should" work too, allocating a larger structure
+    # (a bit strange in this case, but useful in general)
+    plist = []
+    for i in range(20):
+        p = newp(new_pointer_type(BStruct), [[10, 20, 30, 40, 50, 60, 70]])
+        plist.append(p)
+    for i in range(20):
+        p = plist[i]
+        assert p.x[0] == 10
+        assert p.x[1] == 20
+        assert p.x[2] == 30
+        assert p.x[3] == 40 == p.y
+        assert p.x[4] == 50
+        assert p.x[5] == 60
+        assert p.x[6] == 70
+
+def test_struct_array_not_aligned():
+    # struct a { int x; char y; char z[]; };
+    # ends up of size 8, but 'z' is at offset 5
+    BChar = new_primitive_type("char")
+    BInt = new_primitive_type("int")
+    BCharP = new_pointer_type(BChar)
+    BArray = new_array_type(BCharP, None)
+    BStruct = new_struct_type("foo")
+    complete_struct_or_union(BStruct, [('x', BInt),
+                                       ('y', BChar),
+                                       ('z', BArray)])
+    assert sizeof(BStruct) == 2 * size_of_int()
+    def offsetof(BType, fieldname):
+        return typeoffsetof(BType, fieldname)[1]
+    base = offsetof(BStruct, 'z')
+    assert base == size_of_int() + 1
+    #
+    p = newp(new_pointer_type(BStruct), {'z': 3})
+    assert sizeof(p[0]) == base + 3
+    q = newp(new_pointer_type(BStruct), {'z': size_of_int()})
+    assert sizeof(q) == size_of_ptr()
+    assert sizeof(q[0]) == base + size_of_int()
+    assert len(p.z) == 3
+    assert len(p[0].z) == 3
+    assert len(q.z) == size_of_int()
+    assert len(q[0].z) == size_of_int()
+
+def test_ass_slice():
+    BChar = new_primitive_type("char")
+    BArray = new_array_type(new_pointer_type(BChar), None)
+    p = newp(BArray, b"foobar")
+    p[2:5] = [b"*", b"Z", b"T"]
+    p[1:3] = b"XY"
+    assert list(p) == [b"f", b"X", b"Y", b"Z", b"T", b"r", b"\x00"]
+    py.test.raises(TypeError, "p[1:5] = u+'XYZT'")
+    py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]")
+    #
+    for typename in ["wchar_t", "char16_t", "char32_t"]:
+        BUniChar = new_primitive_type(typename)
+        BArray = new_array_type(new_pointer_type(BUniChar), None)
+        p = newp(BArray, u+"foobar")
+        p[2:5] = [u+"*", u+"Z", u+"T"]
+        p[1:3] = u+"XY"
+        assert list(p) == [u+"f", u+"X", u+"Y", u+"Z", u+"T", u+"r", u+"\x00"]
+        py.test.raises(TypeError, "p[1:5] = b'XYZT'")
+        py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]")
+
+def test_void_p_arithmetic():
+    BVoid = new_void_type()
+    BInt = new_primitive_type("intptr_t")
+    p = cast(new_pointer_type(BVoid), 100000)
+    assert int(cast(BInt, p)) == 100000
+    assert int(cast(BInt, p + 42)) == 100042
+    assert int(cast(BInt, p - (-42))) == 100042
+    assert (p + 42) - p == 42
+    q = cast(new_pointer_type(new_primitive_type("char")), 100000)
+    py.test.raises(TypeError, "p - q")
+    py.test.raises(TypeError, "q - p")
+    py.test.raises(TypeError, "p + cast(new_primitive_type('int'), 42)")
+    py.test.raises(TypeError, "p - cast(new_primitive_type('int'), 42)")
+
+def test_sizeof_sliced_array():
+    BInt = new_primitive_type("int")
+    BArray = new_array_type(new_pointer_type(BInt), 10)
+    p = newp(BArray, None)
+    assert sizeof(p[2:9]) == 7 * sizeof(BInt)
+
+def test_packed():
+    BLong = new_primitive_type("long")
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    for extra_args in [(SF_PACKED,), (0, 1)]:
+        BStruct = new_struct_type("struct foo")
+        complete_struct_or_union(BStruct, [('a1', BLong, -1),
+                                           ('a2', BChar, -1),
+                                           ('a3', BShort, -1)],
+                                 None, -1, -1, *extra_args)
+        d = BStruct.fields
+        assert len(d) == 3
+        assert d[0][0] == 'a1'
+        assert d[0][1].type is BLong
+        assert d[0][1].offset == 0
+        assert d[0][1].bitshift == -1
+        assert d[0][1].bitsize == -1
+        assert d[1][0] == 'a2'
+        assert d[1][1].type is BChar
+        assert d[1][1].offset == sizeof(BLong)
+        assert d[1][1].bitshift == -1
+        assert d[1][1].bitsize == -1
+        assert d[2][0] == 'a3'
+        assert d[2][1].type is BShort
+        assert d[2][1].offset == sizeof(BLong) + sizeof(BChar)
+        assert d[2][1].bitshift == -1
+        assert d[2][1].bitsize == -1
+        assert sizeof(BStruct) == sizeof(BLong) + sizeof(BChar) + sizeof(BShort)
+        assert alignof(BStruct) == 1
+    #
+    BStruct2 = new_struct_type("struct foo")
+    complete_struct_or_union(BStruct2, [('b1', BChar, -1),
+                                        ('b2', BLong, -1)],
+                             None, -1, -1, 0, 2)
+    d = BStruct2.fields
+    assert len(d) == 2
+    assert d[0][0] == 'b1'
+    assert d[0][1].type is BChar
+    assert d[0][1].offset == 0
+    assert d[0][1].bitshift == -1
+    assert d[0][1].bitsize == -1
+    assert d[1][0] == 'b2'
+    assert d[1][1].type is BLong
+    assert d[1][1].offset == 2
+    assert d[1][1].bitshift == -1
+    assert d[1][1].bitsize == -1
+    assert sizeof(BStruct2) == 2 + sizeof(BLong)
+    assert alignof(BStruct2) == 2
+
+def test_packed_with_bitfields():
+    if sys.platform == "win32":
+        py.test.skip("testing gcc behavior")
+    BLong = new_primitive_type("long")
+    BChar = new_primitive_type("char")
+    BStruct = new_struct_type("struct foo")
+    py.test.raises(NotImplementedError,
+                   complete_struct_or_union,
+                   BStruct, [('a1', BLong, 30),
+                             ('a2', BChar, 5)],
+                   None, -1, -1, SF_PACKED)
+
+def test_from_buffer():
+    import array
+    a = array.array('H', [10000, 20000, 30000])
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    c = from_buffer(BCharA, a)
+    assert typeof(c) is BCharA
+    assert len(c) == 6
+    assert repr(c) == "<cdata 'char[]' buffer len 6 from 'array.array' object>"
+    p = new_pointer_type(new_primitive_type("unsigned short"))
+    cast(p, c)[1] += 500
+    assert list(a) == [10000, 20500, 30000]
+
+def test_from_buffer_not_str_unicode():
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    p1 = from_buffer(BCharA, b"foo")
+    assert p1 == from_buffer(BCharA, b"foo")
+    import gc; gc.collect()
+    assert p1 == from_buffer(BCharA, b"foo")
+    py.test.raises(TypeError, from_buffer, BCharA, u+"foo")
+    try:
+        from __builtin__ import buffer
+    except ImportError:
+        pass
+    else:
+        # Python 2 only
+        contents = from_buffer(BCharA, buffer(b"foo"))
+        assert len(contents) == len(p1)
+        for i in range(len(contents)):
+            assert contents[i] == p1[i]
+        p4 = buffer(u+"foo")
+        contents = from_buffer(BCharA, buffer(u+"foo"))
+        assert len(contents) == len(p4)
+        for i in range(len(contents)):
+            assert contents[i] == p4[i]
+    try:
+        from __builtin__ import memoryview
+    except ImportError:
+        pass
+    else:
+        contents = from_buffer(BCharA, memoryview(b"foo"))
+        assert len(contents) == len(p1)
+        for i in range(len(contents)):
+            assert contents[i] == p1[i]
+
+
+def test_from_buffer_bytearray():
+    a = bytearray(b"xyz")
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    p = from_buffer(BCharA, a)
+    assert typeof(p) is BCharA
+    assert len(p) == 3
+    assert repr(p) == "<cdata 'char[]' buffer len 3 from 'bytearray' object>"
+    assert p[2] == b"z"
+    p[2] = b"."
+    assert a[2] == ord(".")
+    a[2] = ord("?")
+    assert p[2] == b"?"
+
+def test_from_buffer_more_cases():
+    try:
+        from _cffi_backend import _testbuff
+    except ImportError:
+        py.test.skip("not for pypy")
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    #
+    def check1(bufobj, expected):
+        c = from_buffer(BCharA, bufobj)
+        assert typeof(c) is BCharA
+        if sys.version_info >= (3,):
+            expected = [bytes(c, "ascii") for c in expected]
+        assert list(c) == list(expected)
+    #
+    def check(methods, expected, expected_for_memoryview=None):
+        if sys.version_info >= (3,):
+            if methods <= 7:
+                return
+            if expected_for_memoryview is not None:
+                expected = expected_for_memoryview
+        class X(object):
+            pass
+        _testbuff(X, methods)
+        bufobj = X()
+        check1(bufobj, expected)
+        try:
+            from __builtin__ import buffer
+            bufobjb = buffer(bufobj)
+        except (TypeError, ImportError):
+            pass
+        else:
+            check1(bufobjb, expected)
+        try:
+            bufobjm = memoryview(bufobj)
+        except (TypeError, NameError):
+            pass
+        else:
+            check1(bufobjm, expected_for_memoryview or expected)
+    #
+    check(1, "RDB")
+    check(2, "WRB")
+    check(4, "CHB")
+    check(8, "GTB")
+    check(16, "ROB")
+    #
+    check(1 | 2,  "RDB")
+    check(1 | 4,  "RDB")
+    check(2 | 4,  "CHB")
+    check(1 | 8,  "RDB", "GTB")
+    check(1 | 16, "RDB", "ROB")
+    check(2 | 8,  "WRB", "GTB")
+    check(2 | 16, "WRB", "ROB")
+    check(4 | 8,  "CHB", "GTB")
+    check(4 | 16, "CHB", "ROB")
+
+def test_from_buffer_require_writable():
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    p1 = from_buffer(BCharA, b"foo", False)
+    assert p1 == from_buffer(BCharA, b"foo", False)
+    py.test.raises((TypeError, BufferError), from_buffer, BCharA, b"foo", True)
+    ba = bytearray(b"foo")
+    p1 = from_buffer(BCharA, ba, True)
+    p1[0] = b"g"
+    assert ba == b"goo"
+
+def test_from_buffer_types():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BIntA = new_array_type(BIntP, None)
+    lst = [-12345678, 87654321, 489148]
+    bytestring = buffer(newp(BIntA, lst))[:] + b'XYZ'
+    #
+    p1 = from_buffer(BIntA, bytestring)      # int[]
+    assert typeof(p1) is BIntA
+    assert len(p1) == 3
+    assert p1[0] == lst[0]
+    assert p1[1] == lst[1]
+    assert p1[2] == lst[2]
+    py.test.raises(IndexError, "p1[3]")
+    py.test.raises(IndexError, "p1[-1]")
+    #
+    py.test.raises(TypeError, from_buffer, BInt, bytestring)
+    py.test.raises(TypeError, from_buffer, BIntP, bytestring)
+    #
+    BIntA2 = new_array_type(BIntP, 2)
+    p2 = from_buffer(BIntA2, bytestring)     # int[2]
+    assert typeof(p2) is BIntA2
+    assert len(p2) == 2
+    assert p2[0] == lst[0]
+    assert p2[1] == lst[1]
+    py.test.raises(IndexError, "p2[2]")
+    py.test.raises(IndexError, "p2[-1]")
+    assert p2 == p1
+    #
+    BIntA4 = new_array_type(BIntP, 4)        # int[4]: too big
+    py.test.raises(ValueError, from_buffer, BIntA4, bytestring)
+    #
+    BStruct = new_struct_type("foo")
+    complete_struct_or_union(BStruct, [('a1', BInt, -1),
+                                       ('a2', BInt, -1)])
+    BStructP = new_pointer_type(BStruct)
+    BStructA = new_array_type(BStructP, None)
+    p1 = from_buffer(BStructA, bytestring)   # struct[]
+    assert len(p1) == 1
+    assert typeof(p1) is BStructA
+    assert p1[0].a1 == lst[0]
+    assert p1[0].a2 == lst[1]
+    py.test.raises(IndexError, "p1[1]")
+    #
+    BEmptyStruct = new_struct_type("empty")
+    complete_struct_or_union(BEmptyStruct, [], Ellipsis, 0)
+    assert sizeof(BEmptyStruct) == 0
+    BEmptyStructP = new_pointer_type(BEmptyStruct)
+    BEmptyStructA = new_array_type(BEmptyStructP, None)
+    py.test.raises(ZeroDivisionError, from_buffer,      # empty[]
+                                      BEmptyStructA, bytestring)
+    #
+    BEmptyStructA5 = new_array_type(BEmptyStructP, 5)
+    p1 = from_buffer(BEmptyStructA5, bytestring)   # struct empty[5]
+    assert typeof(p1) is BEmptyStructA5
+    assert len(p1) == 5
+    assert cast(BIntP, p1) == from_buffer(BIntA, bytestring)
+
+def test_memmove():
+    Short = new_primitive_type("short")
+    ShortA = new_array_type(new_pointer_type(Short), None)
+    Char = new_primitive_type("char")
+    CharA = new_array_type(new_pointer_type(Char), None)
+    p = newp(ShortA, [-1234, -2345, -3456, -4567, -5678])
+    memmove(p, p + 1, 4)
+    assert list(p) == [-2345, -3456, -3456, -4567, -5678]
+    p[2] = 999
+    memmove(p + 2, p, 6)
+    assert list(p) == [-2345, -3456, -2345, -3456, 999]
+    memmove(p + 4, newp(CharA, b"\x71\x72"), 2)
+    if sys.byteorder == 'little':
+        assert list(p) == [-2345, -3456, -2345, -3456, 0x7271]
+    else:
+        assert list(p) == [-2345, -3456, -2345, -3456, 0x7172]
+
+def test_memmove_buffer():
+    import array
+    Short = new_primitive_type("short")
+    ShortA = new_array_type(new_pointer_type(Short), None)
+    a = array.array('H', [10000, 20000, 30000])
+    p = newp(ShortA, 5)
+    memmove(p, a, 6)
+    assert list(p) == [10000, 20000, 30000, 0, 0]
+    memmove(p + 1, a, 6)
+    assert list(p) == [10000, 10000, 20000, 30000, 0]
+    b = array.array('h', [-1000, -2000, -3000])
+    memmove(b, a, 4)
+    assert b.tolist() == [10000, 20000, -3000]
+    assert a.tolist() == [10000, 20000, 30000]
+    p[0] = 999
+    p[1] = 998
+    p[2] = 997
+    p[3] = 996
+    p[4] = 995
+    memmove(b, p, 2)
+    assert b.tolist() == [999, 20000, -3000]
+    memmove(b, p + 2, 4)
+    assert b.tolist() == [997, 996, -3000]
+    p[2] = -p[2]
+    p[3] = -p[3]
+    memmove(b, p + 2, 6)
+    assert b.tolist() == [-997, -996, 995]
+
+def test_memmove_readonly_readwrite():
+    SignedChar = new_primitive_type("signed char")
+    SignedCharA = new_array_type(new_pointer_type(SignedChar), None)
+    p = newp(SignedCharA, 5)
+    memmove(p, b"abcde", 3)
+    assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0]
+    memmove(p, bytearray(b"ABCDE"), 2)
+    assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0]
+    py.test.raises((TypeError, BufferError), memmove, b"abcde", p, 3)
+    ba = bytearray(b"xxxxx")
+    memmove(dest=ba, src=p, n=3)
+    assert ba == bytearray(b"ABcxx")
+    memmove(ba, b"EFGH", 4)
+    assert ba == bytearray(b"EFGHx")
+
+def test_memmove_sign_check():
+    SignedChar = new_primitive_type("signed char")
+    SignedCharA = new_array_type(new_pointer_type(SignedChar), None)
+    p = newp(SignedCharA, 5)
+    py.test.raises(ValueError, memmove, p, p + 1, -1)   # not segfault
+
+def test_memmove_bad_cdata():
+    BInt = new_primitive_type("int")
+    p = cast(BInt, 42)
+    py.test.raises(TypeError, memmove, p, bytearray(b'a'), 1)
+    py.test.raises(TypeError, memmove, bytearray(b'a'), p, 1)
+
+def test_dereference_null_ptr():
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    p = cast(BIntPtr, 0)
+    py.test.raises(RuntimeError, "p[0]")
+    py.test.raises(RuntimeError, "p[0] = 42")
+    py.test.raises(RuntimeError, "p[42]")
+    py.test.raises(RuntimeError, "p[42] = -1")
+
+def test_mixup():
+    BStruct1 = new_struct_type("foo")
+    BStruct2 = new_struct_type("foo")   # <= same name as BStruct1
+    BStruct3 = new_struct_type("bar")
+    BStruct1Ptr = new_pointer_type(BStruct1)
+    BStruct2Ptr = new_pointer_type(BStruct2)
+    BStruct3Ptr = new_pointer_type(BStruct3)
+    BStruct1PtrPtr = new_pointer_type(BStruct1Ptr)
+    BStruct2PtrPtr = new_pointer_type(BStruct2Ptr)
+    BStruct3PtrPtr = new_pointer_type(BStruct3Ptr)
+    pp1 = newp(BStruct1PtrPtr)
+    pp2 = newp(BStruct2PtrPtr)
+    pp3 = newp(BStruct3PtrPtr)
+    pp1[0] = pp1[0]
+    e = py.test.raises(TypeError, "pp3[0] = pp1[0]")
+    assert str(e.value).startswith("initializer for ctype 'bar *' must be a ")
+    assert str(e.value).endswith(", not cdata 'foo *'")
+    e = py.test.raises(TypeError, "pp2[0] = pp1[0]")
+    assert str(e.value) == ("initializer for ctype 'foo *' appears indeed to "
+                            "be 'foo *', but the types are different (check "
+                            "that you are not e.g. mixing up different ffi "
+                            "instances)")
+
+def test_stdcall_function_type():
+    assert FFI_CDECL == FFI_DEFAULT_ABI
+    try:
+        stdcall = FFI_STDCALL
+    except NameError:
+        stdcall = FFI_DEFAULT_ABI
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt, BInt), BInt, False, stdcall)
+    if stdcall != FFI_DEFAULT_ABI:
+        assert repr(BFunc) == "<ctype 'int(__stdcall *)(int, int)'>"
+    else:
+        assert repr(BFunc) == "<ctype 'int(*)(int, int)'>"
+
+def test_get_common_types():
+    d = {}
+    _get_common_types(d)
+    assert d['bool'] == '_Bool'
+
+def test_unpack():
+    BChar = new_primitive_type("char")
+    BArray = new_array_type(new_pointer_type(BChar), 10)   # char[10]
+    p = newp(BArray, b"abc\x00def")
+    p0 = p
+    assert unpack(p, 10) == b"abc\x00def\x00\x00\x00"
+    assert unpack(p+1, 5) == b"bc\x00de"
+
+    for typename in ["wchar_t", "char16_t", "char32_t"]:
+        BWChar = new_primitive_type(typename)
+        BArray = new_array_type(new_pointer_type(BWChar), 10)   # wchar_t[10]
+        p = newp(BArray, u"abc\x00def")
+        assert unpack(p, 10) == u"abc\x00def\x00\x00\x00"
+
+    for typename, samples in [
+            ("uint8_t",  [0, 2**8-1]),
+            ("uint16_t", [0, 2**16-1]),
+            ("uint32_t", [0, 2**32-1]),
+            ("uint64_t", [0, 2**64-1]),
+            ("int8_t",  [-2**7, 2**7-1]),
+            ("int16_t", [-2**15, 2**15-1]),
+            ("int32_t", [-2**31, 2**31-1]),
+            ("int64_t", [-2**63, 2**63-1]),
+            ("_Bool", [False, True]),
+            ("float", [0.0, 10.5]),
+            ("double", [12.34, 56.78]),
+            ]:
+        BItem = new_primitive_type(typename)
+        BArray = new_array_type(new_pointer_type(BItem), 10)
+        p = newp(BArray, samples)
+        result = unpack(p, len(samples))
+        assert result == samples
+        for i in range(len(samples)):
+            assert result[i] == p[i] and type(result[i]) is type(p[i])
+            assert (type(result[i]) is bool) == (type(samples[i]) is bool)
+    #
+    BInt = new_primitive_type("int")
+    py.test.raises(TypeError, unpack, p)
+    py.test.raises(TypeError, unpack, b"foobar", 6)
+    py.test.raises(TypeError, unpack, cast(BInt, 42), 1)
+    #
+    BPtr = new_pointer_type(BInt)
+    random_ptr = cast(BPtr, -424344)
+    other_ptr = cast(BPtr, 54321)
+    BArray = new_array_type(new_pointer_type(BPtr), None)
+    lst = unpack(newp(BArray, [random_ptr, other_ptr]), 2)
+    assert lst == [random_ptr, other_ptr]
+    #
+    BFunc = new_function_type((BInt, BInt), BInt, False)
+    BFuncPtr = new_pointer_type(BFunc)
+    lst = unpack(newp(new_array_type(BFuncPtr, None), 2), 2)
+    assert len(lst) == 2
+    assert not lst[0] and not lst[1]
+    assert typeof(lst[0]) is BFunc
+    #
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    e = py.test.raises(ValueError, unpack, cast(BStructPtr, 42), 5)
+    assert str(e.value) == "'foo *' points to items of unknown size"
+    complete_struct_or_union(BStruct, [('a1', BInt, -1),
+                                       ('a2', BInt, -1)])
+    array_of_structs = newp(new_array_type(BStructPtr, None), [[4,5], [6,7]])
+    lst = unpack(array_of_structs, 2)
+    assert typeof(lst[0]) is BStruct
+    assert lst[0].a1 == 4 and lst[1].a2 == 7
+    #
+    py.test.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 0)
+    py.test.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 10)
+    #
+    py.test.raises(ValueError, unpack, p0, -1)
+    py.test.raises(ValueError, unpack, p, -1)
+
+def test_cdata_dir():
+    BInt = new_primitive_type("int")
+    p = cast(BInt, 42)
+    check_dir(p, [])
+    p = newp(new_array_type(new_pointer_type(BInt), None), 5)
+    check_dir(p, [])
+    BStruct = new_struct_type("foo")
+    p = cast(new_pointer_type(BStruct), 0)
+    check_dir(p, [])    # opaque
+    complete_struct_or_union(BStruct, [('a2', BInt, -1),
+                                       ('a1', BInt, -1)])
+    check_dir(p, ['a1', 'a2'])   # always sorted
+    p = newp(new_pointer_type(BStruct), None)
+    check_dir(p, ['a1', 'a2'])
+    check_dir(p[0], ['a1', 'a2'])
+    pp = newp(new_pointer_type(new_pointer_type(BStruct)), p)
+    check_dir(pp, [])
+    check_dir(pp[0], ['a1', 'a2'])
+    check_dir(pp[0][0], ['a1', 'a2'])
+
+def test_char_pointer_conversion():
+    import warnings
+    assert __version__.startswith("1."), (
+        "the warning will be an error if we ever release cffi 2.x")
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BVoidP = new_pointer_type(new_void_type())
+    BUCharP = new_pointer_type(new_primitive_type("unsigned char"))
+    z1 = cast(BCharP, 0)
+    z2 = cast(BIntP, 0)
+    z3 = cast(BVoidP, 0)
+    z4 = cast(BUCharP, 0)
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter("always")
+        newp(new_pointer_type(BIntP), z1)    # warn
+        assert len(w) == 1
+        newp(new_pointer_type(BVoidP), z1)   # fine
+        assert len(w) == 1
+        newp(new_pointer_type(BCharP), z2)   # warn
+        assert len(w) == 2
+        newp(new_pointer_type(BVoidP), z2)   # fine
+        assert len(w) == 2
+        newp(new_pointer_type(BCharP), z3)   # fine
+        assert len(w) == 2
+        newp(new_pointer_type(BIntP), z3)    # fine
+        assert len(w) == 2
+        newp(new_pointer_type(BCharP), z4)   # fine (ignore signedness here)
+        assert len(w) == 2
+        newp(new_pointer_type(BUCharP), z1)  # fine (ignore signedness here)
+        assert len(w) == 2
+        newp(new_pointer_type(BUCharP), z3)  # fine
+        assert len(w) == 2
+    # check that the warnings are associated with lines in this file
+    assert w[1].lineno == w[0].lineno + 4
+
+def test_primitive_comparison():
+    def assert_eq(a, b):
+        assert (a == b) is True
+        assert (b == a) is True
+        assert (a != b) is False
+        assert (b != a) is False
+        assert (a < b) is False
+        assert (a <= b) is True
+        assert (a > b) is False
+        assert (a >= b) is True
+        assert (b < a) is False
+        assert (b <= a) is True
+        assert (b > a) is False
+        assert (b >= a) is True
+        assert hash(a) == hash(b)
+    def assert_lt(a, b, check_hash=True):
+        assert (a == b) is False
+        assert (b == a) is False
+        assert (a != b) is True
+        assert (b != a) is True
+        assert (a < b) is True
+        assert (a <= b) is True
+        assert (a > b) is False
+        assert (a >= b) is False
+        assert (b < a) is False
+        assert (b <= a) is False
+        assert (b > a) is True
+        assert (b >= a) is True
+        if check_hash:
+            assert hash(a) != hash(b)    # (or at least, it is unlikely)
+    def assert_gt(a, b, check_hash=True):
+        assert_lt(b, a, check_hash)
+    def assert_ne(a, b):
+        assert (a == b) is False
+        assert (b == a) is False
+        assert (a != b) is True
+        assert (b != a) is True
+        if strict_compare:
+            py.test.raises(TypeError, "a < b")
+            py.test.raises(TypeError, "a <= b")
+            py.test.raises(TypeError, "a > b")
+            py.test.raises(TypeError, "a >= b")
+            py.test.raises(TypeError, "b < a")
+            py.test.raises(TypeError, "b <= a")
+            py.test.raises(TypeError, "b > a")
+            py.test.raises(TypeError, "b >= a")
+        elif a < b:
+            assert_lt(a, b)
+        else:
+            assert_lt(b, a)
+    assert_eq(5, 5)
+    assert_lt(3, 5)
+    assert_ne('5', 5)
+    #
+    t1 = new_primitive_type("char")
+    t2 = new_primitive_type("int")
+    t3 = new_primitive_type("unsigned char")
+    t4 = new_primitive_type("unsigned int")
+    t5 = new_primitive_type("float")
+    t6 = new_primitive_type("double")
+    assert_eq(cast(t1, 65), b'A')
+    assert_lt(cast(t1, 64), b'\x99')
+    assert_gt(cast(t1, 200), b'A')
+    assert_ne(cast(t1, 65), 65)
+    assert_eq(cast(t2, -25), -25)
+    assert_lt(cast(t2, -25), -24)
+    assert_gt(cast(t2, -25), -26)
+    assert_eq(cast(t3, 65), 65)
+    assert_ne(cast(t3, 65), b'A')
+    assert_ne(cast(t3, 65), cast(t1, 65))
+    assert_gt(cast(t4, -1), -1, check_hash=False)
+    assert_gt(cast(t4, -1), cast(t2, -1), check_hash=False)
+    assert_gt(cast(t4, -1), 99999)
+    assert_eq(cast(t4, -1), 256 ** size_of_int() - 1)
+    assert_eq(cast(t5, 3.0), 3)
+    assert_eq(cast(t5, 3.5), 3.5)
+    assert_lt(cast(t5, 3.3), 3.3)   # imperfect rounding
+    assert_eq(cast(t6, 3.3), 3.3)
+    assert_eq(cast(t5, 3.5), cast(t6, 3.5))
+    assert_lt(cast(t5, 3.1), cast(t6, 3.1))   # imperfect rounding
+    assert_eq(cast(t5, 7.0), cast(t3, 7))
+    assert_lt(cast(t5, 3.1), 3.101)
+    assert_gt(cast(t5, 3.1), 3)
+
+def test_explicit_release_new():
+    # release() on a ffi.new() object has no effect on CPython, but
+    # really releases memory on PyPy.  We can't test that effect
+    # though, because a released cdata is not marked.
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    p = newp(BIntP)
+    p[0] = 42
+    py.test.raises(IndexError, "p[1]")
+    release(p)
+    # here, reading p[0] might give garbage or segfault...
+    release(p)   # no effect
+    #
+    BStruct = new_struct_type("struct foo")
+    BStructP = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('p', BIntP, -1)])
+    pstruct = newp(BStructP)
+    assert pstruct.p == cast(BIntP, 0)
+    release(pstruct)
+    # here, reading pstruct.p might give garbage or segfault...
+    release(pstruct)   # no effect
+
+def test_explicit_release_new_contextmgr():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    with newp(BIntP) as p:
+        p[0] = 42
+        assert p[0] == 42
+    # here, reading p[0] might give garbage or segfault...
+    release(p)   # no effect
+
+def test_explicit_release_badtype():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    p = cast(BIntP, 12345)
+    py.test.raises(ValueError, release, p)
+    py.test.raises(ValueError, release, p)
+    BStruct = new_struct_type("struct foo")
+    BStructP = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('p', BIntP, -1)])
+    pstruct = newp(BStructP)
+    py.test.raises(ValueError, release, pstruct[0])
+
+def test_explicit_release_badtype_contextmgr():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    p = cast(BIntP, 12345)
+    py.test.raises(ValueError, "with p: pass")
+    py.test.raises(ValueError, "with p: pass")
+
+def test_explicit_release_gc():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    seen = []
+    intp1 = newp(BIntP, 12345)
+    p1 = cast(BIntP, intp1)
+    p = gcp(p1, seen.append)
+    assert seen == []
+    release(p)
+    assert seen == [p1]
+    assert p1[0] == 12345
+    assert p[0] == 12345  # true so far, but might change to raise RuntimeError
+    release(p)   # no effect
+
+def test_explicit_release_gc_contextmgr():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    seen = []
+    intp1 = newp(BIntP, 12345)
+    p1 = cast(BIntP, intp1)
+    p = gcp(p1, seen.append)
+    with p:
+        assert p[0] == 12345
+        assert seen == []
+    assert seen == [p1]
+    assert p1[0] == 12345
+    assert p[0] == 12345  # true so far, but might change to raise RuntimeError
+    release(p)   # no effect
+
+def test_explicit_release_from_buffer():
+    a = bytearray(b"xyz")
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    p = from_buffer(BCharA, a)
+    assert p[2] == b"z"
+    release(p)
+    assert p[2] == b"z"  # true so far, but might change to raise RuntimeError
+    release(p)   # no effect
+
+def test_explicit_release_from_buffer_contextmgr():
+    a = bytearray(b"xyz")
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    p = from_buffer(BCharA, a)
+    with p:
+        assert p[2] == b"z"
+    assert p[2] == b"z"  # true so far, but might change to raise RuntimeError
+    release(p)   # no effect
+
+def test_explicit_release_bytearray_on_cpython():
+    if '__pypy__' in sys.builtin_module_names:
+        py.test.skip("pypy's bytearray are never locked")
+    a = bytearray(b"xyz")
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    a += b't' * 10
+    p = from_buffer(BCharA, a)
+    py.test.raises(BufferError, "a += b'u' * 100")
+    release(p)
+    a += b'v' * 100
+    release(p)   # no effect
+    a += b'w' * 1000
+    assert a == bytearray(b"xyz" + b't' * 10 + b'v' * 100 + b'w' * 1000)
diff --git a/c/wchar_helper.h b/c/wchar_helper.h
new file mode 100644
index 0000000..8e6ea58
--- /dev/null
+++ b/c/wchar_helper.h
@@ -0,0 +1,246 @@
+/*
+ * wchar_t helpers
+ */
+
+typedef uint16_t cffi_char16_t;
+typedef uint32_t cffi_char32_t;
+
+
+#if Py_UNICODE_SIZE == 2
+
+/* Before Python 2.7, PyUnicode_FromWideChar is not able to convert
+   wchar_t values greater than 65535 into two-unicode-characters surrogates.
+   But even the Python 2.7 version doesn't detect wchar_t values that are
+   out of range(1114112), and just returns nonsense.
+
+   From cffi 1.11 we can't use it anyway, because we need a version
+   with char32_t input types.
+*/
+static PyObject *
+_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size)
+{
+    PyObject *unicode;
+    register Py_ssize_t i;
+    Py_ssize_t alloc;
+    const cffi_char32_t *orig_w;
+
+    alloc = size;
+    orig_w = w;
+    for (i = size; i > 0; i--) {
+        if (*w > 0xFFFF)
+            alloc++;
+        w++;
+    }
+    w = orig_w;
+    unicode = PyUnicode_FromUnicode(NULL, alloc);
+    if (!unicode)
+        return NULL;
+
+    /* Copy the wchar_t data into the new object */
+    {
+        register Py_UNICODE *u;
+        u = PyUnicode_AS_UNICODE(unicode);
+        for (i = size; i > 0; i--) {
+            if (*w > 0xFFFF) {
+                cffi_char32_t ordinal;
+                if (*w > 0x10FFFF) {
+                    PyErr_Format(PyExc_ValueError,
+                                 "char32_t out of range for "
+                                 "conversion to unicode: 0x%x", (int)*w);
+                    Py_DECREF(unicode);
+                    return NULL;
+                }
+                ordinal = *w++;
+                ordinal -= 0x10000;
+                *u++ = 0xD800 | (ordinal >> 10);
+                *u++ = 0xDC00 | (ordinal & 0x3FF);
+            }
+            else
+                *u++ = *w++;
+        }
+    }
+    return unicode;
+}
+
+static PyObject *
+_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size)
+{
+    return PyUnicode_FromUnicode((const Py_UNICODE *)w, size);
+}
+
+#else   /* Py_UNICODE_SIZE == 4 */
+
+static PyObject *
+_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size)
+{
+    return PyUnicode_FromUnicode((const Py_UNICODE *)w, size);
+}
+
+static PyObject *
+_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size)
+{
+    /* 'size' is the length of the 'w' array */
+    PyObject *result = PyUnicode_FromUnicode(NULL, size);
+
+    if (result != NULL) {
+        Py_UNICODE *u_base = PyUnicode_AS_UNICODE(result);
+        Py_UNICODE *u = u_base;
+
+        if (size == 1) {      /* performance only */
+            *u = (cffi_char32_t)*w;
+        }
+        else {
+            while (size > 0) {
+                cffi_char32_t ch = *w++;
+                size--;
+                if (0xD800 <= ch && ch <= 0xDBFF && size > 0) {
+                    cffi_char32_t ch2 = *w;
+                    if (0xDC00 <= ch2 && ch2 <= 0xDFFF) {
+                        ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000;
+                        w++;
+                        size--;
+                    }
+                }
+                *u++ = ch;
+            }
+            if (PyUnicode_Resize(&result, u - u_base) < 0) {
+                Py_DECREF(result);
+                return NULL;
+            }
+        }
+    }
+    return result;
+}
+
+#endif
+
+
+#define IS_SURROGATE(u)   (0xD800 <= (u)[0] && (u)[0] <= 0xDBFF &&   \
+                           0xDC00 <= (u)[1] && (u)[1] <= 0xDFFF)
+#define AS_SURROGATE(u)   (0x10000 + (((u)[0] - 0xD800) << 10) +     \
+                                     ((u)[1] - 0xDC00))
+
+static int
+_my_PyUnicode_AsSingleChar16(PyObject *unicode, cffi_char16_t *result,
+                             char *err_got)
+{
+    Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode);
+    if (PyUnicode_GET_SIZE(unicode) != 1) {
+        sprintf(err_got, "unicode string of length %zd",
+                PyUnicode_GET_SIZE(unicode));
+        return -1;
+    }
+#if Py_UNICODE_SIZE == 4
+    if (((unsigned int)u[0]) > 0xFFFF)
+    {
+        sprintf(err_got, "larger-than-0xFFFF character");
+        return -1;
+    }
+#endif
+    *result = (cffi_char16_t)u[0];
+    return 0;
+}
+
+static int
+_my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result,
+                             char *err_got)
+{
+    Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode);
+    if (PyUnicode_GET_SIZE(unicode) == 1) {
+        *result = (cffi_char32_t)u[0];
+        return 0;
+    }
+#if Py_UNICODE_SIZE == 2
+    if (PyUnicode_GET_SIZE(unicode) == 2 && IS_SURROGATE(u)) {
+        *result = AS_SURROGATE(u);
+        return 0;
+    }
+#endif
+    sprintf(err_got, "unicode string of length %zd",
+            PyUnicode_GET_SIZE(unicode));
+    return -1;
+}
+
+static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode)
+{
+    Py_ssize_t length = PyUnicode_GET_SIZE(unicode);
+    Py_ssize_t result = length;
+
+#if Py_UNICODE_SIZE == 4
+    Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode);
+    Py_ssize_t i;
+
+    for (i=0; i<length; i++) {
+        if (u[i] > 0xFFFF)
+            result++;
+    }
+#endif
+    return result;
+}
+
+static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode)
+{
+    Py_ssize_t length = PyUnicode_GET_SIZE(unicode);
+    Py_ssize_t result = length;
+
+#if Py_UNICODE_SIZE == 2
+    Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode);
+    Py_ssize_t i;
+
+    for (i=0; i<length-1; i++) {
+        if (IS_SURROGATE(u+i))
+            result--;
+    }
+#endif
+    return result;
+}
+
+static int _my_PyUnicode_AsChar16(PyObject *unicode,
+                                  cffi_char16_t *result,
+                                  Py_ssize_t resultlen)
+{
+    Py_ssize_t len = PyUnicode_GET_SIZE(unicode);
+    Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode);
+    Py_ssize_t i;
+    for (i=0; i<len; i++) {
+#if Py_UNICODE_SIZE == 2
+        cffi_char16_t ordinal = u[i];
+#else
+        cffi_char32_t ordinal = u[i];
+        if (ordinal > 0xFFFF) {
+            if (ordinal > 0x10FFFF) {
+                PyErr_Format(PyExc_ValueError,
+                             "unicode character out of range for "
+                             "conversion to char16_t: 0x%x", (int)ordinal);
+                return -1;
+            }
+            ordinal -= 0x10000;
+            *result++ = 0xD800 | (ordinal >> 10);
+            *result++ = 0xDC00 | (ordinal & 0x3FF);
+            continue;
+        }
+#endif
+        *result++ = ordinal;
+    }
+    return 0;
+}
+
+static int _my_PyUnicode_AsChar32(PyObject *unicode,
+                                  cffi_char32_t *result,
+                                  Py_ssize_t resultlen)
+{
+    Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode);
+    Py_ssize_t i;
+    for (i=0; i<resultlen; i++) {
+        cffi_char32_t ordinal = *u;
+#if Py_UNICODE_SIZE == 2
+        if (IS_SURROGATE(u)) {
+            ordinal = AS_SURROGATE(u);
+            u++;
+        }
+#endif
+        result[i] = ordinal;
+        u++;
+    }
+    return 0;
+}
diff --git a/c/wchar_helper_3.h b/c/wchar_helper_3.h
new file mode 100644
index 0000000..f15464e
--- /dev/null
+++ b/c/wchar_helper_3.h
@@ -0,0 +1,149 @@
+/*
+ * wchar_t helpers, version CPython >= 3.3.
+ *
+ * CPython 3.3 added support for sys.maxunicode == 0x10FFFF on all
+ * platforms, even ones with wchar_t limited to 2 bytes.  As such,
+ * this code here works from the outside like wchar_helper.h in the
+ * case Py_UNICODE_SIZE == 4, but the implementation is very different.
+ */
+
+typedef uint16_t cffi_char16_t;
+typedef uint32_t cffi_char32_t;
+
+
+static PyObject *
+_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size)
+{
+    return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, w, size);
+}
+
+static PyObject *
+_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size)
+{
+    /* are there any surrogate pairs, and if so, how many? */
+    Py_ssize_t i, count_surrogates = 0;
+    for (i = 0; i < size - 1; i++) {
+        if (0xD800 <= w[i] && w[i] <= 0xDBFF &&
+                0xDC00 <= w[i+1] && w[i+1] <= 0xDFFF)
+            count_surrogates++;
+    }
+    if (count_surrogates == 0) {
+        /* no, fast path */
+        return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, w, size);
+    }
+    else
+    {
+        PyObject *result = PyUnicode_New(size - count_surrogates, 0x10FFFF);
+        Py_UCS4 *data;
+        assert(PyUnicode_KIND(result) == PyUnicode_4BYTE_KIND);
+        data = PyUnicode_4BYTE_DATA(result);
+
+        for (i = 0; i < size; i++)
+        {
+            cffi_char32_t ch = w[i];
+            if (0xD800 <= ch && ch <= 0xDBFF && i < size - 1) {
+                cffi_char32_t ch2 = w[i + 1];
+                if (0xDC00 <= ch2 && ch2 <= 0xDFFF) {
+                    ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000;
+                    i++;
+                }
+            }
+            *data++ = ch;
+        }
+        return result;
+    }
+}
+
+static int
+_my_PyUnicode_AsSingleChar16(PyObject *unicode, cffi_char16_t *result,
+                             char *err_got)
+{
+    cffi_char32_t ch;
+    if (PyUnicode_GET_LENGTH(unicode) != 1) {
+        sprintf(err_got, "unicode string of length %zd",
+                PyUnicode_GET_LENGTH(unicode));
+        return -1;
+    }
+    ch = PyUnicode_READ_CHAR(unicode, 0);
+
+    if (ch > 0xFFFF)
+    {
+        sprintf(err_got, "larger-than-0xFFFF character");
+        return -1;
+    }
+    *result = (cffi_char16_t)ch;
+    return 0;
+}
+
+static int
+_my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result,
+                             char *err_got)
+{
+    if (PyUnicode_GET_LENGTH(unicode) != 1) {
+        sprintf(err_got, "unicode string of length %zd",
+                PyUnicode_GET_LENGTH(unicode));
+        return -1;
+    }
+    *result = PyUnicode_READ_CHAR(unicode, 0);
+    return 0;
+}
+
+static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode)
+{
+    Py_ssize_t length = PyUnicode_GET_LENGTH(unicode);
+    Py_ssize_t result = length;
+    unsigned int kind = PyUnicode_KIND(unicode);
+
+    if (kind == PyUnicode_4BYTE_KIND)
+    {
+        Py_UCS4 *data = PyUnicode_4BYTE_DATA(unicode);
+        Py_ssize_t i;
+        for (i = 0; i < length; i++) {
+            if (data[i] > 0xFFFF)
+                result++;
+        }
+    }
+    return result;
+}
+
+static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode)
+{
+    return PyUnicode_GET_LENGTH(unicode);
+}
+
+static int _my_PyUnicode_AsChar16(PyObject *unicode,
+                                  cffi_char16_t *result,
+                                  Py_ssize_t resultlen)
+{
+    Py_ssize_t len = PyUnicode_GET_LENGTH(unicode);
+    unsigned int kind = PyUnicode_KIND(unicode);
+    void *data = PyUnicode_DATA(unicode);
+    Py_ssize_t i;
+
+    for (i = 0; i < len; i++) {
+        cffi_char32_t ordinal = PyUnicode_READ(kind, data, i);
+        if (ordinal > 0xFFFF) {
+            if (ordinal > 0x10FFFF) {
+                PyErr_Format(PyExc_ValueError,
+                             "unicode character out of range for "
+                             "conversion to char16_t: 0x%x", (int)ordinal);
+                return -1;
+            }
+            ordinal -= 0x10000;
+            *result++ = 0xD800 | (ordinal >> 10);
+            *result++ = 0xDC00 | (ordinal & 0x3FF);
+        }
+        else
+            *result++ = ordinal;
+    }
+    return 0;
+}
+
+static int _my_PyUnicode_AsChar32(PyObject *unicode,
+                                  cffi_char32_t *result,
+                                  Py_ssize_t resultlen)
+{
+    if (PyUnicode_AsUCS4(unicode, (Py_UCS4 *)result, resultlen, 0) == NULL)
+        return -1;
+    return 0;
+}
diff --git a/cffi/Android.bp b/cffi/Android.bp
new file mode 100644
index 0000000..e25787e
--- /dev/null
+++ b/cffi/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+python_library {
+    name: "py-cffi",
+    host_supported: true,
+    srcs: [
+        "*.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+    data: [
+        ":py-cffi-headers",
+    ],
+    libs: [
+        "py-cffi-backend",
+        "py-cffi-backend-libffi",
+        "py-pycparser",
+    ],
+    pkg_path: "cffi",
+}
+
+filegroup {
+    name: "py-cffi-headers",
+    srcs: [
+        "*.h",
+    ],
+}
diff --git a/cffi/__init__.py b/cffi/__init__.py
new file mode 100644
index 0000000..5ebb64b
--- /dev/null
+++ b/cffi/__init__.py
@@ -0,0 +1,14 @@
+__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError',
+           'FFIError']
+
+from .api import FFI
+from .error import CDefError, FFIError, VerificationError, VerificationMissing
+from .error import PkgConfigError
+
+__version__ = "1.12.2"
+__version_info__ = (1, 12, 2)
+
+# The verifier module file names are based on the CRC32 of a string that
+# contains the following version number.  It may be older than __version__
+# if nothing is clearly incompatible.
+__version_verifier_modules__ = "0.8.6"
diff --git a/cffi/_cffi_errors.h b/cffi/_cffi_errors.h
new file mode 100644
index 0000000..83cdad0
--- /dev/null
+++ b/cffi/_cffi_errors.h
@@ -0,0 +1,147 @@
+#ifndef CFFI_MESSAGEBOX
+# ifdef _MSC_VER
+#  define CFFI_MESSAGEBOX  1
+# else
+#  define CFFI_MESSAGEBOX  0
+# endif
+#endif
+
+
+#if CFFI_MESSAGEBOX
+/* Windows only: logic to take the Python-CFFI embedding logic
+   initialization errors and display them in a background thread
+   with MessageBox.  The idea is that if the whole program closes
+   as a result of this problem, then likely it is already a console
+   program and you can read the stderr output in the console too.
+   If it is not a console program, then it will likely show its own
+   dialog to complain, or generally not abruptly close, and for this
+   case the background thread should stay alive.
+*/
+static void *volatile _cffi_bootstrap_text;
+
+static PyObject *_cffi_start_error_capture(void)
+{
+    PyObject *result = NULL;
+    PyObject *x, *m, *bi;
+
+    if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text,
+            (void *)1, NULL) != NULL)
+        return (PyObject *)1;
+
+    m = PyImport_AddModule("_cffi_error_capture");
+    if (m == NULL)
+        goto error;
+
+    result = PyModule_GetDict(m);
+    if (result == NULL)
+        goto error;
+
+#if PY_MAJOR_VERSION >= 3
+    bi = PyImport_ImportModule("builtins");
+#else
+    bi = PyImport_ImportModule("__builtin__");
+#endif
+    if (bi == NULL)
+        goto error;
+    PyDict_SetItemString(result, "__builtins__", bi);
+    Py_DECREF(bi);
+
+    x = PyRun_String(
+        "import sys\n"
+        "class FileLike:\n"
+        "  def write(self, x):\n"
+        "    try:\n"
+        "      of.write(x)\n"
+        "    except: pass\n"
+        "    self.buf += x\n"
+        "fl = FileLike()\n"
+        "fl.buf = ''\n"
+        "of = sys.stderr\n"
+        "sys.stderr = fl\n"
+        "def done():\n"
+        "  sys.stderr = of\n"
+        "  return fl.buf\n",   /* make sure the returned value stays alive */
+        Py_file_input,
+        result, result);
+    Py_XDECREF(x);
+
+ error:
+    if (PyErr_Occurred())
+    {
+        PyErr_WriteUnraisable(Py_None);
+        PyErr_Clear();
+    }
+    return result;
+}
+
+#pragma comment(lib, "user32.lib")
+
+static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored)
+{
+    Sleep(666);    /* may be interrupted if the whole process is closing */
+#if PY_MAJOR_VERSION >= 3
+    MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text,
+                L"Python-CFFI error",
+                MB_OK | MB_ICONERROR);
+#else
+    MessageBoxA(NULL, (char *)_cffi_bootstrap_text,
+                "Python-CFFI error",
+                MB_OK | MB_ICONERROR);
+#endif
+    _cffi_bootstrap_text = NULL;
+    return 0;
+}
+
+static void _cffi_stop_error_capture(PyObject *ecap)
+{
+    PyObject *s;
+    void *text;
+
+    if (ecap == (PyObject *)1)
+        return;
+
+    if (ecap == NULL)
+        goto error;
+
+    s = PyRun_String("done()", Py_eval_input, ecap, ecap);
+    if (s == NULL)
+        goto error;
+
+    /* Show a dialog box, but in a background thread, and
+       never show multiple dialog boxes at once. */
+#if PY_MAJOR_VERSION >= 3
+    text = PyUnicode_AsWideCharString(s, NULL);
+#else
+    text = PyString_AsString(s);
+#endif
+
+    _cffi_bootstrap_text = text;
+
+    if (text != NULL)
+    {
+        HANDLE h;
+        h = CreateThread(NULL, 0, _cffi_bootstrap_dialog,
+                         NULL, 0, NULL);
+        if (h != NULL)
+            CloseHandle(h);
+    }
+    /* decref the string, but it should stay alive as 'fl.buf'
+       in the small module above.  It will really be freed only if
+       we later get another similar error.  So it's a leak of at
+       most one copy of the small module.  That's fine for this
+       situation which is usually a "fatal error" anyway. */
+    Py_DECREF(s);
+    PyErr_Clear();
+    return;
+
+  error:
+    _cffi_bootstrap_text = NULL;
+    PyErr_Clear();
+}
+
+#else
+
+static PyObject *_cffi_start_error_capture(void) { return NULL; }
+static void _cffi_stop_error_capture(PyObject *ecap) { }
+
+#endif
diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
new file mode 100644
index 0000000..37ea74f
--- /dev/null
+++ b/cffi/_cffi_include.h
@@ -0,0 +1,308 @@
+#define _CFFI_
+
+/* We try to define Py_LIMITED_API before including Python.h.
+
+   Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and
+   Py_REF_DEBUG are not defined.  This is a best-effort approximation:
+   we can learn about Py_DEBUG from pyconfig.h, but it is unclear if
+   the same works for the other two macros.  Py_DEBUG implies them,
+   but not the other way around.
+
+   Issue #350 is still open: on Windows, the code here causes it to link
+   with PYTHON36.DLL (for example) instead of PYTHON3.DLL.  A fix was
+   attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv
+   does not make PYTHON3.DLL available, and so the "correctly" compiled
+   version would not run inside a virtualenv.  We will re-apply the fix
+   after virtualenv has been fixed for some time.  For explanation, see
+   issue #355.  For a workaround if you want PYTHON3.DLL and don't worry
+   about virtualenv, see issue #350.  See also 'py_limited_api' in
+   setuptools_ext.py.
+*/
+#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API)
+#  include <pyconfig.h>
+#  if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG)
+#    define Py_LIMITED_API
+#  endif
+#endif
+
+#include <Python.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <stddef.h>
+#include "parse_c_type.h"
+
+/* this block of #ifs should be kept exactly identical between
+   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
+   and cffi/_cffi_include.h */
+#if defined(_MSC_VER)
+# include <malloc.h>   /* for alloca() */
+# if _MSC_VER < 1600   /* MSVC < 2010 */
+   typedef __int8 int8_t;
+   typedef __int16 int16_t;
+   typedef __int32 int32_t;
+   typedef __int64 int64_t;
+   typedef unsigned __int8 uint8_t;
+   typedef unsigned __int16 uint16_t;
+   typedef unsigned __int32 uint32_t;
+   typedef unsigned __int64 uint64_t;
+   typedef __int8 int_least8_t;
+   typedef __int16 int_least16_t;
+   typedef __int32 int_least32_t;
+   typedef __int64 int_least64_t;
+   typedef unsigned __int8 uint_least8_t;
+   typedef unsigned __int16 uint_least16_t;
+   typedef unsigned __int32 uint_least32_t;
+   typedef unsigned __int64 uint_least64_t;
+   typedef __int8 int_fast8_t;
+   typedef __int16 int_fast16_t;
+   typedef __int32 int_fast32_t;
+   typedef __int64 int_fast64_t;
+   typedef unsigned __int8 uint_fast8_t;
+   typedef unsigned __int16 uint_fast16_t;
+   typedef unsigned __int32 uint_fast32_t;
+   typedef unsigned __int64 uint_fast64_t;
+   typedef __int64 intmax_t;
+   typedef unsigned __int64 uintmax_t;
+# else
+#  include <stdint.h>
+# endif
+# if _MSC_VER < 1800   /* MSVC < 2013 */
+#  ifndef __cplusplus
+    typedef unsigned char _Bool;
+#  endif
+# endif
+#else
+# include <stdint.h>
+# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
+#  include <alloca.h>
+# endif
+#endif
+
+#ifdef __GNUC__
+# define _CFFI_UNUSED_FN  __attribute__((unused))
+#else
+# define _CFFI_UNUSED_FN  /* nothing */
+#endif
+
+#ifdef __cplusplus
+# ifndef _Bool
+   typedef bool _Bool;   /* semi-hackish: C++ has no _Bool; bool is builtin */
+# endif
+#endif
+
+/**********  CPython-specific section  **********/
+#ifndef PYPY_VERSION
+
+
+#if PY_MAJOR_VERSION >= 3
+# define PyInt_FromLong PyLong_FromLong
+#endif
+
+#define _cffi_from_c_double PyFloat_FromDouble
+#define _cffi_from_c_float PyFloat_FromDouble
+#define _cffi_from_c_long PyInt_FromLong
+#define _cffi_from_c_ulong PyLong_FromUnsignedLong
+#define _cffi_from_c_longlong PyLong_FromLongLong
+#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
+#define _cffi_from_c__Bool PyBool_FromLong
+
+#define _cffi_to_c_double PyFloat_AsDouble
+#define _cffi_to_c_float PyFloat_AsDouble
+
+#define _cffi_from_c_int(x, type)                                        \
+    (((type)-1) > 0 ? /* unsigned */                                     \
+        (sizeof(type) < sizeof(long) ?                                   \
+            PyInt_FromLong((long)x) :                                    \
+         sizeof(type) == sizeof(long) ?                                  \
+            PyLong_FromUnsignedLong((unsigned long)x) :                  \
+            PyLong_FromUnsignedLongLong((unsigned long long)x)) :        \
+        (sizeof(type) <= sizeof(long) ?                                  \
+            PyInt_FromLong((long)x) :                                    \
+            PyLong_FromLongLong((long long)x)))
+
+#define _cffi_to_c_int(o, type)                                          \
+    ((type)(                                                             \
+     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
+                                         : (type)_cffi_to_c_i8(o)) :     \
+     sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
+                                         : (type)_cffi_to_c_i16(o)) :    \
+     sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o)       \
+                                         : (type)_cffi_to_c_i32(o)) :    \
+     sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
+                                         : (type)_cffi_to_c_i64(o)) :    \
+     (Py_FatalError("unsupported size for type " #type), (type)0)))
+
+#define _cffi_to_c_i8                                                    \
+                 ((int(*)(PyObject *))_cffi_exports[1])
+#define _cffi_to_c_u8                                                    \
+                 ((int(*)(PyObject *))_cffi_exports[2])
+#define _cffi_to_c_i16                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[3])
+#define _cffi_to_c_u16                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[4])
+#define _cffi_to_c_i32                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[5])
+#define _cffi_to_c_u32                                                   \
+                 ((unsigned int(*)(PyObject *))_cffi_exports[6])
+#define _cffi_to_c_i64                                                   \
+                 ((long long(*)(PyObject *))_cffi_exports[7])
+#define _cffi_to_c_u64                                                   \
+                 ((unsigned long long(*)(PyObject *))_cffi_exports[8])
+#define _cffi_to_c_char                                                  \
+                 ((int(*)(PyObject *))_cffi_exports[9])
+#define _cffi_from_c_pointer                                             \
+    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10])
+#define _cffi_to_c_pointer                                               \
+    ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11])
+#define _cffi_get_struct_layout                                          \
+    not used any more
+#define _cffi_restore_errno                                              \
+    ((void(*)(void))_cffi_exports[13])
+#define _cffi_save_errno                                                 \
+    ((void(*)(void))_cffi_exports[14])
+#define _cffi_from_c_char                                                \
+    ((PyObject *(*)(char))_cffi_exports[15])
+#define _cffi_from_c_deref                                               \
+    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16])
+#define _cffi_to_c                                                       \
+    ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17])
+#define _cffi_from_c_struct                                              \
+    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18])
+#define _cffi_to_c_wchar_t                                               \
+    ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19])
+#define _cffi_from_c_wchar_t                                             \
+    ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20])
+#define _cffi_to_c_long_double                                           \
+    ((long double(*)(PyObject *))_cffi_exports[21])
+#define _cffi_to_c__Bool                                                 \
+    ((_Bool(*)(PyObject *))_cffi_exports[22])
+#define _cffi_prepare_pointer_call_argument                              \
+    ((Py_ssize_t(*)(struct _cffi_ctypedescr *,                           \
+                    PyObject *, char **))_cffi_exports[23])
+#define _cffi_convert_array_from_object                                  \
+    ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24])
+#define _CFFI_CPIDX  25
+#define _cffi_call_python                                                \
+    ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX])
+#define _cffi_to_c_wchar3216_t                                           \
+    ((int(*)(PyObject *))_cffi_exports[26])
+#define _cffi_from_c_wchar3216_t                                         \
+    ((PyObject *(*)(int))_cffi_exports[27])
+#define _CFFI_NUM_EXPORTS 28
+
+struct _cffi_ctypedescr;
+
+static void *_cffi_exports[_CFFI_NUM_EXPORTS];
+
+#define _cffi_type(index)   (                           \
+    assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \
+    (struct _cffi_ctypedescr *)_cffi_types[index])
+
+static PyObject *_cffi_init(const char *module_name, Py_ssize_t version,
+                            const struct _cffi_type_context_s *ctx)
+{
+    PyObject *module, *o_arg, *new_module;
+    void *raw[] = {
+        (void *)module_name,
+        (void *)version,
+        (void *)_cffi_exports,
+        (void *)ctx,
+    };
+
+    module = PyImport_ImportModule("_cffi_backend");
+    if (module == NULL)
+        goto failure;
+
+    o_arg = PyLong_FromVoidPtr((void *)raw);
+    if (o_arg == NULL)
+        goto failure;
+
+    new_module = PyObject_CallMethod(
+        module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg);
+
+    Py_DECREF(o_arg);
+    Py_DECREF(module);
+    return new_module;
+
+  failure:
+    Py_XDECREF(module);
+    return NULL;
+}
+
+
+#ifdef HAVE_WCHAR_H
+typedef wchar_t _cffi_wchar_t;
+#else
+typedef uint16_t _cffi_wchar_t;   /* same random pick as _cffi_backend.c */
+#endif
+
+_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o)
+{
+    if (sizeof(_cffi_wchar_t) == 2)
+        return (uint16_t)_cffi_to_c_wchar_t(o);
+    else
+        return (uint16_t)_cffi_to_c_wchar3216_t(o);
+}
+
+_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x)
+{
+    if (sizeof(_cffi_wchar_t) == 2)
+        return _cffi_from_c_wchar_t((_cffi_wchar_t)x);
+    else
+        return _cffi_from_c_wchar3216_t((int)x);
+}
+
+_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o)
+{
+    if (sizeof(_cffi_wchar_t) == 4)
+        return (int)_cffi_to_c_wchar_t(o);
+    else
+        return (int)_cffi_to_c_wchar3216_t(o);
+}
+
+_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(int x)
+{
+    if (sizeof(_cffi_wchar_t) == 4)
+        return _cffi_from_c_wchar_t((_cffi_wchar_t)x);
+    else
+        return _cffi_from_c_wchar3216_t(x);
+}
+
+
+/**********  end CPython-specific section  **********/
+#else
+_CFFI_UNUSED_FN
+static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *);
+# define _cffi_call_python  _cffi_call_python_org
+#endif
+
+
+#define _cffi_array_len(array)   (sizeof(array) / sizeof((array)[0]))
+
+#define _cffi_prim_int(size, sign)                                      \
+    ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8  : _CFFI_PRIM_UINT8)  :    \
+     (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) :    \
+     (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) :    \
+     (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) :    \
+     _CFFI__UNKNOWN_PRIM)
+
+#define _cffi_prim_float(size)                                          \
+    ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT :                       \
+     (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE :                     \
+     (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE :       \
+     _CFFI__UNKNOWN_FLOAT_PRIM)
+
+#define _cffi_check_int(got, got_nonpos, expected)      \
+    ((got_nonpos) == (expected <= 0) &&                 \
+     (got) == (unsigned long long)expected)
+
+#ifdef MS_WIN32
+# define _cffi_stdcall  __stdcall
+#else
+# define _cffi_stdcall  /* nothing */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cffi/_embedding.h b/cffi/_embedding.h
new file mode 100644
index 0000000..3953cd7
--- /dev/null
+++ b/cffi/_embedding.h
@@ -0,0 +1,484 @@
+
+/***** Support code for embedding *****/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if defined(_WIN32)
+#  define CFFI_DLLEXPORT  __declspec(dllexport)
+#elif defined(__GNUC__)
+#  define CFFI_DLLEXPORT  __attribute__((visibility("default")))
+#else
+#  define CFFI_DLLEXPORT  /* nothing */
+#endif
+
+
+/* There are two global variables of type _cffi_call_python_fnptr:
+
+   * _cffi_call_python, which we declare just below, is the one called
+     by ``extern "Python"`` implementations.
+
+   * _cffi_call_python_org, which on CPython is actually part of the
+     _cffi_exports[] array, is the function pointer copied from
+     _cffi_backend.
+
+   After initialization is complete, both are equal.  However, the
+   first one remains equal to &_cffi_start_and_call_python until the
+   very end of initialization, when we are (or should be) sure that
+   concurrent threads also see a completely initialized world, and
+   only then is it changed.
+*/
+#undef _cffi_call_python
+typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *);
+static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *);
+static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python;
+
+
+#ifndef _MSC_VER
+   /* --- Assuming a GCC not infinitely old --- */
+# define cffi_compare_and_swap(l,o,n)  __sync_bool_compare_and_swap(l,o,n)
+# define cffi_write_barrier()          __sync_synchronize()
+# if !defined(__amd64__) && !defined(__x86_64__) &&   \
+     !defined(__i386__) && !defined(__i386)
+#   define cffi_read_barrier()         __sync_synchronize()
+# else
+#   define cffi_read_barrier()         (void)0
+# endif
+#else
+   /* --- Windows threads version --- */
+# include <Windows.h>
+# define cffi_compare_and_swap(l,o,n) \
+                               (InterlockedCompareExchangePointer(l,n,o) == (o))
+# define cffi_write_barrier()       InterlockedCompareExchange(&_cffi_dummy,0,0)
+# define cffi_read_barrier()           (void)0
+static volatile LONG _cffi_dummy;
+#endif
+
+#ifdef WITH_THREAD
+# ifndef _MSC_VER
+#  include <pthread.h>
+   static pthread_mutex_t _cffi_embed_startup_lock;
+# else
+   static CRITICAL_SECTION _cffi_embed_startup_lock;
+# endif
+  static char _cffi_embed_startup_lock_ready = 0;
+#endif
+
+static void _cffi_acquire_reentrant_mutex(void)
+{
+    static void *volatile lock = NULL;
+
+    while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) {
+        /* should ideally do a spin loop instruction here, but
+           hard to do it portably and doesn't really matter I
+           think: pthread_mutex_init() should be very fast, and
+           this is only run at start-up anyway. */
+    }
+
+#ifdef WITH_THREAD
+    if (!_cffi_embed_startup_lock_ready) {
+# ifndef _MSC_VER
+        pthread_mutexattr_t attr;
+        pthread_mutexattr_init(&attr);
+        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+        pthread_mutex_init(&_cffi_embed_startup_lock, &attr);
+# else
+        InitializeCriticalSection(&_cffi_embed_startup_lock);
+# endif
+        _cffi_embed_startup_lock_ready = 1;
+    }
+#endif
+
+    while (!cffi_compare_and_swap(&lock, (void *)1, NULL))
+        ;
+
+#ifndef _MSC_VER
+    pthread_mutex_lock(&_cffi_embed_startup_lock);
+#else
+    EnterCriticalSection(&_cffi_embed_startup_lock);
+#endif
+}
+
+static void _cffi_release_reentrant_mutex(void)
+{
+#ifndef _MSC_VER
+    pthread_mutex_unlock(&_cffi_embed_startup_lock);
+#else
+    LeaveCriticalSection(&_cffi_embed_startup_lock);
+#endif
+}
+
+
+/**********  CPython-specific section  **********/
+#ifndef PYPY_VERSION
+
+#include "_cffi_errors.h"
+
+
+#define _cffi_call_python_org  _cffi_exports[_CFFI_CPIDX]
+
+PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void);   /* forward */
+
+static void _cffi_py_initialize(void)
+{
+    /* XXX use initsigs=0, which "skips initialization registration of
+       signal handlers, which might be useful when Python is
+       embedded" according to the Python docs.  But review and think
+       if it should be a user-controllable setting.
+
+       XXX we should also give a way to write errors to a buffer
+       instead of to stderr.
+
+       XXX if importing 'site' fails, CPython (any version) calls
+       exit().  Should we try to work around this behavior here?
+    */
+    Py_InitializeEx(0);
+}
+
+static int _cffi_initialize_python(void)
+{
+    /* This initializes Python, imports _cffi_backend, and then the
+       present .dll/.so is set up as a CPython C extension module.
+    */
+    int result;
+    PyGILState_STATE state;
+    PyObject *pycode=NULL, *global_dict=NULL, *x;
+
+    state = PyGILState_Ensure();
+
+    /* Call the initxxx() function from the present module.  It will
+       create and initialize us as a CPython extension module, instead
+       of letting the startup Python code do it---it might reimport
+       the same .dll/.so and get maybe confused on some platforms.
+       It might also have troubles locating the .dll/.so again for all
+       I know.
+    */
+    (void)_CFFI_PYTHON_STARTUP_FUNC();
+    if (PyErr_Occurred())
+        goto error;
+
+    /* Now run the Python code provided to ffi.embedding_init_code().
+     */
+    pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE,
+                              "<init code for '" _CFFI_MODULE_NAME "'>",
+                              Py_file_input);
+    if (pycode == NULL)
+        goto error;
+    global_dict = PyDict_New();
+    if (global_dict == NULL)
+        goto error;
+    if (PyDict_SetItemString(global_dict, "__builtins__",
+                             PyThreadState_GET()->interp->builtins) < 0)
+        goto error;
+    x = PyEval_EvalCode(
+#if PY_MAJOR_VERSION < 3
+                        (PyCodeObject *)
+#endif
+                        pycode, global_dict, global_dict);
+    if (x == NULL)
+        goto error;
+    Py_DECREF(x);
+
+    /* Done!  Now if we've been called from
+       _cffi_start_and_call_python() in an ``extern "Python"``, we can
+       only hope that the Python code did correctly set up the
+       corresponding @ffi.def_extern() function.  Otherwise, the
+       general logic of ``extern "Python"`` functions (inside the
+       _cffi_backend module) will find that the reference is still
+       missing and print an error.
+     */
+    result = 0;
+ done:
+    Py_XDECREF(pycode);
+    Py_XDECREF(global_dict);
+    PyGILState_Release(state);
+    return result;
+
+ error:;
+    {
+        /* Print as much information as potentially useful.
+           Debugging load-time failures with embedding is not fun
+        */
+        PyObject *ecap;
+        PyObject *exception, *v, *tb, *f, *modules, *mod;
+        PyErr_Fetch(&exception, &v, &tb);
+        ecap = _cffi_start_error_capture();
+        f = PySys_GetObject((char *)"stderr");
+        if (f != NULL && f != Py_None) {
+            PyFile_WriteString(
+                "Failed to initialize the Python-CFFI embedding logic:\n\n", f);
+        }
+
+        if (exception != NULL) {
+            PyErr_NormalizeException(&exception, &v, &tb);
+            PyErr_Display(exception, v, tb);
+        }
+        Py_XDECREF(exception);
+        Py_XDECREF(v);
+        Py_XDECREF(tb);
+
+        if (f != NULL && f != Py_None) {
+            PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
+                               "\ncompiled with cffi version: 1.12.2"
+                               "\n_cffi_backend module: ", f);
+            modules = PyImport_GetModuleDict();
+            mod = PyDict_GetItemString(modules, "_cffi_backend");
+            if (mod == NULL) {
+                PyFile_WriteString("not loaded", f);
+            }
+            else {
+                v = PyObject_GetAttrString(mod, "__file__");
+                PyFile_WriteObject(v, f, 0);
+                Py_XDECREF(v);
+            }
+            PyFile_WriteString("\nsys.path: ", f);
+            PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0);
+            PyFile_WriteString("\n\n", f);
+        }
+        _cffi_stop_error_capture(ecap);
+    }
+    result = -1;
+    goto done;
+}
+
+PyAPI_DATA(char *) _PyParser_TokenNames[];  /* from CPython */
+
+static int _cffi_carefully_make_gil(void)
+{
+    /* This does the basic initialization of Python.  It can be called
+       completely concurrently from unrelated threads.  It assumes
+       that we don't hold the GIL before (if it exists), and we don't
+       hold it afterwards.
+
+       (What it really does used to be completely different in Python 2
+       and Python 3, with the Python 2 solution avoiding the spin-lock
+       around the Py_InitializeEx() call.  However, after recent changes
+       to CPython 2.7 (issue #358) it no longer works.  So we use the
+       Python 3 solution everywhere.)
+
+       This initializes Python by calling Py_InitializeEx().
+       Important: this must not be called concurrently at all.
+       So we use a global variable as a simple spin lock.  This global
+       variable must be from 'libpythonX.Y.so', not from this
+       cffi-based extension module, because it must be shared from
+       different cffi-based extension modules.  We choose
+       _PyParser_TokenNames[0] as a completely arbitrary pointer value
+       that is never written to.  The default is to point to the
+       string "ENDMARKER".  We change it temporarily to point to the
+       next character in that string.  (Yes, I know it's REALLY
+       obscure.)
+    */
+
+#ifdef WITH_THREAD
+    char *volatile *lock = (char *volatile *)_PyParser_TokenNames;
+    char *old_value;
+
+    while (1) {    /* spin loop */
+        old_value = *lock;
+        if (old_value[0] == 'E') {
+            assert(old_value[1] == 'N');
+            if (cffi_compare_and_swap(lock, old_value, old_value + 1))
+                break;
+        }
+        else {
+            assert(old_value[0] == 'N');
+            /* should ideally do a spin loop instruction here, but
+               hard to do it portably and doesn't really matter I
+               think: PyEval_InitThreads() should be very fast, and
+               this is only run at start-up anyway. */
+        }
+    }
+#endif
+
+    /* call Py_InitializeEx() */
+    {
+        PyGILState_STATE state = PyGILState_UNLOCKED;
+        if (!Py_IsInitialized())
+            _cffi_py_initialize();
+        else
+            state = PyGILState_Ensure();
+
+        PyEval_InitThreads();
+        PyGILState_Release(state);
+    }
+
+#ifdef WITH_THREAD
+    /* release the lock */
+    while (!cffi_compare_and_swap(lock, old_value + 1, old_value))
+        ;
+#endif
+
+    return 0;
+}
+
+/**********  end CPython-specific section  **********/
+
+
+#else
+
+
+/**********  PyPy-specific section  **********/
+
+PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]);   /* forward */
+
+static struct _cffi_pypy_init_s {
+    const char *name;
+    void (*func)(const void *[]);
+    const char *code;
+} _cffi_pypy_init = {
+    _CFFI_MODULE_NAME,
+    (void(*)(const void *[]))_CFFI_PYTHON_STARTUP_FUNC,
+    _CFFI_PYTHON_STARTUP_CODE,
+};
+
+extern int pypy_carefully_make_gil(const char *);
+extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *);
+
+static int _cffi_carefully_make_gil(void)
+{
+    return pypy_carefully_make_gil(_CFFI_MODULE_NAME);
+}
+
+static int _cffi_initialize_python(void)
+{
+    return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init);
+}
+
+/**********  end PyPy-specific section  **********/
+
+
+#endif
+
+
+#ifdef __GNUC__
+__attribute__((noinline))
+#endif
+static _cffi_call_python_fnptr _cffi_start_python(void)
+{
+    /* Delicate logic to initialize Python.  This function can be
+       called multiple times concurrently, e.g. when the process calls
+       its first ``extern "Python"`` functions in multiple threads at
+       once.  It can also be called recursively, in which case we must
+       ignore it.  We also have to consider what occurs if several
+       different cffi-based extensions reach this code in parallel
+       threads---it is a different copy of the code, then, and we
+       can't have any shared global variable unless it comes from
+       'libpythonX.Y.so'.
+
+       Idea:
+
+       * _cffi_carefully_make_gil(): "carefully" call
+         PyEval_InitThreads() (possibly with Py_InitializeEx() first).
+
+       * then we use a (local) custom lock to make sure that a call to this
+         cffi-based extension will wait if another call to the *same*
+         extension is running the initialization in another thread.
+         It is reentrant, so that a recursive call will not block, but
+         only one from a different thread.
+
+       * then we grab the GIL and (Python 2) we call Py_InitializeEx().
+         At this point, concurrent calls to Py_InitializeEx() are not
+         possible: we have the GIL.
+
+       * do the rest of the specific initialization, which may
+         temporarily release the GIL but not the custom lock.
+         Only release the custom lock when we are done.
+    */
+    static char called = 0;
+
+    if (_cffi_carefully_make_gil() != 0)
+        return NULL;
+
+    _cffi_acquire_reentrant_mutex();
+
+    /* Here the GIL exists, but we don't have it.  We're only protected
+       from concurrency by the reentrant mutex. */
+
+    /* This file only initializes the embedded module once, the first
+       time this is called, even if there are subinterpreters. */
+    if (!called) {
+        called = 1;  /* invoke _cffi_initialize_python() only once,
+                        but don't set '_cffi_call_python' right now,
+                        otherwise concurrent threads won't call
+                        this function at all (we need them to wait) */
+        if (_cffi_initialize_python() == 0) {
+            /* now initialization is finished.  Switch to the fast-path. */
+
+            /* We would like nobody to see the new value of
+               '_cffi_call_python' without also seeing the rest of the
+               data initialized.  However, this is not possible.  But
+               the new value of '_cffi_call_python' is the function
+               'cffi_call_python()' from _cffi_backend.  So:  */
+            cffi_write_barrier();
+            /* ^^^ we put a write barrier here, and a corresponding
+               read barrier at the start of cffi_call_python().  This
+               ensures that after that read barrier, we see everything
+               done here before the write barrier.
+            */
+
+            assert(_cffi_call_python_org != NULL);
+            _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org;
+        }
+        else {
+            /* initialization failed.  Reset this to NULL, even if it was
+               already set to some other value.  Future calls to
+               _cffi_start_python() are still forced to occur, and will
+               always return NULL from now on. */
+            _cffi_call_python_org = NULL;
+        }
+    }
+
+    _cffi_release_reentrant_mutex();
+
+    return (_cffi_call_python_fnptr)_cffi_call_python_org;
+}
+
+static
+void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args)
+{
+    _cffi_call_python_fnptr fnptr;
+    int current_err = errno;
+#ifdef _MSC_VER
+    int current_lasterr = GetLastError();
+#endif
+    fnptr = _cffi_start_python();
+    if (fnptr == NULL) {
+        fprintf(stderr, "function %s() called, but initialization code "
+                        "failed.  Returning 0.\n", externpy->name);
+        memset(args, 0, externpy->size_of_result);
+    }
+#ifdef _MSC_VER
+    SetLastError(current_lasterr);
+#endif
+    errno = current_err;
+
+    if (fnptr != NULL)
+        fnptr(externpy, args);
+}
+
+
+/* The cffi_start_python() function makes sure Python is initialized
+   and our cffi module is set up.  It can be called manually from the
+   user C code.  The same effect is obtained automatically from any
+   dll-exported ``extern "Python"`` function.  This function returns
+   -1 if initialization failed, 0 if all is OK.  */
+_CFFI_UNUSED_FN
+static int cffi_start_python(void)
+{
+    if (_cffi_call_python == &_cffi_start_and_call_python) {
+        if (_cffi_start_python() == NULL)
+            return -1;
+    }
+    cffi_read_barrier();
+    return 0;
+}
+
+#undef cffi_compare_and_swap
+#undef cffi_write_barrier
+#undef cffi_read_barrier
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cffi/api.py b/cffi/api.py
new file mode 100644
index 0000000..32fe620
--- /dev/null
+++ b/cffi/api.py
@@ -0,0 +1,961 @@
+import sys, types
+from .lock import allocate_lock
+from .error import CDefError
+from . import model
+
+try:
+    callable
+except NameError:
+    # Python 3.1
+    from collections import Callable
+    callable = lambda x: isinstance(x, Callable)
+
+try:
+    basestring
+except NameError:
+    # Python 3.x
+    basestring = str
+
+_unspecified = object()
+
+
+
+class FFI(object):
+    r'''
+    The main top-level class that you instantiate once, or once per module.
+
+    Example usage:
+
+        ffi = FFI()
+        ffi.cdef("""
+            int printf(const char *, ...);
+        """)
+
+        C = ffi.dlopen(None)   # standard library
+        -or-
+        C = ffi.verify()  # use a C compiler: verify the decl above is right
+
+        C.printf("hello, %s!\n", ffi.new("char[]", "world"))
+    '''
+
+    def __init__(self, backend=None):
+        """Create an FFI instance.  The 'backend' argument is used to
+        select a non-default backend, mostly for tests.
+        """
+        if backend is None:
+            # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with
+            # _cffi_backend.so compiled.
+            import _cffi_backend as backend
+            from . import __version__
+            if backend.__version__ != __version__:
+                # bad version!  Try to be as explicit as possible.
+                if hasattr(backend, '__file__'):
+                    # CPython
+                    raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r.  When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r.  The two versions should be equal; check your installation." % (
+                        __version__, __file__,
+                        backend.__version__, backend.__file__))
+                else:
+                    # PyPy
+                    raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r.  This interpreter comes with a built-in '_cffi_backend' module, which is version %s.  The two versions should be equal; check your installation." % (
+                        __version__, __file__, backend.__version__))
+            # (If you insist you can also try to pass the option
+            # 'backend=backend_ctypes.CTypesBackend()', but don't
+            # rely on it!  It's probably not going to work well.)
+
+        from . import cparser
+        self._backend = backend
+        self._lock = allocate_lock()
+        self._parser = cparser.Parser()
+        self._cached_btypes = {}
+        self._parsed_types = types.ModuleType('parsed_types').__dict__
+        self._new_types = types.ModuleType('new_types').__dict__
+        self._function_caches = []
+        self._libraries = []
+        self._cdefsources = []
+        self._included_ffis = []
+        self._windows_unicode = None
+        self._init_once_cache = {}
+        self._cdef_version = None
+        self._embedding = None
+        self._typecache = model.get_typecache(backend)
+        if hasattr(backend, 'set_ffi'):
+            backend.set_ffi(self)
+        for name in list(backend.__dict__):
+            if name.startswith('RTLD_'):
+                setattr(self, name, getattr(backend, name))
+        #
+        with self._lock:
+            self.BVoidP = self._get_cached_btype(model.voidp_type)
+            self.BCharA = self._get_cached_btype(model.char_array_type)
+        if isinstance(backend, types.ModuleType):
+            # _cffi_backend: attach these constants to the class
+            if not hasattr(FFI, 'NULL'):
+                FFI.NULL = self.cast(self.BVoidP, 0)
+                FFI.CData, FFI.CType = backend._get_types()
+        else:
+            # ctypes backend: attach these constants to the instance
+            self.NULL = self.cast(self.BVoidP, 0)
+            self.CData, self.CType = backend._get_types()
+        self.buffer = backend.buffer
+
+    def cdef(self, csource, override=False, packed=False, pack=None):
+        """Parse the given C source.  This registers all declared functions,
+        types, and global variables.  The functions and global variables can
+        then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'.
+        The types can be used in 'ffi.new()' and other functions.
+        If 'packed' is specified as True, all structs declared inside this
+        cdef are packed, i.e. laid out without any field alignment at all.
+        Alternatively, 'pack' can be a small integer, and requests for
+        alignment greater than that are ignored (pack=1 is equivalent to
+        packed=True).
+        """
+        self._cdef(csource, override=override, packed=packed, pack=pack)
+
+    def embedding_api(self, csource, packed=False, pack=None):
+        self._cdef(csource, packed=packed, pack=pack, dllexport=True)
+        if self._embedding is None:
+            self._embedding = ''
+
+    def _cdef(self, csource, override=False, **options):
+        if not isinstance(csource, str):    # unicode, on Python 2
+            if not isinstance(csource, basestring):
+                raise TypeError("cdef() argument must be a string")
+            csource = csource.encode('ascii')
+        with self._lock:
+            self._cdef_version = object()
+            self._parser.parse(csource, override=override, **options)
+            self._cdefsources.append(csource)
+            if override:
+                for cache in self._function_caches:
+                    cache.clear()
+            finishlist = self._parser._recomplete
+            if finishlist:
+                self._parser._recomplete = []
+                for tp in finishlist:
+                    tp.finish_backend_type(self, finishlist)
+
+    def dlopen(self, name, flags=0):
+        """Load and return a dynamic library identified by 'name'.
+        The standard C library can be loaded by passing None.
+        Note that functions and types declared by 'ffi.cdef()' are not
+        linked to a particular library, just like C headers; in the
+        library we only look for the actual (untyped) symbols.
+        """
+        assert isinstance(name, basestring) or name is None
+        with self._lock:
+            lib, function_cache = _make_ffi_library(self, name, flags)
+            self._function_caches.append(function_cache)
+            self._libraries.append(lib)
+        return lib
+
+    def dlclose(self, lib):
+        """Close a library obtained with ffi.dlopen().  After this call,
+        access to functions or variables from the library will fail
+        (possibly with a segmentation fault).
+        """
+        type(lib).__cffi_close__(lib)
+
+    def _typeof_locked(self, cdecl):
+        # call me with the lock!
+        key = cdecl
+        if key in self._parsed_types:
+            return self._parsed_types[key]
+        #
+        if not isinstance(cdecl, str):    # unicode, on Python 2
+            cdecl = cdecl.encode('ascii')
+        #
+        type = self._parser.parse_type(cdecl)
+        really_a_function_type = type.is_raw_function
+        if really_a_function_type:
+            type = type.as_function_pointer()
+        btype = self._get_cached_btype(type)
+        result = btype, really_a_function_type
+        self._parsed_types[key] = result
+        return result
+
+    def _typeof(self, cdecl, consider_function_as_funcptr=False):
+        # string -> ctype object
+        try:
+            result = self._parsed_types[cdecl]
+        except KeyError:
+            with self._lock:
+                result = self._typeof_locked(cdecl)
+        #
+        btype, really_a_function_type = result
+        if really_a_function_type and not consider_function_as_funcptr:
+            raise CDefError("the type %r is a function type, not a "
+                            "pointer-to-function type" % (cdecl,))
+        return btype
+
+    def typeof(self, cdecl):
+        """Parse the C type given as a string and return the
+        corresponding <ctype> object.
+        It can also be used on 'cdata' instance to get its C type.
+        """
+        if isinstance(cdecl, basestring):
+            return self._typeof(cdecl)
+        if isinstance(cdecl, self.CData):
+            return self._backend.typeof(cdecl)
+        if isinstance(cdecl, types.BuiltinFunctionType):
+            res = _builtin_function_type(cdecl)
+            if res is not None:
+                return res
+        if (isinstance(cdecl, types.FunctionType)
+                and hasattr(cdecl, '_cffi_base_type')):
+            with self._lock:
+                return self._get_cached_btype(cdecl._cffi_base_type)
+        raise TypeError(type(cdecl))
+
+    def sizeof(self, cdecl):
+        """Return the size in bytes of the argument.  It can be a
+        string naming a C type, or a 'cdata' instance.
+        """
+        if isinstance(cdecl, basestring):
+            BType = self._typeof(cdecl)
+            return self._backend.sizeof(BType)
+        else:
+            return self._backend.sizeof(cdecl)
+
+    def alignof(self, cdecl):
+        """Return the natural alignment size in bytes of the C type
+        given as a string.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._backend.alignof(cdecl)
+
+    def offsetof(self, cdecl, *fields_or_indexes):
+        """Return the offset of the named field inside the given
+        structure or array, which must be given as a C type name.
+        You can give several field names in case of nested structures.
+        You can also give numeric values which correspond to array
+        items, in case of an array type.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._typeoffsetof(cdecl, *fields_or_indexes)[1]
+
+    def new(self, cdecl, init=None):
+        """Allocate an instance according to the specified C type and
+        return a pointer to it.  The specified C type must be either a
+        pointer or an array: ``new('X *')`` allocates an X and returns
+        a pointer to it, whereas ``new('X[n]')`` allocates an array of
+        n X'es and returns an array referencing it (which works
+        mostly like a pointer, like in C).  You can also use
+        ``new('X[]', n)`` to allocate an array of a non-constant
+        length n.
+
+        The memory is initialized following the rules of declaring a
+        global variable in C: by default it is zero-initialized, but
+        an explicit initializer can be given which can be used to
+        fill all or part of the memory.
+
+        When the returned <cdata> object goes out of scope, the memory
+        is freed.  In other words the returned <cdata> object has
+        ownership of the value of type 'cdecl' that it points to.  This
+        means that the raw data can be used as long as this object is
+        kept alive, but must not be used for a longer time.  Be careful
+        about that when copying the pointer to the memory somewhere
+        else, e.g. into another structure.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._backend.newp(cdecl, init)
+
+    def new_allocator(self, alloc=None, free=None,
+                      should_clear_after_alloc=True):
+        """Return a new allocator, i.e. a function that behaves like ffi.new()
+        but uses the provided low-level 'alloc' and 'free' functions.
+
+        'alloc' is called with the size as argument.  If it returns NULL, a
+        MemoryError is raised.  'free' is called with the result of 'alloc'
+        as argument.  Both can be either Python function or directly C
+        functions.  If 'free' is None, then no free function is called.
+        If both 'alloc' and 'free' are None, the default is used.
+
+        If 'should_clear_after_alloc' is set to False, then the memory
+        returned by 'alloc' is assumed to be already cleared (or you are
+        fine with garbage); otherwise CFFI will clear it.
+        """
+        compiled_ffi = self._backend.FFI()
+        allocator = compiled_ffi.new_allocator(alloc, free,
+                                               should_clear_after_alloc)
+        def allocate(cdecl, init=None):
+            if isinstance(cdecl, basestring):
+                cdecl = self._typeof(cdecl)
+            return allocator(cdecl, init)
+        return allocate
+
+    def cast(self, cdecl, source):
+        """Similar to a C cast: returns an instance of the named C
+        type initialized with the given 'source'.  The source is
+        casted between integers or pointers of any type.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._backend.cast(cdecl, source)
+
+    def string(self, cdata, maxlen=-1):
+        """Return a Python string (or unicode string) from the 'cdata'.
+        If 'cdata' is a pointer or array of characters or bytes, returns
+        the null-terminated string.  The returned string extends until
+        the first null character, or at most 'maxlen' characters.  If
+        'cdata' is an array then 'maxlen' defaults to its length.
+
+        If 'cdata' is a pointer or array of wchar_t, returns a unicode
+        string following the same rules.
+
+        If 'cdata' is a single character or byte or a wchar_t, returns
+        it as a string or unicode string.
+
+        If 'cdata' is an enum, returns the value of the enumerator as a
+        string, or 'NUMBER' if the value is out of range.
+        """
+        return self._backend.string(cdata, maxlen)
+
+    def unpack(self, cdata, length):
+        """Unpack an array of C data of the given length,
+        returning a Python string/unicode/list.
+
+        If 'cdata' is a pointer to 'char', returns a byte string.
+        It does not stop at the first null.  This is equivalent to:
+        ffi.buffer(cdata, length)[:]
+
+        If 'cdata' is a pointer to 'wchar_t', returns a unicode string.
+        'length' is measured in wchar_t's; it is not the size in bytes.
+
+        If 'cdata' is a pointer to anything else, returns a list of
+        'length' items.  This is a faster equivalent to:
+        [cdata[i] for i in range(length)]
+        """
+        return self._backend.unpack(cdata, length)
+
+   #def buffer(self, cdata, size=-1):
+   #    """Return a read-write buffer object that references the raw C data
+   #    pointed to by the given 'cdata'.  The 'cdata' must be a pointer or
+   #    an array.  Can be passed to functions expecting a buffer, or directly
+   #    manipulated with:
+   #
+   #        buf[:]          get a copy of it in a regular string, or
+   #        buf[idx]        as a single character
+   #        buf[:] = ...
+   #        buf[idx] = ...  change the content
+   #    """
+   #    note that 'buffer' is a type, set on this instance by __init__
+
+    def from_buffer(self, cdecl, python_buffer=_unspecified,
+                    require_writable=False):
+        """Return a cdata of the given type pointing to the data of the
+        given Python object, which must support the buffer interface.
+        Note that this is not meant to be used on the built-in types
+        str or unicode (you can build 'char[]' arrays explicitly)
+        but only on objects containing large quantities of raw data
+        in some other format, like 'array.array' or numpy arrays.
+
+        The first argument is optional and default to 'char[]'.
+        """
+        if python_buffer is _unspecified:
+            cdecl, python_buffer = self.BCharA, cdecl
+        elif isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        return self._backend.from_buffer(cdecl, python_buffer,
+                                         require_writable)
+
+    def memmove(self, dest, src, n):
+        """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.
+
+        Like the C function memmove(), the memory areas may overlap;
+        apart from that it behaves like the C function memcpy().
+
+        'src' can be any cdata ptr or array, or any Python buffer object.
+        'dest' can be any cdata ptr or array, or a writable Python buffer
+        object.  The size to copy, 'n', is always measured in bytes.
+
+        Unlike other methods, this one supports all Python buffer including
+        byte strings and bytearrays---but it still does not support
+        non-contiguous buffers.
+        """
+        return self._backend.memmove(dest, src, n)
+
+    def callback(self, cdecl, python_callable=None, error=None, onerror=None):
+        """Return a callback object or a decorator making such a
+        callback object.  'cdecl' must name a C function pointer type.
+        The callback invokes the specified 'python_callable' (which may
+        be provided either directly or via a decorator).  Important: the
+        callback object must be manually kept alive for as long as the
+        callback may be invoked from the C level.
+        """
+        def callback_decorator_wrap(python_callable):
+            if not callable(python_callable):
+                raise TypeError("the 'python_callable' argument "
+                                "is not callable")
+            return self._backend.callback(cdecl, python_callable,
+                                          error, onerror)
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl, consider_function_as_funcptr=True)
+        if python_callable is None:
+            return callback_decorator_wrap                # decorator mode
+        else:
+            return callback_decorator_wrap(python_callable)  # direct mode
+
+    def getctype(self, cdecl, replace_with=''):
+        """Return a string giving the C type 'cdecl', which may be itself
+        a string or a <ctype> object.  If 'replace_with' is given, it gives
+        extra text to append (or insert for more complicated C types), like
+        a variable name, or '*' to get actually the C type 'pointer-to-cdecl'.
+        """
+        if isinstance(cdecl, basestring):
+            cdecl = self._typeof(cdecl)
+        replace_with = replace_with.strip()
+        if (replace_with.startswith('*')
+                and '&[' in self._backend.getcname(cdecl, '&')):
+            replace_with = '(%s)' % replace_with
+        elif replace_with and not replace_with[0] in '[(':
+            replace_with = ' ' + replace_with
+        return self._backend.getcname(cdecl, replace_with)
+
+    def gc(self, cdata, destructor, size=0):
+        """Return a new cdata object that points to the same
+        data.  Later, when this new cdata object is garbage-collected,
+        'destructor(old_cdata_object)' will be called.
+
+        The optional 'size' gives an estimate of the size, used to
+        trigger the garbage collection more eagerly.  So far only used
+        on PyPy.  It tells the GC that the returned object keeps alive
+        roughly 'size' bytes of external memory.
+        """
+        return self._backend.gcp(cdata, destructor, size)
+
+    def _get_cached_btype(self, type):
+        assert self._lock.acquire(False) is False
+        # call me with the lock!
+        try:
+            BType = self._cached_btypes[type]
+        except KeyError:
+            finishlist = []
+            BType = type.get_cached_btype(self, finishlist)
+            for type in finishlist:
+                type.finish_backend_type(self, finishlist)
+        return BType
+
+    def verify(self, source='', tmpdir=None, **kwargs):
+        """Verify that the current ffi signatures compile on this
+        machine, and return a dynamic library object.  The dynamic
+        library can be used to call functions and access global
+        variables declared in this 'ffi'.  The library is compiled
+        by the C compiler: it gives you C-level API compatibility
+        (including calling macros).  This is unlike 'ffi.dlopen()',
+        which requires binary compatibility in the signatures.
+        """
+        from .verifier import Verifier, _caller_dir_pycache
+        #
+        # If set_unicode(True) was called, insert the UNICODE and
+        # _UNICODE macro declarations
+        if self._windows_unicode:
+            self._apply_windows_unicode(kwargs)
+        #
+        # Set the tmpdir here, and not in Verifier.__init__: it picks
+        # up the caller's directory, which we want to be the caller of
+        # ffi.verify(), as opposed to the caller of Veritier().
+        tmpdir = tmpdir or _caller_dir_pycache()
+        #
+        # Make a Verifier() and use it to load the library.
+        self.verifier = Verifier(self, source, tmpdir, **kwargs)
+        lib = self.verifier.load_library()
+        #
+        # Save the loaded library for keep-alive purposes, even
+        # if the caller doesn't keep it alive itself (it should).
+        self._libraries.append(lib)
+        return lib
+
+    def _get_errno(self):
+        return self._backend.get_errno()
+    def _set_errno(self, errno):
+        self._backend.set_errno(errno)
+    errno = property(_get_errno, _set_errno, None,
+                     "the value of 'errno' from/to the C calls")
+
+    def getwinerror(self, code=-1):
+        return self._backend.getwinerror(code)
+
+    def _pointer_to(self, ctype):
+        with self._lock:
+            return model.pointer_cache(self, ctype)
+
+    def addressof(self, cdata, *fields_or_indexes):
+        """Return the address of a <cdata 'struct-or-union'>.
+        If 'fields_or_indexes' are given, returns the address of that
+        field or array item in the structure or array, recursively in
+        case of nested structures.
+        """
+        try:
+            ctype = self._backend.typeof(cdata)
+        except TypeError:
+            if '__addressof__' in type(cdata).__dict__:
+                return type(cdata).__addressof__(cdata, *fields_or_indexes)
+            raise
+        if fields_or_indexes:
+            ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes)
+        else:
+            if ctype.kind == "pointer":
+                raise TypeError("addressof(pointer)")
+            offset = 0
+        ctypeptr = self._pointer_to(ctype)
+        return self._backend.rawaddressof(ctypeptr, cdata, offset)
+
+    def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes):
+        ctype, offset = self._backend.typeoffsetof(ctype, field_or_index)
+        for field1 in fields_or_indexes:
+            ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1)
+            offset += offset1
+        return ctype, offset
+
+    def include(self, ffi_to_include):
+        """Includes the typedefs, structs, unions and enums defined
+        in another FFI instance.  Usage is similar to a #include in C,
+        where a part of the program might include types defined in
+        another part for its own usage.  Note that the include()
+        method has no effect on functions, constants and global
+        variables, which must anyway be accessed directly from the
+        lib object returned by the original FFI instance.
+        """
+        if not isinstance(ffi_to_include, FFI):
+            raise TypeError("ffi.include() expects an argument that is also of"
+                            " type cffi.FFI, not %r" % (
+                                type(ffi_to_include).__name__,))
+        if ffi_to_include is self:
+            raise ValueError("self.include(self)")
+        with ffi_to_include._lock:
+            with self._lock:
+                self._parser.include(ffi_to_include._parser)
+                self._cdefsources.append('[')
+                self._cdefsources.extend(ffi_to_include._cdefsources)
+                self._cdefsources.append(']')
+                self._included_ffis.append(ffi_to_include)
+
+    def new_handle(self, x):
+        return self._backend.newp_handle(self.BVoidP, x)
+
+    def from_handle(self, x):
+        return self._backend.from_handle(x)
+
+    def release(self, x):
+        self._backend.release(x)
+
+    def set_unicode(self, enabled_flag):
+        """Windows: if 'enabled_flag' is True, enable the UNICODE and
+        _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR
+        to be (pointers to) wchar_t.  If 'enabled_flag' is False,
+        declare these types to be (pointers to) plain 8-bit characters.
+        This is mostly for backward compatibility; you usually want True.
+        """
+        if self._windows_unicode is not None:
+            raise ValueError("set_unicode() can only be called once")
+        enabled_flag = bool(enabled_flag)
+        if enabled_flag:
+            self.cdef("typedef wchar_t TBYTE;"
+                      "typedef wchar_t TCHAR;"
+                      "typedef const wchar_t *LPCTSTR;"
+                      "typedef const wchar_t *PCTSTR;"
+                      "typedef wchar_t *LPTSTR;"
+                      "typedef wchar_t *PTSTR;"
+                      "typedef TBYTE *PTBYTE;"
+                      "typedef TCHAR *PTCHAR;")
+        else:
+            self.cdef("typedef char TBYTE;"
+                      "typedef char TCHAR;"
+                      "typedef const char *LPCTSTR;"
+                      "typedef const char *PCTSTR;"
+                      "typedef char *LPTSTR;"
+                      "typedef char *PTSTR;"
+                      "typedef TBYTE *PTBYTE;"
+                      "typedef TCHAR *PTCHAR;")
+        self._windows_unicode = enabled_flag
+
+    def _apply_windows_unicode(self, kwds):
+        defmacros = kwds.get('define_macros', ())
+        if not isinstance(defmacros, (list, tuple)):
+            raise TypeError("'define_macros' must be a list or tuple")
+        defmacros = list(defmacros) + [('UNICODE', '1'),
+                                       ('_UNICODE', '1')]
+        kwds['define_macros'] = defmacros
+
+    def _apply_embedding_fix(self, kwds):
+        # must include an argument like "-lpython2.7" for the compiler
+        def ensure(key, value):
+            lst = kwds.setdefault(key, [])
+            if value not in lst:
+                lst.append(value)
+        #
+        if '__pypy__' in sys.builtin_module_names:
+            import os
+            if sys.platform == "win32":
+                # we need 'libpypy-c.lib'.  Current distributions of
+                # pypy (>= 4.1) contain it as 'libs/python27.lib'.
+                pythonlib = "python{0[0]}{0[1]}".format(sys.version_info)
+                if hasattr(sys, 'prefix'):
+                    ensure('library_dirs', os.path.join(sys.prefix, 'libs'))
+            else:
+                # we need 'libpypy-c.{so,dylib}', which should be by
+                # default located in 'sys.prefix/bin' for installed
+                # systems.
+                if sys.version_info < (3,):
+                    pythonlib = "pypy-c"
+                else:
+                    pythonlib = "pypy3-c"
+                if hasattr(sys, 'prefix'):
+                    ensure('library_dirs', os.path.join(sys.prefix, 'bin'))
+            # On uninstalled pypy's, the libpypy-c is typically found in
+            # .../pypy/goal/.
+            if hasattr(sys, 'prefix'):
+                ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal'))
+        else:
+            if sys.platform == "win32":
+                template = "python%d%d"
+                if hasattr(sys, 'gettotalrefcount'):
+                    template += '_d'
+            else:
+                try:
+                    import sysconfig
+                except ImportError:    # 2.6
+                    from distutils import sysconfig
+                template = "python%d.%d"
+                if sysconfig.get_config_var('DEBUG_EXT'):
+                    template += sysconfig.get_config_var('DEBUG_EXT')
+            pythonlib = (template %
+                    (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
+            if hasattr(sys, 'abiflags'):
+                pythonlib += sys.abiflags
+        ensure('libraries', pythonlib)
+        if sys.platform == "win32":
+            ensure('extra_link_args', '/MANIFEST')
+
+    def set_source(self, module_name, source, source_extension='.c', **kwds):
+        import os
+        if hasattr(self, '_assigned_source'):
+            raise ValueError("set_source() cannot be called several times "
+                             "per ffi object")
+        if not isinstance(module_name, basestring):
+            raise TypeError("'module_name' must be a string")
+        if os.sep in module_name or (os.altsep and os.altsep in module_name):
+            raise ValueError("'module_name' must not contain '/': use a dotted "
+                             "name to make a 'package.module' location")
+        self._assigned_source = (str(module_name), source,
+                                 source_extension, kwds)
+
+    def set_source_pkgconfig(self, module_name, pkgconfig_libs, source,
+                             source_extension='.c', **kwds):
+        from . import pkgconfig
+        if not isinstance(pkgconfig_libs, list):
+            raise TypeError("the pkgconfig_libs argument must be a list "
+                            "of package names")
+        kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs)
+        pkgconfig.merge_flags(kwds, kwds2)
+        self.set_source(module_name, source, source_extension, **kwds)
+
+    def distutils_extension(self, tmpdir='build', verbose=True):
+        from distutils.dir_util import mkpath
+        from .recompiler import recompile
+        #
+        if not hasattr(self, '_assigned_source'):
+            if hasattr(self, 'verifier'):     # fallback, 'tmpdir' ignored
+                return self.verifier.get_extension()
+            raise ValueError("set_source() must be called before"
+                             " distutils_extension()")
+        module_name, source, source_extension, kwds = self._assigned_source
+        if source is None:
+            raise TypeError("distutils_extension() is only for C extension "
+                            "modules, not for dlopen()-style pure Python "
+                            "modules")
+        mkpath(tmpdir)
+        ext, updated = recompile(self, module_name,
+                                 source, tmpdir=tmpdir, extradir=tmpdir,
+                                 source_extension=source_extension,
+                                 call_c_compiler=False, **kwds)
+        if verbose:
+            if updated:
+                sys.stderr.write("regenerated: %r\n" % (ext.sources[0],))
+            else:
+                sys.stderr.write("not modified: %r\n" % (ext.sources[0],))
+        return ext
+
+    def emit_c_code(self, filename):
+        from .recompiler import recompile
+        #
+        if not hasattr(self, '_assigned_source'):
+            raise ValueError("set_source() must be called before emit_c_code()")
+        module_name, source, source_extension, kwds = self._assigned_source
+        if source is None:
+            raise TypeError("emit_c_code() is only for C extension modules, "
+                            "not for dlopen()-style pure Python modules")
+        recompile(self, module_name, source,
+                  c_file=filename, call_c_compiler=False, **kwds)
+
+    def emit_python_code(self, filename):
+        from .recompiler import recompile
+        #
+        if not hasattr(self, '_assigned_source'):
+            raise ValueError("set_source() must be called before emit_c_code()")
+        module_name, source, source_extension, kwds = self._assigned_source
+        if source is not None:
+            raise TypeError("emit_python_code() is only for dlopen()-style "
+                            "pure Python modules, not for C extension modules")
+        recompile(self, module_name, source,
+                  c_file=filename, call_c_compiler=False, **kwds)
+
+    def compile(self, tmpdir='.', verbose=0, target=None, debug=None):
+        """The 'target' argument gives the final file name of the
+        compiled DLL.  Use '*' to force distutils' choice, suitable for
+        regular CPython C API modules.  Use a file name ending in '.*'
+        to ask for the system's default extension for dynamic libraries
+        (.so/.dll/.dylib).
+
+        The default is '*' when building a non-embedded C API extension,
+        and (module_name + '.*') when building an embedded library.
+        """
+        from .recompiler import recompile
+        #
+        if not hasattr(self, '_assigned_source'):
+            raise ValueError("set_source() must be called before compile()")
+        module_name, source, source_extension, kwds = self._assigned_source
+        return recompile(self, module_name, source, tmpdir=tmpdir,
+                         target=target, source_extension=source_extension,
+                         compiler_verbose=verbose, debug=debug, **kwds)
+
+    def init_once(self, func, tag):
+        # Read _init_once_cache[tag], which is either (False, lock) if
+        # we're calling the function now in some thread, or (True, result).
+        # Don't call setdefault() in most cases, to avoid allocating and
+        # immediately freeing a lock; but still use setdefaut() to avoid
+        # races.
+        try:
+            x = self._init_once_cache[tag]
+        except KeyError:
+            x = self._init_once_cache.setdefault(tag, (False, allocate_lock()))
+        # Common case: we got (True, result), so we return the result.
+        if x[0]:
+            return x[1]
+        # Else, it's a lock.  Acquire it to serialize the following tests.
+        with x[1]:
+            # Read again from _init_once_cache the current status.
+            x = self._init_once_cache[tag]
+            if x[0]:
+                return x[1]
+            # Call the function and store the result back.
+            result = func()
+            self._init_once_cache[tag] = (True, result)
+        return result
+
+    def embedding_init_code(self, pysource):
+        if self._embedding:
+            raise ValueError("embedding_init_code() can only be called once")
+        # fix 'pysource' before it gets dumped into the C file:
+        # - remove empty lines at the beginning, so it starts at "line 1"
+        # - dedent, if all non-empty lines are indented
+        # - check for SyntaxErrors
+        import re
+        match = re.match(r'\s*\n', pysource)
+        if match:
+            pysource = pysource[match.end():]
+        lines = pysource.splitlines() or ['']
+        prefix = re.match(r'\s*', lines[0]).group()
+        for i in range(1, len(lines)):
+            line = lines[i]
+            if line.rstrip():
+                while not line.startswith(prefix):
+                    prefix = prefix[:-1]
+        i = len(prefix)
+        lines = [line[i:]+'\n' for line in lines]
+        pysource = ''.join(lines)
+        #
+        compile(pysource, "cffi_init", "exec")
+        #
+        self._embedding = pysource
+
+    def def_extern(self, *args, **kwds):
+        raise ValueError("ffi.def_extern() is only available on API-mode FFI "
+                         "objects")
+
+    def list_types(self):
+        """Returns the user type names known to this FFI instance.
+        This returns a tuple containing three lists of names:
+        (typedef_names, names_of_structs, names_of_unions)
+        """
+        typedefs = []
+        structs = []
+        unions = []
+        for key in self._parser._declarations:
+            if key.startswith('typedef '):
+                typedefs.append(key[8:])
+            elif key.startswith('struct '):
+                structs.append(key[7:])
+            elif key.startswith('union '):
+                unions.append(key[6:])
+        typedefs.sort()
+        structs.sort()
+        unions.sort()
+        return (typedefs, structs, unions)
+
+
+def _load_backend_lib(backend, name, flags):
+    import os
+    if name is None:
+        if sys.platform != "win32":
+            return backend.load_library(None, flags)
+        name = "c"    # Windows: load_library(None) fails, but this works
+                      # on Python 2 (backward compatibility hack only)
+    first_error = None
+    if '.' in name or '/' in name or os.sep in name:
+        try:
+            return backend.load_library(name, flags)
+        except OSError as e:
+            first_error = e
+    import ctypes.util
+    path = ctypes.util.find_library(name)
+    if path is None:
+        if name == "c" and sys.platform == "win32" and sys.version_info >= (3,):
+            raise OSError("dlopen(None) cannot work on Windows for Python 3 "
+                          "(see http://bugs.python.org/issue23606)")
+        msg = ("ctypes.util.find_library() did not manage "
+               "to locate a library called %r" % (name,))
+        if first_error is not None:
+            msg = "%s.  Additionally, %s" % (first_error, msg)
+        raise OSError(msg)
+    return backend.load_library(path, flags)
+
+def _make_ffi_library(ffi, libname, flags):
+    backend = ffi._backend
+    backendlib = _load_backend_lib(backend, libname, flags)
+    #
+    def accessor_function(name):
+        key = 'function ' + name
+        tp, _ = ffi._parser._declarations[key]
+        BType = ffi._get_cached_btype(tp)
+        value = backendlib.load_function(BType, name)
+        library.__dict__[name] = value
+    #
+    def accessor_variable(name):
+        key = 'variable ' + name
+        tp, _ = ffi._parser._declarations[key]
+        BType = ffi._get_cached_btype(tp)
+        read_variable = backendlib.read_variable
+        write_variable = backendlib.write_variable
+        setattr(FFILibrary, name, property(
+            lambda self: read_variable(BType, name),
+            lambda self, value: write_variable(BType, name, value)))
+    #
+    def addressof_var(name):
+        try:
+            return addr_variables[name]
+        except KeyError:
+            with ffi._lock:
+                if name not in addr_variables:
+                    key = 'variable ' + name
+                    tp, _ = ffi._parser._declarations[key]
+                    BType = ffi._get_cached_btype(tp)
+                    if BType.kind != 'array':
+                        BType = model.pointer_cache(ffi, BType)
+                    p = backendlib.load_function(BType, name)
+                    addr_variables[name] = p
+            return addr_variables[name]
+    #
+    def accessor_constant(name):
+        raise NotImplementedError("non-integer constant '%s' cannot be "
+                                  "accessed from a dlopen() library" % (name,))
+    #
+    def accessor_int_constant(name):
+        library.__dict__[name] = ffi._parser._int_constants[name]
+    #
+    accessors = {}
+    accessors_version = [False]
+    addr_variables = {}
+    #
+    def update_accessors():
+        if accessors_version[0] is ffi._cdef_version:
+            return
+        #
+        for key, (tp, _) in ffi._parser._declarations.items():
+            if not isinstance(tp, model.EnumType):
+                tag, name = key.split(' ', 1)
+                if tag == 'function':
+                    accessors[name] = accessor_function
+                elif tag == 'variable':
+                    accessors[name] = accessor_variable
+                elif tag == 'constant':
+                    accessors[name] = accessor_constant
+            else:
+                for i, enumname in enumerate(tp.enumerators):
+                    def accessor_enum(name, tp=tp, i=i):
+                        tp.check_not_partial()
+                        library.__dict__[name] = tp.enumvalues[i]
+                    accessors[enumname] = accessor_enum
+        for name in ffi._parser._int_constants:
+            accessors.setdefault(name, accessor_int_constant)
+        accessors_version[0] = ffi._cdef_version
+    #
+    def make_accessor(name):
+        with ffi._lock:
+            if name in library.__dict__ or name in FFILibrary.__dict__:
+                return    # added by another thread while waiting for the lock
+            if name not in accessors:
+                update_accessors()
+                if name not in accessors:
+                    raise AttributeError(name)
+            accessors[name](name)
+    #
+    class FFILibrary(object):
+        def __getattr__(self, name):
+            make_accessor(name)
+            return getattr(self, name)
+        def __setattr__(self, name, value):
+            try:
+                property = getattr(self.__class__, name)
+            except AttributeError:
+                make_accessor(name)
+                setattr(self, name, value)
+            else:
+                property.__set__(self, value)
+        def __dir__(self):
+            with ffi._lock:
+                update_accessors()
+                return accessors.keys()
+        def __addressof__(self, name):
+            if name in library.__dict__:
+                return library.__dict__[name]
+            if name in FFILibrary.__dict__:
+                return addressof_var(name)
+            make_accessor(name)
+            if name in library.__dict__:
+                return library.__dict__[name]
+            if name in FFILibrary.__dict__:
+                return addressof_var(name)
+            raise AttributeError("cffi library has no function or "
+                                 "global variable named '%s'" % (name,))
+        def __cffi_close__(self):
+            backendlib.close_lib()
+            self.__dict__.clear()
+    #
+    if libname is not None:
+        try:
+            if not isinstance(libname, str):    # unicode, on Python 2
+                libname = libname.encode('utf-8')
+            FFILibrary.__name__ = 'FFILibrary_%s' % libname
+        except UnicodeError:
+            pass
+    library = FFILibrary()
+    return library, library.__dict__
+
+def _builtin_function_type(func):
+    # a hack to make at least ffi.typeof(builtin_function) work,
+    # if the builtin function was obtained by 'vengine_cpy'.
+    import sys
+    try:
+        module = sys.modules[func.__module__]
+        ffi = module._cffi_original_ffi
+        types_of_builtin_funcs = module._cffi_types_of_builtin_funcs
+        tp = types_of_builtin_funcs[func]
+    except (KeyError, AttributeError, TypeError):
+        return None
+    else:
+        with ffi._lock:
+            return ffi._get_cached_btype(tp)
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
new file mode 100644
index 0000000..679ae05
--- /dev/null
+++ b/cffi/backend_ctypes.py
@@ -0,0 +1,1121 @@
+import ctypes, ctypes.util, operator, sys
+from . import model
+
+if sys.version_info < (3,):
+    bytechr = chr
+else:
+    unicode = str
+    long = int
+    xrange = range
+    bytechr = lambda num: bytes([num])
+
+class CTypesType(type):
+    pass
+
+class CTypesData(object):
+    __metaclass__ = CTypesType
+    __slots__ = ['__weakref__']
+    __name__ = '<cdata>'
+
+    def __init__(self, *args):
+        raise TypeError("cannot instantiate %r" % (self.__class__,))
+
+    @classmethod
+    def _newp(cls, init):
+        raise TypeError("expected a pointer or array ctype, got '%s'"
+                        % (cls._get_c_name(),))
+
+    @staticmethod
+    def _to_ctypes(value):
+        raise TypeError
+
+    @classmethod
+    def _arg_to_ctypes(cls, *value):
+        try:
+            ctype = cls._ctype
+        except AttributeError:
+            raise TypeError("cannot create an instance of %r" % (cls,))
+        if value:
+            res = cls._to_ctypes(*value)
+            if not isinstance(res, ctype):
+                res = cls._ctype(res)
+        else:
+            res = cls._ctype()
+        return res
+
+    @classmethod
+    def _create_ctype_obj(cls, init):
+        if init is None:
+            return cls._arg_to_ctypes()
+        else:
+            return cls._arg_to_ctypes(init)
+
+    @staticmethod
+    def _from_ctypes(ctypes_value):
+        raise TypeError
+
+    @classmethod
+    def _get_c_name(cls, replace_with=''):
+        return cls._reftypename.replace(' &', replace_with)
+
+    @classmethod
+    def _fix_class(cls):
+        cls.__name__ = 'CData<%s>' % (cls._get_c_name(),)
+        cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),)
+        cls.__module__ = 'ffi'
+
+    def _get_own_repr(self):
+        raise NotImplementedError
+
+    def _addr_repr(self, address):
+        if address == 0:
+            return 'NULL'
+        else:
+            if address < 0:
+                address += 1 << (8*ctypes.sizeof(ctypes.c_void_p))
+            return '0x%x' % address
+
+    def __repr__(self, c_name=None):
+        own = self._get_own_repr()
+        return '<cdata %r %s>' % (c_name or self._get_c_name(), own)
+
+    def _convert_to_address(self, BClass):
+        if BClass is None:
+            raise TypeError("cannot convert %r to an address" % (
+                self._get_c_name(),))
+        else:
+            raise TypeError("cannot convert %r to %r" % (
+                self._get_c_name(), BClass._get_c_name()))
+
+    @classmethod
+    def _get_size(cls):
+        return ctypes.sizeof(cls._ctype)
+
+    def _get_size_of_instance(self):
+        return ctypes.sizeof(self._ctype)
+
+    @classmethod
+    def _cast_from(cls, source):
+        raise TypeError("cannot cast to %r" % (cls._get_c_name(),))
+
+    def _cast_to_integer(self):
+        return self._convert_to_address(None)
+
+    @classmethod
+    def _alignment(cls):
+        return ctypes.alignment(cls._ctype)
+
+    def __iter__(self):
+        raise TypeError("cdata %r does not support iteration" % (
+            self._get_c_name()),)
+
+    def _make_cmp(name):
+        cmpfunc = getattr(operator, name)
+        def cmp(self, other):
+            v_is_ptr = not isinstance(self, CTypesGenericPrimitive)
+            w_is_ptr = (isinstance(other, CTypesData) and
+                           not isinstance(other, CTypesGenericPrimitive))
+            if v_is_ptr and w_is_ptr:
+                return cmpfunc(self._convert_to_address(None),
+                               other._convert_to_address(None))
+            elif v_is_ptr or w_is_ptr:
+                return NotImplemented
+            else:
+                if isinstance(self, CTypesGenericPrimitive):
+                    self = self._value
+                if isinstance(other, CTypesGenericPrimitive):
+                    other = other._value
+                return cmpfunc(self, other)
+        cmp.func_name = name
+        return cmp
+
+    __eq__ = _make_cmp('__eq__')
+    __ne__ = _make_cmp('__ne__')
+    __lt__ = _make_cmp('__lt__')
+    __le__ = _make_cmp('__le__')
+    __gt__ = _make_cmp('__gt__')
+    __ge__ = _make_cmp('__ge__')
+
+    def __hash__(self):
+        return hash(self._convert_to_address(None))
+
+    def _to_string(self, maxlen):
+        raise TypeError("string(): %r" % (self,))
+
+
+class CTypesGenericPrimitive(CTypesData):
+    __slots__ = []
+
+    def __hash__(self):
+        return hash(self._value)
+
+    def _get_own_repr(self):
+        return repr(self._from_ctypes(self._value))
+
+
+class CTypesGenericArray(CTypesData):
+    __slots__ = []
+
+    @classmethod
+    def _newp(cls, init):
+        return cls(init)
+
+    def __iter__(self):
+        for i in xrange(len(self)):
+            yield self[i]
+
+    def _get_own_repr(self):
+        return self._addr_repr(ctypes.addressof(self._blob))
+
+
+class CTypesGenericPtr(CTypesData):
+    __slots__ = ['_address', '_as_ctype_ptr']
+    _automatic_casts = False
+    kind = "pointer"
+
+    @classmethod
+    def _newp(cls, init):
+        return cls(init)
+
+    @classmethod
+    def _cast_from(cls, source):
+        if source is None:
+            address = 0
+        elif isinstance(source, CTypesData):
+            address = source._cast_to_integer()
+        elif isinstance(source, (int, long)):
+            address = source
+        else:
+            raise TypeError("bad type for cast to %r: %r" %
+                            (cls, type(source).__name__))
+        return cls._new_pointer_at(address)
+
+    @classmethod
+    def _new_pointer_at(cls, address):
+        self = cls.__new__(cls)
+        self._address = address
+        self._as_ctype_ptr = ctypes.cast(address, cls._ctype)
+        return self
+
+    def _get_own_repr(self):
+        try:
+            return self._addr_repr(self._address)
+        except AttributeError:
+            return '???'
+
+    def _cast_to_integer(self):
+        return self._address
+
+    def __nonzero__(self):
+        return bool(self._address)
+    __bool__ = __nonzero__
+
+    @classmethod
+    def _to_ctypes(cls, value):
+        if not isinstance(value, CTypesData):
+            raise TypeError("unexpected %s object" % type(value).__name__)
+        address = value._convert_to_address(cls)
+        return ctypes.cast(address, cls._ctype)
+
+    @classmethod
+    def _from_ctypes(cls, ctypes_ptr):
+        address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0
+        return cls._new_pointer_at(address)
+
+    @classmethod
+    def _initialize(cls, ctypes_ptr, value):
+        if value:
+            ctypes_ptr.contents = cls._to_ctypes(value).contents
+
+    def _convert_to_address(self, BClass):
+        if (BClass in (self.__class__, None) or BClass._automatic_casts
+            or self._automatic_casts):
+            return self._address
+        else:
+            return CTypesData._convert_to_address(self, BClass)
+
+
+class CTypesBaseStructOrUnion(CTypesData):
+    __slots__ = ['_blob']
+
+    @classmethod
+    def _create_ctype_obj(cls, init):
+        # may be overridden
+        raise TypeError("cannot instantiate opaque type %s" % (cls,))
+
+    def _get_own_repr(self):
+        return self._addr_repr(ctypes.addressof(self._blob))
+
+    @classmethod
+    def _offsetof(cls, fieldname):
+        return getattr(cls._ctype, fieldname).offset
+
+    def _convert_to_address(self, BClass):
+        if getattr(BClass, '_BItem', None) is self.__class__:
+            return ctypes.addressof(self._blob)
+        else:
+            return CTypesData._convert_to_address(self, BClass)
+
+    @classmethod
+    def _from_ctypes(cls, ctypes_struct_or_union):
+        self = cls.__new__(cls)
+        self._blob = ctypes_struct_or_union
+        return self
+
+    @classmethod
+    def _to_ctypes(cls, value):
+        return value._blob
+
+    def __repr__(self, c_name=None):
+        return CTypesData.__repr__(self, c_name or self._get_c_name(' &'))
+
+
+class CTypesBackend(object):
+
+    PRIMITIVE_TYPES = {
+        'char': ctypes.c_char,
+        'short': ctypes.c_short,
+        'int': ctypes.c_int,
+        'long': ctypes.c_long,
+        'long long': ctypes.c_longlong,
+        'signed char': ctypes.c_byte,
+        'unsigned char': ctypes.c_ubyte,
+        'unsigned short': ctypes.c_ushort,
+        'unsigned int': ctypes.c_uint,
+        'unsigned long': ctypes.c_ulong,
+        'unsigned long long': ctypes.c_ulonglong,
+        'float': ctypes.c_float,
+        'double': ctypes.c_double,
+        '_Bool': ctypes.c_bool,
+        }
+
+    for _name in ['unsigned long long', 'unsigned long',
+                  'unsigned int', 'unsigned short', 'unsigned char']:
+        _size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
+        PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_void_p):
+            PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_size_t):
+            PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name]
+
+    for _name in ['long long', 'long', 'int', 'short', 'signed char']:
+        _size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
+        PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_void_p):
+            PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name]
+            PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_size_t):
+            PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name]
+
+
+    def __init__(self):
+        self.RTLD_LAZY = 0   # not supported anyway by ctypes
+        self.RTLD_NOW  = 0
+        self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL
+        self.RTLD_LOCAL = ctypes.RTLD_LOCAL
+
+    def set_ffi(self, ffi):
+        self.ffi = ffi
+
+    def _get_types(self):
+        return CTypesData, CTypesType
+
+    def load_library(self, path, flags=0):
+        cdll = ctypes.CDLL(path, flags)
+        return CTypesLibrary(self, cdll)
+
+    def new_void_type(self):
+        class CTypesVoid(CTypesData):
+            __slots__ = []
+            _reftypename = 'void &'
+            @staticmethod
+            def _from_ctypes(novalue):
+                return None
+            @staticmethod
+            def _to_ctypes(novalue):
+                if novalue is not None:
+                    raise TypeError("None expected, got %s object" %
+                                    (type(novalue).__name__,))
+                return None
+        CTypesVoid._fix_class()
+        return CTypesVoid
+
+    def new_primitive_type(self, name):
+        if name == 'wchar_t':
+            raise NotImplementedError(name)
+        ctype = self.PRIMITIVE_TYPES[name]
+        if name == 'char':
+            kind = 'char'
+        elif name in ('float', 'double'):
+            kind = 'float'
+        else:
+            if name in ('signed char', 'unsigned char'):
+                kind = 'byte'
+            elif name == '_Bool':
+                kind = 'bool'
+            else:
+                kind = 'int'
+            is_signed = (ctype(-1).value == -1)
+        #
+        def _cast_source_to_int(source):
+            if isinstance(source, (int, long, float)):
+                source = int(source)
+            elif isinstance(source, CTypesData):
+                source = source._cast_to_integer()
+            elif isinstance(source, bytes):
+                source = ord(source)
+            elif source is None:
+                source = 0
+            else:
+                raise TypeError("bad type for cast to %r: %r" %
+                                (CTypesPrimitive, type(source).__name__))
+            return source
+        #
+        kind1 = kind
+        class CTypesPrimitive(CTypesGenericPrimitive):
+            __slots__ = ['_value']
+            _ctype = ctype
+            _reftypename = '%s &' % name
+            kind = kind1
+
+            def __init__(self, value):
+                self._value = value
+
+            @staticmethod
+            def _create_ctype_obj(init):
+                if init is None:
+                    return ctype()
+                return ctype(CTypesPrimitive._to_ctypes(init))
+
+            if kind == 'int' or kind == 'byte':
+                @classmethod
+                def _cast_from(cls, source):
+                    source = _cast_source_to_int(source)
+                    source = ctype(source).value     # cast within range
+                    return cls(source)
+                def __int__(self):
+                    return self._value
+
+            if kind == 'bool':
+                @classmethod
+                def _cast_from(cls, source):
+                    if not isinstance(source, (int, long, float)):
+                        source = _cast_source_to_int(source)
+                    return cls(bool(source))
+                def __int__(self):
+                    return self._value
+
+            if kind == 'char':
+                @classmethod
+                def _cast_from(cls, source):
+                    source = _cast_source_to_int(source)
+                    source = bytechr(source & 0xFF)
+                    return cls(source)
+                def __int__(self):
+                    return ord(self._value)
+
+            if kind == 'float':
+                @classmethod
+                def _cast_from(cls, source):
+                    if isinstance(source, float):
+                        pass
+                    elif isinstance(source, CTypesGenericPrimitive):
+                        if hasattr(source, '__float__'):
+                            source = float(source)
+                        else:
+                            source = int(source)
+                    else:
+                        source = _cast_source_to_int(source)
+                    source = ctype(source).value     # fix precision
+                    return cls(source)
+                def __int__(self):
+                    return int(self._value)
+                def __float__(self):
+                    return self._value
+
+            _cast_to_integer = __int__
+
+            if kind == 'int' or kind == 'byte' or kind == 'bool':
+                @staticmethod
+                def _to_ctypes(x):
+                    if not isinstance(x, (int, long)):
+                        if isinstance(x, CTypesData):
+                            x = int(x)
+                        else:
+                            raise TypeError("integer expected, got %s" %
+                                            type(x).__name__)
+                    if ctype(x).value != x:
+                        if not is_signed and x < 0:
+                            raise OverflowError("%s: negative integer" % name)
+                        else:
+                            raise OverflowError("%s: integer out of bounds"
+                                                % name)
+                    return x
+
+            if kind == 'char':
+                @staticmethod
+                def _to_ctypes(x):
+                    if isinstance(x, bytes) and len(x) == 1:
+                        return x
+                    if isinstance(x, CTypesPrimitive):    # <CData <char>>
+                        return x._value
+                    raise TypeError("character expected, got %s" %
+                                    type(x).__name__)
+                def __nonzero__(self):
+                    return ord(self._value) != 0
+            else:
+                def __nonzero__(self):
+                    return self._value != 0
+            __bool__ = __nonzero__
+
+            if kind == 'float':
+                @staticmethod
+                def _to_ctypes(x):
+                    if not isinstance(x, (int, long, float, CTypesData)):
+                        raise TypeError("float expected, got %s" %
+                                        type(x).__name__)
+                    return ctype(x).value
+
+            @staticmethod
+            def _from_ctypes(value):
+                return getattr(value, 'value', value)
+
+            @staticmethod
+            def _initialize(blob, init):
+                blob.value = CTypesPrimitive._to_ctypes(init)
+
+            if kind == 'char':
+                def _to_string(self, maxlen):
+                    return self._value
+            if kind == 'byte':
+                def _to_string(self, maxlen):
+                    return chr(self._value & 0xff)
+        #
+        CTypesPrimitive._fix_class()
+        return CTypesPrimitive
+
+    def new_pointer_type(self, BItem):
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
+            kind = 'charp'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'bytep'
+        elif BItem is getbtype(model.void_type):
+            kind = 'voidp'
+        else:
+            kind = 'generic'
+        #
+        class CTypesPtr(CTypesGenericPtr):
+            __slots__ = ['_own']
+            if kind == 'charp':
+                __slots__ += ['__as_strbuf']
+            _BItem = BItem
+            if hasattr(BItem, '_ctype'):
+                _ctype = ctypes.POINTER(BItem._ctype)
+                _bitem_size = ctypes.sizeof(BItem._ctype)
+            else:
+                _ctype = ctypes.c_void_p
+            if issubclass(BItem, CTypesGenericArray):
+                _reftypename = BItem._get_c_name('(* &)')
+            else:
+                _reftypename = BItem._get_c_name(' * &')
+
+            def __init__(self, init):
+                ctypeobj = BItem._create_ctype_obj(init)
+                if kind == 'charp':
+                    self.__as_strbuf = ctypes.create_string_buffer(
+                        ctypeobj.value + b'\x00')
+                    self._as_ctype_ptr = ctypes.cast(
+                        self.__as_strbuf, self._ctype)
+                else:
+                    self._as_ctype_ptr = ctypes.pointer(ctypeobj)
+                self._address = ctypes.cast(self._as_ctype_ptr,
+                                            ctypes.c_void_p).value
+                self._own = True
+
+            def __add__(self, other):
+                if isinstance(other, (int, long)):
+                    return self._new_pointer_at(self._address +
+                                                other * self._bitem_size)
+                else:
+                    return NotImplemented
+
+            def __sub__(self, other):
+                if isinstance(other, (int, long)):
+                    return self._new_pointer_at(self._address -
+                                                other * self._bitem_size)
+                elif type(self) is type(other):
+                    return (self._address - other._address) // self._bitem_size
+                else:
+                    return NotImplemented
+
+            def __getitem__(self, index):
+                if getattr(self, '_own', False) and index != 0:
+                    raise IndexError
+                return BItem._from_ctypes(self._as_ctype_ptr[index])
+
+            def __setitem__(self, index, value):
+                self._as_ctype_ptr[index] = BItem._to_ctypes(value)
+
+            if kind == 'charp' or kind == 'voidp':
+                @classmethod
+                def _arg_to_ctypes(cls, *value):
+                    if value and isinstance(value[0], bytes):
+                        return ctypes.c_char_p(value[0])
+                    else:
+                        return super(CTypesPtr, cls)._arg_to_ctypes(*value)
+
+            if kind == 'charp' or kind == 'bytep':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = sys.maxsize
+                    p = ctypes.cast(self._as_ctype_ptr,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != b'\x00':
+                        n += 1
+                    return b''.join([p[i] for i in range(n)])
+
+            def _get_own_repr(self):
+                if getattr(self, '_own', False):
+                    return 'owning %d bytes' % (
+                        ctypes.sizeof(self._as_ctype_ptr.contents),)
+                return super(CTypesPtr, self)._get_own_repr()
+        #
+        if (BItem is self.ffi._get_cached_btype(model.void_type) or
+            BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))):
+            CTypesPtr._automatic_casts = True
+        #
+        CTypesPtr._fix_class()
+        return CTypesPtr
+
+    def new_array_type(self, CTypesPtr, length):
+        if length is None:
+            brackets = ' &[]'
+        else:
+            brackets = ' &[%d]' % length
+        BItem = CTypesPtr._BItem
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
+            kind = 'char'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'byte'
+        else:
+            kind = 'generic'
+        #
+        class CTypesArray(CTypesGenericArray):
+            __slots__ = ['_blob', '_own']
+            if length is not None:
+                _ctype = BItem._ctype * length
+            else:
+                __slots__.append('_ctype')
+            _reftypename = BItem._get_c_name(brackets)
+            _declared_length = length
+            _CTPtr = CTypesPtr
+
+            def __init__(self, init):
+                if length is None:
+                    if isinstance(init, (int, long)):
+                        len1 = init
+                        init = None
+                    elif kind == 'char' and isinstance(init, bytes):
+                        len1 = len(init) + 1    # extra null
+                    else:
+                        init = tuple(init)
+                        len1 = len(init)
+                    self._ctype = BItem._ctype * len1
+                self._blob = self._ctype()
+                self._own = True
+                if init is not None:
+                    self._initialize(self._blob, init)
+
+            @staticmethod
+            def _initialize(blob, init):
+                if isinstance(init, bytes):
+                    init = [init[i:i+1] for i in range(len(init))]
+                else:
+                    if isinstance(init, CTypesGenericArray):
+                        if (len(init) != len(blob) or
+                            not isinstance(init, CTypesArray)):
+                            raise TypeError("length/type mismatch: %s" % (init,))
+                    init = tuple(init)
+                if len(init) > len(blob):
+                    raise IndexError("too many initializers")
+                addr = ctypes.cast(blob, ctypes.c_void_p).value
+                PTR = ctypes.POINTER(BItem._ctype)
+                itemsize = ctypes.sizeof(BItem._ctype)
+                for i, value in enumerate(init):
+                    p = ctypes.cast(addr + i * itemsize, PTR)
+                    BItem._initialize(p.contents, value)
+
+            def __len__(self):
+                return len(self._blob)
+
+            def __getitem__(self, index):
+                if not (0 <= index < len(self._blob)):
+                    raise IndexError
+                return BItem._from_ctypes(self._blob[index])
+
+            def __setitem__(self, index, value):
+                if not (0 <= index < len(self._blob)):
+                    raise IndexError
+                self._blob[index] = BItem._to_ctypes(value)
+
+            if kind == 'char' or kind == 'byte':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = len(self._blob)
+                    p = ctypes.cast(self._blob,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != b'\x00':
+                        n += 1
+                    return b''.join([p[i] for i in range(n)])
+
+            def _get_own_repr(self):
+                if getattr(self, '_own', False):
+                    return 'owning %d bytes' % (ctypes.sizeof(self._blob),)
+                return super(CTypesArray, self)._get_own_repr()
+
+            def _convert_to_address(self, BClass):
+                if BClass in (CTypesPtr, None) or BClass._automatic_casts:
+                    return ctypes.addressof(self._blob)
+                else:
+                    return CTypesData._convert_to_address(self, BClass)
+
+            @staticmethod
+            def _from_ctypes(ctypes_array):
+                self = CTypesArray.__new__(CTypesArray)
+                self._blob = ctypes_array
+                return self
+
+            @staticmethod
+            def _arg_to_ctypes(value):
+                return CTypesPtr._arg_to_ctypes(value)
+
+            def __add__(self, other):
+                if isinstance(other, (int, long)):
+                    return CTypesPtr._new_pointer_at(
+                        ctypes.addressof(self._blob) +
+                        other * ctypes.sizeof(BItem._ctype))
+                else:
+                    return NotImplemented
+
+            @classmethod
+            def _cast_from(cls, source):
+                raise NotImplementedError("casting to %r" % (
+                    cls._get_c_name(),))
+        #
+        CTypesArray._fix_class()
+        return CTypesArray
+
+    def _new_struct_or_union(self, kind, name, base_ctypes_class):
+        #
+        class struct_or_union(base_ctypes_class):
+            pass
+        struct_or_union.__name__ = '%s_%s' % (kind, name)
+        kind1 = kind
+        #
+        class CTypesStructOrUnion(CTypesBaseStructOrUnion):
+            __slots__ = ['_blob']
+            _ctype = struct_or_union
+            _reftypename = '%s &' % (name,)
+            _kind = kind = kind1
+        #
+        CTypesStructOrUnion._fix_class()
+        return CTypesStructOrUnion
+
+    def new_struct_type(self, name):
+        return self._new_struct_or_union('struct', name, ctypes.Structure)
+
+    def new_union_type(self, name):
+        return self._new_struct_or_union('union', name, ctypes.Union)
+
+    def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp,
+                                 totalsize=-1, totalalignment=-1, sflags=0,
+                                 pack=0):
+        if totalsize >= 0 or totalalignment >= 0:
+            raise NotImplementedError("the ctypes backend of CFFI does not support "
+                                      "structures completed by verify(); please "
+                                      "compile and install the _cffi_backend module.")
+        struct_or_union = CTypesStructOrUnion._ctype
+        fnames = [fname for (fname, BField, bitsize) in fields]
+        btypes = [BField for (fname, BField, bitsize) in fields]
+        bitfields = [bitsize for (fname, BField, bitsize) in fields]
+        #
+        bfield_types = {}
+        cfields = []
+        for (fname, BField, bitsize) in fields:
+            if bitsize < 0:
+                cfields.append((fname, BField._ctype))
+                bfield_types[fname] = BField
+            else:
+                cfields.append((fname, BField._ctype, bitsize))
+                bfield_types[fname] = Ellipsis
+        if sflags & 8:
+            struct_or_union._pack_ = 1
+        elif pack:
+            struct_or_union._pack_ = pack
+        struct_or_union._fields_ = cfields
+        CTypesStructOrUnion._bfield_types = bfield_types
+        #
+        @staticmethod
+        def _create_ctype_obj(init):
+            result = struct_or_union()
+            if init is not None:
+                initialize(result, init)
+            return result
+        CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj
+        #
+        def initialize(blob, init):
+            if is_union:
+                if len(init) > 1:
+                    raise ValueError("union initializer: %d items given, but "
+                                    "only one supported (use a dict if needed)"
+                                     % (len(init),))
+            if not isinstance(init, dict):
+                if isinstance(init, (bytes, unicode)):
+                    raise TypeError("union initializer: got a str")
+                init = tuple(init)
+                if len(init) > len(fnames):
+                    raise ValueError("too many values for %s initializer" %
+                                     CTypesStructOrUnion._get_c_name())
+                init = dict(zip(fnames, init))
+            addr = ctypes.addressof(blob)
+            for fname, value in init.items():
+                BField, bitsize = name2fieldtype[fname]
+                assert bitsize < 0, \
+                       "not implemented: initializer with bit fields"
+                offset = CTypesStructOrUnion._offsetof(fname)
+                PTR = ctypes.POINTER(BField._ctype)
+                p = ctypes.cast(addr + offset, PTR)
+                BField._initialize(p.contents, value)
+        is_union = CTypesStructOrUnion._kind == 'union'
+        name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
+        #
+        for fname, BField, bitsize in fields:
+            if fname == '':
+                raise NotImplementedError("nested anonymous structs/unions")
+            if hasattr(CTypesStructOrUnion, fname):
+                raise ValueError("the field name %r conflicts in "
+                                 "the ctypes backend" % fname)
+            if bitsize < 0:
+                def getter(self, fname=fname, BField=BField,
+                           offset=CTypesStructOrUnion._offsetof(fname),
+                           PTR=ctypes.POINTER(BField._ctype)):
+                    addr = ctypes.addressof(self._blob)
+                    p = ctypes.cast(addr + offset, PTR)
+                    return BField._from_ctypes(p.contents)
+                def setter(self, value, fname=fname, BField=BField):
+                    setattr(self._blob, fname, BField._to_ctypes(value))
+                #
+                if issubclass(BField, CTypesGenericArray):
+                    setter = None
+                    if BField._declared_length == 0:
+                        def getter(self, fname=fname, BFieldPtr=BField._CTPtr,
+                                   offset=CTypesStructOrUnion._offsetof(fname),
+                                   PTR=ctypes.POINTER(BField._ctype)):
+                            addr = ctypes.addressof(self._blob)
+                            p = ctypes.cast(addr + offset, PTR)
+                            return BFieldPtr._from_ctypes(p)
+                #
+            else:
+                def getter(self, fname=fname, BField=BField):
+                    return BField._from_ctypes(getattr(self._blob, fname))
+                def setter(self, value, fname=fname, BField=BField):
+                    # xxx obscure workaround
+                    value = BField._to_ctypes(value)
+                    oldvalue = getattr(self._blob, fname)
+                    setattr(self._blob, fname, value)
+                    if value != getattr(self._blob, fname):
+                        setattr(self._blob, fname, oldvalue)
+                        raise OverflowError("value too large for bitfield")
+            setattr(CTypesStructOrUnion, fname, property(getter, setter))
+        #
+        CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp))
+        for fname in fnames:
+            if hasattr(CTypesPtr, fname):
+                raise ValueError("the field name %r conflicts in "
+                                 "the ctypes backend" % fname)
+            def getter(self, fname=fname):
+                return getattr(self[0], fname)
+            def setter(self, value, fname=fname):
+                setattr(self[0], fname, value)
+            setattr(CTypesPtr, fname, property(getter, setter))
+
+    def new_function_type(self, BArgs, BResult, has_varargs):
+        nameargs = [BArg._get_c_name() for BArg in BArgs]
+        if has_varargs:
+            nameargs.append('...')
+        nameargs = ', '.join(nameargs)
+        #
+        class CTypesFunctionPtr(CTypesGenericPtr):
+            __slots__ = ['_own_callback', '_name']
+            _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None),
+                                      *[BArg._ctype for BArg in BArgs],
+                                      use_errno=True)
+            _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,))
+
+            def __init__(self, init, error=None):
+                # create a callback to the Python callable init()
+                import traceback
+                assert not has_varargs, "varargs not supported for callbacks"
+                if getattr(BResult, '_ctype', None) is not None:
+                    error = BResult._from_ctypes(
+                        BResult._create_ctype_obj(error))
+                else:
+                    error = None
+                def callback(*args):
+                    args2 = []
+                    for arg, BArg in zip(args, BArgs):
+                        args2.append(BArg._from_ctypes(arg))
+                    try:
+                        res2 = init(*args2)
+                        res2 = BResult._to_ctypes(res2)
+                    except:
+                        traceback.print_exc()
+                        res2 = error
+                    if issubclass(BResult, CTypesGenericPtr):
+                        if res2:
+                            res2 = ctypes.cast(res2, ctypes.c_void_p).value
+                                # .value: http://bugs.python.org/issue1574593
+                        else:
+                            res2 = None
+                    #print repr(res2)
+                    return res2
+                if issubclass(BResult, CTypesGenericPtr):
+                    # The only pointers callbacks can return are void*s:
+                    # http://bugs.python.org/issue5710
+                    callback_ctype = ctypes.CFUNCTYPE(
+                        ctypes.c_void_p,
+                        *[BArg._ctype for BArg in BArgs],
+                        use_errno=True)
+                else:
+                    callback_ctype = CTypesFunctionPtr._ctype
+                self._as_ctype_ptr = callback_ctype(callback)
+                self._address = ctypes.cast(self._as_ctype_ptr,
+                                            ctypes.c_void_p).value
+                self._own_callback = init
+
+            @staticmethod
+            def _initialize(ctypes_ptr, value):
+                if value:
+                    raise NotImplementedError("ctypes backend: not supported: "
+                                          "initializers for function pointers")
+
+            def __repr__(self):
+                c_name = getattr(self, '_name', None)
+                if c_name:
+                    i = self._reftypename.index('(* &)')
+                    if self._reftypename[i-1] not in ' )*':
+                        c_name = ' ' + c_name
+                    c_name = self._reftypename.replace('(* &)', c_name)
+                return CTypesData.__repr__(self, c_name)
+
+            def _get_own_repr(self):
+                if getattr(self, '_own_callback', None) is not None:
+                    return 'calling %r' % (self._own_callback,)
+                return super(CTypesFunctionPtr, self)._get_own_repr()
+
+            def __call__(self, *args):
+                if has_varargs:
+                    assert len(args) >= len(BArgs)
+                    extraargs = args[len(BArgs):]
+                    args = args[:len(BArgs)]
+                else:
+                    assert len(args) == len(BArgs)
+                ctypes_args = []
+                for arg, BArg in zip(args, BArgs):
+                    ctypes_args.append(BArg._arg_to_ctypes(arg))
+                if has_varargs:
+                    for i, arg in enumerate(extraargs):
+                        if arg is None:
+                            ctypes_args.append(ctypes.c_void_p(0))  # NULL
+                            continue
+                        if not isinstance(arg, CTypesData):
+                            raise TypeError(
+                                "argument %d passed in the variadic part "
+                                "needs to be a cdata object (got %s)" %
+                                (1 + len(BArgs) + i, type(arg).__name__))
+                        ctypes_args.append(arg._arg_to_ctypes(arg))
+                result = self._as_ctype_ptr(*ctypes_args)
+                return BResult._from_ctypes(result)
+        #
+        CTypesFunctionPtr._fix_class()
+        return CTypesFunctionPtr
+
+    def new_enum_type(self, name, enumerators, enumvalues, CTypesInt):
+        assert isinstance(name, str)
+        reverse_mapping = dict(zip(reversed(enumvalues),
+                                   reversed(enumerators)))
+        #
+        class CTypesEnum(CTypesInt):
+            __slots__ = []
+            _reftypename = '%s &' % name
+
+            def _get_own_repr(self):
+                value = self._value
+                try:
+                    return '%d: %s' % (value, reverse_mapping[value])
+                except KeyError:
+                    return str(value)
+
+            def _to_string(self, maxlen):
+                value = self._value
+                try:
+                    return reverse_mapping[value]
+                except KeyError:
+                    return str(value)
+        #
+        CTypesEnum._fix_class()
+        return CTypesEnum
+
+    def get_errno(self):
+        return ctypes.get_errno()
+
+    def set_errno(self, value):
+        ctypes.set_errno(value)
+
+    def string(self, b, maxlen=-1):
+        return b._to_string(maxlen)
+
+    def buffer(self, bptr, size=-1):
+        raise NotImplementedError("buffer() with ctypes backend")
+
+    def sizeof(self, cdata_or_BType):
+        if isinstance(cdata_or_BType, CTypesData):
+            return cdata_or_BType._get_size_of_instance()
+        else:
+            assert issubclass(cdata_or_BType, CTypesData)
+            return cdata_or_BType._get_size()
+
+    def alignof(self, BType):
+        assert issubclass(BType, CTypesData)
+        return BType._alignment()
+
+    def newp(self, BType, source):
+        if not issubclass(BType, CTypesData):
+            raise TypeError
+        return BType._newp(source)
+
+    def cast(self, BType, source):
+        return BType._cast_from(source)
+
+    def callback(self, BType, source, error, onerror):
+        assert onerror is None   # XXX not implemented
+        return BType(source, error)
+
+    _weakref_cache_ref = None
+
+    def gcp(self, cdata, destructor, size=0):
+        if self._weakref_cache_ref is None:
+            import weakref
+            class MyRef(weakref.ref):
+                def __eq__(self, other):
+                    myref = self()
+                    return self is other or (
+                        myref is not None and myref is other())
+                def __ne__(self, other):
+                    return not (self == other)
+                def __hash__(self):
+                    try:
+                        return self._hash
+                    except AttributeError:
+                        self._hash = hash(self())
+                        return self._hash
+            self._weakref_cache_ref = {}, MyRef
+        weak_cache, MyRef = self._weakref_cache_ref
+
+        if destructor is None:
+            try:
+                del weak_cache[MyRef(cdata)]
+            except KeyError:
+                raise TypeError("Can remove destructor only on a object "
+                                "previously returned by ffi.gc()")
+            return None
+
+        def remove(k):
+            cdata, destructor = weak_cache.pop(k, (None, None))
+            if destructor is not None:
+                destructor(cdata)
+
+        new_cdata = self.cast(self.typeof(cdata), cdata)
+        assert new_cdata is not cdata
+        weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor)
+        return new_cdata
+
+    typeof = type
+
+    def getcname(self, BType, replace_with):
+        return BType._get_c_name(replace_with)
+
+    def typeoffsetof(self, BType, fieldname, num=0):
+        if isinstance(fieldname, str):
+            if num == 0 and issubclass(BType, CTypesGenericPtr):
+                BType = BType._BItem
+            if not issubclass(BType, CTypesBaseStructOrUnion):
+                raise TypeError("expected a struct or union ctype")
+            BField = BType._bfield_types[fieldname]
+            if BField is Ellipsis:
+                raise TypeError("not supported for bitfields")
+            return (BField, BType._offsetof(fieldname))
+        elif isinstance(fieldname, (int, long)):
+            if issubclass(BType, CTypesGenericArray):
+                BType = BType._CTPtr
+            if not issubclass(BType, CTypesGenericPtr):
+                raise TypeError("expected an array or ptr ctype")
+            BItem = BType._BItem
+            offset = BItem._get_size() * fieldname
+            if offset > sys.maxsize:
+                raise OverflowError
+            return (BItem, offset)
+        else:
+            raise TypeError(type(fieldname))
+
+    def rawaddressof(self, BTypePtr, cdata, offset=None):
+        if isinstance(cdata, CTypesBaseStructOrUnion):
+            ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata))
+        elif isinstance(cdata, CTypesGenericPtr):
+            if offset is None or not issubclass(type(cdata)._BItem,
+                                                CTypesBaseStructOrUnion):
+                raise TypeError("unexpected cdata type")
+            ptr = type(cdata)._to_ctypes(cdata)
+        elif isinstance(cdata, CTypesGenericArray):
+            ptr = type(cdata)._to_ctypes(cdata)
+        else:
+            raise TypeError("expected a <cdata 'struct-or-union'>")
+        if offset:
+            ptr = ctypes.cast(
+                ctypes.c_void_p(
+                    ctypes.cast(ptr, ctypes.c_void_p).value + offset),
+                type(ptr))
+        return BTypePtr._from_ctypes(ptr)
+
+
+class CTypesLibrary(object):
+
+    def __init__(self, backend, cdll):
+        self.backend = backend
+        self.cdll = cdll
+
+    def load_function(self, BType, name):
+        c_func = getattr(self.cdll, name)
+        funcobj = BType._from_ctypes(c_func)
+        funcobj._name = name
+        return funcobj
+
+    def read_variable(self, BType, name):
+        try:
+            ctypes_obj = BType._ctype.in_dll(self.cdll, name)
+        except AttributeError as e:
+            raise NotImplementedError(e)
+        return BType._from_ctypes(ctypes_obj)
+
+    def write_variable(self, BType, name, value):
+        new_ctypes_obj = BType._to_ctypes(value)
+        ctypes_obj = BType._ctype.in_dll(self.cdll, name)
+        ctypes.memmove(ctypes.addressof(ctypes_obj),
+                       ctypes.addressof(new_ctypes_obj),
+                       ctypes.sizeof(BType._ctype))
diff --git a/cffi/cffi_opcode.py b/cffi/cffi_opcode.py
new file mode 100644
index 0000000..a0df98d
--- /dev/null
+++ b/cffi/cffi_opcode.py
@@ -0,0 +1,187 @@
+from .error import VerificationError
+
+class CffiOp(object):
+    def __init__(self, op, arg):
+        self.op = op
+        self.arg = arg
+
+    def as_c_expr(self):
+        if self.op is None:
+            assert isinstance(self.arg, str)
+            return '(_cffi_opcode_t)(%s)' % (self.arg,)
+        classname = CLASS_NAME[self.op]
+        return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg)
+
+    def as_python_bytes(self):
+        if self.op is None and self.arg.isdigit():
+            value = int(self.arg)     # non-negative: '-' not in self.arg
+            if value >= 2**31:
+                raise OverflowError("cannot emit %r: limited to 2**31-1"
+                                    % (self.arg,))
+            return format_four_bytes(value)
+        if isinstance(self.arg, str):
+            raise VerificationError("cannot emit to Python: %r" % (self.arg,))
+        return format_four_bytes((self.arg << 8) | self.op)
+
+    def __str__(self):
+        classname = CLASS_NAME.get(self.op, self.op)
+        return '(%s %s)' % (classname, self.arg)
+
+def format_four_bytes(num):
+    return '\\x%02X\\x%02X\\x%02X\\x%02X' % (
+        (num >> 24) & 0xFF,
+        (num >> 16) & 0xFF,
+        (num >>  8) & 0xFF,
+        (num      ) & 0xFF)
+
+OP_PRIMITIVE       = 1
+OP_POINTER         = 3
+OP_ARRAY           = 5
+OP_OPEN_ARRAY      = 7
+OP_STRUCT_UNION    = 9
+OP_ENUM            = 11
+OP_FUNCTION        = 13
+OP_FUNCTION_END    = 15
+OP_NOOP            = 17
+OP_BITFIELD        = 19
+OP_TYPENAME        = 21
+OP_CPYTHON_BLTN_V  = 23   # varargs
+OP_CPYTHON_BLTN_N  = 25   # noargs
+OP_CPYTHON_BLTN_O  = 27   # O  (i.e. a single arg)
+OP_CONSTANT        = 29
+OP_CONSTANT_INT    = 31
+OP_GLOBAL_VAR      = 33
+OP_DLOPEN_FUNC     = 35
+OP_DLOPEN_CONST    = 37
+OP_GLOBAL_VAR_F    = 39
+OP_EXTERN_PYTHON   = 41
+
+PRIM_VOID          = 0
+PRIM_BOOL          = 1
+PRIM_CHAR          = 2
+PRIM_SCHAR         = 3
+PRIM_UCHAR         = 4
+PRIM_SHORT         = 5
+PRIM_USHORT        = 6
+PRIM_INT           = 7
+PRIM_UINT          = 8
+PRIM_LONG          = 9
+PRIM_ULONG         = 10
+PRIM_LONGLONG      = 11
+PRIM_ULONGLONG     = 12
+PRIM_FLOAT         = 13
+PRIM_DOUBLE        = 14
+PRIM_LONGDOUBLE    = 15
+
+PRIM_WCHAR         = 16
+PRIM_INT8          = 17
+PRIM_UINT8         = 18
+PRIM_INT16         = 19
+PRIM_UINT16        = 20
+PRIM_INT32         = 21
+PRIM_UINT32        = 22
+PRIM_INT64         = 23
+PRIM_UINT64        = 24
+PRIM_INTPTR        = 25
+PRIM_UINTPTR       = 26
+PRIM_PTRDIFF       = 27
+PRIM_SIZE          = 28
+PRIM_SSIZE         = 29
+PRIM_INT_LEAST8    = 30
+PRIM_UINT_LEAST8   = 31
+PRIM_INT_LEAST16   = 32
+PRIM_UINT_LEAST16  = 33
+PRIM_INT_LEAST32   = 34
+PRIM_UINT_LEAST32  = 35
+PRIM_INT_LEAST64   = 36
+PRIM_UINT_LEAST64  = 37
+PRIM_INT_FAST8     = 38
+PRIM_UINT_FAST8    = 39
+PRIM_INT_FAST16    = 40
+PRIM_UINT_FAST16   = 41
+PRIM_INT_FAST32    = 42
+PRIM_UINT_FAST32   = 43
+PRIM_INT_FAST64    = 44
+PRIM_UINT_FAST64   = 45
+PRIM_INTMAX        = 46
+PRIM_UINTMAX       = 47
+PRIM_FLOATCOMPLEX  = 48
+PRIM_DOUBLECOMPLEX = 49
+PRIM_CHAR16        = 50
+PRIM_CHAR32        = 51
+
+_NUM_PRIM          = 52
+_UNKNOWN_PRIM          = -1
+_UNKNOWN_FLOAT_PRIM    = -2
+_UNKNOWN_LONG_DOUBLE   = -3
+
+_IO_FILE_STRUCT        = -1
+
+PRIMITIVE_TO_INDEX = {
+    'char':               PRIM_CHAR,
+    'short':              PRIM_SHORT,
+    'int':                PRIM_INT,
+    'long':               PRIM_LONG,
+    'long long':          PRIM_LONGLONG,
+    'signed char':        PRIM_SCHAR,
+    'unsigned char':      PRIM_UCHAR,
+    'unsigned short':     PRIM_USHORT,
+    'unsigned int':       PRIM_UINT,
+    'unsigned long':      PRIM_ULONG,
+    'unsigned long long': PRIM_ULONGLONG,
+    'float':              PRIM_FLOAT,
+    'double':             PRIM_DOUBLE,
+    'long double':        PRIM_LONGDOUBLE,
+    'float _Complex':     PRIM_FLOATCOMPLEX,
+    'double _Complex':    PRIM_DOUBLECOMPLEX,
+    '_Bool':              PRIM_BOOL,
+    'wchar_t':            PRIM_WCHAR,
+    'char16_t':           PRIM_CHAR16,
+    'char32_t':           PRIM_CHAR32,
+    'int8_t':             PRIM_INT8,
+    'uint8_t':            PRIM_UINT8,
+    'int16_t':            PRIM_INT16,
+    'uint16_t':           PRIM_UINT16,
+    'int32_t':            PRIM_INT32,
+    'uint32_t':           PRIM_UINT32,
+    'int64_t':            PRIM_INT64,
+    'uint64_t':           PRIM_UINT64,
+    'intptr_t':           PRIM_INTPTR,
+    'uintptr_t':          PRIM_UINTPTR,
+    'ptrdiff_t':          PRIM_PTRDIFF,
+    'size_t':             PRIM_SIZE,
+    'ssize_t':            PRIM_SSIZE,
+    'int_least8_t':       PRIM_INT_LEAST8,
+    'uint_least8_t':      PRIM_UINT_LEAST8,
+    'int_least16_t':      PRIM_INT_LEAST16,
+    'uint_least16_t':     PRIM_UINT_LEAST16,
+    'int_least32_t':      PRIM_INT_LEAST32,
+    'uint_least32_t':     PRIM_UINT_LEAST32,
+    'int_least64_t':      PRIM_INT_LEAST64,
+    'uint_least64_t':     PRIM_UINT_LEAST64,
+    'int_fast8_t':        PRIM_INT_FAST8,
+    'uint_fast8_t':       PRIM_UINT_FAST8,
+    'int_fast16_t':       PRIM_INT_FAST16,
+    'uint_fast16_t':      PRIM_UINT_FAST16,
+    'int_fast32_t':       PRIM_INT_FAST32,
+    'uint_fast32_t':      PRIM_UINT_FAST32,
+    'int_fast64_t':       PRIM_INT_FAST64,
+    'uint_fast64_t':      PRIM_UINT_FAST64,
+    'intmax_t':           PRIM_INTMAX,
+    'uintmax_t':          PRIM_UINTMAX,
+    }
+
+F_UNION         = 0x01
+F_CHECK_FIELDS  = 0x02
+F_PACKED        = 0x04
+F_EXTERNAL      = 0x08
+F_OPAQUE        = 0x10
+
+G_FLAGS = dict([('_CFFI_' + _key, globals()[_key])
+                for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED',
+                             'F_EXTERNAL', 'F_OPAQUE']])
+
+CLASS_NAME = {}
+for _name, _value in list(globals().items()):
+    if _name.startswith('OP_') and isinstance(_value, int):
+        CLASS_NAME[_value] = _name[3:]
diff --git a/cffi/commontypes.py b/cffi/commontypes.py
new file mode 100644
index 0000000..8ec97c7
--- /dev/null
+++ b/cffi/commontypes.py
@@ -0,0 +1,80 @@
+import sys
+from . import model
+from .error import FFIError
+
+
+COMMON_TYPES = {}
+
+try:
+    # fetch "bool" and all simple Windows types
+    from _cffi_backend import _get_common_types
+    _get_common_types(COMMON_TYPES)
+except ImportError:
+    pass
+
+COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE')
+COMMON_TYPES['bool'] = '_Bool'    # in case we got ImportError above
+
+for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+    if _type.endswith('_t'):
+        COMMON_TYPES[_type] = _type
+del _type
+
+_CACHE = {}
+
+def resolve_common_type(parser, commontype):
+    try:
+        return _CACHE[commontype]
+    except KeyError:
+        cdecl = COMMON_TYPES.get(commontype, commontype)
+        if not isinstance(cdecl, str):
+            result, quals = cdecl, 0    # cdecl is already a BaseType
+        elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+            result, quals = model.PrimitiveType(cdecl), 0
+        elif cdecl == 'set-unicode-needed':
+            raise FFIError("The Windows type %r is only available after "
+                           "you call ffi.set_unicode()" % (commontype,))
+        else:
+            if commontype == cdecl:
+                raise FFIError(
+                    "Unsupported type: %r.  Please look at "
+        "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations "
+                    "and file an issue if you think this type should really "
+                    "be supported." % (commontype,))
+            result, quals = parser.parse_type_and_quals(cdecl)   # recursive
+
+        assert isinstance(result, model.BaseTypeByIdentity)
+        _CACHE[commontype] = result, quals
+        return result, quals
+
+
+# ____________________________________________________________
+# extra types for Windows (most of them are in commontypes.c)
+
+
+def win_common_types():
+    return {
+        "UNICODE_STRING": model.StructType(
+            "_UNICODE_STRING",
+            ["Length",
+             "MaximumLength",
+             "Buffer"],
+            [model.PrimitiveType("unsigned short"),
+             model.PrimitiveType("unsigned short"),
+             model.PointerType(model.PrimitiveType("wchar_t"))],
+            [-1, -1, -1]),
+        "PUNICODE_STRING": "UNICODE_STRING *",
+        "PCUNICODE_STRING": "const UNICODE_STRING *",
+
+        "TBYTE": "set-unicode-needed",
+        "TCHAR": "set-unicode-needed",
+        "LPCTSTR": "set-unicode-needed",
+        "PCTSTR": "set-unicode-needed",
+        "LPTSTR": "set-unicode-needed",
+        "PTSTR": "set-unicode-needed",
+        "PTBYTE": "set-unicode-needed",
+        "PTCHAR": "set-unicode-needed",
+        }
+
+if sys.platform == 'win32':
+    COMMON_TYPES.update(win_common_types())
diff --git a/cffi/cparser.py b/cffi/cparser.py
new file mode 100644
index 0000000..df6303d
--- /dev/null
+++ b/cffi/cparser.py
@@ -0,0 +1,923 @@
+from . import model
+from .commontypes import COMMON_TYPES, resolve_common_type
+from .error import FFIError, CDefError
+try:
+    from . import _pycparser as pycparser
+except ImportError:
+    import pycparser
+import weakref, re, sys
+
+try:
+    if sys.version_info < (3,):
+        import thread as _thread
+    else:
+        import _thread
+    lock = _thread.allocate_lock()
+except ImportError:
+    lock = None
+
+def _workaround_for_static_import_finders():
+    # Issue #392: packaging tools like cx_Freeze can not find these
+    # because pycparser uses exec dynamic import.  This is an obscure
+    # workaround.  This function is never called.
+    import pycparser.yacctab
+    import pycparser.lextab
+
+CDEF_SOURCE_STRING = "<cdef source string>"
+_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$",
+                        re.DOTALL | re.MULTILINE)
+_r_define  = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)"
+                        r"\b((?:[^\n\\]|\\.)*?)$",
+                        re.DOTALL | re.MULTILINE)
+_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}")
+_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$")
+_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]")
+_r_words = re.compile(r"\w+|\S")
+_parser_cache = None
+_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE)
+_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b")
+_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b")
+_r_cdecl = re.compile(r"\b__cdecl\b")
+_r_extern_python = re.compile(r'\bextern\s*"'
+                              r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.')
+_r_star_const_space = re.compile(       # matches "* const "
+    r"[*]\s*((const|volatile|restrict)\b\s*)+")
+_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+"
+                              r"\.\.\.")
+_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.")
+
+def _get_parser():
+    global _parser_cache
+    if _parser_cache is None:
+        _parser_cache = pycparser.CParser()
+    return _parser_cache
+
+def _workaround_for_old_pycparser(csource):
+    # Workaround for a pycparser issue (fixed between pycparser 2.10 and
+    # 2.14): "char*const***" gives us a wrong syntax tree, the same as
+    # for "char***(*const)".  This means we can't tell the difference
+    # afterwards.  But "char(*const(***))" gives us the right syntax
+    # tree.  The issue only occurs if there are several stars in
+    # sequence with no parenthesis inbetween, just possibly qualifiers.
+    # Attempt to fix it by adding some parentheses in the source: each
+    # time we see "* const" or "* const *", we add an opening
+    # parenthesis before each star---the hard part is figuring out where
+    # to close them.
+    parts = []
+    while True:
+        match = _r_star_const_space.search(csource)
+        if not match:
+            break
+        #print repr(''.join(parts)+csource), '=>',
+        parts.append(csource[:match.start()])
+        parts.append('('); closing = ')'
+        parts.append(match.group())   # e.g. "* const "
+        endpos = match.end()
+        if csource.startswith('*', endpos):
+            parts.append('('); closing += ')'
+        level = 0
+        i = endpos
+        while i < len(csource):
+            c = csource[i]
+            if c == '(':
+                level += 1
+            elif c == ')':
+                if level == 0:
+                    break
+                level -= 1
+            elif c in ',;=':
+                if level == 0:
+                    break
+            i += 1
+        csource = csource[endpos:i] + closing + csource[i:]
+        #print repr(''.join(parts)+csource)
+    parts.append(csource)
+    return ''.join(parts)
+
+def _preprocess_extern_python(csource):
+    # input: `extern "Python" int foo(int);` or
+    #        `extern "Python" { int foo(int); }`
+    # output:
+    #     void __cffi_extern_python_start;
+    #     int foo(int);
+    #     void __cffi_extern_python_stop;
+    #
+    # input: `extern "Python+C" int foo(int);`
+    # output:
+    #     void __cffi_extern_python_plus_c_start;
+    #     int foo(int);
+    #     void __cffi_extern_python_stop;
+    parts = []
+    while True:
+        match = _r_extern_python.search(csource)
+        if not match:
+            break
+        endpos = match.end() - 1
+        #print
+        #print ''.join(parts)+csource
+        #print '=>'
+        parts.append(csource[:match.start()])
+        if 'C' in match.group(1):
+            parts.append('void __cffi_extern_python_plus_c_start; ')
+        else:
+            parts.append('void __cffi_extern_python_start; ')
+        if csource[endpos] == '{':
+            # grouping variant
+            closing = csource.find('}', endpos)
+            if closing < 0:
+                raise CDefError("'extern \"Python\" {': no '}' found")
+            if csource.find('{', endpos + 1, closing) >= 0:
+                raise NotImplementedError("cannot use { } inside a block "
+                                          "'extern \"Python\" { ... }'")
+            parts.append(csource[endpos+1:closing])
+            csource = csource[closing+1:]
+        else:
+            # non-grouping variant
+            semicolon = csource.find(';', endpos)
+            if semicolon < 0:
+                raise CDefError("'extern \"Python\": no ';' found")
+            parts.append(csource[endpos:semicolon+1])
+            csource = csource[semicolon+1:]
+        parts.append(' void __cffi_extern_python_stop;')
+        #print ''.join(parts)+csource
+        #print
+    parts.append(csource)
+    return ''.join(parts)
+
+def _warn_for_string_literal(csource):
+    if '"' in csource:
+        import warnings
+        warnings.warn("String literal found in cdef() or type source. "
+                      "String literals are ignored here, but you should "
+                      "remove them anyway because some character sequences "
+                      "confuse pre-parsing.")
+
+def _preprocess(csource):
+    # Remove comments.  NOTE: this only work because the cdef() section
+    # should not contain any string literal!
+    csource = _r_comment.sub(' ', csource)
+    # Remove the "#define FOO x" lines
+    macros = {}
+    for match in _r_define.finditer(csource):
+        macroname, macrovalue = match.groups()
+        macrovalue = macrovalue.replace('\\\n', '').strip()
+        macros[macroname] = macrovalue
+    csource = _r_define.sub('', csource)
+    #
+    if pycparser.__version__ < '2.14':
+        csource = _workaround_for_old_pycparser(csource)
+    #
+    # BIG HACK: replace WINAPI or __stdcall with "volatile const".
+    # It doesn't make sense for the return type of a function to be
+    # "volatile volatile const", so we abuse it to detect __stdcall...
+    # Hack number 2 is that "int(volatile *fptr)();" is not valid C
+    # syntax, so we place the "volatile" before the opening parenthesis.
+    csource = _r_stdcall2.sub(' volatile volatile const(', csource)
+    csource = _r_stdcall1.sub(' volatile volatile const ', csource)
+    csource = _r_cdecl.sub(' ', csource)
+    #
+    # Replace `extern "Python"` with start/end markers
+    csource = _preprocess_extern_python(csource)
+    #
+    # Now there should not be any string literal left; warn if we get one
+    _warn_for_string_literal(csource)
+    #
+    # Replace "[...]" with "[__dotdotdotarray__]"
+    csource = _r_partial_array.sub('[__dotdotdotarray__]', csource)
+    #
+    # Replace "...}" with "__dotdotdotNUM__}".  This construction should
+    # occur only at the end of enums; at the end of structs we have "...;}"
+    # and at the end of vararg functions "...);".  Also replace "=...[,}]"
+    # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when
+    # giving an unknown value.
+    matches = list(_r_partial_enum.finditer(csource))
+    for number, match in enumerate(reversed(matches)):
+        p = match.start()
+        if csource[p] == '=':
+            p2 = csource.find('...', p, match.end())
+            assert p2 > p
+            csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number,
+                                                 csource[p2+3:])
+        else:
+            assert csource[p:p+3] == '...'
+            csource = '%s __dotdotdot%d__ %s' % (csource[:p], number,
+                                                 csource[p+3:])
+    # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__"
+    csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource)
+    # Replace "float ..." or "double..." with "__dotdotdotfloat__"
+    csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource)
+    # Replace all remaining "..." with the same name, "__dotdotdot__",
+    # which is declared with a typedef for the purpose of C parsing.
+    return csource.replace('...', ' __dotdotdot__ '), macros
+
+def _common_type_names(csource):
+    # Look in the source for what looks like usages of types from the
+    # list of common types.  A "usage" is approximated here as the
+    # appearance of the word, minus a "definition" of the type, which
+    # is the last word in a "typedef" statement.  Approximative only
+    # but should be fine for all the common types.
+    look_for_words = set(COMMON_TYPES)
+    look_for_words.add(';')
+    look_for_words.add(',')
+    look_for_words.add('(')
+    look_for_words.add(')')
+    look_for_words.add('typedef')
+    words_used = set()
+    is_typedef = False
+    paren = 0
+    previous_word = ''
+    for word in _r_words.findall(csource):
+        if word in look_for_words:
+            if word == ';':
+                if is_typedef:
+                    words_used.discard(previous_word)
+                    look_for_words.discard(previous_word)
+                    is_typedef = False
+            elif word == 'typedef':
+                is_typedef = True
+                paren = 0
+            elif word == '(':
+                paren += 1
+            elif word == ')':
+                paren -= 1
+            elif word == ',':
+                if is_typedef and paren == 0:
+                    words_used.discard(previous_word)
+                    look_for_words.discard(previous_word)
+            else:   # word in COMMON_TYPES
+                words_used.add(word)
+        previous_word = word
+    return words_used
+
+
+class Parser(object):
+
+    def __init__(self):
+        self._declarations = {}
+        self._included_declarations = set()
+        self._anonymous_counter = 0
+        self._structnode2type = weakref.WeakKeyDictionary()
+        self._options = {}
+        self._int_constants = {}
+        self._recomplete = []
+        self._uses_new_feature = None
+
+    def _parse(self, csource):
+        csource, macros = _preprocess(csource)
+        # XXX: for more efficiency we would need to poke into the
+        # internals of CParser...  the following registers the
+        # typedefs, because their presence or absence influences the
+        # parsing itself (but what they are typedef'ed to plays no role)
+        ctn = _common_type_names(csource)
+        typenames = []
+        for name in sorted(self._declarations):
+            if name.startswith('typedef '):
+                name = name[8:]
+                typenames.append(name)
+                ctn.discard(name)
+        typenames += sorted(ctn)
+        #
+        csourcelines = []
+        csourcelines.append('# 1 "<cdef automatic initialization code>"')
+        for typename in typenames:
+            csourcelines.append('typedef int %s;' % typename)
+        csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,'
+                            ' __dotdotdot__;')
+        # this forces pycparser to consider the following in the file
+        # called <cdef source string> from line 1
+        csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,))
+        csourcelines.append(csource)
+        fullcsource = '\n'.join(csourcelines)
+        if lock is not None:
+            lock.acquire()     # pycparser is not thread-safe...
+        try:
+            ast = _get_parser().parse(fullcsource)
+        except pycparser.c_parser.ParseError as e:
+            self.convert_pycparser_error(e, csource)
+        finally:
+            if lock is not None:
+                lock.release()
+        # csource will be used to find buggy source text
+        return ast, macros, csource
+
+    def _convert_pycparser_error(self, e, csource):
+        # xxx look for "<cdef source string>:NUM:" at the start of str(e)
+        # and interpret that as a line number.  This will not work if
+        # the user gives explicit ``# NUM "FILE"`` directives.
+        line = None
+        msg = str(e)
+        match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg)
+        if match:
+            linenum = int(match.group(1), 10)
+            csourcelines = csource.splitlines()
+            if 1 <= linenum <= len(csourcelines):
+                line = csourcelines[linenum-1]
+        return line
+
+    def convert_pycparser_error(self, e, csource):
+        line = self._convert_pycparser_error(e, csource)
+
+        msg = str(e)
+        if line:
+            msg = 'cannot parse "%s"\n%s' % (line.strip(), msg)
+        else:
+            msg = 'parse error\n%s' % (msg,)
+        raise CDefError(msg)
+
+    def parse(self, csource, override=False, packed=False, pack=None,
+                    dllexport=False):
+        if packed:
+            if packed != True:
+                raise ValueError("'packed' should be False or True; use "
+                                 "'pack' to give another value")
+            if pack:
+                raise ValueError("cannot give both 'pack' and 'packed'")
+            pack = 1
+        elif pack:
+            if pack & (pack - 1):
+                raise ValueError("'pack' must be a power of two, not %r" %
+                    (pack,))
+        else:
+            pack = 0
+        prev_options = self._options
+        try:
+            self._options = {'override': override,
+                             'packed': pack,
+                             'dllexport': dllexport}
+            self._internal_parse(csource)
+        finally:
+            self._options = prev_options
+
+    def _internal_parse(self, csource):
+        ast, macros, csource = self._parse(csource)
+        # add the macros
+        self._process_macros(macros)
+        # find the first "__dotdotdot__" and use that as a separator
+        # between the repeated typedefs and the real csource
+        iterator = iter(ast.ext)
+        for decl in iterator:
+            if decl.name == '__dotdotdot__':
+                break
+        else:
+            assert 0
+        current_decl = None
+        #
+        try:
+            self._inside_extern_python = '__cffi_extern_python_stop'
+            for decl in iterator:
+                current_decl = decl
+                if isinstance(decl, pycparser.c_ast.Decl):
+                    self._parse_decl(decl)
+                elif isinstance(decl, pycparser.c_ast.Typedef):
+                    if not decl.name:
+                        raise CDefError("typedef does not declare any name",
+                                        decl)
+                    quals = 0
+                    if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and
+                            decl.type.type.names[-1].startswith('__dotdotdot')):
+                        realtype = self._get_unknown_type(decl)
+                    elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and
+                          isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and
+                          isinstance(decl.type.type.type,
+                                     pycparser.c_ast.IdentifierType) and
+                          decl.type.type.type.names[-1].startswith('__dotdotdot')):
+                        realtype = self._get_unknown_ptr_type(decl)
+                    else:
+                        realtype, quals = self._get_type_and_quals(
+                            decl.type, name=decl.name, partial_length_ok=True)
+                    self._declare('typedef ' + decl.name, realtype, quals=quals)
+                elif decl.__class__.__name__ == 'Pragma':
+                    pass    # skip pragma, only in pycparser 2.15
+                else:
+                    raise CDefError("unexpected <%s>: this construct is valid "
+                                    "C but not valid in cdef()" %
+                                    decl.__class__.__name__, decl)
+        except CDefError as e:
+            if len(e.args) == 1:
+                e.args = e.args + (current_decl,)
+            raise
+        except FFIError as e:
+            msg = self._convert_pycparser_error(e, csource)
+            if msg:
+                e.args = (e.args[0] + "\n    *** Err: %s" % msg,)
+            raise
+
+    def _add_constants(self, key, val):
+        if key in self._int_constants:
+            if self._int_constants[key] == val:
+                return     # ignore identical double declarations
+            raise FFIError(
+                "multiple declarations of constant: %s" % (key,))
+        self._int_constants[key] = val
+
+    def _add_integer_constant(self, name, int_str):
+        int_str = int_str.lower().rstrip("ul")
+        neg = int_str.startswith('-')
+        if neg:
+            int_str = int_str[1:]
+        # "010" is not valid oct in py3
+        if (int_str.startswith("0") and int_str != '0'
+                and not int_str.startswith("0x")):
+            int_str = "0o" + int_str[1:]
+        pyvalue = int(int_str, 0)
+        if neg:
+            pyvalue = -pyvalue
+        self._add_constants(name, pyvalue)
+        self._declare('macro ' + name, pyvalue)
+
+    def _process_macros(self, macros):
+        for key, value in macros.items():
+            value = value.strip()
+            if _r_int_literal.match(value):
+                self._add_integer_constant(key, value)
+            elif value == '...':
+                self._declare('macro ' + key, value)
+            else:
+                raise CDefError(
+                    'only supports one of the following syntax:\n'
+                    '  #define %s ...     (literally dot-dot-dot)\n'
+                    '  #define %s NUMBER  (with NUMBER an integer'
+                                    ' constant, decimal/hex/octal)\n'
+                    'got:\n'
+                    '  #define %s %s'
+                    % (key, key, key, value))
+
+    def _declare_function(self, tp, quals, decl):
+        tp = self._get_type_pointer(tp, quals)
+        if self._options.get('dllexport'):
+            tag = 'dllexport_python '
+        elif self._inside_extern_python == '__cffi_extern_python_start':
+            tag = 'extern_python '
+        elif self._inside_extern_python == '__cffi_extern_python_plus_c_start':
+            tag = 'extern_python_plus_c '
+        else:
+            tag = 'function '
+        self._declare(tag + decl.name, tp)
+
+    def _parse_decl(self, decl):
+        node = decl.type
+        if isinstance(node, pycparser.c_ast.FuncDecl):
+            tp, quals = self._get_type_and_quals(node, name=decl.name)
+            assert isinstance(tp, model.RawFunctionType)
+            self._declare_function(tp, quals, decl)
+        else:
+            if isinstance(node, pycparser.c_ast.Struct):
+                self._get_struct_union_enum_type('struct', node)
+            elif isinstance(node, pycparser.c_ast.Union):
+                self._get_struct_union_enum_type('union', node)
+            elif isinstance(node, pycparser.c_ast.Enum):
+                self._get_struct_union_enum_type('enum', node)
+            elif not decl.name:
+                raise CDefError("construct does not declare any variable",
+                                decl)
+            #
+            if decl.name:
+                tp, quals = self._get_type_and_quals(node,
+                                                     partial_length_ok=True)
+                if tp.is_raw_function:
+                    self._declare_function(tp, quals, decl)
+                elif (tp.is_integer_type() and
+                        hasattr(decl, 'init') and
+                        hasattr(decl.init, 'value') and
+                        _r_int_literal.match(decl.init.value)):
+                    self._add_integer_constant(decl.name, decl.init.value)
+                elif (tp.is_integer_type() and
+                        isinstance(decl.init, pycparser.c_ast.UnaryOp) and
+                        decl.init.op == '-' and
+                        hasattr(decl.init.expr, 'value') and
+                        _r_int_literal.match(decl.init.expr.value)):
+                    self._add_integer_constant(decl.name,
+                                               '-' + decl.init.expr.value)
+                elif (tp is model.void_type and
+                      decl.name.startswith('__cffi_extern_python_')):
+                    # hack: `extern "Python"` in the C source is replaced
+                    # with "void __cffi_extern_python_start;" and
+                    # "void __cffi_extern_python_stop;"
+                    self._inside_extern_python = decl.name
+                else:
+                    if self._inside_extern_python !='__cffi_extern_python_stop':
+                        raise CDefError(
+                            "cannot declare constants or "
+                            "variables with 'extern \"Python\"'")
+                    if (quals & model.Q_CONST) and not tp.is_array_type:
+                        self._declare('constant ' + decl.name, tp, quals=quals)
+                    else:
+                        self._declare('variable ' + decl.name, tp, quals=quals)
+
+    def parse_type(self, cdecl):
+        return self.parse_type_and_quals(cdecl)[0]
+
+    def parse_type_and_quals(self, cdecl):
+        ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2]
+        assert not macros
+        exprnode = ast.ext[-1].type.args.params[0]
+        if isinstance(exprnode, pycparser.c_ast.ID):
+            raise CDefError("unknown identifier '%s'" % (exprnode.name,))
+        return self._get_type_and_quals(exprnode.type)
+
+    def _declare(self, name, obj, included=False, quals=0):
+        if name in self._declarations:
+            prevobj, prevquals = self._declarations[name]
+            if prevobj is obj and prevquals == quals:
+                return
+            if not self._options.get('override'):
+                raise FFIError(
+                    "multiple declarations of %s (for interactive usage, "
+                    "try cdef(xx, override=True))" % (name,))
+        assert '__dotdotdot__' not in name.split()
+        self._declarations[name] = (obj, quals)
+        if included:
+            self._included_declarations.add(obj)
+
+    def _extract_quals(self, type):
+        quals = 0
+        if isinstance(type, (pycparser.c_ast.TypeDecl,
+                             pycparser.c_ast.PtrDecl)):
+            if 'const' in type.quals:
+                quals |= model.Q_CONST
+            if 'volatile' in type.quals:
+                quals |= model.Q_VOLATILE
+            if 'restrict' in type.quals:
+                quals |= model.Q_RESTRICT
+        return quals
+
+    def _get_type_pointer(self, type, quals, declname=None):
+        if isinstance(type, model.RawFunctionType):
+            return type.as_function_pointer()
+        if (isinstance(type, model.StructOrUnionOrEnum) and
+                type.name.startswith('$') and type.name[1:].isdigit() and
+                type.forcename is None and declname is not None):
+            return model.NamedPointerType(type, declname, quals)
+        return model.PointerType(type, quals)
+
+    def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False):
+        # first, dereference typedefs, if we have it already parsed, we're good
+        if (isinstance(typenode, pycparser.c_ast.TypeDecl) and
+            isinstance(typenode.type, pycparser.c_ast.IdentifierType) and
+            len(typenode.type.names) == 1 and
+            ('typedef ' + typenode.type.names[0]) in self._declarations):
+            tp, quals = self._declarations['typedef ' + typenode.type.names[0]]
+            quals |= self._extract_quals(typenode)
+            return tp, quals
+        #
+        if isinstance(typenode, pycparser.c_ast.ArrayDecl):
+            # array type
+            if typenode.dim is None:
+                length = None
+            else:
+                length = self._parse_constant(
+                    typenode.dim, partial_length_ok=partial_length_ok)
+            tp, quals = self._get_type_and_quals(typenode.type,
+                                partial_length_ok=partial_length_ok)
+            return model.ArrayType(tp, length), quals
+        #
+        if isinstance(typenode, pycparser.c_ast.PtrDecl):
+            # pointer type
+            itemtype, itemquals = self._get_type_and_quals(typenode.type)
+            tp = self._get_type_pointer(itemtype, itemquals, declname=name)
+            quals = self._extract_quals(typenode)
+            return tp, quals
+        #
+        if isinstance(typenode, pycparser.c_ast.TypeDecl):
+            quals = self._extract_quals(typenode)
+            type = typenode.type
+            if isinstance(type, pycparser.c_ast.IdentifierType):
+                # assume a primitive type.  get it from .names, but reduce
+                # synonyms to a single chosen combination
+                names = list(type.names)
+                if names != ['signed', 'char']:    # keep this unmodified
+                    prefixes = {}
+                    while names:
+                        name = names[0]
+                        if name in ('short', 'long', 'signed', 'unsigned'):
+                            prefixes[name] = prefixes.get(name, 0) + 1
+                            del names[0]
+                        else:
+                            break
+                    # ignore the 'signed' prefix below, and reorder the others
+                    newnames = []
+                    for prefix in ('unsigned', 'short', 'long'):
+                        for i in range(prefixes.get(prefix, 0)):
+                            newnames.append(prefix)
+                    if not names:
+                        names = ['int']    # implicitly
+                    if names == ['int']:   # but kill it if 'short' or 'long'
+                        if 'short' in prefixes or 'long' in prefixes:
+                            names = []
+                    names = newnames + names
+                ident = ' '.join(names)
+                if ident == 'void':
+                    return model.void_type, quals
+                if ident == '__dotdotdot__':
+                    raise FFIError(':%d: bad usage of "..."' %
+                            typenode.coord.line)
+                tp0, quals0 = resolve_common_type(self, ident)
+                return tp0, (quals | quals0)
+            #
+            if isinstance(type, pycparser.c_ast.Struct):
+                # 'struct foobar'
+                tp = self._get_struct_union_enum_type('struct', type, name)
+                return tp, quals
+            #
+            if isinstance(type, pycparser.c_ast.Union):
+                # 'union foobar'
+                tp = self._get_struct_union_enum_type('union', type, name)
+                return tp, quals
+            #
+            if isinstance(type, pycparser.c_ast.Enum):
+                # 'enum foobar'
+                tp = self._get_struct_union_enum_type('enum', type, name)
+                return tp, quals
+        #
+        if isinstance(typenode, pycparser.c_ast.FuncDecl):
+            # a function type
+            return self._parse_function_type(typenode, name), 0
+        #
+        # nested anonymous structs or unions end up here
+        if isinstance(typenode, pycparser.c_ast.Struct):
+            return self._get_struct_union_enum_type('struct', typenode, name,
+                                                    nested=True), 0
+        if isinstance(typenode, pycparser.c_ast.Union):
+            return self._get_struct_union_enum_type('union', typenode, name,
+                                                    nested=True), 0
+        #
+        raise FFIError(":%d: bad or unsupported type declaration" %
+                typenode.coord.line)
+
+    def _parse_function_type(self, typenode, funcname=None):
+        params = list(getattr(typenode.args, 'params', []))
+        for i, arg in enumerate(params):
+            if not hasattr(arg, 'type'):
+                raise CDefError("%s arg %d: unknown type '%s'"
+                    " (if you meant to use the old C syntax of giving"
+                    " untyped arguments, it is not supported)"
+                    % (funcname or 'in expression', i + 1,
+                       getattr(arg, 'name', '?')))
+        ellipsis = (
+            len(params) > 0 and
+            isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and
+            isinstance(params[-1].type.type,
+                       pycparser.c_ast.IdentifierType) and
+            params[-1].type.type.names == ['__dotdotdot__'])
+        if ellipsis:
+            params.pop()
+            if not params:
+                raise CDefError(
+                    "%s: a function with only '(...)' as argument"
+                    " is not correct C" % (funcname or 'in expression'))
+        args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type))
+                for argdeclnode in params]
+        if not ellipsis and args == [model.void_type]:
+            args = []
+        result, quals = self._get_type_and_quals(typenode.type)
+        # the 'quals' on the result type are ignored.  HACK: we absure them
+        # to detect __stdcall functions: we textually replace "__stdcall"
+        # with "volatile volatile const" above.
+        abi = None
+        if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway
+            if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']:
+                abi = '__stdcall'
+        return model.RawFunctionType(tuple(args), result, ellipsis, abi)
+
+    def _as_func_arg(self, type, quals):
+        if isinstance(type, model.ArrayType):
+            return model.PointerType(type.item, quals)
+        elif isinstance(type, model.RawFunctionType):
+            return type.as_function_pointer()
+        else:
+            return type
+
+    def _get_struct_union_enum_type(self, kind, type, name=None, nested=False):
+        # First, a level of caching on the exact 'type' node of the AST.
+        # This is obscure, but needed because pycparser "unrolls" declarations
+        # such as "typedef struct { } foo_t, *foo_p" and we end up with
+        # an AST that is not a tree, but a DAG, with the "type" node of the
+        # two branches foo_t and foo_p of the trees being the same node.
+        # It's a bit silly but detecting "DAG-ness" in the AST tree seems
+        # to be the only way to distinguish this case from two independent
+        # structs.  See test_struct_with_two_usages.
+        try:
+            return self._structnode2type[type]
+        except KeyError:
+            pass
+        #
+        # Note that this must handle parsing "struct foo" any number of
+        # times and always return the same StructType object.  Additionally,
+        # one of these times (not necessarily the first), the fields of
+        # the struct can be specified with "struct foo { ...fields... }".
+        # If no name is given, then we have to create a new anonymous struct
+        # with no caching; in this case, the fields are either specified
+        # right now or never.
+        #
+        force_name = name
+        name = type.name
+        #
+        # get the type or create it if needed
+        if name is None:
+            # 'force_name' is used to guess a more readable name for
+            # anonymous structs, for the common case "typedef struct { } foo".
+            if force_name is not None:
+                explicit_name = '$%s' % force_name
+            else:
+                self._anonymous_counter += 1
+                explicit_name = '$%d' % self._anonymous_counter
+            tp = None
+        else:
+            explicit_name = name
+            key = '%s %s' % (kind, name)
+            tp, _ = self._declarations.get(key, (None, None))
+        #
+        if tp is None:
+            if kind == 'struct':
+                tp = model.StructType(explicit_name, None, None, None)
+            elif kind == 'union':
+                tp = model.UnionType(explicit_name, None, None, None)
+            elif kind == 'enum':
+                if explicit_name == '__dotdotdot__':
+                    raise CDefError("Enums cannot be declared with ...")
+                tp = self._build_enum_type(explicit_name, type.values)
+            else:
+                raise AssertionError("kind = %r" % (kind,))
+            if name is not None:
+                self._declare(key, tp)
+        else:
+            if kind == 'enum' and type.values is not None:
+                raise NotImplementedError(
+                    "enum %s: the '{}' declaration should appear on the first "
+                    "time the enum is mentioned, not later" % explicit_name)
+        if not tp.forcename:
+            tp.force_the_name(force_name)
+        if tp.forcename and '$' in tp.name:
+            self._declare('anonymous %s' % tp.forcename, tp)
+        #
+        self._structnode2type[type] = tp
+        #
+        # enums: done here
+        if kind == 'enum':
+            return tp
+        #
+        # is there a 'type.decls'?  If yes, then this is the place in the
+        # C sources that declare the fields.  If no, then just return the
+        # existing type, possibly still incomplete.
+        if type.decls is None:
+            return tp
+        #
+        if tp.fldnames is not None:
+            raise CDefError("duplicate declaration of struct %s" % name)
+        fldnames = []
+        fldtypes = []
+        fldbitsize = []
+        fldquals = []
+        for decl in type.decls:
+            if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and
+                    ''.join(decl.type.names) == '__dotdotdot__'):
+                # XXX pycparser is inconsistent: 'names' should be a list
+                # of strings, but is sometimes just one string.  Use
+                # str.join() as a way to cope with both.
+                self._make_partial(tp, nested)
+                continue
+            if decl.bitsize is None:
+                bitsize = -1
+            else:
+                bitsize = self._parse_constant(decl.bitsize)
+            self._partial_length = False
+            type, fqual = self._get_type_and_quals(decl.type,
+                                                   partial_length_ok=True)
+            if self._partial_length:
+                self._make_partial(tp, nested)
+            if isinstance(type, model.StructType) and type.partial:
+                self._make_partial(tp, nested)
+            fldnames.append(decl.name or '')
+            fldtypes.append(type)
+            fldbitsize.append(bitsize)
+            fldquals.append(fqual)
+        tp.fldnames = tuple(fldnames)
+        tp.fldtypes = tuple(fldtypes)
+        tp.fldbitsize = tuple(fldbitsize)
+        tp.fldquals = tuple(fldquals)
+        if fldbitsize != [-1] * len(fldbitsize):
+            if isinstance(tp, model.StructType) and tp.partial:
+                raise NotImplementedError("%s: using both bitfields and '...;'"
+                                          % (tp,))
+        tp.packed = self._options.get('packed')
+        if tp.completed:    # must be re-completed: it is not opaque any more
+            tp.completed = 0
+            self._recomplete.append(tp)
+        return tp
+
+    def _make_partial(self, tp, nested):
+        if not isinstance(tp, model.StructOrUnion):
+            raise CDefError("%s cannot be partial" % (tp,))
+        if not tp.has_c_name() and not nested:
+            raise NotImplementedError("%s is partial but has no C name" %(tp,))
+        tp.partial = True
+
+    def _parse_constant(self, exprnode, partial_length_ok=False):
+        # for now, limited to expressions that are an immediate number
+        # or positive/negative number
+        if isinstance(exprnode, pycparser.c_ast.Constant):
+            s = exprnode.value
+            if s.startswith('0'):
+                if s.startswith('0x') or s.startswith('0X'):
+                    return int(s, 16)
+                return int(s, 8)
+            elif '1' <= s[0] <= '9':
+                return int(s, 10)
+            elif s[0] == "'" and s[-1] == "'" and (
+                    len(s) == 3 or (len(s) == 4 and s[1] == "\\")):
+                return ord(s[-2])
+            else:
+                raise CDefError("invalid constant %r" % (s,))
+        #
+        if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and
+                exprnode.op == '+'):
+            return self._parse_constant(exprnode.expr)
+        #
+        if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and
+                exprnode.op == '-'):
+            return -self._parse_constant(exprnode.expr)
+        # load previously defined int constant
+        if (isinstance(exprnode, pycparser.c_ast.ID) and
+                exprnode.name in self._int_constants):
+            return self._int_constants[exprnode.name]
+        #
+        if (isinstance(exprnode, pycparser.c_ast.ID) and
+                    exprnode.name == '__dotdotdotarray__'):
+            if partial_length_ok:
+                self._partial_length = True
+                return '...'
+            raise FFIError(":%d: unsupported '[...]' here, cannot derive "
+                           "the actual array length in this context"
+                           % exprnode.coord.line)
+        #
+        if (isinstance(exprnode, pycparser.c_ast.BinaryOp) and
+                exprnode.op == '+'):
+            return (self._parse_constant(exprnode.left) +
+                    self._parse_constant(exprnode.right))
+        #
+        if (isinstance(exprnode, pycparser.c_ast.BinaryOp) and
+                exprnode.op == '-'):
+            return (self._parse_constant(exprnode.left) -
+                    self._parse_constant(exprnode.right))
+        #
+        raise FFIError(":%d: unsupported expression: expected a "
+                       "simple numeric constant" % exprnode.coord.line)
+
+    def _build_enum_type(self, explicit_name, decls):
+        if decls is not None:
+            partial = False
+            enumerators = []
+            enumvalues = []
+            nextenumvalue = 0
+            for enum in decls.enumerators:
+                if _r_enum_dotdotdot.match(enum.name):
+                    partial = True
+                    continue
+                if enum.value is not None:
+                    nextenumvalue = self._parse_constant(enum.value)
+                enumerators.append(enum.name)
+                enumvalues.append(nextenumvalue)
+                self._add_constants(enum.name, nextenumvalue)
+                nextenumvalue += 1
+            enumerators = tuple(enumerators)
+            enumvalues = tuple(enumvalues)
+            tp = model.EnumType(explicit_name, enumerators, enumvalues)
+            tp.partial = partial
+        else:   # opaque enum
+            tp = model.EnumType(explicit_name, (), ())
+        return tp
+
+    def include(self, other):
+        for name, (tp, quals) in other._declarations.items():
+            if name.startswith('anonymous $enum_$'):
+                continue   # fix for test_anonymous_enum_include
+            kind = name.split(' ', 1)[0]
+            if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'):
+                self._declare(name, tp, included=True, quals=quals)
+        for k, v in other._int_constants.items():
+            self._add_constants(k, v)
+
+    def _get_unknown_type(self, decl):
+        typenames = decl.type.type.names
+        if typenames == ['__dotdotdot__']:
+            return model.unknown_type(decl.name)
+
+        if typenames == ['__dotdotdotint__']:
+            if self._uses_new_feature is None:
+                self._uses_new_feature = "'typedef int... %s'" % decl.name
+            return model.UnknownIntegerType(decl.name)
+
+        if typenames == ['__dotdotdotfloat__']:
+            # note: not for 'long double' so far
+            if self._uses_new_feature is None:
+                self._uses_new_feature = "'typedef float... %s'" % decl.name
+            return model.UnknownFloatType(decl.name)
+
+        raise FFIError(':%d: unsupported usage of "..." in typedef'
+                       % decl.coord.line)
+
+    def _get_unknown_ptr_type(self, decl):
+        if decl.type.type.type.names == ['__dotdotdot__']:
+            return model.unknown_ptr_type(decl.name)
+        raise FFIError(':%d: unsupported usage of "..." in typedef'
+                       % decl.coord.line)
diff --git a/cffi/error.py b/cffi/error.py
new file mode 100644
index 0000000..0a27247
--- /dev/null
+++ b/cffi/error.py
@@ -0,0 +1,31 @@
+
+class FFIError(Exception):
+    __module__ = 'cffi'
+
+class CDefError(Exception):
+    __module__ = 'cffi'
+    def __str__(self):
+        try:
+            current_decl = self.args[1]
+            filename = current_decl.coord.file
+            linenum = current_decl.coord.line
+            prefix = '%s:%d: ' % (filename, linenum)
+        except (AttributeError, TypeError, IndexError):
+            prefix = ''
+        return '%s%s' % (prefix, self.args[0])
+
+class VerificationError(Exception):
+    """ An error raised when verification fails
+    """
+    __module__ = 'cffi'
+
+class VerificationMissing(Exception):
+    """ An error raised when incomplete structures are passed into
+    cdef, but no verification has been done
+    """
+    __module__ = 'cffi'
+
+class PkgConfigError(Exception):
+    """ An error raised for missing modules in pkg-config
+    """
+    __module__ = 'cffi'
diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py
new file mode 100644
index 0000000..8531346
--- /dev/null
+++ b/cffi/ffiplatform.py
@@ -0,0 +1,127 @@
+import sys, os
+from .error import VerificationError
+
+
+LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs',
+                      'extra_objects', 'depends']
+
+def get_extension(srcfilename, modname, sources=(), **kwds):
+    _hack_at_distutils()
+    from distutils.core import Extension
+    allsources = [srcfilename]
+    for src in sources:
+        allsources.append(os.path.normpath(src))
+    return Extension(name=modname, sources=allsources, **kwds)
+
+def compile(tmpdir, ext, compiler_verbose=0, debug=None):
+    """Compile a C extension module using distutils."""
+
+    _hack_at_distutils()
+    saved_environ = os.environ.copy()
+    try:
+        outputfilename = _build(tmpdir, ext, compiler_verbose, debug)
+        outputfilename = os.path.abspath(outputfilename)
+    finally:
+        # workaround for a distutils bugs where some env vars can
+        # become longer and longer every time it is used
+        for key, value in saved_environ.items():
+            if os.environ.get(key) != value:
+                os.environ[key] = value
+    return outputfilename
+
+def _build(tmpdir, ext, compiler_verbose=0, debug=None):
+    # XXX compact but horrible :-(
+    from distutils.core import Distribution
+    import distutils.errors, distutils.log
+    #
+    dist = Distribution({'ext_modules': [ext]})
+    dist.parse_config_files()
+    options = dist.get_option_dict('build_ext')
+    if debug is None:
+        debug = sys.flags.debug
+    options['debug'] = ('ffiplatform', debug)
+    options['force'] = ('ffiplatform', True)
+    options['build_lib'] = ('ffiplatform', tmpdir)
+    options['build_temp'] = ('ffiplatform', tmpdir)
+    #
+    try:
+        old_level = distutils.log.set_threshold(0) or 0
+        try:
+            distutils.log.set_verbosity(compiler_verbose)
+            dist.run_command('build_ext')
+            cmd_obj = dist.get_command_obj('build_ext')
+            [soname] = cmd_obj.get_outputs()
+        finally:
+            distutils.log.set_threshold(old_level)
+    except (distutils.errors.CompileError,
+            distutils.errors.LinkError) as e:
+        raise VerificationError('%s: %s' % (e.__class__.__name__, e))
+    #
+    return soname
+
+try:
+    from os.path import samefile
+except ImportError:
+    def samefile(f1, f2):
+        return os.path.abspath(f1) == os.path.abspath(f2)
+
+def maybe_relative_path(path):
+    if not os.path.isabs(path):
+        return path      # already relative
+    dir = path
+    names = []
+    while True:
+        prevdir = dir
+        dir, name = os.path.split(prevdir)
+        if dir == prevdir or not dir:
+            return path     # failed to make it relative
+        names.append(name)
+        try:
+            if samefile(dir, os.curdir):
+                names.reverse()
+                return os.path.join(*names)
+        except OSError:
+            pass
+
+# ____________________________________________________________
+
+try:
+    int_or_long = (int, long)
+    import cStringIO
+except NameError:
+    int_or_long = int      # Python 3
+    import io as cStringIO
+
+def _flatten(x, f):
+    if isinstance(x, str):
+        f.write('%ds%s' % (len(x), x))
+    elif isinstance(x, dict):
+        keys = sorted(x.keys())
+        f.write('%dd' % len(keys))
+        for key in keys:
+            _flatten(key, f)
+            _flatten(x[key], f)
+    elif isinstance(x, (list, tuple)):
+        f.write('%dl' % len(x))
+        for value in x:
+            _flatten(value, f)
+    elif isinstance(x, int_or_long):
+        f.write('%di' % (x,))
+    else:
+        raise TypeError(
+            "the keywords to verify() contains unsupported object %r" % (x,))
+
+def flatten(x):
+    f = cStringIO.StringIO()
+    _flatten(x, f)
+    return f.getvalue()
+
+def _hack_at_distutils():
+    # Windows-only workaround for some configurations: see
+    # https://bugs.python.org/issue23246 (Python 2.7 with 
+    # a specific MS compiler suite download)
+    if sys.platform == "win32":
+        try:
+            import setuptools    # for side-effects, patches distutils
+        except ImportError:
+            pass
diff --git a/cffi/lock.py b/cffi/lock.py
new file mode 100644
index 0000000..db91b71
--- /dev/null
+++ b/cffi/lock.py
@@ -0,0 +1,30 @@
+import sys
+
+if sys.version_info < (3,):
+    try:
+        from thread import allocate_lock
+    except ImportError:
+        from dummy_thread import allocate_lock
+else:
+    try:
+        from _thread import allocate_lock
+    except ImportError:
+        from _dummy_thread import allocate_lock
+
+
+##import sys
+##l1 = allocate_lock
+
+##class allocate_lock(object):
+##    def __init__(self):
+##        self._real = l1()
+##    def __enter__(self):
+##        for i in range(4, 0, -1):
+##            print sys._getframe(i).f_code
+##        print
+##        return self._real.__enter__()
+##    def __exit__(self, *args):
+##        return self._real.__exit__(*args)
+##    def acquire(self, f):
+##        assert f is False
+##        return self._real.acquire(f)
diff --git a/cffi/model.py b/cffi/model.py
new file mode 100644
index 0000000..5f1b0d2
--- /dev/null
+++ b/cffi/model.py
@@ -0,0 +1,614 @@
+import types
+import weakref
+
+from .lock import allocate_lock
+from .error import CDefError, VerificationError, VerificationMissing
+
+# type qualifiers
+Q_CONST    = 0x01
+Q_RESTRICT = 0x02
+Q_VOLATILE = 0x04
+
+def qualify(quals, replace_with):
+    if quals & Q_CONST:
+        replace_with = ' const ' + replace_with.lstrip()
+    if quals & Q_VOLATILE:
+        replace_with = ' volatile ' + replace_with.lstrip()
+    if quals & Q_RESTRICT:
+        # It seems that __restrict is supported by gcc and msvc.
+        # If you hit some different compiler, add a #define in
+        # _cffi_include.h for it (and in its copies, documented there)
+        replace_with = ' __restrict ' + replace_with.lstrip()
+    return replace_with
+
+
+class BaseTypeByIdentity(object):
+    is_array_type = False
+    is_raw_function = False
+
+    def get_c_name(self, replace_with='', context='a C file', quals=0):
+        result = self.c_name_with_marker
+        assert result.count('&') == 1
+        # some logic duplication with ffi.getctype()... :-(
+        replace_with = replace_with.strip()
+        if replace_with:
+            if replace_with.startswith('*') and '&[' in result:
+                replace_with = '(%s)' % replace_with
+            elif not replace_with[0] in '[(':
+                replace_with = ' ' + replace_with
+        replace_with = qualify(quals, replace_with)
+        result = result.replace('&', replace_with)
+        if '$' in result:
+            raise VerificationError(
+                "cannot generate '%s' in %s: unknown type name"
+                % (self._get_c_name(), context))
+        return result
+
+    def _get_c_name(self):
+        return self.c_name_with_marker.replace('&', '')
+
+    def has_c_name(self):
+        return '$' not in self._get_c_name()
+
+    def is_integer_type(self):
+        return False
+
+    def get_cached_btype(self, ffi, finishlist, can_delay=False):
+        try:
+            BType = ffi._cached_btypes[self]
+        except KeyError:
+            BType = self.build_backend_type(ffi, finishlist)
+            BType2 = ffi._cached_btypes.setdefault(self, BType)
+            assert BType2 is BType
+        return BType
+
+    def __repr__(self):
+        return '<%s>' % (self._get_c_name(),)
+
+    def _get_items(self):
+        return [(name, getattr(self, name)) for name in self._attrs_]
+
+
+class BaseType(BaseTypeByIdentity):
+
+    def __eq__(self, other):
+        return (self.__class__ == other.__class__ and
+                self._get_items() == other._get_items())
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __hash__(self):
+        return hash((self.__class__, tuple(self._get_items())))
+
+
+class VoidType(BaseType):
+    _attrs_ = ()
+
+    def __init__(self):
+        self.c_name_with_marker = 'void&'
+
+    def build_backend_type(self, ffi, finishlist):
+        return global_cache(self, ffi, 'new_void_type')
+
+void_type = VoidType()
+
+
+class BasePrimitiveType(BaseType):
+    def is_complex_type(self):
+        return False
+
+
+class PrimitiveType(BasePrimitiveType):
+    _attrs_ = ('name',)
+
+    ALL_PRIMITIVE_TYPES = {
+        'char':               'c',
+        'short':              'i',
+        'int':                'i',
+        'long':               'i',
+        'long long':          'i',
+        'signed char':        'i',
+        'unsigned char':      'i',
+        'unsigned short':     'i',
+        'unsigned int':       'i',
+        'unsigned long':      'i',
+        'unsigned long long': 'i',
+        'float':              'f',
+        'double':             'f',
+        'long double':        'f',
+        'float _Complex':     'j',
+        'double _Complex':    'j',
+        '_Bool':              'i',
+        # the following types are not primitive in the C sense
+        'wchar_t':            'c',
+        'char16_t':           'c',
+        'char32_t':           'c',
+        'int8_t':             'i',
+        'uint8_t':            'i',
+        'int16_t':            'i',
+        'uint16_t':           'i',
+        'int32_t':            'i',
+        'uint32_t':           'i',
+        'int64_t':            'i',
+        'uint64_t':           'i',
+        'int_least8_t':       'i',
+        'uint_least8_t':      'i',
+        'int_least16_t':      'i',
+        'uint_least16_t':     'i',
+        'int_least32_t':      'i',
+        'uint_least32_t':     'i',
+        'int_least64_t':      'i',
+        'uint_least64_t':     'i',
+        'int_fast8_t':        'i',
+        'uint_fast8_t':       'i',
+        'int_fast16_t':       'i',
+        'uint_fast16_t':      'i',
+        'int_fast32_t':       'i',
+        'uint_fast32_t':      'i',
+        'int_fast64_t':       'i',
+        'uint_fast64_t':      'i',
+        'intptr_t':           'i',
+        'uintptr_t':          'i',
+        'intmax_t':           'i',
+        'uintmax_t':          'i',
+        'ptrdiff_t':          'i',
+        'size_t':             'i',
+        'ssize_t':            'i',
+        }
+
+    def __init__(self, name):
+        assert name in self.ALL_PRIMITIVE_TYPES
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+    def is_char_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'c'
+    def is_integer_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'i'
+    def is_float_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
+    def is_complex_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'j'
+
+    def build_backend_type(self, ffi, finishlist):
+        return global_cache(self, ffi, 'new_primitive_type', self.name)
+
+
+class UnknownIntegerType(BasePrimitiveType):
+    _attrs_ = ('name',)
+
+    def __init__(self, name):
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+    def is_integer_type(self):
+        return True
+
+    def build_backend_type(self, ffi, finishlist):
+        raise NotImplementedError("integer type '%s' can only be used after "
+                                  "compilation" % self.name)
+
+class UnknownFloatType(BasePrimitiveType):
+    _attrs_ = ('name', )
+
+    def __init__(self, name):
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+    def build_backend_type(self, ffi, finishlist):
+        raise NotImplementedError("float type '%s' can only be used after "
+                                  "compilation" % self.name)
+
+
+class BaseFunctionType(BaseType):
+    _attrs_ = ('args', 'result', 'ellipsis', 'abi')
+
+    def __init__(self, args, result, ellipsis, abi=None):
+        self.args = args
+        self.result = result
+        self.ellipsis = ellipsis
+        self.abi = abi
+        #
+        reprargs = [arg._get_c_name() for arg in self.args]
+        if self.ellipsis:
+            reprargs.append('...')
+        reprargs = reprargs or ['void']
+        replace_with = self._base_pattern % (', '.join(reprargs),)
+        if abi is not None:
+            replace_with = replace_with[:1] + abi + ' ' + replace_with[1:]
+        self.c_name_with_marker = (
+            self.result.c_name_with_marker.replace('&', replace_with))
+
+
+class RawFunctionType(BaseFunctionType):
+    # Corresponds to a C type like 'int(int)', which is the C type of
+    # a function, but not a pointer-to-function.  The backend has no
+    # notion of such a type; it's used temporarily by parsing.
+    _base_pattern = '(&)(%s)'
+    is_raw_function = True
+
+    def build_backend_type(self, ffi, finishlist):
+        raise CDefError("cannot render the type %r: it is a function "
+                        "type, not a pointer-to-function type" % (self,))
+
+    def as_function_pointer(self):
+        return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi)
+
+
+class FunctionPtrType(BaseFunctionType):
+    _base_pattern = '(*&)(%s)'
+
+    def build_backend_type(self, ffi, finishlist):
+        result = self.result.get_cached_btype(ffi, finishlist)
+        args = []
+        for tp in self.args:
+            args.append(tp.get_cached_btype(ffi, finishlist))
+        abi_args = ()
+        if self.abi == "__stdcall":
+            if not self.ellipsis:    # __stdcall ignored for variadic funcs
+                try:
+                    abi_args = (ffi._backend.FFI_STDCALL,)
+                except AttributeError:
+                    pass
+        return global_cache(self, ffi, 'new_function_type',
+                            tuple(args), result, self.ellipsis, *abi_args)
+
+    def as_raw_function(self):
+        return RawFunctionType(self.args, self.result, self.ellipsis, self.abi)
+
+
+class PointerType(BaseType):
+    _attrs_ = ('totype', 'quals')
+
+    def __init__(self, totype, quals=0):
+        self.totype = totype
+        self.quals = quals
+        extra = qualify(quals, " *&")
+        if totype.is_array_type:
+            extra = "(%s)" % (extra.lstrip(),)
+        self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra)
+
+    def build_backend_type(self, ffi, finishlist):
+        BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)
+        return global_cache(self, ffi, 'new_pointer_type', BItem)
+
+voidp_type = PointerType(void_type)
+
+def ConstPointerType(totype):
+    return PointerType(totype, Q_CONST)
+
+const_voidp_type = ConstPointerType(void_type)
+
+
+class NamedPointerType(PointerType):
+    _attrs_ = ('totype', 'name')
+
+    def __init__(self, totype, name, quals=0):
+        PointerType.__init__(self, totype, quals)
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+
+class ArrayType(BaseType):
+    _attrs_ = ('item', 'length')
+    is_array_type = True
+
+    def __init__(self, item, length):
+        self.item = item
+        self.length = length
+        #
+        if length is None:
+            brackets = '&[]'
+        elif length == '...':
+            brackets = '&[/*...*/]'
+        else:
+            brackets = '&[%s]' % length
+        self.c_name_with_marker = (
+            self.item.c_name_with_marker.replace('&', brackets))
+
+    def resolve_length(self, newlength):
+        return ArrayType(self.item, newlength)
+
+    def build_backend_type(self, ffi, finishlist):
+        if self.length == '...':
+            raise CDefError("cannot render the type %r: unknown length" %
+                            (self,))
+        self.item.get_cached_btype(ffi, finishlist)   # force the item BType
+        BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)
+        return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)
+
+char_array_type = ArrayType(PrimitiveType('char'), None)
+
+
+class StructOrUnionOrEnum(BaseTypeByIdentity):
+    _attrs_ = ('name',)
+    forcename = None
+
+    def build_c_name_with_marker(self):
+        name = self.forcename or '%s %s' % (self.kind, self.name)
+        self.c_name_with_marker = name + '&'
+
+    def force_the_name(self, forcename):
+        self.forcename = forcename
+        self.build_c_name_with_marker()
+
+    def get_official_name(self):
+        assert self.c_name_with_marker.endswith('&')
+        return self.c_name_with_marker[:-1]
+
+
+class StructOrUnion(StructOrUnionOrEnum):
+    fixedlayout = None
+    completed = 0
+    partial = False
+    packed = 0
+
+    def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None):
+        self.name = name
+        self.fldnames = fldnames
+        self.fldtypes = fldtypes
+        self.fldbitsize = fldbitsize
+        self.fldquals = fldquals
+        self.build_c_name_with_marker()
+
+    def anonymous_struct_fields(self):
+        if self.fldtypes is not None:
+            for name, type in zip(self.fldnames, self.fldtypes):
+                if name == '' and isinstance(type, StructOrUnion):
+                    yield type
+
+    def enumfields(self, expand_anonymous_struct_union=True):
+        fldquals = self.fldquals
+        if fldquals is None:
+            fldquals = (0,) * len(self.fldnames)
+        for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes,
+                                              self.fldbitsize, fldquals):
+            if (name == '' and isinstance(type, StructOrUnion)
+                    and expand_anonymous_struct_union):
+                # nested anonymous struct/union
+                for result in type.enumfields():
+                    yield result
+            else:
+                yield (name, type, bitsize, quals)
+
+    def force_flatten(self):
+        # force the struct or union to have a declaration that lists
+        # directly all fields returned by enumfields(), flattening
+        # nested anonymous structs/unions.
+        names = []
+        types = []
+        bitsizes = []
+        fldquals = []
+        for name, type, bitsize, quals in self.enumfields():
+            names.append(name)
+            types.append(type)
+            bitsizes.append(bitsize)
+            fldquals.append(quals)
+        self.fldnames = tuple(names)
+        self.fldtypes = tuple(types)
+        self.fldbitsize = tuple(bitsizes)
+        self.fldquals = tuple(fldquals)
+
+    def get_cached_btype(self, ffi, finishlist, can_delay=False):
+        BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist,
+                                                     can_delay)
+        if not can_delay:
+            self.finish_backend_type(ffi, finishlist)
+        return BType
+
+    def finish_backend_type(self, ffi, finishlist):
+        if self.completed:
+            if self.completed != 2:
+                raise NotImplementedError("recursive structure declaration "
+                                          "for '%s'" % (self.name,))
+            return
+        BType = ffi._cached_btypes[self]
+        #
+        self.completed = 1
+        #
+        if self.fldtypes is None:
+            pass    # not completing it: it's an opaque struct
+            #
+        elif self.fixedlayout is None:
+            fldtypes = [tp.get_cached_btype(ffi, finishlist)
+                        for tp in self.fldtypes]
+            lst = list(zip(self.fldnames, fldtypes, self.fldbitsize))
+            extra_flags = ()
+            if self.packed:
+                if self.packed == 1:
+                    extra_flags = (8,)    # SF_PACKED
+                else:
+                    extra_flags = (0, self.packed)
+            ffi._backend.complete_struct_or_union(BType, lst, self,
+                                                  -1, -1, *extra_flags)
+            #
+        else:
+            fldtypes = []
+            fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout
+            for i in range(len(self.fldnames)):
+                fsize = fieldsize[i]
+                ftype = self.fldtypes[i]
+                #
+                if isinstance(ftype, ArrayType) and ftype.length == '...':
+                    # fix the length to match the total size
+                    BItemType = ftype.item.get_cached_btype(ffi, finishlist)
+                    nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))
+                    if nrest != 0:
+                        self._verification_error(
+                            "field '%s.%s' has a bogus size?" % (
+                            self.name, self.fldnames[i] or '{}'))
+                    ftype = ftype.resolve_length(nlen)
+                    self.fldtypes = (self.fldtypes[:i] + (ftype,) +
+                                     self.fldtypes[i+1:])
+                #
+                BFieldType = ftype.get_cached_btype(ffi, finishlist)
+                if isinstance(ftype, ArrayType) and ftype.length is None:
+                    assert fsize == 0
+                else:
+                    bitemsize = ffi.sizeof(BFieldType)
+                    if bitemsize != fsize:
+                        self._verification_error(
+                            "field '%s.%s' is declared as %d bytes, but is "
+                            "really %d bytes" % (self.name,
+                                                 self.fldnames[i] or '{}',
+                                                 bitemsize, fsize))
+                fldtypes.append(BFieldType)
+            #
+            lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs))
+            ffi._backend.complete_struct_or_union(BType, lst, self,
+                                                  totalsize, totalalignment)
+        self.completed = 2
+
+    def _verification_error(self, msg):
+        raise VerificationError(msg)
+
+    def check_not_partial(self):
+        if self.partial and self.fixedlayout is None:
+            raise VerificationMissing(self._get_c_name())
+
+    def build_backend_type(self, ffi, finishlist):
+        self.check_not_partial()
+        finishlist.append(self)
+        #
+        return global_cache(self, ffi, 'new_%s_type' % self.kind,
+                            self.get_official_name(), key=self)
+
+
+class StructType(StructOrUnion):
+    kind = 'struct'
+
+
+class UnionType(StructOrUnion):
+    kind = 'union'
+
+
+class EnumType(StructOrUnionOrEnum):
+    kind = 'enum'
+    partial = False
+    partial_resolved = False
+
+    def __init__(self, name, enumerators, enumvalues, baseinttype=None):
+        self.name = name
+        self.enumerators = enumerators
+        self.enumvalues = enumvalues
+        self.baseinttype = baseinttype
+        self.build_c_name_with_marker()
+
+    def force_the_name(self, forcename):
+        StructOrUnionOrEnum.force_the_name(self, forcename)
+        if self.forcename is None:
+            name = self.get_official_name()
+            self.forcename = '$' + name.replace(' ', '_')
+
+    def check_not_partial(self):
+        if self.partial and not self.partial_resolved:
+            raise VerificationMissing(self._get_c_name())
+
+    def build_backend_type(self, ffi, finishlist):
+        self.check_not_partial()
+        base_btype = self.build_baseinttype(ffi, finishlist)
+        return global_cache(self, ffi, 'new_enum_type',
+                            self.get_official_name(),
+                            self.enumerators, self.enumvalues,
+                            base_btype, key=self)
+
+    def build_baseinttype(self, ffi, finishlist):
+        if self.baseinttype is not None:
+            return self.baseinttype.get_cached_btype(ffi, finishlist)
+        #
+        if self.enumvalues:
+            smallest_value = min(self.enumvalues)
+            largest_value = max(self.enumvalues)
+        else:
+            import warnings
+            try:
+                # XXX!  The goal is to ensure that the warnings.warn()
+                # will not suppress the warning.  We want to get it
+                # several times if we reach this point several times.
+                __warningregistry__.clear()
+            except NameError:
+                pass
+            warnings.warn("%r has no values explicitly defined; "
+                          "guessing that it is equivalent to 'unsigned int'"
+                          % self._get_c_name())
+            smallest_value = largest_value = 0
+        if smallest_value < 0:   # needs a signed type
+            sign = 1
+            candidate1 = PrimitiveType("int")
+            candidate2 = PrimitiveType("long")
+        else:
+            sign = 0
+            candidate1 = PrimitiveType("unsigned int")
+            candidate2 = PrimitiveType("unsigned long")
+        btype1 = candidate1.get_cached_btype(ffi, finishlist)
+        btype2 = candidate2.get_cached_btype(ffi, finishlist)
+        size1 = ffi.sizeof(btype1)
+        size2 = ffi.sizeof(btype2)
+        if (smallest_value >= ((-1) << (8*size1-1)) and
+            largest_value < (1 << (8*size1-sign))):
+            return btype1
+        if (smallest_value >= ((-1) << (8*size2-1)) and
+            largest_value < (1 << (8*size2-sign))):
+            return btype2
+        raise CDefError("%s values don't all fit into either 'long' "
+                        "or 'unsigned long'" % self._get_c_name())
+
+def unknown_type(name, structname=None):
+    if structname is None:
+        structname = '$%s' % name
+    tp = StructType(structname, None, None, None)
+    tp.force_the_name(name)
+    tp.origin = "unknown_type"
+    return tp
+
+def unknown_ptr_type(name, structname=None):
+    if structname is None:
+        structname = '$$%s' % name
+    tp = StructType(structname, None, None, None)
+    return NamedPointerType(tp, name)
+
+
+global_lock = allocate_lock()
+_typecache_cffi_backend = weakref.WeakValueDictionary()
+
+def get_typecache(backend):
+    # returns _typecache_cffi_backend if backend is the _cffi_backend
+    # module, or type(backend).__typecache if backend is an instance of
+    # CTypesBackend (or some FakeBackend class during tests)
+    if isinstance(backend, types.ModuleType):
+        return _typecache_cffi_backend
+    with global_lock:
+        if not hasattr(type(backend), '__typecache'):
+            type(backend).__typecache = weakref.WeakValueDictionary()
+        return type(backend).__typecache
+
+def global_cache(srctype, ffi, funcname, *args, **kwds):
+    key = kwds.pop('key', (funcname, args))
+    assert not kwds
+    try:
+        return ffi._typecache[key]
+    except KeyError:
+        pass
+    try:
+        res = getattr(ffi._backend, funcname)(*args)
+    except NotImplementedError as e:
+        raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e))
+    # note that setdefault() on WeakValueDictionary is not atomic
+    # and contains a rare bug (http://bugs.python.org/issue19542);
+    # we have to use a lock and do it ourselves
+    cache = ffi._typecache
+    with global_lock:
+        res1 = cache.get(key)
+        if res1 is None:
+            cache[key] = res
+            return res
+        else:
+            return res1
+
+def pointer_cache(ffi, BType):
+    return global_cache('?', ffi, 'new_pointer_type', BType)
+
+def attach_exception_info(e, name):
+    if e.args and type(e.args[0]) is str:
+        e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:]
diff --git a/cffi/parse_c_type.h b/cffi/parse_c_type.h
new file mode 100644
index 0000000..84e4ef8
--- /dev/null
+++ b/cffi/parse_c_type.h
@@ -0,0 +1,181 @@
+
+/* This part is from file 'cffi/parse_c_type.h'.  It is copied at the
+   beginning of C sources generated by CFFI's ffi.set_source(). */
+
+typedef void *_cffi_opcode_t;
+
+#define _CFFI_OP(opcode, arg)   (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8))
+#define _CFFI_GETOP(cffi_opcode)    ((unsigned char)(uintptr_t)cffi_opcode)
+#define _CFFI_GETARG(cffi_opcode)   (((intptr_t)cffi_opcode) >> 8)
+
+#define _CFFI_OP_PRIMITIVE       1
+#define _CFFI_OP_POINTER         3
+#define _CFFI_OP_ARRAY           5
+#define _CFFI_OP_OPEN_ARRAY      7
+#define _CFFI_OP_STRUCT_UNION    9
+#define _CFFI_OP_ENUM           11
+#define _CFFI_OP_FUNCTION       13
+#define _CFFI_OP_FUNCTION_END   15
+#define _CFFI_OP_NOOP           17
+#define _CFFI_OP_BITFIELD       19
+#define _CFFI_OP_TYPENAME       21
+#define _CFFI_OP_CPYTHON_BLTN_V 23   // varargs
+#define _CFFI_OP_CPYTHON_BLTN_N 25   // noargs
+#define _CFFI_OP_CPYTHON_BLTN_O 27   // O  (i.e. a single arg)
+#define _CFFI_OP_CONSTANT       29
+#define _CFFI_OP_CONSTANT_INT   31
+#define _CFFI_OP_GLOBAL_VAR     33
+#define _CFFI_OP_DLOPEN_FUNC    35
+#define _CFFI_OP_DLOPEN_CONST   37
+#define _CFFI_OP_GLOBAL_VAR_F   39
+#define _CFFI_OP_EXTERN_PYTHON  41
+
+#define _CFFI_PRIM_VOID          0
+#define _CFFI_PRIM_BOOL          1
+#define _CFFI_PRIM_CHAR          2
+#define _CFFI_PRIM_SCHAR         3
+#define _CFFI_PRIM_UCHAR         4
+#define _CFFI_PRIM_SHORT         5
+#define _CFFI_PRIM_USHORT        6
+#define _CFFI_PRIM_INT           7
+#define _CFFI_PRIM_UINT          8
+#define _CFFI_PRIM_LONG          9
+#define _CFFI_PRIM_ULONG        10
+#define _CFFI_PRIM_LONGLONG     11
+#define _CFFI_PRIM_ULONGLONG    12
+#define _CFFI_PRIM_FLOAT        13
+#define _CFFI_PRIM_DOUBLE       14
+#define _CFFI_PRIM_LONGDOUBLE   15
+
+#define _CFFI_PRIM_WCHAR        16
+#define _CFFI_PRIM_INT8         17
+#define _CFFI_PRIM_UINT8        18
+#define _CFFI_PRIM_INT16        19
+#define _CFFI_PRIM_UINT16       20
+#define _CFFI_PRIM_INT32        21
+#define _CFFI_PRIM_UINT32       22
+#define _CFFI_PRIM_INT64        23
+#define _CFFI_PRIM_UINT64       24
+#define _CFFI_PRIM_INTPTR       25
+#define _CFFI_PRIM_UINTPTR      26
+#define _CFFI_PRIM_PTRDIFF      27
+#define _CFFI_PRIM_SIZE         28
+#define _CFFI_PRIM_SSIZE        29
+#define _CFFI_PRIM_INT_LEAST8   30
+#define _CFFI_PRIM_UINT_LEAST8  31
+#define _CFFI_PRIM_INT_LEAST16  32
+#define _CFFI_PRIM_UINT_LEAST16 33
+#define _CFFI_PRIM_INT_LEAST32  34
+#define _CFFI_PRIM_UINT_LEAST32 35
+#define _CFFI_PRIM_INT_LEAST64  36
+#define _CFFI_PRIM_UINT_LEAST64 37
+#define _CFFI_PRIM_INT_FAST8    38
+#define _CFFI_PRIM_UINT_FAST8   39
+#define _CFFI_PRIM_INT_FAST16   40
+#define _CFFI_PRIM_UINT_FAST16  41
+#define _CFFI_PRIM_INT_FAST32   42
+#define _CFFI_PRIM_UINT_FAST32  43
+#define _CFFI_PRIM_INT_FAST64   44
+#define _CFFI_PRIM_UINT_FAST64  45
+#define _CFFI_PRIM_INTMAX       46
+#define _CFFI_PRIM_UINTMAX      47
+#define _CFFI_PRIM_FLOATCOMPLEX 48
+#define _CFFI_PRIM_DOUBLECOMPLEX 49
+#define _CFFI_PRIM_CHAR16       50
+#define _CFFI_PRIM_CHAR32       51
+
+#define _CFFI__NUM_PRIM         52
+#define _CFFI__UNKNOWN_PRIM           (-1)
+#define _CFFI__UNKNOWN_FLOAT_PRIM     (-2)
+#define _CFFI__UNKNOWN_LONG_DOUBLE    (-3)
+
+#define _CFFI__IO_FILE_STRUCT         (-1)
+
+
+struct _cffi_global_s {
+    const char *name;
+    void *address;
+    _cffi_opcode_t type_op;
+    void *size_or_direct_fn;  // OP_GLOBAL_VAR: size, or 0 if unknown
+                              // OP_CPYTHON_BLTN_*: addr of direct function
+};
+
+struct _cffi_getconst_s {
+    unsigned long long value;
+    const struct _cffi_type_context_s *ctx;
+    int gindex;
+};
+
+struct _cffi_struct_union_s {
+    const char *name;
+    int type_index;          // -> _cffi_types, on a OP_STRUCT_UNION
+    int flags;               // _CFFI_F_* flags below
+    size_t size;
+    int alignment;
+    int first_field_index;   // -> _cffi_fields array
+    int num_fields;
+};
+#define _CFFI_F_UNION         0x01   // is a union, not a struct
+#define _CFFI_F_CHECK_FIELDS  0x02   // complain if fields are not in the
+                                     // "standard layout" or if some are missing
+#define _CFFI_F_PACKED        0x04   // for CHECK_FIELDS, assume a packed struct
+#define _CFFI_F_EXTERNAL      0x08   // in some other ffi.include()
+#define _CFFI_F_OPAQUE        0x10   // opaque
+
+struct _cffi_field_s {
+    const char *name;
+    size_t field_offset;
+    size_t field_size;
+    _cffi_opcode_t field_type_op;
+};
+
+struct _cffi_enum_s {
+    const char *name;
+    int type_index;          // -> _cffi_types, on a OP_ENUM
+    int type_prim;           // _CFFI_PRIM_xxx
+    const char *enumerators; // comma-delimited string
+};
+
+struct _cffi_typename_s {
+    const char *name;
+    int type_index;   /* if opaque, points to a possibly artificial
+                         OP_STRUCT which is itself opaque */
+};
+
+struct _cffi_type_context_s {
+    _cffi_opcode_t *types;
+    const struct _cffi_global_s *globals;
+    const struct _cffi_field_s *fields;
+    const struct _cffi_struct_union_s *struct_unions;
+    const struct _cffi_enum_s *enums;
+    const struct _cffi_typename_s *typenames;
+    int num_globals;
+    int num_struct_unions;
+    int num_enums;
+    int num_typenames;
+    const char *const *includes;
+    int num_types;
+    int flags;      /* future extension */
+};
+
+struct _cffi_parse_info_s {
+    const struct _cffi_type_context_s *ctx;
+    _cffi_opcode_t *output;
+    unsigned int output_size;
+    size_t error_location;
+    const char *error_message;
+};
+
+struct _cffi_externpy_s {
+    const char *name;
+    size_t size_of_result;
+    void *reserved1, *reserved2;
+};
+
+#ifdef _CFFI_INTERNAL
+static int parse_c_type(struct _cffi_parse_info_s *info, const char *input);
+static int search_in_globals(const struct _cffi_type_context_s *ctx,
+                             const char *search, size_t search_len);
+static int search_in_struct_unions(const struct _cffi_type_context_s *ctx,
+                                   const char *search, size_t search_len);
+#endif
diff --git a/cffi/pkgconfig.py b/cffi/pkgconfig.py
new file mode 100644
index 0000000..5c93f15
--- /dev/null
+++ b/cffi/pkgconfig.py
@@ -0,0 +1,121 @@
+# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi
+import sys, os, subprocess
+
+from .error import PkgConfigError
+
+
+def merge_flags(cfg1, cfg2):
+    """Merge values from cffi config flags cfg2 to cf1
+
+    Example:
+        merge_flags({"libraries": ["one"]}, {"libraries": ["two"]})
+        {"libraries": ["one", "two"]}
+    """
+    for key, value in cfg2.items():
+        if key not in cfg1:
+            cfg1[key] = value
+        else:
+            if not isinstance(cfg1[key], list):
+                raise TypeError("cfg1[%r] should be a list of strings" % (key,))
+            if not isinstance(value, list):
+                raise TypeError("cfg2[%r] should be a list of strings" % (key,))
+            cfg1[key].extend(value)
+    return cfg1
+
+
+def call(libname, flag, encoding=sys.getfilesystemencoding()):
+    """Calls pkg-config and returns the output if found
+    """
+    a = ["pkg-config", "--print-errors"]
+    a.append(flag)
+    a.append(libname)
+    try:
+        pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    except EnvironmentError as e:
+        raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),))
+
+    bout, berr = pc.communicate()
+    if pc.returncode != 0:
+        try:
+            berr = berr.decode(encoding)
+        except Exception:
+            pass
+        raise PkgConfigError(berr.strip())
+
+    if sys.version_info >= (3,) and not isinstance(bout, str):   # Python 3.x
+        try:
+            bout = bout.decode(encoding)
+        except UnicodeDecodeError:
+            raise PkgConfigError("pkg-config %s %s returned bytes that cannot "
+                                 "be decoded with encoding %r:\n%r" %
+                                 (flag, libname, encoding, bout))
+
+    if os.altsep != '\\' and '\\' in bout:
+        raise PkgConfigError("pkg-config %s %s returned an unsupported "
+                             "backslash-escaped output:\n%r" %
+                             (flag, libname, bout))
+    return bout
+
+
+def flags_from_pkgconfig(libs):
+    r"""Return compiler line flags for FFI.set_source based on pkg-config output
+
+    Usage
+        ...
+        ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"])
+
+    If pkg-config is installed on build machine, then arguments include_dirs,
+    library_dirs, libraries, define_macros, extra_compile_args and
+    extra_link_args are extended with an output of pkg-config for libfoo and
+    libbar.
+
+    Raises PkgConfigError in case the pkg-config call fails.
+    """
+
+    def get_include_dirs(string):
+        return [x[2:] for x in string.split() if x.startswith("-I")]
+
+    def get_library_dirs(string):
+        return [x[2:] for x in string.split() if x.startswith("-L")]
+
+    def get_libraries(string):
+        return [x[2:] for x in string.split() if x.startswith("-l")]
+
+    # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils
+    def get_macros(string):
+        def _macro(x):
+            x = x[2:]    # drop "-D"
+            if '=' in x:
+                return tuple(x.split("=", 1))  # "-Dfoo=bar" => ("foo", "bar")
+            else:
+                return (x, None)               # "-Dfoo" => ("foo", None)
+        return [_macro(x) for x in string.split() if x.startswith("-D")]
+
+    def get_other_cflags(string):
+        return [x for x in string.split() if not x.startswith("-I") and
+                                             not x.startswith("-D")]
+
+    def get_other_libs(string):
+        return [x for x in string.split() if not x.startswith("-L") and
+                                             not x.startswith("-l")]
+
+    # return kwargs for given libname
+    def kwargs(libname):
+        fse = sys.getfilesystemencoding()
+        all_cflags = call(libname, "--cflags")
+        all_libs = call(libname, "--libs")
+        return {
+            "include_dirs": get_include_dirs(all_cflags),
+            "library_dirs": get_library_dirs(all_libs),
+            "libraries": get_libraries(all_libs),
+            "define_macros": get_macros(all_cflags),
+            "extra_compile_args": get_other_cflags(all_cflags),
+            "extra_link_args": get_other_libs(all_libs),
+            }
+
+    # merge all arguments together
+    ret = {}
+    for libname in libs:
+        lib_flags = kwargs(libname)
+        merge_flags(ret, lib_flags)
+    return ret
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
new file mode 100644
index 0000000..20e912b
--- /dev/null
+++ b/cffi/recompiler.py
@@ -0,0 +1,1542 @@
+import os, sys, io
+from . import ffiplatform, model
+from .error import VerificationError
+from .cffi_opcode import *
+
+VERSION_BASE = 0x2601
+VERSION_EMBEDDED = 0x2701
+VERSION_CHAR16CHAR32 = 0x2801
+
+
+class GlobalExpr:
+    def __init__(self, name, address, type_op, size=0, check_value=0):
+        self.name = name
+        self.address = address
+        self.type_op = type_op
+        self.size = size
+        self.check_value = check_value
+
+    def as_c_expr(self):
+        return '  { "%s", (void *)%s, %s, (void *)%s },' % (
+            self.name, self.address, self.type_op.as_c_expr(), self.size)
+
+    def as_python_expr(self):
+        return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,
+                               self.check_value)
+
+class FieldExpr:
+    def __init__(self, name, field_offset, field_size, fbitsize, field_type_op):
+        self.name = name
+        self.field_offset = field_offset
+        self.field_size = field_size
+        self.fbitsize = fbitsize
+        self.field_type_op = field_type_op
+
+    def as_c_expr(self):
+        spaces = " " * len(self.name)
+        return ('  { "%s", %s,\n' % (self.name, self.field_offset) +
+                '     %s   %s,\n' % (spaces, self.field_size) +
+                '     %s   %s },' % (spaces, self.field_type_op.as_c_expr()))
+
+    def as_python_expr(self):
+        raise NotImplementedError
+
+    def as_field_python_expr(self):
+        if self.field_type_op.op == OP_NOOP:
+            size_expr = ''
+        elif self.field_type_op.op == OP_BITFIELD:
+            size_expr = format_four_bytes(self.fbitsize)
+        else:
+            raise NotImplementedError
+        return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(),
+                              size_expr,
+                              self.name)
+
+class StructUnionExpr:
+    def __init__(self, name, type_index, flags, size, alignment, comment,
+                 first_field_index, c_fields):
+        self.name = name
+        self.type_index = type_index
+        self.flags = flags
+        self.size = size
+        self.alignment = alignment
+        self.comment = comment
+        self.first_field_index = first_field_index
+        self.c_fields = c_fields
+
+    def as_c_expr(self):
+        return ('  { "%s", %d, %s,' % (self.name, self.type_index, self.flags)
+                + '\n    %s, %s, ' % (self.size, self.alignment)
+                + '%d, %d ' % (self.first_field_index, len(self.c_fields))
+                + ('/* %s */ ' % self.comment if self.comment else '')
+                + '},')
+
+    def as_python_expr(self):
+        flags = eval(self.flags, G_FLAGS)
+        fields_expr = [c_field.as_field_python_expr()
+                       for c_field in self.c_fields]
+        return "(b'%s%s%s',%s)" % (
+            format_four_bytes(self.type_index),
+            format_four_bytes(flags),
+            self.name,
+            ','.join(fields_expr))
+
+class EnumExpr:
+    def __init__(self, name, type_index, size, signed, allenums):
+        self.name = name
+        self.type_index = type_index
+        self.size = size
+        self.signed = signed
+        self.allenums = allenums
+
+    def as_c_expr(self):
+        return ('  { "%s", %d, _cffi_prim_int(%s, %s),\n'
+                '    "%s" },' % (self.name, self.type_index,
+                                 self.size, self.signed, self.allenums))
+
+    def as_python_expr(self):
+        prim_index = {
+            (1, 0): PRIM_UINT8,  (1, 1):  PRIM_INT8,
+            (2, 0): PRIM_UINT16, (2, 1):  PRIM_INT16,
+            (4, 0): PRIM_UINT32, (4, 1):  PRIM_INT32,
+            (8, 0): PRIM_UINT64, (8, 1):  PRIM_INT64,
+            }[self.size, self.signed]
+        return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index),
+                                     format_four_bytes(prim_index),
+                                     self.name, self.allenums)
+
+class TypenameExpr:
+    def __init__(self, name, type_index):
+        self.name = name
+        self.type_index = type_index
+
+    def as_c_expr(self):
+        return '  { "%s", %d },' % (self.name, self.type_index)
+
+    def as_python_expr(self):
+        return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)
+
+
+# ____________________________________________________________
+
+
+class Recompiler:
+    _num_externpy = 0
+
+    def __init__(self, ffi, module_name, target_is_python=False):
+        self.ffi = ffi
+        self.module_name = module_name
+        self.target_is_python = target_is_python
+        self._version = VERSION_BASE
+
+    def needs_version(self, ver):
+        self._version = max(self._version, ver)
+
+    def collect_type_table(self):
+        self._typesdict = {}
+        self._generate("collecttype")
+        #
+        all_decls = sorted(self._typesdict, key=str)
+        #
+        # prepare all FUNCTION bytecode sequences first
+        self.cffi_types = []
+        for tp in all_decls:
+            if tp.is_raw_function:
+                assert self._typesdict[tp] is None
+                self._typesdict[tp] = len(self.cffi_types)
+                self.cffi_types.append(tp)     # placeholder
+                for tp1 in tp.args:
+                    assert isinstance(tp1, (model.VoidType,
+                                            model.BasePrimitiveType,
+                                            model.PointerType,
+                                            model.StructOrUnionOrEnum,
+                                            model.FunctionPtrType))
+                    if self._typesdict[tp1] is None:
+                        self._typesdict[tp1] = len(self.cffi_types)
+                    self.cffi_types.append(tp1)   # placeholder
+                self.cffi_types.append('END')     # placeholder
+        #
+        # prepare all OTHER bytecode sequences
+        for tp in all_decls:
+            if not tp.is_raw_function and self._typesdict[tp] is None:
+                self._typesdict[tp] = len(self.cffi_types)
+                self.cffi_types.append(tp)        # placeholder
+                if tp.is_array_type and tp.length is not None:
+                    self.cffi_types.append('LEN') # placeholder
+        assert None not in self._typesdict.values()
+        #
+        # collect all structs and unions and enums
+        self._struct_unions = {}
+        self._enums = {}
+        for tp in all_decls:
+            if isinstance(tp, model.StructOrUnion):
+                self._struct_unions[tp] = None
+            elif isinstance(tp, model.EnumType):
+                self._enums[tp] = None
+        for i, tp in enumerate(sorted(self._struct_unions,
+                                      key=lambda tp: tp.name)):
+            self._struct_unions[tp] = i
+        for i, tp in enumerate(sorted(self._enums,
+                                      key=lambda tp: tp.name)):
+            self._enums[tp] = i
+        #
+        # emit all bytecode sequences now
+        for tp in all_decls:
+            method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__)
+            method(tp, self._typesdict[tp])
+        #
+        # consistency check
+        for op in self.cffi_types:
+            assert isinstance(op, CffiOp)
+        self.cffi_types = tuple(self.cffi_types)    # don't change any more
+
+    def _do_collect_type(self, tp):
+        if not isinstance(tp, model.BaseTypeByIdentity):
+            if isinstance(tp, tuple):
+                for x in tp:
+                    self._do_collect_type(x)
+            return
+        if tp not in self._typesdict:
+            self._typesdict[tp] = None
+            if isinstance(tp, model.FunctionPtrType):
+                self._do_collect_type(tp.as_raw_function())
+            elif isinstance(tp, model.StructOrUnion):
+                if tp.fldtypes is not None and (
+                        tp not in self.ffi._parser._included_declarations):
+                    for name1, tp1, _, _ in tp.enumfields():
+                        self._do_collect_type(self._field_type(tp, name1, tp1))
+            else:
+                for _, x in tp._get_items():
+                    self._do_collect_type(x)
+
+    def _generate(self, step_name):
+        lst = self.ffi._parser._declarations.items()
+        for name, (tp, quals) in sorted(lst):
+            kind, realname = name.split(' ', 1)
+            try:
+                method = getattr(self, '_generate_cpy_%s_%s' % (kind,
+                                                                step_name))
+            except AttributeError:
+                raise VerificationError(
+                    "not implemented in recompile(): %r" % name)
+            try:
+                self._current_quals = quals
+                method(tp, realname)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    # ----------
+
+    ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
+
+    def collect_step_tables(self):
+        # collect the declarations for '_cffi_globals', '_cffi_typenames', etc.
+        self._lsts = {}
+        for step_name in self.ALL_STEPS:
+            self._lsts[step_name] = []
+        self._seen_struct_unions = set()
+        self._generate("ctx")
+        self._add_missing_struct_unions()
+        #
+        for step_name in self.ALL_STEPS:
+            lst = self._lsts[step_name]
+            if step_name != "field":
+                lst.sort(key=lambda entry: entry.name)
+            self._lsts[step_name] = tuple(lst)    # don't change any more
+        #
+        # check for a possible internal inconsistency: _cffi_struct_unions
+        # should have been generated with exactly self._struct_unions
+        lst = self._lsts["struct_union"]
+        for tp, i in self._struct_unions.items():
+            assert i < len(lst)
+            assert lst[i].name == tp.name
+        assert len(lst) == len(self._struct_unions)
+        # same with enums
+        lst = self._lsts["enum"]
+        for tp, i in self._enums.items():
+            assert i < len(lst)
+            assert lst[i].name == tp.name
+        assert len(lst) == len(self._enums)
+
+    # ----------
+
+    def _prnt(self, what=''):
+        self._f.write(what + '\n')
+
+    def write_source_to_f(self, f, preamble):
+        if self.target_is_python:
+            assert preamble is None
+            self.write_py_source_to_f(f)
+        else:
+            assert preamble is not None
+            self.write_c_source_to_f(f, preamble)
+
+    def _rel_readlines(self, filename):
+        g = open(os.path.join(os.path.dirname(__file__), filename), 'r')
+        lines = g.readlines()
+        g.close()
+        return lines
+
+    def write_c_source_to_f(self, f, preamble):
+        self._f = f
+        prnt = self._prnt
+        if self.ffi._embedding is not None:
+            prnt('#define _CFFI_USE_EMBEDDING')
+        #
+        # first the '#include' (actually done by inlining the file's content)
+        lines = self._rel_readlines('_cffi_include.h')
+        i = lines.index('#include "parse_c_type.h"\n')
+        lines[i:i+1] = self._rel_readlines('parse_c_type.h')
+        prnt(''.join(lines))
+        #
+        # if we have ffi._embedding != None, we give it here as a macro
+        # and include an extra file
+        base_module_name = self.module_name.split('.')[-1]
+        if self.ffi._embedding is not None:
+            prnt('#define _CFFI_MODULE_NAME  "%s"' % (self.module_name,))
+            prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {')
+            self._print_string_literal_in_array(self.ffi._embedding)
+            prnt('0 };')
+            prnt('#ifdef PYPY_VERSION')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  _cffi_pypyinit_%s' % (
+                base_module_name,))
+            prnt('#elif PY_MAJOR_VERSION >= 3')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  PyInit_%s' % (
+                base_module_name,))
+            prnt('#else')
+            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  init%s' % (
+                base_module_name,))
+            prnt('#endif')
+            lines = self._rel_readlines('_embedding.h')
+            i = lines.index('#include "_cffi_errors.h"\n')
+            lines[i:i+1] = self._rel_readlines('_cffi_errors.h')
+            prnt(''.join(lines))
+            self.needs_version(VERSION_EMBEDDED)
+        #
+        # then paste the C source given by the user, verbatim.
+        prnt('/************************************************************/')
+        prnt()
+        prnt(preamble)
+        prnt()
+        prnt('/************************************************************/')
+        prnt()
+        #
+        # the declaration of '_cffi_types'
+        prnt('static void *_cffi_types[] = {')
+        typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
+        for i, op in enumerate(self.cffi_types):
+            comment = ''
+            if i in typeindex2type:
+                comment = ' // ' + typeindex2type[i]._get_c_name()
+            prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment))
+        if not self.cffi_types:
+            prnt('  0')
+        prnt('};')
+        prnt()
+        #
+        # call generate_cpy_xxx_decl(), for every xxx found from
+        # ffi._parser._declarations.  This generates all the functions.
+        self._seen_constants = set()
+        self._generate("decl")
+        #
+        # the declaration of '_cffi_globals' and '_cffi_typenames'
+        nums = {}
+        for step_name in self.ALL_STEPS:
+            lst = self._lsts[step_name]
+            nums[step_name] = len(lst)
+            if nums[step_name] > 0:
+                prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (
+                    step_name, step_name))
+                for entry in lst:
+                    prnt(entry.as_c_expr())
+                prnt('};')
+                prnt()
+        #
+        # the declaration of '_cffi_includes'
+        if self.ffi._included_ffis:
+            prnt('static const char * const _cffi_includes[] = {')
+            for ffi_to_include in self.ffi._included_ffis:
+                try:
+                    included_module_name, included_source = (
+                        ffi_to_include._assigned_source[:2])
+                except AttributeError:
+                    raise VerificationError(
+                        "ffi object %r includes %r, but the latter has not "
+                        "been prepared with set_source()" % (
+                            self.ffi, ffi_to_include,))
+                if included_source is None:
+                    raise VerificationError(
+                        "not implemented yet: ffi.include() of a Python-based "
+                        "ffi inside a C-based ffi")
+                prnt('  "%s",' % (included_module_name,))
+            prnt('  NULL')
+            prnt('};')
+            prnt()
+        #
+        # the declaration of '_cffi_type_context'
+        prnt('static const struct _cffi_type_context_s _cffi_type_context = {')
+        prnt('  _cffi_types,')
+        for step_name in self.ALL_STEPS:
+            if nums[step_name] > 0:
+                prnt('  _cffi_%ss,' % step_name)
+            else:
+                prnt('  NULL,  /* no %ss */' % step_name)
+        for step_name in self.ALL_STEPS:
+            if step_name != "field":
+                prnt('  %d,  /* num_%ss */' % (nums[step_name], step_name))
+        if self.ffi._included_ffis:
+            prnt('  _cffi_includes,')
+        else:
+            prnt('  NULL,  /* no includes */')
+        prnt('  %d,  /* num_types */' % (len(self.cffi_types),))
+        flags = 0
+        if self._num_externpy:
+            flags |= 1     # set to mean that we use extern "Python"
+        prnt('  %d,  /* flags */' % flags)
+        prnt('};')
+        prnt()
+        #
+        # the init function
+        prnt('#ifdef __GNUC__')
+        prnt('#  pragma GCC visibility push(default)  /* for -fvisibility= */')
+        prnt('#endif')
+        prnt()
+        prnt('#ifdef PYPY_VERSION')
+        prnt('PyMODINIT_FUNC')
+        prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))
+        prnt('{')
+        if self._num_externpy:
+            prnt('    if (((intptr_t)p[0]) >= 0x0A03) {')
+            prnt('        _cffi_call_python_org = '
+                 '(void(*)(struct _cffi_externpy_s *, char *))p[1];')
+            prnt('    }')
+        prnt('    p[0] = (const void *)0x%x;' % self._version)
+        prnt('    p[1] = &_cffi_type_context;')
+        prnt('#if PY_MAJOR_VERSION >= 3')
+        prnt('    return NULL;')
+        prnt('#endif')
+        prnt('}')
+        # on Windows, distutils insists on putting init_cffi_xyz in
+        # 'export_symbols', so instead of fighting it, just give up and
+        # give it one
+        prnt('#  ifdef _MSC_VER')
+        prnt('     PyMODINIT_FUNC')
+        prnt('#  if PY_MAJOR_VERSION >= 3')
+        prnt('     PyInit_%s(void) { return NULL; }' % (base_module_name,))
+        prnt('#  else')
+        prnt('     init%s(void) { }' % (base_module_name,))
+        prnt('#  endif')
+        prnt('#  endif')
+        prnt('#elif PY_MAJOR_VERSION >= 3')
+        prnt('PyMODINIT_FUNC')
+        prnt('PyInit_%s(void)' % (base_module_name,))
+        prnt('{')
+        prnt('  return _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
+            self.module_name, self._version))
+        prnt('}')
+        prnt('#else')
+        prnt('PyMODINIT_FUNC')
+        prnt('init%s(void)' % (base_module_name,))
+        prnt('{')
+        prnt('  _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
+            self.module_name, self._version))
+        prnt('}')
+        prnt('#endif')
+        prnt()
+        prnt('#ifdef __GNUC__')
+        prnt('#  pragma GCC visibility pop')
+        prnt('#endif')
+        self._version = None
+
+    def _to_py(self, x):
+        if isinstance(x, str):
+            return "b'%s'" % (x,)
+        if isinstance(x, (list, tuple)):
+            rep = [self._to_py(item) for item in x]
+            if len(rep) == 1:
+                rep.append('')
+            return "(%s)" % (','.join(rep),)
+        return x.as_python_expr()  # Py2: unicode unexpected; Py3: bytes unexp.
+
+    def write_py_source_to_f(self, f):
+        self._f = f
+        prnt = self._prnt
+        #
+        # header
+        prnt("# auto-generated file")
+        prnt("import _cffi_backend")
+        #
+        # the 'import' of the included ffis
+        num_includes = len(self.ffi._included_ffis or ())
+        for i in range(num_includes):
+            ffi_to_include = self.ffi._included_ffis[i]
+            try:
+                included_module_name, included_source = (
+                    ffi_to_include._assigned_source[:2])
+            except AttributeError:
+                raise VerificationError(
+                    "ffi object %r includes %r, but the latter has not "
+                    "been prepared with set_source()" % (
+                        self.ffi, ffi_to_include,))
+            if included_source is not None:
+                raise VerificationError(
+                    "not implemented yet: ffi.include() of a C-based "
+                    "ffi inside a Python-based ffi")
+            prnt('from %s import ffi as _ffi%d' % (included_module_name, i))
+        prnt()
+        prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,))
+        prnt("    _version = 0x%x," % (self._version,))
+        self._version = None
+        #
+        # the '_types' keyword argument
+        self.cffi_types = tuple(self.cffi_types)    # don't change any more
+        types_lst = [op.as_python_bytes() for op in self.cffi_types]
+        prnt('    _types = %s,' % (self._to_py(''.join(types_lst)),))
+        typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
+        #
+        # the keyword arguments from ALL_STEPS
+        for step_name in self.ALL_STEPS:
+            lst = self._lsts[step_name]
+            if len(lst) > 0 and step_name != "field":
+                prnt('    _%ss = %s,' % (step_name, self._to_py(lst)))
+        #
+        # the '_includes' keyword argument
+        if num_includes > 0:
+            prnt('    _includes = (%s,),' % (
+                ', '.join(['_ffi%d' % i for i in range(num_includes)]),))
+        #
+        # the footer
+        prnt(')')
+
+    # ----------
+
+    def _gettypenum(self, type):
+        # a KeyError here is a bug.  please report it! :-)
+        return self._typesdict[type]
+
+    def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
+        extraarg = ''
+        if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type():
+            if tp.is_integer_type() and tp.name != '_Bool':
+                converter = '_cffi_to_c_int'
+                extraarg = ', %s' % tp.name
+            elif isinstance(tp, model.UnknownFloatType):
+                # don't check with is_float_type(): it may be a 'long
+                # double' here, and _cffi_to_c_double would loose precision
+                converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
+            else:
+                cname = tp.get_c_name('')
+                converter = '(%s)_cffi_to_c_%s' % (cname,
+                                                   tp.name.replace(' ', '_'))
+                if cname in ('char16_t', 'char32_t'):
+                    self.needs_version(VERSION_CHAR16CHAR32)
+            errvalue = '-1'
+        #
+        elif isinstance(tp, model.PointerType):
+            self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
+                                                    tovar, errcode)
+            return
+        #
+        elif (isinstance(tp, model.StructOrUnionOrEnum) or
+              isinstance(tp, model.BasePrimitiveType)):
+            # a struct (not a struct pointer) as a function argument;
+            # or, a complex (the same code works)
+            self._prnt('  if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
+                      % (tovar, self._gettypenum(tp), fromvar))
+            self._prnt('    %s;' % errcode)
+            return
+        #
+        elif isinstance(tp, model.FunctionPtrType):
+            converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
+            extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
+            errvalue = 'NULL'
+        #
+        else:
+            raise NotImplementedError(tp)
+        #
+        self._prnt('  %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
+        self._prnt('  if (%s == (%s)%s && PyErr_Occurred())' % (
+            tovar, tp.get_c_name(''), errvalue))
+        self._prnt('    %s;' % errcode)
+
+    def _extra_local_variables(self, tp, localvars):
+        if isinstance(tp, model.PointerType):
+            localvars.add('Py_ssize_t datasize')
+
+    def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
+        self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')
+        self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (
+            self._gettypenum(tp), fromvar, tovar))
+        self._prnt('  if (datasize != 0) {')
+        self._prnt('    if (datasize < 0)')
+        self._prnt('      %s;' % errcode)
+        self._prnt('    %s = (%s)alloca((size_t)datasize);' % (
+            tovar, tp.get_c_name('')))
+        self._prnt('    memset((void *)%s, 0, (size_t)datasize);' % (tovar,))
+        self._prnt('    if (_cffi_convert_array_from_object('
+                   '(char *)%s, _cffi_type(%d), %s) < 0)' % (
+            tovar, self._gettypenum(tp), fromvar))
+        self._prnt('      %s;' % errcode)
+        self._prnt('  }')
+
+    def _convert_expr_from_c(self, tp, var, context):
+        if isinstance(tp, model.BasePrimitiveType):
+            if tp.is_integer_type() and tp.name != '_Bool':
+                return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
+            elif isinstance(tp, model.UnknownFloatType):
+                return '_cffi_from_c_double(%s)' % (var,)
+            elif tp.name != 'long double' and not tp.is_complex_type():
+                cname = tp.name.replace(' ', '_')
+                if cname in ('char16_t', 'char32_t'):
+                    self.needs_version(VERSION_CHAR16CHAR32)
+                return '_cffi_from_c_%s(%s)' % (cname, var)
+            else:
+                return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+                    var, self._gettypenum(tp))
+        elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
+            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        elif isinstance(tp, model.ArrayType):
+            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+                var, self._gettypenum(model.PointerType(tp.item)))
+        elif isinstance(tp, model.StructOrUnion):
+            if tp.fldnames is None:
+                raise TypeError("'%s' is used as %s, but is opaque" % (
+                    tp._get_c_name(), context))
+            return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        elif isinstance(tp, model.EnumType):
+            return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        else:
+            raise NotImplementedError(tp)
+
+    # ----------
+    # typedefs
+
+    def _typedef_type(self, tp, name):
+        return self._global_type(tp, "(*(%s *)0)" % (name,))
+
+    def _generate_cpy_typedef_collecttype(self, tp, name):
+        self._do_collect_type(self._typedef_type(tp, name))
+
+    def _generate_cpy_typedef_decl(self, tp, name):
+        pass
+
+    def _typedef_ctx(self, tp, name):
+        type_index = self._typesdict[tp]
+        self._lsts["typename"].append(TypenameExpr(name, type_index))
+
+    def _generate_cpy_typedef_ctx(self, tp, name):
+        tp = self._typedef_type(tp, name)
+        self._typedef_ctx(tp, name)
+        if getattr(tp, "origin", None) == "unknown_type":
+            self._struct_ctx(tp, tp.name, approxname=None)
+        elif isinstance(tp, model.NamedPointerType):
+            self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name,
+                             named_ptr=tp)
+
+    # ----------
+    # function declarations
+
+    def _generate_cpy_function_collecttype(self, tp, name):
+        self._do_collect_type(tp.as_raw_function())
+        if tp.ellipsis and not self.target_is_python:
+            self._do_collect_type(tp)
+
+    def _generate_cpy_function_decl(self, tp, name):
+        assert not self.target_is_python
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            # cannot support vararg functions better than this: check for its
+            # exact type (including the fixed arguments), and build it as a
+            # constant function pointer (no CPython wrapper)
+            self._generate_cpy_constant_decl(tp, name)
+            return
+        prnt = self._prnt
+        numargs = len(tp.args)
+        if numargs == 0:
+            argname = 'noarg'
+        elif numargs == 1:
+            argname = 'arg0'
+        else:
+            argname = 'args'
+        #
+        # ------------------------------
+        # the 'd' version of the function, only for addressof(lib, 'func')
+        arguments = []
+        call_arguments = []
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            arguments.append(type.get_c_name(' x%d' % i, context))
+            call_arguments.append('x%d' % i)
+        repr_arguments = ', '.join(arguments)
+        repr_arguments = repr_arguments or 'void'
+        if tp.abi:
+            abi = tp.abi + ' '
+        else:
+            abi = ''
+        name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)
+        prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
+        prnt('{')
+        call_arguments = ', '.join(call_arguments)
+        result_code = 'return '
+        if isinstance(tp.result, model.VoidType):
+            result_code = ''
+        prnt('  %s%s(%s);' % (result_code, name, call_arguments))
+        prnt('}')
+        #
+        prnt('#ifndef PYPY_VERSION')        # ------------------------------
+        #
+        prnt('static PyObject *')
+        prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
+        prnt('{')
+        #
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            arg = type.get_c_name(' x%d' % i, context)
+            prnt('  %s;' % arg)
+        #
+        localvars = set()
+        for type in tp.args:
+            self._extra_local_variables(type, localvars)
+        for decl in localvars:
+            prnt('  %s;' % (decl,))
+        #
+        if not isinstance(tp.result, model.VoidType):
+            result_code = 'result = '
+            context = 'result of %s' % name
+            result_decl = '  %s;' % tp.result.get_c_name(' result', context)
+            prnt(result_decl)
+        else:
+            result_decl = None
+            result_code = ''
+        #
+        if len(tp.args) > 1:
+            rng = range(len(tp.args))
+            for i in rng:
+                prnt('  PyObject *arg%d;' % i)
+            prnt()
+            prnt('  if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % (
+                name, len(rng), len(rng),
+                ', '.join(['&arg%d' % i for i in rng])))
+            prnt('    return NULL;')
+        prnt()
+        #
+        for i, type in enumerate(tp.args):
+            self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
+                                       'return NULL')
+            prnt()
+        #
+        prnt('  Py_BEGIN_ALLOW_THREADS')
+        prnt('  _cffi_restore_errno();')
+        call_arguments = ['x%d' % i for i in range(len(tp.args))]
+        call_arguments = ', '.join(call_arguments)
+        prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
+        prnt('  _cffi_save_errno();')
+        prnt('  Py_END_ALLOW_THREADS')
+        prnt()
+        #
+        prnt('  (void)self; /* unused */')
+        if numargs == 0:
+            prnt('  (void)noarg; /* unused */')
+        if result_code:
+            prnt('  return %s;' %
+                 self._convert_expr_from_c(tp.result, 'result', 'result type'))
+        else:
+            prnt('  Py_INCREF(Py_None);')
+            prnt('  return Py_None;')
+        prnt('}')
+        #
+        prnt('#else')        # ------------------------------
+        #
+        # the PyPy version: need to replace struct/union arguments with
+        # pointers, and if the result is a struct/union, insert a first
+        # arg that is a pointer to the result.  We also do that for
+        # complex args and return type.
+        def need_indirection(type):
+            return (isinstance(type, model.StructOrUnion) or
+                    (isinstance(type, model.PrimitiveType) and
+                     type.is_complex_type()))
+        difference = False
+        arguments = []
+        call_arguments = []
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            indirection = ''
+            if need_indirection(type):
+                indirection = '*'
+                difference = True
+            arg = type.get_c_name(' %sx%d' % (indirection, i), context)
+            arguments.append(arg)
+            call_arguments.append('%sx%d' % (indirection, i))
+        tp_result = tp.result
+        if need_indirection(tp_result):
+            context = 'result of %s' % name
+            arg = tp_result.get_c_name(' *result', context)
+            arguments.insert(0, arg)
+            tp_result = model.void_type
+            result_decl = None
+            result_code = '*result = '
+            difference = True
+        if difference:
+            repr_arguments = ', '.join(arguments)
+            repr_arguments = repr_arguments or 'void'
+            name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,
+                                                       repr_arguments)
+            prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
+            prnt('{')
+            if result_decl:
+                prnt(result_decl)
+            call_arguments = ', '.join(call_arguments)
+            prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
+            if result_decl:
+                prnt('  return result;')
+            prnt('}')
+        else:
+            prnt('#  define _cffi_f_%s _cffi_d_%s' % (name, name))
+        #
+        prnt('#endif')        # ------------------------------
+        prnt()
+
+    def _generate_cpy_function_ctx(self, tp, name):
+        if tp.ellipsis and not self.target_is_python:
+            self._generate_cpy_constant_ctx(tp, name)
+            return
+        type_index = self._typesdict[tp.as_raw_function()]
+        numargs = len(tp.args)
+        if self.target_is_python:
+            meth_kind = OP_DLOPEN_FUNC
+        elif numargs == 0:
+            meth_kind = OP_CPYTHON_BLTN_N   # 'METH_NOARGS'
+        elif numargs == 1:
+            meth_kind = OP_CPYTHON_BLTN_O   # 'METH_O'
+        else:
+            meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'
+        self._lsts["global"].append(
+            GlobalExpr(name, '_cffi_f_%s' % name,
+                       CffiOp(meth_kind, type_index),
+                       size='_cffi_d_%s' % name))
+
+    # ----------
+    # named structs or unions
+
+    def _field_type(self, tp_struct, field_name, tp_field):
+        if isinstance(tp_field, model.ArrayType):
+            actual_length = tp_field.length
+            if actual_length == '...':
+                ptr_struct_name = tp_struct.get_c_name('*')
+                actual_length = '_cffi_array_len(((%s)0)->%s)' % (
+                    ptr_struct_name, field_name)
+            tp_item = self._field_type(tp_struct, '%s[0]' % field_name,
+                                       tp_field.item)
+            tp_field = model.ArrayType(tp_item, actual_length)
+        return tp_field
+
+    def _struct_collecttype(self, tp):
+        self._do_collect_type(tp)
+        if self.target_is_python:
+            # also requires nested anon struct/unions in ABI mode, recursively
+            for fldtype in tp.anonymous_struct_fields():
+                self._struct_collecttype(fldtype)
+
+    def _struct_decl(self, tp, cname, approxname):
+        if tp.fldtypes is None:
+            return
+        prnt = self._prnt
+        checkfuncname = '_cffi_checkfld_%s' % (approxname,)
+        prnt('_CFFI_UNUSED_FN')
+        prnt('static void %s(%s *p)' % (checkfuncname, cname))
+        prnt('{')
+        prnt('  /* only to generate compile-time warnings or errors */')
+        prnt('  (void)p;')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            try:
+                if ftype.is_integer_type() or fbitsize >= 0:
+                    # accept all integers, but complain on float or double
+                    prnt("  (void)((p->%s) | 0);  /* check that '%s.%s' is "
+                         "an integer */" % (fname, cname, fname))
+                    continue
+                # only accept exactly the type declared, except that '[]'
+                # is interpreted as a '*' and so will match any array length.
+                # (It would also match '*', but that's harder to detect...)
+                while (isinstance(ftype, model.ArrayType)
+                       and (ftype.length is None or ftype.length == '...')):
+                    ftype = ftype.item
+                    fname = fname + '[0]'
+                prnt('  { %s = &p->%s; (void)tmp; }' % (
+                    ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
+                    fname))
+            except VerificationError as e:
+                prnt('  /* %s */' % str(e))   # cannot verify it, ignore
+        prnt('}')
+        prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))
+        prnt()
+
+    def _struct_ctx(self, tp, cname, approxname, named_ptr=None):
+        type_index = self._typesdict[tp]
+        reason_for_not_expanding = None
+        flags = []
+        if isinstance(tp, model.UnionType):
+            flags.append("_CFFI_F_UNION")
+        if tp.fldtypes is None:
+            flags.append("_CFFI_F_OPAQUE")
+            reason_for_not_expanding = "opaque"
+        if (tp not in self.ffi._parser._included_declarations and
+                (named_ptr is None or
+                 named_ptr not in self.ffi._parser._included_declarations)):
+            if tp.fldtypes is None:
+                pass    # opaque
+            elif tp.partial or any(tp.anonymous_struct_fields()):
+                pass    # field layout obtained silently from the C compiler
+            else:
+                flags.append("_CFFI_F_CHECK_FIELDS")
+            if tp.packed:
+                if tp.packed > 1:
+                    raise NotImplementedError(
+                        "%r is declared with 'pack=%r'; only 0 or 1 are "
+                        "supported in API mode (try to use \"...;\", which "
+                        "does not require a 'pack' declaration)" %
+                        (tp, tp.packed))
+                flags.append("_CFFI_F_PACKED")
+        else:
+            flags.append("_CFFI_F_EXTERNAL")
+            reason_for_not_expanding = "external"
+        flags = '|'.join(flags) or '0'
+        c_fields = []
+        if reason_for_not_expanding is None:
+            expand_anonymous_struct_union = not self.target_is_python
+            enumfields = list(tp.enumfields(expand_anonymous_struct_union))
+            for fldname, fldtype, fbitsize, fqual in enumfields:
+                fldtype = self._field_type(tp, fldname, fldtype)
+                self._check_not_opaque(fldtype,
+                                       "field '%s.%s'" % (tp.name, fldname))
+                # cname is None for _add_missing_struct_unions() only
+                op = OP_NOOP
+                if fbitsize >= 0:
+                    op = OP_BITFIELD
+                    size = '%d /* bits */' % fbitsize
+                elif cname is None or (
+                        isinstance(fldtype, model.ArrayType) and
+                        fldtype.length is None):
+                    size = '(size_t)-1'
+                else:
+                    size = 'sizeof(((%s)0)->%s)' % (
+                        tp.get_c_name('*') if named_ptr is None
+                                           else named_ptr.name,
+                        fldname)
+                if cname is None or fbitsize >= 0:
+                    offset = '(size_t)-1'
+                elif named_ptr is not None:
+                    offset = '((char *)&((%s)0)->%s) - (char *)0' % (
+                        named_ptr.name, fldname)
+                else:
+                    offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname)
+                c_fields.append(
+                    FieldExpr(fldname, offset, size, fbitsize,
+                              CffiOp(op, self._typesdict[fldtype])))
+            first_field_index = len(self._lsts["field"])
+            self._lsts["field"].extend(c_fields)
+            #
+            if cname is None:  # unknown name, for _add_missing_struct_unions
+                size = '(size_t)-2'
+                align = -2
+                comment = "unnamed"
+            else:
+                if named_ptr is not None:
+                    size = 'sizeof(*(%s)0)' % (named_ptr.name,)
+                    align = '-1 /* unknown alignment */'
+                else:
+                    size = 'sizeof(%s)' % (cname,)
+                    align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,)
+                comment = None
+        else:
+            size = '(size_t)-1'
+            align = -1
+            first_field_index = -1
+            comment = reason_for_not_expanding
+        self._lsts["struct_union"].append(
+            StructUnionExpr(tp.name, type_index, flags, size, align, comment,
+                            first_field_index, c_fields))
+        self._seen_struct_unions.add(tp)
+
+    def _check_not_opaque(self, tp, location):
+        while isinstance(tp, model.ArrayType):
+            tp = tp.item
+        if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None:
+            raise TypeError(
+                "%s is of an opaque type (not declared in cdef())" % location)
+
+    def _add_missing_struct_unions(self):
+        # not very nice, but some struct declarations might be missing
+        # because they don't have any known C name.  Check that they are
+        # not partial (we can't complete or verify them!) and emit them
+        # anonymously.
+        lst = list(self._struct_unions.items())
+        lst.sort(key=lambda tp_order: tp_order[1])
+        for tp, order in lst:
+            if tp not in self._seen_struct_unions:
+                if tp.partial:
+                    raise NotImplementedError("internal inconsistency: %r is "
+                                              "partial but was not seen at "
+                                              "this point" % (tp,))
+                if tp.name.startswith('$') and tp.name[1:].isdigit():
+                    approxname = tp.name[1:]
+                elif tp.name == '_IO_FILE' and tp.forcename == 'FILE':
+                    approxname = 'FILE'
+                    self._typedef_ctx(tp, 'FILE')
+                else:
+                    raise NotImplementedError("internal inconsistency: %r" %
+                                              (tp,))
+                self._struct_ctx(tp, None, approxname)
+
+    def _generate_cpy_struct_collecttype(self, tp, name):
+        self._struct_collecttype(tp)
+    _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype
+
+    def _struct_names(self, tp):
+        cname = tp.get_c_name('')
+        if ' ' in cname:
+            return cname, cname.replace(' ', '_')
+        else:
+            return cname, '_' + cname
+
+    def _generate_cpy_struct_decl(self, tp, name):
+        self._struct_decl(tp, *self._struct_names(tp))
+    _generate_cpy_union_decl = _generate_cpy_struct_decl
+
+    def _generate_cpy_struct_ctx(self, tp, name):
+        self._struct_ctx(tp, *self._struct_names(tp))
+    _generate_cpy_union_ctx = _generate_cpy_struct_ctx
+
+    # ----------
+    # 'anonymous' declarations.  These are produced for anonymous structs
+    # or unions; the 'name' is obtained by a typedef.
+
+    def _generate_cpy_anonymous_collecttype(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_collecttype(tp, name)
+        else:
+            self._struct_collecttype(tp)
+
+    def _generate_cpy_anonymous_decl(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_decl(tp)
+        else:
+            self._struct_decl(tp, name, 'typedef_' + name)
+
+    def _generate_cpy_anonymous_ctx(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._enum_ctx(tp, name)
+        else:
+            self._struct_ctx(tp, name, 'typedef_' + name)
+
+    # ----------
+    # constants, declared with "static const ..."
+
+    def _generate_cpy_const(self, is_int, name, tp=None, category='const',
+                            check_value=None):
+        if (category, name) in self._seen_constants:
+            raise VerificationError(
+                "duplicate declaration of %s '%s'" % (category, name))
+        self._seen_constants.add((category, name))
+        #
+        prnt = self._prnt
+        funcname = '_cffi_%s_%s' % (category, name)
+        if is_int:
+            prnt('static int %s(unsigned long long *o)' % funcname)
+            prnt('{')
+            prnt('  int n = (%s) <= 0;' % (name,))
+            prnt('  *o = (unsigned long long)((%s) | 0);'
+                 '  /* check that %s is an integer */' % (name, name))
+            if check_value is not None:
+                if check_value > 0:
+                    check_value = '%dU' % (check_value,)
+                prnt('  if (!_cffi_check_int(*o, n, %s))' % (check_value,))
+                prnt('    n |= 2;')
+            prnt('  return n;')
+            prnt('}')
+        else:
+            assert check_value is None
+            prnt('static void %s(char *o)' % funcname)
+            prnt('{')
+            prnt('  *(%s)o = %s;' % (tp.get_c_name('*'), name))
+            prnt('}')
+        prnt()
+
+    def _generate_cpy_constant_collecttype(self, tp, name):
+        is_int = tp.is_integer_type()
+        if not is_int or self.target_is_python:
+            self._do_collect_type(tp)
+
+    def _generate_cpy_constant_decl(self, tp, name):
+        is_int = tp.is_integer_type()
+        self._generate_cpy_const(is_int, name, tp)
+
+    def _generate_cpy_constant_ctx(self, tp, name):
+        if not self.target_is_python and tp.is_integer_type():
+            type_op = CffiOp(OP_CONSTANT_INT, -1)
+        else:
+            if self.target_is_python:
+                const_kind = OP_DLOPEN_CONST
+            else:
+                const_kind = OP_CONSTANT
+            type_index = self._typesdict[tp]
+            type_op = CffiOp(const_kind, type_index)
+        self._lsts["global"].append(
+            GlobalExpr(name, '_cffi_const_%s' % name, type_op))
+
+    # ----------
+    # enums
+
+    def _generate_cpy_enum_collecttype(self, tp, name):
+        self._do_collect_type(tp)
+
+    def _generate_cpy_enum_decl(self, tp, name=None):
+        for enumerator in tp.enumerators:
+            self._generate_cpy_const(True, enumerator)
+
+    def _enum_ctx(self, tp, cname):
+        type_index = self._typesdict[tp]
+        type_op = CffiOp(OP_ENUM, -1)
+        if self.target_is_python:
+            tp.check_not_partial()
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            self._lsts["global"].append(
+                GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op,
+                           check_value=enumvalue))
+        #
+        if cname is not None and '$' not in cname and not self.target_is_python:
+            size = "sizeof(%s)" % cname
+            signed = "((%s)-1) <= 0" % cname
+        else:
+            basetp = tp.build_baseinttype(self.ffi, [])
+            size = self.ffi.sizeof(basetp)
+            signed = int(int(self.ffi.cast(basetp, -1)) < 0)
+        allenums = ",".join(tp.enumerators)
+        self._lsts["enum"].append(
+            EnumExpr(tp.name, type_index, size, signed, allenums))
+
+    def _generate_cpy_enum_ctx(self, tp, name):
+        self._enum_ctx(tp, tp._get_c_name())
+
+    # ----------
+    # macros: for now only for integers
+
+    def _generate_cpy_macro_collecttype(self, tp, name):
+        pass
+
+    def _generate_cpy_macro_decl(self, tp, name):
+        if tp == '...':
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        self._generate_cpy_const(True, name, check_value=check_value)
+
+    def _generate_cpy_macro_ctx(self, tp, name):
+        if tp == '...':
+            if self.target_is_python:
+                raise VerificationError(
+                    "cannot use the syntax '...' in '#define %s ...' when "
+                    "using the ABI mode" % (name,))
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        type_op = CffiOp(OP_CONSTANT_INT, -1)
+        self._lsts["global"].append(
+            GlobalExpr(name, '_cffi_const_%s' % name, type_op,
+                       check_value=check_value))
+
+    # ----------
+    # global variables
+
+    def _global_type(self, tp, global_name):
+        if isinstance(tp, model.ArrayType):
+            actual_length = tp.length
+            if actual_length == '...':
+                actual_length = '_cffi_array_len(%s)' % (global_name,)
+            tp_item = self._global_type(tp.item, '%s[0]' % global_name)
+            tp = model.ArrayType(tp_item, actual_length)
+        return tp
+
+    def _generate_cpy_variable_collecttype(self, tp, name):
+        self._do_collect_type(self._global_type(tp, name))
+
+    def _generate_cpy_variable_decl(self, tp, name):
+        prnt = self._prnt
+        tp = self._global_type(tp, name)
+        if isinstance(tp, model.ArrayType) and tp.length is None:
+            tp = tp.item
+            ampersand = ''
+        else:
+            ampersand = '&'
+        # This code assumes that casts from "tp *" to "void *" is a
+        # no-op, i.e. a function that returns a "tp *" can be called
+        # as if it returned a "void *".  This should be generally true
+        # on any modern machine.  The only exception to that rule (on
+        # uncommon architectures, and as far as I can tell) might be
+        # if 'tp' were a function type, but that is not possible here.
+        # (If 'tp' is a function _pointer_ type, then casts from "fn_t
+        # **" to "void *" are again no-ops, as far as I can tell.)
+        decl = '*_cffi_var_%s(void)' % (name,)
+        prnt('static ' + tp.get_c_name(decl, quals=self._current_quals))
+        prnt('{')
+        prnt('  return %s(%s);' % (ampersand, name))
+        prnt('}')
+        prnt()
+
+    def _generate_cpy_variable_ctx(self, tp, name):
+        tp = self._global_type(tp, name)
+        type_index = self._typesdict[tp]
+        if self.target_is_python:
+            op = OP_GLOBAL_VAR
+        else:
+            op = OP_GLOBAL_VAR_F
+        self._lsts["global"].append(
+            GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index)))
+
+    # ----------
+    # extern "Python"
+
+    def _generate_cpy_extern_python_collecttype(self, tp, name):
+        assert isinstance(tp, model.FunctionPtrType)
+        self._do_collect_type(tp)
+    _generate_cpy_dllexport_python_collecttype = \
+      _generate_cpy_extern_python_plus_c_collecttype = \
+      _generate_cpy_extern_python_collecttype
+
+    def _extern_python_decl(self, tp, name, tag_and_space):
+        prnt = self._prnt
+        if isinstance(tp.result, model.VoidType):
+            size_of_result = '0'
+        else:
+            context = 'result of %s' % name
+            size_of_result = '(int)sizeof(%s)' % (
+                tp.result.get_c_name('', context),)
+        prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name)
+        prnt('  { "%s.%s", %s };' % (self.module_name, name, size_of_result))
+        prnt()
+        #
+        arguments = []
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            arg = type.get_c_name(' a%d' % i, context)
+            arguments.append(arg)
+        #
+        repr_arguments = ', '.join(arguments)
+        repr_arguments = repr_arguments or 'void'
+        name_and_arguments = '%s(%s)' % (name, repr_arguments)
+        if tp.abi == "__stdcall":
+            name_and_arguments = '_cffi_stdcall ' + name_and_arguments
+        #
+        def may_need_128_bits(tp):
+            return (isinstance(tp, model.PrimitiveType) and
+                    tp.name == 'long double')
+        #
+        size_of_a = max(len(tp.args)*8, 8)
+        if may_need_128_bits(tp.result):
+            size_of_a = max(size_of_a, 16)
+        if isinstance(tp.result, model.StructOrUnion):
+            size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % (
+                tp.result.get_c_name(''), size_of_a,
+                tp.result.get_c_name(''), size_of_a)
+        prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments)))
+        prnt('{')
+        prnt('  char a[%s];' % size_of_a)
+        prnt('  char *p = a;')
+        for i, type in enumerate(tp.args):
+            arg = 'a%d' % i
+            if (isinstance(type, model.StructOrUnion) or
+                    may_need_128_bits(type)):
+                arg = '&' + arg
+                type = model.PointerType(type)
+            prnt('  *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg))
+        prnt('  _cffi_call_python(&_cffi_externpy__%s, p);' % name)
+        if not isinstance(tp.result, model.VoidType):
+            prnt('  return *(%s)p;' % (tp.result.get_c_name('*'),))
+        prnt('}')
+        prnt()
+        self._num_externpy += 1
+
+    def _generate_cpy_extern_python_decl(self, tp, name):
+        self._extern_python_decl(tp, name, 'static ')
+
+    def _generate_cpy_dllexport_python_decl(self, tp, name):
+        self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ')
+
+    def _generate_cpy_extern_python_plus_c_decl(self, tp, name):
+        self._extern_python_decl(tp, name, '')
+
+    def _generate_cpy_extern_python_ctx(self, tp, name):
+        if self.target_is_python:
+            raise VerificationError(
+                "cannot use 'extern \"Python\"' in the ABI mode")
+        if tp.ellipsis:
+            raise NotImplementedError("a vararg function is extern \"Python\"")
+        type_index = self._typesdict[tp]
+        type_op = CffiOp(OP_EXTERN_PYTHON, type_index)
+        self._lsts["global"].append(
+            GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name))
+
+    _generate_cpy_dllexport_python_ctx = \
+      _generate_cpy_extern_python_plus_c_ctx = \
+      _generate_cpy_extern_python_ctx
+
+    def _print_string_literal_in_array(self, s):
+        prnt = self._prnt
+        prnt('// # NB. this is not a string because of a size limit in MSVC')
+        for line in s.splitlines(True):
+            prnt(('// ' + line).rstrip())
+            printed_line = ''
+            for c in line:
+                if len(printed_line) >= 76:
+                    prnt(printed_line)
+                    printed_line = ''
+                printed_line += '%d,' % (ord(c),)
+            prnt(printed_line)
+
+    # ----------
+    # emitting the opcodes for individual types
+
+    def _emit_bytecode_VoidType(self, tp, index):
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID)
+
+    def _emit_bytecode_PrimitiveType(self, tp, index):
+        prim_index = PRIMITIVE_TO_INDEX[tp.name]
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index)
+
+    def _emit_bytecode_UnknownIntegerType(self, tp, index):
+        s = ('_cffi_prim_int(sizeof(%s), (\n'
+             '           ((%s)-1) | 0 /* check that %s is an integer type */\n'
+             '         ) <= 0)' % (tp.name, tp.name, tp.name))
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
+
+    def _emit_bytecode_UnknownFloatType(self, tp, index):
+        s = ('_cffi_prim_float(sizeof(%s) *\n'
+             '           (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n'
+             '         )' % (tp.name, tp.name))
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
+
+    def _emit_bytecode_RawFunctionType(self, tp, index):
+        self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result])
+        index += 1
+        for tp1 in tp.args:
+            realindex = self._typesdict[tp1]
+            if index != realindex:
+                if isinstance(tp1, model.PrimitiveType):
+                    self._emit_bytecode_PrimitiveType(tp1, index)
+                else:
+                    self.cffi_types[index] = CffiOp(OP_NOOP, realindex)
+            index += 1
+        flags = int(tp.ellipsis)
+        if tp.abi is not None:
+            if tp.abi == '__stdcall':
+                flags |= 2
+            else:
+                raise NotImplementedError("abi=%r" % (tp.abi,))
+        self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags)
+
+    def _emit_bytecode_PointerType(self, tp, index):
+        self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype])
+
+    _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType
+    _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType
+
+    def _emit_bytecode_FunctionPtrType(self, tp, index):
+        raw = tp.as_raw_function()
+        self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw])
+
+    def _emit_bytecode_ArrayType(self, tp, index):
+        item_index = self._typesdict[tp.item]
+        if tp.length is None:
+            self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index)
+        elif tp.length == '...':
+            raise VerificationError(
+                "type %s badly placed: the '...' array length can only be "
+                "used on global arrays or on fields of structures" % (
+                    str(tp).replace('/*...*/', '...'),))
+        else:
+            assert self.cffi_types[index + 1] == 'LEN'
+            self.cffi_types[index] = CffiOp(OP_ARRAY, item_index)
+            self.cffi_types[index + 1] = CffiOp(None, str(tp.length))
+
+    def _emit_bytecode_StructType(self, tp, index):
+        struct_index = self._struct_unions[tp]
+        self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index)
+    _emit_bytecode_UnionType = _emit_bytecode_StructType
+
+    def _emit_bytecode_EnumType(self, tp, index):
+        enum_index = self._enums[tp]
+        self.cffi_types[index] = CffiOp(OP_ENUM, enum_index)
+
+
+if sys.version_info >= (3,):
+    NativeIO = io.StringIO
+else:
+    class NativeIO(io.BytesIO):
+        def write(self, s):
+            if isinstance(s, unicode):
+                s = s.encode('ascii')
+            super(NativeIO, self).write(s)
+
+def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose):
+    if verbose:
+        print("generating %s" % (target_file,))
+    recompiler = Recompiler(ffi, module_name,
+                            target_is_python=(preamble is None))
+    recompiler.collect_type_table()
+    recompiler.collect_step_tables()
+    f = NativeIO()
+    recompiler.write_source_to_f(f, preamble)
+    output = f.getvalue()
+    try:
+        with open(target_file, 'r') as f1:
+            if f1.read(len(output) + 1) != output:
+                raise IOError
+        if verbose:
+            print("(already up-to-date)")
+        return False     # already up-to-date
+    except IOError:
+        tmp_file = '%s.~%d' % (target_file, os.getpid())
+        with open(tmp_file, 'w') as f1:
+            f1.write(output)
+        try:
+            os.rename(tmp_file, target_file)
+        except OSError:
+            os.unlink(target_file)
+            os.rename(tmp_file, target_file)
+        return True
+
+def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False):
+    assert preamble is not None
+    return _make_c_or_py_source(ffi, module_name, preamble, target_c_file,
+                                verbose)
+
+def make_py_source(ffi, module_name, target_py_file, verbose=False):
+    return _make_c_or_py_source(ffi, module_name, None, target_py_file,
+                                verbose)
+
+def _modname_to_file(outputdir, modname, extension):
+    parts = modname.split('.')
+    try:
+        os.makedirs(os.path.join(outputdir, *parts[:-1]))
+    except OSError:
+        pass
+    parts[-1] += extension
+    return os.path.join(outputdir, *parts), parts
+
+
+# Aaargh.  Distutils is not tested at all for the purpose of compiling
+# DLLs that are not extension modules.  Here are some hacks to work
+# around that, in the _patch_for_*() functions...
+
+def _patch_meth(patchlist, cls, name, new_meth):
+    old = getattr(cls, name)
+    patchlist.append((cls, name, old))
+    setattr(cls, name, new_meth)
+    return old
+
+def _unpatch_meths(patchlist):
+    for cls, name, old_meth in reversed(patchlist):
+        setattr(cls, name, old_meth)
+
+def _patch_for_embedding(patchlist):
+    if sys.platform == 'win32':
+        # we must not remove the manifest when building for embedding!
+        from distutils.msvc9compiler import MSVCCompiler
+        _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref',
+                    lambda self, manifest_file: manifest_file)
+
+    if sys.platform == 'darwin':
+        # we must not make a '-bundle', but a '-dynamiclib' instead
+        from distutils.ccompiler import CCompiler
+        def my_link_shared_object(self, *args, **kwds):
+            if '-bundle' in self.linker_so:
+                self.linker_so = list(self.linker_so)
+                i = self.linker_so.index('-bundle')
+                self.linker_so[i] = '-dynamiclib'
+            return old_link_shared_object(self, *args, **kwds)
+        old_link_shared_object = _patch_meth(patchlist, CCompiler,
+                                             'link_shared_object',
+                                             my_link_shared_object)
+
+def _patch_for_target(patchlist, target):
+    from distutils.command.build_ext import build_ext
+    # if 'target' is different from '*', we need to patch some internal
+    # method to just return this 'target' value, instead of having it
+    # built from module_name
+    if target.endswith('.*'):
+        target = target[:-2]
+        if sys.platform == 'win32':
+            target += '.dll'
+        elif sys.platform == 'darwin':
+            target += '.dylib'
+        else:
+            target += '.so'
+    _patch_meth(patchlist, build_ext, 'get_ext_filename',
+                lambda self, ext_name: target)
+
+
+def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
+              c_file=None, source_extension='.c', extradir=None,
+              compiler_verbose=1, target=None, debug=None, **kwds):
+    if not isinstance(module_name, str):
+        module_name = module_name.encode('ascii')
+    if ffi._windows_unicode:
+        ffi._apply_windows_unicode(kwds)
+    if preamble is not None:
+        embedding = (ffi._embedding is not None)
+        if embedding:
+            ffi._apply_embedding_fix(kwds)
+        if c_file is None:
+            c_file, parts = _modname_to_file(tmpdir, module_name,
+                                             source_extension)
+            if extradir:
+                parts = [extradir] + parts
+            ext_c_file = os.path.join(*parts)
+        else:
+            ext_c_file = c_file
+        #
+        if target is None:
+            if embedding:
+                target = '%s.*' % module_name
+            else:
+                target = '*'
+        #
+        ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
+        updated = make_c_source(ffi, module_name, preamble, c_file,
+                                verbose=compiler_verbose)
+        if call_c_compiler:
+            patchlist = []
+            cwd = os.getcwd()
+            try:
+                if embedding:
+                    _patch_for_embedding(patchlist)
+                if target != '*':
+                    _patch_for_target(patchlist, target)
+                if compiler_verbose:
+                    if tmpdir == '.':
+                        msg = 'the current directory is'
+                    else:
+                        msg = 'setting the current directory to'
+                    print('%s %r' % (msg, os.path.abspath(tmpdir)))
+                os.chdir(tmpdir)
+                outputfilename = ffiplatform.compile('.', ext,
+                                                     compiler_verbose, debug)
+            finally:
+                os.chdir(cwd)
+                _unpatch_meths(patchlist)
+            return outputfilename
+        else:
+            return ext, updated
+    else:
+        if c_file is None:
+            c_file, _ = _modname_to_file(tmpdir, module_name, '.py')
+        updated = make_py_source(ffi, module_name, c_file,
+                                 verbose=compiler_verbose)
+        if call_c_compiler:
+            return c_file
+        else:
+            return None, updated
+
diff --git a/cffi/setuptools_ext.py b/cffi/setuptools_ext.py
new file mode 100644
index 0000000..df5a518
--- /dev/null
+++ b/cffi/setuptools_ext.py
@@ -0,0 +1,217 @@
+import os
+import sys
+
+try:
+    basestring
+except NameError:
+    # Python 3.x
+    basestring = str
+
+def error(msg):
+    from distutils.errors import DistutilsSetupError
+    raise DistutilsSetupError(msg)
+
+
+def execfile(filename, glob):
+    # We use execfile() (here rewritten for Python 3) instead of
+    # __import__() to load the build script.  The problem with
+    # a normal import is that in some packages, the intermediate
+    # __init__.py files may already try to import the file that
+    # we are generating.
+    with open(filename) as f:
+        src = f.read()
+    src += '\n'      # Python 2.6 compatibility
+    code = compile(src, filename, 'exec')
+    exec(code, glob, glob)
+
+
+def add_cffi_module(dist, mod_spec):
+    from cffi.api import FFI
+
+    if not isinstance(mod_spec, basestring):
+        error("argument to 'cffi_modules=...' must be a str or a list of str,"
+              " not %r" % (type(mod_spec).__name__,))
+    mod_spec = str(mod_spec)
+    try:
+        build_file_name, ffi_var_name = mod_spec.split(':')
+    except ValueError:
+        error("%r must be of the form 'path/build.py:ffi_variable'" %
+              (mod_spec,))
+    if not os.path.exists(build_file_name):
+        ext = ''
+        rewritten = build_file_name.replace('.', '/') + '.py'
+        if os.path.exists(rewritten):
+            ext = ' (rewrite cffi_modules to [%r])' % (
+                rewritten + ':' + ffi_var_name,)
+        error("%r does not name an existing file%s" % (build_file_name, ext))
+
+    mod_vars = {'__name__': '__cffi__', '__file__': build_file_name}
+    execfile(build_file_name, mod_vars)
+
+    try:
+        ffi = mod_vars[ffi_var_name]
+    except KeyError:
+        error("%r: object %r not found in module" % (mod_spec,
+                                                     ffi_var_name))
+    if not isinstance(ffi, FFI):
+        ffi = ffi()      # maybe it's a function instead of directly an ffi
+    if not isinstance(ffi, FFI):
+        error("%r is not an FFI instance (got %r)" % (mod_spec,
+                                                      type(ffi).__name__))
+    if not hasattr(ffi, '_assigned_source'):
+        error("%r: the set_source() method was not called" % (mod_spec,))
+    module_name, source, source_extension, kwds = ffi._assigned_source
+    if ffi._windows_unicode:
+        kwds = kwds.copy()
+        ffi._apply_windows_unicode(kwds)
+
+    if source is None:
+        _add_py_module(dist, ffi, module_name)
+    else:
+        _add_c_module(dist, ffi, module_name, source, source_extension, kwds)
+
+def _set_py_limited_api(Extension, kwds):
+    """
+    Add py_limited_api to kwds if setuptools >= 26 is in use.
+    Do not alter the setting if it already exists.
+    Setuptools takes care of ignoring the flag on Python 2 and PyPy.
+
+    CPython itself should ignore the flag in a debugging version
+    (by not listing .abi3.so in the extensions it supports), but
+    it doesn't so far, creating troubles.  That's why we check
+    for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent
+    of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401)
+
+    On Windows, with CPython <= 3.4, it's better not to use py_limited_api
+    because virtualenv *still* doesn't copy PYTHON3.DLL on these versions.
+    For now we'll skip py_limited_api on all Windows versions to avoid an
+    inconsistent mess.
+    """
+    if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount')
+            and sys.platform != 'win32'):
+        import setuptools
+        try:
+            setuptools_major_version = int(setuptools.__version__.partition('.')[0])
+            if setuptools_major_version >= 26:
+                kwds['py_limited_api'] = True
+        except ValueError:  # certain development versions of setuptools
+            # If we don't know the version number of setuptools, we
+            # try to set 'py_limited_api' anyway.  At worst, we get a
+            # warning.
+            kwds['py_limited_api'] = True
+    return kwds
+
+def _add_c_module(dist, ffi, module_name, source, source_extension, kwds):
+    from distutils.core import Extension
+    # We are a setuptools extension. Need this build_ext for py_limited_api.
+    from setuptools.command.build_ext import build_ext
+    from distutils.dir_util import mkpath
+    from distutils import log
+    from cffi import recompiler
+
+    allsources = ['$PLACEHOLDER']
+    allsources.extend(kwds.pop('sources', []))
+    kwds = _set_py_limited_api(Extension, kwds)
+    ext = Extension(name=module_name, sources=allsources, **kwds)
+
+    def make_mod(tmpdir, pre_run=None):
+        c_file = os.path.join(tmpdir, module_name + source_extension)
+        log.info("generating cffi module %r" % c_file)
+        mkpath(tmpdir)
+        # a setuptools-only, API-only hook: called with the "ext" and "ffi"
+        # arguments just before we turn the ffi into C code.  To use it,
+        # subclass the 'distutils.command.build_ext.build_ext' class and
+        # add a method 'def pre_run(self, ext, ffi)'.
+        if pre_run is not None:
+            pre_run(ext, ffi)
+        updated = recompiler.make_c_source(ffi, module_name, source, c_file)
+        if not updated:
+            log.info("already up-to-date")
+        return c_file
+
+    if dist.ext_modules is None:
+        dist.ext_modules = []
+    dist.ext_modules.append(ext)
+
+    base_class = dist.cmdclass.get('build_ext', build_ext)
+    class build_ext_make_mod(base_class):
+        def run(self):
+            if ext.sources[0] == '$PLACEHOLDER':
+                pre_run = getattr(self, 'pre_run', None)
+                ext.sources[0] = make_mod(self.build_temp, pre_run)
+            base_class.run(self)
+    dist.cmdclass['build_ext'] = build_ext_make_mod
+    # NB. multiple runs here will create multiple 'build_ext_make_mod'
+    # classes.  Even in this case the 'build_ext' command should be
+    # run once; but just in case, the logic above does nothing if
+    # called again.
+
+
+def _add_py_module(dist, ffi, module_name):
+    from distutils.dir_util import mkpath
+    from setuptools.command.build_py import build_py
+    from setuptools.command.build_ext import build_ext
+    from distutils import log
+    from cffi import recompiler
+
+    def generate_mod(py_file):
+        log.info("generating cffi module %r" % py_file)
+        mkpath(os.path.dirname(py_file))
+        updated = recompiler.make_py_source(ffi, module_name, py_file)
+        if not updated:
+            log.info("already up-to-date")
+
+    base_class = dist.cmdclass.get('build_py', build_py)
+    class build_py_make_mod(base_class):
+        def run(self):
+            base_class.run(self)
+            module_path = module_name.split('.')
+            module_path[-1] += '.py'
+            generate_mod(os.path.join(self.build_lib, *module_path))
+        def get_source_files(self):
+            # This is called from 'setup.py sdist' only.  Exclude
+            # the generate .py module in this case.
+            saved_py_modules = self.py_modules
+            try:
+                if saved_py_modules:
+                    self.py_modules = [m for m in saved_py_modules
+                                         if m != module_name]
+                return base_class.get_source_files(self)
+            finally:
+                self.py_modules = saved_py_modules
+    dist.cmdclass['build_py'] = build_py_make_mod
+
+    # distutils and setuptools have no notion I could find of a
+    # generated python module.  If we don't add module_name to
+    # dist.py_modules, then things mostly work but there are some
+    # combination of options (--root and --record) that will miss
+    # the module.  So we add it here, which gives a few apparently
+    # harmless warnings about not finding the file outside the
+    # build directory.
+    # Then we need to hack more in get_source_files(); see above.
+    if dist.py_modules is None:
+        dist.py_modules = []
+    dist.py_modules.append(module_name)
+
+    # the following is only for "build_ext -i"
+    base_class_2 = dist.cmdclass.get('build_ext', build_ext)
+    class build_ext_make_mod(base_class_2):
+        def run(self):
+            base_class_2.run(self)
+            if self.inplace:
+                # from get_ext_fullpath() in distutils/command/build_ext.py
+                module_path = module_name.split('.')
+                package = '.'.join(module_path[:-1])
+                build_py = self.get_finalized_command('build_py')
+                package_dir = build_py.get_package_dir(package)
+                file_name = module_path[-1] + '.py'
+                generate_mod(os.path.join(package_dir, file_name))
+    dist.cmdclass['build_ext'] = build_ext_make_mod
+
+def cffi_modules(dist, attr, value):
+    assert attr == 'cffi_modules'
+    if isinstance(value, basestring):
+        value = [value]
+
+    for cffi_module in value:
+        add_cffi_module(dist, cffi_module)
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
new file mode 100644
index 0000000..536f11f
--- /dev/null
+++ b/cffi/vengine_cpy.py
@@ -0,0 +1,1015 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
+import sys, imp
+from . import model
+from .error import VerificationError
+
+
+class VCPythonEngine(object):
+    _class_key = 'x'
+    _gen_python_module = True
+
+    def __init__(self, verifier):
+        self.verifier = verifier
+        self.ffi = verifier.ffi
+        self._struct_pending_verification = {}
+        self._types_of_builtin_functions = {}
+
+    def patch_extension_kwds(self, kwds):
+        pass
+
+    def find_module(self, module_name, path, so_suffixes):
+        try:
+            f, filename, descr = imp.find_module(module_name, path)
+        except ImportError:
+            return None
+        if f is not None:
+            f.close()
+        # Note that after a setuptools installation, there are both .py
+        # and .so files with the same basename.  The code here relies on
+        # imp.find_module() locating the .so in priority.
+        if descr[0] not in so_suffixes:
+            return None
+        return filename
+
+    def collect_types(self):
+        self._typesdict = {}
+        self._generate("collecttype")
+
+    def _prnt(self, what=''):
+        self._f.write(what + '\n')
+
+    def _gettypenum(self, type):
+        # a KeyError here is a bug.  please report it! :-)
+        return self._typesdict[type]
+
+    def _do_collect_type(self, tp):
+        if ((not isinstance(tp, model.PrimitiveType)
+             or tp.name == 'long double')
+                and tp not in self._typesdict):
+            num = len(self._typesdict)
+            self._typesdict[tp] = num
+
+    def write_source_to_f(self):
+        self.collect_types()
+        #
+        # The new module will have a _cffi_setup() function that receives
+        # objects from the ffi world, and that calls some setup code in
+        # the module.  This setup code is split in several independent
+        # functions, e.g. one per constant.  The functions are "chained"
+        # by ending in a tail call to each other.
+        #
+        # This is further split in two chained lists, depending on if we
+        # can do it at import-time or if we must wait for _cffi_setup() to
+        # provide us with the <ctype> objects.  This is needed because we
+        # need the values of the enum constants in order to build the
+        # <ctype 'enum'> that we may have to pass to _cffi_setup().
+        #
+        # The following two 'chained_list_constants' items contains
+        # the head of these two chained lists, as a string that gives the
+        # call to do, if any.
+        self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)']
+        #
+        prnt = self._prnt
+        # first paste some standard set of lines that are mostly '#define'
+        prnt(cffimod_header)
+        prnt()
+        # then paste the C source given by the user, verbatim.
+        prnt(self.verifier.preamble)
+        prnt()
+        #
+        # call generate_cpy_xxx_decl(), for every xxx found from
+        # ffi._parser._declarations.  This generates all the functions.
+        self._generate("decl")
+        #
+        # implement the function _cffi_setup_custom() as calling the
+        # head of the chained list.
+        self._generate_setup_custom()
+        prnt()
+        #
+        # produce the method table, including the entries for the
+        # generated Python->C function wrappers, which are done
+        # by generate_cpy_function_method().
+        prnt('static PyMethodDef _cffi_methods[] = {')
+        self._generate("method")
+        prnt('  {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},')
+        prnt('  {NULL, NULL, 0, NULL}    /* Sentinel */')
+        prnt('};')
+        prnt()
+        #
+        # standard init.
+        modname = self.verifier.get_module_name()
+        constants = self._chained_list_constants[False]
+        prnt('#if PY_MAJOR_VERSION >= 3')
+        prnt()
+        prnt('static struct PyModuleDef _cffi_module_def = {')
+        prnt('  PyModuleDef_HEAD_INIT,')
+        prnt('  "%s",' % modname)
+        prnt('  NULL,')
+        prnt('  -1,')
+        prnt('  _cffi_methods,')
+        prnt('  NULL, NULL, NULL, NULL')
+        prnt('};')
+        prnt()
+        prnt('PyMODINIT_FUNC')
+        prnt('PyInit_%s(void)' % modname)
+        prnt('{')
+        prnt('  PyObject *lib;')
+        prnt('  lib = PyModule_Create(&_cffi_module_def);')
+        prnt('  if (lib == NULL)')
+        prnt('    return NULL;')
+        prnt('  if (%s < 0 || _cffi_init() < 0) {' % (constants,))
+        prnt('    Py_DECREF(lib);')
+        prnt('    return NULL;')
+        prnt('  }')
+        prnt('  return lib;')
+        prnt('}')
+        prnt()
+        prnt('#else')
+        prnt()
+        prnt('PyMODINIT_FUNC')
+        prnt('init%s(void)' % modname)
+        prnt('{')
+        prnt('  PyObject *lib;')
+        prnt('  lib = Py_InitModule("%s", _cffi_methods);' % modname)
+        prnt('  if (lib == NULL)')
+        prnt('    return;')
+        prnt('  if (%s < 0 || _cffi_init() < 0)' % (constants,))
+        prnt('    return;')
+        prnt('  return;')
+        prnt('}')
+        prnt()
+        prnt('#endif')
+
+    def load_library(self, flags=None):
+        # XXX review all usages of 'self' here!
+        # import it as a new extension module
+        imp.acquire_lock()
+        try:
+            if hasattr(sys, "getdlopenflags"):
+                previous_flags = sys.getdlopenflags()
+            try:
+                if hasattr(sys, "setdlopenflags") and flags is not None:
+                    sys.setdlopenflags(flags)
+                module = imp.load_dynamic(self.verifier.get_module_name(),
+                                          self.verifier.modulefilename)
+            except ImportError as e:
+                error = "importing %r: %s" % (self.verifier.modulefilename, e)
+                raise VerificationError(error)
+            finally:
+                if hasattr(sys, "setdlopenflags"):
+                    sys.setdlopenflags(previous_flags)
+        finally:
+            imp.release_lock()
+        #
+        # call loading_cpy_struct() to get the struct layout inferred by
+        # the C compiler
+        self._load(module, 'loading')
+        #
+        # the C code will need the <ctype> objects.  Collect them in
+        # order in a list.
+        revmapping = dict([(value, key)
+                           for (key, value) in self._typesdict.items()])
+        lst = [revmapping[i] for i in range(len(revmapping))]
+        lst = list(map(self.ffi._get_cached_btype, lst))
+        #
+        # build the FFILibrary class and instance and call _cffi_setup().
+        # this will set up some fields like '_cffi_types', and only then
+        # it will invoke the chained list of functions that will really
+        # build (notably) the constant objects, as <cdata> if they are
+        # pointers, and store them as attributes on the 'library' object.
+        class FFILibrary(object):
+            _cffi_python_module = module
+            _cffi_ffi = self.ffi
+            _cffi_dir = []
+            def __dir__(self):
+                return FFILibrary._cffi_dir + list(self.__dict__)
+        library = FFILibrary()
+        if module._cffi_setup(lst, VerificationError, library):
+            import warnings
+            warnings.warn("reimporting %r might overwrite older definitions"
+                          % (self.verifier.get_module_name()))
+        #
+        # finally, call the loaded_cpy_xxx() functions.  This will perform
+        # the final adjustments, like copying the Python->C wrapper
+        # functions from the module to the 'library' object, and setting
+        # up the FFILibrary class with properties for the global C variables.
+        self._load(module, 'loaded', library=library)
+        module._cffi_original_ffi = self.ffi
+        module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions
+        return library
+
+    def _get_declarations(self):
+        lst = [(key, tp) for (key, (tp, qual)) in
+                                self.ffi._parser._declarations.items()]
+        lst.sort()
+        return lst
+
+    def _generate(self, step_name):
+        for name, tp in self._get_declarations():
+            kind, realname = name.split(' ', 1)
+            try:
+                method = getattr(self, '_generate_cpy_%s_%s' % (kind,
+                                                                step_name))
+            except AttributeError:
+                raise VerificationError(
+                    "not implemented in verify(): %r" % name)
+            try:
+                method(tp, realname)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    def _load(self, module, step_name, **kwds):
+        for name, tp in self._get_declarations():
+            kind, realname = name.split(' ', 1)
+            method = getattr(self, '_%s_cpy_%s' % (step_name, kind))
+            try:
+                method(tp, realname, module, **kwds)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    def _generate_nothing(self, tp, name):
+        pass
+
+    def _loaded_noop(self, tp, name, module, **kwds):
+        pass
+
+    # ----------
+
+    def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
+        extraarg = ''
+        if isinstance(tp, model.PrimitiveType):
+            if tp.is_integer_type() and tp.name != '_Bool':
+                converter = '_cffi_to_c_int'
+                extraarg = ', %s' % tp.name
+            else:
+                converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
+                                                   tp.name.replace(' ', '_'))
+            errvalue = '-1'
+        #
+        elif isinstance(tp, model.PointerType):
+            self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
+                                                    tovar, errcode)
+            return
+        #
+        elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
+            # a struct (not a struct pointer) as a function argument
+            self._prnt('  if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
+                      % (tovar, self._gettypenum(tp), fromvar))
+            self._prnt('    %s;' % errcode)
+            return
+        #
+        elif isinstance(tp, model.FunctionPtrType):
+            converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
+            extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
+            errvalue = 'NULL'
+        #
+        else:
+            raise NotImplementedError(tp)
+        #
+        self._prnt('  %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
+        self._prnt('  if (%s == (%s)%s && PyErr_Occurred())' % (
+            tovar, tp.get_c_name(''), errvalue))
+        self._prnt('    %s;' % errcode)
+
+    def _extra_local_variables(self, tp, localvars):
+        if isinstance(tp, model.PointerType):
+            localvars.add('Py_ssize_t datasize')
+
+    def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
+        self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')
+        self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (
+            self._gettypenum(tp), fromvar, tovar))
+        self._prnt('  if (datasize != 0) {')
+        self._prnt('    if (datasize < 0)')
+        self._prnt('      %s;' % errcode)
+        self._prnt('    %s = alloca((size_t)datasize);' % (tovar,))
+        self._prnt('    memset((void *)%s, 0, (size_t)datasize);' % (tovar,))
+        self._prnt('    if (_cffi_convert_array_from_object('
+                   '(char *)%s, _cffi_type(%d), %s) < 0)' % (
+            tovar, self._gettypenum(tp), fromvar))
+        self._prnt('      %s;' % errcode)
+        self._prnt('  }')
+
+    def _convert_expr_from_c(self, tp, var, context):
+        if isinstance(tp, model.PrimitiveType):
+            if tp.is_integer_type() and tp.name != '_Bool':
+                return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
+            elif tp.name != 'long double':
+                return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
+            else:
+                return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+                    var, self._gettypenum(tp))
+        elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
+            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        elif isinstance(tp, model.ArrayType):
+            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+                var, self._gettypenum(model.PointerType(tp.item)))
+        elif isinstance(tp, model.StructOrUnion):
+            if tp.fldnames is None:
+                raise TypeError("'%s' is used as %s, but is opaque" % (
+                    tp._get_c_name(), context))
+            return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        elif isinstance(tp, model.EnumType):
+            return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
+                var, self._gettypenum(tp))
+        else:
+            raise NotImplementedError(tp)
+
+    # ----------
+    # typedefs: generates no code so far
+
+    _generate_cpy_typedef_collecttype = _generate_nothing
+    _generate_cpy_typedef_decl   = _generate_nothing
+    _generate_cpy_typedef_method = _generate_nothing
+    _loading_cpy_typedef         = _loaded_noop
+    _loaded_cpy_typedef          = _loaded_noop
+
+    # ----------
+    # function declarations
+
+    def _generate_cpy_function_collecttype(self, tp, name):
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            self._do_collect_type(tp)
+        else:
+            # don't call _do_collect_type(tp) in this common case,
+            # otherwise test_autofilled_struct_as_argument fails
+            for type in tp.args:
+                self._do_collect_type(type)
+            self._do_collect_type(tp.result)
+
+    def _generate_cpy_function_decl(self, tp, name):
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            # cannot support vararg functions better than this: check for its
+            # exact type (including the fixed arguments), and build it as a
+            # constant function pointer (no CPython wrapper)
+            self._generate_cpy_const(False, name, tp)
+            return
+        prnt = self._prnt
+        numargs = len(tp.args)
+        if numargs == 0:
+            argname = 'noarg'
+        elif numargs == 1:
+            argname = 'arg0'
+        else:
+            argname = 'args'
+        prnt('static PyObject *')
+        prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
+        prnt('{')
+        #
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            prnt('  %s;' % type.get_c_name(' x%d' % i, context))
+        #
+        localvars = set()
+        for type in tp.args:
+            self._extra_local_variables(type, localvars)
+        for decl in localvars:
+            prnt('  %s;' % (decl,))
+        #
+        if not isinstance(tp.result, model.VoidType):
+            result_code = 'result = '
+            context = 'result of %s' % name
+            prnt('  %s;' % tp.result.get_c_name(' result', context))
+        else:
+            result_code = ''
+        #
+        if len(tp.args) > 1:
+            rng = range(len(tp.args))
+            for i in rng:
+                prnt('  PyObject *arg%d;' % i)
+            prnt()
+            prnt('  if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
+                'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
+            prnt('    return NULL;')
+        prnt()
+        #
+        for i, type in enumerate(tp.args):
+            self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
+                                       'return NULL')
+            prnt()
+        #
+        prnt('  Py_BEGIN_ALLOW_THREADS')
+        prnt('  _cffi_restore_errno();')
+        prnt('  { %s%s(%s); }' % (
+            result_code, name,
+            ', '.join(['x%d' % i for i in range(len(tp.args))])))
+        prnt('  _cffi_save_errno();')
+        prnt('  Py_END_ALLOW_THREADS')
+        prnt()
+        #
+        prnt('  (void)self; /* unused */')
+        if numargs == 0:
+            prnt('  (void)noarg; /* unused */')
+        if result_code:
+            prnt('  return %s;' %
+                 self._convert_expr_from_c(tp.result, 'result', 'result type'))
+        else:
+            prnt('  Py_INCREF(Py_None);')
+            prnt('  return Py_None;')
+        prnt('}')
+        prnt()
+
+    def _generate_cpy_function_method(self, tp, name):
+        if tp.ellipsis:
+            return
+        numargs = len(tp.args)
+        if numargs == 0:
+            meth = 'METH_NOARGS'
+        elif numargs == 1:
+            meth = 'METH_O'
+        else:
+            meth = 'METH_VARARGS'
+        self._prnt('  {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth))
+
+    _loading_cpy_function = _loaded_noop
+
+    def _loaded_cpy_function(self, tp, name, module, library):
+        if tp.ellipsis:
+            return
+        func = getattr(module, name)
+        setattr(library, name, func)
+        self._types_of_builtin_functions[func] = tp
+
+    # ----------
+    # named structs
+
+    _generate_cpy_struct_collecttype = _generate_nothing
+    def _generate_cpy_struct_decl(self, tp, name):
+        assert name == tp.name
+        self._generate_struct_or_union_decl(tp, 'struct', name)
+    def _generate_cpy_struct_method(self, tp, name):
+        self._generate_struct_or_union_method(tp, 'struct', name)
+    def _loading_cpy_struct(self, tp, name, module):
+        self._loading_struct_or_union(tp, 'struct', name, module)
+    def _loaded_cpy_struct(self, tp, name, module, **kwds):
+        self._loaded_struct_or_union(tp)
+
+    _generate_cpy_union_collecttype = _generate_nothing
+    def _generate_cpy_union_decl(self, tp, name):
+        assert name == tp.name
+        self._generate_struct_or_union_decl(tp, 'union', name)
+    def _generate_cpy_union_method(self, tp, name):
+        self._generate_struct_or_union_method(tp, 'union', name)
+    def _loading_cpy_union(self, tp, name, module):
+        self._loading_struct_or_union(tp, 'union', name, module)
+    def _loaded_cpy_union(self, tp, name, module, **kwds):
+        self._loaded_struct_or_union(tp)
+
+    def _generate_struct_or_union_decl(self, tp, prefix, name):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        cname = ('%s %s' % (prefix, name)).strip()
+        #
+        prnt = self._prnt
+        prnt('static void %s(%s *p)' % (checkfuncname, cname))
+        prnt('{')
+        prnt('  /* only to generate compile-time warnings or errors */')
+        prnt('  (void)p;')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            if (isinstance(ftype, model.PrimitiveType)
+                and ftype.is_integer_type()) or fbitsize >= 0:
+                # accept all integers, but complain on float or double
+                prnt('  (void)((p->%s) << 1);' % fname)
+            else:
+                # only accept exactly the type declared.
+                try:
+                    prnt('  { %s = &p->%s; (void)tmp; }' % (
+                        ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
+                        fname))
+                except VerificationError as e:
+                    prnt('  /* %s */' % str(e))   # cannot verify it, ignore
+        prnt('}')
+        prnt('static PyObject *')
+        prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,))
+        prnt('{')
+        prnt('  struct _cffi_aligncheck { char x; %s y; };' % cname)
+        prnt('  static Py_ssize_t nums[] = {')
+        prnt('    sizeof(%s),' % cname)
+        prnt('    offsetof(struct _cffi_aligncheck, y),')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            if fbitsize >= 0:
+                continue      # xxx ignore fbitsize for now
+            prnt('    offsetof(%s, %s),' % (cname, fname))
+            if isinstance(ftype, model.ArrayType) and ftype.length is None:
+                prnt('    0,  /* %s */' % ftype._get_c_name())
+            else:
+                prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
+        prnt('    -1')
+        prnt('  };')
+        prnt('  (void)self; /* unused */')
+        prnt('  (void)noarg; /* unused */')
+        prnt('  return _cffi_get_struct_layout(nums);')
+        prnt('  /* the next line is not executed, but compiled */')
+        prnt('  %s(0);' % (checkfuncname,))
+        prnt('}')
+        prnt()
+
+    def _generate_struct_or_union_method(self, tp, prefix, name):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        self._prnt('  {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname,
+                                                         layoutfuncname))
+
+    def _loading_struct_or_union(self, tp, prefix, name, module):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        #
+        function = getattr(module, layoutfuncname)
+        layout = function()
+        if isinstance(tp, model.StructOrUnion) and tp.partial:
+            # use the function()'s sizes and offsets to guide the
+            # layout of the struct
+            totalsize = layout[0]
+            totalalignment = layout[1]
+            fieldofs = layout[2::2]
+            fieldsize = layout[3::2]
+            tp.force_flatten()
+            assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+            tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+        else:
+            cname = ('%s %s' % (prefix, name)).strip()
+            self._struct_pending_verification[tp] = layout, cname
+
+    def _loaded_struct_or_union(self, tp):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        self.ffi._get_cached_btype(tp)   # force 'fixedlayout' to be considered
+
+        if tp in self._struct_pending_verification:
+            # check that the layout sizes and offsets match the real ones
+            def check(realvalue, expectedvalue, msg):
+                if realvalue != expectedvalue:
+                    raise VerificationError(
+                        "%s (we have %d, but C compiler says %d)"
+                        % (msg, expectedvalue, realvalue))
+            ffi = self.ffi
+            BStruct = ffi._get_cached_btype(tp)
+            layout, cname = self._struct_pending_verification.pop(tp)
+            check(layout[0], ffi.sizeof(BStruct), "wrong total size")
+            check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
+            i = 2
+            for fname, ftype, fbitsize, fqual in tp.enumfields():
+                if fbitsize >= 0:
+                    continue        # xxx ignore fbitsize for now
+                check(layout[i], ffi.offsetof(BStruct, fname),
+                      "wrong offset for field %r" % (fname,))
+                if layout[i+1] != 0:
+                    BField = ffi._get_cached_btype(ftype)
+                    check(layout[i+1], ffi.sizeof(BField),
+                          "wrong size for field %r" % (fname,))
+                i += 2
+            assert i == len(layout)
+
+    # ----------
+    # 'anonymous' declarations.  These are produced for anonymous structs
+    # or unions; the 'name' is obtained by a typedef.
+
+    _generate_cpy_anonymous_collecttype = _generate_nothing
+
+    def _generate_cpy_anonymous_decl(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_cpy_enum_decl(tp, name, '')
+        else:
+            self._generate_struct_or_union_decl(tp, '', name)
+
+    def _generate_cpy_anonymous_method(self, tp, name):
+        if not isinstance(tp, model.EnumType):
+            self._generate_struct_or_union_method(tp, '', name)
+
+    def _loading_cpy_anonymous(self, tp, name, module):
+        if isinstance(tp, model.EnumType):
+            self._loading_cpy_enum(tp, name, module)
+        else:
+            self._loading_struct_or_union(tp, '', name, module)
+
+    def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
+        if isinstance(tp, model.EnumType):
+            self._loaded_cpy_enum(tp, name, module, **kwds)
+        else:
+            self._loaded_struct_or_union(tp)
+
+    # ----------
+    # constants, likely declared with '#define'
+
+    def _generate_cpy_const(self, is_int, name, tp=None, category='const',
+                            vartp=None, delayed=True, size_too=False,
+                            check_value=None):
+        prnt = self._prnt
+        funcname = '_cffi_%s_%s' % (category, name)
+        prnt('static int %s(PyObject *lib)' % funcname)
+        prnt('{')
+        prnt('  PyObject *o;')
+        prnt('  int res;')
+        if not is_int:
+            prnt('  %s;' % (vartp or tp).get_c_name(' i', name))
+        else:
+            assert category == 'const'
+        #
+        if check_value is not None:
+            self._check_int_constant_value(name, check_value)
+        #
+        if not is_int:
+            if category == 'var':
+                realexpr = '&' + name
+            else:
+                realexpr = name
+            prnt('  i = (%s);' % (realexpr,))
+            prnt('  o = %s;' % (self._convert_expr_from_c(tp, 'i',
+                                                          'variable type'),))
+            assert delayed
+        else:
+            prnt('  o = _cffi_from_c_int_const(%s);' % name)
+        prnt('  if (o == NULL)')
+        prnt('    return -1;')
+        if size_too:
+            prnt('  {')
+            prnt('    PyObject *o1 = o;')
+            prnt('    o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));'
+                 % (name,))
+            prnt('    Py_DECREF(o1);')
+            prnt('    if (o == NULL)')
+            prnt('      return -1;')
+            prnt('  }')
+        prnt('  res = PyObject_SetAttrString(lib, "%s", o);' % name)
+        prnt('  Py_DECREF(o);')
+        prnt('  if (res < 0)')
+        prnt('    return -1;')
+        prnt('  return %s;' % self._chained_list_constants[delayed])
+        self._chained_list_constants[delayed] = funcname + '(lib)'
+        prnt('}')
+        prnt()
+
+    def _generate_cpy_constant_collecttype(self, tp, name):
+        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        if not is_int:
+            self._do_collect_type(tp)
+
+    def _generate_cpy_constant_decl(self, tp, name):
+        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        self._generate_cpy_const(is_int, name, tp)
+
+    _generate_cpy_constant_method = _generate_nothing
+    _loading_cpy_constant = _loaded_noop
+    _loaded_cpy_constant  = _loaded_noop
+
+    # ----------
+    # enums
+
+    def _check_int_constant_value(self, name, value, err_prefix=''):
+        prnt = self._prnt
+        if value <= 0:
+            prnt('  if ((%s) > 0 || (long)(%s) != %dL) {' % (
+                name, name, value))
+        else:
+            prnt('  if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
+                name, name, value))
+        prnt('    char buf[64];')
+        prnt('    if ((%s) <= 0)' % name)
+        prnt('        snprintf(buf, 63, "%%ld", (long)(%s));' % name)
+        prnt('    else')
+        prnt('        snprintf(buf, 63, "%%lu", (unsigned long)(%s));' %
+             name)
+        prnt('    PyErr_Format(_cffi_VerificationError,')
+        prnt('                 "%s%s has the real value %s, not %s",')
+        prnt('                 "%s", "%s", buf, "%d");' % (
+            err_prefix, name, value))
+        prnt('    return -1;')
+        prnt('  }')
+
+    def _enum_funcname(self, prefix, name):
+        # "$enum_$1" => "___D_enum____D_1"
+        name = name.replace('$', '___D_')
+        return '_cffi_e_%s_%s' % (prefix, name)
+
+    def _generate_cpy_enum_decl(self, tp, name, prefix='enum'):
+        if tp.partial:
+            for enumerator in tp.enumerators:
+                self._generate_cpy_const(True, enumerator, delayed=False)
+            return
+        #
+        funcname = self._enum_funcname(prefix, name)
+        prnt = self._prnt
+        prnt('static int %s(PyObject *lib)' % funcname)
+        prnt('{')
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            self._check_int_constant_value(enumerator, enumvalue,
+                                           "enum %s: " % name)
+        prnt('  return %s;' % self._chained_list_constants[True])
+        self._chained_list_constants[True] = funcname + '(lib)'
+        prnt('}')
+        prnt()
+
+    _generate_cpy_enum_collecttype = _generate_nothing
+    _generate_cpy_enum_method = _generate_nothing
+
+    def _loading_cpy_enum(self, tp, name, module):
+        if tp.partial:
+            enumvalues = [getattr(module, enumerator)
+                          for enumerator in tp.enumerators]
+            tp.enumvalues = tuple(enumvalues)
+            tp.partial_resolved = True
+
+    def _loaded_cpy_enum(self, tp, name, module, library):
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            setattr(library, enumerator, enumvalue)
+
+    # ----------
+    # macros: for now only for integers
+
+    def _generate_cpy_macro_decl(self, tp, name):
+        if tp == '...':
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        self._generate_cpy_const(True, name, check_value=check_value)
+
+    _generate_cpy_macro_collecttype = _generate_nothing
+    _generate_cpy_macro_method = _generate_nothing
+    _loading_cpy_macro = _loaded_noop
+    _loaded_cpy_macro  = _loaded_noop
+
+    # ----------
+    # global variables
+
+    def _generate_cpy_variable_collecttype(self, tp, name):
+        if isinstance(tp, model.ArrayType):
+            tp_ptr = model.PointerType(tp.item)
+        else:
+            tp_ptr = model.PointerType(tp)
+        self._do_collect_type(tp_ptr)
+
+    def _generate_cpy_variable_decl(self, tp, name):
+        if isinstance(tp, model.ArrayType):
+            tp_ptr = model.PointerType(tp.item)
+            self._generate_cpy_const(False, name, tp, vartp=tp_ptr,
+                                     size_too = (tp.length == '...'))
+        else:
+            tp_ptr = model.PointerType(tp)
+            self._generate_cpy_const(False, name, tp_ptr, category='var')
+
+    _generate_cpy_variable_method = _generate_nothing
+    _loading_cpy_variable = _loaded_noop
+
+    def _loaded_cpy_variable(self, tp, name, module, library):
+        value = getattr(library, name)
+        if isinstance(tp, model.ArrayType):   # int a[5] is "constant" in the
+                                              # sense that "a=..." is forbidden
+            if tp.length == '...':
+                assert isinstance(value, tuple)
+                (value, size) = value
+                BItemType = self.ffi._get_cached_btype(tp.item)
+                length, rest = divmod(size, self.ffi.sizeof(BItemType))
+                if rest != 0:
+                    raise VerificationError(
+                        "bad size: %r does not seem to be an array of %s" %
+                        (name, tp.item))
+                tp = tp.resolve_length(length)
+            # 'value' is a <cdata 'type *'> which we have to replace with
+            # a <cdata 'type[N]'> if the N is actually known
+            if tp.length is not None:
+                BArray = self.ffi._get_cached_btype(tp)
+                value = self.ffi.cast(BArray, value)
+                setattr(library, name, value)
+            return
+        # remove ptr=<cdata 'int *'> from the library instance, and replace
+        # it by a property on the class, which reads/writes into ptr[0].
+        ptr = value
+        delattr(library, name)
+        def getter(library):
+            return ptr[0]
+        def setter(library, value):
+            ptr[0] = value
+        setattr(type(library), name, property(getter, setter))
+        type(library)._cffi_dir.append(name)
+
+    # ----------
+
+    def _generate_setup_custom(self):
+        prnt = self._prnt
+        prnt('static int _cffi_setup_custom(PyObject *lib)')
+        prnt('{')
+        prnt('  return %s;' % self._chained_list_constants[True])
+        prnt('}')
+
+cffimod_header = r'''
+#include <Python.h>
+#include <stddef.h>
+
+/* this block of #ifs should be kept exactly identical between
+   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
+   and cffi/_cffi_include.h */
+#if defined(_MSC_VER)
+# include <malloc.h>   /* for alloca() */
+# if _MSC_VER < 1600   /* MSVC < 2010 */
+   typedef __int8 int8_t;
+   typedef __int16 int16_t;
+   typedef __int32 int32_t;
+   typedef __int64 int64_t;
+   typedef unsigned __int8 uint8_t;
+   typedef unsigned __int16 uint16_t;
+   typedef unsigned __int32 uint32_t;
+   typedef unsigned __int64 uint64_t;
+   typedef __int8 int_least8_t;
+   typedef __int16 int_least16_t;
+   typedef __int32 int_least32_t;
+   typedef __int64 int_least64_t;
+   typedef unsigned __int8 uint_least8_t;
+   typedef unsigned __int16 uint_least16_t;
+   typedef unsigned __int32 uint_least32_t;
+   typedef unsigned __int64 uint_least64_t;
+   typedef __int8 int_fast8_t;
+   typedef __int16 int_fast16_t;
+   typedef __int32 int_fast32_t;
+   typedef __int64 int_fast64_t;
+   typedef unsigned __int8 uint_fast8_t;
+   typedef unsigned __int16 uint_fast16_t;
+   typedef unsigned __int32 uint_fast32_t;
+   typedef unsigned __int64 uint_fast64_t;
+   typedef __int64 intmax_t;
+   typedef unsigned __int64 uintmax_t;
+# else
+#  include <stdint.h>
+# endif
+# if _MSC_VER < 1800   /* MSVC < 2013 */
+#  ifndef __cplusplus
+    typedef unsigned char _Bool;
+#  endif
+# endif
+#else
+# include <stdint.h>
+# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
+#  include <alloca.h>
+# endif
+#endif
+
+#if PY_MAJOR_VERSION < 3
+# undef PyCapsule_CheckExact
+# undef PyCapsule_GetPointer
+# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))
+# define PyCapsule_GetPointer(capsule, name) \
+    (PyCObject_AsVoidPtr(capsule))
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+# define PyInt_FromLong PyLong_FromLong
+#endif
+
+#define _cffi_from_c_double PyFloat_FromDouble
+#define _cffi_from_c_float PyFloat_FromDouble
+#define _cffi_from_c_long PyInt_FromLong
+#define _cffi_from_c_ulong PyLong_FromUnsignedLong
+#define _cffi_from_c_longlong PyLong_FromLongLong
+#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
+#define _cffi_from_c__Bool PyBool_FromLong
+
+#define _cffi_to_c_double PyFloat_AsDouble
+#define _cffi_to_c_float PyFloat_AsDouble
+
+#define _cffi_from_c_int_const(x)                                        \
+    (((x) > 0) ?                                                         \
+        ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ?      \
+            PyInt_FromLong((long)(x)) :                                  \
+            PyLong_FromUnsignedLongLong((unsigned long long)(x)) :       \
+        ((long long)(x) >= (long long)LONG_MIN) ?                        \
+            PyInt_FromLong((long)(x)) :                                  \
+            PyLong_FromLongLong((long long)(x)))
+
+#define _cffi_from_c_int(x, type)                                        \
+    (((type)-1) > 0 ? /* unsigned */                                     \
+        (sizeof(type) < sizeof(long) ?                                   \
+            PyInt_FromLong((long)x) :                                    \
+         sizeof(type) == sizeof(long) ?                                  \
+            PyLong_FromUnsignedLong((unsigned long)x) :                  \
+            PyLong_FromUnsignedLongLong((unsigned long long)x)) :        \
+        (sizeof(type) <= sizeof(long) ?                                  \
+            PyInt_FromLong((long)x) :                                    \
+            PyLong_FromLongLong((long long)x)))
+
+#define _cffi_to_c_int(o, type)                                          \
+    ((type)(                                                             \
+     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
+                                         : (type)_cffi_to_c_i8(o)) :     \
+     sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
+                                         : (type)_cffi_to_c_i16(o)) :    \
+     sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o)       \
+                                         : (type)_cffi_to_c_i32(o)) :    \
+     sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
+                                         : (type)_cffi_to_c_i64(o)) :    \
+     (Py_FatalError("unsupported size for type " #type), (type)0)))
+
+#define _cffi_to_c_i8                                                    \
+                 ((int(*)(PyObject *))_cffi_exports[1])
+#define _cffi_to_c_u8                                                    \
+                 ((int(*)(PyObject *))_cffi_exports[2])
+#define _cffi_to_c_i16                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[3])
+#define _cffi_to_c_u16                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[4])
+#define _cffi_to_c_i32                                                   \
+                 ((int(*)(PyObject *))_cffi_exports[5])
+#define _cffi_to_c_u32                                                   \
+                 ((unsigned int(*)(PyObject *))_cffi_exports[6])
+#define _cffi_to_c_i64                                                   \
+                 ((long long(*)(PyObject *))_cffi_exports[7])
+#define _cffi_to_c_u64                                                   \
+                 ((unsigned long long(*)(PyObject *))_cffi_exports[8])
+#define _cffi_to_c_char                                                  \
+                 ((int(*)(PyObject *))_cffi_exports[9])
+#define _cffi_from_c_pointer                                             \
+    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
+#define _cffi_to_c_pointer                                               \
+    ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
+#define _cffi_get_struct_layout                                          \
+    ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12])
+#define _cffi_restore_errno                                              \
+    ((void(*)(void))_cffi_exports[13])
+#define _cffi_save_errno                                                 \
+    ((void(*)(void))_cffi_exports[14])
+#define _cffi_from_c_char                                                \
+    ((PyObject *(*)(char))_cffi_exports[15])
+#define _cffi_from_c_deref                                               \
+    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
+#define _cffi_to_c                                                       \
+    ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
+#define _cffi_from_c_struct                                              \
+    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
+#define _cffi_to_c_wchar_t                                               \
+    ((wchar_t(*)(PyObject *))_cffi_exports[19])
+#define _cffi_from_c_wchar_t                                             \
+    ((PyObject *(*)(wchar_t))_cffi_exports[20])
+#define _cffi_to_c_long_double                                           \
+    ((long double(*)(PyObject *))_cffi_exports[21])
+#define _cffi_to_c__Bool                                                 \
+    ((_Bool(*)(PyObject *))_cffi_exports[22])
+#define _cffi_prepare_pointer_call_argument                              \
+    ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23])
+#define _cffi_convert_array_from_object                                  \
+    ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24])
+#define _CFFI_NUM_EXPORTS 25
+
+typedef struct _ctypedescr CTypeDescrObject;
+
+static void *_cffi_exports[_CFFI_NUM_EXPORTS];
+static PyObject *_cffi_types, *_cffi_VerificationError;
+
+static int _cffi_setup_custom(PyObject *lib);   /* forward */
+
+static PyObject *_cffi_setup(PyObject *self, PyObject *args)
+{
+    PyObject *library;
+    int was_alive = (_cffi_types != NULL);
+    (void)self; /* unused */
+    if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError,
+                                       &library))
+        return NULL;
+    Py_INCREF(_cffi_types);
+    Py_INCREF(_cffi_VerificationError);
+    if (_cffi_setup_custom(library) < 0)
+        return NULL;
+    return PyBool_FromLong(was_alive);
+}
+
+static int _cffi_init(void)
+{
+    PyObject *module, *c_api_object = NULL;
+
+    module = PyImport_ImportModule("_cffi_backend");
+    if (module == NULL)
+        goto failure;
+
+    c_api_object = PyObject_GetAttrString(module, "_C_API");
+    if (c_api_object == NULL)
+        goto failure;
+    if (!PyCapsule_CheckExact(c_api_object)) {
+        PyErr_SetNone(PyExc_ImportError);
+        goto failure;
+    }
+    memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"),
+           _CFFI_NUM_EXPORTS * sizeof(void *));
+
+    Py_DECREF(module);
+    Py_DECREF(c_api_object);
+    return 0;
+
+  failure:
+    Py_XDECREF(module);
+    Py_XDECREF(c_api_object);
+    return -1;
+}
+
+#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))
+
+/**********/
+'''
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
new file mode 100644
index 0000000..a64ff64
--- /dev/null
+++ b/cffi/vengine_gen.py
@@ -0,0 +1,675 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
+import sys, os
+import types
+
+from . import model
+from .error import VerificationError
+
+
+class VGenericEngine(object):
+    _class_key = 'g'
+    _gen_python_module = False
+
+    def __init__(self, verifier):
+        self.verifier = verifier
+        self.ffi = verifier.ffi
+        self.export_symbols = []
+        self._struct_pending_verification = {}
+
+    def patch_extension_kwds(self, kwds):
+        # add 'export_symbols' to the dictionary.  Note that we add the
+        # list before filling it.  When we fill it, it will thus also show
+        # up in kwds['export_symbols'].
+        kwds.setdefault('export_symbols', self.export_symbols)
+
+    def find_module(self, module_name, path, so_suffixes):
+        for so_suffix in so_suffixes:
+            basename = module_name + so_suffix
+            if path is None:
+                path = sys.path
+            for dirname in path:
+                filename = os.path.join(dirname, basename)
+                if os.path.isfile(filename):
+                    return filename
+
+    def collect_types(self):
+        pass      # not needed in the generic engine
+
+    def _prnt(self, what=''):
+        self._f.write(what + '\n')
+
+    def write_source_to_f(self):
+        prnt = self._prnt
+        # first paste some standard set of lines that are mostly '#include'
+        prnt(cffimod_header)
+        # then paste the C source given by the user, verbatim.
+        prnt(self.verifier.preamble)
+        #
+        # call generate_gen_xxx_decl(), for every xxx found from
+        # ffi._parser._declarations.  This generates all the functions.
+        self._generate('decl')
+        #
+        # on Windows, distutils insists on putting init_cffi_xyz in
+        # 'export_symbols', so instead of fighting it, just give up and
+        # give it one
+        if sys.platform == 'win32':
+            if sys.version_info >= (3,):
+                prefix = 'PyInit_'
+            else:
+                prefix = 'init'
+            modname = self.verifier.get_module_name()
+            prnt("void %s%s(void) { }\n" % (prefix, modname))
+
+    def load_library(self, flags=0):
+        # import it with the CFFI backend
+        backend = self.ffi._backend
+        # needs to make a path that contains '/', on Posix
+        filename = os.path.join(os.curdir, self.verifier.modulefilename)
+        module = backend.load_library(filename, flags)
+        #
+        # call loading_gen_struct() to get the struct layout inferred by
+        # the C compiler
+        self._load(module, 'loading')
+
+        # build the FFILibrary class and instance, this is a module subclass
+        # because modules are expected to have usually-constant-attributes and
+        # in PyPy this means the JIT is able to treat attributes as constant,
+        # which we want.
+        class FFILibrary(types.ModuleType):
+            _cffi_generic_module = module
+            _cffi_ffi = self.ffi
+            _cffi_dir = []
+            def __dir__(self):
+                return FFILibrary._cffi_dir
+        library = FFILibrary("")
+        #
+        # finally, call the loaded_gen_xxx() functions.  This will set
+        # up the 'library' object.
+        self._load(module, 'loaded', library=library)
+        return library
+
+    def _get_declarations(self):
+        lst = [(key, tp) for (key, (tp, qual)) in
+                                self.ffi._parser._declarations.items()]
+        lst.sort()
+        return lst
+
+    def _generate(self, step_name):
+        for name, tp in self._get_declarations():
+            kind, realname = name.split(' ', 1)
+            try:
+                method = getattr(self, '_generate_gen_%s_%s' % (kind,
+                                                                step_name))
+            except AttributeError:
+                raise VerificationError(
+                    "not implemented in verify(): %r" % name)
+            try:
+                method(tp, realname)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    def _load(self, module, step_name, **kwds):
+        for name, tp in self._get_declarations():
+            kind, realname = name.split(' ', 1)
+            method = getattr(self, '_%s_gen_%s' % (step_name, kind))
+            try:
+                method(tp, realname, module, **kwds)
+            except Exception as e:
+                model.attach_exception_info(e, name)
+                raise
+
+    def _generate_nothing(self, tp, name):
+        pass
+
+    def _loaded_noop(self, tp, name, module, **kwds):
+        pass
+
+    # ----------
+    # typedefs: generates no code so far
+
+    _generate_gen_typedef_decl   = _generate_nothing
+    _loading_gen_typedef         = _loaded_noop
+    _loaded_gen_typedef          = _loaded_noop
+
+    # ----------
+    # function declarations
+
+    def _generate_gen_function_decl(self, tp, name):
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            # cannot support vararg functions better than this: check for its
+            # exact type (including the fixed arguments), and build it as a
+            # constant function pointer (no _cffi_f_%s wrapper)
+            self._generate_gen_const(False, name, tp)
+            return
+        prnt = self._prnt
+        numargs = len(tp.args)
+        argnames = []
+        for i, type in enumerate(tp.args):
+            indirection = ''
+            if isinstance(type, model.StructOrUnion):
+                indirection = '*'
+            argnames.append('%sx%d' % (indirection, i))
+        context = 'argument of %s' % name
+        arglist = [type.get_c_name(' %s' % arg, context)
+                   for type, arg in zip(tp.args, argnames)]
+        tpresult = tp.result
+        if isinstance(tpresult, model.StructOrUnion):
+            arglist.insert(0, tpresult.get_c_name(' *r', context))
+            tpresult = model.void_type
+        arglist = ', '.join(arglist) or 'void'
+        wrappername = '_cffi_f_%s' % name
+        self.export_symbols.append(wrappername)
+        if tp.abi:
+            abi = tp.abi + ' '
+        else:
+            abi = ''
+        funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist)
+        context = 'result of %s' % name
+        prnt(tpresult.get_c_name(funcdecl, context))
+        prnt('{')
+        #
+        if isinstance(tp.result, model.StructOrUnion):
+            result_code = '*r = '
+        elif not isinstance(tp.result, model.VoidType):
+            result_code = 'return '
+        else:
+            result_code = ''
+        prnt('  %s%s(%s);' % (result_code, name, ', '.join(argnames)))
+        prnt('}')
+        prnt()
+
+    _loading_gen_function = _loaded_noop
+
+    def _loaded_gen_function(self, tp, name, module, library):
+        assert isinstance(tp, model.FunctionPtrType)
+        if tp.ellipsis:
+            newfunction = self._load_constant(False, tp, name, module)
+        else:
+            indirections = []
+            base_tp = tp
+            if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args)
+                    or isinstance(tp.result, model.StructOrUnion)):
+                indirect_args = []
+                for i, typ in enumerate(tp.args):
+                    if isinstance(typ, model.StructOrUnion):
+                        typ = model.PointerType(typ)
+                        indirections.append((i, typ))
+                    indirect_args.append(typ)
+                indirect_result = tp.result
+                if isinstance(indirect_result, model.StructOrUnion):
+                    if indirect_result.fldtypes is None:
+                        raise TypeError("'%s' is used as result type, "
+                                        "but is opaque" % (
+                                            indirect_result._get_c_name(),))
+                    indirect_result = model.PointerType(indirect_result)
+                    indirect_args.insert(0, indirect_result)
+                    indirections.insert(0, ("result", indirect_result))
+                    indirect_result = model.void_type
+                tp = model.FunctionPtrType(tuple(indirect_args),
+                                           indirect_result, tp.ellipsis)
+            BFunc = self.ffi._get_cached_btype(tp)
+            wrappername = '_cffi_f_%s' % name
+            newfunction = module.load_function(BFunc, wrappername)
+            for i, typ in indirections:
+                newfunction = self._make_struct_wrapper(newfunction, i, typ,
+                                                        base_tp)
+        setattr(library, name, newfunction)
+        type(library)._cffi_dir.append(name)
+
+    def _make_struct_wrapper(self, oldfunc, i, tp, base_tp):
+        backend = self.ffi._backend
+        BType = self.ffi._get_cached_btype(tp)
+        if i == "result":
+            ffi = self.ffi
+            def newfunc(*args):
+                res = ffi.new(BType)
+                oldfunc(res, *args)
+                return res[0]
+        else:
+            def newfunc(*args):
+                args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
+                return oldfunc(*args)
+        newfunc._cffi_base_type = base_tp
+        return newfunc
+
+    # ----------
+    # named structs
+
+    def _generate_gen_struct_decl(self, tp, name):
+        assert name == tp.name
+        self._generate_struct_or_union_decl(tp, 'struct', name)
+
+    def _loading_gen_struct(self, tp, name, module):
+        self._loading_struct_or_union(tp, 'struct', name, module)
+
+    def _loaded_gen_struct(self, tp, name, module, **kwds):
+        self._loaded_struct_or_union(tp)
+
+    def _generate_gen_union_decl(self, tp, name):
+        assert name == tp.name
+        self._generate_struct_or_union_decl(tp, 'union', name)
+
+    def _loading_gen_union(self, tp, name, module):
+        self._loading_struct_or_union(tp, 'union', name, module)
+
+    def _loaded_gen_union(self, tp, name, module, **kwds):
+        self._loaded_struct_or_union(tp)
+
+    def _generate_struct_or_union_decl(self, tp, prefix, name):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        cname = ('%s %s' % (prefix, name)).strip()
+        #
+        prnt = self._prnt
+        prnt('static void %s(%s *p)' % (checkfuncname, cname))
+        prnt('{')
+        prnt('  /* only to generate compile-time warnings or errors */')
+        prnt('  (void)p;')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            if (isinstance(ftype, model.PrimitiveType)
+                and ftype.is_integer_type()) or fbitsize >= 0:
+                # accept all integers, but complain on float or double
+                prnt('  (void)((p->%s) << 1);' % fname)
+            else:
+                # only accept exactly the type declared.
+                try:
+                    prnt('  { %s = &p->%s; (void)tmp; }' % (
+                        ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
+                        fname))
+                except VerificationError as e:
+                    prnt('  /* %s */' % str(e))   # cannot verify it, ignore
+        prnt('}')
+        self.export_symbols.append(layoutfuncname)
+        prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,))
+        prnt('{')
+        prnt('  struct _cffi_aligncheck { char x; %s y; };' % cname)
+        prnt('  static intptr_t nums[] = {')
+        prnt('    sizeof(%s),' % cname)
+        prnt('    offsetof(struct _cffi_aligncheck, y),')
+        for fname, ftype, fbitsize, fqual in tp.enumfields():
+            if fbitsize >= 0:
+                continue      # xxx ignore fbitsize for now
+            prnt('    offsetof(%s, %s),' % (cname, fname))
+            if isinstance(ftype, model.ArrayType) and ftype.length is None:
+                prnt('    0,  /* %s */' % ftype._get_c_name())
+            else:
+                prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
+        prnt('    -1')
+        prnt('  };')
+        prnt('  return nums[i];')
+        prnt('  /* the next line is not executed, but compiled */')
+        prnt('  %s(0);' % (checkfuncname,))
+        prnt('}')
+        prnt()
+
+    def _loading_struct_or_union(self, tp, prefix, name, module):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+        #
+        BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0]
+        function = module.load_function(BFunc, layoutfuncname)
+        layout = []
+        num = 0
+        while True:
+            x = function(num)
+            if x < 0: break
+            layout.append(x)
+            num += 1
+        if isinstance(tp, model.StructOrUnion) and tp.partial:
+            # use the function()'s sizes and offsets to guide the
+            # layout of the struct
+            totalsize = layout[0]
+            totalalignment = layout[1]
+            fieldofs = layout[2::2]
+            fieldsize = layout[3::2]
+            tp.force_flatten()
+            assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+            tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+        else:
+            cname = ('%s %s' % (prefix, name)).strip()
+            self._struct_pending_verification[tp] = layout, cname
+
+    def _loaded_struct_or_union(self, tp):
+        if tp.fldnames is None:
+            return     # nothing to do with opaque structs
+        self.ffi._get_cached_btype(tp)   # force 'fixedlayout' to be considered
+
+        if tp in self._struct_pending_verification:
+            # check that the layout sizes and offsets match the real ones
+            def check(realvalue, expectedvalue, msg):
+                if realvalue != expectedvalue:
+                    raise VerificationError(
+                        "%s (we have %d, but C compiler says %d)"
+                        % (msg, expectedvalue, realvalue))
+            ffi = self.ffi
+            BStruct = ffi._get_cached_btype(tp)
+            layout, cname = self._struct_pending_verification.pop(tp)
+            check(layout[0], ffi.sizeof(BStruct), "wrong total size")
+            check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
+            i = 2
+            for fname, ftype, fbitsize, fqual in tp.enumfields():
+                if fbitsize >= 0:
+                    continue        # xxx ignore fbitsize for now
+                check(layout[i], ffi.offsetof(BStruct, fname),
+                      "wrong offset for field %r" % (fname,))
+                if layout[i+1] != 0:
+                    BField = ffi._get_cached_btype(ftype)
+                    check(layout[i+1], ffi.sizeof(BField),
+                          "wrong size for field %r" % (fname,))
+                i += 2
+            assert i == len(layout)
+
+    # ----------
+    # 'anonymous' declarations.  These are produced for anonymous structs
+    # or unions; the 'name' is obtained by a typedef.
+
+    def _generate_gen_anonymous_decl(self, tp, name):
+        if isinstance(tp, model.EnumType):
+            self._generate_gen_enum_decl(tp, name, '')
+        else:
+            self._generate_struct_or_union_decl(tp, '', name)
+
+    def _loading_gen_anonymous(self, tp, name, module):
+        if isinstance(tp, model.EnumType):
+            self._loading_gen_enum(tp, name, module, '')
+        else:
+            self._loading_struct_or_union(tp, '', name, module)
+
+    def _loaded_gen_anonymous(self, tp, name, module, **kwds):
+        if isinstance(tp, model.EnumType):
+            self._loaded_gen_enum(tp, name, module, **kwds)
+        else:
+            self._loaded_struct_or_union(tp)
+
+    # ----------
+    # constants, likely declared with '#define'
+
+    def _generate_gen_const(self, is_int, name, tp=None, category='const',
+                            check_value=None):
+        prnt = self._prnt
+        funcname = '_cffi_%s_%s' % (category, name)
+        self.export_symbols.append(funcname)
+        if check_value is not None:
+            assert is_int
+            assert category == 'const'
+            prnt('int %s(char *out_error)' % funcname)
+            prnt('{')
+            self._check_int_constant_value(name, check_value)
+            prnt('  return 0;')
+            prnt('}')
+        elif is_int:
+            assert category == 'const'
+            prnt('int %s(long long *out_value)' % funcname)
+            prnt('{')
+            prnt('  *out_value = (long long)(%s);' % (name,))
+            prnt('  return (%s) <= 0;' % (name,))
+            prnt('}')
+        else:
+            assert tp is not None
+            assert check_value is None
+            if category == 'var':
+                ampersand = '&'
+            else:
+                ampersand = ''
+            extra = ''
+            if category == 'const' and isinstance(tp, model.StructOrUnion):
+                extra = 'const *'
+                ampersand = '&'
+            prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name))
+            prnt('{')
+            prnt('  return (%s%s);' % (ampersand, name))
+            prnt('}')
+        prnt()
+
+    def _generate_gen_constant_decl(self, tp, name):
+        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        self._generate_gen_const(is_int, name, tp)
+
+    _loading_gen_constant = _loaded_noop
+
+    def _load_constant(self, is_int, tp, name, module, check_value=None):
+        funcname = '_cffi_const_%s' % name
+        if check_value is not None:
+            assert is_int
+            self._load_known_int_constant(module, funcname)
+            value = check_value
+        elif is_int:
+            BType = self.ffi._typeof_locked("long long*")[0]
+            BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0]
+            function = module.load_function(BFunc, funcname)
+            p = self.ffi.new(BType)
+            negative = function(p)
+            value = int(p[0])
+            if value < 0 and not negative:
+                BLongLong = self.ffi._typeof_locked("long long")[0]
+                value += (1 << (8*self.ffi.sizeof(BLongLong)))
+        else:
+            assert check_value is None
+            fntypeextra = '(*)(void)'
+            if isinstance(tp, model.StructOrUnion):
+                fntypeextra = '*' + fntypeextra
+            BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0]
+            function = module.load_function(BFunc, funcname)
+            value = function()
+            if isinstance(tp, model.StructOrUnion):
+                value = value[0]
+        return value
+
+    def _loaded_gen_constant(self, tp, name, module, library):
+        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        value = self._load_constant(is_int, tp, name, module)
+        setattr(library, name, value)
+        type(library)._cffi_dir.append(name)
+
+    # ----------
+    # enums
+
+    def _check_int_constant_value(self, name, value):
+        prnt = self._prnt
+        if value <= 0:
+            prnt('  if ((%s) > 0 || (long)(%s) != %dL) {' % (
+                name, name, value))
+        else:
+            prnt('  if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
+                name, name, value))
+        prnt('    char buf[64];')
+        prnt('    if ((%s) <= 0)' % name)
+        prnt('        sprintf(buf, "%%ld", (long)(%s));' % name)
+        prnt('    else')
+        prnt('        sprintf(buf, "%%lu", (unsigned long)(%s));' %
+             name)
+        prnt('    sprintf(out_error, "%s has the real value %s, not %s",')
+        prnt('            "%s", buf, "%d");' % (name[:100], value))
+        prnt('    return -1;')
+        prnt('  }')
+
+    def _load_known_int_constant(self, module, funcname):
+        BType = self.ffi._typeof_locked("char[]")[0]
+        BFunc = self.ffi._typeof_locked("int(*)(char*)")[0]
+        function = module.load_function(BFunc, funcname)
+        p = self.ffi.new(BType, 256)
+        if function(p) < 0:
+            error = self.ffi.string(p)
+            if sys.version_info >= (3,):
+                error = str(error, 'utf-8')
+            raise VerificationError(error)
+
+    def _enum_funcname(self, prefix, name):
+        # "$enum_$1" => "___D_enum____D_1"
+        name = name.replace('$', '___D_')
+        return '_cffi_e_%s_%s' % (prefix, name)
+
+    def _generate_gen_enum_decl(self, tp, name, prefix='enum'):
+        if tp.partial:
+            for enumerator in tp.enumerators:
+                self._generate_gen_const(True, enumerator)
+            return
+        #
+        funcname = self._enum_funcname(prefix, name)
+        self.export_symbols.append(funcname)
+        prnt = self._prnt
+        prnt('int %s(char *out_error)' % funcname)
+        prnt('{')
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            self._check_int_constant_value(enumerator, enumvalue)
+        prnt('  return 0;')
+        prnt('}')
+        prnt()
+
+    def _loading_gen_enum(self, tp, name, module, prefix='enum'):
+        if tp.partial:
+            enumvalues = [self._load_constant(True, tp, enumerator, module)
+                          for enumerator in tp.enumerators]
+            tp.enumvalues = tuple(enumvalues)
+            tp.partial_resolved = True
+        else:
+            funcname = self._enum_funcname(prefix, name)
+            self._load_known_int_constant(module, funcname)
+
+    def _loaded_gen_enum(self, tp, name, module, library):
+        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+            setattr(library, enumerator, enumvalue)
+            type(library)._cffi_dir.append(enumerator)
+
+    # ----------
+    # macros: for now only for integers
+
+    def _generate_gen_macro_decl(self, tp, name):
+        if tp == '...':
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        self._generate_gen_const(True, name, check_value=check_value)
+
+    _loading_gen_macro = _loaded_noop
+
+    def _loaded_gen_macro(self, tp, name, module, library):
+        if tp == '...':
+            check_value = None
+        else:
+            check_value = tp     # an integer
+        value = self._load_constant(True, tp, name, module,
+                                    check_value=check_value)
+        setattr(library, name, value)
+        type(library)._cffi_dir.append(name)
+
+    # ----------
+    # global variables
+
+    def _generate_gen_variable_decl(self, tp, name):
+        if isinstance(tp, model.ArrayType):
+            if tp.length == '...':
+                prnt = self._prnt
+                funcname = '_cffi_sizeof_%s' % (name,)
+                self.export_symbols.append(funcname)
+                prnt("size_t %s(void)" % funcname)
+                prnt("{")
+                prnt("  return sizeof(%s);" % (name,))
+                prnt("}")
+            tp_ptr = model.PointerType(tp.item)
+            self._generate_gen_const(False, name, tp_ptr)
+        else:
+            tp_ptr = model.PointerType(tp)
+            self._generate_gen_const(False, name, tp_ptr, category='var')
+
+    _loading_gen_variable = _loaded_noop
+
+    def _loaded_gen_variable(self, tp, name, module, library):
+        if isinstance(tp, model.ArrayType):   # int a[5] is "constant" in the
+                                              # sense that "a=..." is forbidden
+            if tp.length == '...':
+                funcname = '_cffi_sizeof_%s' % (name,)
+                BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0]
+                function = module.load_function(BFunc, funcname)
+                size = function()
+                BItemType = self.ffi._get_cached_btype(tp.item)
+                length, rest = divmod(size, self.ffi.sizeof(BItemType))
+                if rest != 0:
+                    raise VerificationError(
+                        "bad size: %r does not seem to be an array of %s" %
+                        (name, tp.item))
+                tp = tp.resolve_length(length)
+            tp_ptr = model.PointerType(tp.item)
+            value = self._load_constant(False, tp_ptr, name, module)
+            # 'value' is a <cdata 'type *'> which we have to replace with
+            # a <cdata 'type[N]'> if the N is actually known
+            if tp.length is not None:
+                BArray = self.ffi._get_cached_btype(tp)
+                value = self.ffi.cast(BArray, value)
+            setattr(library, name, value)
+            type(library)._cffi_dir.append(name)
+            return
+        # remove ptr=<cdata 'int *'> from the library instance, and replace
+        # it by a property on the class, which reads/writes into ptr[0].
+        funcname = '_cffi_var_%s' % name
+        BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0]
+        function = module.load_function(BFunc, funcname)
+        ptr = function()
+        def getter(library):
+            return ptr[0]
+        def setter(library, value):
+            ptr[0] = value
+        setattr(type(library), name, property(getter, setter))
+        type(library)._cffi_dir.append(name)
+
+cffimod_header = r'''
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>   /* XXX for ssize_t on some platforms */
+
+/* this block of #ifs should be kept exactly identical between
+   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
+   and cffi/_cffi_include.h */
+#if defined(_MSC_VER)
+# include <malloc.h>   /* for alloca() */
+# if _MSC_VER < 1600   /* MSVC < 2010 */
+   typedef __int8 int8_t;
+   typedef __int16 int16_t;
+   typedef __int32 int32_t;
+   typedef __int64 int64_t;
+   typedef unsigned __int8 uint8_t;
+   typedef unsigned __int16 uint16_t;
+   typedef unsigned __int32 uint32_t;
+   typedef unsigned __int64 uint64_t;
+   typedef __int8 int_least8_t;
+   typedef __int16 int_least16_t;
+   typedef __int32 int_least32_t;
+   typedef __int64 int_least64_t;
+   typedef unsigned __int8 uint_least8_t;
+   typedef unsigned __int16 uint_least16_t;
+   typedef unsigned __int32 uint_least32_t;
+   typedef unsigned __int64 uint_least64_t;
+   typedef __int8 int_fast8_t;
+   typedef __int16 int_fast16_t;
+   typedef __int32 int_fast32_t;
+   typedef __int64 int_fast64_t;
+   typedef unsigned __int8 uint_fast8_t;
+   typedef unsigned __int16 uint_fast16_t;
+   typedef unsigned __int32 uint_fast32_t;
+   typedef unsigned __int64 uint_fast64_t;
+   typedef __int64 intmax_t;
+   typedef unsigned __int64 uintmax_t;
+# else
+#  include <stdint.h>
+# endif
+# if _MSC_VER < 1800   /* MSVC < 2013 */
+#  ifndef __cplusplus
+    typedef unsigned char _Bool;
+#  endif
+# endif
+#else
+# include <stdint.h>
+# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
+#  include <alloca.h>
+# endif
+#endif
+'''
diff --git a/cffi/verifier.py b/cffi/verifier.py
new file mode 100644
index 0000000..59b78c2
--- /dev/null
+++ b/cffi/verifier.py
@@ -0,0 +1,306 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
+import sys, os, binascii, shutil, io
+from . import __version_verifier_modules__
+from . import ffiplatform
+from .error import VerificationError
+
+if sys.version_info >= (3, 3):
+    import importlib.machinery
+    def _extension_suffixes():
+        return importlib.machinery.EXTENSION_SUFFIXES[:]
+else:
+    import imp
+    def _extension_suffixes():
+        return [suffix for suffix, _, type in imp.get_suffixes()
+                if type == imp.C_EXTENSION]
+
+
+if sys.version_info >= (3,):
+    NativeIO = io.StringIO
+else:
+    class NativeIO(io.BytesIO):
+        def write(self, s):
+            if isinstance(s, unicode):
+                s = s.encode('ascii')
+            super(NativeIO, self).write(s)
+
+
+class Verifier(object):
+
+    def __init__(self, ffi, preamble, tmpdir=None, modulename=None,
+                 ext_package=None, tag='', force_generic_engine=False,
+                 source_extension='.c', flags=None, relative_to=None, **kwds):
+        if ffi._parser._uses_new_feature:
+            raise VerificationError(
+                "feature not supported with ffi.verify(), but only "
+                "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,))
+        self.ffi = ffi
+        self.preamble = preamble
+        if not modulename:
+            flattened_kwds = ffiplatform.flatten(kwds)
+        vengine_class = _locate_engine_class(ffi, force_generic_engine)
+        self._vengine = vengine_class(self)
+        self._vengine.patch_extension_kwds(kwds)
+        self.flags = flags
+        self.kwds = self.make_relative_to(kwds, relative_to)
+        #
+        if modulename:
+            if tag:
+                raise TypeError("can't specify both 'modulename' and 'tag'")
+        else:
+            key = '\x00'.join([sys.version[:3], __version_verifier_modules__,
+                               preamble, flattened_kwds] +
+                              ffi._cdefsources)
+            if sys.version_info >= (3,):
+                key = key.encode('utf-8')
+            k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
+            k1 = k1.lstrip('0x').rstrip('L')
+            k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
+            k2 = k2.lstrip('0').rstrip('L')
+            modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key,
+                                              k1, k2)
+        suffix = _get_so_suffixes()[0]
+        self.tmpdir = tmpdir or _caller_dir_pycache()
+        self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension)
+        self.modulefilename = os.path.join(self.tmpdir, modulename + suffix)
+        self.ext_package = ext_package
+        self._has_source = False
+        self._has_module = False
+
+    def write_source(self, file=None):
+        """Write the C source code.  It is produced in 'self.sourcefilename',
+        which can be tweaked beforehand."""
+        with self.ffi._lock:
+            if self._has_source and file is None:
+                raise VerificationError(
+                    "source code already written")
+            self._write_source(file)
+
+    def compile_module(self):
+        """Write the C source code (if not done already) and compile it.
+        This produces a dynamic link library in 'self.modulefilename'."""
+        with self.ffi._lock:
+            if self._has_module:
+                raise VerificationError("module already compiled")
+            if not self._has_source:
+                self._write_source()
+            self._compile_module()
+
+    def load_library(self):
+        """Get a C module from this Verifier instance.
+        Returns an instance of a FFILibrary class that behaves like the
+        objects returned by ffi.dlopen(), but that delegates all
+        operations to the C module.  If necessary, the C code is written
+        and compiled first.
+        """
+        with self.ffi._lock:
+            if not self._has_module:
+                self._locate_module()
+                if not self._has_module:
+                    if not self._has_source:
+                        self._write_source()
+                    self._compile_module()
+            return self._load_library()
+
+    def get_module_name(self):
+        basename = os.path.basename(self.modulefilename)
+        # kill both the .so extension and the other .'s, as introduced
+        # by Python 3: 'basename.cpython-33m.so'
+        basename = basename.split('.', 1)[0]
+        # and the _d added in Python 2 debug builds --- but try to be
+        # conservative and not kill a legitimate _d
+        if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'):
+            basename = basename[:-2]
+        return basename
+
+    def get_extension(self):
+        ffiplatform._hack_at_distutils() # backward compatibility hack
+        if not self._has_source:
+            with self.ffi._lock:
+                if not self._has_source:
+                    self._write_source()
+        sourcename = ffiplatform.maybe_relative_path(self.sourcefilename)
+        modname = self.get_module_name()
+        return ffiplatform.get_extension(sourcename, modname, **self.kwds)
+
+    def generates_python_module(self):
+        return self._vengine._gen_python_module
+
+    def make_relative_to(self, kwds, relative_to):
+        if relative_to and os.path.dirname(relative_to):
+            dirname = os.path.dirname(relative_to)
+            kwds = kwds.copy()
+            for key in ffiplatform.LIST_OF_FILE_NAMES:
+                if key in kwds:
+                    lst = kwds[key]
+                    if not isinstance(lst, (list, tuple)):
+                        raise TypeError("keyword '%s' should be a list or tuple"
+                                        % (key,))
+                    lst = [os.path.join(dirname, fn) for fn in lst]
+                    kwds[key] = lst
+        return kwds
+
+    # ----------
+
+    def _locate_module(self):
+        if not os.path.isfile(self.modulefilename):
+            if self.ext_package:
+                try:
+                    pkg = __import__(self.ext_package, None, None, ['__doc__'])
+                except ImportError:
+                    return      # cannot import the package itself, give up
+                    # (e.g. it might be called differently before installation)
+                path = pkg.__path__
+            else:
+                path = None
+            filename = self._vengine.find_module(self.get_module_name(), path,
+                                                 _get_so_suffixes())
+            if filename is None:
+                return
+            self.modulefilename = filename
+        self._vengine.collect_types()
+        self._has_module = True
+
+    def _write_source_to(self, file):
+        self._vengine._f = file
+        try:
+            self._vengine.write_source_to_f()
+        finally:
+            del self._vengine._f
+
+    def _write_source(self, file=None):
+        if file is not None:
+            self._write_source_to(file)
+        else:
+            # Write our source file to an in memory file.
+            f = NativeIO()
+            self._write_source_to(f)
+            source_data = f.getvalue()
+
+            # Determine if this matches the current file
+            if os.path.exists(self.sourcefilename):
+                with open(self.sourcefilename, "r") as fp:
+                    needs_written = not (fp.read() == source_data)
+            else:
+                needs_written = True
+
+            # Actually write the file out if it doesn't match
+            if needs_written:
+                _ensure_dir(self.sourcefilename)
+                with open(self.sourcefilename, "w") as fp:
+                    fp.write(source_data)
+
+            # Set this flag
+            self._has_source = True
+
+    def _compile_module(self):
+        # compile this C source
+        tmpdir = os.path.dirname(self.sourcefilename)
+        outputfilename = ffiplatform.compile(tmpdir, self.get_extension())
+        try:
+            same = ffiplatform.samefile(outputfilename, self.modulefilename)
+        except OSError:
+            same = False
+        if not same:
+            _ensure_dir(self.modulefilename)
+            shutil.move(outputfilename, self.modulefilename)
+        self._has_module = True
+
+    def _load_library(self):
+        assert self._has_module
+        if self.flags is not None:
+            return self._vengine.load_library(self.flags)
+        else:
+            return self._vengine.load_library()
+
+# ____________________________________________________________
+
+_FORCE_GENERIC_ENGINE = False      # for tests
+
+def _locate_engine_class(ffi, force_generic_engine):
+    if _FORCE_GENERIC_ENGINE:
+        force_generic_engine = True
+    if not force_generic_engine:
+        if '__pypy__' in sys.builtin_module_names:
+            force_generic_engine = True
+        else:
+            try:
+                import _cffi_backend
+            except ImportError:
+                _cffi_backend = '?'
+            if ffi._backend is not _cffi_backend:
+                force_generic_engine = True
+    if force_generic_engine:
+        from . import vengine_gen
+        return vengine_gen.VGenericEngine
+    else:
+        from . import vengine_cpy
+        return vengine_cpy.VCPythonEngine
+
+# ____________________________________________________________
+
+_TMPDIR = None
+
+def _caller_dir_pycache():
+    if _TMPDIR:
+        return _TMPDIR
+    result = os.environ.get('CFFI_TMPDIR')
+    if result:
+        return result
+    filename = sys._getframe(2).f_code.co_filename
+    return os.path.abspath(os.path.join(os.path.dirname(filename),
+                           '__pycache__'))
+
+def set_tmpdir(dirname):
+    """Set the temporary directory to use instead of __pycache__."""
+    global _TMPDIR
+    _TMPDIR = dirname
+
+def cleanup_tmpdir(tmpdir=None, keep_so=False):
+    """Clean up the temporary directory by removing all files in it
+    called `_cffi_*.{c,so}` as well as the `build` subdirectory."""
+    tmpdir = tmpdir or _caller_dir_pycache()
+    try:
+        filelist = os.listdir(tmpdir)
+    except OSError:
+        return
+    if keep_so:
+        suffix = '.c'   # only remove .c files
+    else:
+        suffix = _get_so_suffixes()[0].lower()
+    for fn in filelist:
+        if fn.lower().startswith('_cffi_') and (
+                fn.lower().endswith(suffix) or fn.lower().endswith('.c')):
+            try:
+                os.unlink(os.path.join(tmpdir, fn))
+            except OSError:
+                pass
+    clean_dir = [os.path.join(tmpdir, 'build')]
+    for dir in clean_dir:
+        try:
+            for fn in os.listdir(dir):
+                fn = os.path.join(dir, fn)
+                if os.path.isdir(fn):
+                    clean_dir.append(fn)
+                else:
+                    os.unlink(fn)
+        except OSError:
+            pass
+
+def _get_so_suffixes():
+    suffixes = _extension_suffixes()
+    if not suffixes:
+        # bah, no C_EXTENSION available.  Occurs on pypy without cpyext
+        if sys.platform == 'win32':
+            suffixes = [".pyd"]
+        else:
+            suffixes = [".so"]
+
+    return suffixes
+
+def _ensure_dir(filename):
+    dirname = os.path.dirname(filename)
+    if dirname and not os.path.isdir(dirname):
+        os.makedirs(dirname)
diff --git a/demo/_curses.py b/demo/_curses.py
new file mode 100644
index 0000000..46443c2
--- /dev/null
+++ b/demo/_curses.py
@@ -0,0 +1,1075 @@
+"""Reimplementation of the standard extension module '_curses' using cffi."""
+
+import sys
+from functools import wraps
+
+from _curses_cffi import ffi, lib
+
+
+def _copy_to_globals(name):
+    globals()[name] = getattr(lib, name)
+
+
+def _setup():
+    for name in ['ERR', 'OK', 'KEY_MIN', 'KEY_MAX',
+                 'A_ATTRIBUTES', 'A_NORMAL', 'A_STANDOUT', 'A_UNDERLINE',
+                 'A_REVERSE', 'A_BLINK', 'A_DIM', 'A_BOLD', 'A_ALTCHARSET',
+                 'A_PROTECT', 'A_CHARTEXT', 'A_COLOR',
+                 'COLOR_BLACK', 'COLOR_RED', 'COLOR_GREEN', 'COLOR_YELLOW',
+                 'COLOR_BLUE', 'COLOR_MAGENTA', 'COLOR_CYAN', 'COLOR_WHITE',
+                 ]:
+        _copy_to_globals(name)
+
+    if not lib._m_NetBSD:
+        _copy_to_globals('A_INVIS')
+
+    for name in ['A_HORIZONTAL', 'A_LEFT', 'A_LOW', 'A_RIGHT', 'A_TOP',
+                 'A_VERTICAL',
+                 ]:
+        if hasattr(lib, name):
+            _copy_to_globals(name)
+
+    if lib._m_NCURSES_MOUSE_VERSION:
+        for name in ["BUTTON1_PRESSED", "BUTTON1_RELEASED", "BUTTON1_CLICKED",
+                     "BUTTON1_DOUBLE_CLICKED", "BUTTON1_TRIPLE_CLICKED",
+                     "BUTTON2_PRESSED", "BUTTON2_RELEASED", "BUTTON2_CLICKED",
+                     "BUTTON2_DOUBLE_CLICKED", "BUTTON2_TRIPLE_CLICKED",
+                     "BUTTON3_PRESSED", "BUTTON3_RELEASED", "BUTTON3_CLICKED",
+                     "BUTTON3_DOUBLE_CLICKED", "BUTTON3_TRIPLE_CLICKED",
+                     "BUTTON4_PRESSED", "BUTTON4_RELEASED", "BUTTON4_CLICKED",
+                     "BUTTON4_DOUBLE_CLICKED", "BUTTON4_TRIPLE_CLICKED",
+                     "BUTTON_SHIFT", "BUTTON_CTRL", "BUTTON_ALT",
+                     "ALL_MOUSE_EVENTS", "REPORT_MOUSE_POSITION",
+                     ]:
+            _copy_to_globals(name)
+
+    if not lib._m_NetBSD:
+        for key in range(lib.KEY_MIN, lib.KEY_MAX):
+            key_n = lib.keyname(key)
+            if key_n == ffi.NULL:
+                continue
+            key_n = ffi.string(key_n)
+            if key_n == b"UNKNOWN KEY":
+                continue
+            if not isinstance(key_n, str):   # python 3
+                key_n = key_n.decode()
+            key_n = key_n.replace('(', '').replace(')', '')
+            globals()[key_n] = key
+
+_setup()
+
+# Do we want this?
+# version = "2.2"
+# __version__ = "2.2"
+
+
+# ____________________________________________________________
+
+
+_initialised_setupterm = False
+_initialised = False
+_initialised_color = False
+
+
+def _ensure_initialised_setupterm():
+    if not _initialised_setupterm:
+        raise error("must call (at least) setupterm() first")
+
+
+def _ensure_initialised():
+    if not _initialised:
+        raise error("must call initscr() first")
+
+
+def _ensure_initialised_color():
+    if not _initialised and _initialised_color:
+        raise error("must call start_color() first")
+
+
+def _check_ERR(code, fname):
+    if code != lib.ERR:
+        return None
+    elif fname is None:
+        raise error("curses function returned ERR")
+    else:
+        raise error("%s() returned ERR" % (fname,))
+
+
+def _check_NULL(rval):
+    if rval == ffi.NULL:
+        raise error("curses function returned NULL")
+    return rval
+
+
+def _call_lib(method_name, *args):
+    return getattr(lib, method_name)(*args)
+
+
+def _call_lib_check_ERR(method_name, *args):
+    return _check_ERR(_call_lib(method_name, *args), method_name)
+
+
+def _mk_no_return(method_name):
+    def _execute():
+        _ensure_initialised()
+        return _call_lib_check_ERR(method_name)
+    _execute.__name__ = method_name
+    return _execute
+
+
+def _mk_flag_func(method_name):
+    # This is in the CPython implementation, but not documented anywhere.
+    # We have to support it, though, even if it make me sad.
+    def _execute(flag=True):
+        _ensure_initialised()
+        if flag:
+            return _call_lib_check_ERR(method_name)
+        else:
+            return _call_lib_check_ERR('no' + method_name)
+    _execute.__name__ = method_name
+    return _execute
+
+
+def _mk_return_val(method_name):
+    def _execute():
+        return _call_lib(method_name)
+    _execute.__name__ = method_name
+    return _execute
+
+
+def _mk_w_getyx(method_name):
+    def _execute(self):
+        y = _call_lib(method_name + 'y', self._win)
+        x = _call_lib(method_name + 'x', self._win)
+        return (y, x)
+    _execute.__name__ = method_name
+    return _execute
+
+
+def _mk_w_no_return(method_name):
+    def _execute(self, *args):
+        return _call_lib_check_ERR(method_name, self._win, *args)
+    _execute.__name__ = method_name
+    return _execute
+
+
+def _mk_w_return_val(method_name):
+    def _execute(self, *args):
+        return _call_lib(method_name, self._win, *args)
+    _execute.__name__ = method_name
+    return _execute
+
+
+def _chtype(ch):
+    return int(ffi.cast("chtype", ch))
+
+def _texttype(text):
+    if isinstance(text, str):
+        return text
+    elif isinstance(text, unicode):
+        return str(text)   # default encoding
+    else:
+        raise TypeError("str or unicode expected, got a '%s' object"
+                        % (type(text).__name__,))
+
+
+def _extract_yx(args):
+    if len(args) >= 2:
+        return (args[0], args[1], args[2:])
+    return (None, None, args)
+
+
+def _process_args(funcname, args, count, optcount, frontopt=0):
+    outargs = []
+    if frontopt:
+        if len(args) > count + optcount:
+            # We have the front optional args here.
+            outargs.extend(args[:frontopt])
+            args = args[frontopt:]
+        else:
+            # No front optional args, so make them None.
+            outargs.extend([None] * frontopt)
+    if (len(args) < count) or (len(args) > count + optcount):
+        raise error("%s requires %s to %s arguments" % (
+                funcname, count, count + optcount + frontopt))
+    outargs.extend(args)
+    return outargs
+
+
+def _argspec(count, optcount=0, frontopt=0):
+    def _argspec_deco(func):
+        @wraps(func)
+        def _wrapped(self, *args):
+            outargs = _process_args(
+                func.__name__, args, count, optcount, frontopt)
+            return func(self, *outargs)
+        return _wrapped
+    return _argspec_deco
+
+
+# ____________________________________________________________
+
+
+class error(Exception):
+    pass
+
+
+class Window(object):
+    def __init__(self, window):
+        self._win = window
+
+    def __del__(self):
+        if self._win != lib.stdscr:
+            lib.delwin(self._win)
+
+    untouchwin = _mk_w_no_return("untouchwin")
+    touchwin = _mk_w_no_return("touchwin")
+    redrawwin = _mk_w_no_return("redrawwin")
+    insertln = _mk_w_no_return("winsertln")
+    erase = _mk_w_no_return("werase")
+    deleteln = _mk_w_no_return("wdeleteln")
+
+    is_wintouched = _mk_w_return_val("is_wintouched")
+
+    syncdown = _mk_w_return_val("wsyncdown")
+    syncup = _mk_w_return_val("wsyncup")
+    standend = _mk_w_return_val("wstandend")
+    standout = _mk_w_return_val("wstandout")
+    cursyncup = _mk_w_return_val("wcursyncup")
+    clrtoeol = _mk_w_return_val("wclrtoeol")
+    clrtobot = _mk_w_return_val("wclrtobot")
+    clear = _mk_w_return_val("wclear")
+
+    idcok = _mk_w_no_return("idcok")
+    immedok = _mk_w_no_return("immedok")
+    timeout = _mk_w_no_return("wtimeout")
+
+    getyx = _mk_w_getyx("getcur")
+    getbegyx = _mk_w_getyx("getbeg")
+    getmaxyx = _mk_w_getyx("getmax")
+    getparyx = _mk_w_getyx("getpar")
+
+    clearok = _mk_w_no_return("clearok")
+    idlok = _mk_w_no_return("idlok")
+    leaveok = _mk_w_no_return("leaveok")
+    notimeout = _mk_w_no_return("notimeout")
+    scrollok = _mk_w_no_return("scrollok")
+    insdelln = _mk_w_no_return("winsdelln")
+    syncok = _mk_w_no_return("syncok")
+
+    mvwin = _mk_w_no_return("mvwin")
+    mvderwin = _mk_w_no_return("mvderwin")
+    move = _mk_w_no_return("wmove")
+
+    if not lib._m_STRICT_SYSV_CURSES:
+        resize = _mk_w_no_return("wresize")
+
+    if lib._m_NetBSD:
+        keypad = _mk_w_return_val("keypad")
+        nodelay = _mk_w_return_val("nodelay")
+    else:
+        keypad = _mk_w_no_return("keypad")
+        nodelay = _mk_w_no_return("nodelay")
+
+    @_argspec(1, 1, 2)
+    def addch(self, y, x, ch, attr=None):
+        if attr is None:
+            attr = lib.A_NORMAL
+        ch = _chtype(ch)
+
+        if y is not None:
+            code = lib.mvwaddch(self._win, y, x, ch | attr)
+        else:
+            code = lib.waddch(self._win, ch | attr)
+        return _check_ERR(code, "addch")
+
+    @_argspec(1, 1, 2)
+    def addstr(self, y, x, text, attr=None):
+        text = _texttype(text)
+        if attr is not None:
+            attr_old = lib.getattrs(self._win)
+            lib.wattrset(self._win, attr)
+        if y is not None:
+            code = lib.mvwaddstr(self._win, y, x, text)
+        else:
+            code = lib.waddstr(self._win, text)
+        if attr is not None:
+            lib.wattrset(self._win, attr_old)
+        return _check_ERR(code, "addstr")
+
+    @_argspec(2, 1, 2)
+    def addnstr(self, y, x, text, n, attr=None):
+        text = _texttype(text)
+        if attr is not None:
+            attr_old = lib.getattrs(self._win)
+            lib.wattrset(self._win, attr)
+        if y is not None:
+            code = lib.mvwaddnstr(self._win, y, x, text, n)
+        else:
+            code = lib.waddnstr(self._win, text, n)
+        if attr is not None:
+            lib.wattrset(self._win, attr_old)
+        return _check_ERR(code, "addnstr")
+
+    def bkgd(self, ch, attr=None):
+        if attr is None:
+            attr = lib.A_NORMAL
+        return _check_ERR(lib.wbkgd(self._win, _chtype(ch) | attr), "bkgd")
+
+    attroff = _mk_w_no_return("wattroff")
+    attron = _mk_w_no_return("wattron")
+    attrset = _mk_w_no_return("wattrset")
+
+    def bkgdset(self, ch, attr=None):
+        if attr is None:
+            attr = lib.A_NORMAL
+        lib.wbkgdset(self._win, _chtype(ch) | attr)
+        return None
+
+    def border(self, ls=0, rs=0, ts=0, bs=0, tl=0, tr=0, bl=0, br=0):
+        lib.wborder(self._win,
+                    _chtype(ls), _chtype(rs), _chtype(ts), _chtype(bs),
+                    _chtype(tl), _chtype(tr), _chtype(bl), _chtype(br))
+        return None
+
+    def box(self, vertint=0, horint=0):
+        lib.box(self._win, vertint, horint)
+        return None
+
+    @_argspec(1, 1, 2)
+    def chgat(self, y, x, num, attr=None):
+        # These optional args are in a weird order.
+        if attr is None:
+            attr = num
+            num = -1
+
+        color = ((attr >> 8) & 0xff)
+        attr = attr - (color << 8)
+
+        if y is not None:
+            code = lib.mvwchgat(self._win, y, x, num, attr, color, ffi.NULL)
+            lib.touchline(self._win, y, 1)
+        else:
+            yy, _ = self.getyx()
+            code = lib.wchgat(self._win, num, attr, color, ffi.NULL)
+            lib.touchline(self._win, yy, 1)
+        return _check_ERR(code, "chgat")
+
+    def delch(self, *args):
+        if len(args) == 0:
+            code = lib.wdelch(self._win)
+        elif len(args) == 2:
+            code = lib.mvwdelch(self._win, *args)
+        else:
+            raise error("delch requires 0 or 2 arguments")
+        return _check_ERR(code, "[mv]wdelch")
+
+    def derwin(self, *args):
+        nlines = 0
+        ncols = 0
+        if len(args) == 2:
+            begin_y, begin_x = args
+        elif len(args) == 4:
+            nlines, ncols, begin_y, begin_x = args
+        else:
+            raise error("derwin requires 2 or 4 arguments")
+
+        win = lib.derwin(self._win, nlines, ncols, begin_y, begin_x)
+        return Window(_check_NULL(win))
+
+    def echochar(self, ch, attr=None):
+        if attr is None:
+            attr = lib.A_NORMAL
+        ch = _chtype(ch)
+
+        if lib._m_ispad(self._win):
+            code = lib.pechochar(self._win, ch | attr)
+        else:
+            code = lib.wechochar(self._win, ch | attr)
+        return _check_ERR(code, "echochar")
+
+    if lib._m_NCURSES_MOUSE_VERSION:
+        enclose = _mk_w_return_val("wenclose")
+
+    getbkgd = _mk_w_return_val("getbkgd")
+
+    def getch(self, *args):
+        if len(args) == 0:
+            val = lib.wgetch(self._win)
+        elif len(args) == 2:
+            val = lib.mvwgetch(self._win, *args)
+        else:
+            raise error("getch requires 0 or 2 arguments")
+        return val
+
+    def getkey(self, *args):
+        if len(args) == 0:
+            val = lib.wgetch(self._win)
+        elif len(args) == 2:
+            val = lib.mvwgetch(self._win, *args)
+        else:
+            raise error("getkey requires 0 or 2 arguments")
+
+        if val == lib.ERR:
+            raise error("no input")
+        elif val <= 255:
+            return chr(val)
+        else:
+            # XXX: The following line is different if `__NetBSD__` is defined.
+            val = lib.keyname(val)
+            if val == ffi.NULL:
+                return ""
+            return ffi.string(val)
+
+    @_argspec(0, 1, 2)
+    def getstr(self, y, x, n=1023):
+        n = min(n, 1023)
+        buf = ffi.new("char[1024]")  # /* This should be big enough.. I hope */
+
+        if y is None:
+            val = lib.wgetnstr(self._win, buf, n)
+        else:
+            val = lib.mvwgetnstr(self._win, y, x, buf, n)
+
+        if val == lib.ERR:
+            return ""
+        return ffi.string(buf)
+
+    @_argspec(2, 1, 2)
+    def hline(self, y, x, ch, n, attr=None):
+        ch = _chtype(ch)
+        if attr is None:
+            attr = lib.A_NORMAL
+        if y is not None:
+            _check_ERR(lib.wmove(self._win, y, x), "wmove")
+        return _check_ERR(lib.whline(self._win, ch | attr, n), "hline")
+
+    @_argspec(1, 1, 2)
+    def insch(self, y, x, ch, attr=None):
+        ch = _chtype(ch)
+        if attr is None:
+            attr = lib.A_NORMAL
+        if y is not None:
+            code = lib.mvwinsch(self._win, y, x, ch | attr)
+        else:
+            code = lib.winsch(self._win, ch | attr)
+        return _check_ERR(code, "insch")
+
+    def inch(self, *args):
+        if len(args) == 0:
+            return lib.winch(self._win)
+        elif len(args) == 2:
+            return lib.mvwinch(self._win, *args)
+        else:
+            raise error("inch requires 0 or 2 arguments")
+
+    @_argspec(0, 1, 2)
+    def instr(self, y, x, n=1023):
+        n = min(n, 1023)
+        buf = ffi.new("char[1024]")  # /* This should be big enough.. I hope */
+        if y is None:
+            code = lib.winnstr(self._win, buf, n)
+        else:
+            code = lib.mvwinnstr(self._win, y, x, buf, n)
+
+        if code == lib.ERR:
+            return ""
+        return ffi.string(buf)
+
+    @_argspec(1, 1, 2)
+    def insstr(self, y, x, text, attr=None):
+        text = _texttype(text)
+        if attr is not None:
+            attr_old = lib.getattrs(self._win)
+            lib.wattrset(self._win, attr)
+        if y is not None:
+            code = lib.mvwinsstr(self._win, y, x, text)
+        else:
+            code = lib.winsstr(self._win, text)
+        if attr is not None:
+            lib.wattrset(self._win, attr_old)
+        return _check_ERR(code, "insstr")
+
+    @_argspec(2, 1, 2)
+    def insnstr(self, y, x, text, n, attr=None):
+        text = _texttype(text)
+        if attr is not None:
+            attr_old = lib.getattrs(self._win)
+            lib.wattrset(self._win, attr)
+        if y is not None:
+            code = lib.mvwinsnstr(self._win, y, x, text, n)
+        else:
+            code = lib.winsnstr(self._win, text, n)
+        if attr is not None:
+            lib.wattrset(self._win, attr_old)
+        return _check_ERR(code, "insnstr")
+
+    def is_linetouched(self, line):
+        code = lib.is_linetouched(self._win, line)
+        if code == lib.ERR:
+            raise error("is_linetouched: line number outside of boundaries")
+        if code == lib.FALSE:
+            return False
+        return True
+
+    def noutrefresh(self, *args):
+        if lib._m_ispad(self._win):
+            if len(args) != 6:
+                raise error(
+                    "noutrefresh() called for a pad requires 6 arguments")
+            return _check_ERR(lib.pnoutrefresh(self._win, *args),
+                              "pnoutrefresh")
+        else:
+            # XXX: Better args check here? We need zero args.
+            return _check_ERR(lib.wnoutrefresh(self._win, *args),
+                              "wnoutrefresh")
+
+    nooutrefresh = noutrefresh  # "to be removed in 2.3", but in 2.7, 3.x.
+
+    def _copywin(self, dstwin, overlay,
+                 sminr, sminc, dminr, dminc, dmaxr, dmaxc):
+        return _check_ERR(lib.copywin(self._win, dstwin._win,
+                                      sminr, sminc, dminr, dminc, dmaxr, dmaxc,
+                                      overlay), "copywin")
+
+    def overlay(self, dstwin, *args):
+        if len(args) == 6:
+            return self._copywin(dstwin, True, *args)
+        elif len(args) == 0:
+            return _check_ERR(lib.overlay(self._win, dstwin._win), "overlay")
+        else:
+            raise error("overlay requires one or seven arguments")
+
+    def overwrite(self, dstwin, *args):
+        if len(args) == 6:
+            return self._copywin(dstwin, False, *args)
+        elif len(args) == 0:
+            return _check_ERR(lib.overwrite(self._win, dstwin._win),
+                              "overwrite")
+        else:
+            raise error("overwrite requires one or seven arguments")
+
+    def putwin(self, filep):
+        # filestar = ffi.new("FILE *", filep)
+        return _check_ERR(lib.putwin(self._win, filep), "putwin")
+
+    def redrawln(self, beg, num):
+        return _check_ERR(lib.wredrawln(self._win, beg, num), "redrawln")
+
+    def refresh(self, *args):
+        if lib._m_ispad(self._win):
+            if len(args) != 6:
+                raise error(
+                    "noutrefresh() called for a pad requires 6 arguments")
+            return _check_ERR(lib.prefresh(self._win, *args), "prefresh")
+        else:
+            # XXX: Better args check here? We need zero args.
+            return _check_ERR(lib.wrefresh(self._win, *args), "wrefresh")
+
+    def setscrreg(self, y, x):
+        return _check_ERR(lib.wsetscrreg(self._win, y, x), "wsetscrreg")
+
+    def subwin(self, *args):
+        nlines = 0
+        ncols = 0
+        if len(args) == 2:
+            begin_y, begin_x = args
+        elif len(args) == 4:
+            nlines, ncols, begin_y, begin_x = args
+        else:
+            raise error("subwin requires 2 or 4 arguments")
+
+        if lib._m_ispad(self._win):
+            win = lib.subpad(self._win, nlines, ncols, begin_y, begin_x)
+        else:
+            win = lib.subwin(self._win, nlines, ncols, begin_y, begin_x)
+        return Window(_check_NULL(win))
+
+    def scroll(self, nlines=None):
+        if nlines is None:
+            return _check_ERR(lib.scroll(self._win), "scroll")
+        else:
+            return _check_ERR(lib.wscrl(self._win, nlines), "scroll")
+
+    def touchline(self, st, cnt, val=None):
+        if val is None:
+            return _check_ERR(lib.touchline(self._win, st, cnt), "touchline")
+        else:
+            return _check_ERR(lib.wtouchln(self._win, st, cnt, val),
+                              "touchline")
+
+    @_argspec(2, 1, 2)
+    def vline(self, y, x, ch, n, attr=None):
+        ch = _chtype(ch)
+        if attr is None:
+            attr = lib.A_NORMAL
+        if y is not None:
+            _check_ERR(lib.wmove(self._win, y, x), "wmove")
+        return _check_ERR(lib.wvline(self._win, ch | attr, n), "vline")
+
+
+beep = _mk_no_return("beep")
+def_prog_mode = _mk_no_return("def_prog_mode")
+def_shell_mode = _mk_no_return("def_shell_mode")
+doupdate = _mk_no_return("doupdate")
+endwin = _mk_no_return("endwin")
+flash = _mk_no_return("flash")
+nocbreak = _mk_no_return("nocbreak")
+noecho = _mk_no_return("noecho")
+nonl = _mk_no_return("nonl")
+noraw = _mk_no_return("noraw")
+reset_prog_mode = _mk_no_return("reset_prog_mode")
+reset_shell_mode = _mk_no_return("reset_shell_mode")
+resetty = _mk_no_return("resetty")
+savetty = _mk_no_return("savetty")
+
+cbreak = _mk_flag_func("cbreak")
+echo = _mk_flag_func("echo")
+nl = _mk_flag_func("nl")
+raw = _mk_flag_func("raw")
+
+baudrate = _mk_return_val("baudrate")
+termattrs = _mk_return_val("termattrs")
+
+termname = _mk_return_val("termname")
+longname = _mk_return_val("longname")
+
+can_change_color = _mk_return_val("can_change_color")
+has_colors = _mk_return_val("has_colors")
+has_ic = _mk_return_val("has_ic")
+has_il = _mk_return_val("has_il")
+isendwin = _mk_return_val("isendwin")
+flushinp = _mk_return_val("flushinp")
+noqiflush = _mk_return_val("noqiflush")
+
+
+def filter():
+    lib.filter()
+    return None
+
+
+def color_content(color):
+    _ensure_initialised_color()
+    r, g, b = ffi.new("short *"), ffi.new("short *"), ffi.new("short *")
+    if lib.color_content(color, r, g, b) == lib.ERR:
+        raise error("Argument 1 was out of range. Check value of COLORS.")
+    return (r[0], g[0], b[0])
+
+
+def color_pair(n):
+    _ensure_initialised_color()
+    return (n << 8)
+
+
+def curs_set(vis):
+    _ensure_initialised()
+    val = lib.curs_set(vis)
+    _check_ERR(val, "curs_set")
+    return val
+
+
+def delay_output(ms):
+    _ensure_initialised()
+    return _check_ERR(lib.delay_output(ms), "delay_output")
+
+
+def erasechar():
+    _ensure_initialised()
+    return lib.erasechar()
+
+
+def getsyx():
+    _ensure_initialised()
+    yx = ffi.new("int[2]")
+    lib._m_getsyx(yx)
+    return (yx[0], yx[1])
+
+
+if lib._m_NCURSES_MOUSE_VERSION:
+
+    def getmouse():
+        _ensure_initialised()
+        mevent = ffi.new("MEVENT *")
+        _check_ERR(lib.getmouse(mevent), "getmouse")
+        return (mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate)
+
+    def ungetmouse(id, x, y, z, bstate):
+        _ensure_initialised()
+        mevent = ffi.new("MEVENT *")
+        mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate = (
+            id, x, y, z, bstate)
+        return _check_ERR(lib.ungetmouse(mevent), "ungetmouse")
+
+
+def getwin(filep):
+    return Window(_check_NULL(lib.getwin(filep)))
+
+
+def halfdelay(tenths):
+    _ensure_initialised()
+    return _check_ERR(lib.halfdelay(tenths), "halfdelay")
+
+
+if not lib._m_STRICT_SYSV_CURSES:
+    def has_key(ch):
+        _ensure_initialised()
+        return lib.has_key(ch)
+
+
+def init_color(color, r, g, b):
+    _ensure_initialised_color()
+    return _check_ERR(lib.init_color(color, r, g, b), "init_color")
+
+
+def init_pair(pair, f, b):
+    _ensure_initialised_color()
+    return _check_ERR(lib.init_pair(pair, f, b), "init_pair")
+
+
+def _mk_acs(name, ichar):
+    if len(ichar) == 1:
+        globals()[name] = lib.acs_map[ord(ichar)]
+    else:
+        globals()[name] = globals()[ichar]
+
+
+def _map_acs():
+    _mk_acs("ACS_ULCORNER", 'l')
+    _mk_acs("ACS_LLCORNER", 'm')
+    _mk_acs("ACS_URCORNER", 'k')
+    _mk_acs("ACS_LRCORNER", 'j')
+    _mk_acs("ACS_LTEE", 't')
+    _mk_acs("ACS_RTEE", 'u')
+    _mk_acs("ACS_BTEE", 'v')
+    _mk_acs("ACS_TTEE", 'w')
+    _mk_acs("ACS_HLINE", 'q')
+    _mk_acs("ACS_VLINE", 'x')
+    _mk_acs("ACS_PLUS", 'n')
+    _mk_acs("ACS_S1", 'o')
+    _mk_acs("ACS_S9", 's')
+    _mk_acs("ACS_DIAMOND", '`')
+    _mk_acs("ACS_CKBOARD", 'a')
+    _mk_acs("ACS_DEGREE", 'f')
+    _mk_acs("ACS_PLMINUS", 'g')
+    _mk_acs("ACS_BULLET", '~')
+    _mk_acs("ACS_LARROW", ',')
+    _mk_acs("ACS_RARROW", '+')
+    _mk_acs("ACS_DARROW", '.')
+    _mk_acs("ACS_UARROW", '-')
+    _mk_acs("ACS_BOARD", 'h')
+    _mk_acs("ACS_LANTERN", 'i')
+    _mk_acs("ACS_BLOCK", '0')
+    _mk_acs("ACS_S3", 'p')
+    _mk_acs("ACS_S7", 'r')
+    _mk_acs("ACS_LEQUAL", 'y')
+    _mk_acs("ACS_GEQUAL", 'z')
+    _mk_acs("ACS_PI", '{')
+    _mk_acs("ACS_NEQUAL", '|')
+    _mk_acs("ACS_STERLING", '}')
+    _mk_acs("ACS_BSSB", "ACS_ULCORNER")
+    _mk_acs("ACS_SSBB", "ACS_LLCORNER")
+    _mk_acs("ACS_BBSS", "ACS_URCORNER")
+    _mk_acs("ACS_SBBS", "ACS_LRCORNER")
+    _mk_acs("ACS_SBSS", "ACS_RTEE")
+    _mk_acs("ACS_SSSB", "ACS_LTEE")
+    _mk_acs("ACS_SSBS", "ACS_BTEE")
+    _mk_acs("ACS_BSSS", "ACS_TTEE")
+    _mk_acs("ACS_BSBS", "ACS_HLINE")
+    _mk_acs("ACS_SBSB", "ACS_VLINE")
+    _mk_acs("ACS_SSSS", "ACS_PLUS")
+
+
+def initscr():
+    if _initialised:
+        lib.wrefresh(lib.stdscr)
+        return Window(lib.stdscr)
+
+    win = _check_NULL(lib.initscr())
+    globals()['_initialised_setupterm'] = True
+    globals()['_initialised'] = True
+
+    _map_acs()
+
+    globals()["LINES"] = lib.LINES
+    globals()["COLS"] = lib.COLS
+
+    return Window(win)
+
+
+def setupterm(term=None, fd=-1):
+    if fd == -1:
+        # XXX: Check for missing stdout here?
+        fd = sys.stdout.fileno()
+
+    if _initialised_setupterm:
+        return None
+
+    if term is None:
+        term = ffi.NULL
+    err = ffi.new("int *")
+    if lib.setupterm(term, fd, err) == lib.ERR:
+        err = err[0]
+        if err == 0:
+            raise error("setupterm: could not find terminal")
+        elif err == -1:
+            raise error("setupterm: could not find terminfo database")
+        else:
+            raise error("setupterm: unknown error")
+
+    globals()["_initialised_setupterm"] = True
+    return None
+
+
+def intrflush(ch):
+    _ensure_initialised()
+    return _check_ERR(lib.intrflush(ffi.NULL, ch), "intrflush")
+
+
+# XXX: #ifdef HAVE_CURSES_IS_TERM_RESIZED
+def is_term_resized(lines, columns):
+    _ensure_initialised()
+    return lib.is_term_resized(lines, columns)
+
+
+if not lib._m_NetBSD:
+    def keyname(ch):
+        _ensure_initialised()
+        if ch < 0:
+            raise error("invalid key number")
+        knp = lib.keyname(ch)
+        if knp == ffi.NULL:
+            return ""
+        return ffi.string(knp)
+
+
+def killchar():
+    return lib.killchar()
+
+
+def meta(ch):
+    return _check_ERR(lib.meta(lib.stdscr, ch), "meta")
+
+
+if lib._m_NCURSES_MOUSE_VERSION:
+
+    def mouseinterval(interval):
+        _ensure_initialised()
+        return _check_ERR(lib.mouseinterval(interval), "mouseinterval")
+
+    def mousemask(newmask):
+        _ensure_initialised()
+        oldmask = ffi.new("mmask_t *")
+        availmask = lib.mousemask(newmask, oldmask)
+        return (availmask, oldmask)
+
+
+def napms(ms):
+    _ensure_initialised()
+    return lib.napms(ms)
+
+
+def newpad(nlines, ncols):
+    _ensure_initialised()
+    return Window(_check_NULL(lib.newpad(nlines, ncols)))
+
+
+def newwin(nlines, ncols, begin_y=None, begin_x=None):
+    _ensure_initialised()
+    if begin_x is None:
+        if begin_y is not None:
+            raise error("newwin requires 2 or 4 arguments")
+        begin_y = begin_x = 0
+
+    return Window(_check_NULL(lib.newwin(nlines, ncols, begin_y, begin_x)))
+
+
+def pair_content(pair):
+    _ensure_initialised_color()
+    f = ffi.new("short *")
+    b = ffi.new("short *")
+    if lib.pair_content(pair, f, b) == lib.ERR:
+        raise error("Argument 1 was out of range. (1..COLOR_PAIRS-1)")
+    return (f, b)
+
+
+def pair_number(pairvalue):
+    _ensure_initialised_color()
+    return (pairvalue & lib.A_COLOR) >> 8
+
+
+def putp(text):
+    text = _texttype(text)
+    return _check_ERR(lib.putp(text), "putp")
+
+
+def qiflush(flag=True):
+    _ensure_initialised()
+    if flag:
+        lib.qiflush()
+    else:
+        lib.noqiflush()
+    return None
+
+
+# XXX: Do something about the following?
+# /* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES
+#  * and _curses.COLS */
+# #if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM)
+# static int
+# update_lines_cols(void)
+# {
+#     PyObject *o;
+#     PyObject *m = PyImport_ImportModuleNoBlock("curses");
+
+#     if (!m)
+#         return 0;
+
+#     o = PyInt_FromLong(LINES);
+#     if (!o) {
+#         Py_DECREF(m);
+#         return 0;
+#     }
+#     if (PyObject_SetAttrString(m, "LINES", o)) {
+#         Py_DECREF(m);
+#         Py_DECREF(o);
+#         return 0;
+#     }
+#     if (PyDict_SetItemString(ModDict, "LINES", o)) {
+#         Py_DECREF(m);
+#         Py_DECREF(o);
+#         return 0;
+#     }
+#     Py_DECREF(o);
+#     o = PyInt_FromLong(COLS);
+#     if (!o) {
+#         Py_DECREF(m);
+#         return 0;
+#     }
+#     if (PyObject_SetAttrString(m, "COLS", o)) {
+#         Py_DECREF(m);
+#         Py_DECREF(o);
+#         return 0;
+#     }
+#     if (PyDict_SetItemString(ModDict, "COLS", o)) {
+#         Py_DECREF(m);
+#         Py_DECREF(o);
+#         return 0;
+#     }
+#     Py_DECREF(o);
+#     Py_DECREF(m);
+#     return 1;
+# }
+# #endif
+
+# #ifdef HAVE_CURSES_RESIZETERM
+# static PyObject *
+# PyCurses_ResizeTerm(PyObject *self, PyObject *args)
+# {
+#     int lines;
+#     int columns;
+#     PyObject *result;
+
+#     PyCursesInitialised;
+
+#     if (!PyArg_ParseTuple(args,"ii:resizeterm", &lines, &columns))
+#         return NULL;
+
+#     result = PyCursesCheckERR(resizeterm(lines, columns), "resizeterm");
+#     if (!result)
+#         return NULL;
+#     if (!update_lines_cols())
+#         return NULL;
+#     return result;
+# }
+
+# #endif
+
+# #ifdef HAVE_CURSES_RESIZE_TERM
+# static PyObject *
+# PyCurses_Resize_Term(PyObject *self, PyObject *args)
+# {
+#     int lines;
+#     int columns;
+
+#     PyObject *result;
+
+#     PyCursesInitialised;
+
+#     if (!PyArg_ParseTuple(args,"ii:resize_term", &lines, &columns))
+#         return NULL;
+
+#     result = PyCursesCheckERR(resize_term(lines, columns), "resize_term");
+#     if (!result)
+#         return NULL;
+#     if (!update_lines_cols())
+#         return NULL;
+#     return result;
+# }
+# #endif /* HAVE_CURSES_RESIZE_TERM */
+
+
+def setsyx(y, x):
+    _ensure_initialised()
+    lib.setsyx(y, x)
+    return None
+
+
+def start_color():
+    _check_ERR(lib.start_color(), "start_color")
+    globals()["COLORS"] = lib.COLORS
+    globals()["COLOR_PAIRS"] = lib.COLOR_PAIRS
+    globals()["_initialised_color"] = True
+    return None
+
+
+def tigetflag(capname):
+    _ensure_initialised_setupterm()
+    return lib.tigetflag(capname)
+
+
+def tigetnum(capname):
+    _ensure_initialised_setupterm()
+    return lib.tigetnum(capname)
+
+
+def tigetstr(capname):
+    _ensure_initialised_setupterm()
+    val = lib.tigetstr(capname)
+    if int(ffi.cast("intptr_t", val)) in (0, -1):
+        return None
+    return ffi.string(val)
+
+
+def tparm(fmt, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0):
+    args = [ffi.cast("int", i) for i in (i1, i2, i3, i4, i5, i6, i7, i8, i9)]
+    result = lib.tparm(fmt, *args)
+    if result == ffi.NULL:
+        raise error("tparm() returned NULL")
+    return ffi.string(result)
+
+
+def typeahead(fd):
+    _ensure_initialised()
+    return _check_ERR(lib.typeahead(fd), "typeahead")
+
+
+def unctrl(ch):
+    _ensure_initialised()
+    return lib.unctrl(_chtype(ch))
+
+
+def ungetch(ch):
+    _ensure_initialised()
+    return _check_ERR(lib.ungetch(_chtype(ch)), "ungetch")
+
+
+def use_env(flag):
+    lib.use_env(flag)
+    return None
+
+
+if not lib._m_STRICT_SYSV_CURSES:
+
+    def use_default_colors():
+        _ensure_initialised_color()
+        return _check_ERR(lib.use_default_colors(), "use_default_colors")
diff --git a/demo/_curses_build.py b/demo/_curses_build.py
new file mode 100644
index 0000000..1a1a3ec
--- /dev/null
+++ b/demo/_curses_build.py
@@ -0,0 +1,327 @@
+import sys
+if sys.platform == 'win32':
+    #This module does not exist in windows
+    raise ImportError('No module named _curses')
+
+from cffi import FFI
+
+ffi = FFI()
+
+ffi.cdef("""
+typedef ... WINDOW;
+typedef ... SCREEN;
+typedef unsigned long... mmask_t;
+typedef unsigned char bool;
+typedef unsigned long... chtype;
+typedef chtype attr_t;
+
+typedef struct
+{
+    short id;           /* ID to distinguish multiple devices */
+    int x, y, z;        /* event coordinates (character-cell) */
+    mmask_t bstate;     /* button state bits */
+}
+MEVENT;
+
+static const int ERR, OK;
+static const int TRUE, FALSE;
+static const int KEY_MIN, KEY_MAX;
+
+static const int COLOR_BLACK;
+static const int COLOR_RED;
+static const int COLOR_GREEN;
+static const int COLOR_YELLOW;
+static const int COLOR_BLUE;
+static const int COLOR_MAGENTA;
+static const int COLOR_CYAN;
+static const int COLOR_WHITE;
+
+static const chtype A_ATTRIBUTES;
+static const chtype A_NORMAL;
+static const chtype A_STANDOUT;
+static const chtype A_UNDERLINE;
+static const chtype A_REVERSE;
+static const chtype A_BLINK;
+static const chtype A_DIM;
+static const chtype A_BOLD;
+static const chtype A_ALTCHARSET;
+static const chtype A_INVIS;
+static const chtype A_PROTECT;
+static const chtype A_CHARTEXT;
+static const chtype A_COLOR;
+
+static const int BUTTON1_RELEASED;
+static const int BUTTON1_PRESSED;
+static const int BUTTON1_CLICKED;
+static const int BUTTON1_DOUBLE_CLICKED;
+static const int BUTTON1_TRIPLE_CLICKED;
+static const int BUTTON2_RELEASED;
+static const int BUTTON2_PRESSED;
+static const int BUTTON2_CLICKED;
+static const int BUTTON2_DOUBLE_CLICKED;
+static const int BUTTON2_TRIPLE_CLICKED;
+static const int BUTTON3_RELEASED;
+static const int BUTTON3_PRESSED;
+static const int BUTTON3_CLICKED;
+static const int BUTTON3_DOUBLE_CLICKED;
+static const int BUTTON3_TRIPLE_CLICKED;
+static const int BUTTON4_RELEASED;
+static const int BUTTON4_PRESSED;
+static const int BUTTON4_CLICKED;
+static const int BUTTON4_DOUBLE_CLICKED;
+static const int BUTTON4_TRIPLE_CLICKED;
+static const int BUTTON_SHIFT;
+static const int BUTTON_CTRL;
+static const int BUTTON_ALT;
+static const int ALL_MOUSE_EVENTS;
+static const int REPORT_MOUSE_POSITION;
+
+int setupterm(char *, int, int *);
+
+WINDOW *stdscr;
+int COLORS;
+int COLOR_PAIRS;
+int COLS;
+int LINES;
+
+int baudrate(void);
+int beep(void);
+int box(WINDOW *, chtype, chtype);
+bool can_change_color(void);
+int cbreak(void);
+int clearok(WINDOW *, bool);
+int color_content(short, short*, short*, short*);
+int copywin(const WINDOW*, WINDOW*, int, int, int, int, int, int, int);
+int curs_set(int);
+int def_prog_mode(void);
+int def_shell_mode(void);
+int delay_output(int);
+int delwin(WINDOW *);
+WINDOW * derwin(WINDOW *, int, int, int, int);
+int doupdate(void);
+int echo(void);
+int endwin(void);
+char erasechar(void);
+void filter(void);
+int flash(void);
+int flushinp(void);
+chtype getbkgd(WINDOW *);
+WINDOW * getwin(FILE *);
+int halfdelay(int);
+bool has_colors(void);
+bool has_ic(void);
+bool has_il(void);
+void idcok(WINDOW *, bool);
+int idlok(WINDOW *, bool);
+void immedok(WINDOW *, bool);
+WINDOW * initscr(void);
+int init_color(short, short, short, short);
+int init_pair(short, short, short);
+int intrflush(WINDOW *, bool);
+bool isendwin(void);
+bool is_linetouched(WINDOW *, int);
+bool is_wintouched(WINDOW *);
+const char * keyname(int);
+int keypad(WINDOW *, bool);
+char killchar(void);
+int leaveok(WINDOW *, bool);
+char * longname(void);
+int meta(WINDOW *, bool);
+int mvderwin(WINDOW *, int, int);
+int mvwaddch(WINDOW *, int, int, const chtype);
+int mvwaddnstr(WINDOW *, int, int, const char *, int);
+int mvwaddstr(WINDOW *, int, int, const char *);
+int mvwchgat(WINDOW *, int, int, int, attr_t, short, const void *);
+int mvwdelch(WINDOW *, int, int);
+int mvwgetch(WINDOW *, int, int);
+int mvwgetnstr(WINDOW *, int, int, char *, int);
+int mvwin(WINDOW *, int, int);
+chtype mvwinch(WINDOW *, int, int);
+int mvwinnstr(WINDOW *, int, int, char *, int);
+int mvwinsch(WINDOW *, int, int, chtype);
+int mvwinsnstr(WINDOW *, int, int, const char *, int);
+int mvwinsstr(WINDOW *, int, int, const char *);
+int napms(int);
+WINDOW * newpad(int, int);
+WINDOW * newwin(int, int, int, int);
+int nl(void);
+int nocbreak(void);
+int nodelay(WINDOW *, bool);
+int noecho(void);
+int nonl(void);
+void noqiflush(void);
+int noraw(void);
+int notimeout(WINDOW *, bool);
+int overlay(const WINDOW*, WINDOW *);
+int overwrite(const WINDOW*, WINDOW *);
+int pair_content(short, short*, short*);
+int pechochar(WINDOW *, const chtype);
+int pnoutrefresh(WINDOW*, int, int, int, int, int, int);
+int prefresh(WINDOW *, int, int, int, int, int, int);
+int putwin(WINDOW *, FILE *);
+void qiflush(void);
+int raw(void);
+int redrawwin(WINDOW *);
+int resetty(void);
+int reset_prog_mode(void);
+int reset_shell_mode(void);
+int savetty(void);
+int scroll(WINDOW *);
+int scrollok(WINDOW *, bool);
+int start_color(void);
+WINDOW * subpad(WINDOW *, int, int, int, int);
+WINDOW * subwin(WINDOW *, int, int, int, int);
+int syncok(WINDOW *, bool);
+chtype termattrs(void);
+char * termname(void);
+int touchline(WINDOW *, int, int);
+int touchwin(WINDOW *);
+int typeahead(int);
+int ungetch(int);
+int untouchwin(WINDOW *);
+void use_env(bool);
+int waddch(WINDOW *, const chtype);
+int waddnstr(WINDOW *, const char *, int);
+int waddstr(WINDOW *, const char *);
+int wattron(WINDOW *, int);
+int wattroff(WINDOW *, int);
+int wattrset(WINDOW *, int);
+int wbkgd(WINDOW *, chtype);
+void wbkgdset(WINDOW *, chtype);
+int wborder(WINDOW *, chtype, chtype, chtype, chtype,
+            chtype, chtype, chtype, chtype);
+int wchgat(WINDOW *, int, attr_t, short, const void *);
+int wclear(WINDOW *);
+int wclrtobot(WINDOW *);
+int wclrtoeol(WINDOW *);
+void wcursyncup(WINDOW *);
+int wdelch(WINDOW *);
+int wdeleteln(WINDOW *);
+int wechochar(WINDOW *, const chtype);
+int werase(WINDOW *);
+int wgetch(WINDOW *);
+int wgetnstr(WINDOW *, char *, int);
+int whline(WINDOW *, chtype, int);
+chtype winch(WINDOW *);
+int winnstr(WINDOW *, char *, int);
+int winsch(WINDOW *, chtype);
+int winsdelln(WINDOW *, int);
+int winsertln(WINDOW *);
+int winsnstr(WINDOW *, const char *, int);
+int winsstr(WINDOW *, const char *);
+int wmove(WINDOW *, int, int);
+int wresize(WINDOW *, int, int);
+int wnoutrefresh(WINDOW *);
+int wredrawln(WINDOW *, int, int);
+int wrefresh(WINDOW *);
+int wscrl(WINDOW *, int);
+int wsetscrreg(WINDOW *, int, int);
+int wstandout(WINDOW *);
+int wstandend(WINDOW *);
+void wsyncdown(WINDOW *);
+void wsyncup(WINDOW *);
+void wtimeout(WINDOW *, int);
+int wtouchln(WINDOW *, int, int, int);
+int wvline(WINDOW *, chtype, int);
+int tigetflag(char *);
+int tigetnum(char *);
+char * tigetstr(char *);
+int putp(const char *);
+char * tparm(const char *, ...);
+int getattrs(const WINDOW *);
+int getcurx(const WINDOW *);
+int getcury(const WINDOW *);
+int getbegx(const WINDOW *);
+int getbegy(const WINDOW *);
+int getmaxx(const WINDOW *);
+int getmaxy(const WINDOW *);
+int getparx(const WINDOW *);
+int getpary(const WINDOW *);
+
+int getmouse(MEVENT *);
+int ungetmouse(MEVENT *);
+mmask_t mousemask(mmask_t, mmask_t *);
+bool wenclose(const WINDOW *, int, int);
+int mouseinterval(int);
+
+void setsyx(int y, int x);
+const char *unctrl(chtype);
+int use_default_colors(void);
+
+int has_key(int);
+bool is_term_resized(int, int);
+
+#define _m_STRICT_SYSV_CURSES ...
+#define _m_NCURSES_MOUSE_VERSION ...
+#define _m_NetBSD ...
+int _m_ispad(WINDOW *);
+
+chtype acs_map[];
+
+// For _curses_panel:
+
+typedef ... PANEL;
+
+WINDOW *panel_window(const PANEL *);
+void update_panels(void);
+int hide_panel(PANEL *);
+int show_panel(PANEL *);
+int del_panel(PANEL *);
+int top_panel(PANEL *);
+int bottom_panel(PANEL *);
+PANEL *new_panel(WINDOW *);
+PANEL *panel_above(const PANEL *);
+PANEL *panel_below(const PANEL *);
+int set_panel_userptr(PANEL *, void *);
+const void *panel_userptr(const PANEL *);
+int move_panel(PANEL *, int, int);
+int replace_panel(PANEL *,WINDOW *);
+int panel_hidden(const PANEL *);
+
+void _m_getsyx(int *yx);
+""")
+
+
+ffi.set_source("_curses_cffi", """
+#ifdef __APPLE__
+/* the following define is necessary for OS X 10.6+; without it, the
+   Apple-supplied ncurses.h sets NCURSES_OPAQUE to 1, and then Python
+   can't get at the WINDOW flags field. */
+#define NCURSES_OPAQUE 0
+#endif
+
+#include <ncurses.h>
+#include <panel.h>
+#include <term.h>
+
+#if defined STRICT_SYSV_CURSES
+#define _m_STRICT_SYSV_CURSES TRUE
+#else
+#define _m_STRICT_SYSV_CURSES FALSE
+#endif
+
+#if defined NCURSES_MOUSE_VERSION
+#define _m_NCURSES_MOUSE_VERSION TRUE
+#else
+#define _m_NCURSES_MOUSE_VERSION FALSE
+#endif
+
+#if defined __NetBSD__
+#define _m_NetBSD TRUE
+#else
+#define _m_NetBSD FALSE
+#endif
+
+int _m_ispad(WINDOW *win) {
+    // <curses.h> may not have _flags (and possibly _ISPAD),
+    // but for now let's assume that <ncurses.h> always has it
+    return (win->_flags & _ISPAD);
+}
+
+void _m_getsyx(int *yx) {
+    getsyx(yx[0], yx[1]);
+}
+""", libraries=['ncurses', 'panel'])
+
+if __name__ == '__main__':
+    ffi.compile()
diff --git a/demo/_curses_setup.py b/demo/_curses_setup.py
new file mode 100644
index 0000000..ec3d20e
--- /dev/null
+++ b/demo/_curses_setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+setup(
+    name="_curses",
+    version="0.1",
+    py_modules=["_curses"],
+    setup_requires=["cffi>=1.0.dev0"],
+    cffi_modules=[
+        "_curses_build.py:ffi",
+    ],
+    install_requires=["cffi>=1.0.dev0"],   # should maybe be "cffi-backend" only?
+    zip_safe=False,
+)
diff --git a/demo/api.py b/demo/api.py
new file mode 100644
index 0000000..8cc6407
--- /dev/null
+++ b/demo/api.py
@@ -0,0 +1,62 @@
+import cffi
+from cffi import FFI
+
+class PythonFFI(FFI):
+
+    def __init__(self, backend=None):
+        FFI.__init__(self, backend=backend)
+        self._pyexports = {}
+
+    def pyexport(self, signature):
+        tp = self._typeof(signature, consider_function_as_funcptr=True)
+        def decorator(func):
+            name = func.__name__
+            if name in self._pyexports:
+                raise cffi.CDefError("duplicate pyexport'ed function %r"
+                                     % (name,))
+            callback_var = self.getctype(tp, name)
+            self.cdef("%s;" % callback_var)
+            self._pyexports[name] = _PyExport(tp, func)
+        return decorator
+
+    def verify(self, source='', **kwargs):
+        extras = []
+        pyexports = sorted(self._pyexports.items())
+        for name, export in pyexports:
+            callback_var = self.getctype(export.tp, name)
+            extras.append("%s;" % callback_var)
+        extras.append(source)
+        source = '\n'.join(extras)
+        lib = FFI.verify(self, source, **kwargs)
+        for name, export in pyexports:
+            cb = self.callback(export.tp, export.func)
+            export.cb = cb
+            setattr(lib, name, cb)
+        return lib
+
+
+class _PyExport(object):
+    def __init__(self, tp, func):
+        self.tp = tp
+        self.func = func
+
+
+if __name__ == '__main__':
+    ffi = PythonFFI()
+
+    @ffi.pyexport("int(int)")
+    def add1(n):
+        print n
+        return n + 1
+
+    ffi.cdef("""
+        int f(int);
+    """)
+
+    lib = ffi.verify("""
+        int f(int x) {
+            return add1(add1(x));
+        }
+    """)
+
+    assert lib.f(5) == 7
diff --git a/demo/bsdopendirtype.py b/demo/bsdopendirtype.py
new file mode 100644
index 0000000..75a996a
--- /dev/null
+++ b/demo/bsdopendirtype.py
@@ -0,0 +1,48 @@
+from _bsdopendirtype import ffi, lib
+
+
+def _posix_error():
+    raise OSError(ffi.errno, os.strerror(ffi.errno))
+
+_dtype_to_smode = {
+    lib.DT_BLK:  0o060000,
+    lib.DT_CHR:  0o020000,
+    lib.DT_DIR:  0o040000,
+    lib.DT_FIFO: 0o010000,
+    lib.DT_LNK:  0o120000,
+    lib.DT_REG:  0o100000,
+    lib.DT_SOCK: 0o140000,
+}
+
+def opendir(dir):
+    if len(dir) == 0:
+        dir = b'.'
+    dirname = dir
+    if not dirname.endswith(b'/'):
+        dirname += b'/'
+    dirp = lib.opendir(dir)
+    if dirp == ffi.NULL:
+        raise _posix_error()
+    try:
+        while True:
+            ffi.errno = 0
+            dirent = lib.readdir(dirp)
+            if dirent == ffi.NULL:
+                if ffi.errno != 0:
+                    raise _posix_error()
+                return
+            name = ffi.string(dirent.d_name)
+            if name == b'.' or name == b'..':
+                continue
+            name = dirname + name
+            try:
+                smode = _dtype_to_smode[dirent.d_type]
+            except KeyError:
+                smode = os.lstat(name).st_mode
+            yield name, smode
+    finally:
+        lib.closedir(dirp)
+
+if __name__ == '__main__':
+    for name, smode in opendir(b'/tmp'):
+        print(hex(smode), name)
diff --git a/demo/bsdopendirtype_build.py b/demo/bsdopendirtype_build.py
new file mode 100644
index 0000000..3c5bb0b
--- /dev/null
+++ b/demo/bsdopendirtype_build.py
@@ -0,0 +1,23 @@
+from cffi import FFI
+
+ffibuilder = FFI()
+ffibuilder.cdef("""
+    typedef ... DIR;
+    struct dirent {
+        unsigned char d_type;   /* type of file */
+        char d_name[];          /* filename */
+        ...;
+    };
+    DIR *opendir(const char *name);
+    int closedir(DIR *dirp);
+    struct dirent *readdir(DIR *dirp);
+    static const int DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK;
+""")
+
+ffibuilder.set_source("_bsdopendirtype", """
+    #include <sys/types.h>
+    #include <dirent.h>
+""")
+
+if __name__ == '__main__':
+    ffibuilder.compile(verbose=True)
diff --git a/demo/bsdopendirtype_setup.py b/demo/bsdopendirtype_setup.py
new file mode 100644
index 0000000..30a6cfb
--- /dev/null
+++ b/demo/bsdopendirtype_setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+setup(
+    name="example",
+    version="0.1",
+    py_modules=["bsdopendirtype"],
+    setup_requires=["cffi>=1.0.dev0"],
+    cffi_modules=[
+        "bsdopendirtype_build.py:ffibuilder",
+    ],
+    install_requires=["cffi>=1.0.dev0"],   # should maybe be "cffi-backend" only?
+    zip_safe=False,
+)
diff --git a/demo/btrfs-snap.py b/demo/btrfs-snap.py
new file mode 100644
index 0000000..fceeaa1
--- /dev/null
+++ b/demo/btrfs-snap.py
@@ -0,0 +1,52 @@
+"""
+btrfs-snap.py: source target newname
+
+creates a exactly named snapshots and bails out if they exist
+"""
+
+import argparse
+import fcntl
+import os
+import sys
+
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.cdef("""
+    #define BTRFS_IOC_SNAP_CREATE_V2 ...
+    struct btrfs_ioctl_vol_args_v2 {
+        int64_t fd;
+        char name[];
+        ...;
+    };
+""")
+
+ffi.set_source("_btrfs_cffi", "#include <btrfs/ioctl.h>")
+ffi.compile()
+
+# ____________________________________________________________
+
+
+from _btrfs_cffi import ffi, lib
+
+parser = argparse.ArgumentParser(usage=__doc__.strip())
+parser.add_argument('source', help='source subvolume')
+parser.add_argument('target', help='target directory')
+parser.add_argument('newname', help='name of the new snapshot')
+opts = parser.parse_args()
+
+source = os.open(opts.source, os.O_DIRECTORY)
+target = os.open(opts.target, os.O_DIRECTORY)
+
+
+args = ffi.new('struct btrfs_ioctl_vol_args_v2 *')
+args.name = opts.newname
+args.fd = source
+args_buffer = ffi.buffer(args)
+try:
+    fcntl.ioctl(target, lib.BTRFS_IOC_SNAP_CREATE_V2, args_buffer)
+except IOError as e:
+    print e
+    sys.exit(1)
+
diff --git a/demo/cffi-cocoa.py b/demo/cffi-cocoa.py
new file mode 100644
index 0000000..9e86d99
--- /dev/null
+++ b/demo/cffi-cocoa.py
@@ -0,0 +1,102 @@
+# Based on http://cocoawithlove.com/2010/09/minimalist-cocoa-programming.html
+# by Juraj Sukop.  This demo was eventually expanded into a more complete
+# Cocoa library available at https://bitbucket.org/sukop/nspython .
+
+from cffi import FFI
+
+ffi = FFI()
+ffi.cdef('''
+    
+    typedef signed char BOOL;
+    
+    typedef long NSInteger;
+    typedef unsigned long NSUInteger;
+    typedef NSInteger NSApplicationActivationPolicy;
+    typedef NSUInteger NSBackingStoreType;
+    typedef NSUInteger NSStringEncoding;
+    
+    typedef double CGFloat;
+    struct CGPoint {
+        CGFloat x;
+        CGFloat y;
+    };
+    typedef struct CGPoint CGPoint;
+    struct CGSize {
+        CGFloat width;
+        CGFloat height;
+    };
+    typedef struct CGSize CGSize;
+    struct CGRect {
+        CGPoint origin;
+        CGSize size;
+    };
+    typedef struct CGRect CGRect;
+    
+    typedef CGPoint NSPoint;
+    typedef CGSize NSSize;
+    typedef CGRect NSRect;
+    
+    typedef struct objc_class *Class;
+    typedef struct objc_object {
+        Class isa;
+    } *id;
+    typedef struct objc_selector *SEL;
+
+    SEL sel_registerName(const char *str);
+    id objc_getClass(const char *name);
+    id objc_msgSend(id theReceiver, SEL theSelector, ...);
+    
+''')
+
+objc = ffi.dlopen('objc')
+appkit = ffi.dlopen('AppKit')
+
+nil = ffi.NULL
+YES = ffi.cast('BOOL', 1)
+NO = ffi.cast('BOOL', 0)
+
+NSASCIIStringEncoding = ffi.cast('NSStringEncoding', 1)
+NSApplicationActivationPolicyRegular = ffi.cast('NSApplicationActivationPolicy', 0)
+NSTitledWindowMask = ffi.cast('NSUInteger', 1)
+NSBackingStoreBuffered = ffi.cast('NSBackingStoreType', 2)
+
+NSMakePoint = lambda x, y: ffi.new('NSPoint *', (x, y))[0]
+NSMakeRect = lambda x, y, w, h: ffi.new('NSRect *', ((x, y), (w, h)))[0]
+
+get, send, sel = objc.objc_getClass, objc.objc_msgSend, objc.sel_registerName
+at = lambda s: send(
+    get('NSString'),
+    sel('stringWithCString:encoding:'),
+    ffi.new('char[]', s), NSASCIIStringEncoding)
+
+send(get('NSAutoreleasePool'), sel('new'))
+app = send(get('NSApplication'), sel('sharedApplication'))
+send(app, sel('setActivationPolicy:'), NSApplicationActivationPolicyRegular)
+
+menubar = send(send(get('NSMenu'), sel('new')), sel('autorelease'))
+appMenuItem = send(send(get('NSMenuItem'), sel('new')), sel('autorelease'))
+send(menubar, sel('addItem:'), appMenuItem)
+send(app, sel('setMainMenu:'), menubar)
+
+appMenu = send(send(get('NSMenu'), sel('new')), sel('autorelease'))
+appName = send(send(get('NSProcessInfo'), sel('processInfo')), sel('processName'))
+quitTitle = send(at('Quit '), sel('stringByAppendingString:'), appName)
+quitMenuItem = send(send(send(
+            get('NSMenuItem'), sel('alloc')),
+        sel('initWithTitle:action:keyEquivalent:'),
+        quitTitle, sel('terminate:'), at('q')),
+    sel('autorelease'))
+send(appMenu, sel('addItem:'), quitMenuItem)
+send(appMenuItem, sel('setSubmenu:'), appMenu)
+
+window = send(send(send(
+            get('NSWindow'), sel('alloc')),
+        sel('initWithContentRect:styleMask:backing:defer:'),
+        NSMakeRect(0, 0, 200, 200), NSTitledWindowMask, NSBackingStoreBuffered, NO),
+    sel('autorelease'))
+send(window, sel('cascadeTopLeftFromPoint:'), NSMakePoint(20, 20))
+send(window, sel('setTitle:'), appName)
+send(window, sel('makeKeyAndOrderFront:'), nil)
+
+send(app, sel('activateIgnoringOtherApps:'), YES)
+send(app, sel('run'))
diff --git a/demo/embedding.py b/demo/embedding.py
new file mode 100644
index 0000000..b15c050
--- /dev/null
+++ b/demo/embedding.py
@@ -0,0 +1,21 @@
+import cffi
+
+ffibuilder = cffi.FFI()
+
+ffibuilder.embedding_api("""
+    int add(int, int);
+""")
+
+ffibuilder.embedding_init_code("""
+    from _embedding_cffi import ffi
+    print("preparing")   # printed once
+
+    @ffi.def_extern()
+    def add(x, y):
+        print("adding %d and %d" % (x, y))
+        return x + y
+""")
+
+ffibuilder.set_source("_embedding_cffi", "")
+
+ffibuilder.compile(verbose=True)
diff --git a/demo/embedding_test.c b/demo/embedding_test.c
new file mode 100644
index 0000000..ede8cb9
--- /dev/null
+++ b/demo/embedding_test.c
@@ -0,0 +1,43 @@
+/* There are two options:
+
+   =====1=====
+
+   Link this program with _embedding_test.so.
+   E.g. with gcc:
+
+      gcc -o embedding_test embedding_test.c _embedding_cffi*.so
+
+   You must then run the executable with the right command
+   (LD_LIBRARY_PATH on Linux), otherwise it won't find the
+   _embedding_cffi*.so:
+
+      LD_LIBRARY_PATH=. ./embedding_test
+
+   There are platform-specific options to gcc to avoid needing
+   that, too.  Linux:
+
+      gcc -o embedding_test embedding_test.c _embedding_cffi*.so  \
+          -Wl,-rpath=\$ORIGIN/
+
+   =====2=====
+
+   Compile and link the _embedding_test.c source code together with
+   this example (e.g. with PyPy):
+
+      gcc -o embedding_test embedding_test.c _embedding_cffi.c  \
+          -I/opt/pypy/include -pthread -lpypy-c
+*/
+
+#include <stdio.h>
+
+extern int add(int x, int y);
+
+
+int main(void)
+{
+    int res = add(40, 2);
+    printf("result: %d\n", res);
+    res = add(100, -5);
+    printf("result: %d\n", res);
+    return 0;
+}
diff --git a/demo/extern_python.py b/demo/extern_python.py
new file mode 100644
index 0000000..f315cc5
--- /dev/null
+++ b/demo/extern_python.py
@@ -0,0 +1,26 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.cdef("""int my_algo(int); extern "Python" int f(int);""")
+
+ffi.set_source("_extern_python_cffi", """
+    static int f(int);
+    static int my_algo(int n) {
+        int i, sum = 0;
+        for (i = 0; i < n; i++)
+            sum += f(i);
+        return sum;
+    }
+""")
+
+ffi.compile()
+
+
+from _extern_python_cffi import ffi, lib
+
+@ffi.def_extern()
+def f(n):
+    return n * n
+
+assert lib.my_algo(10) == 0+1+4+9+16+25+36+49+64+81
diff --git a/demo/extern_python_varargs.py b/demo/extern_python_varargs.py
new file mode 100644
index 0000000..ee78079
--- /dev/null
+++ b/demo/extern_python_varargs.py
@@ -0,0 +1,61 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.cdef("""
+    int my_algo(int);
+    typedef ... va_list;
+    extern "Python" int f(int, va_list *);
+
+    int fetch_int(va_list *);
+    double fetch_double(va_list *);
+    void *fetch_ptr(va_list *);
+""")
+
+ffi.set_source("_extern_python_cffi", """
+    #include <stdarg.h>
+
+    static int f(int, va_list *);
+
+    static int f1(int n, ...)
+    {
+        va_list ap;
+        va_start(ap, n);
+        int res = f(n, &ap);
+        va_end(ap);
+        return res;
+    }
+
+    static int fetch_int(va_list *va) { return va_arg((*va), int); }
+    static double fetch_double(va_list *va) { return va_arg((*va), double); }
+    static void * fetch_ptr(va_list *va) { return va_arg((*va), void *); }
+    
+    static int my_algo(int n) {
+        return f1(3, n, n+1, n+2) + f1(1, &n) + f1(2, 12.3, 45.6);
+    }
+""")
+
+ffi.compile()
+
+
+from _extern_python_cffi import ffi, lib
+
+@ffi.def_extern()
+def f(n, va):
+    if n == 3:
+        x = lib.fetch_int(va)
+        y = lib.fetch_int(va)
+        z = lib.fetch_int(va)
+        print (x, y, z)
+    elif n == 1:
+        ptr = lib.fetch_ptr(va)
+        print 'ptr to:', ffi.cast("int *", ptr)[0]
+    elif n == 2:
+        x = lib.fetch_double(va)
+        y = lib.fetch_double(va)
+        print (x, y)
+    else:
+        raise AssertionError(n)
+    return 14
+
+print lib.my_algo(10)
diff --git a/demo/fastcsv.py b/demo/fastcsv.py
new file mode 100644
index 0000000..6b8d0b4
--- /dev/null
+++ b/demo/fastcsv.py
@@ -0,0 +1,266 @@
+import csv
+import cffi
+
+# IN-PROGRESS.  See the demo at the end of the file
+
+
+def _make_ffi_from_dialect(dialect_name):
+    dialect = csv.get_dialect(dialect_name)
+
+    ffi = cffi.FFI()
+
+    ffi.cdef("""
+        long parse_line(char *rawline, long inputlength);
+    """)
+
+    d = {'quotechar': ord(dialect.quotechar),
+         'quoting': int(dialect.quoting),
+         'skipinitialspace': int(dialect.skipinitialspace),
+         'delimiter': ord(dialect.delimiter),
+         'doublequote': int(dialect.doublequote),
+         'strict': int(dialect.strict),
+         }
+    if dialect.escapechar is not None:
+        d['is_escape_char'] = '== %d' % ord(dialect.escapechar)
+    else:
+        d['is_escape_char'] = '&& 0'
+
+    ffi.set_source('_fastcsv_' + dialect_name, r'''
+
+    typedef enum {
+        START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD,
+        IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD,
+        EAT_CRNL
+    } ParserState;
+
+    typedef enum {
+        QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE
+    } QuoteStyle;
+
+    typedef struct {
+        ParserState state;          /* current CSV parse state */
+        char *field;                /* build current field in here */
+        int field_size;             /* size of allocated buffer */
+        int field_len;              /* length of current field */
+        int numeric_field;          /* treat field as numeric */
+    } ReaderObj;
+
+    static void
+    parse_add_char(ReaderObj *self, char c)
+    {
+        *self->field++ = c;
+    }
+
+    static void
+    parse_save_field(ReaderObj *self)
+    {
+        *self->field++ = 0;
+    }
+
+    static int
+    parse_process_char(ReaderObj *self, char c)
+    {
+        switch (self->state) {
+        case START_RECORD:
+            /* start of record */
+            if (c == '\0')
+                /* empty line - return [] */
+                break;
+            else if (c == '\n' || c == '\r') {
+                self->state = EAT_CRNL;
+                break;
+            }
+            /* normal character - handle as START_FIELD */
+            self->state = START_FIELD;
+            /* fallthru */
+        case START_FIELD:
+            /* expecting field */
+            if (c == '\n' || c == '\r' || c == '\0') {
+                /* save empty field - return [fields] */
+                parse_save_field(self);
+                self->state = (c == '\0' ? START_RECORD : EAT_CRNL);
+            }
+            else if (c == %(quotechar)d &&
+                     %(quoting)d != QUOTE_NONE) {
+                /* start quoted field */
+                self->state = IN_QUOTED_FIELD;
+            }
+            else if (c %(is_escape_char)s) {
+                /* possible escaped character */
+                self->state = ESCAPED_CHAR;
+            }
+            else if (c == ' ' && %(skipinitialspace)d)
+                /* ignore space at start of field */
+                ;
+            else if (c == %(delimiter)d) {
+                /* save empty field */
+                parse_save_field(self);
+            }
+            else {
+                /* begin new unquoted field */
+                if (%(quoting)d == QUOTE_NONNUMERIC)
+                    self->numeric_field = 1;
+                parse_add_char(self, c);
+                self->state = IN_FIELD;
+            }
+            break;
+
+        case ESCAPED_CHAR:
+            if (c == '\0')
+                c = '\n';
+            parse_add_char(self, c);
+            self->state = IN_FIELD;
+            break;
+
+        case IN_FIELD:
+            /* in unquoted field */
+            if (c == '\n' || c == '\r' || c == '\0') {
+                /* end of line - return [fields] */
+                parse_save_field(self);
+                self->state = (c == '\0' ? START_RECORD : EAT_CRNL);
+            }
+            else if (c %(is_escape_char)s) {
+                /* possible escaped character */
+                self->state = ESCAPED_CHAR;
+            }
+            else if (c == %(delimiter)d) {
+                /* save field - wait for new field */
+                parse_save_field(self);
+                self->state = START_FIELD;
+            }
+            else {
+                /* normal character - save in field */
+                parse_add_char(self, c);
+            }
+            break;
+
+        case IN_QUOTED_FIELD:
+            /* in quoted field */
+            if (c == '\0')
+                ;
+            else if (c %(is_escape_char)s) {
+                /* Possible escape character */
+                self->state = ESCAPE_IN_QUOTED_FIELD;
+            }
+            else if (c == %(quotechar)d &&
+                     %(quoting)d != QUOTE_NONE) {
+                if (%(doublequote)d) {
+                    /* doublequote; " represented by "" */
+                    self->state = QUOTE_IN_QUOTED_FIELD;
+                }
+                else {
+                    /* end of quote part of field */
+                    self->state = IN_FIELD;
+                }
+            }
+            else {
+                /* normal character - save in field */
+                parse_add_char(self, c);
+            }
+            break;
+
+        case ESCAPE_IN_QUOTED_FIELD:
+            if (c == '\0')
+                c = '\n';
+            parse_add_char(self, c);
+            self->state = IN_QUOTED_FIELD;
+            break;
+
+        case QUOTE_IN_QUOTED_FIELD:
+            /* doublequote - seen a quote in an quoted field */
+            if (%(quoting)d != QUOTE_NONE &&
+                c == %(quotechar)d) {
+                /* save "" as " */
+                parse_add_char(self, c);
+                self->state = IN_QUOTED_FIELD;
+            }
+            else if (c == %(delimiter)d) {
+                /* save field - wait for new field */
+                parse_save_field(self);
+                self->state = START_FIELD;
+            }
+            else if (c == '\n' || c == '\r' || c == '\0') {
+                /* end of line - return [fields] */
+                parse_save_field(self);
+                self->state = (c == '\0' ? START_RECORD : EAT_CRNL);
+            }
+            else if (!%(strict)d) {
+                parse_add_char(self, c);
+                self->state = IN_FIELD;
+            }
+            else {
+                /* illegal */
+                /*PyErr_Format(error_obj, "'%%c' expected after '%%c'",
+                                dialect->delimiter,
+                                dialect->quotechar);*/
+                return -1;
+            }
+            break;
+
+        case EAT_CRNL:
+            if (c == '\n' || c == '\r')
+                ;
+            else if (c == '\0')
+                self->state = START_RECORD;
+            else {
+                /*PyErr_Format(error_obj, "new-line character seen in unquoted field - do you need to open the file in universal-newline mode?");*/
+                return -1;
+            }
+            break;
+
+        }
+        return 0;
+    }
+
+    static void
+    parse_reset(ReaderObj *self, char *rawline)
+    {
+        self->field = rawline;
+        self->state = START_RECORD;
+        self->numeric_field = 0;
+    }
+
+    long parse_line(char *rawline, long inputlength)
+    {
+        char *p;
+        ReaderObj reader;
+        parse_reset(&reader, rawline);
+
+        for (p=rawline; inputlength > 0; inputlength--, p++) {
+            if (parse_process_char(&reader, *p) < 0)
+                return -1;
+        }
+        if (parse_process_char(&reader, 0) < 0)
+            return -1;
+        return reader.field - rawline - 1;
+    }
+    ''' % d)
+
+    ffi.compile()
+
+
+def fastcsv_reader(f, dialect_name):
+    try:
+        module = __import__('_fastcsv_' + dialect_name)
+    except ImportError:
+        _make_ffi_from_dialect(dialect_name)
+        module = __import__('_fastcsv_' + dialect_name)
+    ffi, lib = module.ffi, module.lib
+    #
+    linelen = -1
+    for line in f:
+        if linelen <= len(line):
+            linelen = 2 * len(line)
+            rawline = ffi.new("char[]", linelen)
+        ffi.buffer(rawline, len(line))[:] = line
+        n = lib.parse_line(rawline, len(line))
+        assert n >= 0
+        yield ffi.buffer(rawline, n)[:].split('\x00')
+
+
+if __name__ == '__main__':
+    csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE)
+    with open('/etc/passwd', 'rb') as f:
+        reader = fastcsv_reader(f, 'unixpwd')
+        for row in reader:
+            print row
diff --git a/demo/gmp.py b/demo/gmp.py
new file mode 100644
index 0000000..44f233c
--- /dev/null
+++ b/demo/gmp.py
@@ -0,0 +1,33 @@
+import sys
+#
+# This is only a demo based on the GMP library.
+# There is a rather more complete (but perhaps outdated) version available at:
+# http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files
+#
+
+try:
+    from _gmp_cffi import ffi, lib
+except ImportError:
+    print 'run gmp_build first, then make sure the shared object is on sys.path'
+    sys.exit(1)
+
+# ffi "knows" about the declared variables and functions from the
+#     cdef parts of the module created from gmp_build
+# lib "knows" how to call the functions from the set_source parts
+#     of the module.
+
+# ____________________________________________________________
+
+a = ffi.new("mpz_t")
+b = ffi.new("mpz_t")
+
+if len(sys.argv) < 3:
+    print 'call as %s bigint1, bigint2' % sys.argv[0]
+    sys.exit(2)
+
+lib.mpz_init_set_str(a, sys.argv[1], 10)	# Assume decimal integers
+lib.mpz_init_set_str(b, sys.argv[2], 10)	# Assume decimal integers
+lib.mpz_add(a, a, b)			# a=a+b
+
+s = lib.mpz_get_str(ffi.NULL, 10, a)
+print ffi.string(s)
diff --git a/demo/gmp_build.py b/demo/gmp_build.py
new file mode 100644
index 0000000..e1a6000
--- /dev/null
+++ b/demo/gmp_build.py
@@ -0,0 +1,26 @@
+import cffi
+
+#
+# This is only a demo based on the GMP library.
+# There is a rather more complete (but perhaps outdated) version available at:
+# http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files
+#
+
+ffibuilder = cffi.FFI()
+
+ffibuilder.cdef("""
+
+    typedef struct { ...; } MP_INT;
+    typedef MP_INT mpz_t[1];
+
+    int mpz_init_set_str (MP_INT *dest_integer, char *src_cstring, int base);
+    void mpz_add (MP_INT *sum, MP_INT *addend1, MP_INT *addend2);
+    char * mpz_get_str (char *string, int base, MP_INT *integer);
+
+""")
+
+ffibuilder.set_source('_gmp_cffi', "#include <gmp.h>",
+                 libraries=['gmp', 'm'])
+
+if __name__ == '__main__':
+    ffibuilder.compile(verbose=True)
diff --git a/demo/manual.c b/demo/manual.c
new file mode 100644
index 0000000..5b360e8
--- /dev/null
+++ b/demo/manual.c
@@ -0,0 +1,166 @@
+#include "_cffi_include.h"
+
+
+#define AA  (42)
+#define BB  (&bb)
+static int bb = 16261;
+
+int foo42(int a, int *b)
+{
+    return a - *b;
+}
+
+int foo64(int a)
+{
+    return ~a;
+}
+
+struct foo_s {
+    int a;
+};
+
+/************************************************************/
+
+static void *_cffi_types[] = {
+    _CFFI_OP(_CFFI_OP_FUNCTION, 1),
+    _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT),
+    _CFFI_OP(_CFFI_OP_POINTER, 1),
+    _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
+    _CFFI_OP(_CFFI_OP_FUNCTION, 1),
+    _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT),
+    _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
+    _CFFI_OP(_CFFI_OP_STRUCT_UNION, 0),
+};
+
+#ifndef PYPY_VERSION
+static PyObject *
+_cffi_f_foo42(PyObject *self, PyObject *args)
+{
+  int x0;
+  int * x1;
+  Py_ssize_t datasize;
+  int result;
+  PyObject *arg0;
+  PyObject *arg1;
+
+  if (!PyArg_ParseTuple(args, "OO:foo42", &arg0, &arg1))
+    return NULL;
+
+  x0 = _cffi_to_c_int(arg0, int);
+  if (x0 == (int)-1 && PyErr_Occurred())
+    return NULL;
+
+  datasize = _cffi_prepare_pointer_call_argument(
+      _cffi_types[1], arg1, (char **)&x1);
+  if (datasize != 0) {
+    if (datasize < 0)
+      return NULL;
+    x1 = alloca(datasize);
+    memset((void *)x1, 0, datasize);
+    if (_cffi_convert_array_from_object((char *)x1, _cffi_types[1], arg1) < 0)
+      return NULL;
+  }
+
+  Py_BEGIN_ALLOW_THREADS
+  _cffi_restore_errno();
+  { result = foo42(x0, x1); }
+  _cffi_save_errno();
+  Py_END_ALLOW_THREADS
+
+  return _cffi_from_c_int(result, int);
+}
+#else
+static int _cffi_f_foo42(int x0, int *x1)
+{
+  return foo42(x0, x1);
+}
+#endif
+
+#ifndef PYPY_VERSION
+static PyObject *
+_cffi_f_foo64(PyObject *self, PyObject *arg0)
+{
+  int x0;
+  int result;
+
+  x0 = _cffi_to_c_int(arg0, int);
+  if (x0 == (int)-1 && PyErr_Occurred())
+    return NULL;
+
+  Py_BEGIN_ALLOW_THREADS
+  _cffi_restore_errno();
+  { result = foo64(x0); }
+  _cffi_save_errno();
+  Py_END_ALLOW_THREADS
+
+  return _cffi_from_c_int(result, int);
+}
+#else
+static int _cffi_f_foo64(int x0)
+{
+  return foo64(x0);
+}
+#endif
+
+static int _cffi_const_AA(unsigned long long *output)
+{
+    *output = (unsigned long long)((AA) << 0);   // integer
+    return (AA) <= 0;
+}
+
+static void _cffi_const_BB(char *output)
+{
+    *(int **)output = BB;
+}
+
+static const struct _cffi_global_s _cffi_globals[] = {
+    { "AA",    &_cffi_const_AA, _CFFI_OP(_CFFI_OP_CONSTANT_INT, 0) },
+    { "BB",    &_cffi_const_BB, _CFFI_OP(_CFFI_OP_CONSTANT, 2) },
+    { "bb",    &bb, _CFFI_OP(_CFFI_OP_GLOBAL_VAR, 1) },
+    { "foo42", &_cffi_f_foo42, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 0) },
+    { "foo64", &_cffi_f_foo64, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 4) },
+};
+
+struct _cffi_align_foo_s { char x; struct foo_s y; };
+
+static const struct _cffi_struct_union_s _cffi_struct_unions[] = {
+    { "foo_s", 7, 0,
+      sizeof(struct foo_s),
+      offsetof(struct _cffi_align_foo_s, y),
+      1, 0 },
+};
+
+static const struct _cffi_field_s _cffi_fields[] = {
+    { "a", offsetof(struct foo_s, a), sizeof(((struct foo_s *)0)->a),
+      _CFFI_OP(_CFFI_OP_NOOP, 1) },
+};
+
+static const struct _cffi_type_context_s _cffi_type_context = {
+    _cffi_types,
+    _cffi_globals,
+    _cffi_fields,
+    _cffi_struct_unions,
+    NULL,
+    NULL,
+    5,  /* num_globals */
+    1,  /* num_struct_unions */
+    0,
+    0,
+    NULL,
+    8,  /* num_types */
+};
+
+#ifndef PYPY_VERSION
+PyMODINIT_FUNC
+initmanual(void)
+{
+    _cffi_init("manual", 0x2601, &_cffi_type_context);
+}
+#else
+PyMODINIT_FUNC
+_cffi_pypyinit_manual(const void *p[])
+{
+    p[0] = (const void *)0x2601;
+    p[1] = &_cffi_type_context;
+}
+#endif
diff --git a/demo/manual2.py b/demo/manual2.py
new file mode 100644
index 0000000..2986244
--- /dev/null
+++ b/demo/manual2.py
@@ -0,0 +1,34 @@
+import _cffi_backend
+
+ffi = _cffi_backend.FFI(b"manual2",
+    _version = 0x2601,
+    _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x00\x09\x00\x00\x00\x0B\x00\x00\x01\x03',
+    _globals = (b'\xff\xff\xff\x0bAA',0,b'\xff\xff\xff\x0bBB',-1,b'\xff\xff\xff\x0bCC',2,b'\xff\xff\xff\x1fFOO',0x9999999999999999,b'\x00\x00\x00#close',0,b'\x00\x00\x05#stdout',0),
+    _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x00point_s',b'\x00\x00\x01\x11\xff\xff\xff\xffx',b'\x00\x00\x01\x11\xff\xff\xff\xffy'),),
+    _enums = (b'\x00\x00\x00\x04\x00\x00\x00\x07myenum_e\x00AA,BB,CC',),
+    _typenames = (b'\x00\x00\x00\x01myint_t',),
+)
+
+
+
+# trying it out
+lib = ffi.dlopen(None)
+assert lib.AA == 0
+assert lib.BB == -1
+assert lib.FOO == 0x9999999999999999
+x = lib.close(-42)
+assert x == -1
+
+print lib.stdout
+
+print ffi.new("struct point_s *")
+print ffi.offsetof("struct point_s", "x")
+print ffi.offsetof("struct point_s", "y")
+print ffi.new("struct point_s[CC]")
+assert ffi.sizeof("struct point_s[CC]") == 2 * ffi.sizeof("struct point_s")
+
+print ffi.cast("enum myenum_e", 2)
+print ffi.cast("myint_t", -2)
+assert ffi.typeof("myint_t") == ffi.typeof("int")
+
+del ffi, lib
diff --git a/demo/pwuid.py b/demo/pwuid.py
new file mode 100644
index 0000000..dda9299
--- /dev/null
+++ b/demo/pwuid.py
@@ -0,0 +1,7 @@
+import sys, os
+
+# run pwuid_build first, then make sure the shared object is on sys.path
+from _pwuid_cffi import ffi, lib
+
+
+print ffi.string(lib.getpwuid(0).pw_name)
diff --git a/demo/pwuid_build.py b/demo/pwuid_build.py
new file mode 100644
index 0000000..7ef0d76
--- /dev/null
+++ b/demo/pwuid_build.py
@@ -0,0 +1,18 @@
+from cffi import FFI
+ffi = FFI()
+ffi.cdef("""     // some declarations from the man page
+    struct passwd {
+        char *pw_name;
+        ...; 
+    };
+    struct passwd *getpwuid(int uid);
+""")
+
+ffi.set_source('_pwuid_cffi', """   // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""")
+
+
+if __name__ == '__main__':
+    ffi.compile()
diff --git a/demo/py.cleanup b/demo/py.cleanup
new file mode 100755
index 0000000..512389f
--- /dev/null
+++ b/demo/py.cleanup
@@ -0,0 +1,31 @@
+#! /usr/bin/env python
+import sys, os, stat
+from bsdopendirtype import opendir
+
+def clean(path):
+    global count
+    try:
+        content = opendir(path)
+    except OSError:
+        print >> sys.stderr, "skipping", path
+        return
+    for filename, smode in content:
+        if stat.S_ISDIR(smode):
+            clean(filename)
+            if filename.endswith('/__pycache__'):
+                try:
+                    os.rmdir(filename)
+                except OSError:
+                    pass
+        elif (filename.endswith('.pyc') or filename.endswith('.pyo') or
+              filename.endswith('.pyc~') or filename.endswith('.pyo~')):
+            os.unlink(filename)
+            count += 1
+
+count = 0
+
+for arg in sys.argv[1:] or ['.']:
+    print "cleaning path", arg, "of .pyc/.pyo/__pycache__ files"
+    clean(arg)
+
+print "%d files removed" % (count,)
diff --git a/demo/pyobj.py b/demo/pyobj.py
new file mode 100644
index 0000000..b40343a
--- /dev/null
+++ b/demo/pyobj.py
@@ -0,0 +1,124 @@
+
+referents = []     # list "object descriptor -> python object"
+freelist = None
+
+def store(x):
+    "Store the object 'x' and returns a new object descriptor for it."
+    global freelist
+    p = freelist
+    if p is None:
+        p = len(referents)
+        referents.append(x)
+    else:
+        freelist = referents[p]
+        referents[p] = x
+    return p
+
+def discard(p):
+    """Discard (i.e. close) the object descriptor 'p'.
+    Return the original object that was attached to 'p'."""
+    global freelist
+    x = referents[p]
+    referents[p] = freelist
+    freelist = p
+    return x
+
+class Ref(object):
+    """For use in 'with Ref(x) as ob': open an object descriptor
+    and returns it in 'ob', and close it automatically when the
+    'with' statement finishes."""
+    def __init__(self, x):
+        self.x = x
+    def __enter__(self):
+        self.p = p = store(self.x)
+        return p
+    def __exit__(self, *args):
+        discard(self.p)
+
+def count_pyobj_alive():
+    result = len(referents)
+    p = freelist
+    while p is not None:
+        assert result > 0
+        result -= 1
+        p = referents[p]
+    return result
+
+# ------------------------------------------------------------
+
+if __name__ == '__main__':
+    import api
+
+    ffi = api.PythonFFI()
+
+    ffi.cdef("""
+        typedef int pyobj_t;
+        int sum_integers(pyobj_t p_list);
+        pyobj_t sum_objects(pyobj_t p_list, pyobj_t p_initial);
+    """)
+
+    @ffi.pyexport("int(pyobj_t)")
+    def length(p_list):
+        list = referents[p_list]
+        return len(list)
+
+    @ffi.pyexport("int(pyobj_t, int)")
+    def getitem(p_list, index):
+        list = referents[p_list]
+        return list[index]
+
+    @ffi.pyexport("pyobj_t(pyobj_t)")
+    def pyobj_dup(p):
+        return store(referents[p])
+
+    @ffi.pyexport("void(pyobj_t)")
+    def pyobj_close(p):
+        discard(p)
+
+    @ffi.pyexport("pyobj_t(pyobj_t, int)")
+    def pyobj_getitem(p_list, index):
+        list = referents[p_list]
+        return store(list[index])
+
+    @ffi.pyexport("pyobj_t(pyobj_t, pyobj_t)")
+    def pyobj_add(p1, p2):
+        return store(referents[p1] + referents[p2])
+
+    lib = ffi.verify("""
+        typedef int pyobj_t;    /* an "object descriptor" number */
+
+        int sum_integers(pyobj_t p_list) {
+            /* this a demo function written in C, using the API
+               defined above: length() and getitem(). */
+            int i, result = 0;
+            int count = length(p_list);
+            for (i=0; i<count; i++) {
+                int n = getitem(p_list, i);
+                result += n;
+            }
+            return result;
+        }
+
+        pyobj_t sum_objects(pyobj_t p_list, pyobj_t p_initial) {
+            /* same as above, but keeps all additions as Python objects */
+            int i;
+            int count = length(p_list);
+            pyobj_t p1 = pyobj_dup(p_initial);
+            for (i=0; i<count; i++) {
+                pyobj_t p2 = pyobj_getitem(p_list, i);
+                pyobj_t p3 = pyobj_add(p1, p2);
+                pyobj_close(p2);
+                pyobj_close(p1);
+                p1 = p3;
+            }
+            return p1;
+        }
+    """)
+
+    with Ref([10, 20, 30, 40]) as p_list:
+        print lib.sum_integers(p_list)
+        with Ref(5) as p_initial:
+            result = discard(lib.sum_objects(p_list, p_initial))
+            print result
+
+    assert count_pyobj_alive() == 0
diff --git a/demo/readdir.py b/demo/readdir.py
new file mode 100644
index 0000000..b966246
--- /dev/null
+++ b/demo/readdir.py
@@ -0,0 +1,35 @@
+# A Linux-only demo
+#
+import sys
+
+if not sys.platform.startswith('linux'):
+    raise Exception("Linux-only demo")
+
+from _readdir import ffi
+lib = ffi.dlopen(None)
+
+
+def walk(basefd, path):
+    print '{', path
+    dirfd = lib.openat(basefd, path, 0)
+    if dirfd < 0:
+        # error in openat()
+        return
+    dir = lib.fdopendir(dirfd)
+    dirent = ffi.new("struct dirent *")
+    result = ffi.new("struct dirent **")
+    while True:
+        if lib.readdir_r(dir, dirent, result):
+            # error in readdir_r()
+            break
+        if result[0] == ffi.NULL:
+            break
+        name = ffi.string(dirent.d_name)
+        print '%3d %s' % (dirent.d_type, name)
+        if dirent.d_type == 4 and name != '.' and name != '..':
+            walk(dirfd, name)
+    lib.closedir(dir)
+    print '}'
+
+
+walk(-1, "/tmp")
diff --git a/demo/readdir2.py b/demo/readdir2.py
new file mode 100644
index 0000000..b564b51
--- /dev/null
+++ b/demo/readdir2.py
@@ -0,0 +1,35 @@
+# A Linux-only demo, using set_source() instead of hard-coding the exact layouts
+#
+import sys
+
+if not sys.platform.startswith('linux'):
+    raise Exception("Linux-only demo")
+
+# run readdir2_build first, then make sure the shared object is on sys.path
+from _readdir2_cffi import ffi, lib
+
+
+def walk(basefd, path):
+    print '{', path
+    dirfd = lib.openat(basefd, path, 0)
+    if dirfd < 0:
+        # error in openat()
+        return
+    dir = lib.fdopendir(dirfd)
+    dirent = ffi.new("struct dirent *")
+    result = ffi.new("struct dirent **")
+    while True:
+        if lib.readdir_r(dir, dirent, result):
+            # error in readdir_r()
+            break
+        if result[0] == ffi.NULL:
+            break
+        name = ffi.string(dirent.d_name)
+        print '%3d %s' % (dirent.d_type, name)
+        if dirent.d_type == lib.DT_DIR and name != '.' and name != '..':
+            walk(dirfd, name)
+    lib.closedir(dir)
+    print '}'
+
+
+walk(-1, "/tmp")
diff --git a/demo/readdir2_build.py b/demo/readdir2_build.py
new file mode 100644
index 0000000..5cfd872
--- /dev/null
+++ b/demo/readdir2_build.py
@@ -0,0 +1,36 @@
+from cffi import FFI
+
+ffi = FFI()
+ffi.cdef("""
+
+    typedef ... DIR;
+
+    struct dirent {
+        unsigned char  d_type;      /* type of file; not supported
+                                       by all file system types */
+        char           d_name[...]; /* filename */
+        ...;
+    };
+
+    int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
+    int openat(int dirfd, const char *pathname, int flags);
+    DIR *fdopendir(int fd);
+    int closedir(DIR *dirp);
+
+    static const int DT_DIR;
+
+""")
+ffi.set_source("_readdir2_cffi", """
+#ifndef _ATFILE_SOURCE
+#  define _ATFILE_SOURCE
+#endif
+#ifndef _BSD_SOURCE
+#  define _BSD_SOURCE
+#endif
+#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+""")
+
+if __name__ == '__main__':
+    ffi.compile()
diff --git a/demo/readdir2_setup.py b/demo/readdir2_setup.py
new file mode 100644
index 0000000..bd8c19f
--- /dev/null
+++ b/demo/readdir2_setup.py
@@ -0,0 +1,9 @@
+from distutils.core import setup
+import readdir2_build
+
+setup(
+    name="readdir2",
+    version="0.1",
+    py_modules=["readdir2"],
+    ext_modules=[readdir2_build.ffi.distutils_extension('build')],
+)
diff --git a/demo/readdir_build.py b/demo/readdir_build.py
new file mode 100644
index 0000000..f97f404
--- /dev/null
+++ b/demo/readdir_build.py
@@ -0,0 +1,33 @@
+import sys
+from cffi import FFI
+
+if not sys.platform.startswith('linux'):
+    raise Exception("Linux-only demo")
+
+
+ffi = FFI()
+ffi.cdef("""
+
+    typedef void DIR;
+    typedef long ino_t;
+    typedef long off_t;
+
+    struct dirent {
+        ino_t          d_ino;       /* inode number */
+        off_t          d_off;       /* offset to the next dirent */
+        unsigned short d_reclen;    /* length of this record */
+        unsigned char  d_type;      /* type of file; not supported
+                                       by all file system types */
+        char           d_name[256]; /* filename */
+    };
+
+    int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
+    int openat(int dirfd, const char *pathname, int flags);
+    DIR *fdopendir(int fd);
+    int closedir(DIR *dirp);
+
+""")
+ffi.set_source("_readdir", None)
+
+if __name__ == '__main__':
+    ffi.compile()
diff --git a/demo/readdir_ctypes.py b/demo/readdir_ctypes.py
new file mode 100644
index 0000000..4fd1d17
--- /dev/null
+++ b/demo/readdir_ctypes.py
@@ -0,0 +1,69 @@
+# A Linux-only demo
+#
+# For comparison purposes, this is a ctypes version of readdir.py.
+import sys
+import ctypes
+
+if not sys.platform.startswith('linux'):
+    raise Exception("Linux-only demo")
+
+
+DIR_p = ctypes.c_void_p
+ino_t = ctypes.c_long
+off_t = ctypes.c_long
+
+class DIRENT(ctypes.Structure):
+    _fields_ = [
+        ('d_ino', ino_t),                 # inode number
+        ('d_off', off_t),                 # offset to the next dirent
+        ('d_reclen', ctypes.c_ushort),    # length of this record
+        ('d_type', ctypes.c_ubyte),       # type of file; not supported
+                                          #   by all file system types
+        ('d_name', ctypes.c_char * 256),  # filename
+        ]
+DIRENT_p = ctypes.POINTER(DIRENT)
+DIRENT_pp = ctypes.POINTER(DIRENT_p)
+
+C = ctypes.CDLL(None)
+
+readdir_r = C.readdir_r
+readdir_r.argtypes = [DIR_p, DIRENT_p, DIRENT_pp]
+readdir_r.restype = ctypes.c_int
+
+openat = C.openat
+openat.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int]
+openat.restype = ctypes.c_int
+
+fdopendir = C.fdopendir
+fdopendir.argtypes = [ctypes.c_int]
+fdopendir.restype = DIR_p
+
+closedir = C.closedir
+closedir.argtypes = [DIR_p]
+closedir.restype = ctypes.c_int
+
+
+def walk(basefd, path):
+    print '{', path
+    dirfd = openat(basefd, path, 0)
+    if dirfd < 0:
+        # error in openat()
+        return
+    dir = fdopendir(dirfd)
+    dirent = DIRENT()
+    result = DIRENT_p()
+    while True:
+        if readdir_r(dir, dirent, result):
+            # error in readdir_r()
+            break
+        if not result:
+            break
+        name = dirent.d_name
+        print '%3d %s' % (dirent.d_type, name)
+        if dirent.d_type == 4 and name != '.' and name != '..':
+            walk(dirfd, name)
+    closedir(dir)
+    print '}'
+
+
+walk(-1, "/tmp")
diff --git a/demo/readdir_setup.py b/demo/readdir_setup.py
new file mode 100644
index 0000000..c8abdcb
--- /dev/null
+++ b/demo/readdir_setup.py
@@ -0,0 +1,11 @@
+from setuptools import setup
+
+setup(
+    name="example",
+    version="0.1",
+    py_modules=["readdir"],
+    setup_requires=["cffi>=1.0.dev0"],
+    cffi_modules=["readdir_build.py:ffi"],
+    install_requires=["cffi>=1.0.dev0"],
+    zip_safe=False,
+)
diff --git a/demo/recopendirtype.py b/demo/recopendirtype.py
new file mode 100644
index 0000000..768318b
--- /dev/null
+++ b/demo/recopendirtype.py
@@ -0,0 +1,50 @@
+from _recopendirtype import ffi, lib
+
+
+def _posix_error():
+    raise OSError(ffi.errno, os.strerror(ffi.errno))
+
+_dtype_to_smode = {
+    lib.DT_BLK:  0o060000,
+    lib.DT_CHR:  0o020000,
+    lib.DT_DIR:  0o040000,
+    lib.DT_FIFO: 0o010000,
+    lib.DT_LNK:  0o120000,
+    lib.DT_REG:  0o100000,
+    lib.DT_SOCK: 0o140000,
+}
+
+def opendir(dir):
+    if len(dir) == 0:
+        dir = b'.'
+    dirname = dir
+    if not dirname.endswith(b'/'):
+        dirname += b'/'
+    dirp = lib.opendir(dir)
+    if dirp == ffi.NULL:
+        raise _posix_error()
+    dirent = ffi.new("struct dirent *")
+    result = ffi.new("struct dirent **")
+    try:
+        while True:
+            ffi.errno = 0
+            err = lib.readdir_r(dirp, dirent, result)
+            if err:       # really got an error
+                raise OSError(err, os.strerror(err))
+            if result[0] == ffi.NULL:
+                return    # 
+            name = ffi.string(dirent.d_name)
+            if name == b'.' or name == b'..':
+                continue
+            name = dirname + name
+            try:
+                smode = _dtype_to_smode[dirent.d_type]
+            except KeyError:
+                smode = os.lstat(name).st_mode
+            yield name, smode
+    finally:
+        lib.closedir(dirp)
+
+if __name__ == '__main__':
+    for name, smode in opendir(b'/tmp'):
+        print(hex(smode), name)
diff --git a/demo/recopendirtype_build.py b/demo/recopendirtype_build.py
new file mode 100644
index 0000000..fa62a05
--- /dev/null
+++ b/demo/recopendirtype_build.py
@@ -0,0 +1,19 @@
+from cffi import FFI
+import bsdopendirtype_build
+
+ffi = FFI()
+
+# ========== This is a demo of ffi.include() ==========
+ffi.include(bsdopendirtype_build.ffi)
+
+ffi.cdef("""
+    int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
+""")
+
+ffi.set_source("_recopendirtype", """
+    #include <sys/types.h>
+    #include <dirent.h>
+""")
+
+if __name__ == '__main__':
+    ffi.compile()
diff --git a/demo/setup_manual.py b/demo/setup_manual.py
new file mode 100644
index 0000000..2569bb4
--- /dev/null
+++ b/demo/setup_manual.py
@@ -0,0 +1,5 @@
+from distutils.core import setup
+from distutils.extension import Extension
+setup(name='manual',
+      ext_modules=[Extension(name='manual',
+                             sources=['manual.c'])])
diff --git a/demo/winclipboard.py b/demo/winclipboard.py
new file mode 100644
index 0000000..5278cd0
--- /dev/null
+++ b/demo/winclipboard.py
@@ -0,0 +1,40 @@
+__author__ = "Israel Fruchter <israel.fruchter@gmail.com>"
+
+import sys, os
+
+if not sys.platform == 'win32':
+    raise Exception("Windows-only demo")
+
+try:
+    from _winclipboard_cffi import ffi, lib
+except ImportError:
+    print 'run winclipboard_build first, then make sure the shared object is on sys.path'
+    sys.exit(1)
+
+# ffi "knows" about the declared variables and functions from the
+#     cdef parts of the module _winclipboard_cffi created,
+# lib "knows" how to call the functions from the set_source parts
+#     of the module.
+
+def CopyToClipboard(string):
+    '''
+        use win32 api to copy `string` to the clipboard
+    '''
+    hWnd = lib.GetConsoleWindow()
+  
+    if lib.OpenClipboard(hWnd):
+        cstring = ffi.new("char[]", string)
+        size = ffi.sizeof(cstring)
+        
+        # make it a moveable memory for other processes
+        hGlobal = lib.GlobalAlloc(lib.GMEM_MOVEABLE, size)
+        buffer = lib.GlobalLock(hGlobal)
+        lib.memcpy(buffer, cstring, size)
+        lib.GlobalUnlock(hGlobal)
+        
+        res = lib.EmptyClipboard()
+        res = lib.SetClipboardData(lib.CF_TEXT, buffer)
+ 
+        lib.CloseClipboard()
+        
+CopyToClipboard("hello world from cffi")
diff --git a/demo/winclipboard_build.py b/demo/winclipboard_build.py
new file mode 100644
index 0000000..1a510eb
--- /dev/null
+++ b/demo/winclipboard_build.py
@@ -0,0 +1,36 @@
+from cffi import FFI
+
+ffi = FFI()
+ffi.cdef('''
+    typedef void * HANDLE;
+    typedef HANDLE HWND;
+    typedef int BOOL;
+    typedef unsigned int UINT;
+    typedef int SIZE_T;
+    typedef char * LPTSTR;
+    typedef HANDLE HGLOBAL;
+    typedef HANDLE LPVOID;
+
+    HWND GetConsoleWindow(void);
+
+    LPVOID GlobalLock( HGLOBAL hMem );
+    BOOL GlobalUnlock( HGLOBAL hMem );
+    HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
+
+    BOOL  OpenClipboard(HWND hWndNewOwner);
+    BOOL  CloseClipboard(void);
+    BOOL  EmptyClipboard(void);
+    HANDLE  SetClipboardData(UINT uFormat, HANDLE hMem);
+
+    #define CF_TEXT ...
+    #define GMEM_MOVEABLE ...
+
+    void * memcpy(void * s1, void * s2, int n);
+    ''')
+
+ffi.set_source('_winclipboard_cffi', '''
+    #include <windows.h>
+''', libraries=["user32"])
+
+if __name__ == '__main__':
+    ffi.compile()
diff --git a/demo/xclient.py b/demo/xclient.py
new file mode 100644
index 0000000..e4b3dd2
--- /dev/null
+++ b/demo/xclient.py
@@ -0,0 +1,27 @@
+import sys, os
+
+# run xclient_build first, then make sure the shared object is on sys.path
+from _xclient_cffi import ffi, lib
+
+
+# ffi "knows" about the declared variables and functions from the
+#     cdef parts of the module xclient_build created,
+# lib "knows" how to call the functions from the set_source parts
+#     of the module.
+
+
+class XError(Exception):
+    pass
+
+def main():
+    display = lib.XOpenDisplay(ffi.NULL)
+    if display == ffi.NULL:
+        raise XError("cannot open display")
+    w = lib.XCreateSimpleWindow(display, lib.DefaultRootWindow(display),
+                            10, 10, 500, 350, 0, 0, 0)
+    lib.XMapRaised(display, w)
+    event = ffi.new("XEvent *")
+    lib.XNextEvent(display, event)
+
+if __name__ == '__main__':
+    main()
diff --git a/demo/xclient_build.py b/demo/xclient_build.py
new file mode 100644
index 0000000..d6ce9da
--- /dev/null
+++ b/demo/xclient_build.py
@@ -0,0 +1,25 @@
+from cffi import FFI
+ffi = FFI()
+ffi.cdef("""
+
+typedef ... Display;
+typedef struct { ...; } Window;
+
+typedef struct { int type; ...; } XEvent;
+
+Display *XOpenDisplay(char *display_name);
+Window DefaultRootWindow(Display *display);
+int XMapRaised(Display *display, Window w);
+Window XCreateSimpleWindow(Display *display, Window parent, int x, int y,
+                           unsigned int width, unsigned int height,
+                           unsigned int border_width, unsigned long border,
+                           unsigned long background);
+int XNextEvent(Display *display, XEvent *event_return);
+""")
+
+ffi.set_source('_xclient_cffi', """
+            #include <X11/Xlib.h>
+""", libraries=['X11'])
+
+if __name__ == '__main__':
+    ffi.compile(verbose=True)
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..285361c
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,89 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  dirhtml   to make HTML files named index.html in directories"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  qthelp    to make HTML files and a qthelp project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CFFI.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CFFI.qhc"
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/doc/make.bat b/doc/make.bat
new file mode 100644
index 0000000..5daa6b5
--- /dev/null
+++ b/doc/make.bat
@@ -0,0 +1,113 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+set SPHINXBUILD=sphinx-build
+set BUILDDIR=build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html      to make standalone HTML files
+	echo.  dirhtml   to make HTML files named index.html in directories
+	echo.  pickle    to make pickle files
+	echo.  json      to make JSON files
+	echo.  htmlhelp  to make HTML files and a HTML help project
+	echo.  qthelp    to make HTML files and a qthelp project
+	echo.  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  changes   to make an overview over all changed/added/deprecated items
+	echo.  linkcheck to check all external links for integrity
+	echo.  doctest   to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\CFFI.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\CFFI.ghc
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+:end
diff --git a/doc/misc/design.rst b/doc/misc/design.rst
new file mode 100644
index 0000000..390049c
--- /dev/null
+++ b/doc/misc/design.rst
@@ -0,0 +1,51 @@
+================
+Design decisions
+================
+
+* Generally follow LuaJIT's ffi: http://luajit.org/ext_ffi.html
+
+* Be explicit: almost no automatic conversions.  Here is the set
+  of automatic conversions: the various C integer types are
+  automatically wrapped and unwrapped to regular applevel integers.  The
+  type ``char`` might correspond to single-character strings instead;
+  for integer correspondance you would use ``signed char`` or ``unsigned
+  char``.  We might also decide that ``const char *`` automatically maps
+  to strings; for cases where you don't want that, use ``char *``.
+
+* Integers are not automatically converted when passed as vararg
+  arguments.  You have to use explicitly ``ffi.new("int", 42)`` or
+  ``ffi.new("long", 42)`` to resolve the ambiguity.  Floats would be
+  fine (varargs in C can only accept ``double``, not ``float``), but
+  there is again ambiguity between characters and strings.  Even with
+  floats the result is a bit strange because passing a float works
+  but passing an integer not.  I would fix this once and for all by
+  saying that varargs must *always* be a cdata (from ``ffi.new()``).
+  The possibly acceptable exception would be None (for ``NULL``).
+
+* The internal class ``blob`` is used for raw-malloced data.  You only
+  get a class that has internally a ``blob`` instance (or maybe is a
+  subclass of ``blob``) by calling ``ffi.new(struct-or-array-type)``.
+  The other cases, namely the cases where the type is a pointer or a
+  primitive, don't need a blob because it's not possible to take their
+  raw address.
+
+* It would be possible to add a debug mode: when we cast ``struct foo``
+  to ``struct foo *`` or store it in some other struct, then we would
+  additionally record a weakref to the original ``struct foo`` blob.
+  If later we try to access the ``struct foo *`` but the weakref shows
+  that the blob was freed, we complain.  This is a difference with
+  ctypes, which in these cases would store a strong reference and
+  keep the blob alive.  "Explicit is better than implicit", so we ask
+  the user to keep a reference to the original blob alive as long as
+  it may be used (instead of doing the right things in 90% of the cases
+  but still crashing in the remaining 10%).
+
+* LuaJIT uses ``struct foo &`` for a number of things, like for ``p[0]``
+  if ``p`` is a ``struct foo *``.  I suppose it's not a bad idea at least
+  to have internally such types, even if you can't specify them through
+  pycparser.  Basically ``struct foo &`` is a type that doesn't own a
+  blob, whereas ``struct foo`` is the type that does.
+
+* LuaJIT uses ``int[?]`` which pycparser doesn't accept.  I propose
+  instead to use ``int[]`` for the same purpose (its use is anyway quite
+  close to the C standard's use of ``int[]``).
diff --git a/doc/misc/grant-cffi-1.0.rst b/doc/misc/grant-cffi-1.0.rst
new file mode 100644
index 0000000..b026209
--- /dev/null
+++ b/doc/misc/grant-cffi-1.0.rst
@@ -0,0 +1,124 @@
+
+===========================
+Grant Proposal for CFFI 1.0
+===========================
+
+*Accepted by the PSF board on April 4, 2015*
+
+This Grant Proposal is to give a boost towards "CFFI 1.0".  Two main
+issues with the current CFFI need to be solved: the difficulties of
+installation, and the potentially large time taken at import.
+
+1. The difficulties of installation can be seen from outside by looking
+at various workarounds and 3rd-party documentation that have grown into
+existence.  For example, the `setup.py` of projects like cryptography,
+PyNaCl and bcrypt deploys workarounds that are explicitly documented in
+https://caremad.io/2014/11/distributing-a-cffi-project/.
+
+2. The time taken at import is excessive in some cases.  For example,
+importing `pygame-cffi` on a Raspberry Pi ARM board takes on the order
+of 10 to 20 seconds (and this is the "fast" case where the compiler
+doesn't need to be invoked any more).
+
+
+Technical Overview
+------------------
+
+"CFFI" is an existing Python project which complements the ctypes,
+SWIG and Cython approaches to ease writing C Extension Modules for
+Python.  It has several advantages over the previous approaches, which
+are presented at the start of the documentation at
+http://cffi.readthedocs.org/en/latest/ .  It has been very successful
+so far: http://pypi-ranking.info/alltime records almost 7 million
+downloads (for comparison, the #1 of all packages has almost 36
+million downloads).  CFFI works on any Python >= 2.6, including 3.x,
+as well as on PyPy.
+
+One problem is that while getting started with CFFI is very easy, the
+installation process of a package that uses CFFI has got its rough
+edges.  CFFI (at least in its "verify()" mode) is based on calling the
+C compiler to get information about the exact C types, structures,
+argument types to functions, and so on.  The C compiler is invoked
+transparently at run-time, and the results cached.  A
+correctly-installed package using CFFI should cache the results at
+installation time, but it can be difficult to ensure that no more
+run-time compiler invocation is needed; doing so requires following
+some extra guidelines or understanding some internal details.  (The
+problem is particularly acute on Windows where a typical user might
+not have a proper C compiler installed.)
+
+To fix this, we have in mind adding a different CFFI mode (replacing
+"verify()"), while keeping the access to the underlying C library
+unmodified.  In this mode, the code containing the cdef() and verify()
+invocations would be moved to a separate Python source file.  Running
+that Python file would produce a dynamically-linked library.  There
+would be no caching logic involved; you would need to run it
+explicitly during development whenever you made changes to it, to
+re-generate and re-compile the dynamically-linked library.
+
+When distributed, the same file would be run (once) during
+installation.  This can be fully automated in setuptools-based
+setup.py files; alternatively, it can be done in distutils-based
+setup.py files by requiring prior manual installation of CFFI itself.
+
+A major difference with the existing verify() approach would be that
+the ``.so/.dll/.dylib`` file would not be immediately loaded into the
+process; you would load it only from the installed program at
+run-time, and get the ``ffi`` and ``lib`` objects in this way (these
+are the two objects that you use so far to access a C library with
+verify()).
+
+Additionally, this would solve another issue: every import of a large
+CFFI-using package takes a while so far.  This is caused by CFFI
+needing to parse again the C source code given in the cdef() (adding a
+run-time dependency to the ``pycparser`` and ``ply`` packages).  CFFI
+also computes a CRC to know if it can reuse its cache.  In the
+proposed change, all the cdef() code would be pre-parsed and stored in
+the dynamically-linked library, and no CRC would be needed.  This
+would massively reduce the import times.
+
+
+Grant objective
+---------------
+
+The objective is to give a boost towards "CFFI 1.0", which needs to have
+the functionalities described above in order to solve the two main
+issues with the current CFFI: the difficulties of installation, and the
+time taken at import.
+
+Included in the objective: the internal refactorings of CFFI that are
+needed to get it done cleanly.  The goal is to avoid simply adding
+another layer on top of the old unchanged CFFI.
+
+This work may happen eventually in any case, but support from the PSF
+would help make it happen sooner rather than later.
+
+
+Grant size
+----------
+
+2'500 US$ for supporting the development time.  This would cover 2.5
+weeks of full-time work at the part-time cost of 25 US$ per hour.
+
+The estimated work time until the CFFI 1.0 release is a bit larger
+than that (I estimate it at roughly 4 weeks), but 2.5 weeks should
+cover all the basics.  An extended grant size of 4'000 US$ would be
+appreciated but not required ``:-)``
+
+
+Grant beneficiaries
+-------------------
+
+Armin Rigo, main author of CFFI, committing 2.5 weeks of full-time
+work.
+
+
+Grant follow-up
+---------------
+
+I will report on the success of the grant on the CFFI mailing list and
+on the blog I usually post to (the PyPy blog) and mention the PSF as
+providing the grant.  The PSF will receive an email pointing to these
+postings once they are out.  Moreover a full CFFI 1.0 release should
+follow (likely starting with beta versions); the PSF will receive
+another email pointing to it.
diff --git a/doc/misc/parse_c_type.rst b/doc/misc/parse_c_type.rst
new file mode 100644
index 0000000..1d1029d
--- /dev/null
+++ b/doc/misc/parse_c_type.rst
@@ -0,0 +1,72 @@
+==================================================
+CPython C extension module produced by recompile()
+==================================================
+
+Global variable::
+
+  _cffi_opcode_t _cffi_types[];
+
+Every _cffi_types entry is initially an odd integer.  At runtime, it
+is fixed to be a `CTypeDescrObject *` when the odd integer is
+interpreted and turned into a real <ctype> object.
+
+The generated C functions are listed in _cffi_globals, a sorted array
+of entries which get turned lazily into real <builtin function
+objects>.  Each entry in this array has an index in the _cffi_types
+array, which describe the function type (OP_FUNCTION opcode, see
+below).  We turn the odd integers describing argument and return types
+into real CTypeDescrObjects at the point where the entry is turned
+into a real builtin function object.
+
+The odd integers are "opcodes" that contain a type info in the lowest
+byte.  The remaining high bytes of the integer is an "arg" that depends
+on the type info:
+
+OP_PRIMITIVE
+    the arg tells which primitive type it is (an index in some list)
+
+OP_POINTER
+    the arg is the index of the item type in the _cffi_types array.
+
+OP_ARRAY
+    the arg is the index of the item type in the _cffi_types array.
+    followed by another opcode that contains (uintptr_t)length_of_array.
+
+OP_OPEN_ARRAY
+    for syntax like "int[]".  same as OP_ARRAY but without the length
+
+OP_STRUCT_UNION
+    the arg is the index of the struct/union in _cffi_structs_unions
+
+OP_ENUM
+    the arg is the index of the enum in _cffi_enums
+
+OP_TYPENAME
+    the arg is the index of the typename in _cffi_typenames
+
+OP_FUNCTION
+    the arg is the index of the result type in _cffi_types.
+    followed by other opcodes for the arguments.
+    terminated by OP_FUNCTION_END.
+
+OP_FUNCTION_END
+    the arg's lowest bit is set if there is a "..." argument.
+
+OP_NOOP
+    simple indirection: the arg is the index to look further in
+
+There are other opcodes, used not inside _cffi_types but in other
+individual ``type_op`` fields.  Most importantly, these are used
+on _cffi_globals entries:
+
+OP_CPYTHON_BLTN_*
+    declare a function
+
+OP_CONSTANT
+    declare a non-integral constant
+
+OP_CONSTANT_INT
+    declare an int constant
+
+OP_GLOBAL_VAR
+    declare a global var
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
new file mode 100644
index 0000000..f0bc6ba
--- /dev/null
+++ b/doc/source/cdef.rst
@@ -0,0 +1,992 @@
+======================================
+Preparing and Distributing modules
+======================================
+
+.. contents::
+
+There are three or four different ways to use CFFI in a project.
+In order of complexity:
+
+* The **"in-line", "ABI mode"**:
+
+  .. code-block:: python
+
+    import cffi
+
+    ffi = cffi.FFI()
+    ffi.cdef("C-like declarations")
+    lib = ffi.dlopen("libpath")
+
+    # use ffi and lib here
+
+.. _out-of-line-abi:
+
+* The **"out-of-line",** but still **"ABI mode",** useful to organize
+  the code and reduce the import time:
+
+  .. code-block:: python
+
+    # in a separate file "package/foo_build.py"
+    import cffi
+
+    ffibuilder = cffi.FFI()
+    ffibuilder.set_source("package._foo", None)
+    ffibuilder.cdef("C-like declarations")
+
+    if __name__ == "__main__":
+        ffibuilder.compile()
+
+  Running ``python foo_build.py`` produces a file ``_foo.py``, which
+  can then be imported in the main program:
+
+  .. code-block:: python
+
+    from package._foo import ffi
+    lib = ffi.dlopen("libpath")
+
+    # use ffi and lib here
+
+.. _out-of-line-api:
+
+* The **"out-of-line", "API mode"** gives you the most flexibility
+  and speed to access a C library at the level of C, instead of at the
+  binary level:
+
+  .. code-block:: python
+
+    # in a separate file "package/foo_build.py"
+    import cffi
+
+    ffibuilder = cffi.FFI()
+    ffibuilder.set_source("package._foo", r"""real C code""")   # <=
+    ffibuilder.cdef("C-like declarations with '...'")
+
+    if __name__ == "__main__":
+        ffibuilder.compile(verbose=True)
+
+  Running ``python foo_build.py`` produces a file ``_foo.c`` and
+  invokes the C compiler to turn it into a file ``_foo.so`` (or
+  ``_foo.pyd`` or ``_foo.dylib``).  It is a C extension module which
+  can be imported in the main program:
+
+  .. code-block:: python
+
+    from package._foo import ffi, lib
+    # no ffi.dlopen()
+
+    # use ffi and lib here
+
+.. _distutils-setuptools:
+
+* Finally, you can (but don't have to) use CFFI's **Distutils** or
+  **Setuptools integration** when writing a ``setup.py``.  For
+  Distutils (only in out-of-line API mode):
+
+  .. code-block:: python
+
+    # setup.py (requires CFFI to be installed first)
+    from distutils.core import setup
+
+    import foo_build   # possibly with sys.path tricks to find it
+
+    setup(
+        ...,
+        ext_modules=[foo_build.ffibuilder.distutils_extension()],
+    )
+
+  For Setuptools (out-of-line, but works in ABI or API mode;
+  recommended):
+
+  .. code-block:: python
+
+    # setup.py (with automatic dependency tracking)
+    from setuptools import setup
+
+    setup(
+        ...,
+        setup_requires=["cffi>=1.0.0"],
+        cffi_modules=["package/foo_build.py:ffibuilder"],
+        install_requires=["cffi>=1.0.0"],
+    )
+
+  Note again that the ``foo_build.py`` example contains the following
+  lines, which mean that the ``ffibuilder`` is not actually compiled
+  when ``package.foo_build`` is merely imported---it will be compiled
+  independently by the Setuptools logic, using compilation parameters
+  provided by Setuptools:
+
+  .. code-block:: python
+
+    if __name__ == "__main__":    # not when running with setuptools
+        ffibuilder.compile(verbose=True)
+
+* Note that some bundler tools that try to find all modules used by a
+  project, like PyInstaller, will miss ``_cffi_backend`` in the
+  out-of-line mode because your program contains no explicit ``import
+  cffi`` or ``import _cffi_backend``.  You need to add
+  ``_cffi_backend`` explicitly (as a "hidden import" in PyInstaller,
+  but it can also be done more generally by adding the line ``import
+  _cffi_backend`` in your main program).
+
+Note that CFFI actually contains two different ``FFI`` classes.  The
+page `Using the ffi/lib objects`_ describes the common functionality.
+It is what you get in the ``from package._foo import ffi`` lines above.
+On the other hand, the extended ``FFI`` class is the one you get from
+``import cffi; ffi_or_ffibuilder = cffi.FFI()``.  It has the same
+functionality (for in-line use), but also the extra methods described
+below (to prepare the FFI).  NOTE: We use the name ``ffibuilder``
+instead of ``ffi`` in the out-of-line context, when the code is about
+producing a ``_foo.so`` file; this is an attempt to distinguish it
+from the different ``ffi`` object that you get by later saying
+``from _foo import ffi``.
+
+.. _`Using the ffi/lib objects`: using.html
+
+The reason for this split of functionality is that a regular program
+using CFFI out-of-line does not need to import the ``cffi`` pure
+Python package at all.  (Internally it still needs ``_cffi_backend``,
+a C extension module that comes with CFFI; this is why CFFI is also
+listed in ``install_requires=..`` above.  In the future this might be
+split into a different PyPI package that only installs
+``_cffi_backend``.)
+
+Note that a few small differences do exist: notably, ``from _foo import
+ffi`` returns an object of a type written in C, which does not let you
+add random attributes to it (nor does it have all the
+underscore-prefixed internal attributes of the Python version).
+Similarly, the ``lib`` objects returned by the C version are read-only,
+apart from writes to global variables.  Also, ``lib.__dict__`` does
+not work before version 1.2 or if ``lib`` happens to declare a name
+called ``__dict__`` (use instead ``dir(lib)``).  The same is true
+for ``lib.__class__``, ``lib.__all__`` and ``lib.__name__`` added
+in successive versions.
+
+
+.. _cdef:
+
+ffi/ffibuilder.cdef(): declaring types and functions
+----------------------------------------------------
+
+**ffi/ffibuilder.cdef(source)**: parses the given C source.
+It registers all the functions, types, constants and global variables in
+the C source.  The types can be used immediately in ``ffi.new()`` and
+other functions.  Before you can access the functions and global
+variables, you need to give ``ffi`` another piece of information: where
+they actually come from (which you do with either ``ffi.dlopen()`` or
+``ffi.set_source()``).
+
+.. _`all types listed above`:
+
+The C source is parsed internally (using ``pycparser``).  This code
+cannot contain ``#include``.  It should typically be a self-contained
+piece of declarations extracted from a man page.  The only things it
+can assume to exist are the standard types:
+
+* char, short, int, long, long long (both signed and unsigned)
+
+* float, double, long double
+
+* intN_t, uintN_t (for N=8,16,32,64), intptr_t, uintptr_t, ptrdiff_t,
+  size_t, ssize_t
+
+* wchar_t (if supported by the backend).  *New in version 1.11:*
+  char16_t and char32_t.
+
+* _Bool and bool (equivalent).  If not directly supported by the C
+  compiler, this is declared with the size of ``unsigned char``.
+
+* FILE.  `See here.`__
+
+* all `common Windows types`_ are defined if you run
+  on Windows (``DWORD``, ``LPARAM``, etc.).  Exception:
+  ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE PTCHAR`` are
+  not automatically defined; see `ffi.set_unicode()`_.
+
+* the other standard integer types from
+  stdint.h, like ``intmax_t``, as long as they map to integers of 1,
+  2, 4 or 8 bytes.  Larger integers are not supported.
+
+.. __: ref.html#file
+.. _`common Windows types`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
+
+The declarations can also contain "``...``" at various places; these are
+placeholders that will be completed by the compiler.  More information
+about it below in `Letting the C compiler fill the gaps`_.
+
+Note that all standard type names listed above are handled as
+*defaults* only (apart from the ones that are keywords in the C
+language).  If your ``cdef`` contains an explicit typedef that
+redefines one of the types above, then the default described above is
+ignored.  (This is a bit hard to implement cleanly, so in some corner
+cases it might fail, notably with the error ``Multiple type specifiers
+with a type tag``.  Please report it as a bug if it does.)
+
+Multiple calls to ``ffi.cdef()`` are possible.  Beware that it can be
+slow to call ``ffi.cdef()`` a lot of times, a consideration that is
+important mainly in in-line mode.
+
+The ``ffi.cdef()`` call optionally takes an extra argument: either
+``packed`` or ``pack``.  If you pass ``packed=True``,
+then all structs declared within
+this cdef are "packed".  (If you need both packed and non-packed
+structs, use several cdefs in sequence.)  This
+has a meaning similar to ``__attribute__((packed))`` in GCC.  It
+specifies that all structure fields should have an alignment of one
+byte.  (Note that the packed attribute has no effect on bit fields so
+far, which mean that they may be packed differently than on GCC.
+Also, this has no effect on structs declared with ``"...;"``---more
+about it later in `Letting the C compiler fill the gaps`_.)
+*New in version 1.12:*  In ABI mode, you can also pass ``pack=n``,
+with an integer ``n`` which must be a power of two.  Then the
+alignment of any field is limited to ``n`` if it would otherwise be
+greater than ``n``.  Passing ``pack=1`` is equivalent to passing
+``packed=True``.  This is meant to emulate ``#pragma pack(n)`` from
+the MSVC compiler.  On Windows, the default is ``pack=8`` (from cffi
+1.12 onwards); on other platforms, the default is ``pack=None``.
+
+Note that you can use the type-qualifiers ``const`` and ``restrict``
+(but not ``__restrict`` or ``__restrict__``) in the ``cdef()``, but
+this has no effect on the cdata objects that you get at run-time (they
+are never ``const``).  The effect is limited to knowing if a global
+variable is meant to be a constant or not.  Also, *new in version
+1.3:* when using ``set_source()`` or ``verify()``, these two
+qualifiers are copied from the cdef to the generated C code; this
+fixes warnings by the C compiler.
+
+Note a trick if you copy-paste code from sources in which there are
+extra macros (for example, the Windows documentation uses SAL
+annotations like ``_In_`` or ``_Out_``).  These hints must be removed
+in the string given to cdef(), but it can be done programmatically
+like this::
+
+    ffi.cdef(re.sub(r"\b(_In_|_Inout_|_Out_|_Outptr_)(opt_)?\b", " ",
+      """
+        DWORD WINAPI GetModuleFileName(
+          _In_opt_ HMODULE hModule,
+          _Out_    LPTSTR  lpFilename,
+          _In_     DWORD   nSize
+        );
+      """))
+
+Note also that pycparser, the underlying C parser, recognizes
+preprocessor-like directives in the following format: ``# NUMBER
+"FILE"``.  For example, if you put ``# 42 "foo.h"`` in the middle of the
+string passed to ``cdef()`` and there is an error two lines later, then
+it is reported with an error message that starts with ``foo.h:43:`` (the
+line which is given the number 42 is the line immediately after the
+directive).  *New in version 1.10.1:*  CFFI automatically puts the line
+``# 1 "<cdef source string>"`` just before the string you give to
+``cdef()``.
+
+
+.. _`ffi.set_unicode()`:
+
+**ffi.set_unicode(enabled_flag)**: Windows: if ``enabled_flag`` is
+True, enable the ``UNICODE`` and ``_UNICODE`` defines in C, and
+declare the types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE
+PTCHAR`` to be (pointers to) ``wchar_t``.  If ``enabled_flag`` is
+False, declare these types to be (pointers to) plain 8-bit characters.
+(These types are not predeclared at all if you don't call
+``set_unicode()``.)
+
+The reason behind this method is that a lot of standard functions have
+two versions, like ``MessageBoxA()`` and ``MessageBoxW()``.  The
+official interface is ``MessageBox()`` with arguments like
+``LPTCSTR``.  Depending on whether ``UNICODE`` is defined or not, the
+standard header renames the generic function name to one of the two
+specialized versions, and declares the correct (unicode or not) types.
+
+Usually, the right thing to do is to call this method with True.  Be
+aware (particularly on Python 2) that, afterwards, you need to pass unicode
+strings as arguments instead of byte strings.
+
+
+.. _loading-libraries:
+
+ffi.dlopen(): loading libraries in ABI mode
+-------------------------------------------
+
+``ffi.dlopen(libpath, [flags])``: this function opens a shared library and
+returns a module-like library object.  Use this when you are fine with
+the limitations of ABI-level access to the system (dependency on ABI
+details, getting crashes instead of C compiler errors/warnings, and
+higher overhead to call the C functions).  In case of doubt, read again
+`ABI versus API`_ in the overview.
+
+.. _`ABI versus API`: overview.html#abi-versus-api
+
+You can use the library object to call the functions previously
+declared by ``ffi.cdef()``, to read constants, and to read or write
+global variables.  Note that you can use a single ``cdef()`` to
+declare functions from multiple libraries, as long as you load each of
+them with ``dlopen()`` and access the functions from the correct one.
+
+The ``libpath`` is the file name of the shared library, which can
+contain a full path or not (in which case it is searched in standard
+locations, as described in ``man dlopen``), with extensions or not.
+Alternatively, if ``libpath`` is None, it returns the standard C library
+(which can be used to access the functions of glibc, on Linux).  Note
+that ``libpath`` `cannot be None`__ on Windows with Python 3.
+
+.. __: http://bugs.python.org/issue23606
+
+Let me state it again: this gives ABI-level access to the library, so
+you need to have all types declared manually exactly as they were
+while the library was made.  No checking is done.  Mismatches can
+cause random crashes.  API-level access, on the other hand, is safer.
+Speed-wise, API-level access is much faster (it is common to have
+the opposite misconception about performance).
+
+Note that only functions and global variables live in library objects;
+the types exist in the ``ffi`` instance independently of library objects.
+This is due to the C model: the types you declare in C are not tied to a
+particular library, as long as you ``#include`` their headers; but you
+cannot call functions from a library without linking it in your program,
+as ``dlopen()`` does dynamically in C.
+
+For the optional ``flags`` argument, see ``man dlopen`` (ignored on
+Windows).  It defaults to ``ffi.RTLD_NOW``.
+
+This function returns a "library" object that gets closed when it goes
+out of scope.  Make sure you keep the library object around as long as
+needed.  (Alternatively, the out-of-line FFIs have a method
+``ffi.dlclose(lib)``.)
+
+.. _dlopen-note:
+
+Note: the old version of ``ffi.dlopen()`` from the in-line ABI mode
+tries to use ``ctypes.util.find_library()`` if it cannot directly find
+the library.  The newer out-of-line ``ffi.dlopen()`` no longer does it
+automatically; it simply passes the argument it receives to the
+underlying ``dlopen()`` or ``LoadLibrary()`` function.  If needed, it
+is up to you to use ``ctypes.util.find_library()`` or any other way to
+look for the library's filename.  This also means that
+``ffi.dlopen(None)`` no longer work on Windows; try instead
+``ffi.dlopen(ctypes.util.find_library('c'))``.
+
+
+ffibuilder.set_source(): preparing out-of-line modules
+------------------------------------------------------
+
+**ffibuilder.set_source(module_name, c_header_source, [\*\*keywords...])**:
+prepare the ffi for producing out-of-line an external module called
+``module_name``.
+
+``ffibuilder.set_source()`` by itself does not write any file, but merely
+records its arguments for later.  It can therefore be called before or
+after ``ffibuilder.cdef()``.
+
+In **ABI mode,** you call ``ffibuilder.set_source(module_name, None)``.  The
+argument is the name (or dotted name inside a package) of the Python
+module to generate.  In this mode, no C compiler is called.
+
+In **API mode,** the ``c_header_source`` argument is a string that
+will be pasted into the .c file generated.  Typically, it is specified as
+``r""" ...multiple lines of C code... """`` (the ``r`` prefix allows these
+lines to contain a literal ``\n``, for example).  This piece of C code
+typically contains some ``#include``, but may also contain more,
+like definitions for custom "wrapper" C functions.  The goal is that
+the .c file can be generated like this::
+
+    // C file "module_name.c"
+    #include <Python.h>
+
+    ...c_header_source...
+
+    ...magic code...
+
+where the "magic code" is automatically generated from the ``cdef()``.
+For example, if the ``cdef()`` contains ``int foo(int x);`` then the
+magic code will contain logic to call the function ``foo()`` with an
+integer argument, itself wrapped inside some CPython or PyPy-specific
+code.
+
+The keywords arguments to ``set_source()`` control how the C compiler
+will be called.  They are passed directly to distutils_ or setuptools_
+and include at least ``sources``, ``include_dirs``, ``define_macros``,
+``undef_macros``, ``libraries``, ``library_dirs``, ``extra_objects``,
+``extra_compile_args`` and ``extra_link_args``.  You typically need at
+least ``libraries=['foo']`` in order to link with ``libfoo.so`` or
+``libfoo.so.X.Y``, or ``foo.dll`` on Windows.  The ``sources`` is a
+list of extra .c files compiled and linked together (the file
+``module_name.c`` shown above is always generated and automatically added as the
+first argument to ``sources``).  See the distutils documentations for
+`more information about the other arguments`__.
+
+.. __: http://docs.python.org/distutils/setupscript.html#library-options
+.. _distutils: http://docs.python.org/distutils/setupscript.html#describing-extension-modules
+.. _setuptools: https://pythonhosted.org/setuptools/setuptools.html
+
+An extra keyword argument processed internally is
+``source_extension``, defaulting to ``".c"``.  The file generated will
+be actually called ``module_name + source_extension``.  Example for
+C++ (but note that there are still a few known issues of C-versus-C++
+compatibility):
+
+.. code-block:: python
+
+    ffibuilder.set_source("mymodule", r'''
+    extern "C" {
+        int somefunc(int somearg) { return real_cpp_func(somearg); }
+    }
+    ''', source_extension='.cpp')
+
+.. _pkgconfig:
+
+**ffibuilder.set_source_pkgconfig(module_name, pkgconfig_libs,
+c_header_source, [\*\*keywords...])**:
+
+*New in version 1.12.*  This is equivalent to ``set_source()`` but it
+first calls the system utility ``pkg-config`` with the package names
+given in the list ``pkgconfig_libs``.  It collects the information
+obtained in this way and adds it to the explicitly-provided
+``**keywords`` (if any).  This should probably not be used on Windows.
+
+If the ``pkg-config`` program is not installed or does not know about
+the requested library, the call fails with ``cffi.PkgConfigError``.  If
+necessary, you can catch this error and try to call ``set_source()``
+directly.  (Ideally, you should also do that if the ``ffibuilder``
+instance has no method ``set_source_pkgconfig()``, to support older
+versions of cffi.)
+
+
+Letting the C compiler fill the gaps
+------------------------------------
+
+If you are using a C compiler ("API mode"), then:
+
+*  functions taking or returning integer or float-point arguments can be
+   misdeclared: if e.g. a function is declared by ``cdef()`` as taking a
+   ``int``, but actually takes a ``long``, then the C compiler handles the
+   difference.
+
+*  other arguments are checked: you get a compilation warning or error
+   if you pass a ``int *`` argument to a function expecting a ``long *``.
+
+*  similarly, most other things declared in the ``cdef()`` are checked,
+   to the best we implemented so far; mistakes give compilation
+   warnings or errors.
+
+Moreover, you can use "``...``" (literally, dot-dot-dot) in the
+``cdef()`` at various places, in order to ask the C compiler to fill
+in the details.  These places are:
+
+*  structure declarations: any ``struct { }`` that ends with "``...;``" as
+   the last "field" is
+   partial: it may be missing fields and/or have them declared out of order.
+   This declaration will be corrected by the compiler.  (But note that you
+   can only access fields that you declared, not others.)  Any ``struct``
+   declaration which doesn't use "``...``" is assumed to be exact, but this is
+   checked: you get an error if it is not correct.
+
+*  integer types: the syntax "``typedef
+   int... foo_t;``" declares the type ``foo_t`` as an integer type
+   whose exact size and signedness is not specified.  The compiler will
+   figure it out.  (Note that this requires ``set_source()``; it does
+   not work with ``verify()``.)  The ``int...`` can be replaced with
+   ``long...`` or ``unsigned long long...`` or any other primitive
+   integer type, with no effect.  The type will always map to one of
+   ``(u)int(8,16,32,64)_t`` in Python, but in the generated C code,
+   only ``foo_t`` is used.
+
+* *New in version 1.3:* floating-point types: "``typedef
+  float... foo_t;``" (or equivalently "``typedef double... foo_t;``")
+  declares ``foo_t`` as a-float-or-a-double; the compiler will figure
+  out which it is.  Note that if the actual C type is even larger
+  (``long double`` on some platforms), then compilation will fail.
+  The problem is that the Python "float" type cannot be used to store
+  the extra precision.  (Use the non-dot-dot-dot syntax ``typedef long
+  double foo_t;`` as usual, which returns values that are not Python
+  floats at all but cdata "long double" objects.)
+
+*  unknown types: the syntax "``typedef ... foo_t;``" declares the type
+   ``foo_t`` as opaque.  Useful mainly for when the API takes and returns
+   ``foo_t *`` without you needing to look inside the ``foo_t``.  Also
+   works with "``typedef ... *foo_p;``" which declares the pointer type
+   ``foo_p`` without giving a name to the opaque type itself.  Note that
+   such an opaque struct has no known size, which prevents some operations
+   from working (mostly like in C).  *You cannot use this syntax to
+   declare a specific type, like an integer type!  It declares opaque
+   struct-like types only.*  In some cases you need to say that
+   ``foo_t`` is not opaque, but just a struct where you don't know any
+   field; then you would use "``typedef struct { ...; } foo_t;``".
+
+*  array lengths: when used as structure fields or in global variables,
+   arrays can have an unspecified length, as in "``int n[...];``".  The
+   length is completed by the C compiler.
+   This is slightly different from "``int n[];``", because the latter
+   means that the length is not known even to the C compiler, and thus
+   no attempt is made to complete it.  This supports
+   multidimensional arrays: "``int n[...][...];``".
+
+   *New in version 1.2:* "``int m[][...];``", i.e. ``...`` can be used
+   in the innermost dimensions without being also used in the outermost
+   dimension.  In the example given, the length of the ``m`` array is
+   assumed not to be known to the C compiler, but the length of every
+   item (like the sub-array ``m[0]``) is always known the C compiler.
+   In other words, only the outermost dimension can be specified as
+   ``[]``, both in C and in CFFI, but any dimension can be given as
+   ``[...]`` in CFFI.
+
+*  enums: if you don't know the exact order (or values) of the declared
+   constants, then use this syntax: "``enum foo { A, B, C, ... };``"
+   (with a trailing "``...``").  The C compiler will be used to figure
+   out the exact values of the constants.  An alternative syntax is
+   "``enum foo { A=..., B, C };``" or even
+   "``enum foo { A=..., B=..., C=... };``".  Like
+   with structs, an ``enum`` without "``...``" is assumed to
+   be exact, and this is checked.
+
+*  integer constants and macros: you can write in the ``cdef`` the line
+   "``#define FOO ...``", with any macro name FOO but with ``...`` as
+   a value.  Provided the macro
+   is defined to be an integer value, this value will be available via
+   an attribute of the library object.  The
+   same effect can be achieved by writing a declaration
+   ``static const int FOO;``.  The latter is more general because it
+   supports other types than integer types (note: the C syntax is then
+   to write the ``const`` together with the variable name, as in
+   ``static char *const FOO;``).
+
+Currently, it is not supported to find automatically which of the
+various integer or float types you need at which place---except in the
+following case: if such a type is explicitly named.  For an integer
+type, use ``typedef int... the_type_name;``, or another type like
+``typedef unsigned long... the_type_name;``.  Both are equivalent and
+replaced by the real C type, which must be an integer type.
+Similarly, for floating-point types, use ``typedef float...
+the_type_name;`` or equivalently ``typedef double...  the_type_name;``.
+Note that ``long double`` cannot be detected this way.
+
+In the case of function arguments or return types, when it is a simple
+integer/float type, you can simply misdeclare it.  If you misdeclare a
+function ``void f(long)`` as ``void f(int)``, it still works (but you
+have to call it with arguments that fit an int).  It works because the C
+compiler will do the casting for us.  This C-level casting of arguments
+and return types only works for regular function, and not for function
+pointer types; currently, it also does not work for variadic functions.
+
+For more complex types, you have no choice but be precise.  For example,
+you cannot misdeclare a ``int *`` argument as ``long *``, or a global
+array ``int a[5];`` as ``long a[5];``.  CFFI considers `all types listed
+above`_ as primitive (so ``long long a[5];`` and ``int64_t a[5]`` are
+different declarations).  The reason for that is detailed in `a comment
+about an issue.`__
+
+.. __: https://bitbucket.org/cffi/cffi/issues/265/cffi-doesnt-allow-creating-pointers-to#comment-28406958
+
+
+ffibuilder.compile() etc.: compiling out-of-line modules
+--------------------------------------------------------
+
+You can use one of the following functions to actually generate the
+.py or .c file prepared with ``ffibuilder.set_source()`` and
+``ffibuilder.cdef()``.
+
+Note that these function won't overwrite a .py/.c file with exactly
+the same content, to preserve the mtime.  In some cases where you need
+the mtime to be updated anyway, delete the file before calling the
+functions.
+
+*New in version 1.8:* the C code produced by ``emit_c_code()`` or
+``compile()`` contains ``#define Py_LIMITED_API``.  This means that on
+CPython >= 3.2, compiling this source produces a binary .so/.dll that
+should work for any version of CPython >= 3.2 (as opposed to only for
+the same version of CPython x.y).  However, the standard ``distutils``
+package will still produce a file called e.g.
+``NAME.cpython-35m-x86_64-linux-gnu.so``.  You can manually rename it to
+``NAME.abi3.so``, or use setuptools version 26 or later.  Also, note
+that compiling with a debug version of Python will not actually define
+``Py_LIMITED_API``, as doing so makes ``Python.h`` unhappy.
+
+*New in version 1.12:* ``Py_LIMITED_API`` is now defined on Windows too.
+If you use ``virtualenv``, you need a recent version of it: versions
+older than 16.0.0 forgot to copy ``python3.dll`` into the virtual
+environment.  In case upgrading ``virtualenv`` is a real problem, you
+can manually edit the C code to remove the first line ``# define
+Py_LIMITED_API``.
+
+**ffibuilder.compile(tmpdir='.', verbose=False, debug=None):**
+explicitly generate the .py or .c file,
+and (if .c) compile it.  The output file is (or are) put in the
+directory given by ``tmpdir``.  In the examples given here, we use
+``if __name__ == "__main__": ffibuilder.compile()`` in the build scripts---if
+they are directly executed, this makes them rebuild the .py/.c file in
+the current directory.  (Note: if a package is specified in the call
+to ``set_source()``, then a corresponding subdirectory of the ``tmpdir``
+is used.)
+
+*New in version 1.4:* ``verbose`` argument.  If True, it prints the
+usual distutils output, including the command lines that call the
+compiler.  (This parameter might be changed to True by default in a
+future release.)
+
+*New in version 1.8.1:* ``debug`` argument.  If set to a bool, it
+controls whether the C code is compiled in debug mode or not.  The
+default None means to use the host Python's ``sys.flags.debug``.
+Starting with version 1.8.1, if you are running a debug-mode Python, the
+C code is thus compiled in debug mode by default (note that it is anyway
+necessary to do so on Windows).
+
+**ffibuilder.emit_python_code(filename):** generate the given .py file (same
+as ``ffibuilder.compile()`` for ABI mode, with an explicitly-named file to
+write).  If you choose, you can include this .py file pre-packaged in
+your own distributions: it is identical for any Python version (2 or
+3).
+
+**ffibuilder.emit_c_code(filename):** generate the given .c file (for API
+mode) without compiling it.  Can be used if you have some other method
+to compile it, e.g. if you want to integrate with some larger build
+system that will compile this file for you.  You can also distribute
+the .c file: unless the build script you used depends on the OS or
+platform, the .c file itself is generic (it would be exactly the same
+if produced on a different OS, with a different version of CPython, or
+with PyPy; it is done with generating the appropriate ``#ifdef``).
+
+**ffibuilder.distutils_extension(tmpdir='build', verbose=True):** for
+distutils-based ``setup.py`` files.  Calling this creates the .c file
+if needed in the given ``tmpdir``, and returns a
+``distutils.core.Extension`` instance.
+
+For Setuptools, you use instead the line
+``cffi_modules=["path/to/foo_build.py:ffibuilder"]`` in ``setup.py``.  This
+line asks Setuptools to import and use a helper provided by CFFI,
+which in turn executes the file ``path/to/foo_build.py`` (as with
+``execfile()``) and looks up its global variable called ``ffibuilder``.  You
+can also say ``cffi_modules=["path/to/foo_build.py:maker"]``, where
+``maker`` names a global function; it is called with no argument and
+is supposed to return a ``FFI`` object.
+
+
+ffi/ffibuilder.include(): combining multiple CFFI interfaces
+------------------------------------------------------------
+
+**ffi/ffibuilder.include(other_ffi)**: includes the typedefs, structs, unions,
+enums and constants defined in another FFI instance.  This is meant
+for large projects where one CFFI-based interface depends on some
+types declared in a different CFFI-based interface.
+
+*Note that you should only use one ffi object per library; the intended
+usage of ffi.include() is if you want to interface with several
+inter-dependent libraries.*  For only one library, make one ``ffi``
+object.  (You can write several ``cdef()`` calls over the same ``ffi``
+from several Python files, if one file would be too large.)
+
+For out-of-line modules, the ``ffibuilder.include(other_ffibuilder)``
+line should
+occur in the build script, and the ``other_ffibuilder`` argument should be
+another FFI instance that comes from another build script.  When the two build
+scripts are turned into generated files, say ``_ffi.so`` and
+``_other_ffi.so``, then importing ``_ffi.so`` will internally cause
+``_other_ffi.so`` to be imported.  At that point, the real
+declarations from ``_other_ffi.so`` are combined with the real
+declarations from ``_ffi.so``.
+
+The usage of ``ffi.include()`` is the cdef-level equivalent of a
+``#include`` in C, where a part of the program might include types and
+functions defined in another part for its own usage.  You can see on
+the ``ffi`` object (and associated ``lib`` objects on the *including*
+side) the types and constants declared on the included side.  In API
+mode, you can also see the functions and global variables directly.
+In ABI mode, these must be accessed via the original ``other_lib``
+object returned by the ``dlopen()`` method on ``other_ffi``.
+
+
+ffi.cdef() limitations
+----------------------
+
+All of the ANSI C *declarations* should be supported in ``cdef()``,
+and some of C99.  (This excludes any ``#include`` or ``#ifdef``.)
+Known missing features that are either in C99, or are GCC or MSVC
+extensions:
+
+* Any ``__attribute__`` or ``#pragma pack(n)``
+
+* Additional types: special-size floating and fixed
+  point types, vector types, and so on.
+
+* The C99 types ``float _Complex`` and ``double _Complex`` are supported
+  by cffi since version 1.11, but not libffi: you cannot call C
+  functions with complex arguments or return value, except if they are
+  directly API-mode functions.  The type ``long double _Complex`` is not
+  supported at all (declare and use it as if it were an array of two
+  ``long double``, and write wrapper functions in C with set_source()).
+
+* ``__restrict__`` or ``__restrict`` are extensions of, respectively,
+   GCC and MSVC.  They are not recognized.  But ``restrict`` is a C
+   keyword and is accepted (and ignored).
+
+Note that declarations like ``int field[];`` in
+structures are interpreted as variable-length structures.  Declarations
+like ``int field[...];`` on the other hand are arrays whose length is
+going to be completed by the compiler.  You can use ``int field[];``
+for array fields that are not, in fact, variable-length; it works too,
+but in this case, as CFFI
+believes it cannot ask the C compiler for the length of the array, you
+get reduced safety checks: for example, you risk overwriting the
+following fields by passing too many array items in the constructor.
+
+*New in version 1.2:*
+Thread-local variables (``__thread``) can be accessed, as well as
+variables defined as dynamic macros (``#define myvar  (*fetchme())``).
+Before version 1.2, you need to write getter/setter functions.
+
+Note that if you declare a variable in ``cdef()`` without using
+``const``, CFFI assumes it is a read-write variable and generates two
+pieces of code, one to read it and one to write it.  If the variable
+cannot in fact be written to in C code, for one reason or another, it
+will not compile.  In this case, you can declare it as a constant: for
+example, instead of ``foo_t *myglob;`` you would use ``foo_t *const
+myglob;``.  Note also that ``const foo_t *myglob;``  is a *variable;* it
+contains a variable pointer to a constant ``foo_t``.
+
+
+Debugging dlopen'ed C libraries
+-------------------------------
+
+A few C libraries are actually hard to use correctly in a ``dlopen()``
+setting.  This is because most C libraries are intended for, and tested
+with, a situation where they are *linked* with another program, using
+either static linking or dynamic linking --- but from a program written
+in C, at start-up, using the linker's capabilities instead of
+``dlopen()``.
+
+This can occasionally create issues.  You would have the same issues in
+another setting than CFFI, like with ``ctypes`` or even plain C code that
+calls ``dlopen()``.  This section contains a few generally useful
+environment variables (on Linux) that can help when debugging these
+issues.
+
+**export LD_TRACE_LOADED_OBJECTS=all**
+
+    provides a lot of information, sometimes too much depending on the
+    setting.  Output verbose debugging information about the dynamic
+    linker. If set to ``all`` prints all debugging information it has, if
+    set to ``help`` prints a help message about which categories can be
+    specified in this environment variable
+
+**export LD_VERBOSE=1**
+
+    (glibc since 2.1) If set to a nonempty string, output symbol
+    versioning information about the program if querying information
+    about the program (i.e., either ``LD_TRACE_LOADED_OBJECTS`` has been set,
+    or ``--list`` or ``--verify`` options have been given to the dynamic
+    linker).
+
+**export LD_WARN=1**
+
+    (ELF only)(glibc since 2.1.3) If set to a nonempty string, warn
+    about unresolved symbols.
+
+
+ffi.verify(): in-line API-mode
+------------------------------
+
+**ffi.verify()** is supported for backward compatibility, but is
+deprecated.  ``ffi.verify(c_header_source, tmpdir=.., ext_package=..,
+modulename=.., flags=.., **kwargs)`` makes and compiles a C file from
+the ``ffi.cdef()``, like ``ffi.set_source()`` in API mode, and then
+immediately loads and returns the dynamic library object.  Some
+non-trivial logic is used to decide if the dynamic library must be
+recompiled or not; see below for ways to control it.
+
+The ``c_header_source`` and the extra keyword arguments have the
+same meaning as in ``ffi.set_source()``.
+
+One remaining use case for ``ffi.verify()`` would be the following
+hack to find explicitly the size of any type, in bytes, and have it
+available in Python immediately (e.g. because it is needed in order to
+write the rest of the build script):
+
+.. code-block:: python
+
+    ffi = cffi.FFI()
+    ffi.cdef("const int mysize;")
+    lib = ffi.verify("const int mysize = sizeof(THE_TYPE);")
+    print lib.mysize
+
+Extra arguments to ``ffi.verify()``:
+    
+*  ``tmpdir`` controls where the C
+   files are created and compiled. Unless the ``CFFI_TMPDIR`` environment
+   variable is set, the default is
+   ``directory_containing_the_py_file/__pycache__`` using the
+   directory name of the .py file that contains the actual call to
+   ``ffi.verify()``.  (This is a bit of a hack but is generally
+   consistent with the location of the .pyc files for your library.
+   The name ``__pycache__`` itself comes from Python 3.)
+
+*  ``ext_package`` controls in which package the
+   compiled extension module should be looked from.  This is
+   only useful after distributing ffi.verify()-based modules.
+
+*  The ``tag`` argument gives an extra string inserted in the
+   middle of the extension module's name: ``_cffi_<tag>_<hash>``.
+   Useful to give a bit more context, e.g. when debugging.
+
+*  The ``modulename`` argument can be used to force a specific module
+   name, overriding the name ``_cffi_<tag>_<hash>``.  Use with care,
+   e.g. if you are passing variable information to ``verify()`` but
+   still want the module name to be always the same (e.g. absolute
+   paths to local files).  In this case, no hash is computed and if
+   the module name already exists it will be reused without further
+   check.  Be sure to have other means of clearing the ``tmpdir``
+   whenever you change your sources.
+
+* ``source_extension`` has the same meaning as in ``ffibuilder.set_source()``.
+
+*  The optional ``flags`` argument (ignored on Windows) defaults to
+   ``ffi.RTLD_NOW``; see ``man dlopen``.  (With
+   ``ffibuilder.set_source()``, you would use ``sys.setdlopenflags()``.)
+
+*  The optional ``relative_to`` argument is useful if you need to list
+   local files passed to the C compiler::
+
+     ext = ffi.verify(..., sources=['foo.c'], relative_to=__file__)
+
+   The line above is roughly the same as::
+
+     ext = ffi.verify(..., sources=['/path/to/this/file/foo.c'])
+
+   except that the default name of the produced library is built from
+   the CRC checkum of the argument ``sources``, as well as most other
+   arguments you give to ``ffi.verify()`` -- but not ``relative_to``.
+   So if you used the second line, it would stop finding the
+   already-compiled library after your project is installed, because
+   the ``'/path/to/this/file'`` suddenly changed.  The first line does
+   not have this problem.
+
+Note that during development, every time you change the C sources that
+you pass to ``cdef()`` or ``verify()``, then the latter will create a
+new module file name, based on two CRC32 hashes computed from these
+strings.  This creates more and more files in the ``__pycache__``
+directory.  It is recommended that you clean it up from time to time.
+A nice way to do that is to add, in your test suite, a call to
+``cffi.verifier.cleanup_tmpdir()``.  Alternatively, you can manually
+remove the whole ``__pycache__`` directory.
+
+An alternative cache directory can be given as the ``tmpdir`` argument
+to ``verify()``, via the environment variable ``CFFI_TMPDIR``, or by
+calling ``cffi.verifier.set_tmpdir(path)`` prior to calling
+``verify``.
+
+
+Upgrading from CFFI 0.9 to CFFI 1.0
+-----------------------------------
+
+CFFI 1.0 is backward-compatible, but it is still a good idea to
+consider moving to the out-of-line approach new in 1.0.  Here are the
+steps.
+
+**ABI mode** if your CFFI project uses ``ffi.dlopen()``:
+
+.. code-block:: python
+
+    import cffi
+
+    ffi = cffi.FFI()
+    ffi.cdef("stuff")
+    lib = ffi.dlopen("libpath")
+
+and *if* the "stuff" part is big enough that import time is a concern,
+then rewrite it as described in `the out-of-line but still ABI mode`__
+above.  Optionally, see also the `setuptools integration`__ paragraph.
+
+.. __: out-of-line-abi_
+.. __: distutils-setuptools_
+
+
+**API mode** if your CFFI project uses ``ffi.verify()``:
+
+.. code-block:: python
+
+    import cffi
+
+    ffi = cffi.FFI()
+    ffi.cdef("stuff")
+    lib = ffi.verify("real C code")
+
+then you should really rewrite it as described in `the out-of-line,
+API mode`__ above.  It avoids a number of issues that have caused
+``ffi.verify()`` to grow a number of extra arguments over time.  Then
+see the `distutils or setuptools`__ paragraph.  Also, remember to
+remove the ``ext_package=".."`` from your ``setup.py``, which was
+sometimes needed with ``verify()`` but is just creating confusion with
+``set_source()``.
+
+.. __: out-of-line-api_
+.. __: distutils-setuptools_
+
+The following example should work both with old (pre-1.0) and new
+versions of CFFI---supporting both is important to run on old
+versions of PyPy (CFFI 1.0 does not work in PyPy < 2.6):
+
+.. code-block:: python
+
+    # in a separate file "package/foo_build.py"
+    import cffi
+
+    ffi = cffi.FFI()
+    C_HEADER_SRC = r'''
+        #include "somelib.h"
+    '''
+    C_KEYWORDS = dict(libraries=['somelib'])
+
+    if hasattr(ffi, 'set_source'):
+        ffi.set_source("package._foo", C_HEADER_SRC, **C_KEYWORDS)
+
+    ffi.cdef('''
+        int foo(int);
+    ''')
+
+    if __name__ == "__main__":
+        ffi.compile()
+
+And in the main program:
+
+.. code-block:: python
+
+    try:
+        from package._foo import ffi, lib
+    except ImportError:
+        from package.foo_build import ffi, C_HEADER_SRC, C_KEYWORDS
+        lib = ffi.verify(C_HEADER_SRC, **C_KEYWORDS)
+
+(FWIW, this latest trick can be used more generally to allow the
+import to "work" even if the ``_foo`` module was not generated.)
+
+Writing a ``setup.py`` script that works both with CFFI 0.9 and 1.0
+requires explicitly checking the version of CFFI that we can have---it
+is hard-coded as a built-in module in PyPy:
+
+.. code-block:: python
+
+    if '_cffi_backend' in sys.builtin_module_names:   # PyPy
+        import _cffi_backend
+        requires_cffi = "cffi==" + _cffi_backend.__version__
+    else:
+        requires_cffi = "cffi>=1.0.0"
+
+Then we use the ``requires_cffi`` variable to give different arguments to
+``setup()`` as needed, e.g.:
+
+.. code-block:: python
+
+    if requires_cffi.startswith("cffi==0."):
+        # backward compatibility: we have "cffi==0.*"
+        from package.foo_build import ffi
+        extra_args = dict(
+            ext_modules=[ffi.verifier.get_extension()],
+            ext_package="...",    # if needed
+        )
+    else:
+        extra_args = dict(
+            setup_requires=[requires_cffi],
+            cffi_modules=['package/foo_build.py:ffi'],
+        )
+    setup(
+        name=...,
+        ...,
+        install_requires=[requires_cffi],
+        **extra_args
+    )
diff --git a/doc/source/conf.py b/doc/source/conf.py
new file mode 100644
index 0000000..3400cd1
--- /dev/null
+++ b/doc/source/conf.py
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+#
+# CFFI documentation build configuration file, created by
+# sphinx-quickstart on Thu Jun 14 16:37:47 2012.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'CFFI'
+copyright = u'2012-2018, Armin Rigo, Maciej Fijalkowski'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.12'
+# The full version, including alpha/beta/rc tags.
+release = '1.12.2'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+#html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'CFFIdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'CFFI.tex', u'CFFI Documentation',
+   u'Armin Rigo, Maciej Fijalkowski', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
diff --git a/doc/source/embedding.rst b/doc/source/embedding.rst
new file mode 100644
index 0000000..181249c
--- /dev/null
+++ b/doc/source/embedding.rst
@@ -0,0 +1,524 @@
+================================
+Using CFFI for embedding
+================================
+
+.. contents::
+
+You can use CFFI to generate C code which exports the API of your choice
+to any C application that wants to link with this C code.  This API,
+which you define yourself, ends up as the API of a ``.so/.dll/.dylib``
+library---or you can statically link it within a larger application.
+
+Possible use cases:
+
+* Exposing a library written in Python directly to C/C++ programs.
+
+* Using Python to make a "plug-in" for an existing C/C++ program that is
+  already written to load them.
+
+* Using Python to implement part of a larger C/C++ application (with
+  static linking).
+
+* Writing a small C/C++ wrapper around Python, hiding the fact that the
+  application is actually written in Python (to make a custom
+  command-line interface; for distribution purposes; or simply to make
+  it a bit harder to reverse-engineer the application).
+
+The general idea is as follows:
+
+* You write and execute a Python script, which produces a ``.c`` file
+  with the API of your choice (and optionally compile it into a
+  ``.so/.dll/.dylib``).  The script also gives some Python code to be
+  "frozen" inside the ``.so``.
+
+* At runtime, the C application loads this ``.so/.dll/.dylib`` (or is
+  statically linked with the ``.c`` source) without having to know that
+  it was produced from Python and CFFI.
+
+* The first time a C function is called, Python is initialized and
+  the frozen Python code is executed.
+
+* The frozen Python code defines more Python functions that implement the
+  C functions of your API, which are then used for all subsequent C
+  function calls.
+
+One of the goals of this approach is to be entirely independent from
+the CPython C API: no ``Py_Initialize()`` nor ``PyRun_SimpleString()``
+nor even ``PyObject``.  It works identically on CPython and PyPy.
+
+This is entirely *new in version 1.5.*  (PyPy contains CFFI 1.5 since
+release 5.0.)
+
+
+Usage
+-----
+
+.. __: overview.html#embedding
+
+See the `paragraph in the overview page`__ for a quick introduction.
+In this section, we explain every step in more details.  We will use
+here this slightly expanded example:
+
+.. code-block:: c
+
+    /* file plugin.h */
+    typedef struct { int x, y; } point_t;
+    extern int do_stuff(point_t *);
+
+.. code-block:: c
+
+    /* file plugin.h, Windows-friendly version */
+    typedef struct { int x, y; } point_t;
+
+    /* When including this file from ffibuilder.set_source(), the
+       following macro is defined to '__declspec(dllexport)'.  When
+       including this file directly from your C program, we define
+       it to 'extern __declspec(dllimport)' instead.
+
+       With non-MSVC compilers we simply define it to 'extern'.
+       (The 'extern' is needed for sharing global variables;
+       functions would be fine without it.  The macros always
+       include 'extern': you must not repeat it when using the
+       macros later.)
+    */
+    #ifndef CFFI_DLLEXPORT
+    #  if defined(_MSC_VER)
+    #    define CFFI_DLLEXPORT  extern __declspec(dllimport)
+    #  else
+    #    define CFFI_DLLEXPORT  extern
+    #  endif
+    #endif
+
+    CFFI_DLLEXPORT int do_stuff(point_t *);
+
+.. code-block:: python
+
+    # file plugin_build.py
+    import cffi
+    ffibuilder = cffi.FFI()
+
+    with open('plugin.h') as f:
+        # read plugin.h and pass it to embedding_api(), manually
+        # removing the '#' directives and the CFFI_DLLEXPORT
+        data = ''.join([line for line in f if not line.startswith('#')])
+        data = data.replace('CFFI_DLLEXPORT', '')
+        ffibuilder.embedding_api(data)
+
+    ffibuilder.set_source("my_plugin", r'''
+        #include "plugin.h"
+    ''')
+
+    ffibuilder.embedding_init_code("""
+        from my_plugin import ffi
+
+        @ffi.def_extern()
+        def do_stuff(p):
+            print("adding %d and %d" % (p.x, p.y))
+            return p.x + p.y
+    """)
+
+    ffibuilder.compile(target="plugin-1.5.*", verbose=True)
+    # or: ffibuilder.emit_c_code("my_plugin.c")
+
+Running the code above produces a *DLL*, i,e, a dynamically-loadable
+library.  It is a file with the extension ``.dll`` on Windows,
+``.dylib`` on Mac OS/X, or ``.so`` on other platforms.  As usual, it
+is produced by generating some intermediate ``.c`` code and then
+calling the regular platform-specific C compiler.  See below__ for
+some pointers to C-level issues with using the produced library.
+
+.. __: `Issues about using the .so`_
+
+Here are some details about the methods used above:
+
+* **ffibuilder.embedding_api(source):** parses the given C source, which
+  declares functions that you want to be exported by the DLL.  It can
+  also declare types, constants and global variables that are part of
+  the C-level API of your DLL.
+
+  The functions that are found in ``source`` will be automatically
+  defined in the ``.c`` file: they will contain code that initializes
+  the Python interpreter the first time any of them is called,
+  followed by code to call the attached Python function (with
+  ``@ffi.def_extern()``, see next point).
+
+  The global variables, on the other hand, are not automatically
+  produced.  You have to write their definition explicitly in
+  ``ffibuilder.set_source()``, as regular C code (see the point after next).
+
+* **ffibuilder.embedding_init_code(python_code):** this gives
+  initialization-time Python source code.  This code is copied
+  ("frozen") inside the DLL.  At runtime, the code is executed when
+  the DLL is first initialized, just after Python itself is
+  initialized.  This newly initialized Python interpreter has got an
+  extra "built-in" module that can be loaded magically without
+  accessing any files, with a line like "``from my_plugin import ffi,
+  lib``".  The name ``my_plugin`` comes from the first argument to
+  ``ffibuilder.set_source()``.  This module represents "the caller's C world"
+  from the point of view of Python.
+
+  The initialization-time Python code can import other modules or
+  packages as usual.  You may have typical Python issues like needing
+  to set up ``sys.path`` somehow manually first.
+
+  For every function declared within ``ffibuilder.embedding_api()``, the
+  initialization-time Python code or one of the modules it imports
+  should use the decorator ``@ffi.def_extern()`` to attach a
+  corresponding Python function to it.
+
+  If the initialization-time Python code fails with an exception, then
+  you get a traceback printed to stderr, along with more information
+  to help you identify problems like wrong ``sys.path``.  If some
+  function remains unattached at the time where the C code tries to
+  call it, an error message is also printed to stderr and the function
+  returns zero/null.
+
+  Note that the CFFI module never calls ``exit()``, but CPython itself
+  contains code that calls ``exit()``, for example if importing
+  ``site`` fails.  This may be worked around in the future.
+
+* **ffibuilder.set_source(c_module_name, c_code):** set the name of the
+  module from Python's point of view.  It also gives more C code which
+  will be included in the generated C code.  In trivial examples it
+  can be an empty string.  It is where you would ``#include`` some
+  other files, define global variables, and so on.  The macro
+  ``CFFI_DLLEXPORT`` is available to this C code: it expands to the
+  platform-specific way of saying "the following declaration should be
+  exported from the DLL".  For example, you would put "``extern int
+  my_glob;``" in ``ffibuilder.embedding_api()`` and "``CFFI_DLLEXPORT int
+  my_glob = 42;``" in ``ffibuilder.set_source()``.
+
+  Currently, any *type* declared in ``ffibuilder.embedding_api()`` must also
+  be present in the ``c_code``.  This is automatic if this code
+  contains a line like ``#include "plugin.h"`` in the example above.
+
+* **ffibuilder.compile([target=...] [, verbose=True]):** make the C code and
+  compile it.  By default, it produces a file called
+  ``c_module_name.dll``, ``c_module_name.dylib`` or
+  ``c_module_name.so``, but the default can be changed with the
+  optional ``target`` keyword argument.  You can use
+  ``target="foo.*"`` with a literal ``*`` to ask for a file called
+  ``foo.dll`` on Windows, ``foo.dylib`` on OS/X and ``foo.so``
+  elsewhere.  One reason for specifying an alternate ``target`` is to
+  include characters not usually allowed in Python module names, like
+  "``plugin-1.5.*``".
+
+  For more complicated cases, you can call instead
+  ``ffibuilder.emit_c_code("foo.c")`` and compile the resulting ``foo.c``
+  file using other means.  CFFI's compilation logic is based on the
+  standard library ``distutils`` package, which is really developed
+  and tested for the purpose of making CPython extension modules; it
+  might not always be appropriate for making general DLLs.  Also, just
+  getting the C code is what you need if you do not want to make a
+  stand-alone ``.so/.dll/.dylib`` file: this C file can be compiled
+  and statically linked as part of a larger application.
+
+
+More reading
+------------
+
+If you're reading this page about embedding and you are not familiar
+with CFFI already, here are a few pointers to what you could read
+next:
+
+* For the ``@ffi.def_extern()`` functions, integer C types are passed
+  simply as Python integers; and simple pointers-to-struct and basic
+  arrays are all straightforward enough.  However, sooner or later you
+  will need to read about this topic in more details here__.
+
+* ``@ffi.def_extern()``: see `documentation here,`__ notably on what
+  happens if the Python function raises an exception.
+
+* To create Python objects attached to C data, one common solution is
+  to use ``ffi.new_handle()``.  See documentation here__.
+
+* In embedding mode, the major direction is C code that calls Python
+  functions.  This is the opposite of the regular extending mode of
+  CFFI, in which the major direction is Python code calling C.  That's
+  why the page `Using the ffi/lib objects`_ talks first about the
+  latter, and why the direction "C code that calls Python" is
+  generally referred to as "callbacks" in that page.  If you also
+  need to have your Python code call C code, read more about
+  `Embedding and Extending`_ below.
+
+* ``ffibuilder.embedding_api(source)``: follows the same syntax as
+  ``ffibuilder.cdef()``, `documented here.`__  You can use the "``...``"
+  syntax as well, although in practice it may be less useful than it
+  is for ``cdef()``.  On the other hand, it is expected that often the
+  C sources that you need to give to ``ffibuilder.embedding_api()`` would be
+  exactly the same as the content of some ``.h`` file that you want to
+  give to users of your DLL.  That's why the example above does this::
+
+      with open('foo.h') as f:
+          ffibuilder.embedding_api(f.read())
+
+  Note that a drawback of this approach is that ``ffibuilder.embedding_api()``
+  doesn't support ``#ifdef`` directives.  You may have to use a more
+  convoluted expression like::
+
+      with open('foo.h') as f:
+          lines = [line for line in f if not line.startswith('#')]
+          ffibuilder.embedding_api(''.join(lines))
+
+  As in the example above, you can also use the same ``foo.h`` from
+  ``ffibuilder.set_source()``::
+
+      ffibuilder.set_source('module_name', r'''
+          #include "foo.h"
+      ''')
+
+
+.. __: using.html#working
+.. __: using.html#def-extern
+.. __: ref.html#ffi-new-handle
+.. __: cdef.html#cdef
+
+.. _`Using the ffi/lib objects`: using.html
+
+
+Troubleshooting
+---------------
+
+* The error message
+
+    cffi extension module 'c_module_name' has unknown version 0x2701
+
+  means that the running Python interpreter located a CFFI version older
+  than 1.5.  CFFI 1.5 or newer must be installed in the running Python.
+
+* On PyPy, the error message
+
+    debug: pypy_setup_home: directories 'lib-python' and 'lib_pypy' not
+    found in pypy's shared library location or in any parent directory
+
+  means that the ``libpypy-c.so`` file was found, but the standard library
+  was not found from this location.  This occurs at least on some Linux
+  distributions, because they put ``libpypy-c.so`` inside ``/usr/lib/``,
+  instead of the way we recommend, which is: keep that file inside
+  ``/opt/pypy/bin/`` and put a symlink to there from ``/usr/lib/``.
+  The quickest fix is to do that change manually.
+
+
+Issues about using the .so
+--------------------------
+
+This paragraph describes issues that are not necessarily specific to
+CFFI.  It assumes that you have obtained the ``.so/.dylib/.dll`` file as
+described above, but that you have troubles using it.  (In summary: it
+is a mess.  This is my own experience, slowly built by using Google and
+by listening to reports from various platforms.  Please report any
+inaccuracies in this paragraph or better ways to do things.)
+
+* The file produced by CFFI should follow this naming pattern:
+  ``libmy_plugin.so`` on Linux, ``libmy_plugin.dylib`` on Mac, or
+  ``my_plugin.dll`` on Windows (no ``lib`` prefix on Windows).
+
+* First note that this file does not contain the Python interpreter
+  nor the standard library of Python.  You still need it to be
+  somewhere.  There are ways to compact it to a smaller number of files,
+  but this is outside the scope of CFFI (please report if you used some
+  of these ways successfully so that I can add some links here).
+
+* In what we'll call the "main program", the ``.so`` can be either
+  used dynamically (e.g. by calling ``dlopen()`` or ``LoadLibrary()``
+  inside the main program), or at compile-time (e.g. by compiling it
+  with ``gcc -lmy_plugin``).  The former case is always used if you're
+  building a plugin for a program, and the program itself doesn't need
+  to be recompiled.  The latter case is for making a CFFI library that
+  is more tightly integrated inside the main program.
+
+* In the case of compile-time usage: you can add the gcc
+  option ``-Lsome/path/`` before ``-lmy_plugin`` to describe where the
+  ``libmy_plugin.so`` is.  On some platforms, notably Linux, ``gcc``
+  will complain if it can find ``libmy_plugin.so`` but not
+  ``libpython27.so`` or ``libpypy-c.so``.  To fix it, you need to call
+  ``LD_LIBRARY_PATH=/some/path/to/libpypy gcc``.
+
+* When actually executing the main program, it needs to find the
+  ``libmy_plugin.so`` but also ``libpython27.so`` or ``libpypy-c.so``.
+  For PyPy, unpack a PyPy distribution and you get a full directory
+  structure with ``libpypy-c.so`` inside a ``bin`` subdirectory, or on
+  Windows ``pypy-c.dll`` inside the top directory; you must not move
+  this file around, but just point to it.  One way to point to it is by
+  running the main program with some environment variable:
+  ``LD_LIBRARY_PATH=/some/path/to/libpypy`` on Linux,
+  ``DYLD_LIBRARY_PATH=/some/path/to/libpypy`` on OS/X.
+
+* You can avoid the ``LD_LIBRARY_PATH`` issue if you compile
+  ``libmy_plugin.so`` with the path hard-coded inside in the first
+  place.  On Linux, this is done by ``gcc -Wl,-rpath=/some/path``.  You
+  would put this option in ``ffibuilder.set_source("my_plugin", ...,
+  extra_link_args=['-Wl,-rpath=/some/path/to/libpypy'])``.  The path can
+  start with ``$ORIGIN`` to mean "the directory where
+  ``libmy_plugin.so`` is".  You can then specify a path relative to that
+  place, like ``extra_link_args=['-Wl,-rpath=$ORIGIN/../venv/bin']``.
+  Use ``ldd libmy_plugin.so`` to look at what path is currently compiled
+  in after the expansion of ``$ORIGIN``.)
+
+  After this, you don't need ``LD_LIBRARY_PATH`` any more to locate
+  ``libpython27.so`` or ``libpypy-c.so`` at runtime.  In theory it
+  should also cover the call to ``gcc`` for the main program.  I wasn't
+  able to make ``gcc`` happy without ``LD_LIBRARY_PATH`` on Linux if
+  the rpath starts with ``$ORIGIN``, though.
+
+* The same rpath trick might be used to let the main program find
+  ``libmy_plugin.so`` in the first place without ``LD_LIBRARY_PATH``.
+  (This doesn't apply if the main program uses ``dlopen()`` to load it
+  as a dynamic plugin.)  You'd make the main program with ``gcc
+  -Wl,-rpath=/path/to/libmyplugin``, possibly with ``$ORIGIN``.  The
+  ``$`` in ``$ORIGIN`` causes various shell problems on its own: if
+  using a common shell you need to say ``gcc
+  -Wl,-rpath=\$ORIGIN``.  From a Makefile, you need to say
+  something like ``gcc -Wl,-rpath=\$$ORIGIN``.
+
+* On some Linux distributions, notably Debian, the ``.so`` files of
+  CPython C extension modules may be compiled without saying that they
+  depend on ``libpythonX.Y.so``.  This makes such Python systems
+  unsuitable for embedding if the embedder uses ``dlopen(...,
+  RTLD_LOCAL)``.  You get an ``undefined symbol`` error.  See
+  `issue #264`__.  A workaround is to first call
+  ``dlopen("libpythonX.Y.so", RTLD_LAZY|RTLD_GLOBAL)``, which will
+  force ``libpythonX.Y.so`` to be loaded first.
+
+.. __: https://bitbucket.org/cffi/cffi/issues/264/
+
+
+Using multiple CFFI-made DLLs
+-----------------------------
+
+Multiple CFFI-made DLLs can be used by the same process.
+
+Note that all CFFI-made DLLs in a process share a single Python
+interpreter.  The effect is the same as the one you get by trying to
+build a large Python application by assembling a lot of unrelated
+packages.  Some of these might be libraries that monkey-patch some
+functions from the standard library, for example, which might be
+unexpected from other parts.
+
+
+Multithreading
+--------------
+
+Multithreading should work transparently, based on Python's standard
+Global Interpreter Lock.
+
+If two threads both try to call a C function when Python is not yet
+initialized, then locking occurs.  One thread proceeds with
+initialization and blocks the other thread.  The other thread will be
+allowed to continue only when the execution of the initialization-time
+Python code is done.
+
+If the two threads call two *different* CFFI-made DLLs, the Python
+initialization itself will still be serialized, but the two pieces of
+initialization-time Python code will not.  The idea is that there is a
+priori no reason for one DLL to wait for initialization of the other
+DLL to be complete.
+
+After initialization, Python's standard Global Interpreter Lock kicks
+in.  The end result is that when one CPU progresses on executing
+Python code, no other CPU can progress on executing more Python code
+from another thread of the same process.  At regular intervals, the
+lock switches to a different thread, so that no single thread should
+appear to block indefinitely.
+
+
+Testing
+-------
+
+For testing purposes, a CFFI-made DLL can be imported in a running
+Python interpreter instead of being loaded like a C shared library.
+
+You might have some issues with the file name: for example, on
+Windows, Python expects the file to be called ``c_module_name.pyd``,
+but the CFFI-made DLL is called ``target.dll`` instead.  The base name
+``target`` is the one specified in ``ffibuilder.compile()``, and on Windows
+the extension is ``.dll`` instead of ``.pyd``.  You have to rename or
+copy the file, or on POSIX use a symlink.
+
+The module then works like a regular CFFI extension module.  It is
+imported with "``from c_module_name import ffi, lib``" and exposes on
+the ``lib`` object all C functions.  You can test it by calling these
+C functions.  The initialization-time Python code frozen inside the
+DLL is executed the first time such a call is done.
+
+
+Embedding and Extending
+-----------------------
+
+The embedding mode is not incompatible with the non-embedding mode of
+CFFI.
+
+You can use *both* ``ffibuilder.embedding_api()`` and
+``ffibuilder.cdef()`` in the
+same build script.  You put in the former the declarations you want to
+be exported by the DLL; you put in the latter only the C functions and
+types that you want to share between C and Python, but not export from
+the DLL.
+
+As an example of that, consider the case where you would like to have
+a DLL-exported C function written in C directly, maybe to handle some
+cases before calling Python functions.  To do that, you must *not* put
+the function's signature in ``ffibuilder.embedding_api()``.  (Note that this
+requires more hacks if you use ``ffibuilder.embedding_api(f.read())``.)
+You must only write the custom function definition in
+``ffibuilder.set_source()``, and prefix it with the macro CFFI_DLLEXPORT:
+
+.. code-block:: c
+
+    CFFI_DLLEXPORT int myfunc(int a, int b)
+    {
+        /* implementation here */
+    }
+
+This function can, if it wants, invoke Python functions using the
+general mechanism of "callbacks"---called this way because it is a
+call from C to Python, although in this case it is not calling
+anything back:
+
+.. code-block:: python
+
+    ffibuilder.cdef("""
+        extern "Python" int mycb(int);
+    """)
+
+    ffibuilder.set_source("my_plugin", r"""
+
+        static int mycb(int);   /* the callback: forward declaration, to make
+                                   it accessible from the C code that follows */
+
+        CFFI_DLLEXPORT int myfunc(int a, int b)
+        {
+            int product = a * b;   /* some custom C code */
+            return mycb(product);
+        }
+    """)
+
+and then the Python initialization code needs to contain the lines:
+
+.. code-block:: python
+
+    @ffi.def_extern()
+    def mycb(x):
+        print "hi, I'm called with x =", x
+        return x * 10
+
+This ``@ffi.def_extern`` is attaching a Python function to the C
+callback ``mycb()``, which in this case is not exported from the DLL.
+Nevertheless, the automatic initialization of Python occurs when
+``mycb()`` is called, if it happens to be the first function called
+from C.  More precisely, it does not happen when ``myfunc()`` is
+called: this is just a C function, with no extra code magically
+inserted around it.  It only happens when ``myfunc()`` calls
+``mycb()``.
+
+As the above explanation hints, this is how ``ffibuilder.embedding_api()``
+actually implements function calls that directly invoke Python code;
+here, we have merely decomposed it explicitly, in order to add some
+custom C code in the middle.
+
+In case you need to force, from C code, Python to be initialized
+before the first ``@ffi.def_extern()`` is called, you can do so by
+calling the C function ``cffi_start_python()`` with no argument.  It
+returns an integer, 0 or -1, to tell if the initialization succeeded
+or not.  Currently there is no way to prevent a failing initialization
+from also dumping a traceback and more information to stderr.
diff --git a/doc/source/goals.rst b/doc/source/goals.rst
new file mode 100644
index 0000000..0fda659
--- /dev/null
+++ b/doc/source/goals.rst
@@ -0,0 +1,69 @@
+Goals
+-----
+
+The interface is based on `LuaJIT's FFI`_, and follows a few principles:
+
+* The goal is to call C code from Python without learning a 3rd language:
+  existing alternatives require users to learn domain specific language
+  (Cython_, SWIG_) or API (ctypes_). The CFFI design requires users to know
+  only C and Python, minimizing the extra bits of API that need to be learned.
+
+* Keep all the Python-related logic in Python so that you don't need to
+  write much C code (unlike `CPython native C extensions`_).
+
+* The preferred way is to work at the level of the API (Application
+  Programming Interface): the C compiler is called from the declarations
+  you write to validate and link to the C language constructs.
+  Alternatively, it is also possible to work at the ABI level
+  (Application Binary Interface), the way ctypes_ work.
+  However, on non-Windows platforms, C libraries typically
+  have a specified C API but not an ABI (e.g. they may
+  document a "struct" as having at least these fields, but maybe more).
+
+* Try to be complete.  For now some C99 constructs are not supported,
+  but all C89 should be, including macros (and including macro "abuses",
+  which you can `manually wrap`_ in saner-looking C functions).
+
+* Attempt to support both PyPy and CPython, with a reasonable path
+  for other Python implementations like IronPython and Jython.
+
+* Note that this project is **not** about embedding executable C code in
+  Python, unlike `Weave`_.  This is about calling existing C libraries
+  from Python.
+
+* There is no C++ support.  Sometimes, it is reasonable to write a C
+  wrapper around the C++ code and then call this C API with CFFI.
+  Otherwise, look at other projects.  I would recommend cppyy_, which
+  has got some similarities (and also works efficiently on both CPython
+  and PyPy).
+
+.. _`LuaJIT's FFI`: http://luajit.org/ext_ffi.html
+.. _`Cython`: http://www.cython.org
+.. _`SWIG`: http://www.swig.org/
+.. _`CPython native C extensions`: http://docs.python.org/extending/extending.html
+.. _`native C extensions`: http://docs.python.org/extending/extending.html
+.. _`ctypes`: http://docs.python.org/library/ctypes.html
+.. _`Weave`: http://wiki.scipy.org/Weave
+.. _`cppyy`: http://cppyy.readthedocs.io/en/latest/
+.. _`manually wrap`: overview.html#abi-versus-api
+
+Get started by reading `the overview`__.
+
+.. __: overview.html
+
+
+Comments and bugs
+-----------------
+
+The best way to contact us is on the IRC ``#pypy`` channel of
+``irc.freenode.net``.  Feel free to discuss matters either there or in
+the `mailing list`_.  Please report to the `issue tracker`_ any bugs.
+
+As a general rule, when there is a design issue to resolve, we pick the
+solution that is the "most C-like".  We hope that this module has got
+everything you need to access C code and nothing more.
+
+--- the authors, Armin Rigo and Maciej Fijalkowski
+
+.. _`issue tracker`: https://bitbucket.org/cffi/cffi/issues
+.. _`mailing list`: https://groups.google.com/forum/#!forum/python-cffi
diff --git a/doc/source/index.rst b/doc/source/index.rst
new file mode 100644
index 0000000..1126318
--- /dev/null
+++ b/doc/source/index.rst
@@ -0,0 +1,22 @@
+================================
+CFFI documentation
+================================
+
+C Foreign Function Interface for Python.  Interact with almost any C
+code from Python, based on C-like declarations that you can often
+copy-paste from header files or documentation.
+
+.. toctree::
+   :maxdepth: 2
+
+   goals
+   whatsnew
+   installation
+   overview
+   using
+   ref
+   cdef
+   embedding
+
+
+
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
new file mode 100644
index 0000000..4af17d5
--- /dev/null
+++ b/doc/source/installation.rst
@@ -0,0 +1,206 @@
+=======================================================
+Installation and Status
+=======================================================
+
+Quick installation for CPython (cffi is distributed with PyPy):
+
+* ``pip install cffi``
+
+* or get the source code via the `Python Package Index`__.
+
+.. __: http://pypi.python.org/pypi/cffi
+
+In more details:
+
+This code has been developed on Linux, but should work on any POSIX
+platform as well as on Windows 32 and 64.  (It relies occasionally on
+libffi, so it depends on libffi being bug-free; this may not be fully
+the case on some of the more exotic platforms.)
+
+CFFI supports CPython 2.6, 2.7, 3.x (tested with 3.2 to 3.4); and is
+distributed with PyPy (CFFI 1.0 is distributed with and requires
+PyPy 2.6).
+
+The core speed of CFFI is better than ctypes, with import times being
+either lower if you use the post-1.0 features, or much higher if you
+don't.  The wrapper Python code you typically need to write around the
+raw CFFI interface slows things down on CPython, but not unreasonably
+so.  On PyPy, this wrapper code has a minimal impact thanks to the JIT
+compiler.  This makes CFFI the recommended way to interface with C
+libraries on PyPy.
+
+Requirements:
+
+* CPython 2.6 or 2.7 or 3.x, or PyPy (PyPy 2.0 for the earliest
+  versions of CFFI; or PyPy 2.6 for CFFI 1.0).
+
+* in some cases you need to be able to compile C extension modules.
+  On non-Windows platforms, this usually means installing the package
+  ``python-dev``.  Refer to the appropriate docs for your OS.
+
+* on CPython, on non-Windows platforms, you also need to install
+  ``libffi-dev`` in order to compile CFFI itself.
+
+* pycparser >= 2.06: https://github.com/eliben/pycparser (automatically
+  tracked by ``pip install cffi``).
+
+* `py.test`_ is needed to run the tests of CFFI itself.
+
+.. _`py.test`: http://pypi.python.org/pypi/pytest
+
+Download and Installation:
+
+* https://pypi.python.org/pypi/cffi
+
+* Checksums of the "source" package version 1.12.2:
+
+   - MD5: 4d7dcb6c7c738c15d2ece9bd4c5f86da
+
+   - SHA: 5f579d4980cbcc8aac592721f714ef6a64370ab1
+
+   - SHA256: e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7
+
+* Or grab the most current version from the `Bitbucket page`_:
+  ``hg clone https://bitbucket.org/cffi/cffi``
+
+* ``python setup.py install`` or ``python setup_base.py install``
+  (should work out of the box on Linux or Windows; see below for
+  `MacOS X`_ or `Windows 64`_.)
+
+* running the tests: ``py.test  c/  testing/`` (if you didn't
+  install cffi yet, you need first ``python setup_base.py build_ext -f
+  -i``)
+
+.. _`Bitbucket page`: https://bitbucket.org/cffi/cffi
+
+Demos:
+
+* The `demo`_ directory contains a number of small and large demos
+  of using ``cffi``.
+
+* The documentation below might be sketchy on details; for now the
+  ultimate reference is given by the tests, notably
+  `testing/cffi1/test_verify1.py`_ and `testing/cffi0/backend_tests.py`_.
+
+.. _`demo`: https://bitbucket.org/cffi/cffi/src/default/demo
+.. _`testing/cffi1/test_verify1.py`: https://bitbucket.org/cffi/cffi/src/default/testing/cffi1/test_verify1.py
+.. _`testing/cffi0/backend_tests.py`: https://bitbucket.org/cffi/cffi/src/default/testing/cffi0/backend_tests.py
+
+
+Platform-specific instructions
+------------------------------
+
+``libffi`` is notoriously messy to install and use --- to the point that
+CPython includes its own copy to avoid relying on external packages.
+CFFI does the same for Windows, but not for other platforms (which should
+have their own working libffi's).
+Modern Linuxes work out of the box thanks to ``pkg-config``.  Here are some
+(user-supplied) instructions for other platforms.
+
+
+MacOS X
++++++++
+
+**Homebrew** (Thanks David Griffin for this)
+
+1) Install homebrew: http://brew.sh
+
+2) Run the following commands in a terminal
+
+::
+
+    brew install pkg-config libffi
+    PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig pip install cffi
+
+
+Alternatively, **on OS/X 10.6** (Thanks Juraj Sukop for this)
+
+For building libffi you can use the default install path, but then, in
+``setup.py`` you need to change::
+
+    include_dirs = []
+
+to::
+
+    include_dirs = ['/usr/local/lib/libffi-3.0.11/include']
+
+Then running ``python setup.py build`` complains about "fatal error: error writing to -: Broken pipe", which can be fixed by running::
+
+    ARCHFLAGS="-arch i386 -arch x86_64" python setup.py build
+
+as described here_.
+
+.. _here: http://superuser.com/questions/259278/python-2-6-1-pycrypto-2-3-pypi-package-broken-pipe-during-build
+
+
+Windows (regular 32-bit)
+++++++++++++++++++++++++
+
+Win32 works and is tested at least each official release.
+
+The recommended C compiler compatible with Python 2.7 is this one:
+http://www.microsoft.com/en-us/download/details.aspx?id=44266
+There is a known problem with distutils on Python 2.7, as 
+explained in https://bugs.python.org/issue23246, and the same 
+problem applies whenever you want to run compile() to build a dll with
+this specific compiler suite download. 
+``import setuptools`` might help, but YMMV
+
+For Python 3.4 and beyond:
+https://www.visualstudio.com/en-us/downloads/visual-studio-2015-ctp-vs
+
+
+Windows 64
+++++++++++
+
+Win64 received very basic testing and we applied a few essential
+fixes in cffi 0.7. The comment above applies for Python 2.7 on 
+Windows 64 as well. Please report any other issue.
+
+Note as usual that this is only about running the 64-bit version of
+Python on the 64-bit OS.  If you're running the 32-bit version (the
+common case apparently), then you're running Win32 as far as we're
+concerned.
+
+.. _`issue 9`: https://bitbucket.org/cffi/cffi/issue/9
+.. _`Python issue 7546`: http://bugs.python.org/issue7546
+
+
+Linux and OS/X: UCS2 versus UCS4
+++++++++++++++++++++++++++++++++
+
+This is about getting an ImportError about ``_cffi_backend.so`` with a
+message like ``Symbol not found: _PyUnicodeUCS2_AsASCIIString``.  This
+error occurs in Python 2 as soon as you mix "ucs2" and "ucs4" builds of
+Python.  It means that you are now running a Python compiled with
+"ucs4", but the extension module ``_cffi_backend.so`` was compiled by a
+different Python: one that was running "ucs2".  (If the opposite problem
+occurs, you get an error about ``_PyUnicodeUCS4_AsASCIIString``
+instead.)
+
+If you are using ``pyenv``, then see
+https://github.com/yyuu/pyenv/issues/257.
+
+More generally, the solution that should always work is to download the
+sources of CFFI (instead of a prebuilt binary) and make sure that you
+build it with the same version of Python than the one that will use it.
+For example, with virtualenv:
+
+* ``virtualenv ~/venv``
+
+* ``cd ~/path/to/sources/of/cffi``
+
+* ``~/venv/bin/python setup.py build --force`` # forcing a rebuild to
+  make sure
+
+* ``~/venv/bin/python setup.py install``
+
+This will compile and install CFFI in this virtualenv, using the
+Python from this virtualenv.
+
+
+NetBSD
+++++++
+
+You need to make sure you have an up-to-date version of libffi, which
+fixes some bugs.
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
new file mode 100644
index 0000000..bcc5663
--- /dev/null
+++ b/doc/source/overview.rst
@@ -0,0 +1,651 @@
+=======================================================
+Overview
+=======================================================
+
+.. contents::
+   
+
+The first section presents a simple working
+example of using CFFI to call a C function in a compiled shared object
+(DLL) from Python. CFFI is
+flexible and covers several other use cases presented in the second
+section. The third section shows how to export Python functions
+to a Python interpreter embedded in a C or C++ application. The last
+two sections delve deeper in the CFFI library.
+
+Make sure you have `cffi installed`__.
+
+.. __: installation.html
+
+.. _out-of-line-api-level:
+.. _real-example:
+
+
+Main mode of usage
+------------------
+
+The main way to use CFFI is as an interface to some already-compiled
+shared object which is provided by other means.  Imagine that you have a
+system-installed shared object called ``piapprox.dll`` (Windows) or
+``libpiapprox.so`` (Linux and others) or ``libpiapprox.dylib`` (OS X),
+exporting a function ``float pi_approx(int n);`` that computes some
+approximation of pi given a number of iterations. You want to call
+this function from Python. Note this method works equally well with a
+static library ``piapprox.lib`` (Windows) or ``libpiapprox.a``.
+
+Create the file ``piapprox_build.py``:
+
+.. code-block:: python
+
+      from cffi import FFI
+      ffibuilder = FFI()
+
+      # cdef() expects a single string declaring the C types, functions and
+      # globals needed to use the shared object. It must be in valid C syntax.
+      ffibuilder.cdef("""
+          float pi_approx(int n);
+      """)
+
+      # set_source() gives the name of the python extension module to
+      # produce, and some C source code as a string.  This C code needs
+      # to make the declarated functions, types and globals available,
+      # so it is often just the "#include".
+      ffibuilder.set_source("_pi_cffi",
+      """
+      	   #include "pi.h"   // the C header of the library
+      """,
+      	   libraries=['piapprox'])   # library name, for the linker
+
+      if __name__ == "__main__":
+          ffibuilder.compile(verbose=True)
+
+Execute this script.  If everything is OK, it should produce
+``_pi_cffi.c``, and then invoke the compiler on it.  The produced
+``_pi_cffi.c`` contains a copy of the string given in ``set_source()``,
+in this example the ``#include "pi.h"``. Afterwards, it contains glue code
+for all the functions, types and globals declared in the ``cdef()`` above.
+
+At runtime, you use the extension module like this:
+
+.. code-block:: python
+
+    from _pi_cffi import ffi, lib
+    print(lib.pi_approx(5000))
+
+That's all!  In the rest of this page, we describe some more advanced
+examples and other CFFI modes.  In particular, there is a complete
+example `if you don't have an already-installed C library to call`_.
+
+For more information about the ``cdef()`` and ``set_source()`` methods
+of the ``FFI`` class, see `Preparing and Distributing modules`__.
+
+.. __: cdef.html
+
+When your example works, a common alternative to running the build
+script manually is to have it run as part of a ``setup.py``.  Here is
+an example using the Setuptools distribution:
+
+.. code-block:: python
+
+    from setuptools import setup
+
+    setup(
+        ...
+        setup_requires=["cffi>=1.0.0"],
+        cffi_modules=["piapprox_build:ffibuilder"], # "filename:global"
+        install_requires=["cffi>=1.0.0"],
+    )
+
+
+Other CFFI modes
+----------------
+
+CFFI can be used in one of four modes: "ABI" versus "API" level,
+each with "in-line" or "out-of-line" preparation (or compilation).
+
+The **ABI mode** accesses libraries at the binary level, whereas the
+faster **API mode** accesses them with a C compiler.  We explain the
+difference in more details below__.
+
+.. __: `abi-versus-api`_
+
+In the **in-line mode,** everything is set up every time you import
+your Python code.  In the **out-of-line mode,** you have a separate
+step of preparation (and possibly C compilation) that produces a
+module which your main program can then import.
+
+
+Simple example (ABI level, in-line)
++++++++++++++++++++++++++++++++++++
+
+May look familiar to those who have used ctypes_.
+
+.. code-block:: python
+
+    >>> from cffi import FFI
+    >>> ffi = FFI()
+    >>> ffi.cdef("""
+    ...     int printf(const char *format, ...);   // copy-pasted from the man page
+    ... """)                                  
+    >>> C = ffi.dlopen(None)                     # loads the entire C namespace
+    >>> arg = ffi.new("char[]", b"world")        # equivalent to C code: char arg[] = "world";
+    >>> C.printf(b"hi there, %s.\n", arg)        # call printf
+    hi there, world.
+    17                                           # this is the return value
+    >>>
+
+Note that ``char *`` arguments expect a ``bytes`` object.  If you have a
+``str`` (or a ``unicode`` on Python 2) you need to encode it explicitly
+with ``somestring.encode(myencoding)``.
+
+*Python 3 on Windows:* ``ffi.dlopen(None)`` does not work.  This problem
+is messy and not really fixable.  The problem does not occur if you try
+to call a function from a specific DLL that exists on your system: then
+you use ``ffi.dlopen("path.dll")``.
+
+*This example does not call any C compiler.  It works in the so-called
+ABI mode, which means that it will crash if you call some function or
+access some fields of a structure that was slightly misdeclared in the
+cdef().*
+
+If using a C compiler to install your module is an option, it is highly
+recommended to use the API mode instead.  (It is also faster.)
+
+
+Struct/Array Example (minimal, in-line)
++++++++++++++++++++++++++++++++++++++++
+
+.. code-block:: python
+
+    from cffi import FFI
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct {
+            unsigned char r, g, b;
+        } pixel_t;
+    """)
+    image = ffi.new("pixel_t[]", 800*600)
+
+    f = open('data', 'rb')     # binary mode -- important
+    f.readinto(ffi.buffer(image))
+    f.close()
+
+    image[100].r = 255
+    image[100].g = 192
+    image[100].b = 128
+
+    f = open('data', 'wb')
+    f.write(ffi.buffer(image))
+    f.close()
+
+This can be used as a more flexible replacement of the struct_ and
+array_ modules, and replaces ctypes_.  You could also call ``ffi.new("pixel_t[600][800]")``
+and get a two-dimensional array.
+
+.. _struct: http://docs.python.org/library/struct.html
+.. _array: http://docs.python.org/library/array.html
+.. _ctypes: http://docs.python.org/library/ctypes.html
+
+*This example does not call any C compiler.*
+
+This example also admits an out-of-line equivalent.  It is similar to
+the first example `Main mode of usage`_ above,
+but passing ``None`` as the second argument to
+``ffibuilder.set_source()``.  Then in the main program you write
+``from _simple_example import ffi`` and then the same content as the
+in-line example above starting from the line ``image =
+ffi.new("pixel_t[]", 800*600)``.
+
+
+API Mode, calling the C standard library
+++++++++++++++++++++++++++++++++++++++++
+
+.. code-block:: python
+
+    # file "example_build.py"
+
+    # Note: we instantiate the same 'cffi.FFI' class as in the previous
+    # example, but call the result 'ffibuilder' now instead of 'ffi';
+    # this is to avoid confusion with the other 'ffi' object you get below
+
+    from cffi import FFI
+    ffibuilder = FFI()
+
+    ffibuilder.set_source("_example",
+       r""" // passed to the real C compiler,
+            // contains implementation of things declared in cdef()
+            #include <sys/types.h>
+            #include <pwd.h>
+
+            // We can also define custom wrappers or other functions
+            // here (this is an example only):
+            static struct passwd *get_pw_for_root(void) {
+                return getpwuid(0);
+            }
+        """,
+        libraries=[])   # or a list of libraries to link with
+        # (more arguments like setup.py's Extension class:
+        # include_dirs=[..], extra_objects=[..], and so on)
+
+    ffibuilder.cdef("""
+        // declarations that are shared between Python and C
+        struct passwd {
+            char *pw_name;
+            ...;     // literally dot-dot-dot
+        };
+        struct passwd *getpwuid(int uid);     // defined in <pwd.h>
+        struct passwd *get_pw_for_root(void); // defined in set_source()
+    """)
+
+    if __name__ == "__main__":
+        ffibuilder.compile(verbose=True)
+
+You need to run the ``example_build.py`` script once to generate
+"source code" into the file ``_example.c`` and compile this to a
+regular C extension module.  (CFFI selects either Python or C for the
+module to generate based on whether the second argument to
+``set_source()`` is ``None`` or not.)
+
+*You need a C compiler for this single step.  It produces a file called
+e.g. _example.so or _example.pyd.  If needed, it can be distributed in
+precompiled form like any other extension module.*
+
+Then, in your main program, you use:
+
+.. code-block:: python
+
+    from _example import ffi, lib
+
+    p = lib.getpwuid(0)
+    assert ffi.string(p.pw_name) == b'root'
+    p = lib.get_pw_for_root()
+    assert ffi.string(p.pw_name) == b'root'
+
+Note that this works independently of the exact C layout of ``struct
+passwd`` (it is "API level", as opposed to "ABI level").  It requires
+a C compiler in order to run ``example_build.py``, but it is much more
+portable than trying to get the details of the fields of ``struct
+passwd`` exactly right.  Similarly, in the ``cdef()`` we declared
+``getpwuid()`` as taking an ``int`` argument; on some platforms this
+might be slightly incorrect---but it does not matter.
+
+Note also that at runtime, the API mode is faster than the ABI mode.
+
+To integrate it inside a ``setup.py`` distribution with Setuptools:
+
+.. code-block:: python
+
+    from setuptools import setup
+
+    setup(
+        ...
+        setup_requires=["cffi>=1.0.0"],
+        cffi_modules=["example_build.py:ffibuilder"],
+        install_requires=["cffi>=1.0.0"],
+    )
+
+
+.. _`if you don't have an already-installed C library to call`:
+
+API Mode, calling C sources instead of a compiled library
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+If you want to call some library that is not precompiled, but for which
+you have C sources, then the easiest solution is to make a single
+extension module that is compiled from both the C sources of this
+library, and the additional CFFI wrappers.  For example, say you start
+with the files ``pi.c`` and ``pi.h``:
+
+   .. code-block:: C
+
+      /* filename: pi.c*/
+      # include <stdlib.h>
+      # include <math.h>
+       
+      /* Returns a very crude approximation of Pi
+         given a int: a number of iteration */
+      float pi_approx(int n){
+      
+        double i,x,y,sum=0;
+      
+        for(i=0;i<n;i++){
+      
+          x=rand();
+          y=rand();
+      
+          if (sqrt(x*x+y*y) < sqrt((double)RAND_MAX*RAND_MAX))
+            sum++; }
+      
+        return 4*(float)sum/(float)n; }
+
+   .. code-block:: C
+
+      /* filename: pi.h*/
+      float pi_approx(int n);
+      
+Create a script named ``pi_extension_build.py``, building
+the C extension:
+
+   .. code-block:: python
+
+      from cffi import FFI
+      ffibuilder = FFI()
+      
+      ffibuilder.cdef("float pi_approx(int n);")
+   
+      ffibuilder.set_source("_pi",  # name of the output C extension
+      """
+          #include "pi.h"',
+      """,
+          sources=['pi.c'],   # includes pi.c as additional sources
+          libraries=['m'])    # on Unix, link with the math library
+   
+      if __name__ == "__main__":
+          ffibuilder.compile(verbose=True)
+
+Build the extension:
+   
+   .. code-block:: shell
+
+      python pi_extension_build.py
+
+Observe, in the working directory, the generated output files:
+``_pi.c``, ``_pi.o`` and the compiled C extension (called ``_pi.so`` on
+Linux for example).  It can be called from Python:
+
+   .. code-block:: python
+   
+       from _pi.lib import pi_approx
+   
+       approx = pi_approx(10)
+       assert str(pi_approximation).startswith("3.")
+   
+       approx = pi_approx(10000)
+       assert str(approx).startswith("3.1")  
+
+
+.. _performance:
+
+Purely for performance (API level, out-of-line)
++++++++++++++++++++++++++++++++++++++++++++++++
+
+A variant of the `section above`__ where the goal is not to call an
+existing C library, but to compile and call some C function written
+directly in the build script:
+
+.. __: real-example_
+
+.. code-block:: python
+
+    # file "example_build.py"
+
+    from cffi import FFI
+    ffibuilder = FFI()
+
+    ffibuilder.cdef("int foo(int *, int *, int);")
+
+    ffibuilder.set_source("_example",
+    r"""
+        static int foo(int *buffer_in, int *buffer_out, int x)
+        {
+            /* some algorithm that is seriously faster in C than in Python */
+        }
+    """)
+
+    if __name__ == "__main__":
+        ffibuilder.compile(verbose=True)
+
+.. code-block:: python
+
+    # file "example.py"
+
+    from _example import ffi, lib
+
+    buffer_in = ffi.new("int[]", 1000)
+    # initialize buffer_in here...
+
+    # easier to do all buffer allocations in Python and pass them to C,
+    # even for output-only arguments
+    buffer_out = ffi.new("int[]", 1000)
+
+    result = lib.foo(buffer_in, buffer_out, 1000)
+
+*You need a C compiler to run example_build.py, once.  It produces a
+file called e.g. _example.so or _example.pyd.  If needed, it can be
+distributed in precompiled form like any other extension module.*
+
+
+.. _out-of-line-abi-level:
+
+Out-of-line, ABI level
+++++++++++++++++++++++
+
+The out-of-line ABI mode is a mixture of the regular (API) out-of-line
+mode and the in-line ABI mode.  It lets you use the ABI mode, with its
+advantages (not requiring a C compiler) and problems (crashes more
+easily).
+
+This mixture mode lets you massively reduces the import times, because
+it is slow to parse a large C header.  It also allows you to do more
+detailed checkings during build-time without worrying about performance
+(e.g. calling ``cdef()`` many times with small pieces of declarations,
+based on the version of libraries detected on the system).
+
+.. code-block:: python
+
+    # file "simple_example_build.py"
+
+    from cffi import FFI
+
+    ffibuilder = FFI()
+    # Note that the actual source is None
+    ffibuilder.set_source("_simple_example", None)
+    ffibuilder.cdef("""
+        int printf(const char *format, ...);
+    """)
+
+    if __name__ == "__main__":
+        ffibuilder.compile(verbose=True)
+
+Running it once produces ``_simple_example.py``.  Your main program
+only imports this generated module, not ``simple_example_build.py``
+any more:
+
+.. code-block:: python
+
+    from _simple_example import ffi
+
+    lib = ffi.dlopen(None)      # Unix: open the standard C library
+    #import ctypes.util         # or, try this on Windows:
+    #lib = ffi.dlopen(ctypes.util.find_library("c"))
+
+    lib.printf(b"hi there, number %d\n", ffi.cast("int", 2))
+
+Note that this ``ffi.dlopen()``, unlike the one from in-line mode,
+does not invoke any additional magic to locate the library: it must be
+a path name (with or without a directory), as required by the C
+``dlopen()`` or ``LoadLibrary()`` functions.  This means that
+``ffi.dlopen("libfoo.so")`` is ok, but ``ffi.dlopen("foo")`` is not.
+In the latter case, you could replace it with
+``ffi.dlopen(ctypes.util.find_library("foo"))``.  Also, None is only
+recognized on Unix to open the standard C library.
+
+For distribution purposes, remember that there is a new
+``_simple_example.py`` file generated.  You can either include it
+statically within your project's source files, or, with Setuptools,
+you can say in the ``setup.py``:
+
+.. code-block:: python
+
+    from setuptools import setup
+
+    setup(
+        ...
+        setup_requires=["cffi>=1.0.0"],
+        cffi_modules=["simple_example_build.py:ffibuilder"],
+        install_requires=["cffi>=1.0.0"],
+    )
+
+In summary, this mode is useful when you wish to declare many C structures but
+do not need fast interaction with a shared object. It is useful for parsing
+binary files, for instance.
+
+
+In-line, API level
+++++++++++++++++++
+
+The "API level + in-line" mode combination exists but is long
+deprecated.  It used to be done with ``lib = ffi.verify("C header")``.
+The out-of-line variant with ``set_source("modname", "C header")`` is
+preferred and avoids a number of problems when the project grows in
+size.
+
+
+.. _embedding:
+
+Embedding
+---------
+
+*New in version 1.5.*
+
+CFFI can be used for embedding__: creating a standard
+dynamically-linked library (``.dll`` under Windows, ``.so`` elsewhere)
+which can be used from a C application.
+
+.. code-block:: python
+
+    import cffi
+    ffibuilder = cffi.FFI()
+
+    ffibuilder.embedding_api("""
+        int do_stuff(int, int);
+    """)
+
+    ffibuilder.set_source("my_plugin", "")
+
+    ffibuilder.embedding_init_code("""
+        from my_plugin import ffi
+
+        @ffi.def_extern()
+        def do_stuff(x, y):
+            print("adding %d and %d" % (x, y))
+            return x + y
+    """)
+
+    ffibuilder.compile(target="plugin-1.5.*", verbose=True)
+
+This simple example creates ``plugin-1.5.dll`` or ``plugin-1.5.so`` as
+a DLL with a single exported function, ``do_stuff()``.  You execute
+the script above once, with the interpreter you want to have
+internally used; it can be CPython 2.x or 3.x or PyPy.  This DLL can
+then be used "as usual" from an application; the application doesn't
+need to know that it is talking with a library made with Python and
+CFFI.  At runtime, when the application calls ``int do_stuff(int,
+int)``, the Python interpreter is automatically initialized and ``def
+do_stuff(x, y):`` gets called.  `See the details in the documentation
+about embedding.`__
+
+.. __: embedding.html
+.. __: embedding.html
+
+
+What actually happened?
+-----------------------
+
+The CFFI interface operates on the same level as C - you declare types
+and functions using the same syntax as you would define them in C.  This
+means that most of the documentation or examples can be copied straight
+from the man pages.
+
+The declarations can contain **types, functions, constants**
+and **global variables.** What you pass to the ``cdef()`` must not
+contain more than that; in particular, ``#ifdef`` or ``#include``
+directives are not supported.  The cdef in the above examples are just
+that - they declared "there is a function in the C level with this
+given signature", or "there is a struct type with this shape".
+
+In the ABI examples, the ``dlopen()`` calls load libraries manually.
+At the binary level, a program is split into multiple namespaces---a
+global one (on some platforms), plus one namespace per library.  So
+``dlopen()`` returns a ``<FFILibrary>`` object, and this object has
+got as attributes all function, constant and variable symbols that are
+coming from this library and that have been declared in the
+``cdef()``.  If you have several interdependent libraries to load,
+you would call ``cdef()`` only once but ``dlopen()`` several times.
+
+By opposition, the API mode works more closely like a C program: the C
+linker (static or dynamic) is responsible for finding any symbol used.
+You name the libraries in the ``libraries`` keyword argument to
+``set_source()``, but never need to say which symbol comes
+from which library.
+Other common arguments to ``set_source()`` include ``library_dirs`` and
+``include_dirs``; all these arguments are passed to the standard
+distutils/setuptools.
+
+The ``ffi.new()`` lines allocate C objects.  They are filled
+with zeroes initially, unless the optional second argument is used.
+If specified, this argument gives an "initializer", like you can use
+with C code to initialize global variables.
+
+The actual ``lib.*()`` function calls should be obvious: it's like C.
+
+
+.. _abi-versus-api:
+
+ABI versus API
+--------------
+
+Accessing the C library at the binary level ("ABI") is fraught
+with problems, particularly on non-Windows platforms.
+
+The most immediate drawback of the ABI level is that calling functions
+needs to go through the very general *libffi* library, which is slow
+(and not always perfectly tested on non-standard platforms).  The API
+mode instead compiles a CPython C wrapper that directly invokes the
+target function.  It can be massively faster (and works
+better than libffi ever will).
+
+The more fundamental reason to prefer the API mode is that *the C
+libraries are typically meant to be used with a C compiler.* You are not
+supposed to do things like guess where fields are in the structures.
+The "real example" above shows how CFFI uses a C compiler under the
+hood: this example uses ``set_source(..., "C source...")`` and never
+``dlopen()``.  When using this approach,
+we have the advantage that we can use literally "``...``" at various places in
+the ``cdef()``, and the missing information will be completed with the
+help of the C compiler.  CFFI will turn this into a single C source file,
+which contains the "C source" part unmodified, followed by some
+"magic" C code and declarations derived from the ``cdef()``.  When
+this C file is compiled, the resulting C extension module will contain
+all the information we need---or the C compiler will give warnings or
+errors, as usual e.g. if we misdeclare some function's signature.
+
+Note that the "C source" part from ``set_source()`` can contain
+arbitrary C code.  You can use this to declare some
+more helper functions written in C.  To export
+these helpers to Python, put their signature in the ``cdef()`` too.
+(You can use the ``static`` C keyword in the "C source" part,
+as in ``static int myhelper(int x) { return x * 42; }``,
+because these helpers are only
+referenced from the "magic" C code that is generated afterwards in the
+same C file.)
+
+This can be used for example to wrap "crazy" macros into more standard
+C functions.  The extra layer of C can be useful for other reasons
+too, like calling functions that expect some complicated argument
+structures that you prefer to build in C rather than in Python.  (On
+the other hand, if all you need is to call "function-like" macros,
+then you can directly declare them in the ``cdef()`` as if they were
+functions.)
+
+The generated piece of C code should be the same independently on the
+platform on which you run it (or the Python version), so in simple cases
+you can directly distribute the pre-generated C code and treat it as a
+regular C extension module (which depends on the ``_cffi_backend``
+module, on CPython).  The special Setuptools lines in the `example
+above`__ are meant for the more complicated cases where we need to
+regenerate the C sources as well---e.g. because the Python script that
+regenerates this file will itself look around the system to know what it
+should include or not.
+
+.. __: real-example_
diff --git a/doc/source/ref.rst b/doc/source/ref.rst
new file mode 100644
index 0000000..3dc8e4b
--- /dev/null
+++ b/doc/source/ref.rst
@@ -0,0 +1,1007 @@
+================================
+CFFI Reference
+================================
+
+.. contents::
+
+
+FFI Interface
+-------------
+
+*This page documents the runtime interface of the two types "FFI" and
+"CompiledFFI".  These two types are very similar to each other.  You get
+a CompiledFFI object if you import an out-of-line module.  You get a FFI
+object from explicitly writing cffi.FFI().  Unlike CompiledFFI, the type
+FFI has also got additional methods documented on the* `next page`__.
+
+.. __: cdef.html
+
+
+ffi.NULL
+++++++++
+
+**ffi.NULL**: a constant NULL of type ``<cdata 'void *'>``.
+
+
+ffi.error
++++++++++
+
+**ffi.error**: the Python exception raised in various cases.  (Don't
+confuse it with ``ffi.errno``.)
+
+
+ffi.new()
++++++++++
+
+**ffi.new(cdecl, init=None)**:
+allocate an instance according to the specified C type and return a
+pointer to it.  The specified C type must be either a pointer or an
+array: ``new('X *')`` allocates an X and returns a pointer to it,
+whereas ``new('X[n]')`` allocates an array of n X'es and returns an
+array referencing it (which works mostly like a pointer, like in C).
+You can also use ``new('X[]', n)`` to allocate an array of a
+non-constant length n.  See the `detailed documentation`__ for other
+valid initializers.
+
+.. __: using.html#working
+
+When the returned ``<cdata>`` object goes out of scope, the memory is
+freed.  In other words the returned ``<cdata>`` object has ownership of
+the value of type ``cdecl`` that it points to.  This means that the raw
+data can be used as long as this object is kept alive, but must not be
+used for a longer time.  Be careful about that when copying the
+pointer to the memory somewhere else, e.g. into another structure.
+Also, this means that a line like ``x = ffi.new(...)[0]`` is *always
+wrong:* the newly allocated object goes out of scope instantly, and so
+is freed immediately, and ``x`` is garbage.
+
+The returned memory is initially cleared (filled with zeroes), before
+the optional initializer is applied.  For performance, see
+`ffi.new_allocator()`_ for a way to allocate non-zero-initialized
+memory.
+
+*New in version 1.12:* see also ``ffi.release()``.
+
+
+ffi.cast()
+++++++++++
+
+**ffi.cast("C type", value)**: similar to a C cast: returns an
+instance of the named C type initialized with the given value.  The
+value is casted between integers or pointers of any type.
+
+
+.. _ffi-errno:
+.. _ffi-getwinerror:
+
+ffi.errno, ffi.getwinerror()
+++++++++++++++++++++++++++++
+
+**ffi.errno**: the value of ``errno`` received from the most recent C call
+in this thread, and passed to the following C call.  (This is a thread-local
+read-write property.)
+
+**ffi.getwinerror(code=-1)**: on Windows, in addition to ``errno`` we
+also save and restore the ``GetLastError()`` value across function
+calls.  This function returns this error code as a tuple ``(code,
+message)``, adding a readable message like Python does when raising
+WindowsError.  If the argument ``code`` is given, format that code into
+a message instead of using ``GetLastError()``.
+(Note that it is also possible to declare and call the ``GetLastError()``
+function as usual.)
+
+
+.. _ffi-string:
+.. _ffi-unpack:
+
+ffi.string(), ffi.unpack()
+++++++++++++++++++++++++++
+
+**ffi.string(cdata, [maxlen])**: return a Python string (or unicode
+string) from the 'cdata'.
+
+- If 'cdata' is a pointer or array of characters or bytes, returns the
+  null-terminated string.  The returned string extends until the first
+  null character.  The 'maxlen' argument limits how far we look for a
+  null character.  If 'cdata' is an
+  array then 'maxlen' defaults to its length.  See ``ffi.unpack()`` below
+  for a way to continue past the first null character.  *Python 3:* this
+  returns a ``bytes``, not a ``str``.
+
+- If 'cdata' is a pointer or array of wchar_t, returns a unicode string
+  following the same rules.  *New in version 1.11:* can also be
+  char16_t or char32_t.
+
+- If 'cdata' is a single character or byte or a wchar_t or charN_t,
+  returns it as a byte string or unicode string.  (Note that in some
+  situation a single wchar_t or char32_t may require a Python unicode
+  string of length 2.)
+
+- If 'cdata' is an enum, returns the value of the enumerator as a string.
+  If the value is out of range, it is simply returned as the stringified
+  integer.
+
+**ffi.unpack(cdata, length)**: unpacks an array of C data of the given
+length, returning a Python string/unicode/list.  The 'cdata' should be
+a pointer; if it is an array it is first converted to the pointer
+type.  *New in version 1.6.*
+
+- If 'cdata' is a pointer to 'char', returns a byte string.  It does
+  not stop at the first null.  (An equivalent way to do that is
+  ``ffi.buffer(cdata, length)[:]``.)
+
+- If 'cdata' is a pointer to 'wchar_t', returns a unicode string.
+  ('length' is measured in number of wchar_t; it is not the size in
+  bytes.)  *New in version 1.11:* can also be char16_t or char32_t.
+
+- If 'cdata' is a pointer to anything else, returns a list, of the
+  given 'length'.  (A slower way to do that is ``[cdata[i] for i in
+  range(length)]``.)
+
+
+.. _ffi-buffer:
+.. _ffi-from-buffer:
+
+ffi.buffer(), ffi.from_buffer()
++++++++++++++++++++++++++++++++
+
+**ffi.buffer(cdata, [size])**: return a buffer object that references
+the raw C data pointed to by the given 'cdata', of 'size' bytes.  What
+Python calls "a buffer", or more precisely "an object supporting the
+buffer interface", is an object that represents some raw memory and
+that can be passed around to various built-in or extension functions;
+these built-in functions read from or write to the raw memory directly,
+without needing an extra copy.
+
+The 'cdata' argument
+must be a pointer or an array.  If unspecified, the size of the
+buffer is either the size of what ``cdata`` points to, or the whole size
+of the array.
+
+Here are a few examples of where buffer() would be useful:
+
+-  use ``file.write()`` and ``file.readinto()`` with
+   such a buffer (for files opened in binary mode)
+
+-  overwrite the content of a struct: if ``p`` is a cdata pointing to
+   it, use ``ffi.buffer(p)[:] = newcontent``, where ``newcontent`` is
+   a bytes object (``str`` in Python 2).
+
+Remember that like in C, you can use ``array + index`` to get the pointer
+to the index'th item of an array.  (In C you might more naturally write
+``&array[index]``, but that is equivalent.)
+
+The returned object's type is not the builtin ``buffer`` nor ``memoryview``
+types, because these types' API changes too much across Python versions.
+Instead it has the following Python API (a subset of Python 2's ``buffer``)
+in addition to supporting the buffer interface:
+
+- ``buf[:]`` or ``bytes(buf)``: copy data out of the buffer, returning a
+  regular byte string (or ``buf[start:end]`` for a part)
+
+- ``buf[:] = newstr``: copy data into the buffer (or ``buf[start:end]
+  = newstr``)
+
+- ``len(buf)``, ``buf[index]``, ``buf[index] = newchar``: access as a sequence
+  of characters.
+
+The buffer object returned by ``ffi.buffer(cdata)`` keeps alive the
+``cdata`` object: if it was originally an owning cdata, then its
+owned memory will not be freed as long as the buffer is alive.
+
+Python 2/3 compatibility note: you should avoid using ``str(buf)``,
+because it gives inconsistent results between Python 2 and Python 3.
+(This is similar to how ``str()`` gives inconsistent results on regular
+byte strings).  Use ``buf[:]`` instead.
+
+*New in version 1.10:* ``ffi.buffer`` is now the type of the returned
+buffer objects; ``ffi.buffer()`` actually calls the constructor.
+
+**ffi.from_buffer([cdecl,] python_buffer, require_writable=False)**:
+return an array cdata (by default a ``<cdata 'char[]'>``) that
+points to the data of the given Python object, which must support the
+buffer interface.  Note that ``ffi.from_buffer()`` turns a generic
+Python buffer object into a cdata object, whereas ``ffi.buffer()`` does
+the opposite conversion.  Both calls don't actually copy any data.
+
+``ffi.from_buffer()`` is meant to be used on objects
+containing large quantities of raw data, like bytearrays
+or ``array.array`` or numpy
+arrays.  It supports both the old *buffer* API (in Python 2.x) and the
+new *memoryview* API.  Note that if you pass a read-only buffer object,
+you still get a regular ``<cdata 'char[]'>``; it is your responsibility
+not to write there if the original buffer doesn't expect you to.
+*In particular, never modify byte strings!*
+
+The original object is kept alive (and, in case
+of memoryview, locked) as long as the cdata object returned by
+``ffi.from_buffer()`` is alive.
+
+A common use case is calling a C function with some ``char *`` that
+points to the internal buffer of a Python object; for this case you
+can directly pass ``ffi.from_buffer(python_buffer)`` as argument to
+the call.
+
+*New in version 1.10:* the ``python_buffer`` can be anything supporting
+the buffer/memoryview interface (except unicode strings).  Previously,
+bytearray objects were supported in version 1.7 onwards (careful, if you
+resize the bytearray, the ``<cdata>`` object will point to freed
+memory); and byte strings were supported in version 1.8 onwards.
+
+*New in version 1.12:* added the optional *first* argument ``cdecl``, and
+the keyword argument ``require_writable``:
+
+* ``cdecl`` defaults to ``"char[]"``, but a different array type can be
+  specified for the result.  A value like ``"int[]"`` will return an array of
+  ints instead of chars, and its length will be set to the number of ints
+  that fit in the buffer (rounded down if the division is not exact).  Values
+  like ``"int[42]"`` or ``"int[2][3]"`` will return an array of exactly 42
+  (resp. 2-by-3) ints, raising a ValueError if the buffer is too small.  The
+  difference between specifying ``"int[]"`` and using the older code ``p1 =
+  ffi.from_buffer(x); p2 = ffi.cast("int *", p1)`` is that the older code
+  needs to keep ``p1`` alive as long as ``p2`` is in use, because only ``p1``
+  keeps the underlying Python object alive and locked.  (In addition,
+  ``ffi.from_buffer("int[]", x)`` gives better array bound checking.)
+
+* if ``require_writable`` is set to True, the function fails if the buffer
+  obtained from ``python_buffer`` is read-only (e.g. if ``python_buffer`` is
+  a byte string).  The exact exception is raised by the object itself, and
+  for things like bytes it varies with the Python version, so don't rely on
+  it.  (Before version 1.12, the same effect can be achieved with a hack:
+  call ``ffi.memmove(python_buffer, b"", 0)``.  This has no effect if the
+  object is writable, but fails if it is read-only.)  Please keep in mind
+  that CFFI does not implement the C keyword ``const``: even if you set
+  ``require_writable`` to False explicitly, you still get a regular
+  read-write cdata pointer.
+
+*New in version 1.12:* see also ``ffi.release()``.
+
+
+ffi.memmove()
++++++++++++++
+
+**ffi.memmove(dest, src, n)**: copy ``n`` bytes from memory area
+``src`` to memory area ``dest``.  See examples below.  Inspired by the
+C functions ``memcpy()`` and ``memmove()``---like the latter, the
+areas can overlap.  Each of ``dest`` and ``src`` can be either a cdata
+pointer or a Python object supporting the buffer/memoryview interface.
+In the case of ``dest``, the buffer/memoryview must be writable.
+*New in version 1.3.*  Examples:
+
+* ``ffi.memmove(myptr, b"hello", 5)`` copies the 5 bytes of
+  ``b"hello"`` to the area that ``myptr`` points to.
+
+* ``ba = bytearray(100); ffi.memmove(ba, myptr, 100)`` copies 100
+  bytes from ``myptr`` into the bytearray ``ba``.
+
+* ``ffi.memmove(myptr + 1, myptr, 100)`` shifts 100 bytes from
+  the memory at ``myptr`` to the memory at ``myptr + 1``.
+
+In versions before 1.10, ``ffi.from_buffer()`` had restrictions on the
+type of buffer, which made ``ffi.memmove()`` more general.
+
+.. _ffi-typeof:
+.. _ffi-sizeof:
+.. _ffi-alignof:
+
+ffi.typeof(), ffi.sizeof(), ffi.alignof()
++++++++++++++++++++++++++++++++++++++++++
+
+**ffi.typeof("C type" or cdata object)**: return an object of type
+``<ctype>`` corresponding to the parsed string, or to the C type of the
+cdata instance.  Usually you don't need to call this function or to
+explicitly manipulate ``<ctype>`` objects in your code: any place that
+accepts a C type can receive either a string or a pre-parsed ``ctype``
+object (and because of caching of the string, there is no real
+performance difference).  It can still be useful in writing typechecks,
+e.g.:
+
+.. code-block:: python
+  
+    def myfunction(ptr):
+        assert ffi.typeof(ptr) is ffi.typeof("foo_t*")
+        ...
+
+Note also that the mapping from strings like ``"foo_t*"`` to the
+``<ctype>`` objects is stored in some internal dictionary.  This
+guarantees that there is only one ``<ctype 'foo_t *'>`` object, so you
+can use the ``is`` operator to compare it.  The downside is that the
+dictionary entries are immortal for now.  In the future, we may add
+transparent reclamation of old, unused entries.  In the meantime, note
+that using strings like ``"int[%d]" % length`` to name a type will
+create many immortal cached entries if called with many different
+lengths.
+
+**ffi.sizeof("C type" or cdata object)**: return the size of the
+argument in bytes.  The argument can be either a C type, or a cdata object,
+like in the equivalent ``sizeof`` operator in C.
+
+For ``array = ffi.new("T[]", n)``, then ``ffi.sizeof(array)`` returns
+``n * ffi.sizeof("T")``.  *New in version 1.9:* Similar rules apply for
+structures with a variable-sized array at the end.  More precisely, if
+``p`` was returned by ``ffi.new("struct foo *", ...)``, then
+``ffi.sizeof(p[0])`` now returns the total allocated size.  In previous
+versions, it used to just return ``ffi.sizeof(ffi.typeof(p[0]))``, which
+is the size of the structure ignoring the variable-sized part.  (Note
+that due to alignment, it is possible for ``ffi.sizeof(p[0])`` to return
+a value smaller than ``ffi.sizeof(ffi.typeof(p[0]))``.)
+
+**ffi.alignof("C type")**: return the natural alignment size in bytes of
+the argument.  Corresponds to the ``__alignof__`` operator in GCC.
+
+
+.. _ffi-offsetof:
+.. _ffi-addressof:
+
+ffi.offsetof(), ffi.addressof()
++++++++++++++++++++++++++++++++
+
+**ffi.offsetof("C struct or array type", \*fields_or_indexes)**: return the
+offset within the struct of the given field.  Corresponds to ``offsetof()``
+in C.
+
+You can give several field names in case of nested structures.  You
+can also give numeric values which correspond to array items, in case
+of a pointer or array type.  For example, ``ffi.offsetof("int[5]", 2)``
+is equal to the size of two integers, as is ``ffi.offsetof("int *", 2)``.
+
+
+**ffi.addressof(cdata, \*fields_or_indexes)**: limited equivalent to
+the '&' operator in C:
+
+1. ``ffi.addressof(<cdata 'struct-or-union'>)`` returns a cdata that
+is a pointer to this struct or union.  The returned pointer is only
+valid as long as the original ``cdata`` object is; be sure to keep it
+alive if it was obtained directly from ``ffi.new()``.
+
+2. ``ffi.addressof(<cdata>, field-or-index...)`` returns the address
+of a field or array item inside the given structure or array.  In case
+of nested structures or arrays, you can give more than one field or
+index to look recursively.  Note that ``ffi.addressof(array, index)``
+can also be expressed as ``array + index``: this is true both in CFFI
+and in C, where ``&array[index]`` is just ``array + index``.
+
+3. ``ffi.addressof(<library>, "name")`` returns the address of the
+named function or global variable from the given library object.
+For functions, it returns a regular cdata
+object containing a pointer to the function.
+
+Note that the case 1. cannot be used to take the address of a
+primitive or pointer, but only a struct or union.  It would be
+difficult to implement because only structs and unions are internally
+stored as an indirect pointer to the data.  If you need a C int whose
+address can be taken, use ``ffi.new("int[1]")`` in the first place;
+similarly, for a pointer, use ``ffi.new("foo_t *[1]")``.
+
+
+.. _ffi-cdata:
+.. _ffi-ctype:
+
+ffi.CData, ffi.CType
+++++++++++++++++++++
+
+**ffi.CData, ffi.CType**: the Python type of the objects referred to
+as ``<cdata>`` and ``<ctype>`` in the rest of this document.  Note
+that some cdata objects may be actually of a subclass of
+``ffi.CData``, and similarly with ctype, so you should check with
+``if isinstance(x, ffi.CData)``.  Also, ``<ctype>`` objects have
+a number of attributes for introspection: ``kind`` and ``cname`` are
+always present, and depending on the kind they may also have
+``item``, ``length``, ``fields``, ``args``, ``result``, ``ellipsis``,
+``abi``, ``elements`` and ``relements``.
+
+*New in version 1.10:* ``ffi.buffer`` is now `a type`__ as well.
+
+.. __: #ffi-buffer
+
+
+.. _ffi-gc:
+
+ffi.gc()
+++++++++
+
+**ffi.gc(cdata, destructor, size=0)**:
+return a new cdata object that points to the
+same data.  Later, when this new cdata object is garbage-collected,
+``destructor(old_cdata_object)`` will be called.  Example of usage:
+``ptr = ffi.gc(lib.custom_malloc(42), lib.custom_free)``.
+Note that like objects
+returned by ``ffi.new()``, the returned pointer objects have *ownership*,
+which means the destructor is called as soon as *this* exact returned
+object is garbage-collected.
+
+*New in version 1.12:* see also ``ffi.release()``.
+
+**ffi.gc(ptr, None, size=0)**:
+removes the ownership on a object returned by a
+regular call to ``ffi.gc``, and no destructor will be called when it
+is garbage-collected.  The object is modified in-place, and the
+function returns ``None``.  *New in version 1.7: ffi.gc(ptr, None)*
+
+Note that ``ffi.gc()`` should be avoided for limited resources, or (with
+cffi below 1.11) for large memory allocations.  This is particularly
+true on PyPy: its GC does not know how much memory or how many resources
+the returned ``ptr`` holds.  It will only run its GC when enough memory
+it knows about has been allocated (and thus run the destructor possibly
+later than you would expect).  Moreover, the destructor is called in
+whatever thread PyPy is at that moment, which might be a problem for
+some C libraries.  In these cases, consider writing a wrapper class with
+custom ``__enter__()`` and ``__exit__()`` methods, allocating and
+freeing the C data at known points in time, and using it in a ``with``
+statement.  In cffi 1.12, see also ``ffi.release()``.
+
+*New in version 1.11:* the ``size`` argument.  If given, this should be
+an estimate of the size (in bytes) that ``ptr`` keeps alive.  This
+information is passed on to the garbage collector, fixing part of the
+problem described above.  The ``size`` argument is most important on
+PyPy; on CPython, it is ignored so far, but in the future it could be
+used to trigger more eagerly the cyclic reference GC, too (see CPython
+`issue 31105`__).
+
+The form ``ffi.gc(ptr, None, size=0)`` can be called with a negative
+``size``, to cancel the estimate.  It is not mandatory, though:
+nothing gets out of sync if the size estimates do not match.  It only
+makes the next GC start more or less early.
+
+Note that if you have several ``ffi.gc()`` objects, the corresponding
+destructors will be called in a random order.  If you need a particular
+order, see the discussion in `issue 340`__.
+
+.. __: http://bugs.python.org/issue31105
+.. __: https://bitbucket.org/cffi/cffi/issues/340/resources-release-issues
+
+
+.. _ffi-new-handle:
+.. _ffi-from-handle:
+
+ffi.new_handle(), ffi.from_handle()
++++++++++++++++++++++++++++++++++++
+
+**ffi.new_handle(python_object)**: return a non-NULL cdata of type
+``void *`` that contains an opaque reference to ``python_object``.  You
+can pass it around to C functions or store it into C structures.  Later,
+you can use **ffi.from_handle(p)** to retrieve the original
+``python_object`` from a value with the same ``void *`` pointer.
+*Calling ffi.from_handle(p) is invalid and will likely crash if
+the cdata object returned by new_handle() is not kept alive!*
+
+See a `typical usage example`_ below.
+
+(In case you are wondering, this ``void *`` is not the ``PyObject *``
+pointer.  This wouldn't make sense on PyPy anyway.)
+
+The ``ffi.new_handle()/from_handle()`` functions *conceptually* work
+like this:
+
+* ``new_handle()`` returns cdata objects that contains references to
+  the Python objects; we call them collectively the "handle" cdata
+  objects.  The ``void *`` value in these handle cdata objects are
+  random but unique.
+
+* ``from_handle(p)`` searches all live "handle" cdata objects for the
+  one that has the same value ``p`` as its ``void *`` value.  It then
+  returns the Python object referenced by that handle cdata object.
+  If none is found, you get "undefined behavior" (i.e. crashes).
+
+The "handle" cdata object keeps the Python object alive, similar to
+how ``ffi.new()`` returns a cdata object that keeps a piece of memory
+alive.  If the handle cdata object *itself* is not alive any more,
+then the association ``void * -> python_object`` is dead and
+``from_handle()`` will crash.
+
+*New in version 1.4:* two calls to ``new_handle(x)`` are guaranteed to
+return cdata objects with different ``void *`` values, even with the
+same ``x``.  This is a useful feature that avoids issues with unexpected
+duplicates in the following trick: if you need to keep alive the
+"handle" until explicitly asked to free it, but don't have a natural
+Python-side place to attach it to, then the easiest is to ``add()`` it
+to a global set.  It can later be removed from the set by
+``global_set.discard(p)``, with ``p`` any cdata object whose ``void *``
+value compares equal.
+
+.. _`typical usage example`:
+
+Usage example: suppose you have a C library where you must call a
+``lib.process_document()`` function which invokes some callback.  The
+``process_document()`` function receives a pointer to a callback and a
+``void *`` argument.  The callback is then invoked with the ``void
+*data`` argument that is equal to the provided value.  In this typical
+case, you can implement it like this (out-of-line API mode)::
+
+    class MyDocument:
+        ...
+
+        def process(self):
+            h = ffi.new_handle(self)
+            lib.process_document(lib.my_callback,   # the callback
+                                 h,                 # 'void *data'
+                                 args...)
+            # 'h' stays alive until here, which means that the
+            # ffi.from_handle() done in my_callback() during
+            # the call to process_document() is safe
+
+        def callback(self, arg1, arg2):
+            ...
+
+    # the actual callback is this one-liner global function:
+    @ffi.def_extern()
+    def my_callback(arg1, arg2, data):
+        return ffi.from_handle(data).callback(arg1, arg2)
+
+
+.. _ffi-dlopen:
+.. _ffi-dlclose:
+
+ffi.dlopen(), ffi.dlclose()
++++++++++++++++++++++++++++
+
+**ffi.dlopen(libpath, [flags])**: opens and returns a "handle" to a
+dynamic library, as a ``<lib>`` object.  See `Preparing and
+Distributing modules`_.
+
+**ffi.dlclose(lib)**: explicitly closes a ``<lib>`` object returned
+by ``ffi.dlopen()``.
+
+**ffi.RLTD_...**: constants: flags for ``ffi.dlopen()``.
+
+
+ffi.new_allocator()
++++++++++++++++++++
+
+**ffi.new_allocator(alloc=None, free=None, should_clear_after_alloc=True)**:
+returns a new allocator.  An "allocator" is a callable that behaves like
+``ffi.new()`` but uses the provided low-level ``alloc`` and ``free``
+functions.  *New in version 1.2.*
+
+``alloc()`` is invoked with the size as sole argument.  If it returns
+NULL, a MemoryError is raised.  Later, if ``free`` is not None, it will
+be called with the result of ``alloc()`` as argument.  Both can be either
+Python function or directly C functions.  If only ``free`` is None, then no
+free function is called.  If both ``alloc`` and ``free`` are None, the
+default alloc/free combination is used.  (In other words, the call
+``ffi.new(*args)`` is equivalent to ``ffi.new_allocator()(*args)``.)
+
+If ``should_clear_after_alloc`` is set to False, then the memory
+returned by ``alloc()`` is assumed to be already cleared (or you are
+fine with garbage); otherwise CFFI will clear it.  Example: for
+performance, if you are using ``ffi.new()`` to allocate large chunks of
+memory where the initial content can be left uninitialized, you can do::
+
+    # at module level
+    new_nonzero = ffi.new_allocator(should_clear_after_alloc=False)
+
+    # then replace `p = ffi.new("char[]", bigsize)` with:
+        p = new_nonzero("char[]", bigsize)
+
+**NOTE:** the following is a general warning that applies particularly
+(but not only) to PyPy versions 5.6 or older (PyPy > 5.6 attempts to
+account for the memory returned by ``ffi.new()`` or a custom allocator;
+and CPython uses reference counting).  If you do large allocations, then
+there is no hard guarantee about when the memory will be freed.  You
+should avoid both ``new()`` and ``new_allocator()()`` if you want to be
+sure that the memory is promptly released, e.g. before you allocate more
+of it.
+
+An alternative is to declare and call the C ``malloc()`` and ``free()``
+functions, or some variant like ``mmap()`` and ``munmap()``.  Then you
+control exactly when the memory is allocated and freed.  For example,
+add these two lines to your existing ``ffibuilder.cdef()``::
+
+    void *malloc(size_t size);
+    void free(void *ptr);
+
+and then call these two functions manually::
+
+    p = lib.malloc(n * ffi.sizeof("int"))
+    try:
+        my_array = ffi.cast("int *", p)
+        ...
+    finally:
+        lib.free(p)
+
+In cffi version 1.12 you can indeed use ``ffi.new_allocator()`` but use the
+``with`` statement (see ``ffi.release()``) to force the free function to be
+called at a known point.  The above is equivalent to this code::
+
+    my_new = ffi.new_allocator(lib.malloc, lib.free)  # at global level
+    ...
+    with my_new("int[]", n) as my_array:
+        ...
+
+
+.. _ffi-release:
+
+ffi.release() and the context manager
++++++++++++++++++++++++++++++++++++++
+
+**ffi.release(cdata)**: release the resources held by a cdata object from
+``ffi.new()``, ``ffi.gc()``, ``ffi.from_buffer()`` or
+``ffi.new_allocator()()``.  The cdata object must not be used afterwards.
+The normal Python destructor of the cdata object releases the same resources,
+but this allows the releasing to occur at a known time, as opposed as at an
+unspecified point in the future.
+*New in version 1.12.*
+
+``ffi.release(cdata)`` is equivalent to ``cdata.__exit__()``, which means that
+you can use the ``with`` statement to ensure that the cdata is released at the
+end of a block (in version 1.12 and above)::
+
+    with ffi.from_buffer(...) as p:
+        do something with p
+
+The effect is more precisely as follows:
+
+* on an object returned from ``ffi.gc(destructor)``, ``ffi.release()`` will
+  cause the ``destructor`` to be called immediately.
+
+* on an object returned from a custom allocator, the custom free function
+  is called immediately.
+
+* on CPython, ``ffi.from_buffer(buf)`` locks the buffer, so ``ffi.release()``
+  can be used to unlock it at a known time.  On PyPy, there is no locking
+  (so far); the effect of ``ffi.release()`` is limited to removing the link,
+  allowing the original buffer object to be garbage-collected even if the
+  cdata object stays alive.
+
+* on CPython this method has no effect (so far) on objects returned by
+  ``ffi.new()``, because the memory is allocated inline with the cdata object
+  and cannot be freed independently.  It might be fixed in future releases of
+  cffi.
+
+* on PyPy, ``ffi.release()`` frees the ``ffi.new()`` memory immediately.  It is
+  useful because otherwise the memory is kept alive until the next GC occurs.
+  If you allocate large amounts of memory with ``ffi.new()`` and don't free
+  them with ``ffi.release()``, PyPy (>= 5.7) runs its GC more often to
+  compensate, so the total memory allocated should be kept within bounds
+  anyway; but calling ``ffi.release()`` explicitly should improve performance
+  by reducing the frequency of GC runs.
+
+After ``ffi.release(x)``, do not use anything pointed to by ``x`` any longer.
+As an exception to this rule, you can call ``ffi.release(x)`` several times
+for the exact same cdata object ``x``; the calls after the first one are
+ignored.
+
+
+ffi.init_once()
++++++++++++++++
+
+**ffi.init_once(function, tag)**: run ``function()`` once.  The
+``tag`` should be a primitive object, like a string, that identifies
+the function: ``function()`` is only called the first time we see the
+``tag``.  The return value of ``function()`` is remembered and
+returned by the current and all future ``init_once()`` with the same
+tag.  If ``init_once()`` is called from multiple threads in parallel,
+all calls block until the execution of ``function()`` is done.  If
+``function()`` raises an exception, it is propagated and nothing is
+cached (i.e. ``function()`` will be called again, in case we catch the
+exception and try ``init_once()`` again).  *New in version 1.4.*
+
+Example::
+
+    from _xyz_cffi import ffi, lib
+
+    def initlib():
+        lib.init_my_library()
+
+    def make_new_foo():
+        ffi.init_once(initlib, "init")
+        return lib.make_foo()
+
+``init_once()`` is optimized to run very quickly if ``function()`` has
+already been called.  (On PyPy, the cost is zero---the JIT usually
+removes everything in the machine code it produces.)
+
+*Note:* one motivation__ for ``init_once()`` is the CPython notion of
+"subinterpreters" in the embedded case.  If you are using the
+out-of-line API mode, ``function()`` is called only once even in the
+presence of multiple subinterpreters, and its return value is shared
+among all subinterpreters.  The goal is to mimic the way traditional
+CPython C extension modules have their init code executed only once in
+total even if there are subinterpreters.  In the example above, the C
+function ``init_my_library()`` is called once in total, not once per
+subinterpreter.  For this reason, avoid Python-level side-effects in
+``function()`` (as they will only be applied in the first
+subinterpreter to run); instead, return a value, as in the following
+example::
+
+   def init_get_max():
+       return lib.initialize_once_and_get_some_maximum_number()
+
+   def process(i):
+       if i > ffi.init_once(init_get_max, "max"):
+           raise IndexError("index too large!")
+       ...
+
+.. __: https://bitbucket.org/cffi/cffi/issues/233/
+
+
+.. _ffi-getctype:
+.. _ffi-list-types:
+
+ffi.getctype(), ffi.list_types()
+++++++++++++++++++++++++++++++++
+
+**ffi.getctype("C type" or <ctype>, extra="")**: return the string
+representation of the given C type.  If non-empty, the "extra" string is
+appended (or inserted at the right place in more complicated cases); it
+can be the name of a variable to declare, or an extra part of the type
+like ``"*"`` or ``"[5]"``.  For example
+``ffi.getctype(ffi.typeof(x), "*")`` returns the string representation
+of the C type "pointer to the same type than x"; and
+``ffi.getctype("char[80]", "a") == "char a[80]"``.
+
+**ffi.list_types()**: Returns the user type names known to this FFI
+instance.  This returns a tuple containing three lists of names:
+``(typedef_names, names_of_structs, names_of_unions)``.  *New in
+version 1.6.*
+
+
+.. _`Preparing and Distributing modules`: cdef.html#loading-libraries
+
+
+Conversions
+-----------
+
+This section documents all the conversions that are allowed when
+*writing into* a C data structure (or passing arguments to a function
+call), and *reading from* a C data structure (or getting the result of a
+function call).  The last column gives the type-specific operations
+allowed.
+
++---------------+------------------------+------------------+----------------+
+|    C type     |   writing into         | reading from     |other operations|
++===============+========================+==================+================+
+|   integers    | an integer or anything | a Python int or  | int(), bool()  |
+|   and enums   | on which int() works   | long, depending  | `[6]`,         |
+|   `[5]`       | (but not a float!).    | on the type      | ``<``          |
+|               | Must be within range.  | (ver. 1.10: or a |                |
+|               |                        | bool)            |                |
++---------------+------------------------+------------------+----------------+
+|   ``char``    | a string of length 1   | a string of      | int(), bool(), |
+|               | or another <cdata char>| length 1         | ``<``          |
++---------------+------------------------+------------------+----------------+
+| ``wchar_t``,  | a unicode of length 1  | a unicode of     |                |
+| ``char16_t``, | (or maybe 2 if         | length 1         | int(),         |
+| ``char32_t``  | surrogates) or         | (or maybe 2 if   | bool(), ``<``  |
+| `[8]`         | another similar <cdata>| surrogates)      |                |
++---------------+------------------------+------------------+----------------+
+|  ``float``,   | a float or anything on | a Python float   | float(), int(),|
+|  ``double``   | which float() works    |                  | bool(), ``<``  |
++---------------+------------------------+------------------+----------------+
+|``long double``| another <cdata> with   | a <cdata>, to    | float(), int(),|
+|               | a ``long double``, or  | avoid loosing    | bool()         |
+|               | anything on which      | precision `[3]`  |                |
+|               | float() works          |                  |                |
++---------------+------------------------+------------------+----------------+
+| ``float``     | a complex number       | a Python complex | complex(),     |
+| ``_Complex``, | or anything on which   | number           | bool()         |
+| ``double``    | complex() works        |                  | `[7]`          |
+| ``_Complex``  |                        |                  |                |
++---------------+------------------------+------------------+----------------+
+|  pointers     | another <cdata> with   | a <cdata>        |``[]`` `[4]`,   |
+|               | a compatible type (i.e.|                  |``+``, ``-``,   |
+|               | same type              |                  |bool()          |
+|               | or ``void*``, or as an |                  |                |
+|               | array instead) `[1]`   |                  |                |
++---------------+------------------------+                  |                |
+|  ``void *``   | another <cdata> with   |                  |                |
+|               | any pointer or array   |                  |                |
+|               | type                   |                  |                |
++---------------+------------------------+                  +----------------+
+|  pointers to  | same as pointers       |                  | ``[]``, ``+``, |
+|  structure or |                        |                  | ``-``, bool(), |
+|  union        |                        |                  | and read/write |
+|               |                        |                  | struct fields  |
++---------------+------------------------+                  +----------------+
+| function      | same as pointers       |                  | bool(),        |
+| pointers      |                        |                  | call `[2]`     |
++---------------+------------------------+------------------+----------------+
+|  arrays       | a list or tuple of     | a <cdata>        |len(), iter(),  |
+|               | items                  |                  |``[]`` `[4]`,   |
+|               |                        |                  |``+``, ``-``    |
++---------------+------------------------+                  +----------------+
+| ``char[]``,   | same as arrays, or a   |                  | len(), iter(), |
+| ``un/signed`` | Python byte string     |                  | ``[]``, ``+``, |
+| ``char[]``,   |                        |                  | ``-``          |
+| ``_Bool[]``   |                        |                  |                |
++---------------+------------------------+                  +----------------+
+|``wchar_t[]``, | same as arrays, or a   |                  | len(), iter(), |
+|``char16_t[]``,| Python unicode string  |                  | ``[]``,        |
+|``char32_t[]`` |                        |                  | ``+``, ``-``   |
+|               |                        |                  |                |
++---------------+------------------------+------------------+----------------+
+| structure     | a list or tuple or     | a <cdata>        | read/write     |
+|               | dict of the field      |                  | fields         |
+|               | values, or a same-type |                  |                |
+|               | <cdata>                |                  |                |
++---------------+------------------------+                  +----------------+
+| union         | same as struct, but    |                  | read/write     |
+|               | with at most one field |                  | fields         |
++---------------+------------------------+------------------+----------------+
+
+`[1]` ``item *`` is ``item[]`` in function arguments:
+
+   In a function declaration, as per the C standard, a ``item *``
+   argument is identical to a ``item[]`` argument (and ``ffi.cdef()``
+   doesn't record the difference).  So when you call such a function,
+   you can pass an argument that is accepted by either C type, like
+   for example passing a Python string to a ``char *`` argument
+   (because it works for ``char[]`` arguments) or a list of integers
+   to a ``int *`` argument (it works for ``int[]`` arguments).  Note
+   that even if you want to pass a single ``item``, you need to
+   specify it in a list of length 1; for example, a ``struct point_s
+   *`` argument might be passed as ``[[x, y]]`` or ``[{'x': 5, 'y':
+   10}]``.
+
+   As an optimization, CFFI assumes that a
+   function with a ``char *`` argument to which you pass a Python
+   string will not actually modify the array of characters passed in,
+   and so passes directly a pointer inside the Python string object.
+   (On PyPy, this optimization is only available since PyPy 5.4
+   with CFFI 1.8.)
+
+`[2]` C function calls are done with the GIL released.
+
+   Note that we assume that the called functions are *not* using the
+   Python API from Python.h.  For example, we don't check afterwards
+   if they set a Python exception.  You may work around it, but mixing
+   CFFI with ``Python.h`` is not recommended.  (If you do that, on
+   PyPy and on some platforms like Windows, you may need to explicitly
+   link to ``libpypy-c.dll`` to access the CPython C API compatibility
+   layer; indeed, CFFI-generated modules on PyPy don't link to
+   ``libpypy-c.dll`` on their own.  But really, don't do that in the
+   first place.)
+
+`[3]` ``long double`` support:
+
+   We keep ``long double`` values inside a cdata object to avoid
+   loosing precision.  Normal Python floating-point numbers only
+   contain enough precision for a ``double``.  If you really want to
+   convert such an object to a regular Python float (i.e. a C
+   ``double``), call ``float()``.  If you need to do arithmetic on
+   such numbers without any precision loss, you need instead to define
+   and use a family of C functions like ``long double add(long double
+   a, long double b);``.
+
+`[4]` Slicing with ``x[start:stop]``:
+
+   Slicing is allowed, as long as you specify explicitly both ``start``
+   and ``stop`` (and don't give any ``step``).  It gives a cdata
+   object that is a "view" of all items from ``start`` to ``stop``.
+   It is a cdata of type "array" (so e.g. passing it as an argument to a
+   C function would just convert it to a pointer to the ``start`` item).
+   As with indexing, negative bounds mean really negative indices, like in
+   C.  As for slice assignment, it accepts any iterable, including a list
+   of items or another array-like cdata object, but the length must match.
+   (Note that this behavior differs from initialization: e.g. you can
+   say ``chararray[10:15] = "hello"``, but the assigned string must be of
+   exactly the correct length; no implicit null character is added.)
+
+`[5]` Enums are handled like ints:
+
+   Like C, enum types are mostly int types (unsigned or signed, int or
+   long; note that GCC's first choice is unsigned).  Reading an enum
+   field of a structure, for example, returns you an integer.  To
+   compare their value symbolically, use code like ``if x.field ==
+   lib.FOO``.  If you really want to get their value as a string, use
+   ``ffi.string(ffi.cast("the_enum_type", x.field))``.
+
+`[6]` bool() on a primitive cdata:
+
+   *New in version 1.7.*  In previous versions, it only worked on
+   pointers; for primitives it always returned True.
+
+   *New in version 1.10:*  The C type ``_Bool`` or ``bool`` converts to
+   Python booleans now.  You get an exception if a C ``_Bool`` happens
+   to contain a value different from 0 and 1 (this case triggers
+   undefined behavior in C; if you really have to interface with a
+   library relying on this, don't use ``_Bool`` in the CFFI side).
+   Also, when converting from a byte string to a ``_Bool[]``, only the
+   bytes ``\x00`` and ``\x01`` are accepted.
+
+`[7]` libffi does not support complex numbers:
+
+   *New in version 1.11:* CFFI now supports complex numbers directly.
+   Note however that libffi does not.  This means that C functions that
+   take directly as argument types or return type a complex type cannot
+   be called by CFFI, unless they are directly using the API mode.
+
+`[8]` ``wchar_t``, ``char16_t`` and ``char32_t``
+
+   See `Unicode character types`_ below.
+
+
+.. _file:
+
+Support for FILE
+++++++++++++++++
+
+You can declare C functions taking a ``FILE *`` argument and
+call them with a Python file object.  If needed, you can also do ``c_f
+= ffi.cast("FILE *", fileobj)`` and then pass around ``c_f``.
+
+Note, however, that CFFI does this by a best-effort approach.  If you
+need finer control over buffering, flushing, and timely closing of the
+``FILE *``, then you should not use this special support for ``FILE *``.
+Instead, you can handle regular ``FILE *`` cdata objects that you
+explicitly make using fdopen(), like this:
+
+.. code-block:: python
+
+    ffi.cdef('''
+        FILE *fdopen(int, const char *);   // from the C <stdio.h>
+        int fclose(FILE *);
+    ''')
+
+    myfile.flush()                    # make sure the file is flushed
+    newfd = os.dup(myfile.fileno())   # make a copy of the file descriptor
+    fp = lib.fdopen(newfd, "w")       # make a cdata 'FILE *' around newfd
+    lib.write_stuff_to_file(fp)       # invoke the external function
+    lib.fclose(fp)                    # when you're done, close fp (and newfd)
+
+The special support for ``FILE *`` is anyway implemented in a similar manner
+on CPython 3.x and on PyPy, because these Python implementations' files are
+not natively based on ``FILE *``.  Doing it explicity offers more control.
+
+
+.. _unichar:
+
+Unicode character types
++++++++++++++++++++++++
+
+The ``wchar_t`` type has the same signedness as the underlying
+platform's.  For example, on Linux, it is a signed 32-bit integer.
+However, the types ``char16_t`` and ``char32_t`` (*new in version 1.11*)
+are always unsigned.
+
+Note that CFFI assumes that these types are meant to contain UTF-16 or
+UTF-32 characters in the native endianness.  More precisely:
+
+* ``char32_t`` is assumed to contain UTF-32, or UCS4, which is just the
+  unicode codepoint;
+
+* ``char16_t`` is assumed to contain UTF-16, i.e. UCS2 plus surrogates;
+
+* ``wchar_t`` is assumed to contain either UTF-32 or UTF-16 based on its
+  actual platform-defined size of 4 or 2 bytes.
+
+Whether this assumption is true or not is unspecified by the C language.
+In theory, the C library you are interfacing with could use one of these
+types with a different meaning.  You would then need to handle it
+yourself---for example, by using ``uint32_t`` instead of ``char32_t`` in
+the ``cdef()``, and building the expected arrays of ``uint32_t``
+manually.
+
+Python itself can be compiled with ``sys.maxunicode == 65535`` or
+``sys.maxunicode == 1114111`` (Python >= 3.3 is always 1114111).  This
+changes the handling of surrogates (which are pairs of 16-bit
+"characters" which actually stand for a single codepoint whose value is
+greater than 65535).  If your Python is ``sys.maxunicode == 1114111``,
+then it can store arbitrary unicode codepoints; surrogates are
+automatically inserted when converting from Python unicodes to UTF-16,
+and automatically removed when converting back.   On the other hand, if
+your Python is ``sys.maxunicode == 65535``, then it is the other way
+around: surrogates are removed when converting from Python unicodes
+to UTF-32, and added when converting back.  In other words, surrogate
+conversion is done only when there is a size mismatch.
+
+Note that Python's internal representations is not specified.  For
+example, on CPython >= 3.3, it will use 1- or 2- or 4-bytes arrays
+depending on what the string actually contains.  With CFFI, when you
+pass a Python byte string to a C function expecting a ``char*``, then
+we pass directly a pointer to the existing data without needing a
+temporary buffer; however, the same cannot cleanly be done with
+*unicode* string arguments and the ``wchar_t*`` / ``char16_t*`` /
+``char32_t*`` types, because of the changing internal
+representation.  As a result, and for consistency, CFFI always allocates
+a temporary buffer for unicode strings.
+
+**Warning:** for now, if you use ``char16_t`` and ``char32_t`` with
+``set_source()``, you have to make sure yourself that the types are
+declared by the C source you provide to ``set_source()``.  They would be
+declared if you ``#include`` a library that explicitly uses them, for
+example, or when using C++11.  Otherwise, you need ``#include
+<uchar.h>`` on Linux, or more generally something like ``typedef
+uint16_t char16_t;``.  This is not done automatically by CFFI because
+``uchar.h`` is not standard across platforms, and writing a ``typedef``
+like above would crash if the type happens to be already defined.
diff --git a/doc/source/using.rst b/doc/source/using.rst
new file mode 100644
index 0000000..ff8e5f1
--- /dev/null
+++ b/doc/source/using.rst
@@ -0,0 +1,1027 @@
+================================
+Using the ffi/lib objects
+================================
+
+.. contents::
+
+Keep this page under your pillow.
+
+
+.. _working:
+
+Working with pointers, structures and arrays
+--------------------------------------------
+
+The C code's integers and floating-point values are mapped to Python's
+regular ``int``, ``long`` and ``float``.  Moreover, the C type ``char``
+corresponds to single-character strings in Python.  (If you want it to
+map to small integers, use either ``signed char`` or ``unsigned char``.)
+
+Similarly, the C type ``wchar_t`` corresponds to single-character
+unicode strings.  Note that in some situations (a narrow Python build
+with an underlying 4-bytes wchar_t type), a single wchar_t character
+may correspond to a pair of surrogates, which is represented as a
+unicode string of length 2.  If you need to convert such a 2-chars
+unicode string to an integer, ``ord(x)`` does not work; use instead
+``int(ffi.cast('wchar_t', x))``.
+
+*New in version 1.11:* in addition to ``wchar_t``, the C types
+``char16_t`` and ``char32_t`` work the same but with a known fixed size.
+In previous versions, this could be achieved using ``uint16_t`` and
+``int32_t`` but without automatic conversion to Python unicodes.
+
+Pointers, structures and arrays are more complex: they don't have an
+obvious Python equivalent.  Thus, they correspond to objects of type
+``cdata``, which are printed for example as
+``<cdata 'struct foo_s *' 0xa3290d8>``.
+
+``ffi.new(ctype, [initializer])``: this function builds and returns a
+new cdata object of the given ``ctype``.  The ctype is usually some
+constant string describing the C type.  It must be a pointer or array
+type.  If it is a pointer, e.g. ``"int *"`` or ``struct foo *``, then
+it allocates the memory for one ``int`` or ``struct foo``.  If it is
+an array, e.g. ``int[10]``, then it allocates the memory for ten
+``int``.  In both cases the returned cdata is of type ``ctype``.
+
+The memory is initially filled with zeros.  An initializer can be given
+too, as described later.
+
+Example::
+
+    >>> ffi.new("int *")
+    <cdata 'int *' owning 4 bytes>
+    >>> ffi.new("int[10]")
+    <cdata 'int[10]' owning 40 bytes>
+
+    >>> ffi.new("char *")          # allocates only one char---not a C string!
+    <cdata 'char *' owning 1 bytes>
+    >>> ffi.new("char[]", "foobar")  # this allocates a C string, ending in \0
+    <cdata 'char[]' owning 7 bytes>
+
+Unlike C, the returned pointer object has *ownership* on the allocated
+memory: when this exact object is garbage-collected, then the memory is
+freed.  If, at the level of C, you store a pointer to the memory
+somewhere else, then make sure you also keep the object alive for as
+long as needed.  (This also applies if you immediately cast the returned
+pointer to a pointer of a different type: only the original object has
+ownership, so you must keep it alive.  As soon as you forget it, then
+the casted pointer will point to garbage!  In other words, the ownership
+rules are attached to the *wrapper* cdata objects: they are not, and
+cannot, be attached to the underlying raw memory.)  Example:
+
+.. code-block:: python
+
+    global_weakkeydict = weakref.WeakKeyDictionary()
+
+    def make_foo():
+        s1   = ffi.new("struct foo *")
+        fld1 = ffi.new("struct bar *")
+        fld2 = ffi.new("struct bar *")
+        s1.thefield1 = fld1
+        s1.thefield2 = fld2
+        # here the 'fld1' and 'fld2' object must not go away,
+        # otherwise 's1.thefield1/2' will point to garbage!
+        global_weakkeydict[s1] = (fld1, fld2)
+        # now 's1' keeps alive 'fld1' and 'fld2'.  When 's1' goes
+        # away, then the weak dictionary entry will be removed.
+        return s1
+
+Usually you don't need a weak dict: for example, to call a function with
+a ``char * *`` argument that contains a pointer to a ``char *`` pointer,
+it is enough to do this:
+
+.. code-block:: python
+
+    p = ffi.new("char[]", "hello, world")    # p is a 'char *'
+    q = ffi.new("char **", p)                # q is a 'char **'
+    lib.myfunction(q)
+    # p is alive at least until here, so that's fine
+
+However, this is always wrong (usage of freed memory):
+
+.. code-block:: python
+
+    p = ffi.new("char **", ffi.new("char[]", "hello, world"))
+    # WRONG!  as soon as p is built, the inner ffi.new() gets freed!
+
+This is wrong too, for the same reason:
+
+.. code-block:: python
+
+    p = ffi.new("struct my_stuff")
+    p.foo = ffi.new("char[]", "hello, world")
+    # WRONG!  as soon as p.foo is set, the ffi.new() gets freed!
+
+
+The cdata objects support mostly the same operations as in C: you can
+read or write from pointers, arrays and structures.  Dereferencing a
+pointer is done usually in C with the syntax ``*p``, which is not valid
+Python, so instead you have to use the alternative syntax ``p[0]``
+(which is also valid C).  Additionally, the ``p.x`` and ``p->x``
+syntaxes in C both become ``p.x`` in Python.
+
+We have ``ffi.NULL`` to use in the same places as the C ``NULL``.
+Like the latter, it is actually defined to be ``ffi.cast("void *",
+0)``.  For example, reading a NULL pointer returns a ``<cdata 'type *'
+NULL>``, which you can check for e.g. by comparing it with
+``ffi.NULL``.
+
+There is no general equivalent to the ``&`` operator in C (because it
+would not fit nicely in the model, and it does not seem to be needed
+here).  There is `ffi.addressof()`__, but only for some cases.  You
+cannot take the "address" of a number in Python, for example; similarly,
+you cannot take the address of a CFFI pointer.  If you have this kind
+of C code::
+
+    int x, y;
+    fetch_size(&x, &y);
+
+    opaque_t *handle;      // some opaque pointer
+    init_stuff(&handle);   // initializes the variable 'handle'
+    more_stuff(handle);    // pass the handle around to more functions
+
+then you need to rewrite it like this, replacing the variables in C
+with what is logically pointers to the variables:
+
+.. code-block:: python
+
+    px = ffi.new("int *")
+    py = ffi.new("int *")              arr = ffi.new("int[2]")
+    lib.fetch_size(px, py)    -OR-     lib.fetch_size(arr, arr + 1)
+    x = px[0]                          x = arr[0]
+    y = py[0]                          y = arr[1]
+
+    p_handle = ffi.new("opaque_t **")
+    lib.init_stuff(p_handle)   # pass the pointer to the 'handle' pointer
+    handle = p_handle[0]       # now we can read 'handle' out of 'p_handle'
+    lib.more_stuff(handle)
+
+.. __: ref.html#ffi-addressof
+
+
+Any operation that would in C return a pointer or array or struct type
+gives you a fresh cdata object.  Unlike the "original" one, these fresh
+cdata objects don't have ownership: they are merely references to
+existing memory.
+
+As an exception to the above rule, dereferencing a pointer that owns a
+*struct* or *union* object returns a cdata struct or union object
+that "co-owns" the same memory.  Thus in this case there are two
+objects that can keep the same memory alive.  This is done for cases where
+you really want to have a struct object but don't have any convenient
+place to keep alive the original pointer object (returned by
+``ffi.new()``).
+
+Example:
+
+.. code-block:: python
+
+    # void somefunction(int *);
+
+    x = ffi.new("int *")      # allocate one int, and return a pointer to it
+    x[0] = 42                 # fill it
+    lib.somefunction(x)       # call the C function
+    print x[0]                # read the possibly-changed value
+
+The equivalent of C casts are provided with ``ffi.cast("type", value)``.
+They should work in the same cases as they do in C.  Additionally, this
+is the only way to get cdata objects of integer or floating-point type::
+
+    >>> x = ffi.cast("int", 42)
+    >>> x
+    <cdata 'int' 42>
+    >>> int(x)
+    42
+
+To cast a pointer to an int, cast it to ``intptr_t`` or ``uintptr_t``,
+which are defined by C to be large enough integer types (example on 32
+bits)::
+
+    >>> int(ffi.cast("intptr_t", pointer_cdata))    # signed
+    -1340782304
+    >>> int(ffi.cast("uintptr_t", pointer_cdata))   # unsigned
+    2954184992L
+
+The initializer given as the optional second argument to ``ffi.new()``
+can be mostly anything that you would use as an initializer for C code,
+with lists or tuples instead of using the C syntax ``{ .., .., .. }``.
+Example::
+
+    typedef struct { int x, y; } foo_t;
+
+    foo_t v = { 1, 2 };            // C syntax
+    v = ffi.new("foo_t *", [1, 2]) # CFFI equivalent
+
+    foo_t v = { .y=1, .x=2 };                // C99 syntax
+    v = ffi.new("foo_t *", {'y': 1, 'x': 2}) # CFFI equivalent
+
+Like C, arrays of chars can also be initialized from a string, in
+which case a terminating null character is appended implicitly::
+
+    >>> x = ffi.new("char[]", "hello")
+    >>> x
+    <cdata 'char[]' owning 6 bytes>
+    >>> len(x)        # the actual size of the array
+    6
+    >>> x[5]          # the last item in the array
+    '\x00'
+    >>> x[0] = 'H'    # change the first item
+    >>> ffi.string(x) # interpret 'x' as a regular null-terminated string
+    'Hello'
+
+Similarly, arrays of wchar_t or char16_t or char32_t can be initialized
+from a unicode string,
+and calling ``ffi.string()`` on the cdata object returns the current unicode
+string stored in the source array (adding surrogates if necessary).
+See the `Unicode character types`__ section for more details.
+
+.. __: ref.html#unichar
+
+Note that unlike Python lists or tuples, but like C, you *cannot* index in
+a C array from the end using negative numbers.
+
+More generally, the C array types can have their length unspecified in C
+types, as long as their length can be derived from the initializer, like
+in C::
+
+    int array[] = { 1, 2, 3, 4 };           // C syntax
+    array = ffi.new("int[]", [1, 2, 3, 4])  # CFFI equivalent
+
+As an extension, the initializer can also be just a number, giving
+the length (in case you just want zero-initialization)::
+
+    int array[1000];                  // C syntax
+    array = ffi.new("int[1000]")      # CFFI 1st equivalent
+    array = ffi.new("int[]", 1000)    # CFFI 2nd equivalent
+
+This is useful if the length is not actually a constant, to avoid things
+like ``ffi.new("int[%d]" % x)``.  Indeed, this is not recommended:
+``ffi`` normally caches the string ``"int[]"`` to not need to re-parse
+it all the time.
+
+The C99 variable-sized structures are supported too, as long as the
+initializer says how long the array should be:
+
+.. code-block:: python
+
+    # typedef struct { int x; int y[]; } foo_t;
+
+    p = ffi.new("foo_t *", [5, [6, 7, 8]]) # length 3
+    p = ffi.new("foo_t *", [5, 3])         # length 3 with 0 in the array
+    p = ffi.new("foo_t *", {'y': 3})       # length 3 with 0 everywhere
+
+Finally, note that any Python object used as initializer can also be
+used directly without ``ffi.new()`` in assignments to array items or
+struct fields.  In fact, ``p = ffi.new("T*", initializer)`` is
+equivalent to ``p = ffi.new("T*"); p[0] = initializer``.  Examples:
+
+.. code-block:: python
+
+    # if 'p' is a <cdata 'int[5][5]'>
+    p[2] = [10, 20]             # writes to p[2][0] and p[2][1]
+
+    # if 'p' is a <cdata 'foo_t *'>, and foo_t has fields x, y and z
+    p[0] = {'x': 10, 'z': 20}   # writes to p.x and p.z; p.y unmodified
+
+    # if, on the other hand, foo_t has a field 'char a[5]':
+    p.a = "abc"                 # writes 'a', 'b', 'c' and '\0'; p.a[4] unmodified
+
+In function calls, when passing arguments, these rules can be used too;
+see `Function calls`_.
+
+
+Python 3 support
+----------------
+
+Python 3 is supported, but the main point to note is that the ``char`` C
+type corresponds to the ``bytes`` Python type, and not ``str``.  It is
+your responsibility to encode/decode all Python strings to bytes when
+passing them to or receiving them from CFFI.
+
+This only concerns the ``char`` type and derivative types; other parts
+of the API that accept strings in Python 2 continue to accept strings in
+Python 3.
+
+
+An example of calling a main-like thing
+---------------------------------------
+
+Imagine we have something like this:
+
+.. code-block:: python
+
+   from cffi import FFI
+   ffi = FFI()
+   ffi.cdef("""
+      int main_like(int argv, char *argv[]);
+   """)
+   lib = ffi.dlopen("some_library.so")
+
+Now, everything is simple, except, how do we create the ``char**`` argument
+here?
+The first idea:
+
+.. code-block:: python
+
+   lib.main_like(2, ["arg0", "arg1"])
+
+does not work, because the initializer receives two Python ``str`` objects
+where it was expecting ``<cdata 'char *'>`` objects.  You need to use
+``ffi.new()`` explicitly to make these objects:
+
+.. code-block:: python
+
+   lib.main_like(2, [ffi.new("char[]", "arg0"),
+                     ffi.new("char[]", "arg1")])
+
+Note that the two ``<cdata 'char[]'>`` objects are kept alive for the
+duration of the call: they are only freed when the list itself is freed,
+and the list is only freed when the call returns.
+
+If you want instead to build an "argv" variable that you want to reuse,
+then more care is needed:
+
+.. code-block:: python
+
+   # DOES NOT WORK!
+   argv = ffi.new("char *[]", [ffi.new("char[]", "arg0"),
+                               ffi.new("char[]", "arg1")])
+
+In the above example, the inner "arg0" string is deallocated as soon
+as "argv" is built.  You have to make sure that you keep a reference
+to the inner "char[]" objects, either directly or by keeping the list
+alive like this:
+
+.. code-block:: python
+
+   argv_keepalive = [ffi.new("char[]", "arg0"),
+                     ffi.new("char[]", "arg1")]
+   argv = ffi.new("char *[]", argv_keepalive)
+
+
+Function calls
+--------------
+
+When calling C functions, passing arguments follows mostly the same
+rules as assigning to structure fields, and the return value follows the
+same rules as reading a structure field.  For example:
+
+.. code-block:: python
+
+    # int foo(short a, int b);
+
+    n = lib.foo(2, 3)     # returns a normal integer
+    lib.foo(40000, 3)     # raises OverflowError
+
+You can pass to ``char *`` arguments a normal Python string (but don't
+pass a normal Python string to functions that take a ``char *``
+argument and may mutate it!):
+
+.. code-block:: python
+
+    # size_t strlen(const char *);
+
+    assert lib.strlen("hello") == 5
+
+You can also pass unicode strings as ``wchar_t *`` or ``char16_t *`` or
+``char32_t *`` arguments.  Note that
+the C language makes no difference between argument declarations that
+use ``type *`` or ``type[]``.  For example, ``int *`` is fully
+equivalent to ``int[]`` (or even ``int[5]``; the 5 is ignored).  For CFFI,
+this means that you can always pass arguments that can be converted to
+either ``int *`` or ``int[]``.  For example:
+
+.. code-block:: python
+
+    # void do_something_with_array(int *array);
+
+    lib.do_something_with_array([1, 2, 3, 4, 5])    # works for int[]
+
+See `Reference: conversions`__ for a similar way to pass ``struct foo_s
+*`` arguments---but in general, it is clearer in this case to pass
+``ffi.new('struct foo_s *', initializer)``.
+
+__ ref.html#conversions
+
+CFFI supports passing and returning structs and unions to functions and
+callbacks.  Example:
+
+.. code-block:: python
+
+    # struct foo_s { int a, b; };
+    # struct foo_s function_returning_a_struct(void);
+
+    myfoo = lib.function_returning_a_struct()
+    # `myfoo`: <cdata 'struct foo_s' owning 8 bytes>
+
+For performance, non-variadic API-level functions that you get by
+writing ``lib.some_function`` are not ``<cdata>``
+objects, but an object of a different type (on CPython, ``<built-in
+function>``).  This means you cannot pass them directly to some other C
+function expecting a function pointer argument.  Only ``ffi.typeof()``
+works on them.  To get a cdata containing a regular function pointer,
+use ``ffi.addressof(lib, "name")``.
+
+There are a few (obscure) limitations to the supported argument and
+return types.  These limitations come from libffi and apply only to
+calling ``<cdata>`` function pointers; in other words, they don't
+apply to non-variadic ``cdef()``-declared functions if you are using
+the API mode.  The limitations are that you cannot pass directly as
+argument or return type:
+
+* a union (but a *pointer* to a union is fine);
+
+* a struct which uses bitfields (but a *pointer* to such a struct is
+  fine);
+
+* a struct that was declared with "``...``" in the ``cdef()``.
+
+In API mode, you can work around these limitations: for example, if you
+need to call such a function pointer from Python, you can instead write
+a custom C function that accepts the function pointer and the real
+arguments and that does the call from C.  Then declare that custom C
+function in the ``cdef()`` and use it from Python.
+
+
+Variadic function calls
+-----------------------
+
+Variadic functions in C (which end with "``...``" as their last
+argument) can be declared and called normally, with the exception that
+all the arguments passed in the variable part *must* be cdata objects.
+This is because it would not be possible to guess, if you wrote this::
+
+    lib.printf("hello, %d\n", 42)   # doesn't work!
+
+that you really meant the 42 to be passed as a C ``int``, and not a
+``long`` or ``long long``.  The same issue occurs with ``float`` versus
+``double``.  So you have to force cdata objects of the C type you want,
+if necessary with ``ffi.cast()``:
+
+.. code-block:: python
+  
+    lib.printf("hello, %d\n", ffi.cast("int", 42))
+    lib.printf("hello, %ld\n", ffi.cast("long", 42))
+    lib.printf("hello, %f\n", ffi.cast("double", 42))
+
+But of course:
+
+.. code-block:: python
+
+    lib.printf("hello, %s\n", ffi.new("char[]", "world"))
+
+Note that if you are using ``dlopen()``, the function declaration in the
+``cdef()`` must match the original one in C exactly, as usual --- in
+particular, if this function is variadic in C, then its ``cdef()``
+declaration must also be variadic.  You cannot declare it in the
+``cdef()`` with fixed arguments instead, even if you plan to only call
+it with these argument types.  The reason is that some architectures
+have a different calling convention depending on whether the function
+signature is fixed or not.  (On x86-64, the difference can sometimes be
+seen in PyPy's JIT-generated code if some arguments are ``double``.)
+
+Note that the function signature ``int foo();`` is interpreted by CFFI
+as equivalent to ``int foo(void);``.  This differs from the C standard,
+in which ``int foo();`` is really like ``int foo(...);`` and can be
+called with any arguments.  (This feature of C is a pre-C89 relic: the
+arguments cannot be accessed at all in the body of ``foo()`` without
+relying on compiler-specific extensions.  Nowadays virtually all code
+with ``int foo();`` really means ``int foo(void);``.)
+
+
+Memory pressure (PyPy)
+----------------------
+
+This paragraph applies only to PyPy, because its garbage collector (GC)
+is different from CPython's.  It is very common in C code to have pairs
+of functions, one which performs memory allocations or acquires other
+resources, and the other which frees them again.  Depending on how you
+structure your Python code, the freeing function is only called when the
+GC decides a particular (Python) object can be freed.  This occurs
+notably in these cases:
+
+* If you use a ``__del__()`` method to call the freeing function.
+
+* If you use ``ffi.gc()`` without also using ``ffi.release()``.
+
+* This does not occur if you call the freeing function at a
+  deterministic time, like in a regular ``try: finally:`` block.  It
+  does however occur *inside a generator---* if the generator is not
+  explicitly exhausted but forgotten at a ``yield`` point, then the code
+  in the enclosing ``finally`` block is only invoked at the next GC.
+
+In these cases, you may have to use the built-in function
+``__pypy__.add_memory_pressure(n)``.  Its argument ``n`` is an estimate
+of how much memory pressure to add.  For example, if the pair of C
+functions that we are talking about is ``malloc(n)`` and ``free()`` or
+similar, you would call ``__pypy__.add_memory_pressure(n)`` after
+``malloc(n)``.  Doing so is not always a complete answer to the problem,
+but it makes the next GC occur earlier, which is often enough.
+
+The same applies if the memory allocations are indirect, e.g. the C
+function allocates some internal data structures.  In that case, call
+``__pypy__.add_memory_pressure(n)`` with an argument ``n`` that is an
+rough estimation.  Knowing the exact size is not important, and memory
+pressure doesn't have to be manually brought down again after calling
+the freeing function.  If you are writing wrappers for the allocating /
+freeing pair of functions, you should probably call
+``__pypy__.add_memory_pressure()`` in the former even if the user may
+invoke the latter at a known point with a ``finally:`` block.
+
+In case this solution is not sufficient, or if the acquired resource is
+not memory but something else more limited (like file descriptors), then
+there is no better way than restructuring your code to make sure the
+freeing function is called at a known point and not indirectly by the
+GC.
+
+Note that in PyPy <= 5.6 the discussion above also applies to
+``ffi.new()``.  In more recent versions of PyPy, both ``ffi.new()`` and
+``ffi.new_allocator()()`` automatically account for the memory pressure
+they create.  (In case you need to support both older and newer PyPy's,
+try calling ``__pypy__.add_memory_pressure()`` anyway; it is better to
+overestimate than not account for the memory pressure.)
+
+
+.. _extern-python:
+.. _`extern "Python"`:
+
+Extern "Python" (new-style callbacks)
+-------------------------------------
+
+When the C code needs a pointer to a function which invokes back a
+Python function of your choice, here is how you do it in the
+out-of-line API mode.  The next section about Callbacks_ describes the
+ABI-mode solution.
+
+This is *new in version 1.4.*  Use old-style Callbacks_ if backward
+compatibility is an issue.  (The original callbacks are slower to
+invoke and have the same issue as libffi's callbacks; notably, see the
+warning__.  The new style described in the present section does not
+use libffi's callbacks at all.)
+
+.. __: Callbacks_
+
+In the builder script, declare in the cdef a function prefixed with
+``extern "Python"``::
+
+    ffibuilder.cdef("""
+        extern "Python" int my_callback(int, int);
+
+        void library_function(int(*callback)(int, int));
+    """)
+    ffibuilder.set_source("_my_example", r"""
+        #include <some_library.h>
+    """)
+
+The function ``my_callback()`` is then implemented in Python inside
+your application's code::
+
+    from _my_example import ffi, lib
+
+    @ffi.def_extern()
+    def my_callback(x, y):
+        return 42
+
+You obtain a ``<cdata>`` pointer-to-function object by getting
+``lib.my_callback``.  This ``<cdata>`` can be passed to C code and
+then works like a callback: when the C code calls this function
+pointer, the Python function ``my_callback`` is called.  (You need
+to pass ``lib.my_callback`` to C code, and not ``my_callback``: the
+latter is just the Python function above, which cannot be passed to C.)
+
+CFFI implements this by defining ``my_callback`` as a static C
+function, written after the ``set_source()`` code.  The ``<cdata>``
+then points to this function.  What this function does is invoke the
+Python function object that is, at runtime, attached with
+``@ffi.def_extern()``.
+
+The ``@ffi.def_extern()`` decorator should be applied to **global
+functions,** one for each ``extern "Python"`` function of the same
+name.
+
+To support some corner cases, it is possible to redefine the attached
+Python function by calling ``@ffi.def_extern()`` again for the same
+name---but this is not recommended!  Better attach a single global
+Python function for this name, and write it more flexibly in the first
+place.  This is because each ``extern "Python"`` function turns into
+only one C function.  Calling ``@ffi.def_extern()`` again changes this
+function's C logic to call the new Python function; the old Python
+function is not callable any more.  The C function pointer you get
+from ``lib.my_function`` is always this C function's address, i.e. it
+remains the same.
+
+Extern "Python" and ``void *`` arguments
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As described just before, you cannot use ``extern "Python"`` to make a
+variable number of C function pointers.  However, achieving that
+result is not possible in pure C code either.  For this reason, it is
+usual for C to define callbacks with a ``void *data`` argument.  You
+can use ``ffi.new_handle()`` and ``ffi.from_handle()`` to pass a
+Python object through this ``void *`` argument.  For example, if the C
+type of the callbacks is::
+
+    typedef void (*event_cb_t)(event_t *evt, void *userdata);
+
+and you register events by calling this function::
+
+    void event_cb_register(event_cb_t cb, void *userdata);
+
+Then you would write this in the build script::
+
+    ffibuilder.cdef("""
+        typedef ... event_t;
+        typedef void (*event_cb_t)(event_t *evt, void *userdata);
+        void event_cb_register(event_cb_t cb, void *userdata);
+
+        extern "Python" void my_event_callback(event_t *, void *);
+    """)
+    ffibuilder.set_source("_demo_cffi", r"""
+        #include <the_event_library.h>
+    """)
+
+and in your main application you register events like this::
+
+    from _demo_cffi import ffi, lib
+
+    class Widget(object):
+        def __init__(self):
+            userdata = ffi.new_handle(self)
+            self._userdata = userdata     # must keep this alive!
+            lib.event_cb_register(lib.my_event_callback, userdata)
+
+        def process_event(self, evt):
+            print "got event!"
+
+    @ffi.def_extern()
+    def my_event_callback(evt, userdata):
+        widget = ffi.from_handle(userdata)
+        widget.process_event(evt)
+
+Some other libraries don't have an explicit ``void *`` argument, but
+let you attach the ``void *`` to an existing structure.  For example,
+the library might say that ``widget->userdata`` is a generic field
+reserved for the application.  If the event's signature is now this::
+
+    typedef void (*event_cb_t)(widget_t *w, event_t *evt);
+
+Then you can use the ``void *`` field in the low-level
+``widget_t *`` like this::
+
+    from _demo_cffi import ffi, lib
+
+    class Widget(object):
+        def __init__(self):
+            ll_widget = lib.new_widget(500, 500)
+            self.ll_widget = ll_widget       # <cdata 'struct widget *'>
+            userdata = ffi.new_handle(self)
+            self._userdata = userdata        # must still keep this alive!
+            ll_widget.userdata = userdata    # this makes a copy of the "void *"
+            lib.event_cb_register(ll_widget, lib.my_event_callback)
+
+        def process_event(self, evt):
+            print "got event!"
+
+    @ffi.def_extern()
+    def my_event_callback(ll_widget, evt):
+        widget = ffi.from_handle(ll_widget.userdata)
+        widget.process_event(evt)
+
+Extern "Python" accessed from C directly
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In case you want to access some ``extern "Python"`` function directly
+from the C code written in ``set_source()``, you need to write a
+forward declaration.  (By default it needs to be static, but see
+`next paragraph`__.)  The real implementation of this function
+is added by CFFI *after* the C code---this is needed because the
+declaration might use types defined by ``set_source()``
+(e.g. ``event_t`` above, from the ``#include``), so it cannot be
+generated before.
+
+.. __: `extern-python-c`_
+
+::
+
+    ffibuilder.set_source("_demo_cffi", r"""
+        #include <the_event_library.h>
+
+        static void my_event_callback(widget_t *, event_t *);
+
+        /* here you can write C code which uses '&my_event_callback' */
+    """)
+
+This can also be used to write custom C code which calls Python
+directly.  Here is an example (inefficient in this case, but might be
+useful if the logic in ``my_algo()`` is much more complex)::
+
+    ffibuilder.cdef("""
+        extern "Python" int f(int);
+        int my_algo(int);
+    """)
+    ffibuilder.set_source("_example_cffi", r"""
+        static int f(int);   /* the forward declaration */
+
+        static int my_algo(int n) {
+            int i, sum = 0;
+            for (i = 0; i < n; i++)
+                sum += f(i);     /* call f() here */
+            return sum;
+        }
+    """)
+
+.. _extern-python-c:
+
+Extern "Python+C"
+~~~~~~~~~~~~~~~~~
+
+Functions declared with ``extern "Python"`` are generated as
+``static`` functions in the C source.  However, in some cases it is
+convenient to make them non-static, typically when you want to make
+them directly callable from other C source files.  To do that, you can
+say ``extern "Python+C"`` instead of just ``extern "Python"``.  *New
+in version 1.6.*
+
++------------------------------------+--------------------------------------+
+| if the cdef contains               | then CFFI generates                  |
++------------------------------------+--------------------------------------+
+| ``extern "Python" int f(int);``    | ``static int f(int) { /* code */ }`` |
++------------------------------------+--------------------------------------+
+| ``extern "Python+C" int f(int);``  | ``int f(int) { /* code */ }``        |
++------------------------------------+--------------------------------------+
+
+The name ``extern "Python+C"`` comes from the fact that we want an
+extern function in both senses: as an ``extern "Python"``, and as a
+C function that is not static.
+
+You cannot make CFFI generate additional macros or other
+compiler-specific stuff like the GCC ``__attribute__``.  You can only
+control whether the function should be ``static`` or not.  But often,
+these attributes must be written alongside the function *header*, and
+it is fine if the function *implementation* does not repeat them::
+
+    ffibuilder.cdef("""
+        extern "Python+C" int f(int);      /* not static */
+    """)
+    ffibuilder.set_source("_example_cffi", r"""
+        /* the forward declaration, setting a gcc attribute
+           (this line could also be in some .h file, to be included
+           both here and in the other C files of the project) */
+        int f(int) __attribute__((visibility("hidden")));
+    """)
+
+
+Extern "Python": reference
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``extern "Python"`` must appear in the cdef().  Like the C++ ``extern
+"C"`` syntax, it can also be used with braces around a group of
+functions::
+
+    extern "Python" {
+        int foo(int);
+        int bar(int);
+    }
+
+The ``extern "Python"`` functions cannot be variadic for now.  This
+may be implemented in the future.  (`This demo`__ shows how to do it
+anyway, but it is a bit lengthy.)
+
+.. __: https://bitbucket.org/cffi/cffi/src/default/demo/extern_python_varargs.py
+
+Each corresponding Python callback function is defined with the
+``@ffi.def_extern()`` decorator.  Be careful when writing this
+function: if it raises an exception, or tries to return an object of
+the wrong type, then the exception cannot be propagated.  Instead, the
+exception is printed to stderr and the C-level callback is made to
+return a default value.  This can be controlled with ``error`` and
+``onerror``, described below.
+
+.. _def-extern:
+
+The ``@ffi.def_extern()`` decorator takes these optional arguments:
+
+* ``name``: the name of the function as written in the cdef.  By default
+  it is taken from the name of the Python function you decorate.
+
+.. _error_onerror:
+
+* ``error``: the returned value in case the Python function raises an
+  exception.  It is 0 or null by default.  The exception is still
+  printed to stderr, so this should be used only as a last-resort
+  solution.
+
+* ``onerror``: if you want to be sure to catch all exceptions, use
+  ``@ffi.def_extern(onerror=my_handler)``.  If an exception occurs and
+  ``onerror`` is specified, then ``onerror(exception, exc_value,
+  traceback)`` is called.  This is useful in some situations where you
+  cannot simply write ``try: except:`` in the main callback function,
+  because it might not catch exceptions raised by signal handlers: if
+  a signal occurs while in C, the Python signal handler is called as
+  soon as possible, which is after entering the callback function but
+  *before* executing even the ``try:``.  If the signal handler raises,
+  we are not in the ``try: except:`` yet.
+
+  If ``onerror`` is called and returns normally, then it is assumed
+  that it handled the exception on its own and nothing is printed to
+  stderr.  If ``onerror`` raises, then both tracebacks are printed.
+  Finally, ``onerror`` can itself provide the result value of the
+  callback in C, but doesn't have to: if it simply returns None---or
+  if ``onerror`` itself fails---then the value of ``error`` will be
+  used, if any.
+
+  Note the following hack: in ``onerror``, you can access the original
+  callback arguments as follows.  First check if ``traceback`` is not
+  None (it is None e.g. if the whole function ran successfully but
+  there was an error converting the value returned: this occurs after
+  the call).  If ``traceback`` is not None, then
+  ``traceback.tb_frame`` is the frame of the outermost function,
+  i.e. directly the frame of the function decorated with
+  ``@ffi.def_extern()``.  So you can get the value of ``argname`` in
+  that frame by reading ``traceback.tb_frame.f_locals['argname']``.
+
+
+.. _Callbacks:
+
+Callbacks (old style)
+---------------------
+
+Here is how to make a new ``<cdata>`` object that contains a pointer
+to a function, where that function invokes back a Python function of
+your choice::
+
+    >>> @ffi.callback("int(int, int)")
+    >>> def myfunc(x, y):
+    ...    return x + y
+    ...
+    >>> myfunc
+    <cdata 'int(*)(int, int)' calling <function myfunc at 0xf757bbc4>>
+
+Note that ``"int(*)(int, int)"`` is a C *function pointer* type, whereas
+``"int(int, int)"`` is a C *function* type.  Either can be specified to
+ffi.callback() and the result is the same.
+
+.. warning::
+
+    Callbacks are provided for the ABI mode or for backward
+    compatibility.  If you are using the out-of-line API mode, it is
+    recommended to use the `extern "Python"`_ mechanism instead of
+    callbacks: it gives faster and cleaner code.  It also avoids several
+    issues with old-style callbacks:
+
+    - On less common architecture, libffi is more likely to crash on
+      callbacks (`e.g. on NetBSD`__);
+
+    - On hardened systems like PAX and SELinux, the extra memory
+      protections can interfere (for example, on SELinux you need to
+      run with ``deny_execmem`` set to ``off``).
+
+    Note also that a cffi fix for the latter issue was attempted---see
+    the ``ffi_closure_alloc`` branch---but was not merged because it
+    creates potential `memory corruption`__ with ``fork()``.
+
+.. __: https://github.com/pyca/pyopenssl/issues/596
+.. __: https://bugzilla.redhat.com/show_bug.cgi?id=1249685
+
+Warning: like ffi.new(), ffi.callback() returns a cdata that has
+ownership of its C data.  (In this case, the necessary C data contains
+the libffi data structures to do a callback.)  This means that the
+callback can only be invoked as long as this cdata object is alive.
+If you store the function pointer into C code, then make sure you also
+keep this object alive for as long as the callback may be invoked.
+The easiest way to do that is to always use ``@ffi.callback()`` at
+module-level only, and to pass "context" information around with
+`ffi.new_handle()`__, if possible.  Example:
+
+.. __: ref.html#new-handle
+
+.. code-block:: python
+
+    # a good way to use this decorator is once at global level
+    @ffi.callback("int(int, void *)")
+    def my_global_callback(x, handle):
+        return ffi.from_handle(handle).some_method(x)
+
+
+    class Foo(object):
+
+        def __init__(self):
+            handle = ffi.new_handle(self)
+            self._handle = handle   # must be kept alive
+            lib.register_stuff_with_callback_and_voidp_arg(my_global_callback, handle)
+
+        def some_method(self, x):
+            print "method called!"
+
+(See also the section about `extern "Python"`_ above, where the same
+general style is used.)
+
+Note that callbacks of a variadic function type are not supported.  A
+workaround is to add custom C code.  In the following example, a
+callback gets a first argument that counts how many extra ``int``
+arguments are passed:
+
+.. code-block:: python
+
+    # file "example_build.py"
+
+    import cffi
+
+    ffibuilder = cffi.FFI()
+    ffibuilder.cdef("""
+        int (*python_callback)(int how_many, int *values);
+        void *const c_callback;   /* pass this const ptr to C routines */
+    """)
+    ffibuilder.set_source("_example", r"""
+        #include <stdarg.h>
+        #include <alloca.h>
+        static int (*python_callback)(int how_many, int *values);
+        static int c_callback(int how_many, ...) {
+            va_list ap;
+            /* collect the "..." arguments into the values[] array */
+            int i, *values = alloca(how_many * sizeof(int));
+            va_start(ap, how_many);
+            for (i=0; i<how_many; i++)
+                values[i] = va_arg(ap, int);
+            va_end(ap);
+            return python_callback(how_many, values);
+        }
+    """)
+    ffibuilder.compile(verbose=True)
+
+.. code-block:: python
+    
+    # file "example.py"
+
+    from _example import ffi, lib
+
+    @ffi.callback("int(int, int *)")
+    def python_callback(how_many, values):
+        print ffi.unpack(values, how_many)
+        return 0
+    lib.python_callback = python_callback
+
+Deprecated: you can also use ``ffi.callback()`` not as a decorator but
+directly as ``ffi.callback("int(int, int)", myfunc)``.  This is
+discouraged: using this a style, we are more likely to forget the
+callback object too early, when it is still in use.
+
+The ``ffi.callback()`` decorator also accepts the optional argument
+``error``, and from CFFI version 1.2 the optional argument ``onerror``.
+These two work in the same way as `described above for extern "Python".`__
+
+.. __: error_onerror_
+
+
+
+Windows: calling conventions
+----------------------------
+
+On Win32, functions can have two main calling conventions: either
+"cdecl" (the default), or "stdcall" (also known as "WINAPI").  There
+are also other rare calling conventions, but these are not supported.
+*New in version 1.3.*
+
+When you issue calls from Python to C, the implementation is such that
+it works with any of these two main calling conventions; you don't
+have to specify it.  However, if you manipulate variables of type
+"function pointer" or declare callbacks, then the calling convention
+must be correct.  This is done by writing ``__cdecl`` or ``__stdcall``
+in the type, like in C::
+
+    @ffi.callback("int __stdcall(int, int)")
+    def AddNumbers(x, y):
+        return x + y
+
+or::
+
+    ffibuilder.cdef("""
+        struct foo_s {
+            int (__stdcall *MyFuncPtr)(int, int);
+        };
+    """)
+
+``__cdecl`` is supported but is always the default so it can be left
+out.  In the ``cdef()``, you can also use ``WINAPI`` as equivalent to
+``__stdcall``.  As mentioned above, it is mostly not needed (but doesn't
+hurt) to say ``WINAPI`` or ``__stdcall`` when declaring a plain
+function in the ``cdef()``.  (The difference can still be seen if you
+take explicitly a pointer to this function with ``ffi.addressof()``,
+or if the function is ``extern "Python"``.)
+
+These calling convention specifiers are accepted but ignored on any
+platform other than 32-bit Windows.
+
+In CFFI versions before 1.3, the calling convention specifiers are not
+recognized.  In API mode, you could work around it by using an
+indirection, like in the example in the section about Callbacks_
+(``"example_build.py"``).  There was no way to use stdcall callbacks
+in ABI mode.
+
+
+FFI Interface
+-------------
+
+(The reference for the FFI interface has been moved to the `next page`__.)
+
+.. __: ref.html
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
new file mode 100644
index 0000000..18bb218
--- /dev/null
+++ b/doc/source/whatsnew.rst
@@ -0,0 +1,714 @@
+======================
+What's New
+======================
+
+
+v1.12.2
+=======
+
+* Added temporary workaround to compile on CPython 3.8.0a2.
+
+
+v1.12.1
+=======
+
+* CPython 3 on Windows: we again no longer compile with ``Py_LIMITED_API``
+  by default because such modules *still* cannot be used with virtualenv.
+  The problem is that it doesn't work in CPython <= 3.4, and for
+  technical reason we can't enable this flag automatically based on the
+  version of Python.
+
+  Like before, `Issue #350`_ mentions a workaround if you still want
+  the ``Py_LIMITED_API`` flag and *either* you are not concerned about
+  virtualenv *or* you are sure your module will not be used on CPython
+  <= 3.4: pass ``define_macros=[("Py_LIMITED_API", None)]`` to the
+  ``ffibuilder.set_source()`` call.
+
+
+v1.12
+=====
+
+* `Direct support for pkg-config`__.
+
+* ``ffi.from_buffer()`` takes a new optional *first* argument that gives
+  the array type of the result.  It also takes an optional keyword argument
+  ``require_writable`` to refuse read-only Python buffers.
+
+* ``ffi.new()``, ``ffi.gc()`` or ``ffi.from_buffer()`` cdata objects
+  can now be released at known times, either by using the ``with``
+  keyword or by calling the new ``ffi.release()``.
+
+* Windows, CPython 3.x: cffi modules are linked with ``python3.dll``
+  again.  This makes them independant on the exact CPython version,
+  like they are on other platforms.  **It requires virtualenv 16.0.0.**
+
+* Accept an expression like ``ffi.new("int[4]", p)`` if ``p`` is itself
+  another cdata ``int[4]``.
+
+* CPython 2.x: ``ffi.dlopen()`` failed with non-ascii file names on Posix
+
+* CPython: if a thread is started from C and then runs Python code (with
+  callbacks or with the embedding solution), then previous versions of
+  cffi would contain possible crashes and/or memory leaks.  Hopefully,
+  this has been fixed (see `issue #362`_).
+
+* Support for ``ffi.cdef(..., pack=N)`` where N is a power of two.
+  Means to emulate ``#pragma pack(N)`` on MSVC.  Also, the default on
+  Windows is now ``pack=8``, like on MSVC.  This might make a difference
+  in corner cases, although I can't think of one in the context of CFFI.
+  The old way ``ffi.cdef(..., packed=True)`` remains and is equivalent
+  to ``pack=1`` (saying e.g. that fields like ``int`` should be aligned
+  to 1 byte instead of 4).
+
+.. __: cdef.html#pkgconfig
+.. _`issue #362`: https://bitbucket.org/cffi/cffi/issues/362/
+
+
+Older Versions
+==============
+
+v1.11.5
+-------
+
+* `Issue #357`_: fix ``ffi.emit_python_code()`` which generated a buggy
+  Python file if you are using a ``struct`` with an anonymous ``union``
+  field or vice-versa.
+
+* Windows: ``ffi.dlopen()`` should now handle unicode filenames.
+
+* ABI mode: implemented ``ffi.dlclose()`` for the in-line case (it used
+  to be present only in the out-of-line case).
+
+* Fixed a corner case for ``setup.py install --record=xx --root=yy``
+  with an out-of-line ABI module.  Also fixed `Issue #345`_.
+
+* More hacks on Windows for running CFFI's own ``setup.py``.
+
+* `Issue #358`_: in embedding, to protect against (the rare case of)
+  Python initialization from several threads in parallel, we have to use
+  a spin-lock.  On CPython 3 it is worse because it might spin-lock for
+  a long time (execution of ``Py_InitializeEx()``).  Sadly, recent
+  changes to CPython make that solution needed on CPython 2 too.
+
+* CPython 3 on Windows: we no longer compile with ``Py_LIMITED_API``
+  by default because such modules cannot be used with virtualenv.
+  `Issue #350`_ mentions a workaround if you still want that and are not
+  concerned about virtualenv: pass a ``define_macros=[("Py_LIMITED_API",
+  None)]`` to the ``ffibuilder.set_source()`` call.
+
+.. _`Issue #345`: https://bitbucket.org/cffi/cffi/issues/345/
+.. _`Issue #350`: https://bitbucket.org/cffi/cffi/issues/350/
+.. _`Issue #358`: https://bitbucket.org/cffi/cffi/issues/358/
+.. _`Issue #357`: https://bitbucket.org/cffi/cffi/issues/357/
+
+
+v1.11.4
+-------
+
+* Windows: reverted linking with ``python3.dll``, because
+  virtualenv does not make this DLL available to virtual environments
+  for now.  See `Issue #355`_.  On Windows only, the C extension
+  modules created by cffi follow for now the standard naming scheme
+  ``foo.cp36-win32.pyd``, to make it clear that they are regular
+  CPython modules depending on ``python36.dll``.
+
+.. _`Issue #355`: https://bitbucket.org/cffi/cffi/issues/355/
+
+
+v1.11.3
+-------
+
+* Fix on CPython 3.x: reading the attributes ``__loader__`` or
+  ``__spec__`` from the cffi-generated lib modules gave a buggy
+  SystemError.  (These attributes are always None, and provided only to
+  help compatibility with tools that expect them in all modules.)
+
+* More Windows fixes: workaround for MSVC not supporting large
+  literal strings in C code (from
+  ``ffi.embedding_init_code(large_string)``); and an issue with
+  ``Py_LIMITED_API`` linking with ``python35.dll/python36.dll`` instead
+  of ``python3.dll``.
+
+* Small documentation improvements.
+
+
+v1.11.2
+-------
+
+* Fix Windows issue with managing the thread-state on CPython 3.0 to 3.5
+
+
+v1.11.1
+-------
+
+* Fix tests, remove deprecated C API usage
+
+* Fix (hack) for 3.6.0/3.6.1/3.6.2 giving incompatible binary extensions
+  (cpython issue `#29943`_)
+
+* Fix for 3.7.0a1+
+
+.. _`#29943`: https://bugs.python.org/issue29943
+
+
+v1.11
+-----
+
+* Support the modern standard types ``char16_t`` and ``char32_t``.
+  These work like ``wchar_t``: they represent one unicode character, or
+  when used as ``charN_t *`` or ``charN_t[]`` they represent a unicode
+  string.  The difference with ``wchar_t`` is that they have a known,
+  fixed size.  They should work at all places that used to work with
+  ``wchar_t`` (please report an issue if I missed something).  Note
+  that with ``set_source()``, you need to make sure that these types are
+  actually defined by the C source you provide (if used in ``cdef()``).
+
+* Support the C99 types ``float _Complex`` and ``double _Complex``.
+  Note that libffi doesn't support them, which means that in the ABI
+  mode you still cannot call C functions that take complex numbers
+  directly as arguments or return type.
+
+* Fixed a rare race condition when creating multiple ``FFI`` instances
+  from multiple threads.  (Note that you aren't meant to create many
+  ``FFI`` instances: in inline mode, you should write ``ffi =
+  cffi.FFI()`` at module level just after ``import cffi``; and in
+  out-of-line mode you don't instantiate ``FFI`` explicitly at all.)
+
+* Windows: using callbacks can be messy because the CFFI internal error
+  messages show up to stderr---but stderr goes nowhere in many
+  applications.  This makes it particularly hard to get started with the
+  embedding mode.  (Once you get started, you can at least use
+  ``@ffi.def_extern(onerror=...)`` and send the error logs where it
+  makes sense for your application, or record them in log files, and so
+  on.)  So what is new in CFFI is that now, on Windows CFFI will try to
+  open a non-modal MessageBox (in addition to sending raw messages to
+  stderr).  The MessageBox is only visible if the process stays alive:
+  typically, console applications that crash close immediately, but that
+  is also the situation where stderr should be visible anyway.
+
+* Progress on support for `callbacks in NetBSD`__.
+
+* Functions returning booleans would in some case still return 0 or 1
+  instead of False or True.  Fixed.
+
+* `ffi.gc()`__ now takes an optional third parameter, which gives an
+  estimate of the size (in bytes) of the object.  So far, this is only
+  used by PyPy, to make the next GC occur more quickly (`issue #320`__).
+  In the future, this might have an effect on CPython too (provided
+  the CPython `issue 31105`__ is addressed).
+
+* Add a note to the documentation: the ABI mode gives function objects
+  that are *slower* to call than the API mode does.  For some reason it
+  is often thought to be faster.  It is not!
+
+.. __: https://bitbucket.org/cffi/cffi/issues/321/cffi-191-segmentation-fault-during-self
+.. __: ref.html#ffi-gc
+.. __: https://bitbucket.org/cffi/cffi/issues/320/improve-memory_pressure-management
+.. __: http://bugs.python.org/issue31105
+
+
+v1.10.1
+-------
+
+(only released inside PyPy 5.8.0)
+
+* Fixed the line numbers reported in case of ``cdef()`` errors.
+  Also, I just noticed, but pycparser always supported the preprocessor
+  directive ``# 42 "foo.h"`` to mean "from the next line, we're in file
+  foo.h starting from line 42", which it puts in the error messages.
+
+
+v1.10
+-----
+
+* Issue #295: use calloc() directly instead of
+  PyObject_Malloc()+memset() to handle ffi.new() with a default
+  allocator.  Speeds up ``ffi.new(large-array)`` where most of the time
+  you never touch most of the array.
+
+* Some OS/X build fixes ("only with Xcode but without CLT").
+
+* Improve a couple of error messages: when getting mismatched versions
+  of cffi and its backend; and when calling functions which cannot be
+  called with libffi because an argument is a struct that is "too
+  complicated" (and not a struct *pointer*, which always works).
+
+* Add support for some unusual compilers (non-msvc, non-gcc, non-icc,
+  non-clang)
+
+* Implemented the remaining cases for ``ffi.from_buffer``.  Now all
+  buffer/memoryview objects can be passed.  The one remaining check is
+  against passing unicode strings in Python 2.  (They support the buffer
+  interface, but that gives the raw bytes behind the UTF16/UCS4 storage,
+  which is most of the times not what you expect.  In Python 3 this has
+  been fixed and the unicode strings don't support the memoryview
+  interface any more.)
+
+* The C type ``_Bool`` or ``bool`` now converts to a Python boolean
+  when reading, instead of the content of the byte as an integer.  The
+  potential incompatibility here is what occurs if the byte contains a
+  value different from 0 and 1.  Previously, it would just return it;
+  with this change, CFFI raises an exception in this case.  But this
+  case means "undefined behavior" in C; if you really have to interface
+  with a library relying on this, don't use ``bool`` in the CFFI side.
+  Also, it is still valid to use a byte string as initializer for a
+  ``bool[]``, but now it must only contain ``\x00`` or ``\x01``.  As an
+  aside, ``ffi.string()`` no longer works on ``bool[]`` (but it never
+  made much sense, as this function stops at the first zero).
+
+* ``ffi.buffer`` is now the name of cffi's buffer type, and
+  ``ffi.buffer()`` works like before but is the constructor of that type.
+
+* ``ffi.addressof(lib, "name")``  now works also in in-line mode, not
+  only in out-of-line mode.  This is useful for taking the address of
+  global variables.
+
+* Issue #255: ``cdata`` objects of a primitive type (integers, floats,
+  char) are now compared and ordered by value.  For example, ``<cdata
+  'int' 42>`` compares equal to ``42`` and ``<cdata 'char' b'A'>``
+  compares equal to ``b'A'``.  Unlike C, ``<cdata 'int' -1>`` does not
+  compare equal to ``ffi.cast("unsigned int", -1)``: it compares
+  smaller, because ``-1 < 4294967295``.
+
+* PyPy: ``ffi.new()`` and ``ffi.new_allocator()()`` did not record
+  "memory pressure", causing the GC to run too infrequently if you call
+  ``ffi.new()`` very often and/or with large arrays.  Fixed in PyPy 5.7.
+
+* Support in ``ffi.cdef()`` for numeric expressions with ``+`` or
+  ``-``.  Assumes that there is no overflow; it should be fixed first
+  before we add more general support for arbitrary arithmetic on
+  constants.
+
+
+v1.9
+----
+
+* Structs with variable-sized arrays as their last field: now we track
+  the length of the array after ``ffi.new()`` is called, just like we
+  always tracked the length of ``ffi.new("int[]", 42)``.  This lets us
+  detect out-of-range accesses to array items.  This also lets us
+  display a better ``repr()``, and have the total size returned by
+  ``ffi.sizeof()`` and ``ffi.buffer()``.  Previously both functions
+  would return a result based on the size of the declared structure
+  type, with an assumed empty array.  (Thanks andrew for starting this
+  refactoring.)
+
+* Add support in ``cdef()/set_source()`` for unspecified-length arrays
+  in typedefs: ``typedef int foo_t[...];``.  It was already supported
+  for global variables or structure fields.
+
+* I turned in v1.8 a warning from ``cffi/model.py`` into an error:
+  ``'enum xxx' has no values explicitly defined: refusing to guess which
+  integer type it is meant to be (unsigned/signed, int/long)``.  Now I'm
+  turning it back to a warning again; it seems that guessing that the
+  enum has size ``int`` is a 99%-safe bet.  (But not 100%, so it stays
+  as a warning.)
+
+* Fix leaks in the code handling ``FILE *`` arguments.  In CPython 3
+  there is a remaining issue that is hard to fix: if you pass a Python
+  file object to a ``FILE *`` argument, then ``os.dup()`` is used and
+  the new file descriptor is only closed when the GC reclaims the Python
+  file object---and not at the earlier time when you call ``close()``,
+  which only closes the original file descriptor.  If this is an issue,
+  you should avoid this automatic convertion of Python file objects:
+  instead, explicitly manipulate file descriptors and call ``fdopen()``
+  from C (...via cffi).
+
+
+v1.8.3
+------
+
+* When passing a ``void *`` argument to a function with a different
+  pointer type, or vice-versa, the cast occurs automatically, like in C.
+  The same occurs for initialization with ``ffi.new()`` and a few other
+  places.  However, I thought that ``char *`` had the same
+  property---but I was mistaken.  In C you get the usual warning if you
+  try to give a ``char *`` to a ``char **`` argument, for example.
+  Sorry about the confusion.  This has been fixed in CFFI by giving for
+  now a warning, too.  It will turn into an error in a future version.
+
+
+v1.8.2
+------
+
+* Issue #283: fixed ``ffi.new()`` on structures/unions with nested
+  anonymous structures/unions, when there is at least one union in
+  the mix.  When initialized with a list or a dict, it should now
+  behave more closely like the ``{ }`` syntax does in GCC.
+
+
+v1.8.1
+------
+
+* CPython 3.x: experimental: the generated C extension modules now use
+  the "limited API", which means that, as a compiled .so/.dll, it should
+  work directly on any version of CPython >= 3.2.  The name produced by
+  distutils is still version-specific.  To get the version-independent
+  name, you can rename it manually to ``NAME.abi3.so``, or use the very
+  recent setuptools 26.
+
+* Added ``ffi.compile(debug=...)``, similar to ``python setup.py build
+  --debug`` but defaulting to True if we are running a debugging
+  version of Python itself.
+
+
+v1.8
+----
+
+* Removed the restriction that ``ffi.from_buffer()`` cannot be used on
+  byte strings.  Now you can get a ``char *`` out of a byte string,
+  which is valid as long as the string object is kept alive.  (But
+  don't use it to *modify* the string object!  If you need this, use
+  ``bytearray`` or other official techniques.)
+
+* PyPy 5.4 can now pass a byte string directly to a ``char *``
+  argument (in older versions, a copy would be made).  This used to be
+  a CPython-only optimization.
+
+
+v1.7
+----
+
+* ``ffi.gc(p, None)`` removes the destructor on an object previously
+  created by another call to ``ffi.gc()``
+
+* ``bool(ffi.cast("primitive type", x))`` now returns False if the
+  value is zero (including ``-0.0``), and True otherwise.  Previously
+  this would only return False for cdata objects of a pointer type when
+  the pointer is NULL.
+
+* bytearrays: ``ffi.from_buffer(bytearray-object)`` is now supported.
+  (The reason it was not supported was that it was hard to do in PyPy,
+  but it works since PyPy 5.3.)  To call a C function with a ``char *``
+  argument from a buffer object---now including bytearrays---you write
+  ``lib.foo(ffi.from_buffer(x))``.  Additionally, this is now supported:
+  ``p[0:length] = bytearray-object``.  The problem with this was that a
+  iterating over bytearrays gives *numbers* instead of *characters*.
+  (Now it is implemented with just a memcpy, of course, not actually
+  iterating over the characters.)
+
+* C++: compiling the generated C code with C++ was supposed to work,
+  but failed if you make use the ``bool`` type (because that is rendered
+  as the C ``_Bool`` type, which doesn't exist in C++).
+
+* ``help(lib)`` and ``help(lib.myfunc)`` now give useful information,
+  as well as ``dir(p)`` where ``p`` is a struct or pointer-to-struct.
+
+
+v1.6
+----
+
+* `ffi.list_types()`_
+
+* `ffi.unpack()`_
+
+* `extern "Python+C"`_
+
+* in API mode, ``lib.foo.__doc__`` contains the C signature now.  On
+  CPython you can say ``help(lib.foo)``, but for some reason
+  ``help(lib)`` (or ``help(lib.foo)`` on PyPy) is still useless; I
+  haven't yet figured out the hacks needed to convince ``pydoc`` to
+  show more.  (You can use ``dir(lib)`` but it is not most helpful.)
+
+* Yet another attempt at robustness of ``ffi.def_extern()`` against
+  CPython's interpreter shutdown logic.
+
+.. _`ffi.list_types()`: ref.html#ffi-list-types
+.. _`ffi.unpack()`: ref.html#ffi-unpack
+.. _`extern "Python+C"`: using.html#extern-python-c
+
+
+v1.5.2
+------
+
+* Fix 1.5.1 for Python 2.6.
+
+
+v1.5.1
+------
+
+* A few installation-time tweaks (thanks Stefano!)
+
+* Issue #245: Win32: ``__stdcall`` was never generated for
+  ``extern "Python"`` functions
+
+* Issue #246: trying to be more robust against CPython's fragile
+  interpreter shutdown logic
+
+
+v1.5.0
+------
+
+* Support for `using CFFI for embedding`__.
+
+.. __: embedding.html
+
+
+v1.4.2
+------
+
+Nothing changed from v1.4.1.
+
+
+v1.4.1
+------
+
+* Fix the compilation failure of cffi on CPython 3.5.0.  (3.5.1 works;
+  some detail changed that makes some underscore-starting macros
+  disappear from view of extension modules, and I worked around it,
+  thinking it changed in all 3.5 versions---but no: it was only in
+  3.5.1.)
+
+
+v1.4.0
+------
+
+* A `better way to do callbacks`__ has been added (faster and more
+  portable, and usually cleaner).  It is a mechanism for the
+  out-of-line API mode that replaces the dynamic creation of callback
+  objects (i.e. C functions that invoke Python) with the static
+  declaration in ``cdef()`` of which callbacks are needed.  This is
+  more C-like, in that you have to structure your code around the idea
+  that you get a fixed number of function pointers, instead of
+  creating them on-the-fly.
+
+* ``ffi.compile()`` now takes an optional ``verbose`` argument.  When
+  ``True``, distutils prints the calls to the compiler.
+
+* ``ffi.compile()`` used to fail if given ``sources`` with a path that
+  includes ``".."``.  Fixed.
+
+* ``ffi.init_once()`` added.  See docs__.
+
+* ``dir(lib)`` now works on libs returned by ``ffi.dlopen()`` too.
+
+* Cleaned up and modernized the content of the ``demo`` subdirectory
+  in the sources (thanks matti!).
+
+* ``ffi.new_handle()`` is now guaranteed to return unique ``void *``
+  values, even if called twice on the same object.  Previously, in
+  that case, CPython would return two ``cdata`` objects with the same
+  ``void *`` value.  This change is useful to add and remove handles
+  from a global dict (or set) without worrying about duplicates.
+  It already used to work like that on PyPy.
+  *This change can break code that used to work on CPython by relying
+  on the object to be kept alive by other means than keeping the
+  result of ffi.new_handle() alive.*  (The corresponding `warning in
+  the docs`__ of ``ffi.new_handle()`` has been here since v0.8!)
+
+.. __: using.html#extern-python
+.. __: ref.html#ffi-init-once
+.. __: ref.html#ffi-new-handle
+
+
+v1.3.1
+------
+
+* The optional typedefs (``bool``, ``FILE`` and all Windows types) were
+  not always available from out-of-line FFI objects.
+
+* Opaque enums are phased out from the cdefs: they now give a warning,
+  instead of (possibly wrongly) being assumed equal to ``unsigned int``.
+  Please report if you get a reasonable use case for them.
+
+* Some parsing details, notably ``volatile`` is passed along like
+  ``const`` and ``restrict``.  Also, older versions of pycparser
+  mis-parse some pointer-to-pointer types like ``char * const *``: the
+  "const" ends up at the wrong place.  Added a workaround.
+
+
+v1.3.0
+------
+
+* Added `ffi.memmove()`_.
+
+* Pull request #64: out-of-line API mode: we can now declare
+  floating-point types with ``typedef float... foo_t;``.  This only
+  works if ``foo_t`` is a float or a double, not ``long double``.
+
+* Issue #217: fix possible unaligned pointer manipulation, which crashes
+  on some architectures (64-bit, non-x86).
+
+* Issues #64 and #126: when using ``set_source()`` or ``verify()``,
+  the ``const`` and ``restrict`` keywords are copied from the cdef
+  to the generated C code; this fixes warnings by the C compiler.
+  It also fixes corner cases like ``typedef const int T; T a;``
+  which would previously not consider ``a`` as a constant.  (The
+  cdata objects themselves are never ``const``.)
+
+* Win32: support for ``__stdcall``.  For callbacks and function
+  pointers; regular C functions still don't need to have their `calling
+  convention`_ declared.
+
+* Windows: CPython 2.7 distutils doesn't work with Microsoft's official
+  Visual Studio for Python, and I'm told this is `not a bug`__.  For
+  ffi.compile(), we `removed a workaround`__ that was inside cffi but
+  which had unwanted side-effects.  Try saying ``import setuptools``
+  first, which patches distutils...
+
+.. _`ffi.memmove()`: ref.html#ffi-memmove
+.. __: https://bugs.python.org/issue23246
+.. __: https://bitbucket.org/cffi/cffi/pull-requests/65/remove-_hack_at_distutils-which-imports/diff
+.. _`calling convention`: using.html#windows-calling-conventions
+
+
+v1.2.1
+------
+
+Nothing changed from v1.2.0.
+
+
+v1.2.0
+------
+
+* Out-of-line mode: ``int a[][...];`` can be used to declare a structure
+  field or global variable which is, simultaneously, of total length
+  unknown to the C compiler (the ``a[]`` part) and each element is
+  itself an array of N integers, where the value of N *is* known to the
+  C compiler (the ``int`` and ``[...]`` parts around it).  Similarly,
+  ``int a[5][...];`` is supported (but probably less useful: remember
+  that in C it means ``int (a[5])[...];``).
+
+* PyPy: the ``lib.some_function`` objects were missing the attributes
+  ``__name__``, ``__module__`` and ``__doc__`` that are expected e.g. by
+  some decorators-management functions from ``functools``.
+
+* Out-of-line API mode: you can now do ``from _example.lib import x``
+  to import the name ``x`` from ``_example.lib``, even though the
+  ``lib`` object is not a standard module object.  (Also works in ``from
+  _example.lib import *``, but this is even more of a hack and will fail
+  if ``lib`` happens to declare a name called ``__all__``.  Note that
+  ``*`` excludes the global variables; only the functions and constants
+  make sense to import like this.)
+
+* ``lib.__dict__`` works again and gives you a copy of the
+  dict---assuming that ``lib`` has got no symbol called precisely
+  ``__dict__``.  (In general, it is safer to use ``dir(lib)``.)
+
+* Out-of-line API mode: global variables are now fetched on demand at
+  every access.  It fixes issue #212 (Windows DLL variables), and also
+  allows variables that are defined as dynamic macros (like ``errno``)
+  or ``__thread`` -local variables.  (This change might also tighten
+  the C compiler's check on the variables' type.)
+
+* Issue #209: dereferencing NULL pointers now raises RuntimeError
+  instead of segfaulting.  Meant as a debugging aid.  The check is
+  only for NULL: if you dereference random or dead pointers you might
+  still get segfaults.
+
+* Issue #152: callbacks__: added an argument ``ffi.callback(...,
+  onerror=...)``.  If the main callback function raises an exception
+  and ``onerror`` is provided, then ``onerror(exception, exc_value,
+  traceback)`` is called.  This is similar to writing a ``try:
+  except:`` in the main callback function, but in some cases (e.g. a
+  signal) an exception can occur at the very start of the callback
+  function---before it had time to enter the ``try: except:`` block.
+
+* Issue #115: added ``ffi.new_allocator()``, which officializes
+  support for `alternative allocators`__.
+
+.. __: using.html#callbacks
+.. __: ref.html#ffi-new-allocator
+
+
+v1.1.2
+------
+
+* ``ffi.gc()``: fixed a race condition in multithreaded programs
+  introduced in 1.1.1
+
+
+v1.1.1
+------
+
+* Out-of-line mode: ``ffi.string()``, ``ffi.buffer()`` and
+  ``ffi.getwinerror()`` didn't accept their arguments as keyword
+  arguments, unlike their in-line mode equivalent.  (It worked in PyPy.)
+
+* Out-of-line ABI mode: documented a restriction__ of ``ffi.dlopen()``
+  when compared to the in-line mode.
+
+* ``ffi.gc()``: when called several times with equal pointers, it was
+  accidentally registering only the last destructor, or even none at
+  all depending on details.  (It was correctly registering all of them
+  only in PyPy, and only with the out-of-line FFIs.)
+
+.. __: cdef.html#dlopen-note
+
+
+v1.1.0
+------
+
+* Out-of-line API mode: we can now declare integer types with
+  ``typedef int... foo_t;``.  The exact size and signedness of ``foo_t``
+  is figured out by the compiler.
+
+* Out-of-line API mode: we can now declare multidimensional arrays
+  (as fields or as globals) with ``int n[...][...]``.  Before, only the
+  outermost dimension would support the ``...`` syntax.
+
+* Out-of-line ABI mode: we now support any constant declaration,
+  instead of only integers whose value is given in the cdef.  Such "new"
+  constants, i.e. either non-integers or without a value given in the
+  cdef, must correspond to actual symbols in the lib.  At runtime they
+  are looked up the first time we access them.  This is useful if the
+  library defines ``extern const sometype somename;``.
+
+* ``ffi.addressof(lib, "func_name")`` now returns a regular cdata object
+  of type "pointer to function".  You can use it on any function from a
+  library in API mode (in ABI mode, all functions are already regular
+  cdata objects).  To support this, you need to recompile your cffi
+  modules.
+
+* Issue #198: in API mode, if you declare constants of a ``struct``
+  type, what you saw from lib.CONSTANT was corrupted.
+
+* Issue #196: ``ffi.set_source("package._ffi", None)`` would
+  incorrectly generate the Python source to ``package._ffi.py`` instead
+  of ``package/_ffi.py``.  Also fixed: in some cases, if the C file was
+  in ``build/foo.c``, the .o file would be put in ``build/build/foo.o``.
+
+
+v1.0.3
+------
+
+* Same as 1.0.2, apart from doc and test fixes on some platforms.
+
+
+v1.0.2
+------
+
+* Variadic C functions (ending in a "..." argument) were not supported
+  in the out-of-line ABI mode.  This was a bug---there was even a
+  (non-working) example__ doing exactly that!
+
+.. __: overview.html#out-of-line-abi-level
+
+
+v1.0.1
+------
+
+* ``ffi.set_source()`` crashed if passed a ``sources=[..]`` argument.
+  Fixed by chrippa on pull request #60.
+
+* Issue #193: if we use a struct between the first cdef() where it is
+  declared and another cdef() where its fields are defined, then this
+  definition was ignored.
+
+* Enums were buggy if you used too many "..." in their definition.
+
+
+v1.0.0
+------
+
+* The main news item is out-of-line module generation:
+
+  * `for ABI level`_, with ``ffi.dlopen()``
+
+  * `for API level`_, which used to be with ``ffi.verify()``, now deprecated
+
+* (this page will list what is new from all versions from 1.0.0
+  forward.)
+
+.. _`for ABI level`: overview.html#out-of-line-abi-level
+.. _`for API level`: overview.html#out-of-line-api-level
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..a97f028
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+pycparser
+pytest
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..0c9e0fc
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[metadata]
+license_file = LICENSE
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..f980590
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,250 @@
+import sys, os
+import subprocess
+import errno
+
+# on Windows we give up and always import setuptools early to fix things for us
+if sys.platform == "win32":
+    import setuptools
+
+
+sources = ['c/_cffi_backend.c']
+libraries = ['ffi']
+include_dirs = ['/usr/include/ffi',
+                '/usr/include/libffi']    # may be changed by pkg-config
+define_macros = []
+library_dirs = []
+extra_compile_args = []
+extra_link_args = []
+
+
+def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False):
+    pkg_config = os.environ.get('PKG_CONFIG','pkg-config')
+    try:
+        p = subprocess.Popen([pkg_config, option, 'libffi'],
+                             stdout=subprocess.PIPE)
+    except OSError as e:
+        if e.errno not in [errno.ENOENT, errno.EACCES]:
+            raise
+    else:
+        t = p.stdout.read().decode().strip()
+        p.stdout.close()
+        if p.wait() == 0:
+            res = t.split()
+            # '-I/usr/...' -> '/usr/...'
+            for x in res:
+                assert x.startswith(result_prefix)
+            res = [x[len(result_prefix):] for x in res]
+            #print 'PKG_CONFIG:', option, res
+            #
+            sysroot = sysroot and os.environ.get('PKG_CONFIG_SYSROOT_DIR', '')
+            if sysroot:
+                # old versions of pkg-config don't support this env var,
+                # so here we emulate its effect if needed
+                res = [path if path.startswith(sysroot)
+                            else sysroot + path
+                         for path in res]
+            #
+            resultlist[:] = res
+
+no_compiler_found = False
+def no_working_compiler_found():
+    sys.stderr.write("""
+    No working compiler found, or bogus compiler options passed to
+    the compiler from Python's standard "distutils" module.  See
+    the error messages above.  Likely, the problem is not related
+    to CFFI but generic to the setup.py of any Python package that
+    tries to compile C code.  (Hints: on OS/X 10.8, for errors about
+    -mno-fused-madd see http://stackoverflow.com/questions/22313407/
+    Otherwise, see https://wiki.python.org/moin/CompLangPython or
+    the IRC channel #python on irc.freenode.net.)
+
+    Trying to continue anyway.  If you are trying to install CFFI from
+    a build done in a different context, you can ignore this warning.
+    \n""")
+    global no_compiler_found
+    no_compiler_found = True
+
+def get_config():
+    from distutils.core import Distribution
+    from distutils.sysconfig import get_config_vars
+    get_config_vars()      # workaround for a bug of distutils, e.g. on OS/X
+    config = Distribution().get_command_obj('config')
+    return config
+
+def ask_supports_thread():
+    config = get_config()
+    ok = (sys.platform != 'win32' and
+          config.try_compile('__thread int some_threadlocal_variable_42;'))
+    if ok:
+        define_macros.append(('USE__THREAD', None))
+    else:
+        ok1 = config.try_compile('int some_regular_variable_42;')
+        if not ok1:
+            no_working_compiler_found()
+        else:
+            sys.stderr.write("Note: will not use '__thread' in the C code\n")
+            _safe_to_ignore()
+
+def ask_supports_sync_synchronize():
+    if sys.platform == 'win32' or no_compiler_found:
+        return
+    config = get_config()
+    ok = config.try_link('int main(void) { __sync_synchronize(); return 0; }')
+    if ok:
+        define_macros.append(('HAVE_SYNC_SYNCHRONIZE', None))
+    else:
+        sys.stderr.write("Note: will not use '__sync_synchronize()'"
+                         " in the C code\n")
+        _safe_to_ignore()
+
+def _safe_to_ignore():
+    sys.stderr.write("***** The above error message can be safely ignored.\n\n")
+
+def uses_msvc():
+    config = get_config()
+    return config.try_compile('#ifndef _MSC_VER\n#error "not MSVC"\n#endif')
+
+def use_pkg_config():
+    if sys.platform == 'darwin' and os.path.exists('/usr/local/bin/brew'):
+        use_homebrew_for_libffi()
+
+    _ask_pkg_config(include_dirs,       '--cflags-only-I', '-I', sysroot=True)
+    _ask_pkg_config(extra_compile_args, '--cflags-only-other')
+    _ask_pkg_config(library_dirs,       '--libs-only-L', '-L', sysroot=True)
+    _ask_pkg_config(extra_link_args,    '--libs-only-other')
+    _ask_pkg_config(libraries,          '--libs-only-l', '-l')
+
+def use_homebrew_for_libffi():
+    # We can build by setting:
+    # PKG_CONFIG_PATH = $(brew --prefix libffi)/lib/pkgconfig
+    with os.popen('brew --prefix libffi') as brew_prefix_cmd:
+        prefix = brew_prefix_cmd.read().strip()
+    pkgconfig = os.path.join(prefix, 'lib', 'pkgconfig')
+    os.environ['PKG_CONFIG_PATH'] = (
+        os.environ.get('PKG_CONFIG_PATH', '') + ':' + pkgconfig)
+
+
+if sys.platform == 'win32' and uses_msvc():
+    COMPILE_LIBFFI = 'c/libffi_msvc'    # from the CPython distribution
+else:
+    COMPILE_LIBFFI = None
+
+if COMPILE_LIBFFI:
+    assert os.path.isdir(COMPILE_LIBFFI), "directory not found!"
+    include_dirs[:] = [COMPILE_LIBFFI]
+    libraries[:] = []
+    _filenames = [filename.lower() for filename in os.listdir(COMPILE_LIBFFI)]
+    _filenames = [filename for filename in _filenames
+                           if filename.endswith('.c')]
+    if sys.maxsize > 2**32:
+        # 64-bit: unlist win32.c, and add instead win64.obj.  If the obj
+        # happens to get outdated at some point in the future, you need to
+        # rebuild it manually from win64.asm.
+        _filenames.remove('win32.c')
+        extra_link_args.append(os.path.join(COMPILE_LIBFFI, 'win64.obj'))
+    sources.extend(os.path.join(COMPILE_LIBFFI, filename)
+                   for filename in _filenames)
+else:
+    use_pkg_config()
+    ask_supports_thread()
+    ask_supports_sync_synchronize()
+
+if 'freebsd' in sys.platform:
+    include_dirs.append('/usr/local/include')
+    library_dirs.append('/usr/local/lib')
+
+if 'darwin' in sys.platform:
+    try:
+        p = subprocess.Popen(['xcrun', '--show-sdk-path'],
+                             stdout=subprocess.PIPE)
+    except OSError as e:
+        if e.errno not in [errno.ENOENT, errno.EACCES]:
+            raise
+    else:
+        t = p.stdout.read().decode().strip()
+        p.stdout.close()
+        if p.wait() == 0:
+            include_dirs.append(t + '/usr/include/ffi')
+
+
+
+if __name__ == '__main__':
+    from setuptools import setup, Distribution, Extension
+
+    class CFFIDistribution(Distribution):
+        def has_ext_modules(self):
+            # Event if we don't have extension modules (e.g. on PyPy) we want to
+            # claim that we do so that wheels get properly tagged as Python
+            # specific.  (thanks dstufft!)
+            return True
+
+    # On PyPy, cffi is preinstalled and it is not possible, at least for now,
+    # to install a different version.  We work around it by making the setup()
+    # arguments mostly empty in this case.
+    cpython = ('_cffi_backend' not in sys.builtin_module_names)
+
+    setup(
+        name='cffi',
+        description='Foreign Function Interface for Python calling C code.',
+        long_description="""
+CFFI
+====
+
+Foreign Function Interface for Python calling C code.
+Please see the `Documentation <http://cffi.readthedocs.org/>`_.
+
+Contact
+-------
+
+`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
+""",
+        version='1.12.2',
+        packages=['cffi'] if cpython else [],
+        package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', 
+                               '_embedding.h', '_cffi_errors.h']}
+                     if cpython else {},
+        zip_safe=False,
+
+        url='http://cffi.readthedocs.org',
+        author='Armin Rigo, Maciej Fijalkowski',
+        author_email='python-cffi@googlegroups.com',
+
+        license='MIT',
+
+        distclass=CFFIDistribution,
+        ext_modules=[Extension(
+            name='_cffi_backend',
+            include_dirs=include_dirs,
+            sources=sources,
+            libraries=libraries,
+            define_macros=define_macros,
+            library_dirs=library_dirs,
+            extra_compile_args=extra_compile_args,
+            extra_link_args=extra_link_args,
+        )] if cpython else [],
+
+        install_requires=[
+            'pycparser' if sys.version_info >= (2, 7) else 'pycparser<2.19',
+        ] if cpython else [],
+
+        entry_points = {
+            "distutils.setup_keywords": [
+                "cffi_modules = cffi.setuptools_ext:cffi_modules",
+            ],
+        },
+
+        classifiers=[
+            'Programming Language :: Python',
+            'Programming Language :: Python :: 2',
+            'Programming Language :: Python :: 2.6',
+            'Programming Language :: Python :: 2.7',
+            'Programming Language :: Python :: 3',
+            'Programming Language :: Python :: 3.2',
+            'Programming Language :: Python :: 3.3',
+            'Programming Language :: Python :: 3.4',
+            'Programming Language :: Python :: 3.5',
+            'Programming Language :: Python :: 3.6',
+            'Programming Language :: Python :: Implementation :: CPython',
+            'Programming Language :: Python :: Implementation :: PyPy',
+        ],
+    )
diff --git a/setup_base.py b/setup_base.py
new file mode 100644
index 0000000..667c7d5
--- /dev/null
+++ b/setup_base.py
@@ -0,0 +1,22 @@
+import sys, os
+
+
+from setup import include_dirs, sources, libraries, define_macros
+from setup import library_dirs, extra_compile_args, extra_link_args
+
+
+if __name__ == '__main__':
+    from distutils.core import setup
+    from distutils.extension import Extension
+    standard = '__pypy__' not in sys.builtin_module_names
+    setup(packages=['cffi'],
+          requires=['pycparser'],
+          ext_modules=[Extension(name = '_cffi_backend',
+                                 include_dirs=include_dirs,
+                                 sources=sources,
+                                 libraries=libraries,
+                                 define_macros=define_macros,
+                                 library_dirs=library_dirs,
+                                 extra_compile_args=extra_compile_args,
+                                 extra_link_args=extra_link_args,
+                                 )] * standard)
diff --git a/testing/__init__.py b/testing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/__init__.py
diff --git a/testing/cffi0/__init__.py b/testing/cffi0/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/cffi0/__init__.py
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
new file mode 100644
index 0000000..13a4c78
--- /dev/null
+++ b/testing/cffi0/backend_tests.py
@@ -0,0 +1,1990 @@
+import py
+import platform
+import sys, ctypes
+from cffi import FFI, CDefError, FFIError, VerificationMissing
+from testing.support import *
+
+SIZE_OF_INT   = ctypes.sizeof(ctypes.c_int)
+SIZE_OF_LONG  = ctypes.sizeof(ctypes.c_long)
+SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short)
+SIZE_OF_PTR   = ctypes.sizeof(ctypes.c_void_p)
+SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar)
+
+def needs_dlopen_none():
+    if sys.platform == 'win32' and sys.version_info >= (3,):
+        py.test.skip("dlopen(None) cannot work on Windows for Python 3")
+
+
+class BackendTests:
+
+    def test_integer_ranges(self):
+        ffi = FFI(backend=self.Backend())
+        for (c_type, size) in [('char', 1),
+                               ('short', 2),
+                               ('short int', 2),
+                               ('', 4),
+                               ('int', 4),
+                               ('long', SIZE_OF_LONG),
+                               ('long int', SIZE_OF_LONG),
+                               ('long long', 8),
+                               ('long long int', 8),
+                               ]:
+            for unsigned in [None, False, True]:
+                c_decl = {None: '',
+                          False: 'signed ',
+                          True: 'unsigned '}[unsigned] + c_type
+                if c_decl == 'char' or c_decl == '':
+                    continue
+                self._test_int_type(ffi, c_decl, size, unsigned)
+
+    def test_fixedsize_int(self):
+        ffi = FFI(backend=self.Backend())
+        for size in [1, 2, 4, 8]:
+            self._test_int_type(ffi, 'int%d_t' % (8*size), size, False)
+            self._test_int_type(ffi, 'uint%d_t' % (8*size), size, True)
+        self._test_int_type(ffi, 'intptr_t', SIZE_OF_PTR, False)
+        self._test_int_type(ffi, 'uintptr_t', SIZE_OF_PTR, True)
+        self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False)
+        self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True)
+        self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False)
+
+    def _test_int_type(self, ffi, c_decl, size, unsigned):
+        if unsigned:
+            min = 0
+            max = (1 << (8*size)) - 1
+        else:
+            min = -(1 << (8*size-1))
+            max = (1 << (8*size-1)) - 1
+        min = int(min)
+        max = int(max)
+        p = ffi.cast(c_decl, min)
+        assert p == min
+        assert hash(p) == hash(min)
+        assert bool(p) is bool(min)
+        assert int(p) == min
+        p = ffi.cast(c_decl, max)
+        assert int(p) == max
+        p = ffi.cast(c_decl, long(max))
+        assert int(p) == max
+        q = ffi.cast(c_decl, min - 1)
+        assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max
+        q = ffi.cast(c_decl, long(min - 1))
+        assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max
+        assert q == p
+        assert int(q) == int(p)
+        assert hash(q) == hash(p)
+        c_decl_ptr = '%s *' % c_decl
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, min - 1)
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, max + 1)
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1))
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(max + 1))
+        assert ffi.new(c_decl_ptr, min)[0] == min
+        assert ffi.new(c_decl_ptr, max)[0] == max
+        assert ffi.new(c_decl_ptr, long(min))[0] == min
+        assert ffi.new(c_decl_ptr, long(max))[0] == max
+
+    def test_new_unsupported_type(self):
+        ffi = FFI(backend=self.Backend())
+        e = py.test.raises(TypeError, ffi.new, "int")
+        assert str(e.value) == "expected a pointer or array ctype, got 'int'"
+
+    def test_new_single_integer(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *")     # similar to ffi.new("int[1]")
+        assert p[0] == 0
+        p[0] = -123
+        assert p[0] == -123
+        p = ffi.new("int *", -42)
+        assert p[0] == -42
+        assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT
+
+    def test_new_array_no_arg(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[10]")
+        # the object was zero-initialized:
+        for i in range(10):
+            assert p[i] == 0
+
+    def test_array_indexing(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[10]")
+        p[0] = 42
+        p[9] = 43
+        assert p[0] == 42
+        assert p[9] == 43
+        py.test.raises(IndexError, "p[10]")
+        py.test.raises(IndexError, "p[10] = 44")
+        py.test.raises(IndexError, "p[-1]")
+        py.test.raises(IndexError, "p[-1] = 44")
+
+    def test_new_array_args(self):
+        ffi = FFI(backend=self.Backend())
+        # this tries to be closer to C: where we say "int x[5] = {10, 20, ..}"
+        # then here we must enclose the items in a list
+        p = ffi.new("int[5]", [10, 20, 30, 40, 50])
+        assert p[0] == 10
+        assert p[1] == 20
+        assert p[2] == 30
+        assert p[3] == 40
+        assert p[4] == 50
+        p = ffi.new("int[4]", [25])
+        assert p[0] == 25
+        assert p[1] == 0     # follow C convention rather than LuaJIT's
+        assert p[2] == 0
+        assert p[3] == 0
+        p = ffi.new("int[4]", [ffi.cast("int", -5)])
+        assert p[0] == -5
+        assert repr(p) == "<cdata 'int[4]' owning %d bytes>" % (4*SIZE_OF_INT)
+
+    def test_new_array_varsize(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[]", 10)     # a single integer is the length
+        assert p[9] == 0
+        py.test.raises(IndexError, "p[10]")
+        #
+        py.test.raises(TypeError, ffi.new, "int[]")
+        #
+        p = ffi.new("int[]", [-6, -7])    # a list is all the items, like C
+        assert p[0] == -6
+        assert p[1] == -7
+        py.test.raises(IndexError, "p[2]")
+        assert repr(p) == "<cdata 'int[]' owning %d bytes>" % (2*SIZE_OF_INT)
+        #
+        p = ffi.new("int[]", 0)
+        py.test.raises(IndexError, "p[0]")
+        py.test.raises(ValueError, ffi.new, "int[]", -1)
+        assert repr(p) == "<cdata 'int[]' owning 0 bytes>"
+
+    def test_pointer_init(self):
+        ffi = FFI(backend=self.Backend())
+        n = ffi.new("int *", 24)
+        a = ffi.new("int *[10]", [ffi.NULL, ffi.NULL, n, n, ffi.NULL])
+        for i in range(10):
+            if i not in (2, 3):
+                assert a[i] == ffi.NULL
+        assert a[2] == a[3] == n
+
+    def test_cannot_cast(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short int[10]")
+        e = py.test.raises(TypeError, ffi.new, "long int **", a)
+        msg = str(e.value)
+        assert "'short[10]'" in msg and "'long *'" in msg
+
+    def test_new_pointer_to_array(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("int[4]", [100, 102, 104, 106])
+        p = ffi.new("int **", a)
+        assert p[0] == ffi.cast("int *", a)
+        assert p[0][2] == 104
+        p = ffi.cast("int *", a)
+        assert p[0] == 100
+        assert p[1] == 102
+        assert p[2] == 104
+        assert p[3] == 106
+        # keepalive: a
+
+    def test_pointer_direct(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.cast("int*", 0)
+        assert p is not None
+        assert bool(p) is False
+        assert p == ffi.cast("int*", 0)
+        assert p != None
+        assert repr(p) == "<cdata 'int *' NULL>"
+        a = ffi.new("int[]", [123, 456])
+        p = ffi.cast("int*", a)
+        assert bool(p) is True
+        assert p == ffi.cast("int*", a)
+        assert p != ffi.cast("int*", 0)
+        assert p[0] == 123
+        assert p[1] == 456
+
+    def test_repr(self):
+        typerepr = self.TypeRepr
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { short a, b, c; };")
+        p = ffi.cast("short unsigned int", 0)
+        assert repr(p) == "<cdata 'unsigned short' 0>"
+        assert repr(ffi.typeof(p)) == typerepr % "unsigned short"
+        p = ffi.cast("unsigned short int", 0)
+        assert repr(p) == "<cdata 'unsigned short' 0>"
+        assert repr(ffi.typeof(p)) == typerepr % "unsigned short"
+        p = ffi.cast("int*", 0)
+        assert repr(p) == "<cdata 'int *' NULL>"
+        assert repr(ffi.typeof(p)) == typerepr % "int *"
+        #
+        p = ffi.new("int*")
+        assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT
+        assert repr(ffi.typeof(p)) == typerepr % "int *"
+        p = ffi.new("int**")
+        assert repr(p) == "<cdata 'int * *' owning %d bytes>" % SIZE_OF_PTR
+        assert repr(ffi.typeof(p)) == typerepr % "int * *"
+        p = ffi.new("int [2]")
+        assert repr(p) == "<cdata 'int[2]' owning %d bytes>" % (2*SIZE_OF_INT)
+        assert repr(ffi.typeof(p)) == typerepr % "int[2]"
+        p = ffi.new("int*[2][3]")
+        assert repr(p) == "<cdata 'int *[2][3]' owning %d bytes>" % (
+            6*SIZE_OF_PTR)
+        assert repr(ffi.typeof(p)) == typerepr % "int *[2][3]"
+        p = ffi.new("struct foo *")
+        assert repr(p) == "<cdata 'struct foo *' owning %d bytes>" % (
+            3*SIZE_OF_SHORT)
+        assert repr(ffi.typeof(p)) == typerepr % "struct foo *"
+        #
+        q = ffi.cast("short", -123)
+        assert repr(q) == "<cdata 'short' -123>"
+        assert repr(ffi.typeof(q)) == typerepr % "short"
+        p = ffi.new("int*")
+        q = ffi.cast("short*", p)
+        assert repr(q).startswith("<cdata 'short *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "short *"
+        p = ffi.new("int [2]")
+        q = ffi.cast("int*", p)
+        assert repr(q).startswith("<cdata 'int *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "int *"
+        p = ffi.new("struct foo*")
+        q = ffi.cast("struct foo *", p)
+        assert repr(q).startswith("<cdata 'struct foo *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "struct foo *"
+        prevrepr = repr(q)
+        q = q[0]
+        assert repr(q) == prevrepr.replace(' *', ' &')
+        assert repr(ffi.typeof(q)) == typerepr % "struct foo"
+
+    def test_new_array_of_array(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[3][4]")
+        p[0][0] = 10
+        p[2][3] = 33
+        assert p[0][0] == 10
+        assert p[2][3] == 33
+        py.test.raises(IndexError, "p[1][-1]")
+
+    def test_constructor_array_of_array(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[3][2]", [[10, 11], [12, 13], [14, 15]])
+        assert p[2][1] == 15
+
+    def test_new_array_of_pointer_1(self):
+        ffi = FFI(backend=self.Backend())
+        n = ffi.new("int*", 99)
+        p = ffi.new("int*[4]")
+        p[3] = n
+        a = p[3]
+        assert repr(a).startswith("<cdata 'int *' 0x")
+        assert a[0] == 99
+
+    def test_new_array_of_pointer_2(self):
+        ffi = FFI(backend=self.Backend())
+        n = ffi.new("int[1]", [99])
+        p = ffi.new("int*[4]")
+        p[3] = n
+        a = p[3]
+        assert repr(a).startswith("<cdata 'int *' 0x")
+        assert a[0] == 99
+
+    def test_char(self):
+        ffi = FFI(backend=self.Backend())
+        assert ffi.new("char*", b"\xff")[0] == b'\xff'
+        assert ffi.new("char*")[0] == b'\x00'
+        assert int(ffi.cast("char", 300)) == 300 - 256
+        assert not bool(ffi.cast("char", 0))
+        assert bool(ffi.cast("char", 1))
+        assert bool(ffi.cast("char", 255))
+        py.test.raises(TypeError, ffi.new, "char*", 32)
+        py.test.raises(TypeError, ffi.new, "char*", u+"x")
+        py.test.raises(TypeError, ffi.new, "char*", b"foo")
+        #
+        p = ffi.new("char[]", [b'a', b'b', b'\x9c'])
+        assert len(p) == 3
+        assert p[0] == b'a'
+        assert p[1] == b'b'
+        assert p[2] == b'\x9c'
+        p[0] = b'\xff'
+        assert p[0] == b'\xff'
+        p = ffi.new("char[]", b"abcd")
+        assert len(p) == 5
+        assert p[4] == b'\x00'    # like in C, with:  char[] p = "abcd";
+        #
+        p = ffi.new("char[4]", b"ab")
+        assert len(p) == 4
+        assert [p[i] for i in range(4)] == [b'a', b'b', b'\x00', b'\x00']
+        p = ffi.new("char[2]", b"ab")
+        assert len(p) == 2
+        assert [p[i] for i in range(2)] == [b'a', b'b']
+        py.test.raises(IndexError, ffi.new, "char[2]", b"abc")
+
+    def check_wchar_t(self, ffi):
+        try:
+            ffi.cast("wchar_t", 0)
+        except NotImplementedError:
+            py.test.skip("NotImplementedError: wchar_t")
+
+    def test_wchar_t(self):
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        assert ffi.new("wchar_t*", u+'x')[0] == u+'x'
+        assert ffi.new("wchar_t*", u+'\u1234')[0] == u+'\u1234'
+        if SIZE_OF_WCHAR > 2:
+            assert ffi.new("wchar_t*", u+'\U00012345')[0] == u+'\U00012345'
+        else:
+            py.test.raises(TypeError, ffi.new, "wchar_t*", u+'\U00012345')
+        assert ffi.new("wchar_t*")[0] == u+'\x00'
+        assert int(ffi.cast("wchar_t", 300)) == 300
+        assert not bool(ffi.cast("wchar_t", 0))
+        assert bool(ffi.cast("wchar_t", 1))
+        assert bool(ffi.cast("wchar_t", 65535))
+        if SIZE_OF_WCHAR > 2:
+            assert bool(ffi.cast("wchar_t", 65536))
+        py.test.raises(TypeError, ffi.new, "wchar_t*", 32)
+        py.test.raises(TypeError, ffi.new, "wchar_t*", "foo")
+        #
+        p = ffi.new("wchar_t[]", [u+'a', u+'b', u+'\u1234'])
+        assert len(p) == 3
+        assert p[0] == u+'a'
+        assert p[1] == u+'b' and type(p[1]) is unicode
+        assert p[2] == u+'\u1234'
+        p[0] = u+'x'
+        assert p[0] == u+'x' and type(p[0]) is unicode
+        p[1] = u+'\u1357'
+        assert p[1] == u+'\u1357'
+        p = ffi.new("wchar_t[]", u+"abcd")
+        assert len(p) == 5
+        assert p[4] == u+'\x00'
+        p = ffi.new("wchar_t[]", u+"a\u1234b")
+        assert len(p) == 4
+        assert p[1] == u+'\u1234'
+        #
+        p = ffi.new("wchar_t[]", u+'\U00023456')
+        if SIZE_OF_WCHAR == 2:
+            assert len(p) == 3
+            assert p[0] == u+'\ud84d'
+            assert p[1] == u+'\udc56'
+            assert p[2] == u+'\x00'
+        else:
+            assert len(p) == 2
+            assert p[0] == u+'\U00023456'
+            assert p[1] == u+'\x00'
+        #
+        p = ffi.new("wchar_t[4]", u+"ab")
+        assert len(p) == 4
+        assert [p[i] for i in range(4)] == [u+'a', u+'b', u+'\x00', u+'\x00']
+        p = ffi.new("wchar_t[2]", u+"ab")
+        assert len(p) == 2
+        assert [p[i] for i in range(2)] == [u+'a', u+'b']
+        py.test.raises(IndexError, ffi.new, "wchar_t[2]", u+"abc")
+
+    def test_none_as_null_doesnt_work(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int*[1]")
+        assert p[0] is not None
+        assert p[0] != None
+        assert p[0] == ffi.NULL
+        assert repr(p[0]) == "<cdata 'int *' NULL>"
+        #
+        n = ffi.new("int*", 99)
+        p = ffi.new("int*[]", [n])
+        assert p[0][0] == 99
+        py.test.raises(TypeError, "p[0] = None")
+        p[0] = ffi.NULL
+        assert p[0] == ffi.NULL
+
+    def test_float(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("float[]", [-2, -2.5])
+        assert p[0] == -2.0
+        assert p[1] == -2.5
+        p[1] += 17.75
+        assert p[1] == 15.25
+        #
+        p = ffi.new("float*", 15.75)
+        assert p[0] == 15.75
+        py.test.raises(TypeError, int, p)
+        py.test.raises(TypeError, float, p)
+        p[0] = 0.0
+        assert bool(p) is True
+        #
+        p = ffi.new("float*", 1.1)
+        f = p[0]
+        assert f != 1.1      # because of rounding effect
+        assert abs(f - 1.1) < 1E-7
+        #
+        INF = 1E200 * 1E200
+        assert 1E200 != INF
+        p[0] = 1E200
+        assert p[0] == INF     # infinite, not enough precision
+
+    def test_struct_simple(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a; short b, c; };")
+        s = ffi.new("struct foo*")
+        assert s.a == s.b == s.c == 0
+        s.b = -23
+        assert s.b == -23
+        py.test.raises(OverflowError, "s.b = 32768")
+        #
+        s = ffi.new("struct foo*", [-2, -3])
+        assert s.a == -2
+        assert s.b == -3
+        assert s.c == 0
+        py.test.raises((AttributeError, TypeError), "del s.a")
+        assert repr(s) == "<cdata 'struct foo *' owning %d bytes>" % (
+            SIZE_OF_INT + 2 * SIZE_OF_SHORT)
+        #
+        py.test.raises(ValueError, ffi.new, "struct foo*", [1, 2, 3, 4])
+
+    def test_constructor_struct_from_dict(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a; short b, c; };")
+        s = ffi.new("struct foo*", {'b': 123, 'c': 456})
+        assert s.a == 0
+        assert s.b == 123
+        assert s.c == 456
+        py.test.raises(KeyError, ffi.new, "struct foo*", {'d': 456})
+
+    def test_struct_pointer(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a; short b, c; };")
+        s = ffi.new("struct foo*")
+        assert s[0].a == s[0].b == s[0].c == 0
+        s[0].b = -23
+        assert s[0].b == s.b == -23
+        py.test.raises(OverflowError, "s[0].b = -32769")
+        py.test.raises(IndexError, "s[1]")
+
+    def test_struct_opaque(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new, "struct baz*")
+        p = ffi.new("struct baz **")    # this works
+        assert p[0] == ffi.NULL
+
+    def test_pointer_to_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a; short b, c; };")
+        s = ffi.new("struct foo *")
+        s.a = -42
+        assert s[0].a == -42
+        p = ffi.new("struct foo **", s)
+        assert p[0].a == -42
+        assert p[0][0].a == -42
+        p[0].a = -43
+        assert s.a == -43
+        assert s[0].a == -43
+        p[0][0].a = -44
+        assert s.a == -44
+        assert s[0].a == -44
+        s.a = -45
+        assert p[0].a == -45
+        assert p[0][0].a == -45
+        s[0].a = -46
+        assert p[0].a == -46
+        assert p[0][0].a == -46
+
+    def test_constructor_struct_of_array(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a[2]; char b[3]; };")
+        s = ffi.new("struct foo *", [[10, 11], [b'a', b'b', b'c']])
+        assert s.a[1] == 11
+        assert s.b[2] == b'c'
+        s.b[1] = b'X'
+        assert s.b[0] == b'a'
+        assert s.b[1] == b'X'
+        assert s.b[2] == b'c'
+
+    def test_recursive_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int value; struct foo *next; };")
+        s = ffi.new("struct foo*")
+        t = ffi.new("struct foo*")
+        s.value = 123
+        s.next = t
+        t.value = 456
+        assert s.value == 123
+        assert s.next.value == 456
+
+    def test_union_simple(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("union foo { int a; short b, c; };")
+        u = ffi.new("union foo*")
+        assert u.a == u.b == u.c == 0
+        u.b = -23
+        assert u.b == -23
+        assert u.a != 0
+        py.test.raises(OverflowError, "u.b = 32768")
+        #
+        u = ffi.new("union foo*", [-2])
+        assert u.a == -2
+        py.test.raises((AttributeError, TypeError), "del u.a")
+        assert repr(u) == "<cdata 'union foo *' owning %d bytes>" % SIZE_OF_INT
+
+    def test_union_opaque(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new, "union baz *")
+        u = ffi.new("union baz **")   # this works
+        assert u[0] == ffi.NULL
+
+    def test_union_initializer(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("union foo { char a; int b; };")
+        py.test.raises(TypeError, ffi.new, "union foo*", b'A')
+        py.test.raises(TypeError, ffi.new, "union foo*", 5)
+        py.test.raises(ValueError, ffi.new, "union foo*", [b'A', 5])
+        u = ffi.new("union foo*", [b'A'])
+        assert u.a == b'A'
+        py.test.raises(TypeError, ffi.new, "union foo*", [1005])
+        u = ffi.new("union foo*", {'b': 12345})
+        assert u.b == 12345
+        u = ffi.new("union foo*", [])
+        assert u.a == b'\x00'
+        assert u.b == 0
+
+    def test_sizeof_type(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo { int a; short b, c, d; };
+            union foo { int a; short b, c, d; };
+        """)
+        for c_type, expected_size in [
+            ('char', 1),
+            ('unsigned int', 4),
+            ('char *', SIZE_OF_PTR),
+            ('int[5]', 20),
+            ('struct foo', 12),
+            ('union foo', 4),
+            ]:
+            size = ffi.sizeof(c_type)
+            assert size == expected_size, (size, expected_size, ctype)
+
+    def test_sizeof_cdata(self):
+        ffi = FFI(backend=self.Backend())
+        assert ffi.sizeof(ffi.new("short*")) == SIZE_OF_PTR
+        assert ffi.sizeof(ffi.cast("short", 123)) == SIZE_OF_SHORT
+        #
+        a = ffi.new("int[]", [10, 11, 12, 13, 14])
+        assert len(a) == 5
+        assert ffi.sizeof(a) == 5 * SIZE_OF_INT
+
+    def test_string_from_char_pointer(self):
+        ffi = FFI(backend=self.Backend())
+        x = ffi.new("char*", b"x")
+        assert str(x) == repr(x)
+        assert ffi.string(x) == b"x"
+        assert ffi.string(ffi.new("char*", b"\x00")) == b""
+        py.test.raises(TypeError, ffi.new, "char*", unicode("foo"))
+
+    def test_unicode_from_wchar_pointer(self):
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        x = ffi.new("wchar_t*", u+"x")
+        assert unicode(x) == unicode(repr(x))
+        assert ffi.string(x) == u+"x"
+        assert ffi.string(ffi.new("wchar_t*", u+"\x00")) == u+""
+
+    def test_string_from_char_array(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("char[]", b"hello.")
+        p[5] = b'!'
+        assert ffi.string(p) == b"hello!"
+        p[6] = b'?'
+        assert ffi.string(p) == b"hello!?"
+        p[3] = b'\x00'
+        assert ffi.string(p) == b"hel"
+        assert ffi.string(p, 2) == b"he"
+        py.test.raises(IndexError, "p[7] = b'X'")
+        #
+        a = ffi.new("char[]", b"hello\x00world")
+        assert len(a) == 12
+        p = ffi.cast("char *", a)
+        assert ffi.string(p) == b'hello'
+
+    def test_string_from_wchar_array(self):
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        assert ffi.string(ffi.cast("wchar_t", "x")) == u+"x"
+        assert ffi.string(ffi.cast("wchar_t", u+"x")) == u+"x"
+        x = ffi.cast("wchar_t", "x")
+        assert str(x) == repr(x)
+        assert ffi.string(x) == u+"x"
+        #
+        p = ffi.new("wchar_t[]", u+"hello.")
+        p[5] = u+'!'
+        assert ffi.string(p) == u+"hello!"
+        p[6] = u+'\u04d2'
+        assert ffi.string(p) == u+"hello!\u04d2"
+        p[3] = u+'\x00'
+        assert ffi.string(p) == u+"hel"
+        assert ffi.string(p, 123) == u+"hel"
+        py.test.raises(IndexError, "p[7] = u+'X'")
+        #
+        a = ffi.new("wchar_t[]", u+"hello\x00world")
+        assert len(a) == 12
+        p = ffi.cast("wchar_t *", a)
+        assert ffi.string(p) == u+'hello'
+        assert ffi.string(p, 123) == u+'hello'
+        assert ffi.string(p, 5) == u+'hello'
+        assert ffi.string(p, 2) == u+'he'
+
+    def test_fetch_const_char_p_field(self):
+        # 'const' is ignored so far
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { const char *name; };")
+        t = ffi.new("const char[]", b"testing")
+        s = ffi.new("struct foo*", [t])
+        assert type(s.name) not in (bytes, str, unicode)
+        assert ffi.string(s.name) == b"testing"
+        py.test.raises(TypeError, "s.name = None")
+        s.name = ffi.NULL
+        assert s.name == ffi.NULL
+
+    def test_fetch_const_wchar_p_field(self):
+        # 'const' is ignored so far
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        ffi.cdef("struct foo { const wchar_t *name; };")
+        t = ffi.new("const wchar_t[]", u+"testing")
+        s = ffi.new("struct foo*", [t])
+        assert type(s.name) not in (bytes, str, unicode)
+        assert ffi.string(s.name) == u+"testing"
+        s.name = ffi.NULL
+        assert s.name == ffi.NULL
+
+    def test_voidp(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new, "void*")
+        p = ffi.new("void **")
+        assert p[0] == ffi.NULL
+        a = ffi.new("int[]", [10, 11, 12])
+        p = ffi.new("void **", a)
+        vp = p[0]
+        py.test.raises(TypeError, "vp[0]")
+        py.test.raises(TypeError, ffi.new, "short **", a)
+        #
+        ffi.cdef("struct foo { void *p; int *q; short *r; };")
+        s = ffi.new("struct foo *")
+        s.p = a    # works
+        s.q = a    # works
+        py.test.raises(TypeError, "s.r = a")    # fails
+        b = ffi.cast("int *", a)
+        s.p = b    # works
+        s.q = b    # works
+        py.test.raises(TypeError, "s.r = b")    # fails
+
+    def test_functionptr_simple(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0)
+        def cb(n):
+            return n + 1
+        cb.__qualname__ = 'cb'
+        p = ffi.callback("int(*)(int)", cb)
+        res = p(41)     # calling an 'int(*)(int)', i.e. a function pointer
+        assert res == 42 and type(res) is int
+        res = p(ffi.cast("int", -41))
+        assert res == -40 and type(res) is int
+        assert repr(p).startswith(
+            "<cdata 'int(*)(int)' calling <function cb at 0x")
+        assert ffi.typeof(p) is ffi.typeof("int(*)(int)")
+        q = ffi.new("int(**)(int)", p)
+        assert repr(q) == "<cdata 'int(* *)(int)' owning %d bytes>" % (
+            SIZE_OF_PTR)
+        py.test.raises(TypeError, "q(43)")
+        res = q[0](43)
+        assert res == 44
+        q = ffi.cast("int(*)(int)", p)
+        assert repr(q).startswith("<cdata 'int(*)(int)' 0x")
+        res = q(45)
+        assert res == 46
+
+    def test_functionptr_advanced(self):
+        ffi = FFI(backend=self.Backend())
+        t = ffi.typeof("int(*(*)(int))(int)")
+        assert repr(t) == self.TypeRepr % "int(*(*)(int))(int)"
+
+    def test_functionptr_voidptr_return(self):
+        ffi = FFI(backend=self.Backend())
+        def cb():
+            return ffi.NULL
+        p = ffi.callback("void*(*)()", cb)
+        res = p()
+        assert res is not None
+        assert res == ffi.NULL
+        int_ptr = ffi.new('int*')
+        void_ptr = ffi.cast('void*', int_ptr)
+        def cb():
+            return void_ptr
+        p = ffi.callback("void*(*)()", cb)
+        res = p()
+        assert res == void_ptr
+
+    def test_functionptr_intptr_return(self):
+        ffi = FFI(backend=self.Backend())
+        def cb():
+            return ffi.NULL
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert res == ffi.NULL
+        int_ptr = ffi.new('int*')
+        def cb():
+            return int_ptr
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert repr(res).startswith("<cdata 'int *' 0x")
+        assert res == int_ptr
+        int_array_ptr = ffi.new('int[1]')
+        def cb():
+            return int_array_ptr
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert repr(res).startswith("<cdata 'int *' 0x")
+        assert res == int_array_ptr
+
+    def test_functionptr_void_return(self):
+        ffi = FFI(backend=self.Backend())
+        def foo():
+            pass
+        foo_cb = ffi.callback("void foo()", foo)
+        result = foo_cb()
+        assert result is None
+
+    def test_char_cast(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.cast("int", b'\x01')
+        assert ffi.typeof(p) is ffi.typeof("int")
+        assert int(p) == 1
+        p = ffi.cast("int", ffi.cast("char", b"a"))
+        assert int(p) == ord("a")
+        p = ffi.cast("int", ffi.cast("char", b"\x80"))
+        assert int(p) == 0x80     # "char" is considered unsigned in this case
+        p = ffi.cast("int", b"\x81")
+        assert int(p) == 0x81
+
+    def test_wchar_cast(self):
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        p = ffi.cast("int", ffi.cast("wchar_t", u+'\u1234'))
+        assert int(p) == 0x1234
+        p = ffi.cast("long long", ffi.cast("wchar_t", -1))
+        if SIZE_OF_WCHAR == 2:      # 2 bytes, unsigned
+            assert int(p) == 0xffff
+        elif (sys.platform.startswith('linux') and
+              platform.machine().startswith('x86')):   # known to be signed
+            assert int(p) == -1
+        else:                     # in general, it can be either signed or not
+            assert int(p) in [-1, 0xffffffff]  # e.g. on arm, both cases occur
+        p = ffi.cast("int", u+'\u1234')
+        assert int(p) == 0x1234
+
+    def test_cast_array_to_charp(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        p = ffi.cast("char*", a)
+        data = b''.join([p[i] for i in range(4)])
+        if sys.byteorder == 'little':
+            assert data == b'\x34\x12\x78\x56'
+        else:
+            assert data == b'\x12\x34\x56\x78'
+
+    def test_cast_between_pointers(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        p = ffi.cast("short*", a)
+        p2 = ffi.cast("int*", p)
+        q = ffi.cast("char*", p2)
+        data = b''.join([q[i] for i in range(4)])
+        if sys.byteorder == 'little':
+            assert data == b'\x34\x12\x78\x56'
+        else:
+            assert data == b'\x12\x34\x56\x78'
+
+    def test_cast_pointer_and_int(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        l1 = ffi.cast("intptr_t", a)
+        p = ffi.cast("short*", a)
+        l2 = ffi.cast("intptr_t", p)
+        assert int(l1) == int(l2) != 0
+        q = ffi.cast("short*", l1)
+        assert q == ffi.cast("short*", int(l1))
+        assert q[0] == 0x1234
+        assert int(ffi.cast("intptr_t", ffi.NULL)) == 0
+
+    def test_cast_functionptr_and_int(self):
+        ffi = FFI(backend=self.Backend())
+        def cb(n):
+            return n + 1
+        a = ffi.callback("int(*)(int)", cb)
+        p = ffi.cast("void *", a)
+        assert p
+        b = ffi.cast("int(*)(int)", p)
+        assert b(41) == 42
+        assert a == b
+        assert hash(a) == hash(b)
+
+    def test_callback_crash(self):
+        ffi = FFI(backend=self.Backend())
+        def cb(n):
+            raise Exception
+        a = ffi.callback("int(*)(int)", cb, error=42)
+        res = a(1)    # and the error reported to stderr
+        assert res == 42
+
+    def test_structptr_argument(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int a, b; };")
+        def cb(p):
+            return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b
+        a = ffi.callback("int(*)(struct foo_s[])", cb)
+        res = a([[5, 6], {'a': 7, 'b': 8}])
+        assert res == 5678
+        res = a([[5], {'b': 8}])
+        assert res == 5008
+
+    def test_array_argument_as_list(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int a, b; };")
+        seen = []
+        def cb(argv):
+            seen.append(ffi.string(argv[0]))
+            seen.append(ffi.string(argv[1]))
+        a = ffi.callback("void(*)(char *[])", cb)
+        a([ffi.new("char[]", b"foobar"), ffi.new("char[]", b"baz")])
+        assert seen == [b"foobar", b"baz"]
+
+    def test_cast_float(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.cast("float", 12)
+        assert float(a) == 12.0
+        a = ffi.cast("float", 12.5)
+        assert float(a) == 12.5
+        a = ffi.cast("float", b"A")
+        assert float(a) == ord("A")
+        a = ffi.cast("int", 12.9)
+        assert int(a) == 12
+        a = ffi.cast("char", 66.9 + 256)
+        assert ffi.string(a) == b"B"
+        #
+        a = ffi.cast("float", ffi.cast("int", 12))
+        assert float(a) == 12.0
+        a = ffi.cast("float", ffi.cast("double", 12.5))
+        assert float(a) == 12.5
+        a = ffi.cast("float", ffi.cast("char", b"A"))
+        assert float(a) == ord("A")
+        a = ffi.cast("int", ffi.cast("double", 12.9))
+        assert int(a) == 12
+        a = ffi.cast("char", ffi.cast("double", 66.9 + 256))
+        assert ffi.string(a) == b"B"
+
+    def test_enum(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("enum foo { A0, B0, CC0, D0 };")
+        assert ffi.string(ffi.cast("enum foo", 0)) == "A0"
+        assert ffi.string(ffi.cast("enum foo", 2)) == "CC0"
+        assert ffi.string(ffi.cast("enum foo", 3)) == "D0"
+        assert ffi.string(ffi.cast("enum foo", 4)) == "4"
+        ffi.cdef("enum bar { A1, B1=-2, CC1, D1, E1 };")
+        assert ffi.string(ffi.cast("enum bar", 0)) == "A1"
+        assert ffi.string(ffi.cast("enum bar", -2)) == "B1"
+        assert ffi.string(ffi.cast("enum bar", -1)) == "CC1"
+        assert ffi.string(ffi.cast("enum bar", 1)) == "E1"
+        assert ffi.cast("enum bar", -2) == ffi.cast("enum bar", -2)
+        assert ffi.cast("enum foo", 0) == ffi.cast("enum bar", 0)
+        assert ffi.cast("enum bar", 0) == ffi.cast("int", 0)
+        assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC1>"
+        assert repr(ffi.cast("enum foo", -1)) == (  # enums are unsigned, if
+            "<cdata 'enum foo' 4294967295>")        # they contain no neg value
+        ffi.cdef("enum baz { A2=0x1000, B2=0x2000 };")
+        assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A2"
+        assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2"
+
+    def test_enum_in_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("enum foo { A, B, C, D }; struct bar { enum foo e; };")
+        s = ffi.new("struct bar *")
+        s.e = 0
+        assert s.e == 0
+        s.e = 3
+        assert s.e == 3
+        assert s[0].e == 3
+        s[0].e = 2
+        assert s.e == 2
+        assert s[0].e == 2
+        s.e = ffi.cast("enum foo", -1)
+        assert s.e == 4294967295
+        assert s[0].e == 4294967295
+        s.e = s.e
+        py.test.raises(TypeError, "s.e = 'B'")
+        py.test.raises(TypeError, "s.e = '2'")
+        py.test.raises(TypeError, "s.e = '#2'")
+        py.test.raises(TypeError, "s.e = '#7'")
+
+    def test_enum_non_contiguous(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("enum foo { A, B=42, C };")
+        assert ffi.string(ffi.cast("enum foo", 0)) == "A"
+        assert ffi.string(ffi.cast("enum foo", 42)) == "B"
+        assert ffi.string(ffi.cast("enum foo", 43)) == "C"
+        invalid_value = ffi.cast("enum foo", 2)
+        assert int(invalid_value) == 2
+        assert ffi.string(invalid_value) == "2"
+
+    def test_enum_char_hex_oct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef(r"enum foo{A='!', B='\'', C=0x10, D=010, E=- 0x10, F=-010};")
+        assert ffi.string(ffi.cast("enum foo", ord('!'))) == "A"
+        assert ffi.string(ffi.cast("enum foo", ord("'"))) == "B"
+        assert ffi.string(ffi.cast("enum foo", 16)) == "C"
+        assert ffi.string(ffi.cast("enum foo", 8)) == "D"
+        assert ffi.string(ffi.cast("enum foo", -16)) == "E"
+        assert ffi.string(ffi.cast("enum foo", -8)) == "F"
+
+    def test_enum_partial(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef(r"enum foo {A, ...}; enum bar { B, C };")
+        needs_dlopen_none()
+        lib = ffi.dlopen(None)
+        assert lib.B == 0
+        py.test.raises(VerificationMissing, getattr, lib, "A")
+        assert lib.C == 1
+
+    def test_array_of_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a, b; };")
+        s = ffi.new("struct foo[1]")
+        py.test.raises(AttributeError, 's.b')
+        py.test.raises(AttributeError, 's.b = 412')
+        s[0].b = 412
+        assert s[0].b == 412
+        py.test.raises(IndexError, 's[1]')
+
+    def test_pointer_to_array(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int(**)[5]")
+        assert repr(p) == "<cdata 'int(* *)[5]' owning %d bytes>" % SIZE_OF_PTR
+
+    def test_iterate_array(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("char[]", b"hello")
+        assert list(a) == [b"h", b"e", b"l", b"l", b"o", b"\0"]
+        assert list(iter(a)) == [b"h", b"e", b"l", b"l", b"o", b"\0"]
+        #
+        py.test.raises(TypeError, iter, ffi.cast("char *", a))
+        py.test.raises(TypeError, list, ffi.cast("char *", a))
+        py.test.raises(TypeError, iter, ffi.new("int *"))
+        py.test.raises(TypeError, list, ffi.new("int *"))
+
+    def test_offsetof(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a, b, c; };")
+        assert ffi.offsetof("struct foo", "a") == 0
+        assert ffi.offsetof("struct foo", "b") == 4
+        assert ffi.offsetof("struct foo", "c") == 8
+
+    def test_offsetof_nested(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a, b, c; };"
+                 "struct bar { struct foo d, e; };")
+        assert ffi.offsetof("struct bar", "e") == 12
+        py.test.raises(KeyError, ffi.offsetof, "struct bar", "e.a")
+        assert ffi.offsetof("struct bar", "e", "a") == 12
+        assert ffi.offsetof("struct bar", "e", "b") == 16
+        assert ffi.offsetof("struct bar", "e", "c") == 20
+
+    def test_offsetof_array(self):
+        ffi = FFI(backend=self.Backend())
+        assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int")
+        assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int")
+        ffi.cdef("struct bar { int a, b; int c[99]; };")
+        assert ffi.offsetof("struct bar", "c") == 2 * ffi.sizeof("int")
+        assert ffi.offsetof("struct bar", "c", 0) == 2 * ffi.sizeof("int")
+        assert ffi.offsetof("struct bar", "c", 51) == 53 * ffi.sizeof("int")
+
+    def test_alignof(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { char a; short b; char c; };")
+        assert ffi.alignof("int") == 4
+        assert ffi.alignof("double") in (4, 8)
+        assert ffi.alignof("struct foo") == 2
+
+    def test_bitfield(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a:10, b:20, c:3; };")
+        assert ffi.sizeof("struct foo") == 8
+        s = ffi.new("struct foo *")
+        s.a = 511
+        py.test.raises(OverflowError, "s.a = 512")
+        py.test.raises(OverflowError, "s[0].a = 512")
+        assert s.a == 511
+        s.a = -512
+        py.test.raises(OverflowError, "s.a = -513")
+        py.test.raises(OverflowError, "s[0].a = -513")
+        assert s.a == -512
+        s.c = 3
+        assert s.c == 3
+        py.test.raises(OverflowError, "s.c = 4")
+        py.test.raises(OverflowError, "s[0].c = 4")
+        s.c = -4
+        assert s.c == -4
+
+    def test_bitfield_enum(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            typedef enum { AA, BB, CC } foo_e;
+            typedef struct { foo_e f:2; } foo_s;
+        """)
+        s = ffi.new("foo_s *")
+        s.f = 2
+        assert s.f == 2
+
+    def test_anonymous_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("typedef struct { int a; } foo_t;")
+        ffi.cdef("typedef struct { char b, c; } bar_t;")
+        f = ffi.new("foo_t *", [12345])
+        b = ffi.new("bar_t *", [b"B", b"C"])
+        assert f.a == 12345
+        assert b.b == b"B"
+        assert b.c == b"C"
+        assert repr(b).startswith("<cdata 'bar_t *'")
+
+    def test_struct_with_two_usages(self):
+        for name in ['foo_s', '']:    # anonymous or not
+            ffi = FFI(backend=self.Backend())
+            ffi.cdef("typedef struct %s { int a; } foo_t, *foo_p;" % name)
+            f = ffi.new("foo_t *", [12345])
+            ps = ffi.new("foo_p[]", [f])
+
+    def test_pointer_arithmetic(self):
+        ffi = FFI(backend=self.Backend())
+        s = ffi.new("short[]", list(range(100, 110)))
+        p = ffi.cast("short *", s)
+        assert p[2] == 102
+        assert p+1 == p+1
+        assert p+1 != p+0
+        assert p == p+0 == p-0
+        assert (p+1)[0] == 101
+        assert (p+19)[-10] == 109
+        assert (p+5) - (p+1) == 4
+        assert p == s+0
+        assert p+1 == s+1
+
+    def test_pointer_comparison(self):
+        ffi = FFI(backend=self.Backend())
+        s = ffi.new("short[]", list(range(100)))
+        p = ffi.cast("short *", s)
+        assert (p <  s) is False
+        assert (p <= s) is True
+        assert (p == s) is True
+        assert (p != s) is False
+        assert (p >  s) is False
+        assert (p >= s) is True
+        assert (s <  p) is False
+        assert (s <= p) is True
+        assert (s == p) is True
+        assert (s != p) is False
+        assert (s >  p) is False
+        assert (s >= p) is True
+        q = p + 1
+        assert (q <  s) is False
+        assert (q <= s) is False
+        assert (q == s) is False
+        assert (q != s) is True
+        assert (q >  s) is True
+        assert (q >= s) is True
+        assert (s <  q) is True
+        assert (s <= q) is True
+        assert (s == q) is False
+        assert (s != q) is True
+        assert (s >  q) is False
+        assert (s >= q) is False
+        assert (q <  p) is False
+        assert (q <= p) is False
+        assert (q == p) is False
+        assert (q != p) is True
+        assert (q >  p) is True
+        assert (q >= p) is True
+        assert (p <  q) is True
+        assert (p <= q) is True
+        assert (p == q) is False
+        assert (p != q) is True
+        assert (p >  q) is False
+        assert (p >= q) is False
+        #
+        assert (None == s) is False
+        assert (None != s) is True
+        assert (s == None) is False
+        assert (s != None) is True
+        assert (None == q) is False
+        assert (None != q) is True
+        assert (q == None) is False
+        assert (q != None) is True
+
+    def test_integer_comparison(self):
+        ffi = FFI(backend=self.Backend())
+        x = ffi.cast("int", 123)
+        y = ffi.cast("int", 456)
+        assert x < y
+        #
+        z = ffi.cast("double", 78.9)
+        assert x > z
+        assert y > z
+
+    def test_ffi_buffer_ptr(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short *", 100)
+        try:
+            b = ffi.buffer(a)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        assert type(b) is ffi.buffer
+        content = b[:]
+        assert len(content) == len(b) == 2
+        if sys.byteorder == 'little':
+            assert content == b'\x64\x00'
+            assert b[0] == b'\x64'
+            b[0] = b'\x65'
+        else:
+            assert content == b'\x00\x64'
+            assert b[1] == b'\x64'
+            b[1] = b'\x65'
+        assert a[0] == 101
+
+    def test_ffi_buffer_array(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("int[]", list(range(100, 110)))
+        try:
+            b = ffi.buffer(a)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        content = b[:]
+        if sys.byteorder == 'little':
+            assert content.startswith(b'\x64\x00\x00\x00\x65\x00\x00\x00')
+            b[4] = b'\x45'
+        else:
+            assert content.startswith(b'\x00\x00\x00\x64\x00\x00\x00\x65')
+            b[7] = b'\x45'
+        assert len(content) == 4 * 10
+        assert a[1] == 0x45
+
+    def test_ffi_buffer_ptr_size(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short *", 0x4243)
+        try:
+            b = ffi.buffer(a, 1)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        content = b[:]
+        assert len(content) == 1
+        if sys.byteorder == 'little':
+            assert content == b'\x43'
+            b[0] = b'\x62'
+            assert a[0] == 0x4262
+        else:
+            assert content == b'\x42'
+            b[0] = b'\x63'
+            assert a[0] == 0x6343
+
+    def test_ffi_buffer_array_size(self):
+        ffi = FFI(backend=self.Backend())
+        a1 = ffi.new("int[]", list(range(100, 110)))
+        a2 = ffi.new("int[]", list(range(100, 115)))
+        try:
+            ffi.buffer(a1)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        assert ffi.buffer(a1)[:] == ffi.buffer(a2, 4*10)[:]
+
+    def test_ffi_buffer_with_file(self):
+        ffi = FFI(backend=self.Backend())
+        import tempfile, os, array
+        fd, filename = tempfile.mkstemp()
+        f = os.fdopen(fd, 'r+b')
+        a = ffi.new("int[]", list(range(1005)))
+        try:
+            ffi.buffer(a, 512)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        f.write(ffi.buffer(a, 1000 * ffi.sizeof("int")))
+        f.seek(0)
+        assert f.read() == array.array('i', range(1000)).tostring()
+        f.seek(0)
+        b = ffi.new("int[]", 1005)
+        f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int")))
+        assert list(a)[:1000] + [0] * (len(a)-1000) == list(b)
+        f.close()
+        os.unlink(filename)
+
+    def test_ffi_buffer_with_io(self):
+        ffi = FFI(backend=self.Backend())
+        import io, array
+        f = io.BytesIO()
+        a = ffi.new("int[]", list(range(1005)))
+        try:
+            ffi.buffer(a, 512)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        f.write(ffi.buffer(a, 1000 * ffi.sizeof("int")))
+        f.seek(0)
+        assert f.read() == array.array('i', range(1000)).tostring()
+        f.seek(0)
+        b = ffi.new("int[]", 1005)
+        f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int")))
+        assert list(a)[:1000] + [0] * (len(a)-1000) == list(b)
+        f.close()
+
+    def test_ffi_buffer_comparisons(self):
+        ffi = FFI(backend=self.Backend())
+        ba = bytearray(range(100, 110))
+        if sys.version_info >= (2, 7):
+            assert ba == memoryview(ba)    # justification for the following
+        a = ffi.new("uint8_t[]", list(ba))
+        c = ffi.new("uint8_t[]", [99] + list(ba))
+        try:
+            b_full = ffi.buffer(a)
+            b_short = ffi.buffer(a, 3)
+            b_mid = ffi.buffer(a, 6)
+            b_other = ffi.buffer(c, 6)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        else:
+            content = b_full[:]
+            assert content == b_full == ba
+            assert b_other < b_short < b_mid < b_full
+            assert ba > b_mid > ba[0:2]
+            assert b_short != ba[1:4]
+
+    def test_array_in_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int len; short data[5]; };")
+        p = ffi.new("struct foo_s *")
+        p.data[3] = 5
+        assert p.data[3] == 5
+        assert repr(p.data).startswith("<cdata 'short[5]' 0x")
+
+    def test_struct_containing_array_varsize_workaround(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int len; short data[0]; };")
+        p = ffi.new("char[]", ffi.sizeof("struct foo_s") + 7 * SIZE_OF_SHORT)
+        q = ffi.cast("struct foo_s *", p)
+        assert q.len == 0
+        # 'q.data' gets not a 'short[0]', but just a 'short *' instead
+        assert repr(q.data).startswith("<cdata 'short *' 0x")
+        assert q.data[6] == 0
+        q.data[6] = 15
+        assert q.data[6] == 15
+
+    def test_new_struct_containing_array_varsize(self):
+        py.test.skip("later?")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int len; short data[]; };")
+        p = ffi.new("struct foo_s *", 10)     # a single integer is the length
+        assert p.len == 0
+        assert p.data[9] == 0
+        py.test.raises(IndexError, "p.data[10]")
+
+    def test_ffi_typeof_getcname(self):
+        ffi = FFI(backend=self.Backend())
+        assert ffi.getctype("int") == "int"
+        assert ffi.getctype("int", 'x') == "int x"
+        assert ffi.getctype("int*") == "int *"
+        assert ffi.getctype("int*", '') == "int *"
+        assert ffi.getctype("int*", 'x') == "int * x"
+        assert ffi.getctype("int", '*') == "int *"
+        assert ffi.getctype("int", ' * x ') == "int * x"
+        assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *"
+        assert ffi.getctype("int", '[5]') == "int[5]"
+        assert ffi.getctype("int[5]", '[6]') == "int[6][5]"
+        assert ffi.getctype("int[5]", '(*)') == "int(*)[5]"
+        # special-case for convenience: automatically put '()' around '*'
+        assert ffi.getctype("int[5]", '*') == "int(*)[5]"
+        assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]"
+        assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]"
+
+    def test_array_of_func_ptr(self):
+        ffi = FFI(backend=self.Backend())
+        f = ffi.cast("int(*)(int)", 42)
+        assert f != ffi.NULL
+        py.test.raises(CDefError, ffi.cast, "int(int)", 42)
+        py.test.raises(CDefError, ffi.new, "int([5])(int)")
+        a = ffi.new("int(*[5])(int)", [f])
+        assert ffi.getctype(ffi.typeof(a)) == "int(*[5])(int)"
+        assert len(a) == 5
+        assert a[0] == f
+        assert a[1] == ffi.NULL
+        py.test.raises(TypeError, ffi.cast, "int(*)(int)[5]", 0)
+        #
+        def cb(n):
+            return n + 1
+        f = ffi.callback("int(*)(int)", cb)
+        a = ffi.new("int(*[5])(int)", [f, f])
+        assert a[1](42) == 43
+
+    def test_callback_as_function_argument(self):
+        # In C, function arguments can be declared with a function type,
+        # which is automatically replaced with the ptr-to-function type.
+        ffi = FFI(backend=self.Backend())
+        def cb(a, b):
+            return chr(ord(a) + ord(b)).encode()
+        f = ffi.callback("char cb(char, char)", cb)
+        assert f(b'A', b'\x01') == b'B'
+        def g(callback):
+            return callback(b'A', b'\x01')
+        g = ffi.callback("char g(char cb(char, char))", g)
+        assert g(f) == b'B'
+
+    def test_vararg_callback(self):
+        py.test.skip("callback with '...'")
+        ffi = FFI(backend=self.Backend())
+        def cb(i, va_list):
+            j = ffi.va_arg(va_list, "int")
+            k = ffi.va_arg(va_list, "long long")
+            return i * 2 + j * 3 + k * 5
+        f = ffi.callback("long long cb(long i, ...)", cb)
+        res = f(10, ffi.cast("int", 100), ffi.cast("long long", 1000))
+        assert res == 20 + 300 + 5000
+
+    def test_callback_decorator(self):
+        ffi = FFI(backend=self.Backend())
+        #
+        @ffi.callback("long(long, long)", error=42)
+        def cb(a, b):
+            return a - b
+        #
+        assert cb(-100, -10) == -90
+        sz = ffi.sizeof("long")
+        assert cb((1 << (sz*8-1)) - 1, -10) == 42
+
+    def test_unique_types(self):
+        ffi1 = FFI(backend=self.Backend())
+        ffi2 = FFI(backend=self.Backend())
+        assert ffi1.typeof("char") is ffi2.typeof("char ")
+        assert ffi1.typeof("long") is ffi2.typeof("signed long int")
+        assert ffi1.typeof("double *") is ffi2.typeof("double*")
+        assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *")
+        assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]")
+        assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]")
+        assert ffi1.typeof("void") is ffi2.typeof("void")
+        assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)")
+        #
+        # these depend on user-defined data, so should not be shared
+        assert ffi1.typeof("struct foo") is not ffi2.typeof("struct foo")
+        assert ffi1.typeof("union foo *") is not ffi2.typeof("union foo*")
+        # the following test is an opaque enum, which we no longer support
+        #assert ffi1.typeof("enum foo") is not ffi2.typeof("enum foo")
+        # sanity check: twice 'ffi1'
+        assert ffi1.typeof("struct foo*") is ffi1.typeof("struct foo *")
+
+    def test_anonymous_enum(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("typedef enum { Value0 = 0 } e, *pe;\n"
+                 "typedef enum { Value1 = 1 } e1;")
+        assert ffi.getctype("e*") == 'e *'
+        assert ffi.getctype("pe") == 'e *'
+        assert ffi.getctype("e1*") == 'e1 *'
+
+    def test_opaque_enum(self):
+        import warnings
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("enum foo;")
+        with warnings.catch_warnings(record=True) as log:
+            warnings.simplefilter("always")
+            n = ffi.cast("enum foo", -1)
+            assert int(n) == 0xffffffff
+        assert str(log[0].message) == (
+            "'enum foo' has no values explicitly defined; "
+            "guessing that it is equivalent to 'unsigned int'")
+
+    def test_new_ctype(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *")
+        py.test.raises(TypeError, ffi.new, p)
+        p = ffi.new(ffi.typeof("int *"), 42)
+        assert p[0] == 42
+
+    def test_enum_with_non_injective_mapping(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("enum e { AA=0, BB=0, CC=0, DD=0 };")
+        e = ffi.cast("enum e", 0)
+        assert ffi.string(e) == "AA"     # pick the first one arbitrarily
+
+    def test_enum_refer_previous_enum_value(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("enum e { AA, BB=2, CC=4, DD=BB, EE, FF=CC, GG=FF };")
+        assert ffi.string(ffi.cast("enum e", 2)) == "BB"
+        assert ffi.string(ffi.cast("enum e", 3)) == "EE"
+        assert ffi.sizeof("char[DD]") == 2
+        assert ffi.sizeof("char[EE]") == 3
+        assert ffi.sizeof("char[FF]") == 4
+        assert ffi.sizeof("char[GG]") == 4
+
+    def test_nested_anonymous_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                struct { int a, b; };
+                union { int c, d; };
+            };
+        """)
+        assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+        p = ffi.new("struct foo_s *", [1, 2, 3])
+        assert p.a == 1
+        assert p.b == 2
+        assert p.c == 3
+        assert p.d == 3
+        p.d = 17
+        assert p.c == 17
+        p.b = 19
+        assert p.a == 1
+        assert p.b == 19
+        assert p.c == 17
+        assert p.d == 17
+        p = ffi.new("struct foo_s *", {'b': 12, 'd': 14})
+        assert p.a == 0
+        assert p.b == 12
+        assert p.c == 14
+        assert p.d == 14
+        py.test.raises(ValueError, ffi.new, "struct foo_s *", [0, 0, 0, 0])
+
+    def test_nested_field_offset_align(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                struct { int a; char b; };
+                union { char c; };
+            };
+        """)
+        assert ffi.offsetof("struct foo_s", "c") == 2 * SIZE_OF_INT
+        assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+
+    def test_nested_anonymous_union(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            union foo_u {
+                struct { int a, b; };
+                union { int c, d; };
+            };
+        """)
+        assert ffi.sizeof("union foo_u") == 2 * SIZE_OF_INT
+        p = ffi.new("union foo_u *", [5])
+        assert p.a == 5
+        assert p.b == 0
+        assert p.c == 5
+        assert p.d == 5
+        p.d = 17
+        assert p.c == 17
+        assert p.a == 17
+        p.b = 19
+        assert p.a == 17
+        assert p.b == 19
+        assert p.c == 17
+        assert p.d == 17
+        p = ffi.new("union foo_u *", {'d': 14})
+        assert p.a == 14
+        assert p.b == 0
+        assert p.c == 14
+        assert p.d == 14
+        p = ffi.new("union foo_u *", {'a': -63, 'b': 12})
+        assert p.a == -63
+        assert p.b == 12
+        assert p.c == -63
+        assert p.d == -63
+        p = ffi.new("union foo_u *", [123, 456])
+        assert p.a == 123
+        assert p.b == 456
+        assert p.c == 123
+        assert p.d == 123
+        py.test.raises(ValueError, ffi.new, "union foo_u *", [0, 0, 0])
+
+    def test_nested_anonymous_struct_2(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                int a;
+                union { int b; union { int c, d; }; };
+                int e;
+            };
+        """)
+        assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+        p = ffi.new("struct foo_s *", [11, 22, 33])
+        assert p.a == 11
+        assert p.b == p.c == p.d == 22
+        assert p.e == 33
+        py.test.raises(ValueError, ffi.new, "struct foo_s *", [11, 22, 33, 44])
+        FOO = ffi.typeof("struct foo_s")
+        fields = [(name, fld.offset, fld.flags) for (name, fld) in FOO.fields]
+        assert fields == [
+            ('a', 0 * SIZE_OF_INT, 0),
+            ('b', 1 * SIZE_OF_INT, 0),
+            ('c', 1 * SIZE_OF_INT, 1),
+            ('d', 1 * SIZE_OF_INT, 1),
+            ('e', 2 * SIZE_OF_INT, 0),
+        ]
+
+    def test_cast_to_array_type(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[4]", [-5])
+        q = ffi.cast("int[3]", p)
+        assert q[0] == -5
+        assert repr(q).startswith("<cdata 'int[3]' 0x")
+
+    def test_gc(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *", 123)
+        seen = []
+        def destructor(p1):
+            assert p1 is p
+            assert p1[0] == 123
+            seen.append(1)
+        q = ffi.gc(p, destructor)
+        assert ffi.typeof(q) is ffi.typeof(p)
+        import gc; gc.collect()
+        assert seen == []
+        del q
+        import gc; gc.collect(); gc.collect(); gc.collect()
+        assert seen == [1]
+
+    def test_gc_2(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *", 123)
+        seen = []
+        q1 = ffi.gc(p, lambda p: seen.append(1))
+        q2 = ffi.gc(q1, lambda p: seen.append(2))
+        import gc; gc.collect()
+        assert seen == []
+        del q1, q2
+        import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect()
+        assert seen == [2, 1]
+
+    def test_gc_3(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *", 123)
+        r = ffi.new("int *", 123)
+        seen = []
+        seen_r = []
+        q1 = ffi.gc(p, lambda p: seen.append(1))
+        s1 = ffi.gc(r, lambda r: seen_r.append(4))
+        q2 = ffi.gc(q1, lambda p: seen.append(2))
+        s2 = ffi.gc(s1, lambda r: seen_r.append(5))
+        q3 = ffi.gc(q2, lambda p: seen.append(3))
+        import gc; gc.collect()
+        assert seen == []
+        assert seen_r == []
+        del q1, q2, q3, s2, s1
+        import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect()
+        assert seen == [3, 2, 1]
+        assert seen_r == [5, 4]
+
+    def test_gc_4(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *", 123)
+        seen = []
+        q1 = ffi.gc(p, lambda p: seen.append(1))
+        q2 = ffi.gc(q1, lambda p: seen.append(2))
+        q3 = ffi.gc(q2, lambda p: seen.append(3))
+        import gc; gc.collect()
+        assert seen == []
+        del q1, q3     # q2 remains, and has a hard ref to q1
+        import gc; gc.collect(); gc.collect(); gc.collect()
+        assert seen == [3]
+
+    def test_gc_disable(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *", 123)
+        py.test.raises(TypeError, ffi.gc, p, None)
+        seen = []
+        q1 = ffi.gc(p, lambda p: seen.append(1))
+        q2 = ffi.gc(q1, lambda p: seen.append(2))
+        import gc; gc.collect()
+        assert seen == []
+        assert ffi.gc(q1, None) is None
+        del q1, q2
+        import gc; gc.collect(); gc.collect(); gc.collect()
+        assert seen == [2]
+
+    def test_gc_finite_list(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *", 123)
+        keepalive = []
+        for i in range(10):
+            keepalive.append(ffi.gc(p, lambda p: None))
+        del keepalive[:]
+        import gc; gc.collect(); gc.collect()
+        for i in range(10):
+            keepalive.append(ffi.gc(p, lambda p: None))
+
+    def test_CData_CType(self):
+        ffi = FFI(backend=self.Backend())
+        assert isinstance(ffi.cast("int", 0), ffi.CData)
+        assert isinstance(ffi.new("int *"), ffi.CData)
+        assert not isinstance(ffi.typeof("int"), ffi.CData)
+        assert not isinstance(ffi.cast("int", 0), ffi.CType)
+        assert not isinstance(ffi.new("int *"), ffi.CType)
+
+    def test_CData_CType_2(self):
+        ffi = FFI(backend=self.Backend())
+        assert isinstance(ffi.typeof("int"), ffi.CType)
+
+    def test_bool(self):
+        ffi = FFI(backend=self.Backend())
+        assert int(ffi.cast("_Bool", 0.1)) == 1
+        assert int(ffi.cast("_Bool", -0.0)) == 0
+        assert int(ffi.cast("_Bool", b'\x02')) == 1
+        assert int(ffi.cast("_Bool", b'\x00')) == 0
+        assert int(ffi.cast("_Bool", b'\x80')) == 1
+        assert ffi.new("_Bool *", False)[0] == 0
+        assert ffi.new("_Bool *", 1)[0] == 1
+        py.test.raises(OverflowError, ffi.new, "_Bool *", 2)
+        py.test.raises(TypeError, ffi.string, ffi.cast("_Bool", 2))
+
+    def test_use_own_bool(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""typedef int bool;""")
+
+    def test_ordering_bug1(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                struct bar_s *p;
+            };
+            struct bar_s {
+                struct foo_s foo;
+            };
+        """)
+        q = ffi.new("struct foo_s *")
+        bar = ffi.new("struct bar_s *")
+        q.p = bar
+        assert q.p.foo.p == ffi.NULL
+
+    def test_ordering_bug2(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct bar_s;
+
+            struct foo_s {
+                void (*foo)(struct bar_s[]);
+            };
+
+            struct bar_s {
+                struct foo_s foo;
+            };
+        """)
+        q = ffi.new("struct foo_s *")
+
+    def test_addressof(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int x, y; };")
+        p = ffi.new("struct foo_s *")
+        a = ffi.addressof(p[0])
+        assert repr(a).startswith("<cdata 'struct foo_s *' 0x")
+        assert a == p
+        py.test.raises(TypeError, ffi.addressof, p)
+        py.test.raises((AttributeError, TypeError), ffi.addressof, 5)
+        py.test.raises(TypeError, ffi.addressof, ffi.cast("int", 5))
+
+    def test_addressof_field(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int x, y; };")
+        p = ffi.new("struct foo_s *")
+        a = ffi.addressof(p[0], 'y')
+        assert repr(a).startswith("<cdata 'int *' 0x")
+        assert int(ffi.cast("uintptr_t", a)) == (
+            int(ffi.cast("uintptr_t", p)) + ffi.sizeof("int"))
+        assert a == ffi.addressof(p, 'y')
+        assert a != ffi.addressof(p, 'x')
+
+    def test_addressof_field_nested(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int x, y; };"
+                 "struct bar_s { struct foo_s a, b; };")
+        p = ffi.new("struct bar_s *")
+        py.test.raises(KeyError, ffi.addressof, p[0], 'b.y')
+        a = ffi.addressof(p[0], 'b', 'y')
+        assert int(ffi.cast("uintptr_t", a)) == (
+            int(ffi.cast("uintptr_t", p)) +
+            ffi.sizeof("struct foo_s") + ffi.sizeof("int"))
+
+    def test_addressof_anonymous_struct(self):
+        ffi = FFI()
+        ffi.cdef("typedef struct { int x; } foo_t;")
+        p = ffi.new("foo_t *")
+        a = ffi.addressof(p[0])
+        assert a == p
+
+    def test_addressof_array(self):
+        ffi = FFI()
+        p = ffi.new("int[52]")
+        p0 = ffi.addressof(p)
+        assert p0 == p
+        assert ffi.typeof(p0) is ffi.typeof("int(*)[52]")
+        py.test.raises(TypeError, ffi.addressof, p0)
+        #
+        p1 = ffi.addressof(p, 25)
+        assert ffi.typeof(p1) is ffi.typeof("int *")
+        assert (p1 - p) == 25
+        assert ffi.addressof(p, 0) == p
+
+    def test_addressof_pointer(self):
+        ffi = FFI()
+        array = ffi.new("int[50]")
+        p = ffi.cast("int *", array)
+        py.test.raises(TypeError, ffi.addressof, p)
+        assert ffi.addressof(p, 0) == p
+        assert ffi.addressof(p, 25) == p + 25
+        assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p)
+        #
+        ffi.cdef("struct foo { int a, b; };")
+        array = ffi.new("struct foo[50]")
+        p = ffi.cast("int *", array)
+        py.test.raises(TypeError, ffi.addressof, p)
+        assert ffi.addressof(p, 0) == p
+        assert ffi.addressof(p, 25) == p + 25
+        assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p)
+
+    def test_addressof_array_in_struct(self):
+        ffi = FFI()
+        ffi.cdef("struct foo { int a, b; int c[50]; };")
+        p = ffi.new("struct foo *")
+        p1 = ffi.addressof(p, "c", 25)
+        assert ffi.typeof(p1) is ffi.typeof("int *")
+        assert p1 == ffi.cast("int *", p) + 27
+        assert ffi.addressof(p, "c") == ffi.cast("int *", p) + 2
+        assert ffi.addressof(p, "c", 0) == ffi.cast("int *", p) + 2
+        p2 = ffi.addressof(p, 1)
+        assert ffi.typeof(p2) is ffi.typeof("struct foo *")
+        assert p2 == p + 1
+
+    def test_multiple_independent_structs(self):
+        ffi1 = FFI(); ffi1.cdef("struct foo { int x; };")
+        ffi2 = FFI(); ffi2.cdef("struct foo { int y, z; };")
+        foo1 = ffi1.new("struct foo *", [10])
+        foo2 = ffi2.new("struct foo *", [20, 30])
+        assert foo1.x == 10
+        assert foo2.y == 20
+        assert foo2.z == 30
+
+    def test_missing_include(self):
+        backend = self.Backend()
+        ffi1 = FFI(backend=backend)
+        ffi2 = FFI(backend=backend)
+        ffi1.cdef("typedef signed char schar_t;")
+        py.test.raises(CDefError, ffi2.cast, "schar_t", 142)
+
+    def test_include_typedef(self):
+        backend = self.Backend()
+        ffi1 = FFI(backend=backend)
+        ffi2 = FFI(backend=backend)
+        ffi1.cdef("typedef signed char schar_t;")
+        ffi2.include(ffi1)
+        p = ffi2.cast("schar_t", 142)
+        assert int(p) == 142 - 256
+
+    def test_include_struct(self):
+        backend = self.Backend()
+        ffi1 = FFI(backend=backend)
+        ffi2 = FFI(backend=backend)
+        ffi1.cdef("struct foo { int x; };")
+        ffi2.include(ffi1)
+        p = ffi2.new("struct foo *", [142])
+        assert p.x == 142
+
+    def test_include_union(self):
+        backend = self.Backend()
+        ffi1 = FFI(backend=backend)
+        ffi2 = FFI(backend=backend)
+        ffi1.cdef("union foo { int x; };")
+        ffi2.include(ffi1)
+        p = ffi2.new("union foo *", [142])
+        assert p.x == 142
+
+    def test_include_enum(self):
+        backend = self.Backend()
+        ffi1 = FFI(backend=backend)
+        ffi2 = FFI(backend=backend)
+        ffi1.cdef("enum foo { FA, FB, FC };")
+        ffi2.include(ffi1)
+        p = ffi2.cast("enum foo", 1)
+        assert ffi2.string(p) == "FB"
+        assert ffi2.sizeof("char[FC]") == 2
+
+    def test_include_typedef_2(self):
+        backend = self.Backend()
+        ffi1 = FFI(backend=backend)
+        ffi2 = FFI(backend=backend)
+        ffi1.cdef("typedef struct { int x; } *foo_p;")
+        ffi2.include(ffi1)
+        p = ffi2.new("foo_p", [142])
+        assert p.x == 142
+
+    def test_ignore_multiple_declarations_of_constant(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("#define FOO 42")
+        ffi.cdef("#define FOO 42")
+        py.test.raises(FFIError, ffi.cdef, "#define FOO 43")
+
+    def test_struct_packed(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct nonpacked { char a; int b; };")
+        ffi.cdef("struct is_packed { char a; int b; };", packed=True)
+        ffi.cdef("struct is_packed1 { char a; int b; };", pack=1)
+        ffi.cdef("struct is_packed2 { char a; int b; };", pack=2)
+        ffi.cdef("struct is_packed4 { char a; int b; };", pack=4)
+        ffi.cdef("struct is_packed8 { char a; int b; };", pack=8)
+        assert ffi.sizeof("struct nonpacked") == 8
+        assert ffi.sizeof("struct is_packed") == 5
+        assert ffi.sizeof("struct is_packed1") == 5
+        assert ffi.sizeof("struct is_packed2") == 6
+        assert ffi.sizeof("struct is_packed4") == 8
+        assert ffi.sizeof("struct is_packed8") == 8
+        assert ffi.alignof("struct nonpacked") == 4
+        assert ffi.alignof("struct is_packed") == 1
+        assert ffi.alignof("struct is_packed1") == 1
+        assert ffi.alignof("struct is_packed2") == 2
+        assert ffi.alignof("struct is_packed4") == 4
+        assert ffi.alignof("struct is_packed8") == 4
+        for name in ['is_packed', 'is_packed1', 'is_packed2',
+                     'is_packed4', 'is_packed8']:
+            s = ffi.new("struct %s[2]" % name)
+            s[0].b = 42623381
+            s[0].a = b'X'
+            s[1].b = -4892220
+            s[1].a = b'Y'
+            assert s[0].b == 42623381
+            assert s[0].a == b'X'
+            assert s[1].b == -4892220
+            assert s[1].a == b'Y'
+
+    def test_pack_valueerror(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(ValueError, ffi.cdef, "", pack=3)
+        py.test.raises(ValueError, ffi.cdef, "", packed=2)
+        py.test.raises(ValueError, ffi.cdef, "", packed=True, pack=1)
+
+    def test_define_integer_constant(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            #define DOT_0 0
+            #define DOT 100
+            #define DOT_OCT 0100l
+            #define DOT_HEX 0x100u
+            #define DOT_HEX2 0X10
+            #define DOT_UL 1000UL
+            enum foo {AA, BB=DOT, CC};
+        """)
+        needs_dlopen_none()
+        lib = ffi.dlopen(None)
+        assert ffi.string(ffi.cast("enum foo", 100)) == "BB"
+        assert lib.DOT_0 == 0
+        assert lib.DOT == 100
+        assert lib.DOT_OCT == 0o100
+        assert lib.DOT_HEX == 0x100
+        assert lib.DOT_HEX2 == 0x10
+        assert lib.DOT_UL == 1000
+
+    def test_opaque_struct_becomes_nonopaque(self):
+        # Issue #193: if we use a struct between the first cdef() where it is
+        # declared and another cdef() where its fields are defined, then the
+        # definition was ignored.
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s;")
+        py.test.raises(TypeError, ffi.new, "struct foo_s *")
+        ffi.cdef("struct foo_s { int x; };")
+        ffi.new("struct foo_s *")
+
+    def test_ffi_self_include(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(ValueError, ffi.include, ffi)
+
+    def test_anonymous_enum_include(self):
+        ffi1 = FFI()
+        ffi1.cdef("enum { EE1 };")
+        ffi = FFI()
+        ffi.include(ffi1)
+        ffi.cdef("enum { EE2, EE3 };")
+        needs_dlopen_none()
+        lib = ffi.dlopen(None)
+        assert lib.EE1 == 0
+        assert lib.EE2 == 0
+        assert lib.EE3 == 1
+
+    def test_init_once(self):
+        def do_init():
+            seen.append(1)
+            return 42
+        ffi = FFI()
+        seen = []
+        for i in range(3):
+            res = ffi.init_once(do_init, "tag1")
+            assert res == 42
+            assert seen == [1]
+        for i in range(3):
+            res = ffi.init_once(do_init, "tag2")
+            assert res == 42
+            assert seen == [1, 1]
+
+    def test_init_once_multithread(self):
+        import sys, time
+        if sys.version_info < (3,):
+            import thread
+        else:
+            import _thread as thread
+        #
+        def do_init():
+            seen.append('init!')
+            time.sleep(1)
+            seen.append('init done')
+            return 7
+        ffi = FFI()
+        seen = []
+        for i in range(6):
+            def f():
+                res = ffi.init_once(do_init, "tag")
+                seen.append(res)
+            thread.start_new_thread(f, ())
+        time.sleep(1.5)
+        assert seen == ['init!', 'init done'] + 6 * [7]
+
+    def test_sizeof_struct_directly(self):
+        # only works with the Python FFI instances
+        ffi = FFI(backend=self.Backend())
+        assert ffi.sizeof("struct{int a;}") == ffi.sizeof("int")
+
+    def test_callback_large_struct(self):
+        ffi = FFI(backend=self.Backend())
+        # more than 8 bytes
+        ffi.cdef("struct foo_s { unsigned long a, b, c; };")
+        #
+        @ffi.callback("void(struct foo_s)")
+        def cb(s):
+            seen.append(ffi.typeof(s))
+            s.a += 1
+            s.b += 2
+            s.c += 3
+            seen.append(s.a)
+            seen.append(s.b)
+            seen.append(s.c)
+        #
+        s1 = ffi.new("struct foo_s *", {'a': 100, 'b': 200, 'c': 300})
+        seen = []
+        cb(s1[0])
+        assert len(seen) == 4
+        assert s1.a == 100     # unmodified
+        assert s1.b == 200
+        assert s1.c == 300
+        assert seen[0] == ffi.typeof("struct foo_s")
+        assert seen[1] == 101
+        assert seen[2] == 202
+        assert seen[3] == 303
+
+    def test_ffi_array_as_init(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[4]", [10, 20, 30, 400])
+        q = ffi.new("int[4]", p)
+        assert list(q) == [10, 20, 30, 400]
+        py.test.raises(TypeError, ffi.new, "int[3]", p)
+        py.test.raises(TypeError, ffi.new, "int[5]", p)
+        py.test.raises(TypeError, ffi.new, "int16_t[4]", p)
+        s = ffi.new("struct {int i[4];}*", {'i': p})
+        assert list(s.i) == [10, 20, 30, 400]
+
+    def test_too_many_initializers(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(IndexError, ffi.new, "int[4]", [10, 20, 30, 40, 50])
diff --git a/testing/cffi0/callback_in_thread.py b/testing/cffi0/callback_in_thread.py
new file mode 100644
index 0000000..c98605c
--- /dev/null
+++ b/testing/cffi0/callback_in_thread.py
@@ -0,0 +1,42 @@
+import sys, time
+sys.path.insert(0, sys.argv[1])
+from cffi import FFI
+
+def _run_callback_in_thread():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef int (*mycallback_func_t)(int, int);
+        int threaded_ballback_test(mycallback_func_t mycb);
+    """)
+    lib = ffi.verify("""
+        #include <pthread.h>
+        typedef int (*mycallback_func_t)(int, int);
+        void *my_wait_function(void *ptr) {
+            mycallback_func_t cbfunc = (mycallback_func_t)ptr;
+            cbfunc(10, 10);
+            cbfunc(12, 15);
+            return NULL;
+        }
+        int threaded_ballback_test(mycallback_func_t mycb) {
+            pthread_t thread;
+            pthread_create(&thread, NULL, my_wait_function, (void*)mycb);
+            return 0;
+        }
+    """, extra_compile_args=['-pthread'])
+    seen = []
+    @ffi.callback('int(*)(int,int)')
+    def mycallback(x, y):
+        time.sleep(0.022)
+        seen.append((x, y))
+        return 0
+    lib.threaded_ballback_test(mycallback)
+    count = 300
+    while len(seen) != 2:
+        time.sleep(0.01)
+        count -= 1
+        assert count > 0, "timeout"
+    assert seen == [(10, 10), (12, 15)]
+
+print('STARTING')
+_run_callback_in_thread()
+print('DONE')
diff --git a/testing/cffi0/snippets/distutils_module/setup.py b/testing/cffi0/snippets/distutils_module/setup.py
new file mode 100644
index 0000000..a4d5551
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_module/setup.py
@@ -0,0 +1,7 @@
+
+from distutils.core import setup
+import snip_basic_verify
+
+setup(
+    py_modules=['snip_basic_verify'],
+    ext_modules=[snip_basic_verify.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/distutils_module/snip_basic_verify.py b/testing/cffi0/snippets/distutils_module/snip_basic_verify.py
new file mode 100644
index 0000000..e8a867e
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_module/snip_basic_verify.py
@@ -0,0 +1,17 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef("""     // some declarations from the man page
+    struct passwd {
+        char *pw_name;
+        ...;
+    };
+    struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify("""   // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[],    # or a list of libraries to link with
+     force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/distutils_package_1/setup.py b/testing/cffi0/snippets/distutils_package_1/setup.py
new file mode 100644
index 0000000..e3d28a5
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_package_1/setup.py
@@ -0,0 +1,7 @@
+
+from distutils.core import setup
+import snip_basic_verify1
+
+setup(
+    packages=['snip_basic_verify1'],
+    ext_modules=[snip_basic_verify1.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py b/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py
new file mode 100644
index 0000000..e8a867e
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py
@@ -0,0 +1,17 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef("""     // some declarations from the man page
+    struct passwd {
+        char *pw_name;
+        ...;
+    };
+    struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify("""   // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[],    # or a list of libraries to link with
+     force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/distutils_package_2/setup.py b/testing/cffi0/snippets/distutils_package_2/setup.py
new file mode 100644
index 0000000..6d8f72a
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_package_2/setup.py
@@ -0,0 +1,8 @@
+
+from distutils.core import setup
+import snip_basic_verify2
+
+setup(
+    packages=['snip_basic_verify2'],
+    ext_package='snip_basic_verify2',
+    ext_modules=[snip_basic_verify2.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py b/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py
new file mode 100644
index 0000000..b4ee686
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py
@@ -0,0 +1,18 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef("""     // some declarations from the man page
+    struct passwd {
+        char *pw_name;
+        ...;
+    };
+    struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify("""   // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[],    # or a list of libraries to link with
+     ext_package='snip_basic_verify2',
+     force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/infrastructure/setup.py b/testing/cffi0/snippets/infrastructure/setup.py
new file mode 100644
index 0000000..ea89f50
--- /dev/null
+++ b/testing/cffi0/snippets/infrastructure/setup.py
@@ -0,0 +1,5 @@
+
+from distutils.core import setup
+
+setup(packages=['snip_infrastructure'],
+      requires=['cffi'])
diff --git a/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py b/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py
new file mode 100644
index 0000000..dad950d
--- /dev/null
+++ b/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py
@@ -0,0 +1,3 @@
+
+def func():
+    return 42
diff --git a/testing/cffi0/snippets/setuptools_module/setup.py b/testing/cffi0/snippets/setuptools_module/setup.py
new file mode 100644
index 0000000..30f2e04
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_module/setup.py
@@ -0,0 +1,8 @@
+
+from setuptools import setup
+import snip_setuptools_verify
+
+setup(
+    zip_safe=False,
+    py_modules=['snip_setuptools_verify'],
+    ext_modules=[snip_setuptools_verify.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py b/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py
new file mode 100644
index 0000000..e8a867e
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py
@@ -0,0 +1,17 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef("""     // some declarations from the man page
+    struct passwd {
+        char *pw_name;
+        ...;
+    };
+    struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify("""   // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[],    # or a list of libraries to link with
+     force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/setuptools_package_1/setup.py b/testing/cffi0/snippets/setuptools_package_1/setup.py
new file mode 100644
index 0000000..18ea3f6
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_package_1/setup.py
@@ -0,0 +1,8 @@
+
+from setuptools import setup
+import snip_setuptools_verify1
+
+setup(
+    zip_safe=False,
+    packages=['snip_setuptools_verify1'],
+    ext_modules=[snip_setuptools_verify1.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py b/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py
new file mode 100644
index 0000000..e8a867e
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py
@@ -0,0 +1,17 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef("""     // some declarations from the man page
+    struct passwd {
+        char *pw_name;
+        ...;
+    };
+    struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify("""   // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[],    # or a list of libraries to link with
+     force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/setuptools_package_2/setup.py b/testing/cffi0/snippets/setuptools_package_2/setup.py
new file mode 100644
index 0000000..87fb22b
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_package_2/setup.py
@@ -0,0 +1,9 @@
+
+from setuptools import setup
+import snip_setuptools_verify2
+
+setup(
+    zip_safe=False,
+    packages=['snip_setuptools_verify2'],
+    ext_package='snip_setuptools_verify2',
+    ext_modules=[snip_setuptools_verify2.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py b/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py
new file mode 100644
index 0000000..5f4bd13
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py
@@ -0,0 +1,18 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef("""     // some declarations from the man page
+    struct passwd {
+        char *pw_name;
+        ...;
+    };
+    struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify("""   // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[],    # or a list of libraries to link with
+     ext_package='snip_setuptools_verify2',
+     force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/test_cdata.py b/testing/cffi0/test_cdata.py
new file mode 100644
index 0000000..23989ab
--- /dev/null
+++ b/testing/cffi0/test_cdata.py
@@ -0,0 +1,41 @@
+import py
+from cffi import FFI
+
+class FakeBackend(object):
+
+    def nonstandard_integer_types(self):
+        return {}
+
+    def sizeof(self, name):
+        return 1
+
+    def load_library(self, path):
+        return "fake library"
+
+    def new_primitive_type(self, name):
+        return FakeType("primitive " + name)
+
+    def new_void_type(self):
+        return FakeType("void")
+    def new_pointer_type(self, x):
+        return FakeType('ptr-to-%r' % (x,))
+    def new_array_type(self, x, y):
+        return FakeType('array-from-%r-len-%r' % (x, y))
+    def cast(self, x, y):
+        return 'casted!'
+    def _get_types(self):
+        return "CData", "CType"
+
+    buffer = "buffer type"
+
+
+class FakeType(object):
+    def __init__(self, cdecl):
+        self.cdecl = cdecl
+
+
+def test_typeof():
+    ffi = FFI(backend=FakeBackend())
+    clong = ffi.typeof("signed long int")
+    assert isinstance(clong, FakeType)
+    assert clong.cdecl == 'primitive long'
diff --git a/testing/cffi0/test_ctypes.py b/testing/cffi0/test_ctypes.py
new file mode 100644
index 0000000..a70c8f0
--- /dev/null
+++ b/testing/cffi0/test_ctypes.py
@@ -0,0 +1,43 @@
+import py, sys
+from testing.cffi0 import backend_tests
+from cffi.backend_ctypes import CTypesBackend
+
+
+class TestCTypes(backend_tests.BackendTests):
+    # for individual tests see
+    # ====> backend_tests.py
+    
+    Backend = CTypesBackend
+    TypeRepr = "<class 'ffi.CData<%s>'>"
+
+    def test_array_of_func_ptr(self):
+        py.test.skip("ctypes backend: not supported: "
+                     "initializers for function pointers")
+
+    def test_structptr_argument(self):
+        py.test.skip("ctypes backend: not supported: passing a list "
+                     "for a pointer argument")
+
+    def test_array_argument_as_list(self):
+        py.test.skip("ctypes backend: not supported: passing a list "
+                     "for a pointer argument")
+
+    def test_cast_to_array_type(self):
+        py.test.skip("ctypes backend: not supported: casting to array")
+
+    def test_nested_anonymous_struct(self):
+        py.test.skip("ctypes backend: not supported: nested anonymous struct")
+
+    def test_nested_field_offset_align(self):
+        py.test.skip("ctypes backend: not supported: nested anonymous struct")
+
+    def test_nested_anonymous_union(self):
+        py.test.skip("ctypes backend: not supported: nested anonymous union")
+
+    def test_nested_anonymous_struct_2(self):
+        py.test.skip("ctypes backend: not supported: nested anonymous union")
+
+    def test_CData_CType_2(self):
+        if sys.version_info >= (3,):
+            py.test.skip("ctypes backend: not supported in Python 3: CType")
+        backend_tests.BackendTests.test_CData_CType_2(self)
diff --git a/testing/cffi0/test_ffi_backend.py b/testing/cffi0/test_ffi_backend.py
new file mode 100644
index 0000000..12ecaee
--- /dev/null
+++ b/testing/cffi0/test_ffi_backend.py
@@ -0,0 +1,576 @@
+import py, sys, platform
+import pytest
+from testing.cffi0 import backend_tests, test_function, test_ownlib
+from testing.support import u
+from cffi import FFI
+import _cffi_backend
+
+
+class TestFFI(backend_tests.BackendTests,
+              test_function.TestFunction,
+              test_ownlib.TestOwnLib):
+    TypeRepr = "<ctype '%s'>"
+
+    @staticmethod
+    def Backend():
+        return _cffi_backend
+
+    def test_not_supported_bitfield_in_result(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int a,b,c,d,e; int x:1; };")
+        e = py.test.raises(NotImplementedError, ffi.callback,
+                           "struct foo_s foo(void)", lambda: 42)
+        assert str(e.value) == ("struct foo_s(*)(): "
+            "callback with unsupported argument or return type or with '...'")
+
+    def test_inspecttype(self):
+        ffi = FFI(backend=self.Backend())
+        assert ffi.typeof("long").kind == "primitive"
+        assert ffi.typeof("long(*)(long, long**, ...)").cname == (
+            "long(*)(long, long * *, ...)")
+        assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True
+
+    def test_new_handle(self):
+        ffi = FFI(backend=self.Backend())
+        o = [2, 3, 4]
+        p = ffi.new_handle(o)
+        assert ffi.typeof(p) == ffi.typeof("void *")
+        assert ffi.from_handle(p) is o
+        assert ffi.from_handle(ffi.cast("char *", p)) is o
+        py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL)
+
+    def test_callback_onerror(self):
+        ffi = FFI(backend=self.Backend())
+        seen = []
+        def oops(*args):
+            seen.append(args)
+        def otherfunc():
+            raise LookupError
+        def cb(n):
+            otherfunc()
+        a = ffi.callback("int(*)(int)", cb, error=42, onerror=oops)
+        res = a(234)
+        assert res == 42
+        assert len(seen) == 1
+        exc, val, tb = seen[0]
+        assert exc is LookupError
+        assert isinstance(val, LookupError)
+        assert tb.tb_frame.f_code.co_name == 'cb'
+        assert tb.tb_frame.f_locals['n'] == 234
+
+    def test_ffi_new_allocator_2(self):
+        ffi = FFI(backend=self.Backend())
+        seen = []
+        def myalloc(size):
+            seen.append(size)
+            return ffi.new("char[]", b"X" * size)
+        def myfree(raw):
+            seen.append(raw)
+        alloc1 = ffi.new_allocator(myalloc, myfree)
+        alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree,
+                                   should_clear_after_alloc=False)
+        p1 = alloc1("int[10]")
+        p2 = alloc2("int[]", 10)
+        assert seen == [40, 40]
+        assert ffi.typeof(p1) == ffi.typeof("int[10]")
+        assert ffi.sizeof(p1) == 40
+        assert ffi.typeof(p2) == ffi.typeof("int[]")
+        assert ffi.sizeof(p2) == 40
+        assert p1[5] == 0
+        assert p2[6] == ord('X') * 0x01010101
+        raw1 = ffi.cast("char *", p1)
+        raw2 = ffi.cast("char *", p2)
+        del p1, p2
+        retries = 0
+        while len(seen) != 4:
+            retries += 1
+            assert retries <= 5
+            import gc; gc.collect()
+        assert seen == [40, 40, raw1, raw2]
+        assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>"
+        assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>"
+
+    def test_ffi_new_allocator_3(self):
+        ffi = FFI(backend=self.Backend())
+        seen = []
+        def myalloc(size):
+            seen.append(size)
+            return ffi.new("char[]", b"X" * size)
+        alloc1 = ffi.new_allocator(myalloc)    # no 'free'
+        p1 = alloc1("int[10]")
+        assert seen == [40]
+        assert ffi.typeof(p1) == ffi.typeof("int[10]")
+        assert ffi.sizeof(p1) == 40
+        assert p1[5] == 0
+
+    def test_ffi_new_allocator_4(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None)
+        #
+        def myalloc2(size):
+            raise LookupError
+        alloc2 = ffi.new_allocator(myalloc2)
+        py.test.raises(LookupError, alloc2, "int[5]")
+        #
+        def myalloc3(size):
+            return 42
+        alloc3 = ffi.new_allocator(myalloc3)
+        e = py.test.raises(TypeError, alloc3, "int[5]")
+        assert str(e.value) == "alloc() must return a cdata object (got int)"
+        #
+        def myalloc4(size):
+            return ffi.cast("int", 42)
+        alloc4 = ffi.new_allocator(myalloc4)
+        e = py.test.raises(TypeError, alloc4, "int[5]")
+        assert str(e.value) == "alloc() must return a cdata pointer, not 'int'"
+        #
+        def myalloc5(size):
+            return ffi.NULL
+        alloc5 = ffi.new_allocator(myalloc5)
+        py.test.raises(MemoryError, alloc5, "int[5]")
+
+
+class TestBitfield:
+    def check(self, source, expected_ofs_y, expected_align, expected_size):
+        # NOTE: 'expected_*' is the numbers expected from GCC.
+        # The numbers expected from MSVC are not explicitly written
+        # in this file, and will just be taken from the compiler.
+        ffi = FFI()
+        ffi.cdef("struct s1 { %s };" % source)
+        ctype = ffi.typeof("struct s1")
+        # verify the information with gcc
+        ffi1 = FFI()
+        ffi1.cdef("""
+            static const int Gofs_y, Galign, Gsize;
+            struct s1 *try_with_value(int fieldnum, long long value);
+        """)
+        fnames = [name for name, cfield in ctype.fields
+                       if name and cfield.bitsize > 0]
+        setters = ['case %d: s.%s = value; break;' % iname
+                   for iname in enumerate(fnames)]
+        lib = ffi1.verify("""
+            struct s1 { %s };
+            struct sa { char a; struct s1 b; };
+            #define Gofs_y  offsetof(struct s1, y)
+            #define Galign  offsetof(struct sa, b)
+            #define Gsize   sizeof(struct s1)
+            struct s1 *try_with_value(int fieldnum, long long value)
+            {
+                static struct s1 s;
+                memset(&s, 0, sizeof(s));
+                switch (fieldnum) { %s }
+                return &s;
+            }
+        """ % (source, ' '.join(setters)))
+        if sys.platform == 'win32':
+            expected_ofs_y = lib.Gofs_y
+            expected_align = lib.Galign
+            expected_size  = lib.Gsize
+        else:
+            assert (lib.Gofs_y, lib.Galign, lib.Gsize) == (
+                expected_ofs_y, expected_align, expected_size)
+        # the real test follows
+        assert ffi.offsetof("struct s1", "y") == expected_ofs_y
+        assert ffi.alignof("struct s1") == expected_align
+        assert ffi.sizeof("struct s1") == expected_size
+        # compare the actual storage of the two
+        for name, cfield in ctype.fields:
+            if cfield.bitsize < 0 or not name:
+                continue
+            if int(ffi.cast(cfield.type, -1)) == -1:   # signed
+                min_value = -(1 << (cfield.bitsize-1))
+                max_value = (1 << (cfield.bitsize-1)) - 1
+            else:
+                min_value = 0
+                max_value = (1 << cfield.bitsize) - 1
+            for t in [1, 2, 4, 8, 16, 128, 2813, 89728, 981729,
+                     -1,-2,-4,-8,-16,-128,-2813,-89728,-981729]:
+                if min_value <= t <= max_value:
+                    self._fieldcheck(ffi, lib, fnames, name, t)
+
+    def _fieldcheck(self, ffi, lib, fnames, name, value):
+        s = ffi.new("struct s1 *")
+        setattr(s, name, value)
+        assert getattr(s, name) == value
+        raw1 = ffi.buffer(s)[:]
+        buff1 = ffi.buffer(s)
+        t = lib.try_with_value(fnames.index(name), value)
+        raw2 = ffi.buffer(t, len(raw1))[:]
+        assert raw1 == raw2
+        buff2 = ffi.buffer(t, len(buff1))
+        assert buff1 == buff2
+
+    def test_bitfield_basic(self):
+        self.check("int a; int b:9; int c:20; int y;", 8, 4, 12)
+        self.check("int a; short b:9; short c:7; int y;", 8, 4, 12)
+        self.check("int a; short b:9; short c:9; int y;", 8, 4, 12)
+
+    def test_bitfield_reuse_if_enough_space(self):
+        self.check("int a:2; char y;", 1, 4, 4)
+        self.check("int a:1; char b  ; int c:1; char y;", 3, 4, 4)
+        self.check("int a:1; char b:8; int c:1; char y;", 3, 4, 4)
+        self.check("char a; int b:9; char y;", 3, 4, 4)
+        self.check("char a; short b:9; char y;", 4, 2, 6)
+        self.check("int a:2; char b:6; char y;", 1, 4, 4)
+        self.check("int a:2; char b:7; char y;", 2, 4, 4)
+        self.check("int a:2; short b:15; char c:2; char y;", 5, 4, 8)
+        self.check("int a:2; char b:1; char c:1; char y;", 1, 4, 4)
+
+    @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))")
+    def test_bitfield_anonymous_no_align(self):
+        L = FFI().alignof("long long")
+        self.check("char y; int :1;", 0, 1, 2)
+        self.check("char x; int z:1; char y;", 2, 4, 4)
+        self.check("char x; int  :1; char y;", 2, 1, 3)
+        self.check("char x; long long z:48; char y;", 7, L, 8)
+        self.check("char x; long long  :48; char y;", 7, 1, 8)
+        self.check("char x; long long z:56; char y;", 8, L, 8 + L)
+        self.check("char x; long long  :56; char y;", 8, 1, 9)
+        self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L)
+        self.check("char x; long long  :57; char y;", L + 8, 1, L + 9)
+
+    @pytest.mark.skipif(
+        "not platform.machine().startswith(('arm', 'aarch64'))")
+    def test_bitfield_anonymous_align_arm(self):
+        L = FFI().alignof("long long")
+        self.check("char y; int :1;", 0, 4, 4)
+        self.check("char x; int z:1; char y;", 2, 4, 4)
+        self.check("char x; int  :1; char y;", 2, 4, 4)
+        self.check("char x; long long z:48; char y;", 7, L, 8)
+        self.check("char x; long long  :48; char y;", 7, 8, 8)
+        self.check("char x; long long z:56; char y;", 8, L, 8 + L)
+        self.check("char x; long long  :56; char y;", 8, L, 8 + L)
+        self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L)
+        self.check("char x; long long  :57; char y;", L + 8, L, L + 8 + L)
+
+    @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))")
+    def test_bitfield_zero(self):
+        L = FFI().alignof("long long")
+        self.check("char y; int :0;", 0, 1, 4)
+        self.check("char x; int :0; char y;", 4, 1, 5)
+        self.check("char x; int :0; int :0; char y;", 4, 1, 5)
+        self.check("char x; long long :0; char y;", L, 1, L + 1)
+        self.check("short x, y; int :0; int :0;", 2, 2, 4)
+        self.check("char x; int :0; short b:1; char y;", 5, 2, 6)
+        self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8)
+
+    @pytest.mark.skipif(
+        "not platform.machine().startswith(('arm', 'aarch64'))")
+    def test_bitfield_zero_arm(self):
+        L = FFI().alignof("long long")
+        self.check("char y; int :0;", 0, 4, 4)
+        self.check("char x; int :0; char y;", 4, 4, 8)
+        self.check("char x; int :0; int :0; char y;", 4, 4, 8)
+        self.check("char x; long long :0; char y;", L, 8, L + 8)
+        self.check("short x, y; int :0; int :0;", 2, 4, 4)
+        self.check("char x; int :0; short b:1; char y;", 5, 4, 8)
+        self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8)
+
+    def test_error_cases(self):
+        ffi = FFI()
+        py.test.raises(TypeError,
+            'ffi.cdef("struct s1 { float x:1; };"); ffi.new("struct s1 *")')
+        py.test.raises(TypeError,
+            'ffi.cdef("struct s2 { char x:0; };"); ffi.new("struct s2 *")')
+        py.test.raises(TypeError,
+            'ffi.cdef("struct s3 { char x:9; };"); ffi.new("struct s3 *")')
+
+    def test_struct_with_typedef(self):
+        ffi = FFI()
+        ffi.cdef("typedef struct { float x; } foo_t;")
+        p = ffi.new("foo_t *", [5.2])
+        assert repr(p).startswith("<cdata 'foo_t *' ")
+
+    def test_struct_array_no_length(self):
+        ffi = FFI()
+        ffi.cdef("struct foo_s { int x; int a[]; };")
+        p = ffi.new("struct foo_s *", [100, [200, 300, 400]])
+        assert p.x == 100
+        assert ffi.typeof(p.a) is ffi.typeof("int[]")
+        assert len(p.a) == 3                            # length recorded
+        assert p.a[0] == 200
+        assert p.a[1] == 300
+        assert p.a[2] == 400
+        assert list(p.a) == [200, 300, 400]
+        q = ffi.cast("struct foo_s *", p)
+        assert q.x == 100
+        assert ffi.typeof(q.a) is ffi.typeof("int *")   # no length recorded
+        py.test.raises(TypeError, len, q.a)
+        assert q.a[0] == 200
+        assert q.a[1] == 300
+        assert q.a[2] == 400
+        py.test.raises(TypeError, list, q.a)
+
+    @pytest.mark.skipif("sys.platform != 'win32'")
+    def test_getwinerror(self):
+        ffi = FFI()
+        code, message = ffi.getwinerror(1155)
+        assert code == 1155
+        assert message == ("No application is associated with the "
+                           "specified file for this operation")
+        ffi.cdef("void SetLastError(int);")
+        lib = ffi.dlopen("Kernel32.dll")
+        lib.SetLastError(2)
+        code, message = ffi.getwinerror()
+        assert code == 2
+        assert message == "The system cannot find the file specified"
+        code, message = ffi.getwinerror(-1)
+        assert code == 2
+        assert message == "The system cannot find the file specified"
+
+    def test_from_buffer(self):
+        import array
+        ffi = FFI()
+        a = array.array('H', [10000, 20000, 30000])
+        c = ffi.from_buffer(a)
+        assert ffi.typeof(c) is ffi.typeof("char[]")
+        assert len(c) == 6
+        ffi.cast("unsigned short *", c)[1] += 500
+        assert list(a) == [10000, 20500, 30000]
+        assert c == ffi.from_buffer("char[]", a, True)
+        assert c == ffi.from_buffer(a, require_writable=True)
+        #
+        c = ffi.from_buffer("unsigned short[]", a)
+        assert len(c) == 3
+        assert c[1] == 20500
+        #
+        p = ffi.from_buffer(b"abcd")
+        assert p[2] == b"c"
+        #
+        assert p == ffi.from_buffer(b"abcd", require_writable=False)
+        py.test.raises((TypeError, BufferError), ffi.from_buffer,
+                                                 "char[]", b"abcd", True)
+        py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd",
+                                                 require_writable=True)
+
+    def test_release(self):
+        ffi = FFI()
+        p = ffi.new("int[]", 123)
+        ffi.release(p)
+        # here, reading p[0] might give garbage or segfault...
+        ffi.release(p)   # no effect
+
+    def test_memmove(self):
+        ffi = FFI()
+        p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678])
+        ffi.memmove(p, p + 1, 4)
+        assert list(p) == [-2345, -3456, -3456, -4567, -5678]
+        p[2] = 999
+        ffi.memmove(p + 2, p, 6)
+        assert list(p) == [-2345, -3456, -2345, -3456, 999]
+        ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2)
+        if sys.byteorder == 'little':
+            assert list(p) == [-2345, -3456, -2345, -3456, 0x7271]
+        else:
+            assert list(p) == [-2345, -3456, -2345, -3456, 0x7172]
+
+    def test_memmove_buffer(self):
+        import array
+        ffi = FFI()
+        a = array.array('H', [10000, 20000, 30000])
+        p = ffi.new("short[]", 5)
+        ffi.memmove(p, a, 6)
+        assert list(p) == [10000, 20000, 30000, 0, 0]
+        ffi.memmove(p + 1, a, 6)
+        assert list(p) == [10000, 10000, 20000, 30000, 0]
+        b = array.array('h', [-1000, -2000, -3000])
+        ffi.memmove(b, a, 4)
+        assert b.tolist() == [10000, 20000, -3000]
+        assert a.tolist() == [10000, 20000, 30000]
+        p[0] = 999
+        p[1] = 998
+        p[2] = 997
+        p[3] = 996
+        p[4] = 995
+        ffi.memmove(b, p, 2)
+        assert b.tolist() == [999, 20000, -3000]
+        ffi.memmove(b, p + 2, 4)
+        assert b.tolist() == [997, 996, -3000]
+        p[2] = -p[2]
+        p[3] = -p[3]
+        ffi.memmove(b, p + 2, 6)
+        assert b.tolist() == [-997, -996, 995]
+
+    def test_memmove_readonly_readwrite(self):
+        ffi = FFI()
+        p = ffi.new("signed char[]", 5)
+        ffi.memmove(p, b"abcde", 3)
+        assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0]
+        ffi.memmove(p, bytearray(b"ABCDE"), 2)
+        assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0]
+        py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3)
+        ba = bytearray(b"xxxxx")
+        ffi.memmove(dest=ba, src=p, n=3)
+        assert ba == bytearray(b"ABcxx")
+
+    def test_all_primitives(self):
+        ffi = FFI()
+        for name in [
+            "char",
+            "short",
+            "int",
+            "long",
+            "long long",
+            "signed char",
+            "unsigned char",
+            "unsigned short",
+            "unsigned int",
+            "unsigned long",
+            "unsigned long long",
+            "float",
+            "double",
+            "long double",
+            "wchar_t",
+            "char16_t",
+            "char32_t",
+            "_Bool",
+            "int8_t",
+            "uint8_t",
+            "int16_t",
+            "uint16_t",
+            "int32_t",
+            "uint32_t",
+            "int64_t",
+            "uint64_t",
+            "int_least8_t",
+            "uint_least8_t",
+            "int_least16_t",
+            "uint_least16_t",
+            "int_least32_t",
+            "uint_least32_t",
+            "int_least64_t",
+            "uint_least64_t",
+            "int_fast8_t",
+            "uint_fast8_t",
+            "int_fast16_t",
+            "uint_fast16_t",
+            "int_fast32_t",
+            "uint_fast32_t",
+            "int_fast64_t",
+            "uint_fast64_t",
+            "intptr_t",
+            "uintptr_t",
+            "intmax_t",
+            "uintmax_t",
+            "ptrdiff_t",
+            "size_t",
+            "ssize_t",
+            ]:
+            x = ffi.sizeof(name)
+            assert 1 <= x <= 16
+
+    def test_ffi_def_extern(self):
+        ffi = FFI()
+        py.test.raises(ValueError, ffi.def_extern)
+
+    def test_introspect_typedef(self):
+        ffi = FFI()
+        ffi.cdef("typedef int foo_t;")
+        assert ffi.list_types() == (['foo_t'], [], [])
+        assert ffi.typeof('foo_t').kind == 'primitive'
+        assert ffi.typeof('foo_t').cname == 'int'
+        #
+        ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;")
+        assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'foo_t', 'g_t'],
+                                    [], [])
+
+    def test_introspect_struct(self):
+        ffi = FFI()
+        ffi.cdef("struct foo_s { int a; };")
+        assert ffi.list_types() == ([], ['foo_s'], [])
+        assert ffi.typeof('struct foo_s').kind == 'struct'
+        assert ffi.typeof('struct foo_s').cname == 'struct foo_s'
+
+    def test_introspect_union(self):
+        ffi = FFI()
+        ffi.cdef("union foo_s { int a; };")
+        assert ffi.list_types() == ([], [], ['foo_s'])
+        assert ffi.typeof('union foo_s').kind == 'union'
+        assert ffi.typeof('union foo_s').cname == 'union foo_s'
+
+    def test_introspect_struct_and_typedef(self):
+        ffi = FFI()
+        ffi.cdef("typedef struct { int a; } foo_t;")
+        assert ffi.list_types() == (['foo_t'], [], [])
+        assert ffi.typeof('foo_t').kind == 'struct'
+        assert ffi.typeof('foo_t').cname == 'foo_t'
+
+    def test_introspect_included_type(self):
+        ffi1 = FFI()
+        ffi2 = FFI()
+        ffi1.cdef("typedef signed char schar_t; struct sint_t { int x; };")
+        ffi2.include(ffi1)
+        assert ffi1.list_types() == ffi2.list_types() == (
+            ['schar_t'], ['sint_t'], [])
+
+    def test_introspect_order(self):
+        ffi = FFI()
+        ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;")
+        ffi.cdef("union CFFIg   { int a; }; typedef struct CFFIcc  { int a; } CFFIbbb;")
+        ffi.cdef("union CFFIaa  { int a; }; typedef struct CFFIa   { int a; } CFFIbb;")
+        assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'],
+                                    ['CFFIa', 'CFFIcc', 'CFFIccc'],
+                                    ['CFFIaa', 'CFFIaaa', 'CFFIg'])
+
+    def test_unpack(self):
+        ffi = FFI()
+        p = ffi.new("char[]", b"abc\x00def")
+        assert ffi.unpack(p+1, 7) == b"bc\x00def\x00"
+        p = ffi.new("int[]", [-123456789])
+        assert ffi.unpack(p, 1) == [-123456789]
+
+    def test_negative_array_size(self):
+        ffi = FFI()
+        py.test.raises(ValueError, ffi.cast, "int[-5]", 0)
+
+    def test_cannot_instantiate_manually(self):
+        ffi = FFI()
+        ct = type(ffi.typeof("void *"))
+        py.test.raises(TypeError, ct)
+        py.test.raises(TypeError, ct, ffi.NULL)
+        for cd in [type(ffi.cast("void *", 0)),
+                   type(ffi.new("char[]", 3)),
+                   type(ffi.gc(ffi.NULL, lambda x: None))]:
+            py.test.raises(TypeError, cd)
+            py.test.raises(TypeError, cd, ffi.NULL)
+            py.test.raises(TypeError, cd, ffi.typeof("void *"))
+
+    def test_explicitly_defined_char16_t(self):
+        ffi = FFI()
+        ffi.cdef("typedef uint16_t char16_t;")
+        x = ffi.cast("char16_t", 1234)
+        assert ffi.typeof(x) is ffi.typeof("uint16_t")
+
+    def test_char16_t(self):
+        ffi = FFI()
+        x = ffi.new("char16_t[]", 5)
+        assert len(x) == 5 and ffi.sizeof(x) == 10
+        x[2] = u+'\u1324'
+        assert x[2] == u+'\u1324'
+        y = ffi.new("char16_t[]", u+'\u1234\u5678')
+        assert len(y) == 3
+        assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00']
+        assert ffi.string(y) == u+'\u1234\u5678'
+        z = ffi.new("char16_t[]", u+'\U00012345')
+        assert len(z) == 3
+        assert list(z) == [u+'\ud808', u+'\udf45', u+'\x00']
+        assert ffi.string(z) == u+'\U00012345'
+
+    def test_char32_t(self):
+        ffi = FFI()
+        x = ffi.new("char32_t[]", 5)
+        assert len(x) == 5 and ffi.sizeof(x) == 20
+        x[3] = u+'\U00013245'
+        assert x[3] == u+'\U00013245'
+        y = ffi.new("char32_t[]", u+'\u1234\u5678')
+        assert len(y) == 3
+        assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00']
+        py_uni = u+'\U00012345'
+        z = ffi.new("char32_t[]", py_uni)
+        assert len(z) == 2
+        assert list(z) == [py_uni, u+'\x00']    # maybe a 2-unichars string
+        assert ffi.string(z) == py_uni
+        if len(py_uni) == 1:    # 4-bytes unicodes in Python
+            s = ffi.new("char32_t[]", u+'\ud808\udf00')
+            assert len(s) == 3
+            assert list(s) == [u+'\ud808', u+'\udf00', u+'\x00']
diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py
new file mode 100644
index 0000000..ca2353f
--- /dev/null
+++ b/testing/cffi0/test_function.py
@@ -0,0 +1,520 @@
+import py
+from cffi import FFI, CDefError
+import math, os, sys
+import ctypes.util
+from cffi.backend_ctypes import CTypesBackend
+from testing.udir import udir
+from testing.support import FdWriteCapture
+from .backend_tests import needs_dlopen_none
+
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
+
+
+lib_m = 'm'
+if sys.platform == 'win32':
+    #there is a small chance this fails on Mingw via environ $CC
+    import distutils.ccompiler
+    if distutils.ccompiler.get_default_compiler() == 'msvc':
+        lib_m = 'msvcrt'
+
+class TestFunction(object):
+    Backend = CTypesBackend
+
+    def test_sin(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            double sin(double x);
+        """)
+        m = ffi.dlopen(lib_m)
+        x = m.sin(1.23)
+        assert x == math.sin(1.23)
+
+    def test_sinf(self):
+        if sys.platform == 'win32':
+            py.test.skip("no sinf found in the Windows stdlib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            float sinf(float x);
+        """)
+        m = ffi.dlopen(lib_m)
+        x = m.sinf(1.23)
+        assert type(x) is float
+        assert x != math.sin(1.23)    # rounding effects
+        assert abs(x - math.sin(1.23)) < 1E-6
+
+    def test_getenv_no_return_value(self):
+        # check that 'void'-returning functions work too
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            void getenv(char *);
+        """)
+        needs_dlopen_none()
+        m = ffi.dlopen(None)
+        x = m.getenv(b"FOO")
+        assert x is None
+
+    def test_dlopen_filename(self):
+        path = ctypes.util.find_library(lib_m)
+        if not path:
+            py.test.skip("%s not found" % lib_m)
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            double cos(double x);
+        """)
+        m = ffi.dlopen(path)
+        x = m.cos(1.23)
+        assert x == math.cos(1.23)
+
+        m = ffi.dlopen(os.path.basename(path))
+        x = m.cos(1.23)
+        assert x == math.cos(1.23)
+
+    def test_dlopen_flags(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            double cos(double x);
+        """)
+        m = ffi.dlopen(lib_m, ffi.RTLD_LAZY | ffi.RTLD_LOCAL)
+        x = m.cos(1.23)
+        assert x == math.cos(1.23)
+
+    def test_dlopen_constant(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            #define FOOBAR 42
+            static const float baz = 42.5;   /* not visible */
+            double sin(double x);
+        """)
+        m = ffi.dlopen(lib_m)
+        assert m.FOOBAR == 42
+        py.test.raises(NotImplementedError, "m.baz")
+
+    def test_tlsalloc(self):
+        if sys.platform != 'win32':
+            py.test.skip("win32 only")
+        if self.Backend is CTypesBackend:
+            py.test.skip("ctypes complains on wrong calling conv")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("long TlsAlloc(void); int TlsFree(long);")
+        lib = ffi.dlopen('KERNEL32.DLL')
+        x = lib.TlsAlloc()
+        assert x != 0
+        y = lib.TlsFree(x)
+        assert y != 0
+
+    def test_fputs(self):
+        if not sys.platform.startswith('linux'):
+            py.test.skip("probably no symbol 'stderr' in the lib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int fputs(const char *, void *);
+            void *stderr;
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        ffi.C.fputs   # fetch before capturing, for easier debugging
+        with FdWriteCapture() as fd:
+            ffi.C.fputs(b"hello\n", ffi.C.stderr)
+            ffi.C.fputs(b"  world\n", ffi.C.stderr)
+        res = fd.getvalue()
+        assert res == b'hello\n  world\n'
+
+    def test_fputs_without_const(self):
+        if not sys.platform.startswith('linux'):
+            py.test.skip("probably no symbol 'stderr' in the lib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int fputs(char *, void *);
+            void *stderr;
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        ffi.C.fputs   # fetch before capturing, for easier debugging
+        with FdWriteCapture() as fd:
+            ffi.C.fputs(b"hello\n", ffi.C.stderr)
+            ffi.C.fputs(b"  world\n", ffi.C.stderr)
+        res = fd.getvalue()
+        assert res == b'hello\n  world\n'
+
+    def test_vararg(self):
+        if not sys.platform.startswith('linux'):
+            py.test.skip("probably no symbol 'stderr' in the lib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+           int fprintf(void *, const char *format, ...);
+           void *stderr;
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        with FdWriteCapture() as fd:
+            ffi.C.fprintf(ffi.C.stderr, b"hello with no arguments\n")
+            ffi.C.fprintf(ffi.C.stderr,
+                          b"hello, %s!\n", ffi.new("char[]", b"world"))
+            ffi.C.fprintf(ffi.C.stderr,
+                          ffi.new("char[]", b"hello, %s!\n"),
+                          ffi.new("char[]", b"world2"))
+            ffi.C.fprintf(ffi.C.stderr,
+                          b"hello int %d long %ld long long %lld\n",
+                          ffi.cast("int", 42),
+                          ffi.cast("long", 84),
+                          ffi.cast("long long", 168))
+            ffi.C.fprintf(ffi.C.stderr, b"hello %p\n", ffi.NULL)
+        res = fd.getvalue()
+        assert res == (b"hello with no arguments\n"
+                       b"hello, world!\n"
+                       b"hello, world2!\n"
+                       b"hello int 42 long 84 long long 168\n"
+                       b"hello (nil)\n")
+
+    def test_must_specify_type_of_vararg(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+           int printf(const char *format, ...);
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        e = py.test.raises(TypeError, ffi.C.printf, b"hello %d\n", 42)
+        assert str(e.value) == ("argument 2 passed in the variadic part "
+                                "needs to be a cdata object (got int)")
+
+    def test_function_has_a_c_type(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int puts(const char *);
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        fptr = ffi.C.puts
+        assert ffi.typeof(fptr) == ffi.typeof("int(*)(const char*)")
+        if self.Backend is CTypesBackend:
+            assert repr(fptr).startswith("<cdata 'int puts(char *)' 0x")
+
+    def test_function_pointer(self):
+        ffi = FFI(backend=self.Backend())
+        def cb(charp):
+            assert repr(charp).startswith("<cdata 'char *' 0x")
+            return 42
+        fptr = ffi.callback("int(*)(const char *txt)", cb)
+        assert fptr != ffi.callback("int(*)(const char *)", cb)
+        assert repr(fptr) == "<cdata 'int(*)(char *)' calling %r>" % (cb,)
+        res = fptr(b"Hello")
+        assert res == 42
+        #
+        if not sys.platform.startswith('linux'):
+            py.test.skip("probably no symbol 'stderr' in the lib")
+        ffi.cdef("""
+            int fputs(const char *, void *);
+            void *stderr;
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        fptr = ffi.cast("int(*)(const char *txt, void *)", ffi.C.fputs)
+        assert fptr == ffi.C.fputs
+        assert repr(fptr).startswith("<cdata 'int(*)(char *, void *)' 0x")
+        with FdWriteCapture() as fd:
+            fptr(b"world\n", ffi.C.stderr)
+        res = fd.getvalue()
+        assert res == b'world\n'
+
+    def test_callback_returning_void(self):
+        ffi = FFI(backend=self.Backend())
+        for returnvalue in [None, 42]:
+            def cb():
+                return returnvalue
+            fptr = ffi.callback("void(*)(void)", cb)
+            old_stderr = sys.stderr
+            try:
+                sys.stderr = StringIO()
+                returned = fptr()
+                printed = sys.stderr.getvalue()
+            finally:
+                sys.stderr = old_stderr
+            assert returned is None
+            if returnvalue is None:
+                assert printed == ''
+            else:
+                assert "None" in printed
+
+    def test_passing_array(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int strlen(char[]);
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        p = ffi.new("char[]", b"hello")
+        res = ffi.C.strlen(p)
+        assert res == 5
+
+    def test_write_variable(self):
+        if not sys.platform.startswith('linux'):
+            py.test.skip("probably no symbol 'stdout' in the lib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            void *stdout;
+        """)
+        needs_dlopen_none()
+        C = ffi.dlopen(None)
+        pout = C.stdout
+        C.stdout = ffi.NULL
+        assert C.stdout == ffi.NULL
+        C.stdout = pout
+        assert C.stdout == pout
+
+    def test_strchr(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            char *strchr(const char *s, int c);
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        p = ffi.new("char[]", b"hello world!")
+        q = ffi.C.strchr(p, ord('w'))
+        assert ffi.string(q) == b"world!"
+
+    def test_function_with_struct_argument(self):
+        if sys.platform == 'win32':
+            py.test.skip("no 'inet_ntoa'")
+        if (self.Backend is CTypesBackend and
+            '__pypy__' in sys.builtin_module_names):
+            py.test.skip("ctypes limitation on pypy")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct in_addr { unsigned int s_addr; };
+            char *inet_ntoa(struct in_addr in);
+        """)
+        needs_dlopen_none()
+        ffi.C = ffi.dlopen(None)
+        ina = ffi.new("struct in_addr *", [0x04040404])
+        a = ffi.C.inet_ntoa(ina[0])
+        assert ffi.string(a) == b'4.4.4.4'
+
+    def test_function_typedef(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            typedef double func_t(double);
+            func_t sin;
+        """)
+        m = ffi.dlopen(lib_m)
+        x = m.sin(1.23)
+        assert x == math.sin(1.23)
+
+    def test_fputs_custom_FILE(self):
+        if self.Backend is CTypesBackend:
+            py.test.skip("FILE not supported with the ctypes backend")
+        filename = str(udir.join('fputs_custom_FILE'))
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("int fputs(const char *, FILE *);")
+        needs_dlopen_none()
+        C = ffi.dlopen(None)
+        with open(filename, 'wb') as f:
+            f.write(b'[')
+            C.fputs(b"hello from custom file", f)
+            f.write(b'][')
+            C.fputs(b"some more output", f)
+            f.write(b']')
+        with open(filename, 'rb') as f:
+            res = f.read()
+        assert res == b'[hello from custom file][some more output]'
+
+    def test_constants_on_lib(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""enum foo_e { AA, BB, CC=5, DD };
+                    typedef enum { EE=-5, FF } some_enum_t;""")
+        needs_dlopen_none()
+        lib = ffi.dlopen(None)
+        assert lib.AA == 0
+        assert lib.BB == 1
+        assert lib.CC == 5
+        assert lib.DD == 6
+        assert lib.EE == -5
+        assert lib.FF == -4
+
+    def test_void_star_accepts_string(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""int strlen(const void *);""")
+        needs_dlopen_none()
+        lib = ffi.dlopen(None)
+        res = lib.strlen(b"hello")
+        assert res == 5
+
+    def test_signed_char_star_accepts_string(self):
+        if self.Backend is CTypesBackend:
+            py.test.skip("not supported by the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""int strlen(signed char *);""")
+        needs_dlopen_none()
+        lib = ffi.dlopen(None)
+        res = lib.strlen(b"hello")
+        assert res == 5
+
+    def test_unsigned_char_star_accepts_string(self):
+        if self.Backend is CTypesBackend:
+            py.test.skip("not supported by the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""int strlen(unsigned char *);""")
+        needs_dlopen_none()
+        lib = ffi.dlopen(None)
+        res = lib.strlen(b"hello")
+        assert res == 5
+
+    def test_missing_function(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int nonexistent();
+        """)
+        m = ffi.dlopen(lib_m)
+        assert not hasattr(m, 'nonexistent')
+
+    def test_wraps_from_stdlib(self):
+        import functools
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            double sin(double x);
+        """)
+        def my_decorator(f):
+            @functools.wraps(f)
+            def wrapper(*args):
+                return f(*args) + 100
+            return wrapper
+        m = ffi.dlopen(lib_m)
+        sin100 = my_decorator(m.sin)
+        x = sin100(1.23)
+        assert x == math.sin(1.23) + 100
+
+    def test_free_callback_cycle(self):
+        if self.Backend is CTypesBackend:
+            py.test.skip("seems to fail with the ctypes backend on windows")
+        import weakref
+        def make_callback(data):
+            container = [data]
+            callback = ffi.callback('int()', lambda: len(container))
+            container.append(callback)
+            # Ref cycle: callback -> lambda (closure) -> container -> callback
+            return callback
+
+        class Data(object):
+            pass
+        ffi = FFI(backend=self.Backend())
+        data = Data()
+        callback = make_callback(data)
+        wr = weakref.ref(data)
+        del callback, data
+        for i in range(3):
+            if wr() is not None:
+                import gc; gc.collect()
+        assert wr() is None    # 'data' does not leak
+
+    def test_windows_stdcall(self):
+        if sys.platform != 'win32':
+            py.test.skip("Windows-only test")
+        if self.Backend is CTypesBackend:
+            py.test.skip("not with the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency);
+        """)
+        m = ffi.dlopen("Kernel32.dll")
+        p_freq = ffi.new("LONGLONG *")
+        res = m.QueryPerformanceFrequency(p_freq)
+        assert res != 0
+        assert p_freq[0] != 0
+
+    def test_explicit_cdecl_stdcall(self):
+        if sys.platform != 'win32':
+            py.test.skip("Windows-only test")
+        if self.Backend is CTypesBackend:
+            py.test.skip("not with the ctypes backend")
+        win64 = (sys.maxsize > 2**32)
+        #
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency);
+        """)
+        m = ffi.dlopen("Kernel32.dll")
+        tp = ffi.typeof(m.QueryPerformanceFrequency)
+        assert str(tp) == "<ctype 'int(*)(long long *)'>"
+        #
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            BOOL __cdecl QueryPerformanceFrequency(LONGLONG *lpFrequency);
+        """)
+        m = ffi.dlopen("Kernel32.dll")
+        tpc = ffi.typeof(m.QueryPerformanceFrequency)
+        assert tpc is tp
+        #
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            BOOL WINAPI QueryPerformanceFrequency(LONGLONG *lpFrequency);
+        """)
+        m = ffi.dlopen("Kernel32.dll")
+        tps = ffi.typeof(m.QueryPerformanceFrequency)
+        if win64:
+            assert tps is tpc
+        else:
+            assert tps is not tpc
+            assert str(tps) == "<ctype 'int(__stdcall *)(long long *)'>"
+        #
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("typedef int (__cdecl *fnc_t)(int);")
+        ffi.cdef("typedef int (__stdcall *fns_t)(int);")
+        tpc = ffi.typeof("fnc_t")
+        tps = ffi.typeof("fns_t")
+        assert str(tpc) == "<ctype 'int(*)(int)'>"
+        if win64:
+            assert tps is tpc
+        else:
+            assert str(tps) == "<ctype 'int(__stdcall *)(int)'>"
+        #
+        fnc = ffi.cast("fnc_t", 0)
+        fns = ffi.cast("fns_t", 0)
+        ffi.new("fnc_t[]", [fnc])
+        if not win64:
+            py.test.raises(TypeError, ffi.new, "fnc_t[]", [fns])
+            py.test.raises(TypeError, ffi.new, "fns_t[]", [fnc])
+        ffi.new("fns_t[]", [fns])
+
+    def test_stdcall_only_on_windows(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("double __stdcall sin(double x);")     # stdcall ignored
+        m = ffi.dlopen(lib_m)
+        if (sys.platform == 'win32' and sys.maxsize < 2**32 and
+                self.Backend is not CTypesBackend):
+            assert "double(__stdcall *)(double)" in str(ffi.typeof(m.sin))
+        else:
+            assert "double(*)(double)" in str(ffi.typeof(m.sin))
+        x = m.sin(1.23)
+        assert x == math.sin(1.23)
+
+    def test_dir_on_dlopen_lib(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            typedef enum { MYE1, MYE2 } myenum_t;
+            double myfunc(double);
+            double myvar;
+            const double myconst;
+            #define MYFOO 42
+        """)
+        m = ffi.dlopen(lib_m)
+        assert dir(m) == ['MYE1', 'MYE2', 'MYFOO', 'myconst', 'myfunc', 'myvar']
+
+    def test_dlclose(self):
+        if self.Backend is CTypesBackend:
+            py.test.skip("not with the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("int foobar(void); int foobaz;")
+        lib = ffi.dlopen(lib_m)
+        ffi.dlclose(lib)
+        e = py.test.raises(ValueError, getattr, lib, 'foobar')
+        assert str(e.value).startswith("library '")
+        assert str(e.value).endswith("' has already been closed")
+        e = py.test.raises(ValueError, getattr, lib, 'foobaz')
+        assert str(e.value).startswith("library '")
+        assert str(e.value).endswith("' has already been closed")
+        e = py.test.raises(ValueError, setattr, lib, 'foobaz', 42)
+        assert str(e.value).startswith("library '")
+        assert str(e.value).endswith("' has already been closed")
+        ffi.dlclose(lib)    # does not raise
diff --git a/testing/cffi0/test_model.py b/testing/cffi0/test_model.py
new file mode 100644
index 0000000..bb653ca
--- /dev/null
+++ b/testing/cffi0/test_model.py
@@ -0,0 +1,111 @@
+from cffi.model import *
+
+
+def test_void_type():
+    assert void_type.get_c_name() == "void"
+    assert void_type.get_c_name("foo") == "void foo"
+    assert void_type.get_c_name("*foo") == "void *foo"
+
+def test_primitive_type():
+    int_type = PrimitiveType("int")
+    assert int_type.get_c_name() == "int"
+    assert int_type.get_c_name("foo") == "int foo"
+    assert int_type.get_c_name("*foo") == "int *foo"
+    assert int_type.get_c_name("[5]") == "int[5]"
+
+def test_raw_function_type():
+    int_type = PrimitiveType("int")
+    fn_type = RawFunctionType([], int_type, False)
+    assert fn_type.get_c_name() == "int()(void)"
+    assert fn_type.get_c_name("*") == "int( *)(void)"
+    assert fn_type.get_c_name("*foo") == "int( *foo)(void)"
+    fn_type = RawFunctionType([int_type], int_type, False)
+    assert fn_type.get_c_name() == "int()(int)"
+    fn_type = RawFunctionType([int_type] * 2, int_type, False)
+    assert fn_type.get_c_name() == "int()(int, int)"
+    #
+    fn_type = RawFunctionType([int_type], int_type, True)
+    assert fn_type.get_c_name() == "int()(int, ...)"
+    assert fn_type.get_c_name("*foo") == "int( *foo)(int, ...)"
+    #
+    res_type = FunctionPtrType([int_type], int_type, True)
+    fn_type = RawFunctionType([int_type], res_type, True)
+    assert fn_type.get_c_name("x") == "int(*( x)(int, ...))(int, ...)"
+
+def test_function_ptr_type():
+    int_type = PrimitiveType("int")
+    fn_type = FunctionPtrType([], int_type, False)
+    assert fn_type.get_c_name() == "int(*)(void)"
+    assert fn_type.get_c_name("*") == "int(* *)(void)"
+    assert fn_type.get_c_name("*foo") == "int(* *foo)(void)"
+    fn_type = FunctionPtrType([int_type], int_type, False)
+    assert fn_type.get_c_name() == "int(*)(int)"
+    fn_type = FunctionPtrType([int_type] * 2, int_type, False)
+    assert fn_type.get_c_name() == "int(*)(int, int)"
+    #
+    fn_type = FunctionPtrType([int_type], int_type, True)
+    assert fn_type.get_c_name() == "int(*)(int, ...)"
+
+def test_pointer_type():
+    ptr_type = PointerType(PrimitiveType("int"))
+    assert ptr_type.get_c_name("x") == "int * x"
+
+def test_const_pointer_type():
+    ptr_type = ConstPointerType(PrimitiveType("int"))
+    assert ptr_type.get_c_name("x") == "int const * x"
+    ptr_type = ConstPointerType(ArrayType(PrimitiveType("int"), 5))
+    assert ptr_type.get_c_name("") == "int(const *)[5]"
+    assert ptr_type.get_c_name("*x") == "int(const * *x)[5]"
+
+def test_qual_pointer_type():
+    ptr_type = PointerType(PrimitiveType("long long"), Q_RESTRICT)
+    assert ptr_type.get_c_name("") == "long long __restrict *"
+    assert const_voidp_type.get_c_name("") == "void const *"
+
+def test_unknown_pointer_type():
+    ptr_type = unknown_ptr_type("foo_p")
+    assert ptr_type.get_c_name("") == "foo_p"
+    assert ptr_type.get_c_name("x") == "foo_p x"
+
+def test_unknown_type():
+    u_type = unknown_type("foo_t")
+    assert u_type.get_c_name("") == "foo_t"
+    assert u_type.get_c_name("x") == "foo_t x"
+
+def test_array_type():
+    a_type = ArrayType(PrimitiveType("int"), None)
+    assert a_type.get_c_name("") == "int[]"
+    assert a_type.get_c_name("x") == "int x[]"
+    assert a_type.get_c_name("*x") == "int(*x)[]"
+    assert a_type.get_c_name(" *x") == "int(*x)[]"
+    assert a_type.get_c_name("[5]") == "int[5][]"
+    a_type = ArrayType(unknown_type("foo_t"), 5)
+    assert a_type.get_c_name("") == "foo_t[5]"
+    assert a_type.get_c_name("x") == "foo_t x[5]"
+    assert a_type.get_c_name("*x") == "foo_t(*x)[5]"
+    a_type = ArrayType(unknown_ptr_type("foo_p"), None)
+    assert a_type.get_c_name("") == "foo_p[]"
+    assert a_type.get_c_name("x") == "foo_p x[]"
+    assert a_type.get_c_name("*x") == "foo_p(*x)[]"
+    a_type = ArrayType(ConstPointerType(PrimitiveType("int")), None)
+    assert a_type.get_c_name("") == "int const *[]"
+    assert a_type.get_c_name("x") == "int const * x[]"
+    assert a_type.get_c_name("*x") == "int const *(*x)[]"
+    fn_type = FunctionPtrType([], PrimitiveType("int"), False)
+    a_type = ArrayType(fn_type, 5)
+    assert a_type.get_c_name("") == "int(*[5])(void)"
+    assert a_type.get_c_name("x") == "int(* x[5])(void)"
+    assert a_type.get_c_name("*x") == "int(*(*x)[5])(void)"
+
+def test_struct_type():
+    struct_type = StructType("foo_s", None, None, None)
+    assert struct_type.get_c_name() == "struct foo_s"
+    assert struct_type.get_c_name("*x") == "struct foo_s *x"
+
+def test_union_type():
+    union_type = UnionType("foo_s", None, None, None)
+    assert union_type.get_c_name() == "union foo_s"
+
+def test_enum_type():
+    enum_type = EnumType("foo_e", [], [])
+    assert enum_type.get_c_name() == "enum foo_e"
diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py
new file mode 100644
index 0000000..a06df20
--- /dev/null
+++ b/testing/cffi0/test_ownlib.py
@@ -0,0 +1,373 @@
+import py, sys, os
+import subprocess, weakref
+from cffi import FFI
+from cffi.backend_ctypes import CTypesBackend
+from testing.support import u
+
+
+SOURCE = """\
+#include <errno.h>
+
+#ifdef _WIN32
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT
+#endif
+
+EXPORT int test_getting_errno(void) {
+    errno = 123;
+    return -1;
+}
+
+EXPORT int test_setting_errno(void) {
+    return errno;
+};
+
+typedef struct {
+    long x;
+    long y;
+} POINT;
+
+typedef struct {
+    long left;
+    long top;
+    long right;
+    long bottom;
+} RECT;
+
+
+EXPORT int PointInRect(RECT *prc, POINT pt)
+{
+    if (pt.x < prc->left)
+        return 0;
+    if (pt.x > prc->right)
+        return 0;
+    if (pt.y < prc->top)
+        return 0;
+    if (pt.y > prc->bottom)
+        return 0;
+    return 1;
+};
+
+EXPORT long left = 10;
+EXPORT long top = 20;
+EXPORT long right = 30;
+EXPORT long bottom = 40;
+
+EXPORT RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr,
+                        RECT *er, POINT fp, RECT gr)
+{
+    /*Check input */
+    if (ar.left + br->left + dr.left + er->left + gr.left != left * 5)
+    {
+        ar.left = 100;
+        return ar;
+    }
+    if (ar.right + br->right + dr.right + er->right + gr.right != right * 5)
+    {
+        ar.right = 100;
+        return ar;
+    }
+    if (cp.x != fp.x)
+    {
+        ar.left = -100;
+    }
+    if (cp.y != fp.y)
+    {
+        ar.left = -200;
+    }
+    switch(i)
+    {
+    case 0:
+        return ar;
+        break;
+    case 1:
+        return dr;
+        break;
+    case 2:
+        return gr;
+        break;
+
+    }
+    return ar;
+}
+
+EXPORT int my_array[7] = {0, 1, 2, 3, 4, 5, 6};
+
+EXPORT unsigned short foo_2bytes(unsigned short a)
+{
+    return (unsigned short)(a + 42);
+}
+EXPORT unsigned int foo_4bytes(unsigned int a)
+{
+    return (unsigned int)(a + 42);
+}
+
+EXPORT void modify_struct_value(RECT r)
+{
+    r.left = r.right = r.top = r.bottom = 500;
+}
+"""
+
+class TestOwnLib(object):
+    Backend = CTypesBackend
+
+    def setup_class(cls):
+        cls.module = None
+        from testing.udir import udir
+        udir.join('testownlib.c').write(SOURCE)
+        if sys.platform == 'win32':
+            # did we already build it?
+            if cls.Backend is CTypesBackend:
+                dll_path = str(udir) + '\\testownlib1.dll'   # only ascii for the ctypes backend
+            else:
+                dll_path = str(udir) + '\\' + (u+'testownlib\u03be.dll')   # non-ascii char
+            if os.path.exists(dll_path):
+                cls.module = dll_path
+                return
+            # try (not too hard) to find the version used to compile this python
+            # no mingw
+            from distutils.msvc9compiler import get_build_version
+            version = get_build_version()
+            toolskey = "VS%0.f0COMNTOOLS" % version
+            toolsdir = os.environ.get(toolskey, None)
+            if toolsdir is None:
+                return
+            productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
+            productdir = os.path.abspath(productdir)
+            vcvarsall = os.path.join(productdir, "vcvarsall.bat")
+            # 64?
+            arch = 'x86'
+            if sys.maxsize > 2**32:
+                arch = 'amd64'
+            if os.path.isfile(vcvarsall):
+                cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \
+                        ' /LD /Fetestownlib.dll'
+                subprocess.check_call(cmd, cwd = str(udir), shell=True)
+                os.rename(str(udir) + '\\testownlib.dll', dll_path)
+                cls.module = dll_path
+        else:
+            encoded = None
+            if cls.Backend is not CTypesBackend:
+                try:
+                    unicode_name = u+'testownlibcaf\xe9'
+                    encoded = unicode_name.encode(sys.getfilesystemencoding())
+                    if sys.version_info >= (3,):
+                        encoded = str(unicode_name)
+                except UnicodeEncodeError:
+                    pass
+            if encoded is None:
+                unicode_name = u+'testownlib'
+                encoded = str(unicode_name)
+            subprocess.check_call(
+                "cc testownlib.c -shared -fPIC -o '%s.so'" % (encoded,),
+                cwd=str(udir), shell=True)
+            cls.module = os.path.join(str(udir), unicode_name + (u+'.so'))
+        print(repr(cls.module))
+
+    def test_getting_errno(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        if sys.platform == 'win32':
+            py.test.skip("fails, errno at multiple addresses")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int test_getting_errno(void);
+        """)
+        ownlib = ffi.dlopen(self.module)
+        res = ownlib.test_getting_errno()
+        assert res == -1
+        assert ffi.errno == 123
+
+    def test_setting_errno(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        if sys.platform == 'win32':
+            py.test.skip("fails, errno at multiple addresses")
+        if self.Backend is CTypesBackend and '__pypy__' in sys.modules:
+            py.test.skip("XXX errno issue with ctypes on pypy?")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int test_setting_errno(void);
+        """)
+        ownlib = ffi.dlopen(self.module)
+        ffi.errno = 42
+        res = ownlib.test_setting_errno()
+        assert res == 42
+        assert ffi.errno == 42
+
+    def test_my_array_7(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int my_array[7];
+        """)
+        ownlib = ffi.dlopen(self.module)
+        for i in range(7):
+            assert ownlib.my_array[i] == i
+        assert len(ownlib.my_array) == 7
+        if self.Backend is CTypesBackend:
+            py.test.skip("not supported by the ctypes backend")
+        ownlib.my_array = list(range(10, 17))
+        for i in range(7):
+            assert ownlib.my_array[i] == 10 + i
+        ownlib.my_array = list(range(7))
+        for i in range(7):
+            assert ownlib.my_array[i] == i
+
+    def test_my_array_no_length(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        if self.Backend is CTypesBackend:
+            py.test.skip("not supported by the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int my_array[];
+        """)
+        ownlib = ffi.dlopen(self.module)
+        for i in range(7):
+            assert ownlib.my_array[i] == i
+        py.test.raises(TypeError, len, ownlib.my_array)
+        ownlib.my_array = list(range(10, 17))
+        for i in range(7):
+            assert ownlib.my_array[i] == 10 + i
+        ownlib.my_array = list(range(7))
+        for i in range(7):
+            assert ownlib.my_array[i] == i
+
+    def test_keepalive_lib(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int test_getting_errno(void);
+        """)
+        ownlib = ffi.dlopen(self.module)
+        ffi_r = weakref.ref(ffi)
+        ownlib_r = weakref.ref(ownlib)
+        func = ownlib.test_getting_errno
+        del ffi
+        import gc; gc.collect()       # ownlib stays alive
+        assert ownlib_r() is not None
+        assert ffi_r() is not None    # kept alive by ownlib
+        res = func()
+        assert res == -1
+
+    def test_keepalive_ffi(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            int test_getting_errno(void);
+        """)
+        ownlib = ffi.dlopen(self.module)
+        ffi_r = weakref.ref(ffi)
+        ownlib_r = weakref.ref(ownlib)
+        func = ownlib.test_getting_errno
+        del ownlib
+        import gc; gc.collect()       # ffi stays alive
+        assert ffi_r() is not None
+        assert ownlib_r() is not None # kept alive by ffi
+        res = func()
+        assert res == -1
+        if sys.platform != 'win32':  # else, errno at multiple addresses
+            assert ffi.errno == 123
+
+    def test_struct_by_value(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            typedef struct {
+                long x;
+                long y;
+            } POINT;
+
+            typedef struct {
+                long left;
+                long top;
+                long right;
+                long bottom;
+            } RECT;
+            
+            long left, top, right, bottom;
+
+            RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr,
+                        RECT *er, POINT fp, RECT gr);
+        """)
+        ownlib = ffi.dlopen(self.module)
+
+        rect = ffi.new('RECT[1]')
+        pt = ffi.new('POINT[1]')
+        pt[0].x = 15
+        pt[0].y = 25
+        rect[0].left = ownlib.left
+        rect[0].right = ownlib.right
+        rect[0].top = ownlib.top
+        rect[0].bottom = ownlib.bottom
+        
+        for i in range(4):
+            ret = ownlib.ReturnRect(i, rect[0], rect, pt[0], rect[0],
+                                    rect, pt[0], rect[0])
+            assert ret.left == ownlib.left
+            assert ret.right == ownlib.right
+            assert ret.top == ownlib.top
+            assert ret.bottom == ownlib.bottom
+
+    def test_addressof_lib(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        if self.Backend is CTypesBackend:
+            py.test.skip("not implemented with the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("long left; int test_getting_errno(void);")
+        lib = ffi.dlopen(self.module)
+        lib.left = 123456
+        p = ffi.addressof(lib, "left")
+        assert ffi.typeof(p) == ffi.typeof("long *")
+        assert p[0] == 123456
+        p[0] += 1
+        assert lib.left == 123457
+        pfn = ffi.addressof(lib, "test_getting_errno")
+        assert ffi.typeof(pfn) == ffi.typeof("int(*)(void)")
+        assert pfn == lib.test_getting_errno
+
+    def test_char16_char32_t(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        if self.Backend is CTypesBackend:
+            py.test.skip("not implemented with the ctypes backend")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            char16_t foo_2bytes(char16_t);
+            char32_t foo_4bytes(char32_t);
+        """)
+        lib = ffi.dlopen(self.module)
+        assert lib.foo_2bytes(u+'\u1234') == u+'\u125e'
+        assert lib.foo_4bytes(u+'\u1234') == u+'\u125e'
+        assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f'
+
+    def test_modify_struct_value(self):
+        if self.module is None:
+            py.test.skip("fix the auto-generation of the tiny test lib")
+        if self.Backend is CTypesBackend:
+            py.test.skip("fails with the ctypes backend on some architectures")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            typedef struct {
+                long left;
+                long top;
+                long right;
+                long bottom;
+            } RECT;
+
+            void modify_struct_value(RECT r);
+        """)
+        lib = ffi.dlopen(self.module)
+        s = ffi.new("RECT *", [11, 22, 33, 44])
+        lib.modify_struct_value(s[0])
+        assert s.left == 11
+        assert s.top == 22
+        assert s.right == 33
+        assert s.bottom == 44
diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py
new file mode 100644
index 0000000..2d75850
--- /dev/null
+++ b/testing/cffi0/test_parsing.py
@@ -0,0 +1,468 @@
+import py, sys, re
+from cffi import FFI, FFIError, CDefError, VerificationError
+from .backend_tests import needs_dlopen_none
+
+
+class FakeBackend(object):
+
+    def nonstandard_integer_types(self):
+        return {}
+
+    def sizeof(self, name):
+        return 1
+
+    def load_library(self, name, flags):
+        if sys.platform == 'win32':
+            assert name is None or "msvcr" in name
+        else:
+            assert name is None or "libc" in name or "libm" in name
+        return FakeLibrary()
+
+    def new_function_type(self, args, result, has_varargs):
+        args = [arg.cdecl for arg in args]
+        result = result.cdecl
+        return FakeType(
+            '<func (%s), %s, %s>' % (', '.join(args), result, has_varargs))
+
+    def new_primitive_type(self, name):
+        assert name == name.lower()
+        return FakeType('<%s>' % name)
+
+    def new_pointer_type(self, itemtype):
+        return FakeType('<pointer to %s>' % (itemtype,))
+
+    def new_struct_type(self, name):
+        return FakeStruct(name)
+
+    def complete_struct_or_union(self, s, fields, tp=None,
+                                 totalsize=-1, totalalignment=-1, sflags=0):
+        assert isinstance(s, FakeStruct)
+        s.fields = fields
+
+    def new_array_type(self, ptrtype, length):
+        return FakeType('<array %s x %s>' % (ptrtype, length))
+
+    def new_void_type(self):
+        return FakeType("<void>")
+    def cast(self, x, y):
+        return 'casted!'
+    def _get_types(self):
+        return "CData", "CType"
+
+    buffer = "buffer type"
+
+class FakeType(object):
+    def __init__(self, cdecl):
+        self.cdecl = cdecl
+    def __str__(self):
+        return self.cdecl
+
+class FakeStruct(object):
+    def __init__(self, name):
+        self.name = name
+    def __str__(self):
+        return ', '.join([str(y) + str(x) for x, y, z in self.fields])
+
+class FakeLibrary(object):
+
+    def load_function(self, BType, name):
+        return FakeFunction(BType, name)
+
+class FakeFunction(object):
+
+    def __init__(self, BType, name):
+        self.BType = str(BType)
+        self.name = name
+
+lib_m = "m"
+if sys.platform == 'win32':
+    #there is a small chance this fails on Mingw via environ $CC
+    import distutils.ccompiler
+    if distutils.ccompiler.get_default_compiler() == 'msvc':
+        lib_m = 'msvcrt'
+
+def test_simple():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("double sin(double x);")
+    m = ffi.dlopen(lib_m)
+    func = m.sin    # should be a callable on real backends
+    assert func.name == 'sin'
+    assert func.BType == '<func (<double>), <double>, False>'
+
+def test_pipe():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("int pipe(int pipefd[2]);")
+    needs_dlopen_none()
+    C = ffi.dlopen(None)
+    func = C.pipe
+    assert func.name == 'pipe'
+    assert func.BType == '<func (<pointer to <int>>), <int>, False>'
+
+def test_vararg():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("short foo(int, ...);")
+    needs_dlopen_none()
+    C = ffi.dlopen(None)
+    func = C.foo
+    assert func.name == 'foo'
+    assert func.BType == '<func (<int>), <short>, True>'
+
+def test_no_args():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("""
+        int foo(void);
+        """)
+    needs_dlopen_none()
+    C = ffi.dlopen(None)
+    assert C.foo.BType == '<func (), <int>, False>'
+
+def test_typedef():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("""
+        typedef unsigned int UInt;
+        typedef UInt UIntReally;
+        UInt foo(void);
+        """)
+    needs_dlopen_none()
+    C = ffi.dlopen(None)
+    assert str(ffi.typeof("UIntReally")) == '<unsigned int>'
+    assert C.foo.BType == '<func (), <unsigned int>, False>'
+
+def test_typedef_more_complex():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("""
+        typedef struct { int a, b; } foo_t, *foo_p;
+        int foo(foo_p[]);
+        """)
+    needs_dlopen_none()
+    C = ffi.dlopen(None)
+    assert str(ffi.typeof("foo_t")) == '<int>a, <int>b'
+    assert str(ffi.typeof("foo_p")) == '<pointer to <int>a, <int>b>'
+    assert C.foo.BType == ('<func (<pointer to <pointer to '
+                           '<int>a, <int>b>>), <int>, False>')
+
+def test_typedef_array_convert_array_to_pointer():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("""
+        typedef int (*fn_t)(int[5]);
+        """)
+    with ffi._lock:
+        type = ffi._parser.parse_type("fn_t")
+        BType = ffi._get_cached_btype(type)
+    assert str(BType) == '<func (<pointer to <int>>), <int>, False>'
+
+def test_remove_comments():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("""
+        double /*comment here*/ sin   // blah blah
+        /* multi-
+           line-
+           //comment */  (
+        // foo
+        double // bar      /* <- ignored, because it's in a comment itself
+        x, double/*several*//*comment*/y) /*on the same line*/
+        ;
+    """)
+    m = ffi.dlopen(lib_m)
+    func = m.sin
+    assert func.name == 'sin'
+    assert func.BType == '<func (<double>, <double>), <double>, False>'
+
+def test_remove_line_continuation_comments():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("""
+        double // blah \\
+                  more comments
+        x(void);
+        double // blah\\\\
+        y(void);
+        double // blah\\ \
+                  etc
+        z(void);
+    """)
+    m = ffi.dlopen(lib_m)
+    m.x
+    m.y
+    m.z
+
+def test_line_continuation_in_defines():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("""
+        #define ABC\\
+            42
+        #define BCD   \\
+            43
+    """)
+    m = ffi.dlopen(lib_m)
+    assert m.ABC == 42
+    assert m.BCD == 43
+
+def test_define_not_supported_for_now():
+    ffi = FFI(backend=FakeBackend())
+    e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"')
+    assert str(e.value) == (
+        'only supports one of the following syntax:\n'
+        '  #define FOO ...     (literally dot-dot-dot)\n'
+        '  #define FOO NUMBER  (with NUMBER an integer'
+                                    ' constant, decimal/hex/octal)\n'
+        'got:\n'
+        '  #define FOO "blah"')
+
+def test_unnamed_struct():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("typedef struct { int x; } foo_t;\n"
+             "typedef struct { int y; } *bar_p;\n")
+    assert 'typedef foo_t' in ffi._parser._declarations
+    assert 'typedef bar_p' in ffi._parser._declarations
+    assert 'anonymous foo_t' in ffi._parser._declarations
+    type_foo = ffi._parser.parse_type("foo_t")
+    type_bar = ffi._parser.parse_type("bar_p").totype
+    assert repr(type_foo) == "<foo_t>"
+    assert repr(type_bar) == "<struct $1>"
+    py.test.raises(VerificationError, type_bar.get_c_name)
+    assert type_foo.get_c_name() == "foo_t"
+
+def test_override():
+    ffi = FFI(backend=FakeBackend())
+    needs_dlopen_none()
+    C = ffi.dlopen(None)
+    ffi.cdef("int foo(void);")
+    py.test.raises(FFIError, ffi.cdef, "long foo(void);")
+    assert C.foo.BType == '<func (), <int>, False>'
+    ffi.cdef("long foo(void);", override=True)
+    assert C.foo.BType == '<func (), <long>, False>'
+
+def test_cannot_have_only_variadic_part():
+    # this checks that we get a sensible error if we try "int foo(...);"
+    ffi = FFI()
+    e = py.test.raises(CDefError, ffi.cdef, "int foo(...);")
+    assert str(e.value) == (
+           "<cdef source string>:1: foo: a function with only '(...)' "
+           "as argument is not correct C")
+
+def test_parse_error():
+    ffi = FFI()
+    e = py.test.raises(CDefError, ffi.cdef, " x y z ")
+    assert str(e.value).startswith(
+        'cannot parse "x y z"\n<cdef source string>:1:')
+    e = py.test.raises(CDefError, ffi.cdef, "\n\n\n x y z ")
+    assert str(e.value).startswith(
+        'cannot parse "x y z"\n<cdef source string>:4:')
+
+def test_error_custom_lineno():
+    ffi = FFI()
+    e = py.test.raises(CDefError, ffi.cdef, """
+# 42 "foobar"
+
+    a b c d
+    """)
+    assert str(e.value).startswith('parse error\nfoobar:43:')
+
+def test_cannot_declare_enum_later():
+    ffi = FFI()
+    e = py.test.raises(NotImplementedError, ffi.cdef,
+                       "typedef enum foo_e foo_t; enum foo_e { AA, BB };")
+    assert str(e.value) == (
+           "enum foo_e: the '{}' declaration should appear on the "
+           "first time the enum is mentioned, not later")
+
+def test_unknown_name():
+    ffi = FFI()
+    e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown", 0)
+    assert str(e.value) == "unknown identifier 'foobarbazunknown'"
+    e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown*", 0)
+    assert str(e.value).startswith('cannot parse "foobarbazunknown*"')
+    e = py.test.raises(CDefError, ffi.cast, "int(*)(foobarbazunknown)", 0)
+    assert str(e.value).startswith('cannot parse "int(*)(foobarbazunknown)"')
+
+def test_redefine_common_type():
+    prefix = "" if sys.version_info < (3,) else "b"
+    ffi = FFI()
+    ffi.cdef("typedef char FILE;")
+    assert repr(ffi.cast("FILE", 123)) == "<cdata 'char' %s'{'>" % prefix
+    ffi.cdef("typedef char int32_t;")
+    assert repr(ffi.cast("int32_t", 123)) == "<cdata 'char' %s'{'>" % prefix
+    ffi = FFI()
+    ffi.cdef("typedef int bool, *FILE;")
+    assert repr(ffi.cast("bool", 123)) == "<cdata 'int' 123>"
+    assert re.match(r"<cdata 'int [*]' 0[xX]?0*7[bB]>",
+                    repr(ffi.cast("FILE", 123)))
+    ffi = FFI()
+    ffi.cdef("typedef bool (*fn_t)(bool, bool);")   # "bool," but within "( )"
+
+def test_bool():
+    ffi = FFI()
+    ffi.cdef("void f(bool);")
+    #
+    ffi = FFI()
+    ffi.cdef("typedef _Bool bool; void f(bool);")
+
+def test_unknown_argument_type():
+    ffi = FFI()
+    e = py.test.raises(CDefError, ffi.cdef, "void f(foobarbazzz);")
+    assert str(e.value) == ("<cdef source string>:1: f arg 1:"
+                            " unknown type 'foobarbazzz' (if you meant"
+                            " to use the old C syntax of giving untyped"
+                            " arguments, it is not supported)")
+
+def test_void_renamed_as_only_arg():
+    ffi = FFI()
+    ffi.cdef("typedef void void_t1;"
+             "typedef void_t1 void_t;"
+             "typedef int (*func_t)(void_t);")
+    assert ffi.typeof("func_t").args == ()
+
+def test_WPARAM_on_windows():
+    if sys.platform != 'win32':
+        py.test.skip("Only for Windows")
+    ffi = FFI()
+    ffi.cdef("void f(WPARAM);")
+    #
+    # WPARAM -> UINT_PTR -> unsigned 32/64-bit integer
+    ffi = FFI()
+    value = int(ffi.cast("WPARAM", -42))
+    assert value == sys.maxsize * 2 - 40
+
+def test__is_constant_globalvar():
+    for input, expected_output in [
+        ("int a;",          False),
+        ("const int a;",    True),
+        ("int *a;",         False),
+        ("const int *a;",   False),
+        ("int const *a;",   False),
+        ("int *const a;",   True),
+        ("int a[5];",       False),
+        ("const int a[5];", False),
+        ("int *a[5];",      False),
+        ("const int *a[5];", False),
+        ("int const *a[5];", False),
+        ("int *const a[5];", False),
+        ("int a[5][6];",       False),
+        ("const int a[5][6];", False),
+        ]:
+        ffi = FFI()
+        ffi.cdef(input)
+        declarations = ffi._parser._declarations
+        assert ('constant a' in declarations) == expected_output
+        assert ('variable a' in declarations) == (not expected_output)
+
+def test_restrict():
+    from cffi import model
+    for input, expected_output in [
+        ("int a;",             False),
+        ("restrict int a;",    True),
+        ("int *a;",            False),
+        ]:
+        ffi = FFI()
+        ffi.cdef(input)
+        tp, quals = ffi._parser._declarations['variable a']
+        assert bool(quals & model.Q_RESTRICT) == expected_output
+
+def test_different_const_funcptr_types():
+    lst = []
+    for input in [
+        "int(*)(int *a)",
+        "int(*)(int const *a)",
+        "int(*)(int * const a)",
+        "int(*)(int const a[])"]:
+        ffi = FFI(backend=FakeBackend())
+        lst.append(ffi._parser.parse_type(input))
+    assert lst[0] != lst[1]
+    assert lst[0] == lst[2]
+    assert lst[1] == lst[3]
+
+def test_const_pointer_to_pointer():
+    from cffi import model
+    ffi = FFI(backend=FakeBackend())
+    #
+    tp, qual = ffi._parser.parse_type_and_quals("char * * (* const)")
+    assert (str(tp), qual) == ("<char * * *>", model.Q_CONST)
+    tp, qual = ffi._parser.parse_type_and_quals("char * (* const (*))")
+    assert (str(tp), qual) == ("<char * * const *>", 0)
+    tp, qual = ffi._parser.parse_type_and_quals("char (* const (* (*)))")
+    assert (str(tp), qual) == ("<char * const * *>", 0)
+    tp, qual = ffi._parser.parse_type_and_quals("char const * * *")
+    assert (str(tp), qual) == ("<char const * * *>", 0)
+    tp, qual = ffi._parser.parse_type_and_quals("const char * * *")
+    assert (str(tp), qual) == ("<char const * * *>", 0)
+    #
+    tp, qual = ffi._parser.parse_type_and_quals("char * * * const const")
+    assert (str(tp), qual) == ("<char * * *>", model.Q_CONST)
+    tp, qual = ffi._parser.parse_type_and_quals("char * * volatile *")
+    assert (str(tp), qual) == ("<char * * volatile *>", 0)
+    tp, qual = ffi._parser.parse_type_and_quals("char * volatile restrict * *")
+    assert (str(tp), qual) == ("<char * __restrict volatile * *>", 0)
+    tp, qual = ffi._parser.parse_type_and_quals("char const volatile * * *")
+    assert (str(tp), qual) == ("<char volatile const * * *>", 0)
+    tp, qual = ffi._parser.parse_type_and_quals("const char * * *")
+    assert (str(tp), qual) == ("<char const * * *>", 0)
+    #
+    tp, qual = ffi._parser.parse_type_and_quals(
+        "int(char*const*, short****const*)")
+    assert (str(tp), qual) == (
+        "<int()(char * const *, short * * * * const *)>", 0)
+    tp, qual = ffi._parser.parse_type_and_quals(
+        "char*const*(short*const****)")
+    assert (str(tp), qual) == (
+        "<char * const *()(short * const * * * *)>", 0)
+
+def test_enum():
+    ffi = FFI()
+    ffi.cdef("""
+        enum Enum { POS = +1, TWO = 2, NIL = 0, NEG = -1, OP = (POS+TWO)-1};
+        """)
+    needs_dlopen_none()
+    C = ffi.dlopen(None)
+    assert C.POS == 1
+    assert C.TWO == 2
+    assert C.NIL == 0
+    assert C.NEG == -1
+    assert C.OP == 2
+
+def test_stdcall():
+    ffi = FFI()
+    tp = ffi.typeof("int(*)(int __stdcall x(int),"
+                    "       long (__cdecl*y)(void),"
+                    "       short(WINAPI *z)(short))")
+    if sys.platform == 'win32' and sys.maxsize < 2**32:
+        stdcall = '__stdcall '
+    else:
+        stdcall = ''
+    assert str(tp) == (
+        "<ctype 'int(*)(int(%s*)(int), "
+                        "long(*)(), "
+                        "short(%s*)(short))'>" % (stdcall, stdcall))
+
+def test_extern_python():
+    ffi = FFI()
+    ffi.cdef("""
+        int bok(int, int);
+        extern "Python" int foobar(int, int);
+        int baz(int, int);
+    """)
+    assert sorted(ffi._parser._declarations) == [
+        'extern_python foobar', 'function baz', 'function bok']
+    assert (ffi._parser._declarations['function bok'] ==
+            ffi._parser._declarations['extern_python foobar'] ==
+            ffi._parser._declarations['function baz'])
+
+def test_extern_python_group():
+    ffi = FFI()
+    ffi.cdef("""
+        int bok(int);
+        extern "Python" {int foobar(int, int);int bzrrr(int);}
+        int baz(int, int);
+    """)
+    assert sorted(ffi._parser._declarations) == [
+        'extern_python bzrrr', 'extern_python foobar',
+        'function baz', 'function bok']
+    assert (ffi._parser._declarations['function baz'] ==
+            ffi._parser._declarations['extern_python foobar'] !=
+            ffi._parser._declarations['function bok'] ==
+            ffi._parser._declarations['extern_python bzrrr'])
+
+def test_error_invalid_syntax_for_cdef():
+    ffi = FFI()
+    e = py.test.raises(CDefError, ffi.cdef, 'void foo(void) {}')
+    assert str(e.value) == ('<cdef source string>:1: unexpected <FuncDef>: '
+                            'this construct is valid C but not valid in cdef()')
diff --git a/testing/cffi0/test_platform.py b/testing/cffi0/test_platform.py
new file mode 100644
index 0000000..55446ec
--- /dev/null
+++ b/testing/cffi0/test_platform.py
@@ -0,0 +1,25 @@
+import os
+from cffi.ffiplatform import maybe_relative_path, flatten
+
+
+def test_not_absolute():
+    assert maybe_relative_path('foo/bar') == 'foo/bar'
+    assert maybe_relative_path('test_platform.py') == 'test_platform.py'
+
+def test_different_absolute():
+    p = os.path.join('..', 'baz.py')
+    assert maybe_relative_path(p) == p
+
+def test_absolute_mapping():
+    p = os.path.abspath('baz.py')
+    assert maybe_relative_path(p) == 'baz.py'
+    foobaz = os.path.join('foo', 'baz.py')
+    assert maybe_relative_path(os.path.abspath(foobaz)) == foobaz
+
+def test_flatten():
+    assert flatten("foo") == "3sfoo"
+    assert flatten(-10000000000000000000000000000) == \
+           "-10000000000000000000000000000i"
+    assert flatten([4, 5]) == "2l4i5i"
+    assert flatten({4: 5}) == "1d4i5i"
+    assert flatten({"foo": ("bar", "baaz")}) == "1d3sfoo2l3sbar4sbaaz"
diff --git a/testing/cffi0/test_unicode_literals.py b/testing/cffi0/test_unicode_literals.py
new file mode 100644
index 0000000..7b0a5cc
--- /dev/null
+++ b/testing/cffi0/test_unicode_literals.py
@@ -0,0 +1,79 @@
+#
+# ----------------------------------------------
+# WARNING, ALL LITERALS IN THIS FILE ARE UNICODE
+# ----------------------------------------------
+#
+from __future__ import unicode_literals
+#
+#
+#
+import sys, math
+from cffi import FFI
+
+lib_m = "m"
+if sys.platform == 'win32':
+    #there is a small chance this fails on Mingw via environ $CC
+    import distutils.ccompiler
+    if distutils.ccompiler.get_default_compiler() == 'msvc':
+        lib_m = 'msvcrt'
+
+
+def test_cast():
+    ffi = FFI()
+    assert int(ffi.cast("int", 3.14)) == 3        # unicode literal
+
+def test_new():
+    ffi = FFI()
+    assert ffi.new("int[]", [3, 4, 5])[2] == 5    # unicode literal
+
+def test_typeof():
+    ffi = FFI()
+    tp = ffi.typeof("int[51]")                    # unicode literal
+    assert tp.length == 51
+
+def test_sizeof():
+    ffi = FFI()
+    assert ffi.sizeof("int[51]") == 51 * 4        # unicode literal
+
+def test_alignof():
+    ffi = FFI()
+    assert ffi.alignof("int[51]") == 4            # unicode literal
+
+def test_getctype():
+    ffi = FFI()
+    assert ffi.getctype("int**") == "int * *"     # unicode literal
+    assert type(ffi.getctype("int**")) is str
+
+def test_cdef():
+    ffi = FFI()
+    ffi.cdef("typedef int foo_t[50];")            # unicode literal
+
+def test_offsetof():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x, y; } foo_t;")
+    assert ffi.offsetof("foo_t", "y") == 4        # unicode literal
+
+def test_enum():
+    ffi = FFI()
+    ffi.cdef("enum foo_e { AA, BB, CC };")        # unicode literal
+    x = ffi.cast("enum foo_e", 1)
+    assert int(ffi.cast("int", x)) == 1
+
+def test_dlopen():
+    ffi = FFI()
+    ffi.cdef("double sin(double x);")
+    m = ffi.dlopen(lib_m)                           # unicode literal
+    x = m.sin(1.23)
+    assert x == math.sin(1.23)
+
+def test_verify():
+    ffi = FFI()
+    ffi.cdef("double test_verify_1(double x);")   # unicode literal
+    lib = ffi.verify("double test_verify_1(double x) { return x * 42.0; }")
+    assert lib.test_verify_1(-1.5) == -63.0
+
+def test_callback():
+    ffi = FFI()
+    cb = ffi.callback("int(int)",                 # unicode literal
+                      lambda x: x + 42)
+    assert cb(5) == 47
diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py
new file mode 100644
index 0000000..79e1c6c
--- /dev/null
+++ b/testing/cffi0/test_verify.py
@@ -0,0 +1,2524 @@
+import py, re
+import sys, os, math, weakref
+from cffi import FFI, VerificationError, VerificationMissing, model, FFIError
+from testing.support import *
+
+
+lib_m = ['m']
+if sys.platform == 'win32':
+    #there is a small chance this fails on Mingw via environ $CC
+    import distutils.ccompiler
+    if distutils.ccompiler.get_default_compiler() == 'msvc':
+        lib_m = ['msvcrt']
+    pass      # no obvious -Werror equivalent on MSVC
+else:
+    if (sys.platform == 'darwin' and
+          [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]):
+        # assume a standard clang or gcc
+        extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion']
+        # special things for clang
+        extra_compile_args.append('-Qunused-arguments')
+    else:
+        # assume a standard gcc
+        extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion']
+
+    class FFI(FFI):
+        def verify(self, *args, **kwds):
+            return super(FFI, self).verify(
+                *args, extra_compile_args=extra_compile_args, **kwds)
+
+def setup_module():
+    import cffi.verifier
+    cffi.verifier.cleanup_tmpdir()
+    #
+    # check that no $ sign is produced in the C file; it used to be the
+    # case that anonymous enums would produce '$enum_$1', which was
+    # used as part of a function name.  GCC accepts such names, but it's
+    # apparently non-standard.
+    _r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE)
+    _r_string = re.compile(r'\".*?\"')
+    def _write_source_and_check(self, file=None):
+        base_write_source(self, file)
+        if file is None:
+            f = open(self.sourcefilename)
+            data = f.read()
+            f.close()
+            data = _r_comment.sub(' ', data)
+            data = _r_string.sub('"skipped"', data)
+            assert '$' not in data
+    base_write_source = cffi.verifier.Verifier._write_source
+    cffi.verifier.Verifier._write_source = _write_source_and_check
+
+
+def test_module_type():
+    import cffi.verifier
+    ffi = FFI()
+    lib = ffi.verify()
+    if hasattr(lib, '_cffi_python_module'):
+        print('verify got a PYTHON module')
+    if hasattr(lib, '_cffi_generic_module'):
+        print('verify got a GENERIC module')
+    expected_generic = (cffi.verifier._FORCE_GENERIC_ENGINE or
+                        '__pypy__' in sys.builtin_module_names)
+    assert hasattr(lib, '_cffi_python_module') == (not expected_generic)
+    assert hasattr(lib, '_cffi_generic_module') == expected_generic
+
+def test_missing_function(ffi=None):
+    # uses the FFI hacked above with '-Werror'
+    if ffi is None:
+        ffi = FFI()
+    ffi.cdef("void some_completely_unknown_function();")
+    try:
+        lib = ffi.verify()
+    except (VerificationError, OSError):
+        pass     # expected case: we get a VerificationError
+    else:
+        # but depending on compiler and loader details, maybe
+        # 'lib' could actually be imported but will fail if we
+        # actually try to call the unknown function...  Hard
+        # to test anything more.
+        pass
+
+def test_missing_function_import_error():
+    # uses the original FFI that just gives a warning during compilation
+    import cffi
+    test_missing_function(ffi=cffi.FFI())
+
+def test_simple_case():
+    ffi = FFI()
+    ffi.cdef("double sin(double x);")
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    assert lib.sin(1.23) == math.sin(1.23)
+
+def _Wconversion(cdef, source, **kargs):
+    if sys.platform in ('win32', 'darwin'):
+        py.test.skip("needs GCC")
+    ffi = FFI()
+    ffi.cdef(cdef)
+    py.test.raises(VerificationError, ffi.verify, source, **kargs)
+    extra_compile_args_orig = extra_compile_args[:]
+    extra_compile_args.remove('-Wconversion')
+    try:
+        lib = ffi.verify(source, **kargs)
+    finally:
+        extra_compile_args[:] = extra_compile_args_orig
+    return lib
+
+def test_Wconversion_unsigned():
+    _Wconversion("unsigned foo(void);",
+                 "int foo(void) { return -1;}")
+
+def test_Wconversion_integer():
+    _Wconversion("short foo(void);",
+                 "long long foo(void) { return 1<<sizeof(short);}")
+
+def test_Wconversion_floating():
+    lib = _Wconversion("float sin(double);",
+                       "#include <math.h>", libraries=lib_m)
+    res = lib.sin(1.23)
+    assert res != math.sin(1.23)     # not exact, because of double->float
+    assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_Wconversion_float2int():
+    _Wconversion("int sinf(float);",
+                 "#include <math.h>", libraries=lib_m)
+
+def test_Wconversion_double2int():
+    _Wconversion("int sin(double);",
+                 "#include <math.h>", libraries=lib_m)
+
+def test_rounding_1():
+    ffi = FFI()
+    ffi.cdef("double sinf(float x);")
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    res = lib.sinf(1.23)
+    assert res != math.sin(1.23)     # not exact, because of double->float
+    assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_rounding_2():
+    ffi = FFI()
+    ffi.cdef("double sin(float x);")
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    res = lib.sin(1.23)
+    assert res != math.sin(1.23)     # not exact, because of double->float
+    assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_strlen_exact():
+    ffi = FFI()
+    ffi.cdef("size_t strlen(const char *s);")
+    lib = ffi.verify("#include <string.h>")
+    assert lib.strlen(b"hi there!") == 9
+
+def test_strlen_approximate():
+    lib = _Wconversion("int strlen(char *s);",
+                       "#include <string.h>")
+    assert lib.strlen(b"hi there!") == 9
+
+def test_return_approximate():
+    for typename in ['short', 'int', 'long', 'long long']:
+        ffi = FFI()
+        ffi.cdef("%s foo(signed char x);" % typename)
+        lib = ffi.verify("signed char foo(signed char x) { return x;}")
+        assert lib.foo(-128) == -128
+        assert lib.foo(+127) == +127
+
+def test_strlen_array_of_char():
+    ffi = FFI()
+    ffi.cdef("size_t strlen(char[]);")
+    lib = ffi.verify("#include <string.h>")
+    assert lib.strlen(b"hello") == 5
+
+def test_longdouble():
+    ffi = FFI()
+    ffi.cdef("long double sinl(long double x);")
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    for input in [1.23,
+                  ffi.cast("double", 1.23),
+                  ffi.cast("long double", 1.23)]:
+        x = lib.sinl(input)
+        assert repr(x).startswith("<cdata 'long double'")
+        assert (float(x) - math.sin(1.23)) < 1E-10
+
+def test_longdouble_precision():
+    # Test that we don't loose any precision of 'long double' when
+    # passing through Python and CFFI.
+    ffi = FFI()
+    ffi.cdef("long double step1(long double x);")
+    SAME_SIZE = ffi.sizeof("long double") == ffi.sizeof("double")
+    lib = ffi.verify("""
+        long double step1(long double x)
+        {
+            return 4*x-x*x;
+        }
+    """)
+    def do(cast_to_double):
+        x = 0.9789
+        for i in range(10000):
+            x = lib.step1(x)
+            if cast_to_double:
+                x = float(x)
+        return float(x)
+
+    more_precise = do(False)
+    less_precise = do(True)
+    if SAME_SIZE:
+        assert more_precise == less_precise
+    else:
+        assert abs(more_precise - less_precise) > 0.1
+        # Check the particular results on Intel
+        import platform
+        if (platform.machine().startswith('i386') or
+            platform.machine().startswith('i486') or
+            platform.machine().startswith('i586') or
+            platform.machine().startswith('i686') or
+            platform.machine().startswith('x86')):
+            assert abs(more_precise - 0.656769) < 0.001
+            assert abs(less_precise - 3.99091) < 0.001
+        else:
+            py.test.skip("don't know the very exact precision of 'long double'")
+
+
+all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES
+if sys.platform == 'win32':
+    all_primitive_types = all_primitive_types.copy()
+    del all_primitive_types['ssize_t']
+all_integer_types = sorted(tp for tp in all_primitive_types
+                           if all_primitive_types[tp] == 'i')
+all_float_types = sorted(tp for tp in all_primitive_types
+                            if all_primitive_types[tp] == 'f')
+
+def all_signed_integer_types(ffi):
+    return [x for x in all_integer_types if int(ffi.cast(x, -1)) < 0]
+
+def all_unsigned_integer_types(ffi):
+    return [x for x in all_integer_types if int(ffi.cast(x, -1)) > 0]
+
+
+def test_primitive_category():
+    for typename in all_primitive_types:
+        tp = model.PrimitiveType(typename)
+        C = tp.is_char_type()
+        F = tp.is_float_type()
+        X = tp.is_complex_type()
+        I = tp.is_integer_type()
+        assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t'))
+        assert F == (typename in ('float', 'double', 'long double'))
+        assert X == (typename in ('float _Complex', 'double _Complex'))
+        assert I + F + C + X == 1      # one and only one of them is true
+
+def test_all_integer_and_float_types():
+    typenames = []
+    for typename in all_primitive_types:
+        if (all_primitive_types[typename] == 'c' or
+            all_primitive_types[typename] == 'j' or    # complex
+            typename == '_Bool' or typename == 'long double'):
+            pass
+        else:
+            typenames.append(typename)
+    #
+    ffi = FFI()
+    ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp)
+                       for tp in typenames]))
+    lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return (%s)(x+1); }" %
+                                (tp, tp.replace(' ', '_'), tp, tp)
+                                for tp in typenames]))
+    for typename in typenames:
+        foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_'))
+        assert foo(42) == 43
+        if sys.version < '3':
+            assert foo(long(44)) == 45
+        assert foo(ffi.cast(typename, 46)) == 47
+        py.test.raises(TypeError, foo, ffi.NULL)
+        #
+        # check for overflow cases
+        if all_primitive_types[typename] == 'f':
+            continue
+        for value in [-2**80, -2**40, -2**20, -2**10, -2**5, -1,
+                      2**5, 2**10, 2**20, 2**40, 2**80]:
+            overflows = int(ffi.cast(typename, value)) != value
+            if overflows:
+                py.test.raises(OverflowError, foo, value)
+            else:
+                assert foo(value) == value + 1
+
+def test_var_signed_integer_types():
+    ffi = FFI()
+    lst = all_signed_integer_types(ffi)
+    csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_'))
+                         for tp in lst])
+    ffi.cdef(csource)
+    lib = ffi.verify(csource)
+    for tp in lst:
+        varname = 'somevar_%s' % tp.replace(' ', '_')
+        sz = ffi.sizeof(tp)
+        max = (1 << (8*sz-1)) - 1
+        min = -(1 << (8*sz-1))
+        setattr(lib, varname, max)
+        assert getattr(lib, varname) == max
+        setattr(lib, varname, min)
+        assert getattr(lib, varname) == min
+        py.test.raises(OverflowError, setattr, lib, varname, max+1)
+        py.test.raises(OverflowError, setattr, lib, varname, min-1)
+
+def test_var_unsigned_integer_types():
+    ffi = FFI()
+    lst = all_unsigned_integer_types(ffi)
+    csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_'))
+                         for tp in lst])
+    ffi.cdef(csource)
+    lib = ffi.verify(csource)
+    for tp in lst:
+        varname = 'somevar_%s' % tp.replace(' ', '_')
+        sz = ffi.sizeof(tp)
+        if tp != '_Bool':
+            max = (1 << (8*sz)) - 1
+        else:
+            max = 1
+        setattr(lib, varname, max)
+        assert getattr(lib, varname) == max
+        setattr(lib, varname, 0)
+        assert getattr(lib, varname) == 0
+        py.test.raises(OverflowError, setattr, lib, varname, max+1)
+        py.test.raises(OverflowError, setattr, lib, varname, -1)
+
+def test_fn_signed_integer_types():
+    ffi = FFI()
+    lst = all_signed_integer_types(ffi)
+    cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp)
+                         for tp in lst])
+    ffi.cdef(cdefsrc)
+    verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" %
+                           (tp, tp.replace(' ', '_'), tp) for tp in lst])
+    lib = ffi.verify(verifysrc)
+    for tp in lst:
+        fnname = 'somefn_%s' % tp.replace(' ', '_')
+        sz = ffi.sizeof(tp)
+        max = (1 << (8*sz-1)) - 1
+        min = -(1 << (8*sz-1))
+        fn = getattr(lib, fnname)
+        assert fn(max) == max
+        assert fn(min) == min
+        py.test.raises(OverflowError, fn, max + 1)
+        py.test.raises(OverflowError, fn, min - 1)
+
+def test_fn_unsigned_integer_types():
+    ffi = FFI()
+    lst = all_unsigned_integer_types(ffi)
+    cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp)
+                         for tp in lst])
+    ffi.cdef(cdefsrc)
+    verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" %
+                           (tp, tp.replace(' ', '_'), tp) for tp in lst])
+    lib = ffi.verify(verifysrc)
+    for tp in lst:
+        fnname = 'somefn_%s' % tp.replace(' ', '_')
+        sz = ffi.sizeof(tp)
+        if tp != '_Bool':
+            max = (1 << (8*sz)) - 1
+        else:
+            max = 1
+        fn = getattr(lib, fnname)
+        assert fn(max) == max
+        assert fn(0) == 0
+        py.test.raises(OverflowError, fn, max + 1)
+        py.test.raises(OverflowError, fn, -1)
+
+def test_char_type():
+    ffi = FFI()
+    ffi.cdef("char foo(char);")
+    lib = ffi.verify("char foo(char x) { return ++x; }")
+    assert lib.foo(b"A") == b"B"
+    py.test.raises(TypeError, lib.foo, b"bar")
+    py.test.raises(TypeError, lib.foo, "bar")
+
+def test_wchar_type():
+    ffi = FFI()
+    if ffi.sizeof('wchar_t') == 2:
+        uniexample1 = u+'\u1234'
+        uniexample2 = u+'\u1235'
+    else:
+        uniexample1 = u+'\U00012345'
+        uniexample2 = u+'\U00012346'
+    #
+    ffi.cdef("wchar_t foo(wchar_t);")
+    lib = ffi.verify("wchar_t foo(wchar_t x) { return x+1; }")
+    assert lib.foo(uniexample1) == uniexample2
+
+def test_char16_char32_type():
+    py.test.skip("XXX test or fully prevent char16_t and char32_t from "
+                 "working in ffi.verify() mode")
+
+def test_no_argument():
+    ffi = FFI()
+    ffi.cdef("int foo(void);")
+    lib = ffi.verify("int foo(void) { return 42; }")
+    assert lib.foo() == 42
+
+def test_two_arguments():
+    ffi = FFI()
+    ffi.cdef("int foo(int, int);")
+    lib = ffi.verify("int foo(int a, int b) { return a - b; }")
+    assert lib.foo(40, -2) == 42
+
+def test_macro():
+    ffi = FFI()
+    ffi.cdef("int foo(int, int);")
+    lib = ffi.verify("#define foo(a, b) ((a) * (b))")
+    assert lib.foo(-6, -7) == 42
+
+def test_ptr():
+    ffi = FFI()
+    ffi.cdef("int *foo(int *);")
+    lib = ffi.verify("int *foo(int *a) { return a; }")
+    assert lib.foo(ffi.NULL) == ffi.NULL
+    p = ffi.new("int *", 42)
+    q = ffi.new("int *", 42)
+    assert lib.foo(p) == p
+    assert lib.foo(q) != p
+
+def test_bogus_ptr():
+    ffi = FFI()
+    ffi.cdef("int *foo(int *);")
+    lib = ffi.verify("int *foo(int *a) { return a; }")
+    py.test.raises(TypeError, lib.foo, ffi.new("short *", 42))
+
+
+def test_verify_typedefs():
+    py.test.skip("ignored so far")
+    types = ['signed char', 'unsigned char', 'int', 'long']
+    for cdefed in types:
+        for real in types:
+            ffi = FFI()
+            ffi.cdef("typedef %s foo_t;" % cdefed)
+            if cdefed == real:
+                ffi.verify("typedef %s foo_t;" % real)
+            else:
+                py.test.raises(VerificationError, ffi.verify,
+                               "typedef %s foo_t;" % real)
+
+def test_nondecl_struct():
+    ffi = FFI()
+    ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);")
+    lib = ffi.verify("typedef struct foo_s foo_t;\n"
+                     "int bar(foo_t *f) { (void)f; return 42; }\n")
+    assert lib.bar(ffi.NULL) == 42
+
+def test_ffi_full_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { char x; int y; long *z; };")
+    ffi.verify("struct foo_s { char x; int y; long *z; };")
+    #
+    if sys.platform != 'win32':  # XXX fixme: only gives warnings
+        py.test.raises(VerificationError, ffi.verify,
+            "struct foo_s { char x; int y; int *z; };")
+    #
+    py.test.raises(VerificationError, ffi.verify,
+        "struct foo_s { int y; long *z; };")
+    #
+    e = py.test.raises(VerificationError, ffi.verify,
+        "struct foo_s { int y; char x; long *z; };")
+    assert str(e.value) == (
+        "struct foo_s: wrong offset for field 'x'"
+        " (we have 0, but C compiler says 4)")
+    #
+    e = py.test.raises(VerificationError, ffi.verify,
+        "struct foo_s { char x; int y; long *z; char extra; };")
+    assert str(e.value) == (
+        "struct foo_s: wrong total size"
+        " (we have %d, but C compiler says %d)" % (
+            ffi.sizeof("struct foo_s"),
+            ffi.sizeof("struct foo_s") + ffi.sizeof("long*")))
+    #
+    # a corner case that we cannot really detect, but where it has no
+    # bad consequences: the size is the same, but there is an extra field
+    # that replaces what is just padding in our declaration above
+    ffi.verify("struct foo_s { char x, extra; int y; long *z; };")
+    #
+    e = py.test.raises(VerificationError, ffi.verify,
+        "struct foo_s { char x; short pad; short y; long *z; };")
+    assert str(e.value) == (
+        "struct foo_s: wrong size for field 'y'"
+        " (we have 4, but C compiler says 2)")
+
+def test_ffi_nonfull_struct():
+    ffi = FFI()
+    ffi.cdef("""
+    struct foo_s {
+       int x;
+       ...;
+    };
+    """)
+    py.test.raises(VerificationMissing, ffi.sizeof, 'struct foo_s')
+    py.test.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x')
+    py.test.raises(VerificationMissing, ffi.new, 'struct foo_s *')
+    ffi.verify("""
+    struct foo_s {
+       int a, b, x, c, d, e;
+    };
+    """)
+    assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int')
+    assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int')
+
+def test_ffi_nonfull_alignment():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { char x; ...; };")
+    ffi.verify("struct foo_s { int a, b; char x; };")
+    assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int')
+    assert ffi.alignof('struct foo_s') == ffi.sizeof('int')
+
+def _check_field_match(typename, real, expect_mismatch):
+    ffi = FFI()
+    testing_by_size = (expect_mismatch == 'by_size')
+    if testing_by_size:
+        expect_mismatch = ffi.sizeof(typename) != ffi.sizeof(real)
+    ffi.cdef("struct foo_s { %s x; ...; };" % typename)
+    try:
+        ffi.verify("struct foo_s { %s x; };" % real)
+    except VerificationError:
+        if not expect_mismatch:
+            if testing_by_size and typename != real:
+                print("ignoring mismatch between %s* and %s* even though "
+                      "they have the same size" % (typename, real))
+                return
+            raise AssertionError("unexpected mismatch: %s should be accepted "
+                                 "as equal to %s" % (typename, real))
+    else:
+        if expect_mismatch:
+            raise AssertionError("mismatch not detected: "
+                                 "%s != %s" % (typename, real))
+
+def test_struct_bad_sized_integer():
+    for typename in ['int8_t', 'int16_t', 'int32_t', 'int64_t']:
+        for real in ['int8_t', 'int16_t', 'int32_t', 'int64_t']:
+            _check_field_match(typename, real, "by_size")
+
+def test_struct_bad_sized_float():
+    for typename in all_float_types:
+        for real in all_float_types:
+            _check_field_match(typename, real, "by_size")
+
+def test_struct_signedness_ignored():
+    _check_field_match("int", "unsigned int", expect_mismatch=False)
+    _check_field_match("unsigned short", "signed short", expect_mismatch=False)
+
+def test_struct_float_vs_int():
+    if sys.platform == 'win32':
+        py.test.skip("XXX fixme: only gives warnings")
+    ffi = FFI()
+    for typename in all_signed_integer_types(ffi):
+        for real in all_float_types:
+            _check_field_match(typename, real, expect_mismatch=True)
+    for typename in all_float_types:
+        for real in all_signed_integer_types(ffi):
+            _check_field_match(typename, real, expect_mismatch=True)
+
+def test_struct_array_field():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[17]; ...; };")
+    ffi.verify("struct foo_s { int x; int a[17]; int y; };")
+    assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int')
+
+def test_struct_array_no_length():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[]; int y; ...; };\n"
+             "int bar(struct foo_s *);\n")
+    lib = ffi.verify("struct foo_s { int x; int a[17]; int y; };\n"
+                     "int bar(struct foo_s *f) { return f->a[14]; }\n")
+    assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.typeof(s.a) is ffi.typeof('int[]')   # implicit max length
+    assert len(s.a) == 18  # max length, computed from the size and start offset
+    s.a[14] = 4242
+    assert lib.bar(s) == 4242
+    # with no declared length, out-of-bound accesses are not detected
+    s.a[17] = -521
+    assert s.y == s.a[17] == -521
+    #
+    s = ffi.new("struct foo_s *", {'a': list(range(17))})
+    assert s.a[16] == 16
+    # overflows at construction time not detected either
+    s = ffi.new("struct foo_s *", {'a': list(range(18))})
+    assert s.y == s.a[17] == 17
+
+def test_struct_array_guess_length():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[...]; };")
+    ffi.verify("struct foo_s { int x; int a[17]; int y; };")
+    assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int')
+    py.test.raises(IndexError, 's.a[17]')
+
+def test_struct_array_c99_1():
+    if sys.platform == 'win32':
+        py.test.skip("requires C99")
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; int a[]; };")
+    ffi.verify("struct foo_s { int x; int a[]; };")
+    assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *", [424242, 4])
+    assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int')
+    assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int')
+    # ^^^ explanation: if you write in C: "char x[5];", then
+    # "sizeof(x)" will evaluate to 5.  The behavior above is
+    # a generalization of that to "struct foo_s[len(a)=5] x;"
+    # if you could do that in C.
+    assert s.a[3] == 0
+    s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]])
+    assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int')
+    assert s.a[3] == -10
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *", [424242])
+    assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int')
+
+def test_struct_array_c99_2():
+    if sys.platform == 'win32':
+        py.test.skip("requires C99")
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; int a[]; ...; };")
+    ffi.verify("struct foo_s { int x, y; int a[]; };")
+    assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *", [424242, 4])
+    assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int')
+    assert s.a[3] == 0
+    s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]])
+    assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int')
+    assert s.a[3] == -10
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *", [424242])
+    assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int')
+
+def test_struct_ptr_to_array_field():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int (*a)[17]; ...; }; struct bar_s { ...; };")
+    ffi.verify("struct foo_s { int x; int (*a)[17]; int y; };\n"
+               "struct bar_s { int x; int *a; int y; };")
+    assert ffi.sizeof('struct foo_s') == ffi.sizeof("struct bar_s")
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s.a) == ffi.sizeof('int(*)[17]') == ffi.sizeof("int *")
+
+def test_struct_with_bitfield_exact():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a:2, b:3; };")
+    ffi.verify("struct foo_s { int a:2, b:3; };")
+    s = ffi.new("struct foo_s *")
+    s.b = 3
+    py.test.raises(OverflowError, "s.b = 4")
+    assert s.b == 3
+
+def test_struct_with_bitfield_enum():
+    ffi = FFI()
+    code = """
+        typedef enum { AA, BB, CC } foo_e;
+        typedef struct { foo_e f:2; } foo_s;
+    """
+    ffi.cdef(code)
+    ffi.verify(code)
+    s = ffi.new("foo_s *")
+    s.f = 2
+    assert s.f == 2
+
+def test_unsupported_struct_with_bitfield_ellipsis():
+    ffi = FFI()
+    py.test.raises(NotImplementedError, ffi.cdef,
+                   "struct foo_s { int a:2, b:3; ...; };")
+
+def test_global_constants():
+    ffi = FFI()
+    # use 'static const int', as generally documented, although in this
+    # case the 'static' is completely ignored.
+    ffi.cdef("static const int AA, BB, CC, DD;")
+    lib = ffi.verify("#define AA 42\n"
+                     "#define BB (-43)   // blah\n"
+                     "#define CC (22*2)  /* foobar */\n"
+                     "#define DD ((unsigned int)142)  /* foo\nbar */\n")
+    assert lib.AA == 42
+    assert lib.BB == -43
+    assert lib.CC == 44
+    assert lib.DD == 142
+
+def test_global_const_int_size():
+    # integer constants: ignore the declared type, always just use the value
+    for value in [-2**63, -2**31, -2**15,
+                  2**15-1, 2**15, 2**31-1, 2**31, 2**32-1, 2**32,
+                  2**63-1, 2**63, 2**64-1]:
+        ffi = FFI()
+        if value == int(ffi.cast("long long", value)):
+            if value < 0:
+                vstr = '(-%dLL-1)' % (~value,)
+            else:
+                vstr = '%dLL' % value
+        elif value == int(ffi.cast("unsigned long long", value)):
+            vstr = '%dULL' % value
+        else:
+            raise AssertionError(value)
+        ffi.cdef("static const unsigned short AA;")
+        lib = ffi.verify("#define AA %s\n" % vstr)
+        assert lib.AA == value
+        assert type(lib.AA) is type(int(lib.AA))
+
+def test_global_constants_non_int():
+    ffi = FFI()
+    ffi.cdef("static char *const PP;")
+    lib = ffi.verify('static char *const PP = "testing!";\n')
+    assert ffi.typeof(lib.PP) == ffi.typeof("char *")
+    assert ffi.string(lib.PP) == b"testing!"
+
+def test_nonfull_enum():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };")
+    py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2')
+    ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+    assert ffi.string(ffi.cast('enum ee', 11)) == "EE2"
+    assert ffi.string(ffi.cast('enum ee', -10)) == "EE3"
+    #
+    # try again
+    ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+    assert ffi.string(ffi.cast('enum ee', 11)) == "EE2"
+    #
+    assert ffi.typeof("enum ee").relements == {'EE1': 10, 'EE2': 11, 'EE3': -10}
+    assert ffi.typeof("enum ee").elements == {10: 'EE1', 11: 'EE2', -10: 'EE3'}
+
+def test_full_enum():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1, EE2, EE3 };")
+    ffi.verify("enum ee { EE1, EE2, EE3 };")
+    py.test.raises(VerificationError, ffi.verify, "enum ee { EE1, EE2 };")
+    e = py.test.raises(VerificationError, ffi.verify,
+                       "enum ee { EE1, EE3, EE2 };")
+    assert str(e.value) == 'enum ee: EE2 has the real value 2, not 1'
+    # extra items cannot be seen and have no bad consequence anyway
+    lib = ffi.verify("enum ee { EE1, EE2, EE3, EE4 };")
+    assert lib.EE3 == 2
+
+def test_enum_usage():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;")
+    lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;")
+    assert lib.EE2 == 1
+    s = ffi.new("sp", [lib.EE2])
+    assert s.x == 1
+    s.x = 17
+    assert s.x == 17
+
+def test_anonymous_enum():
+    ffi = FFI()
+    ffi.cdef("enum { EE1 }; enum { EE2, EE3 };")
+    lib = ffi.verify("enum { EE1 }; enum { EE2, EE3 };")
+    assert lib.EE1 == 0
+    assert lib.EE2 == 0
+    assert lib.EE3 == 1
+
+def test_nonfull_anonymous_enum():
+    ffi = FFI()
+    ffi.cdef("enum { EE1, ... }; enum { EE3, ... };")
+    lib = ffi.verify("enum { EE2, EE1 }; enum { EE3 };")
+    assert lib.EE1 == 1
+    assert lib.EE3 == 0
+
+def test_nonfull_enum_syntax2():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };")
+    py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1')
+    ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+    assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2'
+    assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3'
+    #
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1, EE2=\t... };")
+    py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1')
+    ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+    assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2'
+    #
+    ffi = FFI()
+    ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };")
+    ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ")
+    assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4'
+    assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5'
+
+def test_nonfull_enum_bug3():
+    ffi = FFI()
+    ffi.cdef("enum ee2 { EE4=..., EE5=... };")
+    ffi.cdef("enum ee6 { EE7=10, EE8=..., EE9=... };")
+
+def test_get_set_errno():
+    ffi = FFI()
+    ffi.cdef("int foo(int);")
+    lib = ffi.verify("""
+        static int foo(int x)
+        {
+            errno += 1;
+            return x * 7;
+        }
+    """)
+    ffi.errno = 15
+    assert lib.foo(6) == 42
+    assert ffi.errno == 16
+
+def test_define_int():
+    ffi = FFI()
+    ffi.cdef("#define FOO ...\n"
+             "\t#\tdefine\tBAR\t...\t\n"
+             "#define BAZ ...\n")
+    lib = ffi.verify("#define FOO 42\n"
+                     "#define BAR (-44)\n"
+                     "#define BAZ 0xffffffffffffffffULL\n")
+    assert lib.FOO == 42
+    assert lib.BAR == -44
+    assert lib.BAZ == 0xffffffffffffffff
+
+def test_access_variable():
+    ffi = FFI()
+    ffi.cdef("int foo(void);\n"
+             "int somenumber;")
+    lib = ffi.verify("""
+        static int somenumber = 2;
+        static int foo(void) {
+            return somenumber * 7;
+        }
+    """)
+    assert lib.somenumber == 2
+    assert lib.foo() == 14
+    lib.somenumber = -6
+    assert lib.foo() == -42
+    assert lib.somenumber == -6
+    lib.somenumber = 2   # reset for the next run, if any
+
+def test_access_address_of_variable():
+    # access the address of 'somenumber': need a trick
+    ffi = FFI()
+    ffi.cdef("int somenumber; static int *const somenumberptr;")
+    lib = ffi.verify("""
+        static int somenumber = 2;
+        #define somenumberptr (&somenumber)
+    """)
+    assert lib.somenumber == 2
+    lib.somenumberptr[0] = 42
+    assert lib.somenumber == 42
+    lib.somenumber = 2    # reset for the next run, if any
+
+def test_access_array_variable(length=5):
+    ffi = FFI()
+    ffi.cdef("int foo(int);\n"
+             "int somenumber[%s];" % (length,))
+    lib = ffi.verify("""
+        static int somenumber[] = {2, 2, 3, 4, 5};
+        static int foo(int i) {
+            return somenumber[i] * 7;
+        }
+    """)
+    if length == '':
+        # a global variable of an unknown array length is implicitly
+        # transformed into a global pointer variable, because we can only
+        # work with array instances whose length we know.  using a pointer
+        # instead of an array gives the correct effects.
+        assert repr(lib.somenumber).startswith("<cdata 'int *' 0x")
+        py.test.raises(TypeError, len, lib.somenumber)
+    else:
+        assert repr(lib.somenumber).startswith("<cdata 'int[%s]' 0x" % length)
+        assert len(lib.somenumber) == 5
+    assert lib.somenumber[3] == 4
+    assert lib.foo(3) == 28
+    lib.somenumber[3] = -6
+    assert lib.foo(3) == -42
+    assert lib.somenumber[3] == -6
+    assert lib.somenumber[4] == 5
+    lib.somenumber[3] = 4    # reset for the next run, if any
+
+def test_access_array_variable_length_hidden():
+    test_access_array_variable(length='')
+
+def test_access_struct_variable():
+    ffi = FFI()
+    ffi.cdef("struct foo { int x; ...; };\n"
+             "int foo(int);\n"
+             "struct foo stuff;")
+    lib = ffi.verify("""
+        struct foo { int x, y, z; };
+        static struct foo stuff = {2, 5, 8};
+        static int foo(int i) {
+            switch (i) {
+            case 0: return stuff.x * 7;
+            case 1: return stuff.y * 7;
+            case 2: return stuff.z * 7;
+            }
+            return -1;
+        }
+    """)
+    assert lib.stuff.x == 2
+    assert lib.foo(0) == 14
+    assert lib.foo(1) == 35
+    assert lib.foo(2) == 56
+    lib.stuff.x = -6
+    assert lib.foo(0) == -42
+    assert lib.foo(1) == 35
+    lib.stuff.x = 2      # reset for the next run, if any
+
+def test_access_callback():
+    ffi = FFI()
+    ffi.cdef("int (*cb)(int);\n"
+             "int foo(int);\n"
+             "void reset_cb(void);")
+    lib = ffi.verify("""
+        static int g(int x) { return x * 7; }
+        static int (*cb)(int);
+        static int foo(int i) { return cb(i) - 1; }
+        static void reset_cb(void) { cb = g; }
+    """)
+    lib.reset_cb()
+    assert lib.foo(6) == 41
+    my_callback = ffi.callback("int(*)(int)", lambda n: n * 222)
+    lib.cb = my_callback
+    assert lib.foo(4) == 887
+
+def test_access_callback_function_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef int mycallback_t(int);\n"
+             "mycallback_t *cb;\n"
+             "int foo(int);\n"
+             "void reset_cb(void);")
+    lib = ffi.verify("""
+        static int g(int x) { return x * 7; }
+        static int (*cb)(int);
+        static int foo(int i) { return cb(i) - 1; }
+        static void reset_cb(void) { cb = g; }
+    """)
+    lib.reset_cb()
+    assert lib.foo(6) == 41
+    my_callback = ffi.callback("int(*)(int)", lambda n: n * 222)
+    lib.cb = my_callback
+    assert lib.foo(4) == 887
+
+def test_ctypes_backend_forces_generic_engine():
+    from cffi.backend_ctypes import CTypesBackend
+    ffi = FFI(backend=CTypesBackend())
+    ffi.cdef("int func(int a);")
+    lib = ffi.verify("int func(int a) { return a * 42; }")
+    assert not hasattr(lib, '_cffi_python_module')
+    assert hasattr(lib, '_cffi_generic_module')
+    assert lib.func(100) == 4200
+
+def test_call_with_struct_ptr():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; ...; } foo_t; int foo(foo_t *);")
+    lib = ffi.verify("""
+        typedef struct { int y, x; } foo_t;
+        static int foo(foo_t *f) { return f->x * 7; }
+    """)
+    f = ffi.new("foo_t *")
+    f.x = 6
+    assert lib.foo(f) == 42
+
+def test_unknown_type():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef ... token_t;
+        int foo(token_t *);
+        #define TOKEN_SIZE ...
+    """)
+    lib = ffi.verify("""
+        typedef float token_t;
+        static int foo(token_t *tk) {
+            if (!tk)
+                return -42;
+            *tk += 1.601f;
+            return (int)*tk;
+        }
+        #define TOKEN_SIZE sizeof(token_t)
+    """)
+    # we cannot let ffi.new("token_t *") work, because we don't know ahead of
+    # time if it's ok to ask 'sizeof(token_t)' in the C code or not.
+    # See test_unknown_type_2.  Workaround.
+    tkmem = ffi.new("char[]", lib.TOKEN_SIZE)    # zero-initialized
+    tk = ffi.cast("token_t *", tkmem)
+    results = [lib.foo(tk) for i in range(6)]
+    assert results == [1, 3, 4, 6, 8, 9]
+    assert lib.foo(ffi.NULL) == -42
+
+def test_unknown_type_2():
+    ffi = FFI()
+    ffi.cdef("typedef ... token_t;")
+    lib = ffi.verify("typedef struct token_s token_t;")
+    # assert did not crash, even though 'sizeof(token_t)' is not valid in C.
+
+def test_unknown_type_3():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef ... *token_p;
+        token_p foo(token_p);
+    """)
+    lib = ffi.verify("""
+        typedef struct _token_s *token_p;
+        token_p foo(token_p arg) {
+            if (arg)
+                return (token_p)0x12347;
+            else
+                return (token_p)0x12345;
+        }
+    """)
+    p = lib.foo(ffi.NULL)
+    assert int(ffi.cast("intptr_t", p)) == 0x12345
+    q = lib.foo(p)
+    assert int(ffi.cast("intptr_t", q)) == 0x12347
+
+def test_varargs():
+    ffi = FFI()
+    ffi.cdef("int foo(int x, ...);")
+    lib = ffi.verify("""
+        int foo(int x, ...) {
+            va_list vargs;
+            va_start(vargs, x);
+            x -= va_arg(vargs, int);
+            x -= va_arg(vargs, int);
+            va_end(vargs);
+            return x;
+        }
+    """)
+    assert lib.foo(50, ffi.cast("int", 5), ffi.cast("int", 3)) == 42
+
+def test_varargs_exact():
+    if sys.platform == 'win32':
+        py.test.skip("XXX fixme: only gives warnings")
+    ffi = FFI()
+    ffi.cdef("int foo(int x, ...);")
+    py.test.raises(VerificationError, ffi.verify, """
+        int foo(long long x, ...) {
+            return x;
+        }
+    """)
+
+def test_varargs_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { char a; int b; }; int foo(int x, ...);")
+    lib = ffi.verify("""
+        struct foo_s {
+            char a; int b;
+        };
+        int foo(int x, ...) {
+            va_list vargs;
+            struct foo_s s;
+            va_start(vargs, x);
+            s = va_arg(vargs, struct foo_s);
+            va_end(vargs);
+            return s.a - s.b;
+        }
+    """)
+    s = ffi.new("struct foo_s *", [b'B', 1])
+    assert lib.foo(50, s[0]) == ord('A')
+
+def test_autofilled_struct_as_argument():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { long a; double b; ...; };\n"
+             "int foo(struct foo_s);")
+    lib = ffi.verify("""
+        struct foo_s {
+            double b;
+            long a;
+        };
+        int foo(struct foo_s s) {
+            return (int)s.a - (int)s.b;
+        }
+    """)
+    s = ffi.new("struct foo_s *", [100, 1])
+    assert lib.foo(s[0]) == 99
+    assert lib.foo([100, 1]) == 99
+
+def test_autofilled_struct_as_argument_dynamic():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { long a; ...; };\n"
+             "int (*foo)(struct foo_s);")
+    lib = ffi.verify("""
+        struct foo_s {
+            double b;
+            long a;
+        };
+        int foo1(struct foo_s s) {
+            return (int)s.a - (int)s.b;
+        }
+        int (*foo)(struct foo_s s) = &foo1;
+    """)
+    e = py.test.raises(NotImplementedError, lib.foo, "?")
+    msg = ("ctype 'struct foo_s' not supported as argument.  It is a struct "
+           'declared with "...;", but the C calling convention may depend on '
+           "the missing fields; or, it contains anonymous struct/unions.  "
+           "Such structs are only supported as argument "
+           "if the function is 'API mode' and non-variadic (i.e. declared "
+           "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking "
+           "a final '...' argument)")
+    assert str(e.value) == msg
+
+def test_func_returns_struct():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { int aa, bb; };
+        struct foo_s foo(int a, int b);
+    """)
+    lib = ffi.verify("""
+        struct foo_s { int aa, bb; };
+        struct foo_s foo(int a, int b) {
+            struct foo_s r;
+            r.aa = a*a;
+            r.bb = b*b;
+            return r;
+        }
+    """)
+    s = lib.foo(6, 7)
+    assert repr(s) == "<cdata 'struct foo_s' owning 8 bytes>"
+    assert s.aa == 36
+    assert s.bb == 49
+
+def test_func_as_funcptr():
+    ffi = FFI()
+    ffi.cdef("int *(*const fooptr)(void);")
+    lib = ffi.verify("""
+        int *foo(void) {
+            return (int*)"foobar";
+        }
+        int *(*fooptr)(void) = foo;
+    """)
+    foochar = ffi.cast("char *(*)(void)", lib.fooptr)
+    s = foochar()
+    assert ffi.string(s) == b"foobar"
+
+def test_funcptr_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        void qsort(void *base, size_t nel, size_t width,
+            int (*compar)(const void *, const void *));
+    """)
+    ffi.verify("#include <stdlib.h>")
+
+def test_func_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        void qsort(void *base, size_t nel, size_t width,
+            int compar(const void *, const void *));
+    """)
+    ffi.verify("#include <stdlib.h>")
+
+def test_array_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        size_t strlen(char string[]);
+    """)
+    ffi.verify("#include <string.h>")
+
+def test_enum_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        enum foo_e { AA, BB, ... };
+        int foo_func(enum foo_e);
+    """)
+    lib = ffi.verify("""
+        enum foo_e { AA, CC, BB };
+        int foo_func(enum foo_e e) { return (int)e; }
+    """)
+    assert lib.foo_func(lib.BB) == 2
+    py.test.raises(TypeError, lib.foo_func, "BB")
+
+def test_enum_as_function_result():
+    ffi = FFI()
+    ffi.cdef("""
+        enum foo_e { AA, BB, ... };
+        enum foo_e foo_func(int x);
+    """)
+    lib = ffi.verify("""
+        enum foo_e { AA, CC, BB };
+        enum foo_e foo_func(int x) { return (enum foo_e)x; }
+    """)
+    assert lib.foo_func(lib.BB) == lib.BB == 2
+
+def test_enum_values():
+    ffi = FFI()
+    ffi.cdef("enum enum1_e { AA, BB };")
+    lib = ffi.verify("enum enum1_e { AA, BB };")
+    assert lib.AA == 0
+    assert lib.BB == 1
+    assert ffi.string(ffi.cast("enum enum1_e", 1)) == 'BB'
+
+def test_typedef_complete_enum():
+    ffi = FFI()
+    ffi.cdef("typedef enum { AA, BB } enum1_t;")
+    lib = ffi.verify("typedef enum { AA, BB } enum1_t;")
+    assert ffi.string(ffi.cast("enum1_t", 1)) == 'BB'
+    assert lib.AA == 0
+    assert lib.BB == 1
+
+def test_typedef_broken_complete_enum():
+    ffi = FFI()
+    ffi.cdef("typedef enum { AA, BB } enum1_t;")
+    py.test.raises(VerificationError, ffi.verify,
+                   "typedef enum { AA, CC, BB } enum1_t;")
+
+def test_typedef_incomplete_enum():
+    ffi = FFI()
+    ffi.cdef("typedef enum { AA, BB, ... } enum1_t;")
+    lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;")
+    assert ffi.string(ffi.cast("enum1_t", 1)) == '1'
+    assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB'
+    assert lib.AA == 0
+    assert lib.BB == 2
+
+def test_typedef_enum_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef enum { AA, BB, ... } foo_t;
+        int foo_func(foo_t);
+    """)
+    lib = ffi.verify("""
+        typedef enum { AA, CC, BB } foo_t;
+        int foo_func(foo_t e) { return (int)e; }
+    """)
+    assert lib.foo_func(lib.BB) == lib.BB == 2
+    py.test.raises(TypeError, lib.foo_func, "BB")
+
+def test_typedef_enum_as_function_result():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef enum { AA, BB, ... } foo_t;
+        foo_t foo_func(int x);
+    """)
+    lib = ffi.verify("""
+        typedef enum { AA, CC, BB } foo_t;
+        foo_t foo_func(int x) { return (foo_t)x; }
+    """)
+    assert lib.foo_func(lib.BB) == lib.BB == 2
+
+def test_function_typedef():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef double func_t(double);
+        func_t sin;
+    """)
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    assert lib.sin(1.23) == math.sin(1.23)
+
+def test_opaque_integer_as_function_result():
+    #import platform
+    #if platform.machine().startswith('sparc'):
+    #    py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)')
+    #elif platform.machine() == 'mips64' and sys.maxsize > 2**32:
+    #    py.test.skip('Segfaults on mips64el')
+    # XXX bad abuse of "struct { ...; }".  It only works a bit by chance
+    # anyway.  XXX think about something better :-(
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct { ...; } myhandle_t;
+        myhandle_t foo(void);
+    """)
+    lib = ffi.verify("""
+        typedef short myhandle_t;
+        myhandle_t foo(void) { return 42; }
+    """)
+    h = lib.foo()
+    assert ffi.sizeof(h) == ffi.sizeof("short")
+
+def test_return_partial_struct():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct { int x; ...; } foo_t;
+        foo_t foo(void);
+    """)
+    lib = ffi.verify("""
+        typedef struct { int y, x; } foo_t;
+        foo_t foo(void) { foo_t r = { 45, 81 }; return r; }
+    """)
+    h = lib.foo()
+    assert ffi.sizeof(h) == 2 * ffi.sizeof("int")
+    assert h.x == 81
+
+def test_take_and_return_partial_structs():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct { int x; ...; } foo_t;
+        foo_t foo(foo_t, foo_t);
+    """)
+    lib = ffi.verify("""
+        typedef struct { int y, x; } foo_t;
+        foo_t foo(foo_t a, foo_t b) {
+            foo_t r = { 100, a.x * 5 + b.x * 7 };
+            return r;
+        }
+    """)
+    args = ffi.new("foo_t[3]")
+    args[0].x = 1000
+    args[2].x = -498
+    h = lib.foo(args[0], args[2])
+    assert ffi.sizeof(h) == 2 * ffi.sizeof("int")
+    assert h.x == 1000 * 5 - 498 * 7
+
+def test_cannot_name_struct_type():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; } **sp; void foo(sp);")
+    e = py.test.raises(VerificationError, ffi.verify,
+                       "typedef struct { int x; } **sp; void foo(sp x) { }")
+    assert 'in argument of foo: unknown type name' in str(e.value)
+
+def test_dont_check_unnamable_fields():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { struct { int x; } someone; };")
+    ffi.verify("struct foo_s { struct { int x; } someone; };")
+    # assert did not crash
+
+def test_nested_anonymous_struct_exact():
+    if sys.platform == 'win32':
+        py.test.skip("nested anonymous struct/union")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { struct { int a; char b; }; union { char c, d; }; };
+    """)
+    ffi.verify("""
+        struct foo_s { struct { int a; char b; }; union { char c, d; }; };
+    """)
+    p = ffi.new("struct foo_s *")
+    assert ffi.sizeof(p[0]) == 3 * ffi.sizeof("int")    # with alignment
+    p.a = 1234567
+    p.b = b'X'
+    p.c = b'Y'
+    assert p.a == 1234567
+    assert p.b == b'X'
+    assert p.c == b'Y'
+    assert p.d == b'Y'
+
+def test_nested_anonymous_struct_exact_error():
+    if sys.platform == 'win32':
+        py.test.skip("nested anonymous struct/union")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { struct { int a; char b; }; union { char c, d; }; };
+    """)
+    py.test.raises(VerificationError, ffi.verify, """
+        struct foo_s { struct { int a; short b; }; union { char c, d; }; };
+    """)
+    py.test.raises(VerificationError, ffi.verify, """
+        struct foo_s { struct { int a; char e, b; }; union { char c, d; }; };
+    """)
+
+def test_nested_anonymous_struct_inexact_1():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { struct { char b; ...; }; union { char c, d; }; };
+    """)
+    ffi.verify("""
+        struct foo_s { int a, padding; char c, d, b; };
+    """)
+    assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int")
+
+def test_nested_anonymous_struct_inexact_2():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { union { char c, d; }; struct { int a; char b; }; ...; };
+    """)
+    ffi.verify("""
+        struct foo_s { int a, padding; char c, d, b; };
+    """)
+    assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int")
+
+def test_ffi_union():
+    ffi = FFI()
+    ffi.cdef("union foo_u { char x; long *z; };")
+    ffi.verify("union foo_u { char x; int y; long *z; };")
+
+def test_ffi_union_partial():
+    ffi = FFI()
+    ffi.cdef("union foo_u { char x; ...; };")
+    ffi.verify("union foo_u { char x; int y; };")
+    assert ffi.sizeof("union foo_u") == 4
+
+def test_ffi_union_with_partial_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; ...; }; union foo_u { struct foo_s s; };")
+    ffi.verify("struct foo_s { int a; int x; }; "
+               "union foo_u { char b[32]; struct foo_s s; };")
+    assert ffi.sizeof("struct foo_s") == 8
+    assert ffi.sizeof("union foo_u") == 32
+
+def test_ffi_union_partial_2():
+    ffi = FFI()
+    ffi.cdef("typedef union { char x; ...; } u1;")
+    ffi.verify("typedef union { char x; int y; } u1;")
+    assert ffi.sizeof("u1") == 4
+
+def test_ffi_union_with_partial_struct_2():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; ...; } s1;"
+             "typedef union { s1 s; } u1;")
+    ffi.verify("typedef struct { int a; int x; } s1; "
+               "typedef union { char b[32]; s1 s; } u1;")
+    assert ffi.sizeof("s1") == 8
+    assert ffi.sizeof("u1") == 32
+    assert ffi.offsetof("u1", "s") == 0
+
+def test_ffi_struct_packed():
+    if sys.platform == 'win32':
+        py.test.skip("needs a GCC extension")
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int b; ...; };")
+    ffi.verify("""
+        struct foo_s {
+            char a;
+            int b;
+        } __attribute__((packed));
+    """)
+
+def test_tmpdir():
+    import tempfile, os
+    from testing.udir import udir
+    tmpdir = tempfile.mkdtemp(dir=str(udir))
+    ffi = FFI()
+    ffi.cdef("int foo(int);")
+    lib = ffi.verify("int foo(int a) { return a + 42; }", tmpdir=tmpdir)
+    assert os.listdir(tmpdir)
+    assert lib.foo(100) == 142
+
+def test_relative_to():
+    import tempfile, os
+    from testing.udir import udir
+    tmpdir = tempfile.mkdtemp(dir=str(udir))
+    ffi = FFI()
+    ffi.cdef("int foo(int);")
+    f = open(os.path.join(tmpdir, 'foo.h'), 'w')
+    f.write("int foo(int a) { return a + 42; }\n")
+    f.close()
+    lib = ffi.verify('#include "foo.h"',
+                     include_dirs=['.'],
+                     relative_to=os.path.join(tmpdir, 'x'))
+    assert lib.foo(100) == 142
+
+def test_bug1():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct tdlhandle_s { ...; } *tdl_handle_t;
+        typedef struct my_error_code_ {
+            tdl_handle_t *rh;
+        } my_error_code_t;
+    """)
+    ffi.verify("""
+        typedef struct tdlhandle_s { int foo; } *tdl_handle_t;
+        typedef struct my_error_code_ {
+            tdl_handle_t *rh;
+        } my_error_code_t;
+    """)
+
+def test_bool():
+    if sys.platform == 'win32':
+        py.test.skip("_Bool not in MSVC")
+    ffi = FFI()
+    ffi.cdef("struct foo_s { _Bool x; };"
+             "_Bool foo(_Bool); _Bool (*foop)(_Bool);")
+    lib = ffi.verify("""
+        struct foo_s { _Bool x; };
+        int foo(int arg) {
+            return !arg;
+        }
+        _Bool _foofunc(_Bool x) {
+            return !x;
+        }
+        _Bool (*foop)(_Bool) = _foofunc;
+    """)
+    p = ffi.new("struct foo_s *")
+    p.x = 1
+    assert p.x is True
+    py.test.raises(OverflowError, "p.x = -1")
+    py.test.raises(TypeError, "p.x = 0.0")
+    assert lib.foop(1) is False
+    assert lib.foop(True) is False
+    assert lib.foop(0) is True
+    py.test.raises(OverflowError, lib.foop, 42)
+    py.test.raises(TypeError, lib.foop, 0.0)
+    assert lib.foo(1) is False
+    assert lib.foo(True) is False
+    assert lib.foo(0) is True
+    py.test.raises(OverflowError, lib.foo, 42)
+    py.test.raises(TypeError, lib.foo, 0.0)
+    assert int(ffi.cast("_Bool", long(1))) == 1
+    assert int(ffi.cast("_Bool", long(0))) == 0
+    assert int(ffi.cast("_Bool", long(-1))) == 1
+    assert int(ffi.cast("_Bool", 10**200)) == 1
+    assert int(ffi.cast("_Bool", 10**40000)) == 1
+    #
+    class Foo(object):
+        def __int__(self):
+            self.seen = 1
+            return result
+    f = Foo()
+    f.seen = 0
+    result = 42
+    assert int(ffi.cast("_Bool", f)) == 1
+    assert f.seen
+    f.seen = 0
+    result = 0
+    assert int(ffi.cast("_Bool", f)) == 0
+    assert f.seen
+    #
+    py.test.raises(TypeError, ffi.cast, "_Bool", [])
+
+def test_bool_on_long_double():
+    if sys.platform == 'win32':
+        py.test.skip("_Bool not in MSVC")
+    f = 1E-250
+    if f == 0.0 or f*f != 0.0:
+        py.test.skip("unexpected precision")
+    ffi = FFI()
+    ffi.cdef("long double square(long double f); _Bool opposite(_Bool);")
+    lib = ffi.verify("long double square(long double f) { return f*f; }\n"
+                     "_Bool opposite(_Bool x) { return !x; }")
+    f0 = lib.square(0.0)
+    f2 = lib.square(f)
+    f3 = lib.square(f * 2.0)
+    if repr(f2) == repr(f3):
+        py.test.skip("long double doesn't have enough precision")
+    assert float(f0) == float(f2) == float(f3) == 0.0  # too tiny for 'double'
+    assert int(ffi.cast("_Bool", f2)) == 1
+    assert int(ffi.cast("_Bool", f3)) == 1
+    assert int(ffi.cast("_Bool", f0)) == 0
+    py.test.raises(TypeError, lib.opposite, f2)
+
+def test_cannot_pass_float():
+    for basetype in ['char', 'short', 'int', 'long', 'long long']:
+        for sign in ['signed', 'unsigned']:
+            type = '%s %s' % (sign, basetype)
+            ffi = FFI()
+            ffi.cdef("struct foo_s { %s x; };\n"
+                     "int foo(%s);" % (type, type))
+            lib = ffi.verify("""
+                struct foo_s { %s x; };
+                int foo(%s arg) {
+                    return !arg;
+                }
+            """ % (type, type))
+            p = ffi.new("struct foo_s *")
+            py.test.raises(TypeError, "p.x = 0.0")
+            assert lib.foo(42) == 0
+            assert lib.foo(0) == 1
+            py.test.raises(TypeError, lib.foo, 0.0)
+
+def test_cast_from_int_type_to_bool():
+    ffi = FFI()
+    for basetype in ['char', 'short', 'int', 'long', 'long long']:
+        for sign in ['signed', 'unsigned']:
+            type = '%s %s' % (sign, basetype)
+            assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1
+            assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1
+            assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0
+
+def test_addressof():
+    ffi = FFI()
+    ffi.cdef("""
+        struct point_s { int x, y; };
+        struct foo_s { int z; struct point_s point; };
+        struct point_s sum_coord(struct point_s *);
+    """)
+    lib = ffi.verify("""
+        struct point_s { int x, y; };
+        struct foo_s { int z; struct point_s point; };
+        struct point_s sum_coord(struct point_s *point) {
+            struct point_s r;
+            r.x = point->x + point->y;
+            r.y = point->x - point->y;
+            return r;
+        }
+    """)
+    p = ffi.new("struct foo_s *")
+    p.point.x = 16
+    p.point.y = 9
+    py.test.raises(TypeError, lib.sum_coord, p.point)
+    res = lib.sum_coord(ffi.addressof(p.point))
+    assert res.x == 25
+    assert res.y == 7
+    res2 = lib.sum_coord(ffi.addressof(res))
+    assert res2.x == 32
+    assert res2.y == 18
+    py.test.raises(TypeError, lib.sum_coord, res2)
+
+def test_callback_in_thread():
+    if sys.platform == 'win32':
+        py.test.skip("pthread only")
+    import os, subprocess, imp
+    arg = os.path.join(os.path.dirname(__file__), 'callback_in_thread.py')
+    g = subprocess.Popen([sys.executable, arg,
+                          os.path.dirname(imp.find_module('cffi')[1])])
+    result = g.wait()
+    assert result == 0
+
+def test_keepalive_lib():
+    ffi = FFI()
+    ffi.cdef("int foobar(void);")
+    lib = ffi.verify("int foobar(void) { return 42; }")
+    func = lib.foobar
+    ffi_r = weakref.ref(ffi)
+    lib_r = weakref.ref(lib)
+    del ffi
+    import gc; gc.collect()       # lib stays alive
+    assert lib_r() is not None
+    assert ffi_r() is not None
+    assert func() == 42
+
+def test_keepalive_ffi():
+    ffi = FFI()
+    ffi.cdef("int foobar(void);")
+    lib = ffi.verify("int foobar(void) { return 42; }")
+    func = lib.foobar
+    ffi_r = weakref.ref(ffi)
+    lib_r = weakref.ref(lib)
+    del lib
+    import gc; gc.collect()       # ffi stays alive
+    assert ffi_r() is not None
+    assert lib_r() is not None
+    assert func() == 42
+
+def test_FILE_stored_in_stdout():
+    if not sys.platform.startswith('linux'):
+        py.test.skip("likely, we cannot assign to stdout")
+    ffi = FFI()
+    ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);")
+    lib = ffi.verify("""
+        #include <stdio.h>
+        FILE *setstdout(FILE *f) {
+            FILE *result = stdout;
+            stdout = f;
+            return result;
+        }
+    """)
+    import os
+    fdr, fdw = os.pipe()
+    fw1 = os.fdopen(fdw, 'wb', 256)
+    old_stdout = lib.setstdout(fw1)
+    try:
+        #
+        fw1.write(b"X")
+        r = lib.printf(b"hello, %d!\n", ffi.cast("int", 42))
+        fw1.close()
+        assert r == len("hello, 42!\n")
+        #
+    finally:
+        lib.setstdout(old_stdout)
+    #
+    result = os.read(fdr, 256)
+    os.close(fdr)
+    # the 'X' might remain in the user-level buffer of 'fw1' and
+    # end up showing up after the 'hello, 42!\n'
+    assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX"
+
+def test_FILE_stored_explicitly():
+    ffi = FFI()
+    ffi.cdef("int myprintf11(const char *, int); FILE *myfile;")
+    lib = ffi.verify("""
+        #include <stdio.h>
+        FILE *myfile;
+        int myprintf11(const char *out, int value) {
+            return fprintf(myfile, out, value);
+        }
+    """)
+    import os
+    fdr, fdw = os.pipe()
+    fw1 = os.fdopen(fdw, 'wb', 256)
+    lib.myfile = ffi.cast("FILE *", fw1)
+    #
+    fw1.write(b"X")
+    r = lib.myprintf11(b"hello, %d!\n", ffi.cast("int", 42))
+    fw1.close()
+    assert r == len("hello, 42!\n")
+    #
+    result = os.read(fdr, 256)
+    os.close(fdr)
+    # the 'X' might remain in the user-level buffer of 'fw1' and
+    # end up showing up after the 'hello, 42!\n'
+    assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX"
+
+def test_global_array_with_missing_length():
+    ffi = FFI()
+    ffi.cdef("int fooarray[];")
+    lib = ffi.verify("int fooarray[50];")
+    assert repr(lib.fooarray).startswith("<cdata 'int *'")
+
+def test_global_array_with_dotdotdot_length():
+    ffi = FFI()
+    ffi.cdef("int fooarray[...];")
+    lib = ffi.verify("int fooarray[50];")
+    assert repr(lib.fooarray).startswith("<cdata 'int[50]'")
+
+def test_bad_global_array_with_dotdotdot_length():
+    ffi = FFI()
+    ffi.cdef("int fooarray[...];")
+    py.test.raises(VerificationError, ffi.verify, "char fooarray[23];")
+
+def test_struct_containing_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { ...; }; struct bar_s { struct foo_s f; ...; };")
+    ffi.verify("struct foo_s { int x; }; struct bar_s { struct foo_s f; };")
+    #
+    ffi = FFI()
+    ffi.cdef("struct foo_s { struct bar_s f; ...; }; struct bar_s { ...; };")
+    ffi.verify("struct bar_s { int x; }; struct foo_s { struct bar_s f; };")
+
+def test_struct_returned_by_func():
+    ffi = FFI()
+    ffi.cdef("typedef ... foo_t; foo_t myfunc(void);")
+    e = py.test.raises(TypeError, ffi.verify,
+                       "typedef struct { int x; } foo_t; "
+                       "foo_t myfunc(void) { foo_t x = { 42 }; return x; }")
+    assert str(e.value) == (
+        "function myfunc: 'foo_t' is used as result type, but is opaque")
+
+def test_include():
+    ffi1 = FFI()
+    ffi1.cdef("typedef struct { int x; ...; } foo_t;")
+    ffi1.verify("typedef struct { int y, x; } foo_t;")
+    ffi2 = FFI()
+    ffi2.include(ffi1)
+    ffi2.cdef("int myfunc(foo_t *);")
+    lib = ffi2.verify("typedef struct { int y, x; } foo_t;"
+                      "int myfunc(foo_t *p) { return 42 * p->x; }")
+    res = lib.myfunc(ffi2.new("foo_t *", {'x': 10}))
+    assert res == 420
+    res = lib.myfunc(ffi1.new("foo_t *", {'x': -10}))
+    assert res == -420
+
+def test_include_enum():
+    ffi1 = FFI()
+    ffi1.cdef("enum foo_e { AA, ... };")
+    lib1 = ffi1.verify("enum foo_e { CC, BB, AA };")
+    ffi2 = FFI()
+    ffi2.include(ffi1)
+    ffi2.cdef("int myfunc(enum foo_e);")
+    lib2 = ffi2.verify("enum foo_e { CC, BB, AA };"
+                       "int myfunc(enum foo_e x) { return (int)x; }")
+    res = lib2.myfunc(lib2.AA)
+    assert res == 2
+
+def test_named_pointer_as_argument():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; } *mystruct_p;\n"
+             "mystruct_p ff5a(mystruct_p);")
+    lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n"
+                     "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }")
+    p = ffi.new("mystruct_p", [-2])
+    q = lib.ff5a(p)
+    assert q == p
+    assert p.x == 38
+
+def test_enum_size():
+    cases = [('123',           4, 4294967295),
+             ('4294967295U',   4, 4294967295),
+             ('-123',          4, -1),
+             ('-2147483647-1', 4, -1),
+             ]
+    if FFI().sizeof("long") == 8:
+        cases += [('4294967296L',        8, 2**64-1),
+                  ('%dUL' % (2**64-1),   8, 2**64-1),
+                  ('-2147483649L',       8, -1),
+                  ('%dL-1L' % (1-2**63), 8, -1)]
+    for hidden_value, expected_size, expected_minus1 in cases:
+        if sys.platform == 'win32' and 'U' in hidden_value:
+            continue   # skipped on Windows
+        ffi = FFI()
+        ffi.cdef("enum foo_e { AA, BB, ... };")
+        lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value)
+        assert lib.AA == 0
+        assert lib.BB == eval(hidden_value.replace('U', '').replace('L', ''))
+        assert ffi.sizeof("enum foo_e") == expected_size
+        assert int(ffi.cast("enum foo_e", -1)) == expected_minus1
+    # test with the large value hidden:
+    # disabled so far, doesn't work
+##    for hidden_value, expected_size, expected_minus1 in cases:
+##        ffi = FFI()
+##        ffi.cdef("enum foo_e { AA, BB, ... };")
+##        lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value)
+##        assert lib.AA == 0
+##        assert ffi.sizeof("enum foo_e") == expected_size
+##        assert int(ffi.cast("enum foo_e", -1)) == expected_minus1
+
+def test_enum_bug118():
+    maxulong = 256 ** FFI().sizeof("unsigned long") - 1
+    for c1, c2, c2c in [(0xffffffff, -1, ''),
+                        (maxulong, -1, ''),
+                        (-1, 0xffffffff, 'U'),
+                        (-1, maxulong, 'UL')]:
+        if c2c and sys.platform == 'win32':
+            continue     # enums may always be signed with MSVC
+        ffi = FFI()
+        ffi.cdef("enum foo_e { AA=%s };" % c1)
+        e = py.test.raises(VerificationError, ffi.verify,
+                           "enum foo_e { AA=%s%s };" % (c2, c2c))
+        assert str(e.value) == ('enum foo_e: AA has the real value %d, not %d'
+                                % (c2, c1))
+
+def test_string_to_voidp_arg():
+    ffi = FFI()
+    ffi.cdef("int myfunc(void *);")
+    lib = ffi.verify("int myfunc(void *p) { return ((signed char *)p)[0]; }")
+    res = lib.myfunc(b"hi!")
+    assert res == ord(b"h")
+    p = ffi.new("char[]", b"gah")
+    res = lib.myfunc(p)
+    assert res == ord(b"g")
+    res = lib.myfunc(ffi.cast("void *", p))
+    assert res == ord(b"g")
+    res = lib.myfunc(ffi.cast("int *", p))
+    assert res == ord(b"g")
+
+def test_callback_indirection():
+    ffi = FFI()
+    ffi.cdef("""
+        int (*python_callback)(int how_many, int *values);
+        int (*const c_callback)(int,...);   /* pass this ptr to C routines */
+        int some_c_function(int(*cb)(int,...));
+    """)
+    lib = ffi.verify("""
+        #include <stdarg.h>
+        #ifdef _WIN32
+        #include <malloc.h>
+        #define alloca _alloca
+        #else
+        # ifdef __FreeBSD__
+        #  include <stdlib.h>
+        # else
+        #  include <alloca.h>
+        # endif
+        #endif
+        static int (*python_callback)(int how_many, int *values);
+        static int c_callback(int how_many, ...) {
+            va_list ap;
+            /* collect the "..." arguments into the values[] array */
+            int i, *values = alloca((size_t)how_many * sizeof(int));
+            va_start(ap, how_many);
+            for (i=0; i<how_many; i++)
+                values[i] = va_arg(ap, int);
+            va_end(ap);
+            return python_callback(how_many, values);
+        }
+        int some_c_function(int(*cb)(int,...)) {
+            int result = cb(2, 10, 20);
+            result += cb(3, 30, 40, 50);
+            return result;
+        }
+    """)
+    seen = []
+    @ffi.callback("int(int, int*)")
+    def python_callback(how_many, values):
+        seen.append([values[i] for i in range(how_many)])
+        return 42
+    lib.python_callback = python_callback
+
+    res = lib.some_c_function(lib.c_callback)
+    assert res == 84
+    assert seen == [[10, 20], [30, 40, 50]]
+
+def test_floatstar_argument():
+    ffi = FFI()
+    ffi.cdef("float sum3floats(float *);")
+    lib = ffi.verify("""
+        float sum3floats(float *f) {
+            return f[0] + f[1] + f[2];
+        }
+    """)
+    assert lib.sum3floats((1.5, 2.5, 3.5)) == 7.5
+    p = ffi.new("float[]", (1.5, 2.5, 3.5))
+    assert lib.sum3floats(p) == 7.5
+
+def test_charstar_argument():
+    ffi = FFI()
+    ffi.cdef("char sum3chars(char *);")
+    lib = ffi.verify("""
+        char sum3chars(char *f) {
+            return (char)(f[0] + f[1] + f[2]);
+        }
+    """)
+    assert lib.sum3chars((b'\x10', b'\x20', b'\x30')) == b'\x60'
+    p = ffi.new("char[]", b'\x10\x20\x30')
+    assert lib.sum3chars(p) == b'\x60'
+
+def test_passing_string_or_NULL():
+    ffi = FFI()
+    ffi.cdef("int seeme1(char *); int seeme2(int *);")
+    lib = ffi.verify("""
+        int seeme1(char *x) {
+            return (x == NULL);
+        }
+        int seeme2(int *x) {
+            return (x == NULL);
+        }
+    """)
+    assert lib.seeme1(b"foo") == 0
+    assert lib.seeme1(ffi.NULL) == 1
+    assert lib.seeme2([42, 43]) == 0
+    assert lib.seeme2(ffi.NULL) == 1
+    py.test.raises(TypeError, lib.seeme1, None)
+    py.test.raises(TypeError, lib.seeme2, None)
+    py.test.raises(TypeError, lib.seeme1, 0.0)
+    py.test.raises(TypeError, lib.seeme2, 0.0)
+    py.test.raises(TypeError, lib.seeme1, 0)
+    py.test.raises(TypeError, lib.seeme2, 0)
+    zeroL  = 99999999999999999999
+    zeroL -= 99999999999999999999
+    py.test.raises(TypeError, lib.seeme2, zeroL)
+
+def test_typeof_function():
+    ffi = FFI()
+    ffi.cdef("int foo(int, char);")
+    lib = ffi.verify("int foo(int x, char y) { (void)x; (void)y; return 42; }")
+    ctype = ffi.typeof(lib.foo)
+    assert len(ctype.args) == 2
+    assert ctype.result == ffi.typeof("int")
+
+def test_call_with_voidstar_arg():
+    ffi = FFI()
+    ffi.cdef("int f(void *);")
+    lib = ffi.verify("int f(void *x) { return ((char*)x)[0]; }")
+    assert lib.f(b"foobar") == ord(b"f")
+
+def test_dir():
+    ffi = FFI()
+    ffi.cdef("""void somefunc(void);
+                extern int somevar, somearray[2];
+                static char *const sv2;
+                enum my_e { AA, BB, ... };
+                #define FOO ...""")
+    lib = ffi.verify("""void somefunc(void) { }
+                        int somevar, somearray[2];
+                        #define sv2 "text"
+                        enum my_e { AA, BB };
+                        #define FOO 42""")
+    assert dir(lib) == ['AA', 'BB', 'FOO', 'somearray',
+                        'somefunc', 'somevar', 'sv2']
+
+def test_typeof_func_with_struct_argument():
+    ffi = FFI()
+    ffi.cdef("""struct s { int a; }; int foo(struct s);""")
+    lib = ffi.verify("""struct s { int a; };
+                        int foo(struct s x) { return x.a; }""")
+    s = ffi.new("struct s *", [-1234])
+    m = lib.foo(s[0])
+    assert m == -1234
+    assert repr(ffi.typeof(lib.foo)) == "<ctype 'int(*)(struct s)'>"
+
+def test_bug_const_char_ptr_array_1():
+    ffi = FFI()
+    ffi.cdef("""const char *a[...];""")
+    lib = ffi.verify("""const char *a[5];""")
+    assert repr(ffi.typeof(lib.a)) == "<ctype 'char *[5]'>"
+
+def test_bug_const_char_ptr_array_2():
+    from cffi import FFI     # ignore warnings
+    ffi = FFI()
+    ffi.cdef("""const int a[];""")
+    lib = ffi.verify("""const int a[5];""")
+    assert repr(ffi.typeof(lib.a)) == "<ctype 'int *'>"
+
+def _test_various_calls(force_libffi):
+    cdef_source = """
+    int xvalue;
+    long long ivalue, rvalue;
+    float fvalue;
+    double dvalue;
+    long double Dvalue;
+    signed char tf_bb(signed char x, signed char c);
+    unsigned char tf_bB(signed char x, unsigned char c);
+    short tf_bh(signed char x, short c);
+    unsigned short tf_bH(signed char x, unsigned short c);
+    int tf_bi(signed char x, int c);
+    unsigned int tf_bI(signed char x, unsigned int c);
+    long tf_bl(signed char x, long c);
+    unsigned long tf_bL(signed char x, unsigned long c);
+    long long tf_bq(signed char x, long long c);
+    unsigned long long tf_bQ(signed char x, unsigned long long c);
+    float tf_bf(signed char x, float c);
+    double tf_bd(signed char x, double c);
+    long double tf_bD(signed char x, long double c);
+    """
+    if force_libffi:
+        cdef_source = (cdef_source
+            .replace('tf_', '(*const tf_')
+            .replace('(signed char x', ')(signed char x'))
+    ffi = FFI()
+    ffi.cdef(cdef_source)
+    lib = ffi.verify("""
+    int xvalue;
+    long long ivalue, rvalue;
+    float fvalue;
+    double dvalue;
+    long double Dvalue;
+
+    typedef signed char b_t;
+    typedef unsigned char B_t;
+    typedef short h_t;
+    typedef unsigned short H_t;
+    typedef int i_t;
+    typedef unsigned int I_t;
+    typedef long l_t;
+    typedef unsigned long L_t;
+    typedef long long q_t;
+    typedef unsigned long long Q_t;
+    typedef float f_t;
+    typedef double d_t;
+    typedef long double D_t;
+    #define S(letter)  xvalue = (int)x; letter##value = (letter##_t)c;
+    #define R(letter)  return (letter##_t)rvalue;
+
+    signed char tf_bb(signed char x, signed char c) { S(i) R(b) }
+    unsigned char tf_bB(signed char x, unsigned char c) { S(i) R(B) }
+    short tf_bh(signed char x, short c) { S(i) R(h) }
+    unsigned short tf_bH(signed char x, unsigned short c) { S(i) R(H) }
+    int tf_bi(signed char x, int c) { S(i) R(i) }
+    unsigned int tf_bI(signed char x, unsigned int c) { S(i) R(I) }
+    long tf_bl(signed char x, long c) { S(i) R(l) }
+    unsigned long tf_bL(signed char x, unsigned long c) { S(i) R(L) }
+    long long tf_bq(signed char x, long long c) { S(i) R(q) }
+    unsigned long long tf_bQ(signed char x, unsigned long long c) { S(i) R(Q) }
+    float tf_bf(signed char x, float c) { S(f) R(f) }
+    double tf_bd(signed char x, double c) { S(d) R(d) }
+    long double tf_bD(signed char x, long double c) { S(D) R(D) }
+    """)
+    lib.rvalue = 0x7182838485868788
+    for kind, cname in [('b', 'signed char'),
+                        ('B', 'unsigned char'),
+                        ('h', 'short'),
+                        ('H', 'unsigned short'),
+                        ('i', 'int'),
+                        ('I', 'unsigned int'),
+                        ('l', 'long'),
+                        ('L', 'unsigned long'),
+                        ('q', 'long long'),
+                        ('Q', 'unsigned long long'),
+                        ('f', 'float'),
+                        ('d', 'double'),
+                        ('D', 'long double')]:
+        sign = +1 if 'unsigned' in cname else -1
+        lib.xvalue = 0
+        lib.ivalue = 0
+        lib.fvalue = 0
+        lib.dvalue = 0
+        lib.Dvalue = 0
+        fun = getattr(lib, 'tf_b' + kind)
+        res = fun(-42, sign * 99)
+        if kind == 'D':
+            res = float(res)
+        assert res == int(ffi.cast(cname, 0x7182838485868788))
+        assert lib.xvalue == -42
+        if kind in 'fdD':
+            assert float(getattr(lib, kind + 'value')) == -99.0
+        else:
+            assert lib.ivalue == sign * 99
+
+def test_various_calls_direct():
+    _test_various_calls(force_libffi=False)
+
+def test_various_calls_libffi():
+    _test_various_calls(force_libffi=True)
+
+def test_ptr_to_opaque():
+    ffi = FFI()
+    ffi.cdef("typedef ... foo_t; int f1(foo_t*); foo_t *f2(int);")
+    lib = ffi.verify("""
+        #include <stdlib.h>
+        typedef struct { int x; } foo_t;
+        int f1(foo_t* p) {
+            int x = p->x;
+            free(p);
+            return x;
+        }
+        foo_t *f2(int x) {
+            foo_t *p = malloc(sizeof(foo_t));
+            p->x = x;
+            return p;
+        }
+    """)
+    p = lib.f2(42)
+    x = lib.f1(p)
+    assert x == 42
+
+def _run_in_multiple_threads(test1):
+    test1()
+    import sys
+    try:
+        import thread
+    except ImportError:
+        import _thread as thread
+    errors = []
+    def wrapper(lock):
+        try:
+            test1()
+        except:
+            errors.append(sys.exc_info())
+        lock.release()
+    locks = []
+    for i in range(10):
+        _lock = thread.allocate_lock()
+        _lock.acquire()
+        thread.start_new_thread(wrapper, (_lock,))
+        locks.append(_lock)
+    for _lock in locks:
+        _lock.acquire()
+        if errors:
+            raise errors[0][1]
+
+def test_errno_working_even_with_pypys_jit():
+    ffi = FFI()
+    ffi.cdef("int f(int);")
+    lib = ffi.verify("""
+        #include <errno.h>
+        int f(int x) { return (errno = errno + x); }
+    """)
+    @_run_in_multiple_threads
+    def test1():
+        ffi.errno = 0
+        for i in range(10000):
+            e = lib.f(1)
+            assert e == i + 1
+            assert ffi.errno == e
+        for i in range(10000):
+            ffi.errno = i
+            e = lib.f(42)
+            assert e == i + 42
+
+def test_getlasterror_working_even_with_pypys_jit():
+    if sys.platform != 'win32':
+        py.test.skip("win32-only test")
+    ffi = FFI()
+    ffi.cdef("void SetLastError(DWORD);")
+    lib = ffi.dlopen("Kernel32.dll")
+    @_run_in_multiple_threads
+    def test1():
+        for i in range(10000):
+            n = (1 << 29) + i
+            lib.SetLastError(n)
+            assert ffi.getwinerror()[0] == n
+
+def test_verify_dlopen_flags():
+    # Careful with RTLD_GLOBAL.  If by chance the FFI is not deleted
+    # promptly, like on PyPy, then other tests may see the same
+    # exported symbols as well.  So we must not export a simple name
+    # like 'foo'!
+    ffi1 = FFI()
+    ffi1.cdef("int foo_verify_dlopen_flags;")
+
+    lib1 = ffi1.verify("int foo_verify_dlopen_flags;",
+                       flags=ffi1.RTLD_GLOBAL | ffi1.RTLD_LAZY)
+    lib2 = get_second_lib()
+
+    lib1.foo_verify_dlopen_flags = 42
+    assert lib2.foo_verify_dlopen_flags == 42
+    lib2.foo_verify_dlopen_flags += 1
+    assert lib1.foo_verify_dlopen_flags == 43
+
+def get_second_lib():
+    # Hack, using modulename makes the test fail
+    ffi2 = FFI()
+    ffi2.cdef("int foo_verify_dlopen_flags;")
+    lib2 = ffi2.verify("int foo_verify_dlopen_flags;",
+                       flags=ffi2.RTLD_GLOBAL | ffi2.RTLD_LAZY)
+    return lib2
+
+def test_consider_not_implemented_function_type():
+    ffi = FFI()
+    ffi.cdef("typedef union { int a; float b; } Data;"
+             "typedef struct { int a:2; } MyStr;"
+             "typedef void (*foofunc_t)(Data);"
+             "typedef Data (*bazfunc_t)(void);"
+             "typedef MyStr (*barfunc_t)(void);")
+    fooptr = ffi.cast("foofunc_t", 123)
+    bazptr = ffi.cast("bazfunc_t", 123)
+    barptr = ffi.cast("barfunc_t", 123)
+    # assert did not crash so far
+    e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *"))
+    assert str(e.value) == (
+        "ctype 'Data' not supported as argument by libffi.  Unions are only "
+        "supported as argument if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+"
+        "ffibuilder.set_source() and not taking a final '...' argument)")
+    e = py.test.raises(NotImplementedError, bazptr)
+    assert str(e.value) == (
+        "ctype 'Data' not supported as return value by libffi.  Unions are "
+        "only supported as return value if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+"
+        "ffibuilder.set_source() and not taking a final '...' argument)")
+    e = py.test.raises(NotImplementedError, barptr)
+    assert str(e.value) == (
+        "ctype 'MyStr' not supported as return value.  It is a struct with "
+        "bit fields, which libffi does not support.  Such structs are only "
+        "supported as return value if the function is 'API mode' and non-"
+        "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder."
+        "set_source() and not taking a final '...' argument)")
+
+def test_verify_extra_arguments():
+    ffi = FFI()
+    ffi.cdef("#define ABA ...")
+    lib = ffi.verify("", define_macros=[('ABA', '42')])
+    assert lib.ABA == 42
+
+def test_implicit_unicode_on_windows():
+    if sys.platform != 'win32':
+        py.test.skip("win32-only test")
+    ffi = FFI()
+    e = py.test.raises(FFIError, ffi.cdef, "int foo(LPTSTR);")
+    assert str(e.value) == ("The Windows type 'LPTSTR' is only available after"
+                            " you call ffi.set_unicode()")
+    for with_unicode in [True, False]:
+        ffi = FFI()
+        ffi.set_unicode(with_unicode)
+        ffi.cdef("""
+            DWORD GetModuleFileName(HMODULE hModule, LPTSTR lpFilename,
+                                    DWORD nSize);
+        """)
+        lib = ffi.verify("""
+            #include <windows.h>
+        """, libraries=['Kernel32'])
+        outbuf = ffi.new("TCHAR[]", 200)
+        n = lib.GetModuleFileName(ffi.NULL, outbuf, 500)
+        assert 0 < n < 500
+        for i in range(n):
+            #print repr(outbuf[i])
+            assert ord(outbuf[i]) != 0
+        assert ord(outbuf[n]) == 0
+        assert ord(outbuf[0]) < 128     # should be a letter, or '\'
+
+def test_use_local_dir():
+    ffi = FFI()
+    lib = ffi.verify("", modulename="test_use_local_dir")
+    this_dir = os.path.dirname(__file__)
+    pycache_files = os.listdir(os.path.join(this_dir, '__pycache__'))
+    assert any('test_use_local_dir' in s for s in pycache_files)
+
+def test_define_known_value():
+    ffi = FFI()
+    ffi.cdef("#define FOO 0x123")
+    lib = ffi.verify("#define FOO 0x123")
+    assert lib.FOO == 0x123
+
+def test_define_wrong_value():
+    ffi = FFI()
+    ffi.cdef("#define FOO 123")
+    e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124")
+    assert str(e.value).endswith("FOO has the real value 124, not 123")
+
+def test_static_const_int_known_value():
+    ffi = FFI()
+    ffi.cdef("static const int FOO = 0x123;")
+    lib = ffi.verify("#define FOO 0x123")
+    assert lib.FOO == 0x123
+
+def test_static_const_int_wrong_value():
+    ffi = FFI()
+    ffi.cdef("static const int FOO = 123;")
+    e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124")
+    assert str(e.value).endswith("FOO has the real value 124, not 123")
+
+def test_const_struct_global():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; ...; } T; const T myglob;")
+    lib = ffi.verify("typedef struct { double y; int x; } T;"
+                     "const T myglob = { 0.1, 42 };")
+    assert ffi.typeof(lib.myglob) == ffi.typeof("T")
+    assert lib.myglob.x == 42
+
+def test_dont_support_int_dotdotdot():
+    ffi = FFI()
+    ffi.cdef("typedef int... t1;")
+    e = py.test.raises(VerificationError, ffi.verify, "")
+    assert str(e.value) == ("feature not supported with ffi.verify(), but only "
+                            "with ffi.set_source(): 'typedef int... t1'")
+    ffi = FFI()
+    ffi.cdef("typedef double ... t1;")
+    e = py.test.raises(VerificationError, ffi.verify, "")
+    assert str(e.value) == ("feature not supported with ffi.verify(), but only "
+                         "with ffi.set_source(): 'typedef float... t1'")
+
+def test_const_fields():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { const int a; void *const b; };""")
+    ffi.verify("""struct foo_s { const int a; void *const b; };""")
+    foo_s = ffi.typeof("struct foo_s")
+    assert foo_s.fields[0][0] == 'a'
+    assert foo_s.fields[0][1].type is ffi.typeof("int")
+    assert foo_s.fields[1][0] == 'b'
+    assert foo_s.fields[1][1].type is ffi.typeof("void *")
+
+def test_win32_calling_convention_0():
+    ffi = FFI()
+    ffi.cdef("""
+        int call1(int(__cdecl   *cb)(int));
+        int (*const call2)(int(__stdcall *cb)(int));
+    """)
+    lib = ffi.verify(r"""
+        #ifndef _MSC_VER
+        #  define __stdcall  /* nothing */
+        #endif
+        int call1(int(*cb)(int)) {
+            int i, result = 0;
+            //printf("call1: cb = %p\n", cb);
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+        int call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            //printf("call2: cb = %p\n", cb);
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+    """)
+    @ffi.callback("int(int)")
+    def cb1(x):
+        return x * 2
+    @ffi.callback("int __stdcall(int)")
+    def cb2(x):
+        return x * 3
+    #print 'cb1 =', cb1
+    res = lib.call1(cb1)
+    assert res == 500*999*2
+    #print 'cb2 =', cb2
+    #print ffi.typeof(lib.call2)
+    #print 'call2 =', lib.call2
+    res = lib.call2(cb2)
+    #print '...'
+    assert res == -500*999*3
+    #print 'done'
+    if sys.platform == 'win32' and sys.maxsize < 2**32:
+        assert '__stdcall' in str(ffi.typeof(cb2))
+        assert '__stdcall' not in str(ffi.typeof(cb1))
+        py.test.raises(TypeError, lib.call1, cb2)
+        py.test.raises(TypeError, lib.call2, cb1)
+    else:
+        assert '__stdcall' not in str(ffi.typeof(cb2))
+        assert ffi.typeof(cb2) is ffi.typeof(cb1)
+
+def test_win32_calling_convention_1():
+    ffi = FFI()
+    ffi.cdef("""
+        int __cdecl   call1(int(__cdecl   *cb)(int));
+        int __stdcall call2(int(__stdcall *cb)(int));
+        int (__cdecl   *const cb1)(int);
+        int (__stdcall *const cb2)(int);
+    """)
+    lib = ffi.verify(r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        int __cdecl   cb1(int x) { return x * 2; }
+        int __stdcall cb2(int x) { return x * 3; }
+
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            //printf("here1\n");
+            //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+        int __stdcall call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            //printf("here1\n");
+            //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2);
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+    """)
+    assert lib.call1(lib.cb1) == 500*999*2
+    assert lib.call2(lib.cb2) == -500*999*3
+
+def test_win32_calling_convention_2():
+    # any mistake in the declaration of plain function (including the
+    # precise argument types and, here, the calling convention) are
+    # automatically corrected.  But this does not apply to the 'cb'
+    # function pointer argument.
+    ffi = FFI()
+    ffi.cdef("""
+        int __stdcall call1(int(__cdecl   *cb)(int));
+        int __cdecl   call2(int(__stdcall *cb)(int));
+        int (__cdecl   *const cb1)(int);
+        int (__stdcall *const cb2)(int);
+    """)
+    lib = ffi.verify(r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            return result;
+        }
+        int __stdcall call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            return result;
+        }
+        int __cdecl   cb1(int x) { return x * 2; }
+        int __stdcall cb2(int x) { return x * 3; }
+    """)
+    assert lib.call1(lib.cb1) == 500*999*2
+    assert lib.call2(lib.cb2) == -500*999*3
+
+def test_win32_calling_convention_3():
+    ffi = FFI()
+    ffi.cdef("""
+        struct point { int x, y; };
+
+        int (*const cb1)(struct point);
+        int (__stdcall *const cb2)(struct point);
+
+        struct point __stdcall call1(int(*cb)(struct point));
+        struct point call2(int(__stdcall *cb)(struct point));
+    """)
+    lib = ffi.verify(r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        struct point { int x, y; };
+        int           cb1(struct point pt) { return pt.x + 10 * pt.y; }
+        int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; }
+        struct point __stdcall call1(int(__cdecl *cb)(struct point)) {
+            int i;
+            struct point result = { 0, 0 };
+            //printf("here1\n");
+            //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            for (i = 0; i < 1000; i++) {
+                struct point p = { i, -i };
+                int r = cb(p);
+                result.x += r;
+                result.y -= r;
+            }
+            return result;
+        }
+        struct point __cdecl call2(int(__stdcall *cb)(struct point)) {
+            int i;
+            struct point result = { 0, 0 };
+            for (i = 0; i < 1000; i++) {
+                struct point p = { -i, i };
+                int r = cb(p);
+                result.x += r;
+                result.y -= r;
+            }
+            return result;
+        }
+    """)
+    if sys.platform == 'win32' and sys.maxsize < 2**32:
+        py.test.raises(TypeError, lib.call1, lib.cb2)
+        py.test.raises(TypeError, lib.call2, lib.cb1)
+    pt = lib.call1(lib.cb1)
+    assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+    pt = lib.call2(lib.cb2)
+    assert (pt.x, pt.y) == (99*500*999, -99*500*999)
+
+def _only_test_on_linux_intel():
+    if not sys.platform.startswith('linux'):
+        py.test.skip('only running the memory-intensive test on Linux')
+    import platform
+    machine = platform.machine()
+    if 'x86' not in machine and 'x64' not in machine:
+        py.test.skip('only running the memory-intensive test on x86/x64')
+
+def test_ffi_gc_size_arg():
+    # with PyPy's GC, these calls to ffi.gc() would rapidly consume
+    # 40 GB of RAM without the third argument
+    _only_test_on_linux_intel()
+    ffi = FFI()
+    ffi.cdef("void *malloc(size_t); void free(void *);")
+    lib = ffi.verify(r"""
+        #include <stdlib.h>
+    """)
+    for i in range(2000):
+        p = lib.malloc(20*1024*1024)    # 20 MB
+        p1 = ffi.cast("char *", p)
+        for j in range(0, 20*1024*1024, 4096):
+            p1[j] = b'!'
+        p = ffi.gc(p, lib.free, 20*1024*1024)
+        del p
+
+def test_ffi_gc_size_arg_2():
+    # a variant of the above: this "attack" works on cpython's cyclic gc too
+    # and I found no obvious way to prevent that.  So for now, this test
+    # is skipped on CPython, where it eats all the memory.
+    if '__pypy__' not in sys.builtin_module_names:
+        py.test.skip("find a way to tweak the cyclic GC of CPython")
+    _only_test_on_linux_intel()
+    ffi = FFI()
+    ffi.cdef("void *malloc(size_t); void free(void *);")
+    lib = ffi.verify(r"""
+        #include <stdlib.h>
+    """)
+    class X(object):
+        pass
+    for i in range(2000):
+        p = lib.malloc(50*1024*1024)    # 50 MB
+        p1 = ffi.cast("char *", p)
+        for j in range(0, 50*1024*1024, 4096):
+            p1[j] = b'!'
+        p = ffi.gc(p, lib.free, 50*1024*1024)
+        x = X()
+        x.p = p
+        x.cyclic = x
+        del p, x
+
+def test_ffi_new_with_cycles():
+    # still another variant, with ffi.new()
+    if '__pypy__' not in sys.builtin_module_names:
+        py.test.skip("find a way to tweak the cyclic GC of CPython")
+    ffi = FFI()
+    ffi.cdef("")
+    lib = ffi.verify("")
+    class X(object):
+        pass
+    for i in range(2000):
+        p = ffi.new("char[]", 50*1024*1024)    # 50 MB
+        for j in range(0, 50*1024*1024, 4096):
+            p[j] = b'!'
+        x = X()
+        x.p = p
+        x.cyclic = x
+        del p, x
diff --git a/testing/cffi0/test_verify2.py b/testing/cffi0/test_verify2.py
new file mode 100644
index 0000000..a4af6d6
--- /dev/null
+++ b/testing/cffi0/test_verify2.py
@@ -0,0 +1,9 @@
+from .test_verify import *
+
+# This test file runs normally after test_verify.  We only clean up the .c
+# sources, to check that it also works when we have only the .so.  The
+# tests should run much faster than test_verify.
+
+def setup_module():
+    import cffi.verifier
+    cffi.verifier.cleanup_tmpdir(keep_so=True)
diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py
new file mode 100644
index 0000000..9325685
--- /dev/null
+++ b/testing/cffi0/test_version.py
@@ -0,0 +1,62 @@
+import py, os, sys
+import cffi, _cffi_backend
+
+def setup_module(mod):
+    if '_cffi_backend' in sys.builtin_module_names:
+        py.test.skip("this is embedded version")
+
+#BACKEND_VERSIONS = {
+#    '0.4.2': '0.4',     # did not change
+#    '0.7.1': '0.7',     # did not change
+#    '0.7.2': '0.7',     # did not change
+#    '0.8.1': '0.8',     # did not change (essentially)
+#    '0.8.4': '0.8.3',   # did not change
+#    }
+
+def test_version():
+    v = cffi.__version__
+    version_info = '.'.join(str(i) for i in cffi.__version_info__)
+    version_info = version_info.replace('.beta.', 'b')
+    version_info = version_info.replace('.plus', '+')
+    assert v == version_info
+    #v = BACKEND_VERSIONS.get(v, v)
+    assert v == _cffi_backend.__version__
+
+def test_doc_version():
+    parent = os.path.dirname(os.path.dirname(cffi.__file__))
+    p = os.path.join(parent, 'doc', 'source', 'conf.py')
+    content = open(p).read()
+    #
+    v = cffi.__version__
+    assert ("version = '%s'\n" % v[:4]) in content
+    assert ("release = '%s'\n" % v) in content
+
+def test_doc_version_file():
+    parent = os.path.dirname(os.path.dirname(cffi.__file__))
+    v = cffi.__version__.replace('+', '')
+    p = os.path.join(parent, 'doc', 'source', 'installation.rst')
+    content = open(p).read()
+    assert (" package version %s:" % v) in content
+
+def test_setup_version():
+    parent = os.path.dirname(os.path.dirname(cffi.__file__))
+    p = os.path.join(parent, 'setup.py')
+    content = open(p).read()
+    #
+    v = cffi.__version__.replace('+', '')
+    assert ("version='%s'" % v) in content
+
+def test_c_version():
+    parent = os.path.dirname(os.path.dirname(cffi.__file__))
+    v = cffi.__version__
+    p = os.path.join(parent, 'c', 'test_c.py')
+    content = open(p).read()
+    #v = BACKEND_VERSIONS.get(v, v)
+    assert (('assert __version__ == "%s"' % v) in content)
+
+def test_embedding_h():
+    parent = os.path.dirname(os.path.dirname(cffi.__file__))
+    v = cffi.__version__
+    p = os.path.join(parent, 'cffi', '_embedding.h')
+    content = open(p).read()
+    assert ('cffi version: %s"' % (v,)) in content
diff --git a/testing/cffi0/test_vgen.py b/testing/cffi0/test_vgen.py
new file mode 100644
index 0000000..1a7e05d
--- /dev/null
+++ b/testing/cffi0/test_vgen.py
@@ -0,0 +1,12 @@
+import cffi.verifier
+from .test_verify import *
+
+
+def setup_module():
+    cffi.verifier.cleanup_tmpdir()
+    cffi.verifier._FORCE_GENERIC_ENGINE = True
+    # Runs all tests with _FORCE_GENERIC_ENGINE = True, to make sure we
+    # also test vengine_gen.py.
+
+def teardown_module():
+    cffi.verifier._FORCE_GENERIC_ENGINE = False
diff --git a/testing/cffi0/test_vgen2.py b/testing/cffi0/test_vgen2.py
new file mode 100644
index 0000000..34147c8
--- /dev/null
+++ b/testing/cffi0/test_vgen2.py
@@ -0,0 +1,13 @@
+import cffi.verifier
+from .test_vgen import *
+
+# This test file runs normally after test_vgen.  We only clean up the .c
+# sources, to check that it also works when we have only the .so.  The
+# tests should run much faster than test_vgen.
+
+def setup_module():
+    cffi.verifier.cleanup_tmpdir(keep_so=True)
+    cffi.verifier._FORCE_GENERIC_ENGINE = True
+
+def teardown_module():
+    cffi.verifier._FORCE_GENERIC_ENGINE = False
diff --git a/testing/cffi0/test_zdistutils.py b/testing/cffi0/test_zdistutils.py
new file mode 100644
index 0000000..b67b105
--- /dev/null
+++ b/testing/cffi0/test_zdistutils.py
@@ -0,0 +1,288 @@
+import sys, os, imp, math, shutil
+import py
+from cffi import FFI, FFIError
+from cffi.verifier import Verifier, _locate_engine_class, _get_so_suffixes
+from cffi.ffiplatform import maybe_relative_path
+from testing.udir import udir
+
+
+class DistUtilsTest(object):
+    def setup_class(self):
+        self.lib_m = "m"
+        if sys.platform == 'win32':
+            #there is a small chance this fails on Mingw via environ $CC
+            import distutils.ccompiler
+            if distutils.ccompiler.get_default_compiler() == 'msvc':
+                self.lib_m = 'msvcrt'
+
+    def teardown_class(self):
+        if udir.isdir():
+            udir.remove(ignore_errors=True)
+        udir.ensure(dir=1)
+
+    def test_locate_engine_class(self):
+        cls = _locate_engine_class(FFI(), self.generic)
+        if self.generic:
+            # asked for the generic engine, which must not generate a
+            # CPython extension module
+            assert not cls._gen_python_module
+        else:
+            # asked for the CPython engine: check that we got it, unless
+            # we are running on top of PyPy, where the generic engine is
+            # always better
+            if '__pypy__' not in sys.builtin_module_names:
+                assert cls._gen_python_module
+
+    def test_write_source(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*hi there %s!*/\n#include <math.h>\n' % self
+        v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+                     libraries=[self.lib_m])
+        v.write_source()
+        with open(v.sourcefilename, 'r') as f:
+            data = f.read()
+        assert csrc in data
+
+    def test_write_source_explicit_filename(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*hi there %s!*/\n#include <math.h>\n' % self
+        v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+                     libraries=[self.lib_m])
+        v.sourcefilename = filename = str(udir.join('write_source.c'))
+        v.write_source()
+        assert filename == v.sourcefilename
+        with open(filename, 'r') as f:
+            data = f.read()
+        assert csrc in data
+
+    def test_write_source_to_file_obj(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*hi there %s!*/\n#include <math.h>\n' % self
+        v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+                     libraries=[self.lib_m])
+        try:
+            from StringIO import StringIO
+        except ImportError:
+            from io import StringIO
+        f = StringIO()
+        v.write_source(file=f)
+        assert csrc in f.getvalue()
+
+    def test_compile_module(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*hi there %s!*/\n#include <math.h>\n' % self
+        v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+                     libraries=[self.lib_m])
+        v.compile_module()
+        assert v.get_module_name().startswith('_cffi_')
+        if v.generates_python_module():
+            mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
+            assert hasattr(mod, '_cffi_setup')
+
+    def test_compile_module_explicit_filename(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*hi there %s!2*/\n#include <math.h>\n' % self
+        v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+                     libraries=[self.lib_m])
+        basename = self.__class__.__name__ + 'test_compile_module'
+        v.modulefilename = filename = str(udir.join(basename + '.so'))
+        v.compile_module()
+        assert filename == v.modulefilename
+        assert v.get_module_name() == basename
+        if v.generates_python_module():
+            mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
+            assert hasattr(mod, '_cffi_setup')
+
+    def test_name_from_checksum_of_cdef(self):
+        names = []
+        for csrc in ['double', 'double', 'float']:
+            ffi = FFI()
+            ffi.cdef("%s sin(double x);" % csrc)
+            v = Verifier(ffi, "#include <math.h>",
+                         force_generic_engine=self.generic,
+                         libraries=[self.lib_m])
+            names.append(v.get_module_name())
+        assert names[0] == names[1] != names[2]
+
+    def test_name_from_checksum_of_csrc(self):
+        names = []
+        for csrc in ['123', '123', '1234']:
+            ffi = FFI()
+            ffi.cdef("double sin(double x);")
+            v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+            names.append(v.get_module_name())
+        assert names[0] == names[1] != names[2]
+
+    def test_load_library(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*hi there %s!3*/\n#include <math.h>\n' % self
+        v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+                     libraries=[self.lib_m])
+        library = v.load_library()
+        assert library.sin(12.3) == math.sin(12.3)
+
+    def test_verifier_args(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*hi there %s!4*/#include "test_verifier_args.h"\n' % self
+        udir.join('test_verifier_args.h').write('#include <math.h>\n')
+        v = Verifier(ffi, csrc, include_dirs=[str(udir)],
+                     force_generic_engine=self.generic,
+                     libraries=[self.lib_m])
+        library = v.load_library()
+        assert library.sin(12.3) == math.sin(12.3)
+
+    def test_verifier_object_from_ffi(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = "/*6%s*/\n#include <math.h>" % self
+        lib = ffi.verify(csrc, force_generic_engine=self.generic,
+                         libraries=[self.lib_m])
+        assert lib.sin(12.3) == math.sin(12.3)
+        assert isinstance(ffi.verifier, Verifier)
+        with open(ffi.verifier.sourcefilename, 'r') as f:
+            data = f.read()
+        assert csrc in data
+
+    def test_extension_object(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*7%s*/' % self + '''
+    #include <math.h>
+    #ifndef TEST_EXTENSION_OBJECT
+    # error "define_macros missing"
+    #endif
+    '''
+        lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')],
+                         force_generic_engine=self.generic,
+                         libraries=[self.lib_m])
+        assert lib.sin(12.3) == math.sin(12.3)
+        v = ffi.verifier
+        ext = v.get_extension()
+        assert 'distutils.extension.Extension' in str(ext.__class__) or \
+               'setuptools.extension.Extension' in str(ext.__class__)
+        assert ext.sources == [maybe_relative_path(v.sourcefilename)]
+        assert ext.name == v.get_module_name()
+        assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')]
+
+    def test_extension_forces_write_source(self):
+        ffi = FFI()
+        ffi.cdef("double sin(double x);")
+        csrc = '/*hi there9!%s*/\n#include <math.h>\n' % self
+        v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+                     libraries=[self.lib_m])
+        assert not os.path.exists(v.sourcefilename)
+        v.get_extension()
+        assert os.path.exists(v.sourcefilename)
+
+    def test_extension_object_extra_sources(self):
+        ffi = FFI()
+        ffi.cdef("double test1eoes(double x);")
+        extra_source = str(udir.join('extension_extra_sources.c'))
+        with open(extra_source, 'w') as f:
+            f.write('double test1eoes(double x) { return x * 6.0; }\n')
+        csrc = '/*9%s*/' % self + '''
+        double test1eoes(double x);   /* or #include "extra_sources.h" */
+        '''
+        lib = ffi.verify(csrc, sources=[extra_source],
+                         force_generic_engine=self.generic)
+        assert lib.test1eoes(7.0) == 42.0
+        v = ffi.verifier
+        ext = v.get_extension()
+        assert 'distutils.extension.Extension' in str(ext.__class__) or \
+               'setuptools.extension.Extension' in str(ext.__class__)
+        assert ext.sources == [maybe_relative_path(v.sourcefilename),
+                               extra_source]
+        assert ext.name == v.get_module_name()
+
+    def test_install_and_reload_module(self, targetpackage='', ext_package=''):
+        KEY = repr(self)
+        if not hasattr(os, 'fork'):
+            py.test.skip("test requires os.fork()")
+
+        if targetpackage:
+            udir.ensure(targetpackage, dir=1).ensure('__init__.py')
+        sys.path.insert(0, str(udir))
+
+        def make_ffi(**verifier_args):
+            ffi = FFI()
+            ffi.cdef("/* %s, %s, %s */" % (KEY, targetpackage, ext_package))
+            ffi.cdef("double test1iarm(double x);")
+            csrc = "double test1iarm(double x) { return x * 42.0; }"
+            lib = ffi.verify(csrc, force_generic_engine=self.generic,
+                             ext_package=ext_package,
+                             **verifier_args)
+            return ffi, lib
+
+        childpid = os.fork()
+        if childpid == 0:
+            # in the child
+            ffi, lib = make_ffi()
+            assert lib.test1iarm(1.5) == 63.0
+            # "install" the module by moving it into udir (/targetpackage)
+            if targetpackage:
+                target = udir.join(targetpackage)
+            else:
+                target = udir
+            shutil.move(ffi.verifier.modulefilename, str(target))
+            os._exit(0)
+        # in the parent
+        _, status = os.waitpid(childpid, 0)
+        if not (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0):
+            raise AssertionError   # see error above in subprocess
+
+        from cffi import ffiplatform
+        prev_compile = ffiplatform.compile
+        try:
+            if targetpackage == ext_package:
+                ffiplatform.compile = lambda *args: dont_call_me_any_more
+            # won't find it in tmpdir, but should find it correctly
+            # installed in udir
+            ffi, lib = make_ffi()
+            assert lib.test1iarm(0.5) == 21.0
+        finally:
+            ffiplatform.compile = prev_compile
+
+    def test_install_and_reload_module_package(self):
+        self.test_install_and_reload_module(targetpackage='foo_iarmp',
+                                            ext_package='foo_iarmp')
+
+    def test_install_and_reload_module_ext_package_not_found(self):
+        self.test_install_and_reload_module(targetpackage='foo_epnf',
+                                            ext_package='not_found')
+
+    def test_tag(self):
+        ffi = FFI()
+        ffi.cdef("/* %s test_tag */ double test1tag(double x);" % self)
+        csrc = "double test1tag(double x) { return x - 42.0; }"
+        lib = ffi.verify(csrc, force_generic_engine=self.generic,
+                         tag='xxtest_tagxx')
+        assert lib.test1tag(143) == 101.0
+        assert '_cffi_xxtest_tagxx_' in ffi.verifier.modulefilename
+
+    def test_modulename(self):
+        ffi = FFI()
+        ffi.cdef("/* %s test_modulename */ double test1foo(double x);" % self)
+        csrc = "double test1foo(double x) { return x - 63.0; }"
+        modname = 'xxtest_modulenamexx%d' % (self.generic,)
+        lib = ffi.verify(csrc, force_generic_engine=self.generic,
+                         modulename=modname)
+        assert lib.test1foo(143) == 80.0
+        suffix = _get_so_suffixes()[0]
+        fn1 = os.path.join(ffi.verifier.tmpdir, modname + '.c')
+        fn2 = os.path.join(ffi.verifier.tmpdir, modname + suffix)
+        assert ffi.verifier.sourcefilename == fn1
+        assert ffi.verifier.modulefilename == fn2
+
+
+class TestDistUtilsCPython(DistUtilsTest):
+    generic = False
+
+class TestDistUtilsGeneric(DistUtilsTest):
+    generic = True
diff --git a/testing/cffi0/test_zintegration.py b/testing/cffi0/test_zintegration.py
new file mode 100644
index 0000000..d56dac2
--- /dev/null
+++ b/testing/cffi0/test_zintegration.py
@@ -0,0 +1,176 @@
+import py, os, sys, shutil
+import subprocess
+from testing.udir import udir
+
+if sys.platform == 'win32':
+    py.test.skip('snippets do not run on win32')
+if sys.version_info < (2, 7):
+    py.test.skip('fails e.g. on a Debian/Ubuntu which patches virtualenv'
+                 ' in a non-2.6-friendly way')
+
+def create_venv(name):
+    tmpdir = udir.join(name)
+    try:
+        subprocess.check_call(['virtualenv', 
+            #'--never-download', <= could be added, but causes failures
+            # in random cases on random machines
+                               '-p', os.path.abspath(sys.executable),
+                               str(tmpdir)])
+    except OSError as e:
+        py.test.skip("Cannot execute virtualenv: %s" % (e,))
+
+    site_packages = None
+    for dirpath, dirnames, filenames in os.walk(str(tmpdir)):
+        if os.path.basename(dirpath) == 'site-packages':
+            site_packages = dirpath
+            break
+    paths = ""
+    if site_packages:
+        try:
+            from cffi import _pycparser
+            modules = ('cffi', '_cffi_backend')
+        except ImportError:
+            modules = ('cffi', '_cffi_backend', 'pycparser')
+            try:
+                import ply
+            except ImportError:
+                pass
+            else:
+                modules += ('ply',)   # needed for older versions of pycparser
+        paths = []
+        for module in modules:
+            target = __import__(module, None, None, [])
+            if not hasattr(target, '__file__'):   # for _cffi_backend on pypy
+                continue
+            src = os.path.abspath(target.__file__)
+            for end in ['__init__.pyc', '__init__.pyo', '__init__.py']:
+                if src.lower().endswith(end):
+                    src = src[:-len(end)-1]
+                    break
+            paths.append(os.path.dirname(src))
+        paths = os.pathsep.join(paths)
+    return tmpdir, paths
+
+SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets')
+
+def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet):
+    venv_dir, paths = venv_dir_and_paths
+    def remove(dir):
+        dir = str(SNIPPET_DIR.join(dirname, dir))
+        shutil.rmtree(dir, ignore_errors=True)
+    remove('build')
+    remove('__pycache__')
+    for basedir in os.listdir(str(SNIPPET_DIR.join(dirname))):
+        remove(os.path.join(basedir, '__pycache__'))
+    olddir = os.getcwd()
+    python_f = udir.join('x.py')
+    python_f.write(py.code.Source(python_snippet))
+    try:
+        os.chdir(str(SNIPPET_DIR.join(dirname)))
+        if os.name == 'nt':
+            bindir = 'Scripts'
+        else:
+            bindir = 'bin'
+        vp = str(venv_dir.join(bindir).join('python'))
+        env = os.environ.copy()
+        env['PYTHONPATH'] = paths
+        subprocess.check_call((vp, 'setup.py', 'clean'), env=env)
+        subprocess.check_call((vp, 'setup.py', 'install'), env=env)
+        subprocess.check_call((vp, str(python_f)), env=env)
+    finally:
+        os.chdir(olddir)
+
+def run_setup_and_program(dirname, python_snippet):
+    venv_dir = create_venv(dirname + '-cpy')
+    really_run_setup_and_program(dirname, venv_dir, python_snippet)
+    #
+    sys._force_generic_engine_ = True
+    try:
+        venv_dir = create_venv(dirname + '-gen')
+        really_run_setup_and_program(dirname, venv_dir, python_snippet)
+    finally:
+        del sys._force_generic_engine_
+    # the two files lextab.py and yacctab.py are created by not-correctly-
+    # installed versions of pycparser.
+    assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'lextab.py')))
+    assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'yacctab.py')))
+
+class TestZIntegration(object):
+    def teardown_class(self):
+        if udir.isdir():
+            udir.remove(ignore_errors=True)
+        udir.ensure(dir=1)
+
+    def test_infrastructure(self):
+        run_setup_and_program('infrastructure', '''
+        import snip_infrastructure
+        assert snip_infrastructure.func() == 42
+        ''')
+
+    def test_distutils_module(self):
+        run_setup_and_program("distutils_module", '''
+        import snip_basic_verify
+        p = snip_basic_verify.C.getpwuid(0)
+        assert snip_basic_verify.ffi.string(p.pw_name) == b"root"
+        ''')
+
+    def test_distutils_package_1(self):
+        run_setup_and_program("distutils_package_1", '''
+        import snip_basic_verify1
+        p = snip_basic_verify1.C.getpwuid(0)
+        assert snip_basic_verify1.ffi.string(p.pw_name) == b"root"
+        ''')
+
+    def test_distutils_package_2(self):
+        run_setup_and_program("distutils_package_2", '''
+        import snip_basic_verify2
+        p = snip_basic_verify2.C.getpwuid(0)
+        assert snip_basic_verify2.ffi.string(p.pw_name) == b"root"
+        ''')
+
+    def test_setuptools_module(self):
+        run_setup_and_program("setuptools_module", '''
+        import snip_setuptools_verify
+        p = snip_setuptools_verify.C.getpwuid(0)
+        assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root"
+        ''')
+
+    def test_setuptools_package_1(self):
+        run_setup_and_program("setuptools_package_1", '''
+        import snip_setuptools_verify1
+        p = snip_setuptools_verify1.C.getpwuid(0)
+        assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root"
+        ''')
+
+    def test_setuptools_package_2(self):
+        run_setup_and_program("setuptools_package_2", '''
+        import snip_setuptools_verify2
+        p = snip_setuptools_verify2.C.getpwuid(0)
+        assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root"
+        ''')
+
+    def test_set_py_limited_api(self):
+        from cffi.setuptools_ext import _set_py_limited_api
+        try:
+            import setuptools
+        except ImportError as e:
+            py.test.skip(str(e))
+        orig_version = setuptools.__version__
+        expecting_limited_api = not hasattr(sys, 'gettotalrefcount')
+        try:
+            setuptools.__version__ = '26.0.0'
+            from setuptools import Extension
+
+            kwds = _set_py_limited_api(Extension, {})
+            assert kwds.get('py_limited_api', False) == expecting_limited_api
+
+            setuptools.__version__ = '25.0'
+            kwds = _set_py_limited_api(Extension, {})
+            assert kwds.get('py_limited_api', False) == False
+
+            setuptools.__version__ = 'development'
+            kwds = _set_py_limited_api(Extension, {})
+            assert kwds.get('py_limited_api', False) == expecting_limited_api
+
+        finally:
+            setuptools.__version__ = orig_version
diff --git a/testing/cffi1/__init__.py b/testing/cffi1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/cffi1/__init__.py
diff --git a/testing/cffi1/test_cffi_binary.py b/testing/cffi1/test_cffi_binary.py
new file mode 100644
index 0000000..25953db
--- /dev/null
+++ b/testing/cffi1/test_cffi_binary.py
@@ -0,0 +1,20 @@
+import py, sys, os
+import _cffi_backend
+
+def test_no_unknown_exported_symbols():
+    if not hasattr(_cffi_backend, '__file__'):
+        py.test.skip("_cffi_backend module is built-in")
+    if not sys.platform.startswith('linux'):
+        py.test.skip("linux-only")
+    g = os.popen("objdump -T '%s'" % _cffi_backend.__file__, 'r')
+    for line in g:
+        if not line.startswith('0'):
+            continue
+        if '*UND*' in line:
+            continue
+        name = line.split()[-1]
+        if name.startswith('_') or name.startswith('.'):
+            continue
+        if name not in ('init_cffi_backend', 'PyInit__cffi_backend'):
+            raise Exception("Unexpected exported name %r" % (name,))
+    g.close()
diff --git a/testing/cffi1/test_commontypes.py b/testing/cffi1/test_commontypes.py
new file mode 100644
index 0000000..ea7ffde
--- /dev/null
+++ b/testing/cffi1/test_commontypes.py
@@ -0,0 +1,34 @@
+import py, os, cffi, re
+import _cffi_backend
+
+
+def getlines():
+    try:
+        f = open(os.path.join(os.path.dirname(cffi.__file__),
+                              '..', 'c', 'commontypes.c'))
+    except IOError:
+        py.test.skip("cannot find ../c/commontypes.c")
+    lines = [line for line in f.readlines() if line.strip().startswith('EQ(')]
+    f.close()
+    return lines
+
+def test_alphabetical_order():
+    lines = getlines()
+    assert lines == sorted(lines)
+
+def test_dependencies():
+    r = re.compile(r'EQ[(]"([^"]+)",(?:\s*"([A-Z0-9_]+)\s*[*]*"[)])?')
+    lines = getlines()
+    d = {}
+    for line in lines:
+        match = r.search(line)
+        if match is not None:
+            d[match.group(1)] = match.group(2)
+    for value in d.values():
+        if value:
+            assert value in d
+
+def test_get_common_types():
+    d = {}
+    _cffi_backend._get_common_types(d)
+    assert d["bool"] == "_Bool"
diff --git a/testing/cffi1/test_dlopen.py b/testing/cffi1/test_dlopen.py
new file mode 100644
index 0000000..1c20550
--- /dev/null
+++ b/testing/cffi1/test_dlopen.py
@@ -0,0 +1,225 @@
+import py
+from cffi import FFI, VerificationError, CDefError
+from cffi.recompiler import make_py_source
+from testing.udir import udir
+
+
+def test_simple():
+    ffi = FFI()
+    ffi.cdef("int close(int); static const int BB = 42; int somevar;")
+    target = udir.join('test_simple.py')
+    make_py_source(ffi, 'test_simple', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_simple',
+    _version = 0x2601,
+    _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F',
+    _globals = (b'\xFF\xFF\xFF\x1FBB',42,b'\x00\x00\x00\x23close',0,b'\x00\x00\x01\x21somevar',0),
+)
+"""
+
+def test_global_constant():
+    ffi = FFI()
+    ffi.cdef("static const long BB; static const float BF = 12;")
+    target = udir.join('test_valid_global_constant.py')
+    make_py_source(ffi, 'test_valid_global_constant', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_valid_global_constant',
+    _version = 0x2601,
+    _types = b'\x00\x00\x0D\x01\x00\x00\x09\x01',
+    _globals = (b'\x00\x00\x01\x25BB',0,b'\x00\x00\x00\x25BF',0),
+)
+"""
+
+def test_invalid_global_constant_3():
+    ffi = FFI()
+    e = py.test.raises(CDefError, ffi.cdef, "#define BB 12.34")
+    assert str(e.value).startswith(
+        "only supports one of the following syntax:")
+
+def test_invalid_dotdotdot_in_macro():
+    ffi = FFI()
+    ffi.cdef("#define FOO ...")
+    target = udir.join('test_invalid_dotdotdot_in_macro.py')
+    e = py.test.raises(VerificationError, make_py_source, ffi,
+                       'test_invalid_dotdotdot_in_macro', str(target))
+    assert str(e.value) == ("macro FOO: cannot use the syntax '...' in "
+                            "'#define FOO ...' when using the ABI mode")
+
+def test_typename():
+    ffi = FFI()
+    ffi.cdef("typedef int foobar_t;")
+    target = udir.join('test_typename.py')
+    make_py_source(ffi, 'test_typename', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_typename',
+    _version = 0x2601,
+    _types = b'\x00\x00\x07\x01',
+    _typenames = (b'\x00\x00\x00\x00foobar_t',),
+)
+"""
+
+def test_enum():
+    ffi = FFI()
+    ffi.cdef("enum myenum_e { AA, BB, CC=-42 };")
+    target = udir.join('test_enum.py')
+    make_py_source(ffi, 'test_enum', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_enum',
+    _version = 0x2601,
+    _types = b'\x00\x00\x00\x0B',
+    _globals = (b'\xFF\xFF\xFF\x0BAA',0,b'\xFF\xFF\xFF\x0BBB',1,b'\xFF\xFF\xFF\x0BCC',-42),
+    _enums = (b'\x00\x00\x00\x00\x00\x00\x00\x15myenum_e\x00AA,BB,CC',),
+)
+"""
+
+def test_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a; signed char b[]; }; struct bar_s;")
+    target = udir.join('test_struct.py')
+    make_py_source(ffi, 'test_struct', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_struct',
+    _version = 0x2601,
+    _types = b'\x00\x00\x07\x01\x00\x00\x03\x01\x00\x00\x01\x07\x00\x00\x00\x09\x00\x00\x01\x09',
+    _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x10bar_s',),(b'\x00\x00\x00\x04\x00\x00\x00\x02foo_s',b'\x00\x00\x00\x11a',b'\x00\x00\x02\x11b')),
+)
+"""
+
+def test_include():
+    ffi = FFI()
+    ffi.cdef("#define ABC 123")
+    ffi.set_source('test_include', None)
+    target = udir.join('test_include.py')
+    make_py_source(ffi, 'test_include', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_include',
+    _version = 0x2601,
+    _types = b'',
+    _globals = (b'\xFF\xFF\xFF\x1FABC',123,),
+)
+"""
+    #
+    ffi2 = FFI()
+    ffi2.include(ffi)
+    target2 = udir.join('test2_include.py')
+    make_py_source(ffi2, 'test2_include', str(target2))
+    assert target2.read() == r"""# auto-generated file
+import _cffi_backend
+from test_include import ffi as _ffi0
+
+ffi = _cffi_backend.FFI('test2_include',
+    _version = 0x2601,
+    _types = b'',
+    _includes = (_ffi0,),
+)
+"""
+
+def test_negative_constant():
+    ffi = FFI()
+    ffi.cdef("static const int BB = -42;")
+    target = udir.join('test_negative_constant.py')
+    make_py_source(ffi, 'test_negative_constant', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_negative_constant',
+    _version = 0x2601,
+    _types = b'',
+    _globals = (b'\xFF\xFF\xFF\x1FBB',-42,),
+)
+"""
+
+def test_struct_included():
+    baseffi = FFI()
+    baseffi.cdef("struct foo_s { int x; };")
+    baseffi.set_source('test_struct_included_base', None)
+    #
+    ffi = FFI()
+    ffi.include(baseffi)
+    target = udir.join('test_struct_included.py')
+    make_py_source(ffi, 'test_struct_included', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+from test_struct_included_base import ffi as _ffi0
+
+ffi = _cffi_backend.FFI('test_struct_included',
+    _version = 0x2601,
+    _types = b'\x00\x00\x00\x09',
+    _struct_unions = ((b'\x00\x00\x00\x00\x00\x00\x00\x08foo_s',),),
+    _includes = (_ffi0,),
+)
+"""
+
+def test_no_cross_include():
+    baseffi = FFI()
+    baseffi.set_source('test_no_cross_include_base', "..source..")
+    #
+    ffi = FFI()
+    ffi.include(baseffi)
+    target = udir.join('test_no_cross_include.py')
+    py.test.raises(VerificationError, make_py_source,
+                   ffi, 'test_no_cross_include', str(target))
+
+def test_array():
+    ffi = FFI()
+    ffi.cdef("typedef int32_t my_array_t[42];")
+    target = udir.join('test_array.py')
+    make_py_source(ffi, 'test_array', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_array',
+    _version = 0x2601,
+    _types = b'\x00\x00\x15\x01\x00\x00\x00\x05\x00\x00\x00\x2A',
+    _typenames = (b'\x00\x00\x00\x01my_array_t',),
+)
+"""
+
+def test_array_overflow():
+    ffi = FFI()
+    ffi.cdef("typedef int32_t my_array_t[3000000000];")
+    target = udir.join('test_array_overflow.py')
+    py.test.raises(OverflowError, make_py_source,
+                   ffi, 'test_array_overflow', str(target))
+
+def test_global_var():
+    ffi = FFI()
+    ffi.cdef("int myglob;")
+    target = udir.join('test_global_var.py')
+    make_py_source(ffi, 'test_global_var', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_global_var',
+    _version = 0x2601,
+    _types = b'\x00\x00\x07\x01',
+    _globals = (b'\x00\x00\x00\x21myglob',0,),
+)
+"""
+
+def test_bitfield():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int y:10; short x:5; };")
+    target = udir.join('test_bitfield.py')
+    make_py_source(ffi, 'test_bitfield', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
+
+ffi = _cffi_backend.FFI('test_bitfield',
+    _version = 0x2601,
+    _types = b'\x00\x00\x07\x01\x00\x00\x05\x01\x00\x00\x00\x09',
+    _struct_unions = ((b'\x00\x00\x00\x02\x00\x00\x00\x02foo_s',b'\x00\x00\x00\x13\x00\x00\x00\x0Ay',b'\x00\x00\x01\x13\x00\x00\x00\x05x'),),
+)
+"""
diff --git a/testing/cffi1/test_dlopen_unicode_literals.py b/testing/cffi1/test_dlopen_unicode_literals.py
new file mode 100644
index 0000000..e792866
--- /dev/null
+++ b/testing/cffi1/test_dlopen_unicode_literals.py
@@ -0,0 +1,9 @@
+import py, os
+
+s = """from __future__ import unicode_literals
+"""
+
+with open(os.path.join(os.path.dirname(__file__), 'test_dlopen.py')) as f:
+    s += f.read()
+
+exec(py.code.compile(s))
diff --git a/testing/cffi1/test_ffi_obj.py b/testing/cffi1/test_ffi_obj.py
new file mode 100644
index 0000000..e07d6f9
--- /dev/null
+++ b/testing/cffi1/test_ffi_obj.py
@@ -0,0 +1,532 @@
+import py, sys
+import _cffi_backend as _cffi1_backend
+
+
+def test_ffi_new():
+    ffi = _cffi1_backend.FFI()
+    p = ffi.new("int *")
+    p[0] = -42
+    assert p[0] == -42
+    assert type(ffi) is ffi.__class__ is _cffi1_backend.FFI
+
+def test_ffi_subclass():
+    class FOO(_cffi1_backend.FFI):
+        def __init__(self, x):
+            self.x = x
+    foo = FOO(42)
+    assert foo.x == 42
+    p = foo.new("int *")
+    assert p[0] == 0
+    assert type(foo) is foo.__class__ is FOO
+
+def test_ffi_no_argument():
+    py.test.raises(TypeError, _cffi1_backend.FFI, 42)
+
+def test_ffi_cache_type():
+    ffi = _cffi1_backend.FFI()
+    t1 = ffi.typeof("int **")
+    t2 = ffi.typeof("int *")
+    assert t2.item is t1.item.item
+    assert t2 is t1.item
+    assert ffi.typeof("int[][10]") is ffi.typeof("int[][10]")
+    assert ffi.typeof("int(*)()") is ffi.typeof("int(*)()")
+
+def test_ffi_type_not_immortal():
+    import weakref, gc
+    ffi = _cffi1_backend.FFI()
+    t1 = ffi.typeof("int **")
+    t2 = ffi.typeof("int *")
+    w1 = weakref.ref(t1)
+    w2 = weakref.ref(t2)
+    del t1, ffi
+    gc.collect()
+    assert w1() is None
+    assert w2() is t2
+    ffi = _cffi1_backend.FFI()
+    assert ffi.typeof(ffi.new("int **")[0]) is t2
+    #
+    ffi = _cffi1_backend.FFI()
+    t1 = ffi.typeof("int ***")
+    t2 = ffi.typeof("int **")
+    w1 = weakref.ref(t1)
+    w2 = weakref.ref(t2)
+    del t2, ffi
+    gc.collect()
+    assert w1() is t1
+    assert w2() is not None   # kept alive by t1
+    ffi = _cffi1_backend.FFI()
+    assert ffi.typeof("int * *") is t1.item
+
+def test_ffi_cache_type_globally():
+    ffi1 = _cffi1_backend.FFI()
+    ffi2 = _cffi1_backend.FFI()
+    t1 = ffi1.typeof("int *")
+    t2 = ffi2.typeof("int *")
+    assert t1 is t2
+
+def test_ffi_invalid():
+    ffi = _cffi1_backend.FFI()
+    # array of 10 times an "int[]" is invalid
+    py.test.raises(ValueError, ffi.typeof, "int[10][]")
+
+def test_ffi_docstrings():
+    # check that all methods of the FFI class have a docstring.
+    check_type = type(_cffi1_backend.FFI.new)
+    for methname in dir(_cffi1_backend.FFI):
+        if not methname.startswith('_'):
+            method = getattr(_cffi1_backend.FFI, methname)
+            if isinstance(method, check_type):
+                assert method.__doc__, "method FFI.%s() has no docstring" % (
+                    methname,)
+
+def test_ffi_NULL():
+    NULL = _cffi1_backend.FFI.NULL
+    assert _cffi1_backend.FFI().typeof(NULL).cname == "void *"
+
+def test_ffi_no_attr():
+    ffi = _cffi1_backend.FFI()
+    py.test.raises(AttributeError, "ffi.no_such_name")
+    py.test.raises(AttributeError, "ffi.no_such_name = 42")
+    py.test.raises(AttributeError, "del ffi.no_such_name")
+
+def test_ffi_string():
+    ffi = _cffi1_backend.FFI()
+    p = ffi.new("char[]", init=b"foobar\x00baz")
+    assert ffi.string(p) == b"foobar"
+    assert ffi.string(cdata=p, maxlen=3) == b"foo"
+
+def test_ffi_errno():
+    # xxx not really checking errno, just checking that we can read/write it
+    ffi = _cffi1_backend.FFI()
+    ffi.errno = 42
+    assert ffi.errno == 42
+
+def test_ffi_alignof():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.alignof("int") == 4
+    assert ffi.alignof("int[]") == 4
+    assert ffi.alignof("int[41]") == 4
+    assert ffi.alignof("short[41]") == 2
+    assert ffi.alignof(ffi.new("int[41]")) == 4
+    assert ffi.alignof(ffi.new("int[]", 41)) == 4
+
+def test_ffi_sizeof():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.sizeof("int") == 4
+    py.test.raises(ffi.error, ffi.sizeof, "int[]")
+    assert ffi.sizeof("int[41]") == 41 * 4
+    assert ffi.sizeof(ffi.new("int[41]")) == 41 * 4
+    assert ffi.sizeof(ffi.new("int[]", 41)) == 41 * 4
+
+def test_ffi_callback():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.callback("int(int)", lambda x: x + 42)(10) == 52
+    assert ffi.callback("int(*)(int)", lambda x: x + 42)(10) == 52
+    assert ffi.callback("int(int)", lambda x: x + "", -66)(10) == -66
+    assert ffi.callback("int(int)", lambda x: x + "", error=-66)(10) == -66
+
+def test_ffi_callback_decorator():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.callback(ffi.typeof("int(*)(int)"))(lambda x: x + 42)(10) == 52
+    deco = ffi.callback("int(int)", error=-66)
+    assert deco(lambda x: x + "")(10) == -66
+    assert deco(lambda x: x + 42)(10) == 52
+
+def test_ffi_callback_onerror():
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    def oops(*args):
+        seen.append(args)
+
+    @ffi.callback("int(int)", onerror=oops)
+    def fn1(x):
+        return x + ""
+    assert fn1(10) == 0
+
+    @ffi.callback("int(int)", onerror=oops, error=-66)
+    def fn2(x):
+        return x + ""
+    assert fn2(10) == -66
+
+    assert len(seen) == 2
+    exc, val, tb = seen[0]
+    assert exc is TypeError
+    assert isinstance(val, TypeError)
+    assert tb.tb_frame.f_code.co_name == "fn1"
+    exc, val, tb = seen[1]
+    assert exc is TypeError
+    assert isinstance(val, TypeError)
+    assert tb.tb_frame.f_code.co_name == "fn2"
+    #
+    py.test.raises(TypeError, ffi.callback, "int(int)",
+                   lambda x: x, onerror=42)   # <- not callable
+
+def test_ffi_getctype():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.getctype("int") == "int"
+    assert ffi.getctype("int", 'x') == "int x"
+    assert ffi.getctype("int*") == "int *"
+    assert ffi.getctype("int*", '') == "int *"
+    assert ffi.getctype("int*", 'x') == "int * x"
+    assert ffi.getctype("int", '*') == "int *"
+    assert ffi.getctype("int", replace_with=' * x ') == "int * x"
+    assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *"
+    assert ffi.getctype("int", '[5]') == "int[5]"
+    assert ffi.getctype("int[5]", '[6]') == "int[6][5]"
+    assert ffi.getctype("int[5]", '(*)') == "int(*)[5]"
+    # special-case for convenience: automatically put '()' around '*'
+    assert ffi.getctype("int[5]", '*') == "int(*)[5]"
+    assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]"
+    assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]"
+
+def test_addressof():
+    ffi = _cffi1_backend.FFI()
+    a = ffi.new("int[10]")
+    b = ffi.addressof(a, 5)
+    b[2] = -123
+    assert a[7] == -123
+
+def test_handle():
+    ffi = _cffi1_backend.FFI()
+    x = [2, 4, 6]
+    xp = ffi.new_handle(x)
+    assert ffi.typeof(xp) == ffi.typeof("void *")
+    assert ffi.from_handle(xp) is x
+    yp = ffi.new_handle([6, 4, 2])
+    assert ffi.from_handle(yp) == [6, 4, 2]
+
+def test_handle_unique():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.new_handle(None) is not ffi.new_handle(None)
+    assert ffi.new_handle(None) != ffi.new_handle(None)
+
+def test_ffi_cast():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.cast("int(*)(int)", 0) == ffi.NULL
+    ffi.callback("int(int)")      # side-effect of registering this string
+    py.test.raises(ffi.error, ffi.cast, "int(int)", 0)
+
+def test_ffi_invalid_type():
+    ffi = _cffi1_backend.FFI()
+    e = py.test.raises(ffi.error, ffi.cast, "", 0)
+    assert str(e.value) == ("identifier expected\n"
+                            "\n"
+                            "^")
+    e = py.test.raises(ffi.error, ffi.cast, "struct struct", 0)
+    assert str(e.value) == ("struct or union name expected\n"
+                            "struct struct\n"
+                            "       ^")
+    e = py.test.raises(ffi.error, ffi.cast, "struct never_heard_of_s", 0)
+    assert str(e.value) == ("undefined struct/union name\n"
+                            "struct never_heard_of_s\n"
+                            "       ^")
+    e = py.test.raises(ffi.error, ffi.cast, "\t\n\x01\x1f~\x7f\x80\xff", 0)
+    marks = "?" if sys.version_info < (3,) else "??"
+    assert str(e.value) == ("identifier expected\n"
+                            "  ??~?%s%s\n"
+                            "  ^" % (marks, marks))
+    e = py.test.raises(ffi.error, ffi.cast, "X" * 600, 0)
+    assert str(e.value) == ("undefined type name")
+
+def test_ffi_buffer():
+    ffi = _cffi1_backend.FFI()
+    a = ffi.new("signed char[]", [5, 6, 7])
+    assert ffi.buffer(a)[:] == b'\x05\x06\x07'
+    assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06'
+    assert type(ffi.buffer(a)) is ffi.buffer
+
+def test_ffi_from_buffer():
+    import array
+    ffi = _cffi1_backend.FFI()
+    a = array.array('H', [10000, 20000, 30000, 40000])
+    c = ffi.from_buffer(a)
+    assert ffi.typeof(c) is ffi.typeof("char[]")
+    assert len(c) == 8
+    ffi.cast("unsigned short *", c)[1] += 500
+    assert list(a) == [10000, 20500, 30000, 40000]
+    py.test.raises(TypeError, ffi.from_buffer, a, True)
+    assert c == ffi.from_buffer("char[]", a, True)
+    assert c == ffi.from_buffer(a, require_writable=True)
+    #
+    c = ffi.from_buffer("unsigned short[]", a)
+    assert len(c) == 4
+    assert c[1] == 20500
+    #
+    c = ffi.from_buffer("unsigned short[2][2]", a)
+    assert len(c) == 2
+    assert len(c[0]) == 2
+    assert c[0][1] == 20500
+    #
+    p = ffi.from_buffer(b"abcd")
+    assert p[2] == b"c"
+    #
+    assert p == ffi.from_buffer(b"abcd", require_writable=False)
+    py.test.raises((TypeError, BufferError), ffi.from_buffer,
+                                             "char[]", b"abcd", True)
+    py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd",
+                                             require_writable=True)
+
+def test_memmove():
+    ffi = _cffi1_backend.FFI()
+    p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678])
+    ffi.memmove(p, p + 1, 4)
+    assert list(p) == [-2345, -3456, -3456, -4567, -5678]
+    p[2] = 999
+    ffi.memmove(p + 2, p, 6)
+    assert list(p) == [-2345, -3456, -2345, -3456, 999]
+    ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2)
+    if sys.byteorder == 'little':
+        assert list(p) == [-2345, -3456, -2345, -3456, 0x7271]
+    else:
+        assert list(p) == [-2345, -3456, -2345, -3456, 0x7172]
+
+def test_memmove_buffer():
+    import array
+    ffi = _cffi1_backend.FFI()
+    a = array.array('H', [10000, 20000, 30000])
+    p = ffi.new("short[]", 5)
+    ffi.memmove(p, a, 6)
+    assert list(p) == [10000, 20000, 30000, 0, 0]
+    ffi.memmove(p + 1, a, 6)
+    assert list(p) == [10000, 10000, 20000, 30000, 0]
+    b = array.array('h', [-1000, -2000, -3000])
+    ffi.memmove(b, a, 4)
+    assert b.tolist() == [10000, 20000, -3000]
+    assert a.tolist() == [10000, 20000, 30000]
+    p[0] = 999
+    p[1] = 998
+    p[2] = 997
+    p[3] = 996
+    p[4] = 995
+    ffi.memmove(b, p, 2)
+    assert b.tolist() == [999, 20000, -3000]
+    ffi.memmove(b, p + 2, 4)
+    assert b.tolist() == [997, 996, -3000]
+    p[2] = -p[2]
+    p[3] = -p[3]
+    ffi.memmove(b, p + 2, 6)
+    assert b.tolist() == [-997, -996, 995]
+
+def test_memmove_readonly_readwrite():
+    ffi = _cffi1_backend.FFI()
+    p = ffi.new("signed char[]", 5)
+    ffi.memmove(p, b"abcde", 3)
+    assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0]
+    ffi.memmove(p, bytearray(b"ABCDE"), 2)
+    assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0]
+    py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3)
+    ba = bytearray(b"xxxxx")
+    ffi.memmove(dest=ba, src=p, n=3)
+    assert ba == bytearray(b"ABcxx")
+
+def test_ffi_types():
+    CData = _cffi1_backend.FFI.CData
+    CType = _cffi1_backend.FFI.CType
+    ffi = _cffi1_backend.FFI()
+    assert isinstance(ffi.cast("int", 42), CData)
+    assert isinstance(ffi.typeof("int"), CType)
+
+def test_ffi_getwinerror():
+    if sys.platform != "win32":
+        py.test.skip("for windows")
+    ffi = _cffi1_backend.FFI()
+    n = (1 << 29) + 42
+    code, message = ffi.getwinerror(code=n)
+    assert code == n
+
+def test_ffi_new_allocator_1():
+    ffi = _cffi1_backend.FFI()
+    alloc1 = ffi.new_allocator()
+    alloc2 = ffi.new_allocator(should_clear_after_alloc=False)
+    for retry in range(100):
+        p1 = alloc1("int[10]")
+        p2 = alloc2("int[10]")
+        combination = 0
+        for i in range(10):
+            assert p1[i] == 0
+            combination |= p2[i]
+            p1[i] = -42
+            p2[i] = -43
+        if combination != 0:
+            break
+        del p1, p2
+        import gc; gc.collect()
+    else:
+        raise AssertionError("cannot seem to get an int[10] not "
+                             "completely cleared")
+
+def test_ffi_new_allocator_2():
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    def myalloc(size):
+        seen.append(size)
+        return ffi.new("char[]", b"X" * size)
+    def myfree(raw):
+        seen.append(raw)
+    alloc1 = ffi.new_allocator(myalloc, myfree)
+    alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree,
+                               should_clear_after_alloc=False)
+    p1 = alloc1("int[10]")
+    p2 = alloc2("int[]", 10)
+    assert seen == [40, 40]
+    assert ffi.typeof(p1) == ffi.typeof("int[10]")
+    assert ffi.sizeof(p1) == 40
+    assert ffi.typeof(p2) == ffi.typeof("int[]")
+    assert ffi.sizeof(p2) == 40
+    assert p1[5] == 0
+    assert p2[6] == ord('X') * 0x01010101
+    raw1 = ffi.cast("char *", p1)
+    raw2 = ffi.cast("char *", p2)
+    del p1, p2
+    retries = 0
+    while len(seen) != 4:
+        retries += 1
+        assert retries <= 5
+        import gc; gc.collect()
+    assert (seen == [40, 40, raw1, raw2] or
+            seen == [40, 40, raw2, raw1])
+    assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>"
+    assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>"
+
+def test_ffi_new_allocator_3():
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    def myalloc(size):
+        seen.append(size)
+        return ffi.new("char[]", b"X" * size)
+    alloc1 = ffi.new_allocator(myalloc)    # no 'free'
+    p1 = alloc1("int[10]")
+    assert seen == [40]
+    assert ffi.typeof(p1) == ffi.typeof("int[10]")
+    assert ffi.sizeof(p1) == 40
+    assert p1[5] == 0
+
+def test_ffi_new_allocator_4():
+    ffi = _cffi1_backend.FFI()
+    py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None)
+    #
+    def myalloc2(size):
+        raise LookupError
+    alloc2 = ffi.new_allocator(myalloc2)
+    py.test.raises(LookupError, alloc2, "int[5]")
+    #
+    def myalloc3(size):
+        return 42
+    alloc3 = ffi.new_allocator(myalloc3)
+    e = py.test.raises(TypeError, alloc3, "int[5]")
+    assert str(e.value) == "alloc() must return a cdata object (got int)"
+    #
+    def myalloc4(size):
+        return ffi.cast("int", 42)
+    alloc4 = ffi.new_allocator(myalloc4)
+    e = py.test.raises(TypeError, alloc4, "int[5]")
+    assert str(e.value) == "alloc() must return a cdata pointer, not 'int'"
+    #
+    def myalloc5(size):
+        return ffi.NULL
+    alloc5 = ffi.new_allocator(myalloc5)
+    py.test.raises(MemoryError, alloc5, "int[5]")
+
+def test_bool_issue228():
+    ffi = _cffi1_backend.FFI()
+    fntype = ffi.typeof("int(*callback)(bool is_valid)")
+    assert repr(fntype.args[0]) == "<ctype '_Bool'>"
+
+def test_FILE_issue228():
+    fntype1 = _cffi1_backend.FFI().typeof("FILE *")
+    fntype2 = _cffi1_backend.FFI().typeof("FILE *")
+    assert repr(fntype1) == "<ctype 'FILE *'>"
+    assert fntype1 is fntype2
+
+def test_cast_from_int_type_to_bool():
+    ffi = _cffi1_backend.FFI()
+    for basetype in ['char', 'short', 'int', 'long', 'long long']:
+        for sign in ['signed', 'unsigned']:
+            type = '%s %s' % (sign, basetype)
+            assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1
+            assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1
+            assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0
+
+def test_init_once():
+    def do_init():
+        seen.append(1)
+        return 42
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    for i in range(3):
+        res = ffi.init_once(do_init, "tag1")
+        assert res == 42
+        assert seen == [1]
+    for i in range(3):
+        res = ffi.init_once(do_init, "tag2")
+        assert res == 42
+        assert seen == [1, 1]
+
+def test_init_once_multithread():
+    if sys.version_info < (3,):
+        import thread
+    else:
+        import _thread as thread
+    import time
+    #
+    def do_init():
+        print('init!')
+        seen.append('init!')
+        time.sleep(1)
+        seen.append('init done')
+        print('init done')
+        return 7
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    for i in range(6):
+        def f():
+            res = ffi.init_once(do_init, "tag")
+            seen.append(res)
+        thread.start_new_thread(f, ())
+    time.sleep(1.5)
+    assert seen == ['init!', 'init done'] + 6 * [7]
+
+def test_init_once_failure():
+    def do_init():
+        seen.append(1)
+        raise ValueError
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    for i in range(5):
+        py.test.raises(ValueError, ffi.init_once, do_init, "tag")
+        assert seen == [1] * (i + 1)
+
+def test_init_once_multithread_failure():
+    if sys.version_info < (3,):
+        import thread
+    else:
+        import _thread as thread
+    import time
+    def do_init():
+        seen.append('init!')
+        time.sleep(1)
+        seen.append('oops')
+        raise ValueError
+    ffi = _cffi1_backend.FFI()
+    seen = []
+    for i in range(3):
+        def f():
+            py.test.raises(ValueError, ffi.init_once, do_init, "tag")
+        thread.start_new_thread(f, ())
+    i = 0
+    while len(seen) < 6:
+        i += 1
+        assert i < 20
+        time.sleep(0.51)
+    assert seen == ['init!', 'oops'] * 3
+
+def test_unpack():
+    ffi = _cffi1_backend.FFI()
+    p = ffi.new("char[]", b"abc\x00def")
+    assert ffi.unpack(p+1, 7) == b"bc\x00def\x00"
+    p = ffi.new("int[]", [-123456789])
+    assert ffi.unpack(p, 1) == [-123456789]
+
+def test_negative_array_size():
+    ffi = _cffi1_backend.FFI()
+    py.test.raises(ffi.error, ffi.cast, "int[-5]", 0)
diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py
new file mode 100644
index 0000000..209cb30
--- /dev/null
+++ b/testing/cffi1/test_new_ffi_1.py
@@ -0,0 +1,1793 @@
+import py
+import platform, imp
+import sys, os, ctypes
+import cffi
+from testing.udir import udir
+from testing.support import *
+from cffi.recompiler import recompile
+from cffi.cffi_opcode import PRIMITIVE_TO_INDEX
+
+SIZE_OF_INT   = ctypes.sizeof(ctypes.c_int)
+SIZE_OF_LONG  = ctypes.sizeof(ctypes.c_long)
+SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short)
+SIZE_OF_PTR   = ctypes.sizeof(ctypes.c_void_p)
+SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar)
+
+
+def setup_module():
+    global ffi, construction_params
+    ffi1 = cffi.FFI()
+    DEFS = r"""
+        struct repr { short a, b, c; };
+        struct simple { int a; short b, c; };
+        struct array { int a[2]; char b[3]; };
+        struct recursive { int value; struct recursive *next; };
+        union simple_u { int a; short b, c; };
+        union init_u { char a; int b; };
+        struct four_s { int a; short b, c, d; };
+        union four_u { int a; short b, c, d; };
+        struct string { const char *name; };
+        struct ustring { const wchar_t *name; };
+        struct voidp { void *p; int *q; short *r; };
+        struct ab { int a, b; };
+        struct abc { int a, b, c; };
+
+        /* don't use A0, B0, CC0, D0 because termios.h might be included
+           and it has its own #defines for these names */
+        enum foq { cffiA0, cffiB0, cffiCC0, cffiD0 };
+        enum bar { A1, B1=-2, CC1, D1, E1 };
+        enum baz { A2=0x1000, B2=0x2000 };
+        enum foo2 { A3, B3, C3, D3 };
+        struct bar_with_e { enum foo2 e; };
+        enum noncont { A4, B4=42, C4 };
+        enum etypes {A5='!', B5='\'', C5=0x10, D5=010, E5=- 0x10, F5=-010};
+        typedef enum { Value0 = 0 } e_t, *pe_t;
+        enum e_noninj { AA3=0, BB3=0, CC3=0, DD3=0 };
+        enum e_prev { AA4, BB4=2, CC4=4, DD4=BB4, EE4, FF4=CC4, GG4=FF4 };
+
+        struct nesting { struct abc d, e; };
+        struct array2 { int a, b; int c[99]; };
+        struct align { char a; short b; char c; };
+        struct bitfield { int a:10, b:20, c:3; };
+        typedef enum { AA2, BB2, CC2 } foo_e_t;
+        typedef struct { foo_e_t f:2; } bfenum_t;
+        typedef struct { int a; } anon_foo_t;
+        typedef struct { char b, c; } anon_bar_t;
+        typedef struct named_foo_s { int a; } named_foo_t, *named_foo_p;
+        typedef struct { int a; } unnamed_foo_t, *unnamed_foo_p;
+        struct nonpacked { char a; int b; };
+        struct array0 { int len; short data[0]; };
+        struct array_no_length { int x; int a[]; };
+
+        struct nested_anon {
+            struct { int a, b; };
+            union { int c, d; };
+        };
+        struct nested_field_ofs_s {
+            struct { int a; char b; };
+            union { char c; };
+        };
+        union nested_anon_u {
+            struct { int a, b; };
+            union { int c, d; };
+        };
+        struct abc50 { int a, b; int c[50]; };
+        struct ints_and_bitfield { int a,b,c,d,e; int x:1; };
+    """
+    DEFS_PACKED = """
+        struct is_packed { char a; int b; } /*here*/;
+    """
+    if sys.platform == "win32":
+        DEFS = DEFS.replace('data[0]', 'data[1]')   # not supported
+        CCODE = (DEFS + "\n#pragma pack(push,1)\n" + DEFS_PACKED +
+                 "\n#pragma pack(pop)\n")
+    else:
+        CCODE = (DEFS +
+                 DEFS_PACKED.replace('/*here*/', '__attribute__((packed))'))
+
+    ffi1.cdef(DEFS)
+    ffi1.cdef(DEFS_PACKED, packed=True)
+    ffi1.set_source("test_new_ffi_1", CCODE)
+
+    outputfilename = recompile(ffi1, "test_new_ffi_1", CCODE,
+                               tmpdir=str(udir))
+    module = imp.load_dynamic("test_new_ffi_1", outputfilename)
+    ffi = module.ffi
+    construction_params = (ffi1, CCODE)
+
+
+class TestNewFFI1:
+
+    def test_integer_ranges(self):
+        for (c_type, size) in [('char', 1),
+                               ('short', 2),
+                               ('short int', 2),
+                               ('', 4),
+                               ('int', 4),
+                               ('long', SIZE_OF_LONG),
+                               ('long int', SIZE_OF_LONG),
+                               ('long long', 8),
+                               ('long long int', 8),
+                               ]:
+            for unsigned in [None, False, True]:
+                c_decl = {None: '',
+                          False: 'signed ',
+                          True: 'unsigned '}[unsigned] + c_type
+                if c_decl == 'char' or c_decl == '':
+                    continue
+                self._test_int_type(ffi, c_decl, size, unsigned)
+
+    def test_fixedsize_int(self):
+        for size in [1, 2, 4, 8]:
+            self._test_int_type(ffi, 'int%d_t' % (8*size), size, False)
+            self._test_int_type(ffi, 'uint%d_t' % (8*size), size, True)
+        self._test_int_type(ffi, 'intptr_t', SIZE_OF_PTR, False)
+        self._test_int_type(ffi, 'uintptr_t', SIZE_OF_PTR, True)
+        self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False)
+        self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True)
+        self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False)
+
+    def _test_int_type(self, ffi, c_decl, size, unsigned):
+        if unsigned:
+            min = 0
+            max = (1 << (8*size)) - 1
+        else:
+            min = -(1 << (8*size-1))
+            max = (1 << (8*size-1)) - 1
+        min = int(min)
+        max = int(max)
+        p = ffi.cast(c_decl, min)
+        assert p == min
+        assert bool(p) is bool(min)
+        assert int(p) == min
+        p = ffi.cast(c_decl, max)
+        assert int(p) == max
+        p = ffi.cast(c_decl, long(max))
+        assert int(p) == max
+        q = ffi.cast(c_decl, min - 1)
+        assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max
+        q = ffi.cast(c_decl, long(min - 1))
+        assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max
+        assert q == p
+        assert int(q) == int(p)
+        assert hash(q) == hash(p)
+        c_decl_ptr = '%s *' % c_decl
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, min - 1)
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, max + 1)
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1))
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(max + 1))
+        assert ffi.new(c_decl_ptr, min)[0] == min
+        assert ffi.new(c_decl_ptr, max)[0] == max
+        assert ffi.new(c_decl_ptr, long(min))[0] == min
+        assert ffi.new(c_decl_ptr, long(max))[0] == max
+
+    def test_new_unsupported_type(self):
+        e = py.test.raises(TypeError, ffi.new, "int")
+        assert str(e.value) == "expected a pointer or array ctype, got 'int'"
+
+    def test_new_single_integer(self):
+        p = ffi.new("int *")     # similar to ffi.new("int[1]")
+        assert p[0] == 0
+        p[0] = -123
+        assert p[0] == -123
+        p = ffi.new("int *", -42)
+        assert p[0] == -42
+        assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT
+
+    def test_new_array_no_arg(self):
+        p = ffi.new("int[10]")
+        # the object was zero-initialized:
+        for i in range(10):
+            assert p[i] == 0
+
+    def test_array_indexing(self):
+        p = ffi.new("int[10]")
+        p[0] = 42
+        p[9] = 43
+        assert p[0] == 42
+        assert p[9] == 43
+        py.test.raises(IndexError, "p[10]")
+        py.test.raises(IndexError, "p[10] = 44")
+        py.test.raises(IndexError, "p[-1]")
+        py.test.raises(IndexError, "p[-1] = 44")
+
+    def test_new_array_args(self):
+        # this tries to be closer to C: where we say "int x[5] = {10, 20, ..}"
+        # then here we must enclose the items in a list
+        p = ffi.new("int[5]", [10, 20, 30, 40, 50])
+        assert p[0] == 10
+        assert p[1] == 20
+        assert p[2] == 30
+        assert p[3] == 40
+        assert p[4] == 50
+        p = ffi.new("int[4]", [25])
+        assert p[0] == 25
+        assert p[1] == 0     # follow C convention rather than LuaJIT's
+        assert p[2] == 0
+        assert p[3] == 0
+        p = ffi.new("int[4]", [ffi.cast("int", -5)])
+        assert p[0] == -5
+        assert repr(p) == "<cdata 'int[4]' owning %d bytes>" % (4*SIZE_OF_INT)
+
+    def test_new_array_varsize(self):
+        p = ffi.new("int[]", 10)     # a single integer is the length
+        assert p[9] == 0
+        py.test.raises(IndexError, "p[10]")
+        #
+        py.test.raises(TypeError, ffi.new, "int[]")
+        #
+        p = ffi.new("int[]", [-6, -7])    # a list is all the items, like C
+        assert p[0] == -6
+        assert p[1] == -7
+        py.test.raises(IndexError, "p[2]")
+        assert repr(p) == "<cdata 'int[]' owning %d bytes>" % (2*SIZE_OF_INT)
+        #
+        p = ffi.new("int[]", 0)
+        py.test.raises(IndexError, "p[0]")
+        py.test.raises(ValueError, ffi.new, "int[]", -1)
+        assert repr(p) == "<cdata 'int[]' owning 0 bytes>"
+
+    def test_pointer_init(self):
+        n = ffi.new("int *", 24)
+        a = ffi.new("int *[10]", [ffi.NULL, ffi.NULL, n, n, ffi.NULL])
+        for i in range(10):
+            if i not in (2, 3):
+                assert a[i] == ffi.NULL
+        assert a[2] == a[3] == n
+
+    def test_cannot_cast(self):
+        a = ffi.new("short int[10]")
+        e = py.test.raises(TypeError, ffi.new, "long int **", a)
+        msg = str(e.value)
+        assert "'short[10]'" in msg and "'long *'" in msg
+
+    def test_new_pointer_to_array(self):
+        a = ffi.new("int[4]", [100, 102, 104, 106])
+        p = ffi.new("int **", a)
+        assert p[0] == ffi.cast("int *", a)
+        assert p[0][2] == 104
+        p = ffi.cast("int *", a)
+        assert p[0] == 100
+        assert p[1] == 102
+        assert p[2] == 104
+        assert p[3] == 106
+        # keepalive: a
+
+    def test_pointer_direct(self):
+        p = ffi.cast("int*", 0)
+        assert p is not None
+        assert bool(p) is False
+        assert p == ffi.cast("int*", 0)
+        assert p != None
+        assert repr(p) == "<cdata 'int *' NULL>"
+        a = ffi.new("int[]", [123, 456])
+        p = ffi.cast("int*", a)
+        assert bool(p) is True
+        assert p == ffi.cast("int*", a)
+        assert p != ffi.cast("int*", 0)
+        assert p[0] == 123
+        assert p[1] == 456
+
+    def test_repr(self):
+        typerepr = "<ctype '%s'>"
+        p = ffi.cast("short unsigned int", 0)
+        assert repr(p) == "<cdata 'unsigned short' 0>"
+        assert repr(ffi.typeof(p)) == typerepr % "unsigned short"
+        p = ffi.cast("unsigned short int", 0)
+        assert repr(p) == "<cdata 'unsigned short' 0>"
+        assert repr(ffi.typeof(p)) == typerepr % "unsigned short"
+        p = ffi.cast("int*", 0)
+        assert repr(p) == "<cdata 'int *' NULL>"
+        assert repr(ffi.typeof(p)) == typerepr % "int *"
+        #
+        p = ffi.new("int*")
+        assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT
+        assert repr(ffi.typeof(p)) == typerepr % "int *"
+        p = ffi.new("int**")
+        assert repr(p) == "<cdata 'int * *' owning %d bytes>" % SIZE_OF_PTR
+        assert repr(ffi.typeof(p)) == typerepr % "int * *"
+        p = ffi.new("int [2]")
+        assert repr(p) == "<cdata 'int[2]' owning %d bytes>" % (2*SIZE_OF_INT)
+        assert repr(ffi.typeof(p)) == typerepr % "int[2]"
+        p = ffi.new("int*[2][3]")
+        assert repr(p) == "<cdata 'int *[2][3]' owning %d bytes>" % (
+            6*SIZE_OF_PTR)
+        assert repr(ffi.typeof(p)) == typerepr % "int *[2][3]"
+        p = ffi.new("struct repr *")
+        assert repr(p) == "<cdata 'struct repr *' owning %d bytes>" % (
+            3*SIZE_OF_SHORT)
+        assert repr(ffi.typeof(p)) == typerepr % "struct repr *"
+        #
+        q = ffi.cast("short", -123)
+        assert repr(q) == "<cdata 'short' -123>"
+        assert repr(ffi.typeof(q)) == typerepr % "short"
+        p = ffi.new("int*")
+        q = ffi.cast("short*", p)
+        assert repr(q).startswith("<cdata 'short *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "short *"
+        p = ffi.new("int [2]")
+        q = ffi.cast("int*", p)
+        assert repr(q).startswith("<cdata 'int *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "int *"
+        p = ffi.new("struct repr*")
+        q = ffi.cast("struct repr *", p)
+        assert repr(q).startswith("<cdata 'struct repr *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "struct repr *"
+        prevrepr = repr(q)
+        q = q[0]
+        assert repr(q) == prevrepr.replace(' *', ' &')
+        assert repr(ffi.typeof(q)) == typerepr % "struct repr"
+
+    def test_new_array_of_array(self):
+        p = ffi.new("int[3][4]")
+        p[0][0] = 10
+        p[2][3] = 33
+        assert p[0][0] == 10
+        assert p[2][3] == 33
+        py.test.raises(IndexError, "p[1][-1]")
+
+    def test_constructor_array_of_array(self):
+        p = ffi.new("int[3][2]", [[10, 11], [12, 13], [14, 15]])
+        assert p[2][1] == 15
+
+    def test_new_array_of_pointer_1(self):
+        n = ffi.new("int*", 99)
+        p = ffi.new("int*[4]")
+        p[3] = n
+        a = p[3]
+        assert repr(a).startswith("<cdata 'int *' 0x")
+        assert a[0] == 99
+
+    def test_new_array_of_pointer_2(self):
+        n = ffi.new("int[1]", [99])
+        p = ffi.new("int*[4]")
+        p[3] = n
+        a = p[3]
+        assert repr(a).startswith("<cdata 'int *' 0x")
+        assert a[0] == 99
+
+    def test_char(self):
+        assert ffi.new("char*", b"\xff")[0] == b'\xff'
+        assert ffi.new("char*")[0] == b'\x00'
+        assert int(ffi.cast("char", 300)) == 300 - 256
+        assert not bool(ffi.cast("char", 0))
+        assert bool(ffi.cast("char", 1))
+        assert bool(ffi.cast("char", 255))
+        py.test.raises(TypeError, ffi.new, "char*", 32)
+        py.test.raises(TypeError, ffi.new, "char*", u+"x")
+        py.test.raises(TypeError, ffi.new, "char*", b"foo")
+        #
+        p = ffi.new("char[]", [b'a', b'b', b'\x9c'])
+        assert len(p) == 3
+        assert p[0] == b'a'
+        assert p[1] == b'b'
+        assert p[2] == b'\x9c'
+        p[0] = b'\xff'
+        assert p[0] == b'\xff'
+        p = ffi.new("char[]", b"abcd")
+        assert len(p) == 5
+        assert p[4] == b'\x00'    # like in C, with:  char[] p = "abcd";
+        #
+        p = ffi.new("char[4]", b"ab")
+        assert len(p) == 4
+        assert [p[i] for i in range(4)] == [b'a', b'b', b'\x00', b'\x00']
+        p = ffi.new("char[2]", b"ab")
+        assert len(p) == 2
+        assert [p[i] for i in range(2)] == [b'a', b'b']
+        py.test.raises(IndexError, ffi.new, "char[2]", b"abc")
+
+    def check_wchar_t(self, ffi):
+        try:
+            ffi.cast("wchar_t", 0)
+        except NotImplementedError:
+            py.test.skip("NotImplementedError: wchar_t")
+
+    def test_wchar_t(self):
+        self.check_wchar_t(ffi)
+        assert ffi.new("wchar_t*", u+'x')[0] == u+'x'
+        assert ffi.new("wchar_t*", u+'\u1234')[0] == u+'\u1234'
+        if SIZE_OF_WCHAR > 2:
+            assert ffi.new("wchar_t*", u+'\U00012345')[0] == u+'\U00012345'
+        else:
+            py.test.raises(TypeError, ffi.new, "wchar_t*", u+'\U00012345')
+        assert ffi.new("wchar_t*")[0] == u+'\x00'
+        assert int(ffi.cast("wchar_t", 300)) == 300
+        assert not bool(ffi.cast("wchar_t", 0))
+        assert bool(ffi.cast("wchar_t", 1))
+        assert bool(ffi.cast("wchar_t", 65535))
+        if SIZE_OF_WCHAR > 2:
+            assert bool(ffi.cast("wchar_t", 65536))
+        py.test.raises(TypeError, ffi.new, "wchar_t*", 32)
+        py.test.raises(TypeError, ffi.new, "wchar_t*", "foo")
+        #
+        p = ffi.new("wchar_t[]", [u+'a', u+'b', u+'\u1234'])
+        assert len(p) == 3
+        assert p[0] == u+'a'
+        assert p[1] == u+'b' and type(p[1]) is unicode
+        assert p[2] == u+'\u1234'
+        p[0] = u+'x'
+        assert p[0] == u+'x' and type(p[0]) is unicode
+        p[1] = u+'\u1357'
+        assert p[1] == u+'\u1357'
+        p = ffi.new("wchar_t[]", u+"abcd")
+        assert len(p) == 5
+        assert p[4] == u+'\x00'
+        p = ffi.new("wchar_t[]", u+"a\u1234b")
+        assert len(p) == 4
+        assert p[1] == u+'\u1234'
+        #
+        p = ffi.new("wchar_t[]", u+'\U00023456')
+        if SIZE_OF_WCHAR == 2:
+            assert len(p) == 3
+            assert p[0] == u+'\ud84d'
+            assert p[1] == u+'\udc56'
+            assert p[2] == u+'\x00'
+        else:
+            assert len(p) == 2
+            assert p[0] == u+'\U00023456'
+            assert p[1] == u+'\x00'
+        #
+        p = ffi.new("wchar_t[4]", u+"ab")
+        assert len(p) == 4
+        assert [p[i] for i in range(4)] == [u+'a', u+'b', u+'\x00', u+'\x00']
+        p = ffi.new("wchar_t[2]", u+"ab")
+        assert len(p) == 2
+        assert [p[i] for i in range(2)] == [u+'a', u+'b']
+        py.test.raises(IndexError, ffi.new, "wchar_t[2]", u+"abc")
+
+    def test_none_as_null_doesnt_work(self):
+        p = ffi.new("int*[1]")
+        assert p[0] is not None
+        assert p[0] != None
+        assert p[0] == ffi.NULL
+        assert repr(p[0]) == "<cdata 'int *' NULL>"
+        #
+        n = ffi.new("int*", 99)
+        p = ffi.new("int*[]", [n])
+        assert p[0][0] == 99
+        py.test.raises(TypeError, "p[0] = None")
+        p[0] = ffi.NULL
+        assert p[0] == ffi.NULL
+
+    def test_float(self):
+        p = ffi.new("float[]", [-2, -2.5])
+        assert p[0] == -2.0
+        assert p[1] == -2.5
+        p[1] += 17.75
+        assert p[1] == 15.25
+        #
+        p = ffi.new("float*", 15.75)
+        assert p[0] == 15.75
+        py.test.raises(TypeError, int, p)
+        py.test.raises(TypeError, float, p)
+        p[0] = 0.0
+        assert bool(p) is True
+        #
+        p = ffi.new("float*", 1.1)
+        f = p[0]
+        assert f != 1.1      # because of rounding effect
+        assert abs(f - 1.1) < 1E-7
+        #
+        INF = 1E200 * 1E200
+        assert 1E200 != INF
+        p[0] = 1E200
+        assert p[0] == INF     # infinite, not enough precision
+
+    def test_struct_simple(self):
+        s = ffi.new("struct simple*")
+        assert s.a == s.b == s.c == 0
+        s.b = -23
+        assert s.b == -23
+        py.test.raises(OverflowError, "s.b = 32768")
+        #
+        s = ffi.new("struct simple*", [-2, -3])
+        assert s.a == -2
+        assert s.b == -3
+        assert s.c == 0
+        py.test.raises((AttributeError, TypeError), "del s.a")
+        assert repr(s) == "<cdata 'struct simple *' owning %d bytes>" % (
+            SIZE_OF_INT + 2 * SIZE_OF_SHORT)
+        #
+        py.test.raises(ValueError, ffi.new, "struct simple*", [1, 2, 3, 4])
+
+    def test_constructor_struct_from_dict(self):
+        s = ffi.new("struct simple*", {'b': 123, 'c': 456})
+        assert s.a == 0
+        assert s.b == 123
+        assert s.c == 456
+        py.test.raises(KeyError, ffi.new, "struct simple*", {'d': 456})
+
+    def test_struct_pointer(self):
+        s = ffi.new("struct simple*")
+        assert s[0].a == s[0].b == s[0].c == 0
+        s[0].b = -23
+        assert s[0].b == s.b == -23
+        py.test.raises(OverflowError, "s[0].b = -32769")
+        py.test.raises(IndexError, "s[1]")
+
+    def test_struct_opaque(self):
+        py.test.raises(ffi.error, ffi.new, "struct baz*")
+        # should 'ffi.new("struct baz **") work?  it used to, but it was
+        # not particularly useful...
+        py.test.raises(ffi.error, ffi.new, "struct baz**")
+
+    def test_pointer_to_struct(self):
+        s = ffi.new("struct simple *")
+        s.a = -42
+        assert s[0].a == -42
+        p = ffi.new("struct simple **", s)
+        assert p[0].a == -42
+        assert p[0][0].a == -42
+        p[0].a = -43
+        assert s.a == -43
+        assert s[0].a == -43
+        p[0][0].a = -44
+        assert s.a == -44
+        assert s[0].a == -44
+        s.a = -45
+        assert p[0].a == -45
+        assert p[0][0].a == -45
+        s[0].a = -46
+        assert p[0].a == -46
+        assert p[0][0].a == -46
+
+    def test_constructor_struct_of_array(self):
+        s = ffi.new("struct array *", [[10, 11], [b'a', b'b', b'c']])
+        assert s.a[1] == 11
+        assert s.b[2] == b'c'
+        s.b[1] = b'X'
+        assert s.b[0] == b'a'
+        assert s.b[1] == b'X'
+        assert s.b[2] == b'c'
+
+    def test_recursive_struct(self):
+        s = ffi.new("struct recursive*")
+        t = ffi.new("struct recursive*")
+        s.value = 123
+        s.next = t
+        t.value = 456
+        assert s.value == 123
+        assert s.next.value == 456
+
+    def test_union_simple(self):
+        u = ffi.new("union simple_u*")
+        assert u.a == u.b == u.c == 0
+        u.b = -23
+        assert u.b == -23
+        assert u.a != 0
+        py.test.raises(OverflowError, "u.b = 32768")
+        #
+        u = ffi.new("union simple_u*", [-2])
+        assert u.a == -2
+        py.test.raises((AttributeError, TypeError), "del u.a")
+        assert repr(u) == "<cdata 'union simple_u *' owning %d bytes>" % (
+            SIZE_OF_INT,)
+
+    def test_union_opaque(self):
+        py.test.raises(ffi.error, ffi.new, "union baz*")
+        # should 'ffi.new("union baz **") work?  it used to, but it was
+        # not particularly useful...
+        py.test.raises(ffi.error, ffi.new, "union baz**")
+
+    def test_union_initializer(self):
+        py.test.raises(TypeError, ffi.new, "union init_u*", b'A')
+        py.test.raises(TypeError, ffi.new, "union init_u*", 5)
+        py.test.raises(ValueError, ffi.new, "union init_u*", [b'A', 5])
+        u = ffi.new("union init_u*", [b'A'])
+        assert u.a == b'A'
+        py.test.raises(TypeError, ffi.new, "union init_u*", [1005])
+        u = ffi.new("union init_u*", {'b': 12345})
+        assert u.b == 12345
+        u = ffi.new("union init_u*", [])
+        assert u.a == b'\x00'
+        assert u.b == 0
+
+    def test_sizeof_type(self):
+        for c_type, expected_size in [
+            ('char', 1),
+            ('unsigned int', 4),
+            ('char *', SIZE_OF_PTR),
+            ('int[5]', 20),
+            ('struct four_s', 12),
+            ('union four_u', 4),
+            ]:
+            size = ffi.sizeof(c_type)
+            assert size == expected_size, (size, expected_size, ctype)
+
+    def test_sizeof_cdata(self):
+        assert ffi.sizeof(ffi.new("short*")) == SIZE_OF_PTR
+        assert ffi.sizeof(ffi.cast("short", 123)) == SIZE_OF_SHORT
+        #
+        a = ffi.new("int[]", [10, 11, 12, 13, 14])
+        assert len(a) == 5
+        assert ffi.sizeof(a) == 5 * SIZE_OF_INT
+
+    def test_string_from_char_pointer(self):
+        x = ffi.new("char*", b"x")
+        assert str(x) == repr(x)
+        assert ffi.string(x) == b"x"
+        assert ffi.string(ffi.new("char*", b"\x00")) == b""
+        py.test.raises(TypeError, ffi.new, "char*", unicode("foo"))
+
+    def test_unicode_from_wchar_pointer(self):
+        self.check_wchar_t(ffi)
+        x = ffi.new("wchar_t*", u+"x")
+        assert unicode(x) == unicode(repr(x))
+        assert ffi.string(x) == u+"x"
+        assert ffi.string(ffi.new("wchar_t*", u+"\x00")) == u+""
+
+    def test_string_from_char_array(self):
+        p = ffi.new("char[]", b"hello.")
+        p[5] = b'!'
+        assert ffi.string(p) == b"hello!"
+        p[6] = b'?'
+        assert ffi.string(p) == b"hello!?"
+        p[3] = b'\x00'
+        assert ffi.string(p) == b"hel"
+        assert ffi.string(p, 2) == b"he"
+        py.test.raises(IndexError, "p[7] = b'X'")
+        #
+        a = ffi.new("char[]", b"hello\x00world")
+        assert len(a) == 12
+        p = ffi.cast("char *", a)
+        assert ffi.string(p) == b'hello'
+
+    def test_string_from_wchar_array(self):
+        self.check_wchar_t(ffi)
+        assert ffi.string(ffi.cast("wchar_t", "x")) == u+"x"
+        assert ffi.string(ffi.cast("wchar_t", u+"x")) == u+"x"
+        x = ffi.cast("wchar_t", "x")
+        assert str(x) == repr(x)
+        assert ffi.string(x) == u+"x"
+        #
+        p = ffi.new("wchar_t[]", u+"hello.")
+        p[5] = u+'!'
+        assert ffi.string(p) == u+"hello!"
+        p[6] = u+'\u04d2'
+        assert ffi.string(p) == u+"hello!\u04d2"
+        p[3] = u+'\x00'
+        assert ffi.string(p) == u+"hel"
+        assert ffi.string(p, 123) == u+"hel"
+        py.test.raises(IndexError, "p[7] = u+'X'")
+        #
+        a = ffi.new("wchar_t[]", u+"hello\x00world")
+        assert len(a) == 12
+        p = ffi.cast("wchar_t *", a)
+        assert ffi.string(p) == u+'hello'
+        assert ffi.string(p, 123) == u+'hello'
+        assert ffi.string(p, 5) == u+'hello'
+        assert ffi.string(p, 2) == u+'he'
+
+    def test_fetch_const_char_p_field(self):
+        # 'const' is ignored so far, in the declaration of 'struct string'
+        t = ffi.new("const char[]", b"testing")
+        s = ffi.new("struct string*", [t])
+        assert type(s.name) not in (bytes, str, unicode)
+        assert ffi.string(s.name) == b"testing"
+        py.test.raises(TypeError, "s.name = None")
+        s.name = ffi.NULL
+        assert s.name == ffi.NULL
+
+    def test_fetch_const_wchar_p_field(self):
+        # 'const' is ignored so far
+        self.check_wchar_t(ffi)
+        t = ffi.new("const wchar_t[]", u+"testing")
+        s = ffi.new("struct ustring*", [t])
+        assert type(s.name) not in (bytes, str, unicode)
+        assert ffi.string(s.name) == u+"testing"
+        s.name = ffi.NULL
+        assert s.name == ffi.NULL
+
+    def test_voidp(self):
+        py.test.raises(TypeError, ffi.new, "void*")
+        p = ffi.new("void **")
+        assert p[0] == ffi.NULL
+        a = ffi.new("int[]", [10, 11, 12])
+        p = ffi.new("void **", a)
+        vp = p[0]
+        py.test.raises(TypeError, "vp[0]")
+        py.test.raises(TypeError, ffi.new, "short **", a)
+        #
+        s = ffi.new("struct voidp *")
+        s.p = a    # works
+        s.q = a    # works
+        py.test.raises(TypeError, "s.r = a")    # fails
+        b = ffi.cast("int *", a)
+        s.p = b    # works
+        s.q = b    # works
+        py.test.raises(TypeError, "s.r = b")    # fails
+
+    def test_functionptr_simple(self):
+        py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0)
+        def cb(n):
+            return n + 1
+        cb.__qualname__ = 'cb'
+        p = ffi.callback("int(*)(int)", cb)
+        res = p(41)     # calling an 'int(*)(int)', i.e. a function pointer
+        assert res == 42 and type(res) is int
+        res = p(ffi.cast("int", -41))
+        assert res == -40 and type(res) is int
+        assert repr(p).startswith(
+            "<cdata 'int(*)(int)' calling <function cb at 0x")
+        assert ffi.typeof(p) is ffi.typeof("int(*)(int)")
+        q = ffi.new("int(**)(int)", p)
+        assert repr(q) == "<cdata 'int(* *)(int)' owning %d bytes>" % (
+            SIZE_OF_PTR)
+        py.test.raises(TypeError, "q(43)")
+        res = q[0](43)
+        assert res == 44
+        q = ffi.cast("int(*)(int)", p)
+        assert repr(q).startswith("<cdata 'int(*)(int)' 0x")
+        res = q(45)
+        assert res == 46
+
+    def test_functionptr_advanced(self):
+        t = ffi.typeof("int(*(*)(int))(int)")
+        assert repr(t) == "<ctype '%s'>" % "int(*(*)(int))(int)"
+
+    def test_functionptr_voidptr_return(self):
+        def cb():
+            return ffi.NULL
+        p = ffi.callback("void*(*)()", cb)
+        res = p()
+        assert res is not None
+        assert res == ffi.NULL
+        int_ptr = ffi.new('int*')
+        void_ptr = ffi.cast('void*', int_ptr)
+        def cb():
+            return void_ptr
+        p = ffi.callback("void*(*)()", cb)
+        res = p()
+        assert res == void_ptr
+
+    def test_functionptr_intptr_return(self):
+        def cb():
+            return ffi.NULL
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert res == ffi.NULL
+        int_ptr = ffi.new('int*')
+        def cb():
+            return int_ptr
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert repr(res).startswith("<cdata 'int *' 0x")
+        assert res == int_ptr
+        int_array_ptr = ffi.new('int[1]')
+        def cb():
+            return int_array_ptr
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert repr(res).startswith("<cdata 'int *' 0x")
+        assert res == int_array_ptr
+
+    def test_functionptr_void_return(self):
+        def foo():
+            pass
+        foo_cb = ffi.callback("void foo()", foo)
+        result = foo_cb()
+        assert result is None
+
+    def test_char_cast(self):
+        p = ffi.cast("int", b'\x01')
+        assert ffi.typeof(p) is ffi.typeof("int")
+        assert int(p) == 1
+        p = ffi.cast("int", ffi.cast("char", b"a"))
+        assert int(p) == ord("a")
+        p = ffi.cast("int", ffi.cast("char", b"\x80"))
+        assert int(p) == 0x80     # "char" is considered unsigned in this case
+        p = ffi.cast("int", b"\x81")
+        assert int(p) == 0x81
+
+    def test_wchar_cast(self):
+        self.check_wchar_t(ffi)
+        p = ffi.cast("int", ffi.cast("wchar_t", u+'\u1234'))
+        assert int(p) == 0x1234
+        p = ffi.cast("long long", ffi.cast("wchar_t", -1))
+        if SIZE_OF_WCHAR == 2:      # 2 bytes, unsigned
+            assert int(p) == 0xffff
+        elif (sys.platform.startswith('linux') and
+              platform.machine().startswith('x86')):   # known to be signed
+            assert int(p) == -1
+        else:                     # in general, it can be either signed or not
+            assert int(p) in [-1, 0xffffffff]  # e.g. on arm, both cases occur
+        p = ffi.cast("int", u+'\u1234')
+        assert int(p) == 0x1234
+
+    def test_cast_array_to_charp(self):
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        p = ffi.cast("char*", a)
+        data = b''.join([p[i] for i in range(4)])
+        if sys.byteorder == 'little':
+            assert data == b'\x34\x12\x78\x56'
+        else:
+            assert data == b'\x12\x34\x56\x78'
+
+    def test_cast_between_pointers(self):
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        p = ffi.cast("short*", a)
+        p2 = ffi.cast("int*", p)
+        q = ffi.cast("char*", p2)
+        data = b''.join([q[i] for i in range(4)])
+        if sys.byteorder == 'little':
+            assert data == b'\x34\x12\x78\x56'
+        else:
+            assert data == b'\x12\x34\x56\x78'
+
+    def test_cast_pointer_and_int(self):
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        l1 = ffi.cast("intptr_t", a)
+        p = ffi.cast("short*", a)
+        l2 = ffi.cast("intptr_t", p)
+        assert int(l1) == int(l2) != 0
+        q = ffi.cast("short*", l1)
+        assert q == ffi.cast("short*", int(l1))
+        assert q[0] == 0x1234
+        assert int(ffi.cast("intptr_t", ffi.NULL)) == 0
+
+    def test_cast_functionptr_and_int(self):
+        def cb(n):
+            return n + 1
+        a = ffi.callback("int(*)(int)", cb)
+        p = ffi.cast("void *", a)
+        assert p
+        b = ffi.cast("int(*)(int)", p)
+        assert b(41) == 42
+        assert a == b
+        assert hash(a) == hash(b)
+
+    def test_callback_crash(self):
+        def cb(n):
+            raise Exception
+        a = ffi.callback("int(*)(int)", cb, error=42)
+        res = a(1)    # and the error reported to stderr
+        assert res == 42
+
+    def test_structptr_argument(self):
+        def cb(p):
+            return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b
+        a = ffi.callback("int(*)(struct ab[])", cb)
+        res = a([[5, 6], {'a': 7, 'b': 8}])
+        assert res == 5678
+        res = a([[5], {'b': 8}])
+        assert res == 5008
+
+    def test_array_argument_as_list(self):
+        seen = []
+        def cb(argv):
+            seen.append(ffi.string(argv[0]))
+            seen.append(ffi.string(argv[1]))
+        a = ffi.callback("void(*)(char *[])", cb)
+        a([ffi.new("char[]", b"foobar"), ffi.new("char[]", b"baz")])
+        assert seen == [b"foobar", b"baz"]
+
+    def test_cast_float(self):
+        a = ffi.cast("float", 12)
+        assert float(a) == 12.0
+        a = ffi.cast("float", 12.5)
+        assert float(a) == 12.5
+        a = ffi.cast("float", b"A")
+        assert float(a) == ord("A")
+        a = ffi.cast("int", 12.9)
+        assert int(a) == 12
+        a = ffi.cast("char", 66.9 + 256)
+        assert ffi.string(a) == b"B"
+        #
+        a = ffi.cast("float", ffi.cast("int", 12))
+        assert float(a) == 12.0
+        a = ffi.cast("float", ffi.cast("double", 12.5))
+        assert float(a) == 12.5
+        a = ffi.cast("float", ffi.cast("char", b"A"))
+        assert float(a) == ord("A")
+        a = ffi.cast("int", ffi.cast("double", 12.9))
+        assert int(a) == 12
+        a = ffi.cast("char", ffi.cast("double", 66.9 + 256))
+        assert ffi.string(a) == b"B"
+
+    def test_enum(self):
+        # enum foq { A0, B0, CC0, D0 };
+        assert ffi.string(ffi.cast("enum foq", 0)) == "cffiA0"
+        assert ffi.string(ffi.cast("enum foq", 2)) == "cffiCC0"
+        assert ffi.string(ffi.cast("enum foq", 3)) == "cffiD0"
+        assert ffi.string(ffi.cast("enum foq", 4)) == "4"
+        # enum bar { A1, B1=-2, CC1, D1, E1 };
+        assert ffi.string(ffi.cast("enum bar", 0)) == "A1"
+        assert ffi.string(ffi.cast("enum bar", -2)) == "B1"
+        assert ffi.string(ffi.cast("enum bar", -1)) == "CC1"
+        assert ffi.string(ffi.cast("enum bar", 1)) == "E1"
+        assert ffi.cast("enum bar", -2) == ffi.cast("enum bar", -2)
+        assert ffi.cast("enum foq", 0) == ffi.cast("enum bar", 0)
+        assert ffi.cast("enum bar", 0) == ffi.cast("int", 0)
+        assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC1>"
+        assert repr(ffi.cast("enum foq", -1)) == (  # enums are unsigned, if
+            "<cdata 'enum foq' 4294967295>") or (   # they contain no neg value
+                sys.platform == "win32")            # (but not on msvc)
+        # enum baz { A2=0x1000, B2=0x2000 };
+        assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A2"
+        assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2"
+
+    def test_enum_in_struct(self):
+        # enum foo2 { A3, B3, C3, D3 };
+        # struct bar_with_e { enum foo2 e; };
+        s = ffi.new("struct bar_with_e *")
+        s.e = 0
+        assert s.e == 0
+        s.e = 3
+        assert s.e == 3
+        assert s[0].e == 3
+        s[0].e = 2
+        assert s.e == 2
+        assert s[0].e == 2
+        s.e = ffi.cast("enum foo2", -1)
+        assert s.e in (4294967295, -1)     # two choices
+        assert s[0].e in (4294967295, -1)
+        s.e = s.e
+        py.test.raises(TypeError, "s.e = 'B3'")
+        py.test.raises(TypeError, "s.e = '2'")
+        py.test.raises(TypeError, "s.e = '#2'")
+        py.test.raises(TypeError, "s.e = '#7'")
+
+    def test_enum_non_contiguous(self):
+        # enum noncont { A4, B4=42, C4 };
+        assert ffi.string(ffi.cast("enum noncont", 0)) == "A4"
+        assert ffi.string(ffi.cast("enum noncont", 42)) == "B4"
+        assert ffi.string(ffi.cast("enum noncont", 43)) == "C4"
+        invalid_value = ffi.cast("enum noncont", 2)
+        assert int(invalid_value) == 2
+        assert ffi.string(invalid_value) == "2"
+
+    def test_enum_char_hex_oct(self):
+        # enum etypes {A5='!', B5='\'', C5=0x10, D5=010, E5=- 0x10, F5=-010};
+        assert ffi.string(ffi.cast("enum etypes", ord('!'))) == "A5"
+        assert ffi.string(ffi.cast("enum etypes", ord("'"))) == "B5"
+        assert ffi.string(ffi.cast("enum etypes", 16)) == "C5"
+        assert ffi.string(ffi.cast("enum etypes", 8)) == "D5"
+        assert ffi.string(ffi.cast("enum etypes", -16)) == "E5"
+        assert ffi.string(ffi.cast("enum etypes", -8)) == "F5"
+
+    def test_array_of_struct(self):
+        s = ffi.new("struct ab[1]")
+        py.test.raises(AttributeError, 's.b')
+        py.test.raises(AttributeError, 's.b = 412')
+        s[0].b = 412
+        assert s[0].b == 412
+        py.test.raises(IndexError, 's[1]')
+
+    def test_pointer_to_array(self):
+        p = ffi.new("int(**)[5]")
+        assert repr(p) == "<cdata 'int(* *)[5]' owning %d bytes>" % SIZE_OF_PTR
+
+    def test_iterate_array(self):
+        a = ffi.new("char[]", b"hello")
+        assert list(a) == [b"h", b"e", b"l", b"l", b"o", b"\0"]
+        assert list(iter(a)) == [b"h", b"e", b"l", b"l", b"o", b"\0"]
+        #
+        py.test.raises(TypeError, iter, ffi.cast("char *", a))
+        py.test.raises(TypeError, list, ffi.cast("char *", a))
+        py.test.raises(TypeError, iter, ffi.new("int *"))
+        py.test.raises(TypeError, list, ffi.new("int *"))
+
+    def test_offsetof(self):
+        # struct abc { int a, b, c; };
+        assert ffi.offsetof("struct abc", "a") == 0
+        assert ffi.offsetof("struct abc", "b") == 4
+        assert ffi.offsetof("struct abc", "c") == 8
+
+    def test_offsetof_nested(self):
+        # struct nesting { struct abc d, e; };
+        assert ffi.offsetof("struct nesting", "e") == 12
+        py.test.raises(KeyError, ffi.offsetof, "struct nesting", "e.a")
+        assert ffi.offsetof("struct nesting", "e", "a") == 12
+        assert ffi.offsetof("struct nesting", "e", "b") == 16
+        assert ffi.offsetof("struct nesting", "e", "c") == 20
+
+    def test_offsetof_array(self):
+        assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int")
+        assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int")
+        # struct array2 { int a, b; int c[99]; };
+        assert ffi.offsetof("struct array2", "c") == 2 * ffi.sizeof("int")
+        assert ffi.offsetof("struct array2", "c", 0) == 2 * ffi.sizeof("int")
+        assert ffi.offsetof("struct array2", "c", 51) == 53 * ffi.sizeof("int")
+
+    def test_alignof(self):
+        # struct align { char a; short b; char c; };
+        assert ffi.alignof("int") == 4
+        assert ffi.alignof("double") in (4, 8)
+        assert ffi.alignof("struct align") == 2
+
+    def test_bitfield(self):
+        # struct bitfield { int a:10, b:20, c:3; };
+        assert ffi.sizeof("struct bitfield") == 8
+        s = ffi.new("struct bitfield *")
+        s.a = 511
+        py.test.raises(OverflowError, "s.a = 512")
+        py.test.raises(OverflowError, "s[0].a = 512")
+        assert s.a == 511
+        s.a = -512
+        py.test.raises(OverflowError, "s.a = -513")
+        py.test.raises(OverflowError, "s[0].a = -513")
+        assert s.a == -512
+        s.c = 3
+        assert s.c == 3
+        py.test.raises(OverflowError, "s.c = 4")
+        py.test.raises(OverflowError, "s[0].c = 4")
+        s.c = -4
+        assert s.c == -4
+
+    def test_bitfield_enum(self):
+        # typedef enum { AA1, BB1, CC1 } foo_e_t;
+        # typedef struct { foo_e_t f:2; } bfenum_t;
+        if sys.platform == "win32":
+            py.test.skip("enums are not unsigned")
+        s = ffi.new("bfenum_t *")
+        s.f = 2
+        assert s.f == 2
+
+    def test_anonymous_struct(self):
+        # typedef struct { int a; } anon_foo_t;
+        # typedef struct { char b, c; } anon_bar_t;
+        f = ffi.new("anon_foo_t *", [12345])
+        b = ffi.new("anon_bar_t *", [b"B", b"C"])
+        assert f.a == 12345
+        assert b.b == b"B"
+        assert b.c == b"C"
+        assert repr(b).startswith("<cdata 'anon_bar_t *'")
+
+    def test_struct_with_two_usages(self):
+        # typedef struct named_foo_s { int a; } named_foo_t, *named_foo_p;
+        # typedef struct { int a; } unnamed_foo_t, *unnamed_foo_p;
+        f = ffi.new("named_foo_t *", [12345])
+        ps = ffi.new("named_foo_p[]", [f])
+        f = ffi.new("unnamed_foo_t *", [12345])
+        ps = ffi.new("unnamed_foo_p[]", [f])
+
+    def test_pointer_arithmetic(self):
+        s = ffi.new("short[]", list(range(100, 110)))
+        p = ffi.cast("short *", s)
+        assert p[2] == 102
+        assert p+1 == p+1
+        assert p+1 != p+0
+        assert p == p+0 == p-0
+        assert (p+1)[0] == 101
+        assert (p+19)[-10] == 109
+        assert (p+5) - (p+1) == 4
+        assert p == s+0
+        assert p+1 == s+1
+
+    def test_pointer_comparison(self):
+        s = ffi.new("short[]", list(range(100)))
+        p = ffi.cast("short *", s)
+        assert (p <  s) is False
+        assert (p <= s) is True
+        assert (p == s) is True
+        assert (p != s) is False
+        assert (p >  s) is False
+        assert (p >= s) is True
+        assert (s <  p) is False
+        assert (s <= p) is True
+        assert (s == p) is True
+        assert (s != p) is False
+        assert (s >  p) is False
+        assert (s >= p) is True
+        q = p + 1
+        assert (q <  s) is False
+        assert (q <= s) is False
+        assert (q == s) is False
+        assert (q != s) is True
+        assert (q >  s) is True
+        assert (q >= s) is True
+        assert (s <  q) is True
+        assert (s <= q) is True
+        assert (s == q) is False
+        assert (s != q) is True
+        assert (s >  q) is False
+        assert (s >= q) is False
+        assert (q <  p) is False
+        assert (q <= p) is False
+        assert (q == p) is False
+        assert (q != p) is True
+        assert (q >  p) is True
+        assert (q >= p) is True
+        assert (p <  q) is True
+        assert (p <= q) is True
+        assert (p == q) is False
+        assert (p != q) is True
+        assert (p >  q) is False
+        assert (p >= q) is False
+        #
+        assert (None == s) is False
+        assert (None != s) is True
+        assert (s == None) is False
+        assert (s != None) is True
+        assert (None == q) is False
+        assert (None != q) is True
+        assert (q == None) is False
+        assert (q != None) is True
+
+    def test_integer_comparison(self):
+        x = ffi.cast("int", 123)
+        y = ffi.cast("int", 456)
+        assert x < y
+        #
+        z = ffi.cast("double", 78.9)
+        assert x > z
+        assert y > z
+
+    def test_ffi_buffer_ptr(self):
+        a = ffi.new("short *", 100)
+        try:
+            b = ffi.buffer(a)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        content = b[:]
+        assert len(content) == len(b) == 2
+        if sys.byteorder == 'little':
+            assert content == b'\x64\x00'
+            assert b[0] == b'\x64'
+            b[0] = b'\x65'
+        else:
+            assert content == b'\x00\x64'
+            assert b[1] == b'\x64'
+            b[1] = b'\x65'
+        assert a[0] == 101
+
+    def test_ffi_buffer_array(self):
+        a = ffi.new("int[]", list(range(100, 110)))
+        try:
+            b = ffi.buffer(a)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        content = b[:]
+        if sys.byteorder == 'little':
+            assert content.startswith(b'\x64\x00\x00\x00\x65\x00\x00\x00')
+            b[4] = b'\x45'
+        else:
+            assert content.startswith(b'\x00\x00\x00\x64\x00\x00\x00\x65')
+            b[7] = b'\x45'
+        assert len(content) == 4 * 10
+        assert a[1] == 0x45
+
+    def test_ffi_buffer_ptr_size(self):
+        a = ffi.new("short *", 0x4243)
+        try:
+            b = ffi.buffer(a, 1)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        content = b[:]
+        assert len(content) == 1
+        if sys.byteorder == 'little':
+            assert content == b'\x43'
+            b[0] = b'\x62'
+            assert a[0] == 0x4262
+        else:
+            assert content == b'\x42'
+            b[0] = b'\x63'
+            assert a[0] == 0x6343
+
+    def test_ffi_buffer_array_size(self):
+        a1 = ffi.new("int[]", list(range(100, 110)))
+        a2 = ffi.new("int[]", list(range(100, 115)))
+        try:
+            ffi.buffer(a1)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        assert ffi.buffer(a1)[:] == ffi.buffer(a2, 4*10)[:]
+
+    def test_ffi_buffer_with_file(self):
+        import tempfile, os, array
+        fd, filename = tempfile.mkstemp()
+        f = os.fdopen(fd, 'r+b')
+        a = ffi.new("int[]", list(range(1005)))
+        try:
+            ffi.buffer(a, 512)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        f.write(ffi.buffer(a, 1000 * ffi.sizeof("int")))
+        f.seek(0)
+        assert f.read() == array.array('i', range(1000)).tostring()
+        f.seek(0)
+        b = ffi.new("int[]", 1005)
+        f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int")))
+        assert list(a)[:1000] + [0] * (len(a)-1000) == list(b)
+        f.close()
+        os.unlink(filename)
+
+    def test_ffi_buffer_with_io(self):
+        import io, array
+        f = io.BytesIO()
+        a = ffi.new("int[]", list(range(1005)))
+        try:
+            ffi.buffer(a, 512)
+        except NotImplementedError as e:
+            py.test.skip(str(e))
+        f.write(ffi.buffer(a, 1000 * ffi.sizeof("int")))
+        f.seek(0)
+        assert f.read() == array.array('i', range(1000)).tostring()
+        f.seek(0)
+        b = ffi.new("int[]", 1005)
+        f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int")))
+        assert list(a)[:1000] + [0] * (len(a)-1000) == list(b)
+        f.close()
+
+    def test_array_in_struct(self):
+        # struct array { int a[2]; char b[3]; };
+        p = ffi.new("struct array *")
+        p.a[1] = 5
+        assert p.a[1] == 5
+        assert repr(p.a).startswith("<cdata 'int[2]' 0x")
+
+    def test_struct_containing_array_varsize_workaround(self):
+        if sys.platform == "win32":
+            py.test.skip("array of length 0 not supported")
+        # struct array0 { int len; short data[0]; };
+        p = ffi.new("char[]", ffi.sizeof("struct array0") + 7 * SIZE_OF_SHORT)
+        q = ffi.cast("struct array0 *", p)
+        assert q.len == 0
+        # 'q.data' gets not a 'short[0]', but just a 'short *' instead
+        assert repr(q.data).startswith("<cdata 'short *' 0x")
+        assert q.data[6] == 0
+        q.data[6] = 15
+        assert q.data[6] == 15
+
+    def test_new_struct_containing_array_varsize(self):
+        py.test.skip("later?")
+        ffi.cdef("struct foo_s { int len; short data[]; };")
+        p = ffi.new("struct foo_s *", 10)     # a single integer is the length
+        assert p.len == 0
+        assert p.data[9] == 0
+        py.test.raises(IndexError, "p.data[10]")
+
+    def test_ffi_typeof_getcname(self):
+        assert ffi.getctype("int") == "int"
+        assert ffi.getctype("int", 'x') == "int x"
+        assert ffi.getctype("int*") == "int *"
+        assert ffi.getctype("int*", '') == "int *"
+        assert ffi.getctype("int*", 'x') == "int * x"
+        assert ffi.getctype("int", '*') == "int *"
+        assert ffi.getctype("int", ' * x ') == "int * x"
+        assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *"
+        assert ffi.getctype("int", '[5]') == "int[5]"
+        assert ffi.getctype("int[5]", '[6]') == "int[6][5]"
+        assert ffi.getctype("int[5]", '(*)') == "int(*)[5]"
+        # special-case for convenience: automatically put '()' around '*'
+        assert ffi.getctype("int[5]", '*') == "int(*)[5]"
+        assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]"
+        assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]"
+
+    def test_array_of_func_ptr(self):
+        f = ffi.cast("int(*)(int)", 42)
+        assert f != ffi.NULL
+        py.test.raises(ffi.error, ffi.cast, "int(int)", 42)
+        py.test.raises(ffi.error, ffi.new, "int([5])(int)")
+        a = ffi.new("int(*[5])(int)", [f])
+        assert ffi.getctype(ffi.typeof(a)) == "int(*[5])(int)"
+        assert len(a) == 5
+        assert a[0] == f
+        assert a[1] == ffi.NULL
+        py.test.raises(TypeError, ffi.cast, "int(*)(int)[5]", 0)
+        #
+        def cb(n):
+            return n + 1
+        f = ffi.callback("int(*)(int)", cb)
+        a = ffi.new("int(*[5])(int)", [f, f])
+        assert a[1](42) == 43
+
+    def test_callback_as_function_argument(self):
+        # In C, function arguments can be declared with a function type,
+        # which is automatically replaced with the ptr-to-function type.
+        def cb(a, b):
+            return chr(ord(a) + ord(b)).encode()
+        f = ffi.callback("char cb(char, char)", cb)
+        assert f(b'A', b'\x01') == b'B'
+        def g(callback):
+            return callback(b'A', b'\x01')
+        g = ffi.callback("char g(char cb(char, char))", g)
+        assert g(f) == b'B'
+
+    def test_vararg_callback(self):
+        py.test.skip("callback with '...'")
+        def cb(i, va_list):
+            j = ffi.va_arg(va_list, "int")
+            k = ffi.va_arg(va_list, "long long")
+            return i * 2 + j * 3 + k * 5
+        f = ffi.callback("long long cb(long i, ...)", cb)
+        res = f(10, ffi.cast("int", 100), ffi.cast("long long", 1000))
+        assert res == 20 + 300 + 5000
+
+    def test_callback_decorator(self):
+        #
+        @ffi.callback("long(long, long)", error=42)
+        def cb(a, b):
+            return a - b
+        #
+        assert cb(-100, -10) == -90
+        sz = ffi.sizeof("long")
+        assert cb((1 << (sz*8-1)) - 1, -10) == 42
+
+    def test_anonymous_enum(self):
+        # typedef enum { Value0 = 0 } e_t, *pe_t;
+        assert ffi.getctype("e_t*") == 'e_t *'
+        assert ffi.getctype("pe_t") == 'e_t *'
+        assert ffi.getctype("foo_e_t*") == 'foo_e_t *'
+
+    def test_new_ctype(self):
+        p = ffi.new("int *")
+        py.test.raises(TypeError, ffi.new, p)
+        p = ffi.new(ffi.typeof("int *"), 42)
+        assert p[0] == 42
+
+    def test_enum_with_non_injective_mapping(self):
+        # enum e_noninj { AA3=0, BB3=0, CC3=0, DD3=0 };
+        e = ffi.cast("enum e_noninj", 0)
+        assert ffi.string(e) == "AA3"     # pick the first one arbitrarily
+
+    def test_enum_refer_previous_enum_value(self):
+        # enum e_prev { AA4, BB4=2, CC4=4, DD4=BB4, EE4, FF4=CC4, GG4=FF4 };
+        assert ffi.string(ffi.cast("enum e_prev", 2)) == "BB4"
+        assert ffi.string(ffi.cast("enum e_prev", 3)) == "EE4"
+        assert ffi.sizeof("char[DD4]") == 2
+        assert ffi.sizeof("char[EE4]") == 3
+        assert ffi.sizeof("char[FF4]") == 4
+        assert ffi.sizeof("char[GG4]") == 4
+
+    def test_nested_anonymous_struct(self):
+        # struct nested_anon {
+        #     struct { int a, b; };
+        #     union { int c, d; };
+        # };
+        assert ffi.sizeof("struct nested_anon") == 3 * SIZE_OF_INT
+        p = ffi.new("struct nested_anon *", [1, 2, 3])
+        assert p.a == 1
+        assert p.b == 2
+        assert p.c == 3
+        assert p.d == 3
+        p.d = 17
+        assert p.c == 17
+        p.b = 19
+        assert p.a == 1
+        assert p.b == 19
+        assert p.c == 17
+        assert p.d == 17
+        p = ffi.new("struct nested_anon *", {'b': 12, 'd': 14})
+        assert p.a == 0
+        assert p.b == 12
+        assert p.c == 14
+        assert p.d == 14
+
+    def test_nested_field_offset_align(self):
+        # struct nested_field_ofs_s {
+        #    struct { int a; char b; };
+        #    union { char c; };
+        # };
+        assert ffi.offsetof("struct nested_field_ofs_s", "c") == 2 * SIZE_OF_INT
+        assert ffi.sizeof("struct nested_field_ofs_s") == 3 * SIZE_OF_INT
+
+    def test_nested_anonymous_union(self):
+        # union nested_anon_u {
+        #     struct { int a, b; };
+        #     union { int c, d; };
+        # };
+        assert ffi.sizeof("union nested_anon_u") == 2 * SIZE_OF_INT
+        p = ffi.new("union nested_anon_u *", [5])
+        assert p.a == 5
+        assert p.b == 0
+        assert p.c == 5
+        assert p.d == 5
+        p.d = 17
+        assert p.c == 17
+        assert p.a == 17
+        p.b = 19
+        assert p.a == 17
+        assert p.b == 19
+        assert p.c == 17
+        assert p.d == 17
+        p = ffi.new("union nested_anon_u *", {'d': 14})
+        assert p.a == 14
+        assert p.b == 0
+        assert p.c == 14
+        assert p.d == 14
+        p = ffi.new("union nested_anon_u *", {'b': 12})
+        assert p.a == 0
+        assert p.b == 12
+        assert p.c == 0
+        assert p.d == 0
+        # we cannot specify several items in the dict, even though
+        # in theory in this particular case it would make sense
+        # to give both 'a' and 'b'
+
+    def test_cast_to_array_type(self):
+        p = ffi.new("int[4]", [-5])
+        q = ffi.cast("int[3]", p)
+        assert q[0] == -5
+        assert repr(q).startswith("<cdata 'int[3]' 0x")
+
+    def test_gc(self):
+        p = ffi.new("int *", 123)
+        seen = []
+        def destructor(p1):
+            assert p1 is p
+            assert p1[0] == 123
+            seen.append(1)
+        q = ffi.gc(p, destructor=destructor)
+        import gc; gc.collect()
+        assert seen == []
+        del q
+        import gc; gc.collect(); gc.collect(); gc.collect()
+        assert seen == [1]
+
+    def test_gc_2(self):
+        p = ffi.new("int *", 123)
+        seen = []
+        q1 = ffi.gc(p, lambda p: seen.append(1))
+        q2 = ffi.gc(q1, lambda p: seen.append(2))
+        import gc; gc.collect()
+        assert seen == []
+        del q1, q2
+        import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect()
+        assert seen == [2, 1]
+
+    def test_gc_3(self):
+        p = ffi.new("int *", 123)
+        r = ffi.new("int *", 123)
+        seen = []
+        seen_r = []
+        q1 = ffi.gc(p, lambda p: seen.append(1))
+        s1 = ffi.gc(r, lambda r: seen_r.append(4))
+        q2 = ffi.gc(q1, lambda p: seen.append(2))
+        s2 = ffi.gc(s1, lambda r: seen_r.append(5))
+        q3 = ffi.gc(q2, lambda p: seen.append(3))
+        import gc; gc.collect()
+        assert seen == []
+        assert seen_r == []
+        del q1, q2, q3, s2, s1
+        import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect()
+        assert seen == [3, 2, 1]
+        assert seen_r == [5, 4]
+
+    def test_gc_4(self):
+        p = ffi.new("int *", 123)
+        seen = []
+        q1 = ffi.gc(p, lambda p: seen.append(1))
+        q2 = ffi.gc(q1, lambda p: seen.append(2))
+        q3 = ffi.gc(q2, lambda p: seen.append(3))
+        import gc; gc.collect()
+        assert seen == []
+        del q1, q3     # q2 remains, and has a hard ref to q1
+        import gc; gc.collect(); gc.collect(); gc.collect()
+        assert seen == [3]
+
+    def test_release(self):
+        p = ffi.new("int[]", 123)
+        ffi.release(p)
+        # here, reading p[0] might give garbage or segfault...
+        ffi.release(p)   # no effect
+
+    def test_release_new_allocator(self):
+        seen = []
+        def myalloc(size):
+            seen.append(size)
+            return ffi.new("char[]", b"X" * size)
+        def myfree(raw):
+            seen.append(raw)
+        alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree)
+        p = alloc2("int[]", 15)
+        assert seen == [15 * 4]
+        ffi.release(p)
+        assert seen == [15 * 4, p]
+        ffi.release(p)    # no effect
+        assert seen == [15 * 4, p]
+        #
+        del seen[:]
+        p = alloc2("struct ab *")
+        assert seen == [2 * 4]
+        ffi.release(p)
+        assert seen == [2 * 4, p]
+        ffi.release(p)    # no effect
+        assert seen == [2 * 4, p]
+
+    def test_CData_CType(self):
+        assert isinstance(ffi.cast("int", 0), ffi.CData)
+        assert isinstance(ffi.new("int *"), ffi.CData)
+        assert not isinstance(ffi.typeof("int"), ffi.CData)
+        assert not isinstance(ffi.cast("int", 0), ffi.CType)
+        assert not isinstance(ffi.new("int *"), ffi.CType)
+
+    def test_CData_CType_2(self):
+        assert isinstance(ffi.typeof("int"), ffi.CType)
+
+    def test_bool(self):
+        assert int(ffi.cast("_Bool", 0.1)) == 1
+        assert int(ffi.cast("_Bool", -0.0)) == 0
+        assert int(ffi.cast("_Bool", b'\x02')) == 1
+        assert int(ffi.cast("_Bool", b'\x00')) == 0
+        assert int(ffi.cast("_Bool", b'\x80')) == 1
+        assert ffi.new("_Bool *", False)[0] == 0
+        assert ffi.new("_Bool *", 1)[0] == 1
+        py.test.raises(OverflowError, ffi.new, "_Bool *", 2)
+        py.test.raises(TypeError, ffi.string, ffi.cast("_Bool", 2))
+
+    def test_addressof(self):
+        p = ffi.new("struct ab *")
+        a = ffi.addressof(p[0])
+        assert repr(a).startswith("<cdata 'struct ab *' 0x")
+        assert a == p
+        py.test.raises(TypeError, ffi.addressof, p)
+        py.test.raises((AttributeError, TypeError), ffi.addressof, 5)
+        py.test.raises(TypeError, ffi.addressof, ffi.cast("int", 5))
+
+    def test_addressof_field(self):
+        p = ffi.new("struct ab *")
+        b = ffi.addressof(p[0], 'b')
+        assert repr(b).startswith("<cdata 'int *' 0x")
+        assert int(ffi.cast("uintptr_t", b)) == (
+            int(ffi.cast("uintptr_t", p)) + ffi.sizeof("int"))
+        assert b == ffi.addressof(p, 'b')
+        assert b != ffi.addressof(p, 'a')
+
+    def test_addressof_field_nested(self):
+        # struct nesting { struct abc d, e; };
+        p = ffi.new("struct nesting *")
+        py.test.raises(KeyError, ffi.addressof, p[0], 'e.b')
+        a = ffi.addressof(p[0], 'e', 'b')
+        assert int(ffi.cast("uintptr_t", a)) == (
+            int(ffi.cast("uintptr_t", p)) +
+            ffi.sizeof("struct abc") + ffi.sizeof("int"))
+
+    def test_addressof_anonymous_struct(self):
+        # typedef struct { int a; } anon_foo_t;
+        p = ffi.new("anon_foo_t *")
+        a = ffi.addressof(p[0])
+        assert a == p
+
+    def test_addressof_array(self):
+        p = ffi.new("int[52]")
+        p0 = ffi.addressof(p)
+        assert p0 == p
+        assert ffi.typeof(p0) is ffi.typeof("int(*)[52]")
+        py.test.raises(TypeError, ffi.addressof, p0)
+        #
+        p1 = ffi.addressof(p, 25)
+        assert ffi.typeof(p1) is ffi.typeof("int *")
+        assert (p1 - p) == 25
+        assert ffi.addressof(p, 0) == p
+
+    def test_addressof_pointer(self):
+        array = ffi.new("int[50]")
+        p = ffi.cast("int *", array)
+        py.test.raises(TypeError, ffi.addressof, p)
+        assert ffi.addressof(p, 0) == p
+        assert ffi.addressof(p, 25) == p + 25
+        assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p)
+        #
+        array = ffi.new("struct ab[50]")
+        p = ffi.cast("int *", array)
+        py.test.raises(TypeError, ffi.addressof, p)
+        assert ffi.addressof(p, 0) == p
+        assert ffi.addressof(p, 25) == p + 25
+        assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p)
+
+    def test_addressof_array_in_struct(self):
+        # struct abc50 { int a, b; int c[50]; };
+        p = ffi.new("struct abc50 *")
+        p1 = ffi.addressof(p, "c", 25)
+        assert ffi.typeof(p1) is ffi.typeof("int *")
+        assert p1 == ffi.cast("int *", p) + 27
+        assert ffi.addressof(p, "c") == ffi.cast("int *", p) + 2
+        assert ffi.addressof(p, "c", 0) == ffi.cast("int *", p) + 2
+        p2 = ffi.addressof(p, 1)
+        assert ffi.typeof(p2) is ffi.typeof("struct abc50 *")
+        assert p2 == p + 1
+
+    def test_multiple_independent_structs(self):
+        CDEF2 = "struct ab { int x; };"
+        ffi2 = cffi.FFI(); ffi2.cdef(CDEF2)
+        outputfilename = recompile(ffi2, "test_multiple_independent_structs",
+                                   CDEF2, tmpdir=str(udir))
+        module = imp.load_dynamic("test_multiple_independent_structs",
+                                  outputfilename)
+        ffi1 = module.ffi
+        foo1 = ffi1.new("struct ab *", [10])
+        foo2 = ffi .new("struct ab *", [20, 30])
+        assert foo1.x == 10
+        assert foo2.a == 20
+        assert foo2.b == 30
+
+    def test_include_struct_union_enum_typedef(self):
+        ffi1, CCODE = construction_params
+        ffi2 = cffi.FFI()
+        ffi2.include(ffi1)
+        outputfilename = recompile(ffi2,
+                                   "test_include_struct_union_enum_typedef",
+                                   CCODE, tmpdir=str(udir))
+        module = imp.load_dynamic("test_include_struct_union_enum_typedef",
+                                  outputfilename)
+        ffi2 = module.ffi
+        #
+        p = ffi2.new("struct nonpacked *", [b'A', -43141])
+        assert p.a == b'A'
+        assert p.b == -43141
+        #
+        p = ffi.new("union simple_u *", [-52525])
+        assert p.a == -52525
+        #
+        p = ffi.cast("enum foq", 2)
+        assert ffi.string(p) == "cffiCC0"
+        assert ffi2.sizeof("char[cffiCC0]") == 2
+        #
+        p = ffi.new("anon_foo_t *", [-52526])
+        assert p.a == -52526
+        p = ffi.new("named_foo_p", [-52527])
+        assert p.a == -52527
+
+    def test_struct_packed(self):
+        # struct nonpacked { char a; int b; };
+        # struct is_packed { char a; int b; } __attribute__((packed));
+        assert ffi.sizeof("struct nonpacked") == 8
+        assert ffi.sizeof("struct is_packed") == 5
+        assert ffi.alignof("struct nonpacked") == 4
+        assert ffi.alignof("struct is_packed") == 1
+        s = ffi.new("struct is_packed[2]")
+        s[0].b = 42623381
+        s[0].a = b'X'
+        s[1].b = -4892220
+        s[1].a = b'Y'
+        assert s[0].b == 42623381
+        assert s[0].a == b'X'
+        assert s[1].b == -4892220
+        assert s[1].a == b'Y'
+
+    def test_not_supported_bitfield_in_result(self):
+        # struct ints_and_bitfield { int a,b,c,d,e; int x:1; };
+        e = py.test.raises(NotImplementedError, ffi.callback,
+                           "struct ints_and_bitfield foo(void)", lambda: 42)
+        assert str(e.value) == ("struct ints_and_bitfield(*)(): "
+            "callback with unsupported argument or return type or with '...'")
+
+    def test_inspecttype(self):
+        assert ffi.typeof("long").kind == "primitive"
+        assert ffi.typeof("long(*)(long, long**, ...)").cname == (
+            "long(*)(long, long * *, ...)")
+        assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True
+
+    def test_new_handle(self):
+        o = [2, 3, 4]
+        p = ffi.new_handle(o)
+        assert ffi.typeof(p) == ffi.typeof("void *")
+        assert ffi.from_handle(p) is o
+        assert ffi.from_handle(ffi.cast("char *", p)) is o
+        py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL)
+
+    def test_struct_array_no_length(self):
+        # struct array_no_length { int x; int a[]; };
+        p = ffi.new("struct array_no_length *", [100, [200, 300, 400]])
+        assert p.x == 100
+        assert ffi.typeof(p.a) is ffi.typeof("int[]")   # length available
+        assert p.a[0] == 200
+        assert p.a[1] == 300
+        assert p.a[2] == 400
+        assert len(p.a) == 3
+        assert list(p.a) == [200, 300, 400]
+        q = ffi.cast("struct array_no_length *", p)
+        assert ffi.typeof(q.a) is ffi.typeof("int *")   # no length available
+        assert q.a[0] == 200
+        assert q.a[1] == 300
+        assert q.a[2] == 400
+        py.test.raises(TypeError, len, q.a)
+        py.test.raises(TypeError, list, q.a)
+
+    def test_all_primitives(self):
+        assert set(PRIMITIVE_TO_INDEX) == set([
+            "char",
+            "short",
+            "int",
+            "long",
+            "long long",
+            "signed char",
+            "unsigned char",
+            "unsigned short",
+            "unsigned int",
+            "unsigned long",
+            "unsigned long long",
+            "float",
+            "double",
+            "long double",
+            "wchar_t",
+            "char16_t",
+            "char32_t",
+            "_Bool",
+            "int8_t",
+            "uint8_t",
+            "int16_t",
+            "uint16_t",
+            "int32_t",
+            "uint32_t",
+            "int64_t",
+            "uint64_t",
+            "int_least8_t",
+            "uint_least8_t",
+            "int_least16_t",
+            "uint_least16_t",
+            "int_least32_t",
+            "uint_least32_t",
+            "int_least64_t",
+            "uint_least64_t",
+            "int_fast8_t",
+            "uint_fast8_t",
+            "int_fast16_t",
+            "uint_fast16_t",
+            "int_fast32_t",
+            "uint_fast32_t",
+            "int_fast64_t",
+            "uint_fast64_t",
+            "intptr_t",
+            "uintptr_t",
+            "intmax_t",
+            "uintmax_t",
+            "ptrdiff_t",
+            "size_t",
+            "ssize_t",
+            'float _Complex',
+            'double _Complex',
+            ])
+        for name in PRIMITIVE_TO_INDEX:
+            x = ffi.sizeof(name)
+            assert 1 <= x <= 16
+
+    def test_emit_c_code(self):
+        ffi = cffi.FFI()
+        ffi.set_source("foobar", "??")
+        c_file = str(udir.join('test_emit_c_code'))
+        ffi.emit_c_code(c_file)
+        assert os.path.isfile(c_file)
+
+    def test_import_from_lib(self):
+        ffi2 = cffi.FFI()
+        ffi2.cdef("int myfunc(int); int myvar;\n#define MYFOO ...\n")
+        outputfilename = recompile(ffi2, "_test_import_from_lib",
+                                   "int myfunc(int x) { return x + 1; }\n"
+                                   "int myvar = -5;\n"
+                                   "#define MYFOO 42", tmpdir=str(udir))
+        imp.load_dynamic("_test_import_from_lib", outputfilename)
+        from _test_import_from_lib.lib import myfunc, myvar, MYFOO
+        assert MYFOO == 42
+        assert myfunc(43) == 44
+        assert myvar == -5     # but can't be changed, so not very useful
+        py.test.raises(ImportError, "from _test_import_from_lib.lib import bar")
+        d = {}
+        exec("from _test_import_from_lib.lib import *", d)
+        assert (set(key for key in d if not key.startswith('_')) ==
+                set(['myfunc', 'MYFOO']))
+        #
+        # also test "import *" on the module itself, which should be
+        # equivalent to "import ffi, lib"
+        d = {}
+        exec("from _test_import_from_lib import *", d)
+        assert (sorted([x for x in d.keys() if not x.startswith('__')]) ==
+                ['ffi', 'lib'])
+
+    def test_char16_t(self):
+        x = ffi.new("char16_t[]", 5)
+        assert len(x) == 5 and ffi.sizeof(x) == 10
+        x[2] = u+'\u1324'
+        assert x[2] == u+'\u1324'
+        y = ffi.new("char16_t[]", u+'\u1234\u5678')
+        assert len(y) == 3
+        assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00']
+        assert ffi.string(y) == u+'\u1234\u5678'
+        z = ffi.new("char16_t[]", u+'\U00012345')
+        assert len(z) == 3
+        assert list(z) == [u+'\ud808', u+'\udf45', u+'\x00']
+        assert ffi.string(z) == u+'\U00012345'
+
+    def test_char32_t(self):
+        x = ffi.new("char32_t[]", 5)
+        assert len(x) == 5 and ffi.sizeof(x) == 20
+        x[3] = u+'\U00013245'
+        assert x[3] == u+'\U00013245'
+        y = ffi.new("char32_t[]", u+'\u1234\u5678')
+        assert len(y) == 3
+        assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00']
+        z = ffi.new("char32_t[]", u+'\U00012345')
+        assert len(z) == 2
+        assert list(z) == [u+'\U00012345', u+'\x00'] # maybe a 2-unichars strin
+        assert ffi.string(z) == u+'\U00012345'
diff --git a/testing/cffi1/test_parse_c_type.py b/testing/cffi1/test_parse_c_type.py
new file mode 100644
index 0000000..a9f5fcb
--- /dev/null
+++ b/testing/cffi1/test_parse_c_type.py
@@ -0,0 +1,372 @@
+import sys, re, os, py
+import cffi
+from cffi import cffi_opcode
+
+if '__pypy__' in sys.builtin_module_names:
+    try:
+        # pytest >= 4.0
+        py.test.skip("not available on pypy", allow_module_level=True)
+    except TypeError:
+        # older pytest
+        py.test.skip("not available on pypy")
+
+cffi_dir = os.path.dirname(cffi_opcode.__file__)
+
+r_macro = re.compile(r"#define \w+[(][^\n]*|#include [^\n]*")
+r_define = re.compile(r"(#define \w+) [^\n]*")
+r_ifdefs = re.compile(r"(#ifdef |#endif)[^\n]*")
+header = open(os.path.join(cffi_dir, 'parse_c_type.h')).read()
+header = r_macro.sub(r"", header)
+header = r_define.sub(r"\1 ...", header)
+header = r_ifdefs.sub(r"", header)
+
+ffi = cffi.FFI()
+ffi.cdef(header)
+
+lib = ffi.verify(
+        open(os.path.join(cffi_dir, '..', 'c', 'parse_c_type.c')).read() + """
+static const char *get_common_type(const char *search, size_t search_len) {
+    return NULL;
+}
+""",    include_dirs=[cffi_dir])
+
+class ParseError(Exception):
+    pass
+
+struct_names = ["bar_s", "foo", "foo_", "foo_s", "foo_s1", "foo_s12"]
+assert struct_names == sorted(struct_names)
+
+enum_names = ["ebar_s", "efoo", "efoo_", "efoo_s", "efoo_s1", "efoo_s12"]
+assert enum_names == sorted(enum_names)
+
+identifier_names = ["id", "id0", "id05", "id05b", "tail"]
+assert identifier_names == sorted(identifier_names)
+
+global_names = ["FIVE", "NEG", "ZERO"]
+assert global_names == sorted(global_names)
+
+ctx = ffi.new("struct _cffi_type_context_s *")
+c_struct_names = [ffi.new("char[]", _n.encode('ascii')) for _n in struct_names]
+ctx_structs = ffi.new("struct _cffi_struct_union_s[]", len(struct_names))
+for _i in range(len(struct_names)):
+    ctx_structs[_i].name = c_struct_names[_i]
+ctx_structs[3].flags = lib._CFFI_F_UNION
+ctx.struct_unions = ctx_structs
+ctx.num_struct_unions = len(struct_names)
+
+c_enum_names = [ffi.new("char[]", _n.encode('ascii')) for _n in enum_names]
+ctx_enums = ffi.new("struct _cffi_enum_s[]", len(enum_names))
+for _i in range(len(enum_names)):
+    ctx_enums[_i].name = c_enum_names[_i]
+ctx.enums = ctx_enums
+ctx.num_enums = len(enum_names)
+
+c_identifier_names = [ffi.new("char[]", _n.encode('ascii'))
+                      for _n in identifier_names]
+ctx_identifiers = ffi.new("struct _cffi_typename_s[]", len(identifier_names))
+for _i in range(len(identifier_names)):
+    ctx_identifiers[_i].name = c_identifier_names[_i]
+    ctx_identifiers[_i].type_index = 100 + _i
+ctx.typenames = ctx_identifiers
+ctx.num_typenames = len(identifier_names)
+
+@ffi.callback("int(unsigned long long *)")
+def fetch_constant_five(p):
+    p[0] = 5
+    return 0
+@ffi.callback("int(unsigned long long *)")
+def fetch_constant_zero(p):
+    p[0] = 0
+    return 1
+@ffi.callback("int(unsigned long long *)")
+def fetch_constant_neg(p):
+    p[0] = 123321
+    return 1
+
+ctx_globals = ffi.new("struct _cffi_global_s[]", len(global_names))
+c_glob_names = [ffi.new("char[]", _n.encode('ascii')) for _n in global_names]
+for _i, _fn in enumerate([fetch_constant_five,
+                          fetch_constant_neg,
+                          fetch_constant_zero]):
+    ctx_globals[_i].name = c_glob_names[_i]
+    ctx_globals[_i].address = _fn
+    ctx_globals[_i].type_op = ffi.cast("_cffi_opcode_t",
+                                       cffi_opcode.OP_CONSTANT_INT if _i != 1
+                                       else cffi_opcode.OP_ENUM)
+ctx.globals = ctx_globals
+ctx.num_globals = len(global_names)
+
+
+def parse(input):
+    out = ffi.new("_cffi_opcode_t[]", 100)
+    info = ffi.new("struct _cffi_parse_info_s *")
+    info.ctx = ctx
+    info.output = out
+    info.output_size = len(out)
+    for j in range(len(out)):
+        out[j] = ffi.cast("void *", -424242)
+    res = lib.parse_c_type(info, input.encode('ascii'))
+    if res < 0:
+        raise ParseError(ffi.string(info.error_message).decode('ascii'),
+                         info.error_location)
+    assert 0 <= res < len(out)
+    result = []
+    for j in range(len(out)):
+        if out[j] == ffi.cast("void *", -424242):
+            assert res < j
+            break
+        i = int(ffi.cast("intptr_t", out[j]))
+        if j == res:
+            result.append('->')
+        result.append(i)
+    return result
+
+def parsex(input):
+    result = parse(input)
+    def str_if_int(x):
+        if isinstance(x, str):
+            return x
+        return '%d,%d' % (x & 255, x >> 8)
+    return '  '.join(map(str_if_int, result))
+
+def parse_error(input, expected_msg, expected_location):
+    e = py.test.raises(ParseError, parse, input)
+    assert e.value.args[0] == expected_msg
+    assert e.value.args[1] == expected_location
+
+def make_getter(name):
+    opcode = getattr(lib, '_CFFI_OP_' + name)
+    def getter(value):
+        return opcode | (value << 8)
+    return getter
+
+Prim = make_getter('PRIMITIVE')
+Pointer = make_getter('POINTER')
+Array = make_getter('ARRAY')
+OpenArray = make_getter('OPEN_ARRAY')
+NoOp = make_getter('NOOP')
+Func = make_getter('FUNCTION')
+FuncEnd = make_getter('FUNCTION_END')
+Struct = make_getter('STRUCT_UNION')
+Enum = make_getter('ENUM')
+Typename = make_getter('TYPENAME')
+
+
+def test_simple():
+    for simple_type, expected in [
+            ("int", lib._CFFI_PRIM_INT),
+            ("signed int", lib._CFFI_PRIM_INT),
+            ("  long  ", lib._CFFI_PRIM_LONG),
+            ("long int", lib._CFFI_PRIM_LONG),
+            ("unsigned short", lib._CFFI_PRIM_USHORT),
+            ("long double", lib._CFFI_PRIM_LONGDOUBLE),
+            (" float  _Complex", lib._CFFI_PRIM_FLOATCOMPLEX),
+            ("double _Complex ", lib._CFFI_PRIM_DOUBLECOMPLEX),
+            ]:
+        assert parse(simple_type) == ['->', Prim(expected)]
+
+def test_array():
+    assert parse("int[5]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5]
+    assert parse("int[]") == [Prim(lib._CFFI_PRIM_INT), '->', OpenArray(0)]
+    assert parse("int[5][8]") == [Prim(lib._CFFI_PRIM_INT),
+                                  '->', Array(3),
+                                  5,
+                                  Array(0),
+                                  8]
+    assert parse("int[][8]") == [Prim(lib._CFFI_PRIM_INT),
+                                 '->', OpenArray(2),
+                                 Array(0),
+                                 8]
+
+def test_pointer():
+    assert parse("int*") == [Prim(lib._CFFI_PRIM_INT), '->', Pointer(0)]
+    assert parse("int***") == [Prim(lib._CFFI_PRIM_INT),
+                               Pointer(0), Pointer(1), '->', Pointer(2)]
+
+def test_grouping():
+    assert parse("int*[]") == [Prim(lib._CFFI_PRIM_INT),
+                               Pointer(0), '->', OpenArray(1)]
+    assert parse("int**[][8]") == [Prim(lib._CFFI_PRIM_INT),
+                                   Pointer(0), Pointer(1),
+                                   '->', OpenArray(4), Array(2), 8]
+    assert parse("int(*)[]") == [Prim(lib._CFFI_PRIM_INT),
+                                 NoOp(3), '->', Pointer(1), OpenArray(0)]
+    assert parse("int(*)[][8]") == [Prim(lib._CFFI_PRIM_INT),
+                                    NoOp(3), '->', Pointer(1),
+                                    OpenArray(4), Array(0), 8]
+    assert parse("int**(**)") == [Prim(lib._CFFI_PRIM_INT),
+                                  Pointer(0), Pointer(1),
+                                  NoOp(2), Pointer(3), '->', Pointer(4)]
+    assert parse("int**(**)[]") == [Prim(lib._CFFI_PRIM_INT),
+                                    Pointer(0), Pointer(1),
+                                    NoOp(6), Pointer(3), '->', Pointer(4),
+                                    OpenArray(2)]
+
+def test_simple_function():
+    assert parse("int()") == [Prim(lib._CFFI_PRIM_INT),
+                              '->', Func(0), FuncEnd(0), 0]
+    assert parse("int(int)") == [Prim(lib._CFFI_PRIM_INT),
+                                 '->', Func(0), NoOp(4), FuncEnd(0),
+                                 Prim(lib._CFFI_PRIM_INT)]
+    assert parse("int(long, char)") == [
+                                 Prim(lib._CFFI_PRIM_INT),
+                                 '->', Func(0), NoOp(5), NoOp(6), FuncEnd(0),
+                                 Prim(lib._CFFI_PRIM_LONG),
+                                 Prim(lib._CFFI_PRIM_CHAR)]
+    assert parse("int(int*)") == [Prim(lib._CFFI_PRIM_INT),
+                                  '->', Func(0), NoOp(5), FuncEnd(0),
+                                  Prim(lib._CFFI_PRIM_INT),
+                                  Pointer(4)]
+    assert parse("int*(void)") == [Prim(lib._CFFI_PRIM_INT),
+                                   Pointer(0),
+                                   '->', Func(1), FuncEnd(0), 0]
+    assert parse("int(int, ...)") == [Prim(lib._CFFI_PRIM_INT),
+                                      '->', Func(0), NoOp(5), FuncEnd(1), 0,
+                                      Prim(lib._CFFI_PRIM_INT)]
+
+def test_internal_function():
+    assert parse("int(*)()") == [Prim(lib._CFFI_PRIM_INT),
+                                 NoOp(3), '->', Pointer(1),
+                                 Func(0), FuncEnd(0), 0]
+    assert parse("int(*())[]") == [Prim(lib._CFFI_PRIM_INT),
+                                   NoOp(6), Pointer(1),
+                                   '->', Func(2), FuncEnd(0), 0,
+                                   OpenArray(0)]
+    assert parse("int(char(*)(long, short))") == [
+        Prim(lib._CFFI_PRIM_INT),
+        '->', Func(0), NoOp(6), FuncEnd(0),
+        Prim(lib._CFFI_PRIM_CHAR),
+        NoOp(7), Pointer(5),
+        Func(4), NoOp(11), NoOp(12), FuncEnd(0),
+        Prim(lib._CFFI_PRIM_LONG),
+        Prim(lib._CFFI_PRIM_SHORT)]
+
+def test_fix_arg_types():
+    assert parse("int(char(long, short))") == [
+        Prim(lib._CFFI_PRIM_INT),
+        '->', Func(0), Pointer(5), FuncEnd(0),
+        Prim(lib._CFFI_PRIM_CHAR),
+        Func(4), NoOp(9), NoOp(10), FuncEnd(0),
+        Prim(lib._CFFI_PRIM_LONG),
+        Prim(lib._CFFI_PRIM_SHORT)]
+    assert parse("int(char[])") == [
+        Prim(lib._CFFI_PRIM_INT),
+        '->', Func(0), Pointer(4), FuncEnd(0),
+        Prim(lib._CFFI_PRIM_CHAR),
+        OpenArray(4)]
+
+def test_enum():
+    for i in range(len(enum_names)):
+        assert parse("enum %s" % (enum_names[i],)) == ['->', Enum(i)]
+        assert parse("enum %s*" % (enum_names[i],)) == [Enum(i),
+                                                        '->', Pointer(0)]
+
+def test_error():
+    parse_error("short short int", "'short' after another 'short' or 'long'", 6)
+    parse_error("long long long", "'long long long' is too long", 10)
+    parse_error("short long", "'long' after 'short'", 6)
+    parse_error("signed unsigned int", "multiple 'signed' or 'unsigned'", 7)
+    parse_error("unsigned signed int", "multiple 'signed' or 'unsigned'", 9)
+    parse_error("long char", "invalid combination of types", 5)
+    parse_error("short char", "invalid combination of types", 6)
+    parse_error("signed void", "invalid combination of types", 7)
+    parse_error("unsigned struct", "invalid combination of types", 9)
+    #
+    parse_error("", "identifier expected", 0)
+    parse_error("]", "identifier expected", 0)
+    parse_error("*", "identifier expected", 0)
+    parse_error("int ]**", "unexpected symbol", 4)
+    parse_error("char char", "unexpected symbol", 5)
+    parse_error("int(int]", "expected ')'", 7)
+    parse_error("int(*]", "expected ')'", 5)
+    parse_error("int(]", "identifier expected", 4)
+    parse_error("int[?]", "expected a positive integer constant", 4)
+    parse_error("int[24)", "expected ']'", 6)
+    parse_error("struct", "struct or union name expected", 6)
+    parse_error("struct 24", "struct or union name expected", 7)
+    parse_error("int[5](*)", "unexpected symbol", 6)
+    parse_error("int a(*)", "identifier expected", 6)
+    parse_error("int[123456789012345678901234567890]", "number too large", 4)
+    #
+    parse_error("_Complex", "identifier expected", 0)
+    parse_error("int _Complex", "_Complex type combination unsupported", 4)
+    parse_error("long double _Complex", "_Complex type combination unsupported",
+                12)
+
+def test_number_too_large():
+    num_max = sys.maxsize
+    assert parse("char[%d]" % num_max) == [Prim(lib._CFFI_PRIM_CHAR),
+                                          '->', Array(0), num_max]
+    parse_error("char[%d]" % (num_max + 1), "number too large", 5)
+
+def test_complexity_limit():
+    parse_error("int" + "[]" * 2500, "internal type complexity limit reached",
+                202)
+
+def test_struct():
+    for i in range(len(struct_names)):
+        if i == 3:
+            tag = "union"
+        else:
+            tag = "struct"
+        assert parse("%s %s" % (tag, struct_names[i])) == ['->', Struct(i)]
+        assert parse("%s %s*" % (tag, struct_names[i])) == [Struct(i),
+                                                            '->', Pointer(0)]
+
+def test_exchanging_struct_union():
+    parse_error("union %s" % (struct_names[0],),
+                "wrong kind of tag: struct vs union", 6)
+    parse_error("struct %s" % (struct_names[3],),
+                "wrong kind of tag: struct vs union", 7)
+
+def test_identifier():
+    for i in range(len(identifier_names)):
+        assert parse("%s" % (identifier_names[i])) == ['->', Typename(i)]
+        assert parse("%s*" % (identifier_names[i])) == [Typename(i),
+                                                        '->', Pointer(0)]
+
+def test_cffi_opcode_sync():
+    import cffi.model
+    for name in dir(lib):
+        if name.startswith('_CFFI_'):
+            assert getattr(cffi_opcode, name[6:]) == getattr(lib, name)
+    assert sorted(cffi_opcode.PRIMITIVE_TO_INDEX.keys()) == (
+        sorted(cffi.model.PrimitiveType.ALL_PRIMITIVE_TYPES.keys()))
+
+def test_array_length_from_constant():
+    parse_error("int[UNKNOWN]", "expected a positive integer constant", 4)
+    assert parse("int[FIVE]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5]
+    assert parse("int[ZERO]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 0]
+    parse_error("int[NEG]", "expected a positive integer constant", 4)
+
+def test_various_constant_exprs():
+    def array(n):
+        return [Prim(lib._CFFI_PRIM_CHAR), '->', Array(0), n]
+    assert parse("char[21]") == array(21)
+    assert parse("char[0x10]") == array(16)
+    assert parse("char[0X21]") == array(33)
+    assert parse("char[0Xb]") == array(11)
+    assert parse("char[0x1C]") == array(0x1C)
+    assert parse("char[0xc6]") == array(0xC6)
+    assert parse("char[010]") == array(8)
+    assert parse("char[021]") == array(17)
+    parse_error("char[08]", "invalid number", 5)
+    parse_error("char[1C]", "invalid number", 5)
+    parse_error("char[0C]", "invalid number", 5)
+    # not supported (really obscure):
+    #    "char[+5]"
+    #    "char['A']"
+
+def test_stdcall_cdecl():
+    assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT),
+                                           '->', Func(0), NoOp(4), FuncEnd(2),
+                                           Prim(lib._CFFI_PRIM_INT)]
+    assert parse("int __stdcall func(int)") == parse("int __stdcall(int)")
+    assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT),
+                                            NoOp(3), '->', Pointer(1),
+                                            Func(0), FuncEnd(2), 0]
+    assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()")
+    parse_error("__stdcall int", "identifier expected", 0)
+    parse_error("__cdecl int", "identifier expected", 0)
+    parse_error("int __stdcall", "expected '('", 13)
+    parse_error("int __cdecl", "expected '('", 11)
diff --git a/testing/cffi1/test_pkgconfig.py b/testing/cffi1/test_pkgconfig.py
new file mode 100644
index 0000000..c725cca
--- /dev/null
+++ b/testing/cffi1/test_pkgconfig.py
@@ -0,0 +1,94 @@
+import sys
+import subprocess
+import py
+import cffi.pkgconfig as pkgconfig
+from cffi import PkgConfigError
+
+
+def mock_call(libname, flag):
+    assert libname=="foobarbaz"
+    flags = {
+        "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n",
+        "--libs": "-L/usr/lib64 -lpython3.6 -shared\n",
+    }
+    return flags[flag]
+
+
+def test_merge_flags():
+    d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []}
+    d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]}
+
+    pkgconfig.merge_flags(d1, d2)
+    assert d1 == {
+        "ham": [1, 2, 3],
+        "spam" : ["a", "b", "c", "spam", "spam", "spam"],
+        "bar" : ["b", "a", "z"],
+        "foo" : []}
+
+
+def test_pkgconfig():
+    assert pkgconfig.flags_from_pkgconfig([]) == {}
+
+    saved = pkgconfig.call
+    try:
+        pkgconfig.call = mock_call
+        flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"])
+    finally:
+        pkgconfig.call = saved
+    assert flags == {
+        'include_dirs': ['/usr/include/python3.6m'],
+        'library_dirs': ['/usr/lib64'],
+        'libraries': ['python3.6'],
+        'define_macros': [('ABCD', None), ('CFFI_TEST', '1')],
+        'extra_compile_args': ['-O42'],
+        'extra_link_args': ['-shared']
+    }
+
+class mock_subprocess:
+    PIPE = Ellipsis
+    class Popen:
+        def __init__(self, cmd, stdout, stderr):
+            if mock_subprocess.RESULT is None:
+                raise OSError("oops can't run")
+            assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo']
+        def communicate(self):
+            bout, berr, rc = mock_subprocess.RESULT
+            self.returncode = rc
+            return bout, berr
+
+def test_call():
+    saved = pkgconfig.subprocess
+    try:
+        pkgconfig.subprocess = mock_subprocess
+
+        mock_subprocess.RESULT = None
+        e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags")
+        assert str(e.value) == "cannot run pkg-config: oops can't run"
+
+        mock_subprocess.RESULT = b"", "Foo error!\n", 1
+        e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags")
+        assert str(e.value) == "Foo error!"
+
+        mock_subprocess.RESULT = b"abc\\def\n", "", 0
+        e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags")
+        assert str(e.value).startswith("pkg-config --cflags libfoo returned an "
+                                       "unsupported backslash-escaped output:")
+
+        mock_subprocess.RESULT = b"abc def\n", "", 0
+        result = pkgconfig.call("libfoo", "--cflags")
+        assert result == "abc def\n"
+
+        mock_subprocess.RESULT = b"abc def\n", "", 0
+        result = pkgconfig.call("libfoo", "--cflags")
+        assert result == "abc def\n"
+
+        if sys.version_info >= (3,):
+            mock_subprocess.RESULT = b"\xff\n", "", 0
+            e = py.test.raises(PkgConfigError, pkgconfig.call,
+                               "libfoo", "--cflags", encoding="utf-8")
+            assert str(e.value) == (
+                "pkg-config --cflags libfoo returned bytes that cannot be "
+                "decoded with encoding 'utf-8':\nb'\\xff\\n'")
+
+    finally:
+        pkgconfig.subprocess = saved
diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py
new file mode 100644
index 0000000..377c29b
--- /dev/null
+++ b/testing/cffi1/test_re_python.py
@@ -0,0 +1,256 @@
+import sys, os
+import py
+from cffi import FFI
+from cffi import recompiler, ffiplatform, VerificationMissing
+from testing.udir import udir
+from testing.support import u
+
+
+def setup_module(mod):
+    SRC = """
+    #include <string.h>
+    #define FOOBAR (-42)
+    static const int FOOBAZ = -43;
+    #define BIGPOS 420000000000L
+    #define BIGNEG -420000000000L
+    int add42(int x) { return x + 42; }
+    int add43(int x, ...) { return x; }
+    int globalvar42 = 1234;
+    const int globalconst42 = 4321;
+    const char *const globalconsthello = "hello";
+    struct foo_s;
+    typedef struct bar_s { int x; signed char a[]; } bar_t;
+    enum foo_e { AA, BB, CC };
+
+    void init_test_re_python(void) { }      /* windows hack */
+    void PyInit__test_re_python(void) { }   /* windows hack */
+    """
+    tmpdir = udir.join('test_re_python')
+    tmpdir.ensure(dir=1)
+    c_file = tmpdir.join('_test_re_python.c')
+    c_file.write(SRC)
+    ext = ffiplatform.get_extension(
+        str(c_file),
+        '_test_re_python',
+        export_symbols=['add42', 'add43', 'globalvar42',
+                        'globalconst42', 'globalconsthello']
+    )
+    outputfilename = ffiplatform.compile(str(tmpdir), ext)
+
+    # test with a non-ascii char
+    ofn, oext = os.path.splitext(outputfilename)
+    if sys.platform == "win32":
+        unicode_name = ofn + (u+'\u03be') + oext
+    else:
+        unicode_name = ofn + (u+'\xe9') + oext
+        try:
+            unicode_name.encode(sys.getfilesystemencoding())
+        except UnicodeEncodeError:
+            unicode_name = None
+    if unicode_name is not None:
+        print(repr(outputfilename) + ' ==> ' + repr(unicode_name))
+        os.rename(outputfilename, unicode_name)
+        outputfilename = unicode_name
+
+    mod.extmod = outputfilename
+    mod.tmpdir = tmpdir
+    #
+    ffi = FFI()
+    ffi.cdef("""
+    #define FOOBAR -42
+    static const int FOOBAZ = -43;
+    #define BIGPOS 420000000000L
+    #define BIGNEG -420000000000L
+    int add42(int);
+    int add43(int, ...);
+    int globalvar42;
+    const int globalconst42;
+    const char *const globalconsthello = "hello";
+    int no_such_function(int);
+    int no_such_globalvar;
+    struct foo_s;
+    typedef struct bar_s { int x; signed char a[]; } bar_t;
+    enum foo_e { AA, BB, CC };
+    int strlen(const char *);
+    struct with_union { union { int a; char b; }; };
+    union with_struct { struct { int a; char b; }; };
+    struct NVGcolor { union { float rgba[4]; struct { float r,g,b,a; }; }; };
+    """)
+    ffi.set_source('re_python_pysrc', None)
+    ffi.emit_python_code(str(tmpdir.join('re_python_pysrc.py')))
+    mod.original_ffi = ffi
+    #
+    sys.path.insert(0, str(tmpdir))
+
+
+def test_constant():
+    from re_python_pysrc import ffi
+    assert ffi.integer_const('FOOBAR') == -42
+    assert ffi.integer_const('FOOBAZ') == -43
+
+def test_large_constant():
+    from re_python_pysrc import ffi
+    assert ffi.integer_const('BIGPOS') == 420000000000
+    assert ffi.integer_const('BIGNEG') == -420000000000
+
+def test_function():
+    import _cffi_backend
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    assert lib.add42(-10) == 32
+    assert type(lib.add42) is _cffi_backend.FFI.CData
+
+def test_function_with_varargs():
+    import _cffi_backend
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod, 0)
+    assert lib.add43(45, ffi.cast("int", -5)) == 45
+    assert type(lib.add43) is _cffi_backend.FFI.CData
+
+def test_dlopen_none():
+    import _cffi_backend
+    from re_python_pysrc import ffi
+    name = None
+    if sys.platform == 'win32':
+        import ctypes.util
+        name = ctypes.util.find_msvcrt()
+        if name is None:
+            py.test.skip("dlopen(None) cannot work on Windows with Python 3")
+    lib = ffi.dlopen(name)
+    assert lib.strlen(b"hello") == 5
+
+def test_dlclose():
+    import _cffi_backend
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    ffi.dlclose(lib)
+    if type(extmod) is not str:   # unicode, on python 2
+        str_extmod = extmod.encode('utf-8')
+    else:
+        str_extmod = extmod
+    e = py.test.raises(ffi.error, getattr, lib, 'add42')
+    assert str(e.value) == (
+        "library '%s' has been closed" % (str_extmod,))
+    ffi.dlclose(lib)   # does not raise
+
+def test_constant_via_lib():
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    assert lib.FOOBAR == -42
+    assert lib.FOOBAZ == -43
+
+def test_opaque_struct():
+    from re_python_pysrc import ffi
+    ffi.cast("struct foo_s *", 0)
+    py.test.raises(TypeError, ffi.new, "struct foo_s *")
+
+def test_nonopaque_struct():
+    from re_python_pysrc import ffi
+    for p in [ffi.new("struct bar_s *", [5, b"foobar"]),
+              ffi.new("bar_t *", [5, b"foobar"])]:
+        assert p.x == 5
+        assert p.a[0] == ord('f')
+        assert p.a[5] == ord('r')
+
+def test_enum():
+    from re_python_pysrc import ffi
+    assert ffi.integer_const("BB") == 1
+    e = ffi.cast("enum foo_e", 2)
+    assert ffi.string(e) == "CC"
+
+def test_include_1():
+    sub_ffi = FFI()
+    sub_ffi.cdef("static const int k2 = 121212;")
+    sub_ffi.include(original_ffi)
+    assert 'macro FOOBAR' in original_ffi._parser._declarations
+    assert 'macro FOOBAZ' in original_ffi._parser._declarations
+    sub_ffi.set_source('re_python_pysrc', None)
+    sub_ffi.emit_python_code(str(tmpdir.join('_re_include_1.py')))
+    #
+    if sys.version_info[:2] >= (3, 3):
+        import importlib
+        importlib.invalidate_caches()  # issue 197 (but can't reproduce myself)
+    #
+    from _re_include_1 import ffi
+    assert ffi.integer_const('FOOBAR') == -42
+    assert ffi.integer_const('FOOBAZ') == -43
+    assert ffi.integer_const('k2') == 121212
+    lib = ffi.dlopen(extmod)     # <- a random unrelated library would be fine
+    assert lib.FOOBAR == -42
+    assert lib.FOOBAZ == -43
+    assert lib.k2 == 121212
+    #
+    p = ffi.new("bar_t *", [5, b"foobar"])
+    assert p.a[4] == ord('a')
+
+def test_global_var():
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    assert lib.globalvar42 == 1234
+    p = ffi.addressof(lib, 'globalvar42')
+    lib.globalvar42 += 5
+    assert p[0] == 1239
+    p[0] -= 1
+    assert lib.globalvar42 == 1238
+
+def test_global_const_int():
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    assert lib.globalconst42 == 4321
+    py.test.raises(AttributeError, ffi.addressof, lib, 'globalconst42')
+
+def test_global_const_nonint():
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    assert ffi.string(lib.globalconsthello, 8) == b"hello"
+    py.test.raises(AttributeError, ffi.addressof, lib, 'globalconsthello')
+
+def test_rtld_constants():
+    from re_python_pysrc import ffi
+    ffi.RTLD_NOW    # check that we have the attributes
+    ffi.RTLD_LAZY
+    ffi.RTLD_GLOBAL
+
+def test_no_such_function_or_global_var():
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    e = py.test.raises(ffi.error, getattr, lib, 'no_such_function')
+    assert str(e.value).startswith(
+        "symbol 'no_such_function' not found in library '")
+    e = py.test.raises(ffi.error, getattr, lib, 'no_such_globalvar')
+    assert str(e.value).startswith(
+        "symbol 'no_such_globalvar' not found in library '")
+
+def test_check_version():
+    import _cffi_backend
+    e = py.test.raises(ImportError, _cffi_backend.FFI,
+                       "foobar", _version=0x2594)
+    assert str(e.value).startswith(
+        "cffi out-of-line Python module 'foobar' has unknown version")
+
+def test_partial_enum():
+    ffi = FFI()
+    ffi.cdef("enum foo { A, B, ... };")
+    ffi.set_source('test_partial_enum', None)
+    py.test.raises(VerificationMissing, ffi.emit_python_code,
+                   str(tmpdir.join('test_partial_enum.py')))
+
+def test_anonymous_union_inside_struct():
+    # based on issue #357
+    from re_python_pysrc import ffi
+    INT = ffi.sizeof("int")
+    assert ffi.offsetof("struct with_union", "a") == 0
+    assert ffi.offsetof("struct with_union", "b") == 0
+    assert ffi.sizeof("struct with_union") == INT
+    #
+    assert ffi.offsetof("union with_struct", "a") == 0
+    assert ffi.offsetof("union with_struct", "b") == INT
+    assert ffi.sizeof("union with_struct") >= INT + 1
+    #
+    FLOAT = ffi.sizeof("float")
+    assert ffi.sizeof("struct NVGcolor") == FLOAT * 4
+    assert ffi.offsetof("struct NVGcolor", "rgba") == 0
+    assert ffi.offsetof("struct NVGcolor", "r") == 0
+    assert ffi.offsetof("struct NVGcolor", "g") == FLOAT
+    assert ffi.offsetof("struct NVGcolor", "b") == FLOAT * 2
+    assert ffi.offsetof("struct NVGcolor", "a") == FLOAT * 3
diff --git a/testing/cffi1/test_realize_c_type.py b/testing/cffi1/test_realize_c_type.py
new file mode 100644
index 0000000..a1f31e6
--- /dev/null
+++ b/testing/cffi1/test_realize_c_type.py
@@ -0,0 +1,73 @@
+import py, sys
+from cffi import cffi_opcode
+
+
+def check(input, expected_output=None, expected_ffi_error=False):
+    import _cffi_backend
+    ffi = _cffi_backend.FFI()
+    if not expected_ffi_error:
+        ct = ffi.typeof(input)
+        assert isinstance(ct, ffi.CType)
+        assert ct.cname == (expected_output or input)
+    else:
+        e = py.test.raises(ffi.error, ffi.typeof, input)
+        if isinstance(expected_ffi_error, str):
+            assert str(e.value) == expected_ffi_error
+
+def test_void():
+    check("void", "void")
+    check("  void  ", "void")
+
+def test_int_star():
+    check("int")
+    check("int *")
+    check("int*", "int *")
+    check("long int", "long")
+    check("long")
+
+def test_noop():
+    check("int(*)", "int *")
+
+def test_array():
+    check("int[6]")
+
+def test_funcptr():
+    check("int(*)(long)")
+    check("int(long)", expected_ffi_error="the type 'int(long)' is a"
+          " function type, not a pointer-to-function type")
+    check("int(void)", expected_ffi_error="the type 'int()' is a"
+          " function type, not a pointer-to-function type")
+
+def test_funcptr_rewrite_args():
+    check("int(*)(int(int))", "int(*)(int(*)(int))")
+    check("int(*)(long[])", "int(*)(long *)")
+    check("int(*)(long[5])", "int(*)(long *)")
+
+def test_all_primitives():
+    for name in cffi_opcode.PRIMITIVE_TO_INDEX:
+        check(name, name)
+
+def check_func(input, expected_output=None):
+    import _cffi_backend
+    ffi = _cffi_backend.FFI()
+    ct = ffi.typeof(ffi.callback(input, lambda: None))
+    assert isinstance(ct, ffi.CType)
+    if sys.platform != 'win32' or sys.maxsize > 2**32:
+        expected_output = expected_output.replace('__stdcall *', '*')
+    assert ct.cname == expected_output
+
+def test_funcptr_stdcall():
+    check_func("int(int)", "int(*)(int)")
+    check_func("int foobar(int)", "int(*)(int)")
+    check_func("int __stdcall(int)", "int(__stdcall *)(int)")
+    check_func("int __stdcall foobar(int)", "int(__stdcall *)(int)")
+    check_func("void __cdecl(void)", "void(*)()")
+    check_func("void __cdecl foobar(void)", "void(*)()")
+    check_func("void __stdcall(void)", "void(__stdcall *)()")
+    check_func("void __stdcall foobar(long, short)",
+                   "void(__stdcall *)(long, short)")
+    check_func("void(void __cdecl(void), void __stdcall(void))",
+                   "void(*)(void(*)(), void(__stdcall *)())")
+
+def test_variadic_overrides_stdcall():
+    check("void (__stdcall*)(int, ...)", "void(*)(int, ...)")
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
new file mode 100644
index 0000000..6a31110
--- /dev/null
+++ b/testing/cffi1/test_recompiler.py
@@ -0,0 +1,2316 @@
+
+import sys, os, py
+from cffi import FFI, VerificationError, FFIError, CDefError
+from cffi import recompiler
+from testing.udir import udir
+from testing.support import u, long
+from testing.support import FdWriteCapture, StdErrCapture, _verify
+
+try:
+    import importlib
+except ImportError:
+    importlib = None
+
+
+def check_type_table(input, expected_output, included=None):
+    ffi = FFI()
+    if included:
+        ffi1 = FFI()
+        ffi1.cdef(included)
+        ffi.include(ffi1)
+    ffi.cdef(input)
+    recomp = recompiler.Recompiler(ffi, 'testmod')
+    recomp.collect_type_table()
+    assert ''.join(map(str, recomp.cffi_types)) == expected_output
+
+def verify(ffi, module_name, source, *args, **kwds):
+    no_cpp = kwds.pop('no_cpp', False)
+    kwds.setdefault('undef_macros', ['NDEBUG'])
+    module_name = '_CFFI_' + module_name
+    ffi.set_source(module_name, source)
+    if not os.environ.get('NO_CPP') and not no_cpp:   # test the .cpp mode too
+        kwds.setdefault('source_extension', '.cpp')
+        source = 'extern "C" {\n%s\n}' % (source,)
+    elif sys.platform != 'win32':
+        # add '-Werror' to the existing 'extra_compile_args' flags
+        kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) +
+                                      ['-Werror'])
+    return _verify(ffi, module_name, source, *args, **kwds)
+
+def test_set_source_no_slashes():
+    ffi = FFI()
+    py.test.raises(ValueError, ffi.set_source, "abc/def", None)
+    py.test.raises(ValueError, ffi.set_source, "abc/def", "C code")
+
+
+def test_type_table_func():
+    check_type_table("double sin(double);",
+                     "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)")
+    check_type_table("float sin(double);",
+                     "(FUNCTION 3)(PRIMITIVE 14)(FUNCTION_END 0)(PRIMITIVE 13)")
+    check_type_table("float sin(void);",
+                     "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 13)")
+    check_type_table("double sin(float); double cos(float);",
+                     "(FUNCTION 3)(PRIMITIVE 13)(FUNCTION_END 0)(PRIMITIVE 14)")
+    check_type_table("double sin(float); double cos(double);",
+                     "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)"   # cos
+                     "(FUNCTION 1)(PRIMITIVE 13)(FUNCTION_END 0)")  # sin
+    check_type_table("float sin(double); float cos(float);",
+                     "(FUNCTION 4)(PRIMITIVE 14)(FUNCTION_END 0)"   # sin
+                     "(FUNCTION 4)(PRIMITIVE 13)(FUNCTION_END 0)")  # cos
+
+def test_type_table_use_noop_for_repeated_args():
+    check_type_table("double sin(double *, double *);",
+                     "(FUNCTION 4)(POINTER 4)(NOOP 1)(FUNCTION_END 0)"
+                     "(PRIMITIVE 14)")
+    check_type_table("double sin(double *, double *, double);",
+                     "(FUNCTION 3)(POINTER 3)(NOOP 1)(PRIMITIVE 14)"
+                     "(FUNCTION_END 0)")
+
+def test_type_table_dont_use_noop_for_primitives():
+    check_type_table("double sin(double, double);",
+                     "(FUNCTION 1)(PRIMITIVE 14)(PRIMITIVE 14)(FUNCTION_END 0)")
+
+def test_type_table_funcptr_as_argument():
+    check_type_table("int sin(double(float));",
+                     "(FUNCTION 6)(PRIMITIVE 13)(FUNCTION_END 0)"
+                     "(FUNCTION 7)(POINTER 0)(FUNCTION_END 0)"
+                     "(PRIMITIVE 14)(PRIMITIVE 7)")
+
+def test_type_table_variadic_function():
+    check_type_table("int sin(int, ...);",
+                     "(FUNCTION 1)(PRIMITIVE 7)(FUNCTION_END 1)(POINTER 0)")
+
+def test_type_table_array():
+    check_type_table("int a[100];",
+                     "(PRIMITIVE 7)(ARRAY 0)(None 100)")
+
+def test_type_table_typedef():
+    check_type_table("typedef int foo_t;",
+                     "(PRIMITIVE 7)")
+
+def test_type_table_prebuilt_type():
+    check_type_table("int32_t f(void);",
+                     "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 21)")
+
+def test_type_table_struct_opaque():
+    check_type_table("struct foo_s;",
+                     "(STRUCT_UNION 0)")
+
+def test_type_table_struct():
+    check_type_table("struct foo_s { int a; long b; };",
+                     "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)")
+
+def test_type_table_union():
+    check_type_table("union foo_u { int a; long b; };",
+                     "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)")
+
+def test_type_table_struct_used():
+    check_type_table("struct foo_s { int a; long b; }; int f(struct foo_s*);",
+                     "(FUNCTION 3)(POINTER 5)(FUNCTION_END 0)"
+                     "(PRIMITIVE 7)(PRIMITIVE 9)"
+                     "(STRUCT_UNION 0)")
+
+def test_type_table_anonymous_struct_with_typedef():
+    check_type_table("typedef struct { int a; long b; } foo_t;",
+                     "(STRUCT_UNION 0)(PRIMITIVE 7)(PRIMITIVE 9)")
+
+def test_type_table_enum():
+    check_type_table("enum foo_e { AA, BB, ... };",
+                     "(ENUM 0)")
+
+def test_type_table_include_1():
+    check_type_table("foo_t sin(foo_t);",
+                     "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)",
+                     included="typedef double foo_t;")
+
+def test_type_table_include_2():
+    check_type_table("struct foo_s *sin(struct foo_s *);",
+                     "(FUNCTION 1)(POINTER 3)(FUNCTION_END 0)(STRUCT_UNION 0)",
+                     included="struct foo_s { int x, y; };")
+
+
+def test_math_sin():
+    import math
+    ffi = FFI()
+    ffi.cdef("float sin(double); double cos(double);")
+    lib = verify(ffi, 'test_math_sin', '#include <math.h>')
+    assert lib.cos(1.43) == math.cos(1.43)
+
+def test_repr_lib():
+    ffi = FFI()
+    lib = verify(ffi, 'test_repr_lib', '')
+    assert repr(lib) == "<Lib object for '_CFFI_test_repr_lib'>"
+
+def test_funcarg_ptr():
+    ffi = FFI()
+    ffi.cdef("int foo(int *);")
+    lib = verify(ffi, 'test_funcarg_ptr', 'int foo(int *p) { return *p; }')
+    assert lib.foo([-12345]) == -12345
+
+def test_funcres_ptr():
+    ffi = FFI()
+    ffi.cdef("int *foo(void);")
+    lib = verify(ffi, 'test_funcres_ptr',
+                 'int *foo(void) { static int x=-12345; return &x; }')
+    assert lib.foo()[0] == -12345
+
+def test_global_var_array():
+    ffi = FFI()
+    ffi.cdef("int a[100];")
+    lib = verify(ffi, 'test_global_var_array', 'int a[100] = { 9999 };')
+    lib.a[42] = 123456
+    assert lib.a[42] == 123456
+    assert lib.a[0] == 9999
+
+def test_verify_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef int **foo_t;")
+    lib = verify(ffi, 'test_verify_typedef', 'typedef int **foo_t;')
+    assert ffi.sizeof("foo_t") == ffi.sizeof("void *")
+
+def test_verify_typedef_dotdotdot():
+    ffi = FFI()
+    ffi.cdef("typedef ... foo_t;")
+    verify(ffi, 'test_verify_typedef_dotdotdot', 'typedef int **foo_t;')
+
+def test_verify_typedef_star_dotdotdot():
+    ffi = FFI()
+    ffi.cdef("typedef ... *foo_t;")
+    verify(ffi, 'test_verify_typedef_star_dotdotdot', 'typedef int **foo_t;')
+
+def test_global_var_int():
+    ffi = FFI()
+    ffi.cdef("int a, b, c;")
+    lib = verify(ffi, 'test_global_var_int', 'int a = 999, b, c;')
+    assert lib.a == 999
+    lib.a -= 1001
+    assert lib.a == -2
+    lib.a = -2147483648
+    assert lib.a == -2147483648
+    py.test.raises(OverflowError, "lib.a = 2147483648")
+    py.test.raises(OverflowError, "lib.a = -2147483649")
+    lib.b = 525      # try with the first access being in setattr, too
+    assert lib.b == 525
+    py.test.raises(AttributeError, "del lib.a")
+    py.test.raises(AttributeError, "del lib.c")
+    py.test.raises(AttributeError, "del lib.foobarbaz")
+
+def test_macro():
+    ffi = FFI()
+    ffi.cdef("#define FOOBAR ...")
+    lib = verify(ffi, 'test_macro', "#define FOOBAR (-6912)")
+    assert lib.FOOBAR == -6912
+    py.test.raises(AttributeError, "lib.FOOBAR = 2")
+
+def test_macro_check_value():
+    # the value '-0x80000000' in C sources does not have a clear meaning
+    # to me; it appears to have a different effect than '-2147483648'...
+    # Moreover, on 32-bits, -2147483648 is actually equal to
+    # -2147483648U, which in turn is equal to 2147483648U and so positive.
+    vals = ['42', '-42', '0x80000000', '-2147483648',
+            '0', '9223372036854775809ULL',
+            '-9223372036854775807LL']
+    if sys.maxsize <= 2**32 or sys.platform == 'win32':
+        vals.remove('-2147483648')
+    ffi = FFI()
+    cdef_lines = ['#define FOO_%d_%d %s' % (i, j, vals[i])
+                  for i in range(len(vals))
+                  for j in range(len(vals))]
+    ffi.cdef('\n'.join(cdef_lines))
+
+    verify_lines = ['#define FOO_%d_%d %s' % (i, j, vals[j])  # [j], not [i]
+                    for i in range(len(vals))
+                    for j in range(len(vals))]
+    lib = verify(ffi, 'test_macro_check_value_ok',
+                 '\n'.join(verify_lines))
+    #
+    for j in range(len(vals)):
+        c_got = int(vals[j].replace('U', '').replace('L', ''), 0)
+        c_compiler_msg = str(c_got)
+        if c_got > 0:
+            c_compiler_msg += ' (0x%x)' % (c_got,)
+        #
+        for i in range(len(vals)):
+            attrname = 'FOO_%d_%d' % (i, j)
+            if i == j:
+                x = getattr(lib, attrname)
+                assert x == c_got
+            else:
+                e = py.test.raises(ffi.error, getattr, lib, attrname)
+                assert str(e.value) == (
+                    "the C compiler says '%s' is equal to "
+                    "%s, but the cdef disagrees" % (attrname, c_compiler_msg))
+
+def test_constant():
+    ffi = FFI()
+    ffi.cdef("static const int FOOBAR;")
+    lib = verify(ffi, 'test_constant', "#define FOOBAR (-6912)")
+    assert lib.FOOBAR == -6912
+    py.test.raises(AttributeError, "lib.FOOBAR = 2")
+
+def test_check_value_of_static_const():
+    ffi = FFI()
+    ffi.cdef("static const int FOOBAR = 042;")
+    lib = verify(ffi, 'test_check_value_of_static_const',
+                 "#define FOOBAR (-6912)")
+    e = py.test.raises(ffi.error, getattr, lib, 'FOOBAR')
+    assert str(e.value) == (
+       "the C compiler says 'FOOBAR' is equal to -6912, but the cdef disagrees")
+
+def test_constant_nonint():
+    ffi = FFI()
+    ffi.cdef("static const double FOOBAR;")
+    lib = verify(ffi, 'test_constant_nonint', "#define FOOBAR (-6912.5)")
+    assert lib.FOOBAR == -6912.5
+    py.test.raises(AttributeError, "lib.FOOBAR = 2")
+
+def test_constant_ptr():
+    ffi = FFI()
+    ffi.cdef("static double *const FOOBAR;")
+    lib = verify(ffi, 'test_constant_ptr', "#define FOOBAR NULL")
+    assert lib.FOOBAR == ffi.NULL
+    assert ffi.typeof(lib.FOOBAR) == ffi.typeof("double *")
+
+def test_dir():
+    ffi = FFI()
+    ffi.cdef("int ff(int); int aa; static const int my_constant;")
+    lib = verify(ffi, 'test_dir', """
+        #define my_constant  (-45)
+        int aa;
+        int ff(int x) { return x+aa; }
+    """)
+    lib.aa = 5
+    assert dir(lib) == ['aa', 'ff', 'my_constant']
+    #
+    aaobj = lib.__dict__['aa']
+    assert not isinstance(aaobj, int)    # some internal object instead
+    assert lib.__dict__ == {
+        'ff': lib.ff,
+        'aa': aaobj,
+        'my_constant': -45}
+    lib.__dict__['ff'] = "??"
+    assert lib.ff(10) == 15
+
+def test_verify_opaque_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s;")
+    lib = verify(ffi, 'test_verify_opaque_struct', "struct foo_s;")
+    assert ffi.typeof("struct foo_s").cname == "struct foo_s"
+
+def test_verify_opaque_union():
+    ffi = FFI()
+    ffi.cdef("union foo_s;")
+    lib = verify(ffi, 'test_verify_opaque_union', "union foo_s;")
+    assert ffi.typeof("union foo_s").cname == "union foo_s"
+
+def test_verify_struct():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { int b; short a; ...; };
+                struct bar_s { struct foo_s *f; };""")
+    lib = verify(ffi, 'test_verify_struct',
+                 """struct foo_s { short a; int b; };
+                    struct bar_s { struct foo_s *f; };""")
+    ffi.typeof("struct bar_s *")
+    p = ffi.new("struct foo_s *", {'a': -32768, 'b': -2147483648})
+    assert p.a == -32768
+    assert p.b == -2147483648
+    py.test.raises(OverflowError, "p.a -= 1")
+    py.test.raises(OverflowError, "p.b -= 1")
+    q = ffi.new("struct bar_s *", {'f': p})
+    assert q.f == p
+    #
+    assert ffi.offsetof("struct foo_s", "a") == 0
+    assert ffi.offsetof("struct foo_s", "b") == 4
+    assert ffi.offsetof(u+"struct foo_s", u+"b") == 4
+    #
+    py.test.raises(TypeError, ffi.addressof, p)
+    assert ffi.addressof(p[0]) == p
+    assert ffi.typeof(ffi.addressof(p[0])) is ffi.typeof("struct foo_s *")
+    assert ffi.typeof(ffi.addressof(p, "b")) is ffi.typeof("int *")
+    assert ffi.addressof(p, "b")[0] == p.b
+
+def test_verify_exact_field_offset():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { int b; short a; };""")
+    lib = verify(ffi, 'test_verify_exact_field_offset',
+                 """struct foo_s { short a; int b; };""")
+    e = py.test.raises(ffi.error, ffi.new, "struct foo_s *", [])    # lazily
+    assert str(e.value) == ("struct foo_s: wrong offset for field 'b' (cdef "
+                       'says 0, but C compiler says 4). fix it or use "...;" '
+                       "in the cdef for struct foo_s to make it flexible")
+
+def test_type_caching():
+    ffi1 = FFI(); ffi1.cdef("struct foo_s;")
+    ffi2 = FFI(); ffi2.cdef("struct foo_s;")    # different one!
+    lib1 = verify(ffi1, 'test_type_caching_1', 'struct foo_s;')
+    lib2 = verify(ffi2, 'test_type_caching_2', 'struct foo_s;')
+    # shared types
+    assert ffi1.typeof("long") is ffi2.typeof("long")
+    assert ffi1.typeof("long**") is ffi2.typeof("long * *")
+    assert ffi1.typeof("long(*)(int, ...)") is ffi2.typeof("long(*)(int, ...)")
+    # non-shared types
+    assert ffi1.typeof("struct foo_s") is not ffi2.typeof("struct foo_s")
+    assert ffi1.typeof("struct foo_s *") is not ffi2.typeof("struct foo_s *")
+    assert ffi1.typeof("struct foo_s*(*)()") is not (
+        ffi2.typeof("struct foo_s*(*)()"))
+    assert ffi1.typeof("void(*)(struct foo_s*)") is not (
+        ffi2.typeof("void(*)(struct foo_s*)"))
+
+def test_verify_enum():
+    ffi = FFI()
+    ffi.cdef("""enum e1 { B1, A1, ... }; enum e2 { B2, A2, ... };""")
+    lib = verify(ffi, 'test_verify_enum',
+                 "enum e1 { A1, B1, C1=%d };" % sys.maxsize +
+                 "enum e2 { A2, B2, C2 };")
+    ffi.typeof("enum e1")
+    ffi.typeof("enum e2")
+    assert lib.A1 == 0
+    assert lib.B1 == 1
+    assert lib.A2 == 0
+    assert lib.B2 == 1
+    assert ffi.sizeof("enum e1") == ffi.sizeof("long")
+    assert ffi.sizeof("enum e2") == ffi.sizeof("int")
+    assert repr(ffi.cast("enum e1", 0)) == "<cdata 'enum e1' 0: A1>"
+
+def test_duplicate_enum():
+    ffi = FFI()
+    ffi.cdef("enum e1 { A1, ... }; enum e2 { A1, ... };")
+    py.test.raises(VerificationError, verify, ffi, 'test_duplicate_enum',
+                    "enum e1 { A1 }; enum e2 { B1 };")
+
+def test_dotdotdot_length_of_array_field():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[...]; int b[...]; };")
+    verify(ffi, 'test_dotdotdot_length_of_array_field',
+           "struct foo_s { int a[42]; int b[11]; };")
+    assert ffi.sizeof("struct foo_s") == (42 + 11) * 4
+    p = ffi.new("struct foo_s *")
+    assert p.a[41] == p.b[10] == 0
+    py.test.raises(IndexError, "p.a[42]")
+    py.test.raises(IndexError, "p.b[11]")
+
+def test_dotdotdot_global_array():
+    ffi = FFI()
+    ffi.cdef("int aa[...]; int bb[...];")
+    lib = verify(ffi, 'test_dotdotdot_global_array',
+                 "int aa[41]; int bb[12];")
+    assert ffi.sizeof(lib.aa) == 41 * 4
+    assert ffi.sizeof(lib.bb) == 12 * 4
+    assert lib.aa[40] == lib.bb[11] == 0
+    py.test.raises(IndexError, "lib.aa[41]")
+    py.test.raises(IndexError, "lib.bb[12]")
+
+def test_misdeclared_field_1():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[5]; };")
+    try:
+        verify(ffi, 'test_misdeclared_field_1',
+               "struct foo_s { int a[6]; };")
+    except VerificationError:
+        pass    # ok, fail during compilation already (e.g. C++)
+    else:
+        assert ffi.sizeof("struct foo_s") == 24  # found by the actual C code
+        try:
+            # lazily build the fields and boom:
+            p = ffi.new("struct foo_s *")
+            p.a
+            assert False, "should have raised"
+        except ffi.error as e:
+            assert str(e).startswith("struct foo_s: wrong size for field 'a' "
+                                     "(cdef says 20, but C compiler says 24)")
+
+def test_open_array_in_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int b; int a[]; };")
+    verify(ffi, 'test_open_array_in_struct',
+           "struct foo_s { int b; int a[]; };")
+    assert ffi.sizeof("struct foo_s") == 4
+    p = ffi.new("struct foo_s *", [5, [10, 20, 30, 40]])
+    assert p.a[2] == 30
+    assert ffi.sizeof(p) == ffi.sizeof("void *")
+    assert ffi.sizeof(p[0]) == 5 * ffi.sizeof("int")
+
+def test_math_sin_type():
+    ffi = FFI()
+    ffi.cdef("double sin(double); void *xxtestfunc();")
+    lib = verify(ffi, 'test_math_sin_type', """
+        #include <math.h>
+        void *xxtestfunc(void) { return 0; }
+    """)
+    # 'lib.sin' is typed as a <built-in method> object on lib
+    assert ffi.typeof(lib.sin).cname == "double(*)(double)"
+    # 'x' is another <built-in method> object on lib, made very indirectly
+    x = type(lib).__dir__.__get__(lib)
+    py.test.raises(TypeError, ffi.typeof, x)
+    #
+    # present on built-in functions on CPython; must be emulated on PyPy:
+    assert lib.sin.__name__ == 'sin'
+    assert lib.sin.__module__ == '_CFFI_test_math_sin_type'
+    assert lib.sin.__doc__ == (
+        "double sin(double);\n"
+        "\n"
+        "CFFI C function from _CFFI_test_math_sin_type.lib")
+
+    assert ffi.typeof(lib.xxtestfunc).cname == "void *(*)()"
+    assert lib.xxtestfunc.__doc__ == (
+        "void *xxtestfunc();\n"
+        "\n"
+        "CFFI C function from _CFFI_test_math_sin_type.lib")
+
+def test_verify_anonymous_struct_with_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int a; long b; ...; } foo_t;")
+    verify(ffi, 'test_verify_anonymous_struct_with_typedef',
+           "typedef struct { long b; int hidden, a; } foo_t;")
+    p = ffi.new("foo_t *", {'b': 42})
+    assert p.b == 42
+    assert repr(p).startswith("<cdata 'foo_t *' ")
+
+def test_verify_anonymous_struct_with_star_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int a; long b; } *foo_t;")
+    verify(ffi, 'test_verify_anonymous_struct_with_star_typedef',
+           "typedef struct { int a; long b; } *foo_t;")
+    p = ffi.new("foo_t", {'b': 42})
+    assert p.b == 42
+
+def test_verify_anonymous_enum_with_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef enum { AA, ... } e1;")
+    lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef1',
+                 "typedef enum { BB, CC, AA } e1;")
+    assert lib.AA == 2
+    assert ffi.sizeof("e1") == ffi.sizeof("int")
+    assert repr(ffi.cast("e1", 2)) == "<cdata 'e1' 2: AA>"
+    #
+    ffi = FFI()
+    ffi.cdef("typedef enum { AA=%d } e1;" % sys.maxsize)
+    lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef2',
+                 "typedef enum { AA=%d } e1;" % sys.maxsize)
+    assert lib.AA == int(ffi.cast("long", sys.maxsize))
+    assert ffi.sizeof("e1") == ffi.sizeof("long")
+
+def test_unique_types():
+    CDEF = "struct foo_s; union foo_u; enum foo_e { AA };"
+    ffi1 = FFI(); ffi1.cdef(CDEF); verify(ffi1, "test_unique_types_1", CDEF)
+    ffi2 = FFI(); ffi2.cdef(CDEF); verify(ffi2, "test_unique_types_2", CDEF)
+    #
+    assert ffi1.typeof("char") is ffi2.typeof("char ")
+    assert ffi1.typeof("long") is ffi2.typeof("signed long int")
+    assert ffi1.typeof("double *") is ffi2.typeof("double*")
+    assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *")
+    assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]")
+    assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]")
+    assert ffi1.typeof("void") is ffi2.typeof("void")
+    assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)")
+    #
+    # these depend on user-defined data, so should not be shared
+    for name in ["struct foo_s",
+                 "union foo_u *",
+                 "enum foo_e",
+                 "struct foo_s *(*)()",
+                 "void(*)(struct foo_s *)",
+                 "struct foo_s *(*[5])[8]",
+                 ]:
+        assert ffi1.typeof(name) is not ffi2.typeof(name)
+    # sanity check: twice 'ffi1'
+    assert ffi1.typeof("struct foo_s*") is ffi1.typeof("struct foo_s *")
+
+def test_module_name_in_package():
+    ffi = FFI()
+    ffi.cdef("int foo(int);")
+    recompiler.recompile(ffi, "test_module_name_in_package.mymod",
+                         "int foo(int x) { return x + 32; }",
+                         tmpdir=str(udir))
+    old_sys_path = sys.path[:]
+    try:
+        package_dir = udir.join('test_module_name_in_package')
+        for name in os.listdir(str(udir)):
+            assert not name.startswith('test_module_name_in_package.')
+        assert os.path.isdir(str(package_dir))
+        assert len(os.listdir(str(package_dir))) > 0
+        assert os.path.exists(str(package_dir.join('mymod.c')))
+        package_dir.join('__init__.py').write('')
+        #
+        getattr(importlib, 'invalidate_caches', object)()
+        #
+        sys.path.insert(0, str(udir))
+        import test_module_name_in_package.mymod
+        assert test_module_name_in_package.mymod.lib.foo(10) == 42
+        assert test_module_name_in_package.mymod.__name__ == (
+            'test_module_name_in_package.mymod')
+    finally:
+        sys.path[:] = old_sys_path
+
+def test_bad_size_of_global_1():
+    ffi = FFI()
+    ffi.cdef("short glob;")
+    py.test.raises(VerificationError, verify, ffi,
+                   "test_bad_size_of_global_1", "long glob;")
+
+def test_bad_size_of_global_2():
+    ffi = FFI()
+    ffi.cdef("int glob[10];")
+    py.test.raises(VerificationError, verify, ffi,
+                   "test_bad_size_of_global_2", "int glob[9];")
+
+def test_unspecified_size_of_global_1():
+    ffi = FFI()
+    ffi.cdef("int glob[];")
+    lib = verify(ffi, "test_unspecified_size_of_global_1", "int glob[10];")
+    assert ffi.typeof(lib.glob) == ffi.typeof("int *")
+
+def test_unspecified_size_of_global_2():
+    ffi = FFI()
+    ffi.cdef("int glob[][5];")
+    lib = verify(ffi, "test_unspecified_size_of_global_2", "int glob[10][5];")
+    assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]")
+
+def test_unspecified_size_of_global_3():
+    ffi = FFI()
+    ffi.cdef("int glob[][...];")
+    lib = verify(ffi, "test_unspecified_size_of_global_3", "int glob[10][5];")
+    assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]")
+
+def test_unspecified_size_of_global_4():
+    ffi = FFI()
+    ffi.cdef("int glob[...][...];")
+    lib = verify(ffi, "test_unspecified_size_of_global_4", "int glob[10][5];")
+    assert ffi.typeof(lib.glob) == ffi.typeof("int[10][5]")
+
+def test_include_1():
+    ffi1 = FFI()
+    ffi1.cdef("typedef double foo_t;")
+    verify(ffi1, "test_include_1_parent", "typedef double foo_t;")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("foo_t ff1(foo_t);")
+    lib = verify(ffi, "test_include_1", "double ff1(double x) { return 42.5; }")
+    assert lib.ff1(0) == 42.5
+    assert ffi1.typeof("foo_t") is ffi.typeof("foo_t") is ffi.typeof("double")
+
+def test_include_1b():
+    ffi1 = FFI()
+    ffi1.cdef("int foo1(int);")
+    lib1 = verify(ffi1, "test_include_1b_parent",
+                  "int foo1(int x) { return x + 10; }")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("int foo2(int);")
+    lib = verify(ffi, "test_include_1b", "int foo2(int x) { return x - 5; }")
+    assert lib.foo2(42) == 37
+    assert lib.foo1(42) == 52
+    assert lib.foo1 is lib1.foo1
+
+def test_include_2():
+    ffi1 = FFI()
+    ffi1.cdef("struct foo_s { int x, y; };")
+    verify(ffi1, "test_include_2_parent", "struct foo_s { int x, y; };")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("struct foo_s *ff2(struct foo_s *);")
+    lib = verify(ffi, "test_include_2",
+                 "struct foo_s { int x, y; }; //usually from a #include\n"
+                 "struct foo_s *ff2(struct foo_s *p) { p->y++; return p; }")
+    p = ffi.new("struct foo_s *")
+    p.y = 41
+    q = lib.ff2(p)
+    assert q == p
+    assert p.y == 42
+    assert ffi1.typeof("struct foo_s") is ffi.typeof("struct foo_s")
+
+def test_include_3():
+    ffi1 = FFI()
+    ffi1.cdef("typedef short sshort_t;")
+    verify(ffi1, "test_include_3_parent", "typedef short sshort_t;")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("sshort_t ff3(sshort_t);")
+    lib = verify(ffi, "test_include_3",
+                 "typedef short sshort_t; //usually from a #include\n"
+                 "sshort_t ff3(sshort_t x) { return x + 42; }")
+    assert lib.ff3(10) == 52
+    assert ffi.typeof(ffi.cast("sshort_t", 42)) is ffi.typeof("short")
+    assert ffi1.typeof("sshort_t") is ffi.typeof("sshort_t")
+
+def test_include_4():
+    ffi1 = FFI()
+    ffi1.cdef("typedef struct { int x; } mystruct_t;")
+    verify(ffi1, "test_include_4_parent",
+           "typedef struct { int x; } mystruct_t;")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("mystruct_t *ff4(mystruct_t *);")
+    lib = verify(ffi, "test_include_4",
+           "typedef struct {int x; } mystruct_t; //usually from a #include\n"
+           "mystruct_t *ff4(mystruct_t *p) { p->x += 42; return p; }")
+    p = ffi.new("mystruct_t *", [10])
+    q = lib.ff4(p)
+    assert q == p
+    assert p.x == 52
+    assert ffi1.typeof("mystruct_t") is ffi.typeof("mystruct_t")
+
+def test_include_5():
+    ffi1 = FFI()
+    ffi1.cdef("typedef struct { int x[2]; int y; } *mystruct_p;")
+    verify(ffi1, "test_include_5_parent",
+           "typedef struct { int x[2]; int y; } *mystruct_p;")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("mystruct_p ff5(mystruct_p);")
+    lib = verify(ffi, "test_include_5",
+        "typedef struct {int x[2]; int y; } *mystruct_p; //usually #include\n"
+        "mystruct_p ff5(mystruct_p p) { p->x[1] += 42; return p; }")
+    assert ffi.alignof(ffi.typeof("mystruct_p").item) == 4
+    assert ffi1.typeof("mystruct_p") is ffi.typeof("mystruct_p")
+    p = ffi.new("mystruct_p", [[5, 10], -17])
+    q = lib.ff5(p)
+    assert q == p
+    assert p.x[0] == 5
+    assert p.x[1] == 52
+    assert p.y == -17
+    assert ffi.alignof(ffi.typeof(p[0])) == 4
+
+def test_include_6():
+    ffi1 = FFI()
+    ffi1.cdef("typedef ... mystruct_t;")
+    verify(ffi1, "test_include_6_parent",
+           "typedef struct _mystruct_s mystruct_t;")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("mystruct_t *ff6(void); int ff6b(mystruct_t *);")
+    lib = verify(ffi, "test_include_6",
+           "typedef struct _mystruct_s mystruct_t; //usually from a #include\n"
+           "struct _mystruct_s { int x; };\n"
+           "static mystruct_t result_struct = { 42 };\n"
+           "mystruct_t *ff6(void) { return &result_struct; }\n"
+           "int ff6b(mystruct_t *p) { return p->x; }")
+    p = lib.ff6()
+    assert ffi.cast("int *", p)[0] == 42
+    assert lib.ff6b(p) == 42
+
+def test_include_7():
+    ffi1 = FFI()
+    ffi1.cdef("typedef ... mystruct_t;\n"
+              "int ff7b(mystruct_t *);")
+    verify(ffi1, "test_include_7_parent",
+           "typedef struct { int x; } mystruct_t;\n"
+           "int ff7b(mystruct_t *p) { return p->x; }")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("mystruct_t *ff7(void);")
+    lib = verify(ffi, "test_include_7",
+           "typedef struct { int x; } mystruct_t; //usually from a #include\n"
+           "static mystruct_t result_struct = { 42 };"
+           "mystruct_t *ff7(void) { return &result_struct; }")
+    p = lib.ff7()
+    assert ffi.cast("int *", p)[0] == 42
+    assert lib.ff7b(p) == 42
+
+def test_include_8():
+    ffi1 = FFI()
+    ffi1.cdef("struct foo_s;")
+    verify(ffi1, "test_include_8_parent", "struct foo_s;")
+    ffi = FFI()
+    ffi.include(ffi1)
+    ffi.cdef("struct foo_s { int x, y; };")
+    verify(ffi, "test_include_8", "struct foo_s { int x, y; };")
+    e = py.test.raises(NotImplementedError, ffi.new, "struct foo_s *")
+    assert str(e.value) == (
+        "'struct foo_s' is opaque in the ffi.include(), but no longer in "
+        "the ffi doing the include (workaround: don't use ffi.include() but"
+        " duplicate the declarations of everything using struct foo_s)")
+
+def test_unicode_libraries():
+    try:
+        unicode
+    except NameError:
+        py.test.skip("for python 2.x")
+    #
+    import math
+    lib_m = "m"
+    if sys.platform == 'win32':
+        #there is a small chance this fails on Mingw via environ $CC
+        import distutils.ccompiler
+        if distutils.ccompiler.get_default_compiler() == 'msvc':
+            lib_m = 'msvcrt'
+    ffi = FFI()
+    ffi.cdef(unicode("float sin(double); double cos(double);"))
+    lib = verify(ffi, 'test_math_sin_unicode', unicode('#include <math.h>'),
+                 libraries=[unicode(lib_m)])
+    assert lib.cos(1.43) == math.cos(1.43)
+
+def test_incomplete_struct_as_arg():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; ...; }; int f(int, struct foo_s);")
+    lib = verify(ffi, "test_incomplete_struct_as_arg",
+                 "struct foo_s { int a, x, z; };\n"
+                 "int f(int b, struct foo_s s) { return s.x * b; }")
+    s = ffi.new("struct foo_s *", [21])
+    assert s.x == 21
+    assert ffi.sizeof(s[0]) == 12
+    assert ffi.offsetof(ffi.typeof(s), 'x') == 4
+    assert lib.f(2, s[0]) == 42
+    assert ffi.typeof(lib.f) == ffi.typeof("int(*)(int, struct foo_s)")
+
+def test_incomplete_struct_as_result():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; ...; }; struct foo_s f(int);")
+    lib = verify(ffi, "test_incomplete_struct_as_result",
+            "struct foo_s { int a, x, z; };\n"
+            "struct foo_s f(int x) { struct foo_s r; r.x = x * 2; return r; }")
+    s = lib.f(21)
+    assert s.x == 42
+    assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)")
+
+def test_incomplete_struct_as_both():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; ...; }; struct bar_s { int y; ...; };\n"
+             "struct foo_s f(int, struct bar_s);")
+    lib = verify(ffi, "test_incomplete_struct_as_both",
+            "struct foo_s { int a, x, z; };\n"
+            "struct bar_s { int b, c, y, d; };\n"
+            "struct foo_s f(int x, struct bar_s b) {\n"
+            "  struct foo_s r; r.x = x * b.y; return r;\n"
+            "}")
+    b = ffi.new("struct bar_s *", [7])
+    s = lib.f(6, b[0])
+    assert s.x == 42
+    assert ffi.typeof(lib.f) == ffi.typeof(
+        "struct foo_s(*)(int, struct bar_s)")
+    s = lib.f(14, {'y': -3})
+    assert s.x == -42
+
+def test_name_of_unnamed_struct():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; } foo_t;\n"
+             "typedef struct { int y; } *bar_p;\n"
+             "typedef struct { int y; } **baz_pp;\n")
+    verify(ffi, "test_name_of_unnamed_struct",
+             "typedef struct { int x; } foo_t;\n"
+             "typedef struct { int y; } *bar_p;\n"
+             "typedef struct { int y; } **baz_pp;\n")
+    assert repr(ffi.typeof("foo_t")) == "<ctype 'foo_t'>"
+    assert repr(ffi.typeof("bar_p")) == "<ctype 'struct $1 *'>"
+    assert repr(ffi.typeof("baz_pp")) == "<ctype 'struct $2 * *'>"
+
+def test_address_of_global_var():
+    ffi = FFI()
+    ffi.cdef("""
+        long bottom, bottoms[2];
+        long FetchRectBottom(void);
+        long FetchRectBottoms1(void);
+        #define FOOBAR 42
+    """)
+    lib = verify(ffi, "test_address_of_global_var", """
+        long bottom, bottoms[2];
+        long FetchRectBottom(void) { return bottom; }
+        long FetchRectBottoms1(void) { return bottoms[1]; }
+        #define FOOBAR 42
+    """)
+    lib.bottom = 300
+    assert lib.FetchRectBottom() == 300
+    lib.bottom += 1
+    assert lib.FetchRectBottom() == 301
+    lib.bottoms[1] = 500
+    assert lib.FetchRectBottoms1() == 500
+    lib.bottoms[1] += 2
+    assert lib.FetchRectBottoms1() == 502
+    #
+    p = ffi.addressof(lib, 'bottom')
+    assert ffi.typeof(p) == ffi.typeof("long *")
+    assert p[0] == 301
+    p[0] += 1
+    assert lib.FetchRectBottom() == 302
+    p = ffi.addressof(lib, 'bottoms')
+    assert ffi.typeof(p) == ffi.typeof("long(*)[2]")
+    assert p[0] == lib.bottoms
+    #
+    py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var')
+    py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR")
+
+def test_defines__CFFI_():
+    # Check that we define the macro _CFFI_ automatically.
+    # It should be done before including Python.h, so that PyPy's Python.h
+    # can check for it.
+    ffi = FFI()
+    ffi.cdef("""
+        #define CORRECT 1
+    """)
+    lib = verify(ffi, "test_defines__CFFI_", """
+    #ifdef _CFFI_
+    #    define CORRECT 1
+    #endif
+    """)
+    assert lib.CORRECT == 1
+
+def test_unpack_args():
+    ffi = FFI()
+    ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);")
+    lib = verify(ffi, "test_unpack_args", """
+    void foo0(void) { }
+    void foo1(int x) { }
+    void foo2(int x, int y) { }
+    """)
+    assert 'foo0' in repr(lib.foo0)
+    assert 'foo1' in repr(lib.foo1)
+    assert 'foo2' in repr(lib.foo2)
+    lib.foo0()
+    lib.foo1(42)
+    lib.foo2(43, 44)
+    e1 = py.test.raises(TypeError, lib.foo0, 42)
+    e2 = py.test.raises(TypeError, lib.foo0, 43, 44)
+    e3 = py.test.raises(TypeError, lib.foo1)
+    e4 = py.test.raises(TypeError, lib.foo1, 43, 44)
+    e5 = py.test.raises(TypeError, lib.foo2)
+    e6 = py.test.raises(TypeError, lib.foo2, 42)
+    e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47)
+    assert str(e1.value) == "foo0() takes no arguments (1 given)"
+    assert str(e2.value) == "foo0() takes no arguments (2 given)"
+    assert str(e3.value) == "foo1() takes exactly one argument (0 given)"
+    assert str(e4.value) == "foo1() takes exactly one argument (2 given)"
+    assert str(e5.value) in ["foo2 expected 2 arguments, got 0",
+                             "foo2() takes exactly 2 arguments (0 given)"]
+    assert str(e6.value) in ["foo2 expected 2 arguments, got 1",
+                             "foo2() takes exactly 2 arguments (1 given)"]
+    assert str(e7.value) in ["foo2 expected 2 arguments, got 3",
+                             "foo2() takes exactly 2 arguments (3 given)"]
+
+def test_address_of_function():
+    ffi = FFI()
+    ffi.cdef("long myfunc(long x);")
+    lib = verify(ffi, "test_addressof_function", """
+        char myfunc(char x) { return (char)(x + 42); }
+    """)
+    assert lib.myfunc(5) == 47
+    assert lib.myfunc(0xABC05) == 47
+    assert not isinstance(lib.myfunc, ffi.CData)
+    assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)")
+    addr = ffi.addressof(lib, 'myfunc')
+    assert addr(5) == 47
+    assert addr(0xABC05) == 47
+    assert isinstance(addr, ffi.CData)
+    assert ffi.typeof(addr) == ffi.typeof("long(*)(long)")
+
+def test_address_of_function_with_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; }; long myfunc(struct foo_s);")
+    lib = verify(ffi, "test_addressof_function_with_struct", """
+        struct foo_s { int x; };
+        char myfunc(struct foo_s input) { return (char)(input.x + 42); }
+    """)
+    s = ffi.new("struct foo_s *", [5])[0]
+    assert lib.myfunc(s) == 47
+    assert not isinstance(lib.myfunc, ffi.CData)
+    assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(struct foo_s)")
+    addr = ffi.addressof(lib, 'myfunc')
+    assert addr(s) == 47
+    assert isinstance(addr, ffi.CData)
+    assert ffi.typeof(addr) == ffi.typeof("long(*)(struct foo_s)")
+
+def test_issue198():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct{...;} opaque_t;
+        const opaque_t CONSTANT;
+        int toint(opaque_t);
+    """)
+    lib = verify(ffi, 'test_issue198', """
+        typedef int opaque_t;
+        #define CONSTANT ((opaque_t)42)
+        static int toint(opaque_t o) { return o; }
+    """)
+    def random_stuff():
+        pass
+    assert lib.toint(lib.CONSTANT) == 42
+    random_stuff()
+    assert lib.toint(lib.CONSTANT) == 42
+
+def test_constant_is_not_a_compiler_constant():
+    ffi = FFI()
+    ffi.cdef("static const float almost_forty_two;")
+    lib = verify(ffi, 'test_constant_is_not_a_compiler_constant', """
+        static float f(void) { return 42.25; }
+        #define almost_forty_two (f())
+    """)
+    assert lib.almost_forty_two == 42.25
+
+def test_constant_of_unknown_size():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef ... opaque_t;
+        const opaque_t CONSTANT;
+    """)
+    lib = verify(ffi, 'test_constant_of_unknown_size',
+                 "typedef int opaque_t;"
+                 "const int CONSTANT = 42;")
+    e = py.test.raises(ffi.error, getattr, lib, 'CONSTANT')
+    assert str(e.value) == ("constant 'CONSTANT' is of "
+                            "type 'opaque_t', whose size is not known")
+
+def test_variable_of_unknown_size():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef ... opaque_t;
+        opaque_t globvar;
+    """)
+    lib = verify(ffi, 'test_variable_of_unknown_size', """
+        typedef char opaque_t[6];
+        opaque_t globvar = "hello";
+    """)
+    # can't read or write it at all
+    e = py.test.raises(TypeError, getattr, lib, 'globvar')
+    assert str(e.value) in ["cdata 'opaque_t' is opaque",
+                            "'opaque_t' is opaque or not completed yet"] #pypy
+    e = py.test.raises(TypeError, setattr, lib, 'globvar', [])
+    assert str(e.value) in ["'opaque_t' is opaque",
+                            "'opaque_t' is opaque or not completed yet"] #pypy
+    # but we can get its address
+    p = ffi.addressof(lib, 'globvar')
+    assert ffi.typeof(p) == ffi.typeof('opaque_t *')
+    assert ffi.string(ffi.cast("char *", p), 8) == b"hello"
+
+def test_constant_of_value_unknown_to_the_compiler():
+    extra_c_source = udir.join(
+        'extra_test_constant_of_value_unknown_to_the_compiler.c')
+    extra_c_source.write('const int external_foo = 42;\n')
+    ffi = FFI()
+    ffi.cdef("const int external_foo;")
+    lib = verify(ffi, 'test_constant_of_value_unknown_to_the_compiler', """
+        extern const int external_foo;
+    """, sources=[str(extra_c_source)])
+    assert lib.external_foo == 42
+
+def test_dotdot_in_source_file_names():
+    extra_c_source = udir.join(
+        'extra_test_dotdot_in_source_file_names.c')
+    extra_c_source.write('const int external_foo = 42;\n')
+    ffi = FFI()
+    ffi.cdef("const int external_foo;")
+    lib = verify(ffi, 'test_dotdot_in_source_file_names', """
+        extern const int external_foo;
+    """, sources=[os.path.join(os.path.dirname(str(extra_c_source)),
+                               'foobar', '..',
+                               os.path.basename(str(extra_c_source)))])
+    assert lib.external_foo == 42
+
+def test_call_with_incomplete_structs():
+    ffi = FFI()
+    ffi.cdef("typedef struct {...;} foo_t; "
+             "foo_t myglob; "
+             "foo_t increment(foo_t s); "
+             "double getx(foo_t s);")
+    lib = verify(ffi, 'test_call_with_incomplete_structs', """
+        typedef double foo_t;
+        double myglob = 42.5;
+        double getx(double x) { return x; }
+        double increment(double x) { return x + 1; }
+    """)
+    assert lib.getx(lib.myglob) == 42.5
+    assert lib.getx(lib.increment(lib.myglob)) == 43.5
+
+def test_struct_array_guess_length_2():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[...][...]; };")
+    lib = verify(ffi, 'test_struct_array_guess_length_2',
+                 "struct foo_s { int x; int a[5][8]; int y; };")
+    assert ffi.sizeof('struct foo_s') == 42 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.typeof(s.a) == ffi.typeof("int[5][8]")
+    assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int')
+    assert s.a[4][7] == 0
+    py.test.raises(IndexError, 's.a[4][8]')
+    py.test.raises(IndexError, 's.a[5][0]')
+    assert ffi.typeof(s.a) == ffi.typeof("int[5][8]")
+    assert ffi.typeof(s.a[0]) == ffi.typeof("int[8]")
+
+def test_struct_array_guess_length_3():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[][...]; };")
+    lib = verify(ffi, 'test_struct_array_guess_length_3',
+                 "struct foo_s { int x; int a[5][7]; int y; };")
+    assert ffi.sizeof('struct foo_s') == 37 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.typeof(s.a) == ffi.typeof("int[][7]")
+    assert s.a[4][6] == 0
+    py.test.raises(IndexError, 's.a[4][7]')
+    assert ffi.typeof(s.a[0]) == ffi.typeof("int[7]")
+
+def test_global_var_array_2():
+    ffi = FFI()
+    ffi.cdef("int a[...][...];")
+    lib = verify(ffi, 'test_global_var_array_2', 'int a[10][8];')
+    lib.a[9][7] = 123456
+    assert lib.a[9][7] == 123456
+    py.test.raises(IndexError, 'lib.a[0][8]')
+    py.test.raises(IndexError, 'lib.a[10][0]')
+    assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]")
+    assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]")
+
+def test_global_var_array_3():
+    ffi = FFI()
+    ffi.cdef("int a[][...];")
+    lib = verify(ffi, 'test_global_var_array_3', 'int a[10][8];')
+    lib.a[9][7] = 123456
+    assert lib.a[9][7] == 123456
+    py.test.raises(IndexError, 'lib.a[0][8]')
+    assert ffi.typeof(lib.a) == ffi.typeof("int(*)[8]")
+    assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]")
+
+def test_global_var_array_4():
+    ffi = FFI()
+    ffi.cdef("int a[10][...];")
+    lib = verify(ffi, 'test_global_var_array_4', 'int a[10][8];')
+    lib.a[9][7] = 123456
+    assert lib.a[9][7] == 123456
+    py.test.raises(IndexError, 'lib.a[0][8]')
+    py.test.raises(IndexError, 'lib.a[10][8]')
+    assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]")
+    assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]")
+
+def test_some_integer_type():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef int... foo_t;
+        typedef unsigned long... bar_t;
+        typedef struct { foo_t a, b; } mystruct_t;
+        foo_t foobar(bar_t, mystruct_t);
+        static const bar_t mu = -20;
+        static const foo_t nu = 20;
+    """)
+    lib = verify(ffi, 'test_some_integer_type', """
+        typedef unsigned long long foo_t;
+        typedef short bar_t;
+        typedef struct { foo_t a, b; } mystruct_t;
+        static foo_t foobar(bar_t x, mystruct_t s) {
+            return (foo_t)x + s.a + s.b;
+        }
+        static const bar_t mu = -20;
+        static const foo_t nu = 20;
+    """)
+    assert ffi.sizeof("foo_t") == ffi.sizeof("unsigned long long")
+    assert ffi.sizeof("bar_t") == ffi.sizeof("short")
+    maxulonglong = 2 ** 64 - 1
+    assert int(ffi.cast("foo_t", -1)) == maxulonglong
+    assert int(ffi.cast("bar_t", -1)) == -1
+    assert lib.foobar(-1, [0, 0]) == maxulonglong
+    assert lib.foobar(2 ** 15 - 1, [0, 0]) == 2 ** 15 - 1
+    assert lib.foobar(10, [20, 31]) == 61
+    assert lib.foobar(0, [0, maxulonglong]) == maxulonglong
+    py.test.raises(OverflowError, lib.foobar, 2 ** 15, [0, 0])
+    py.test.raises(OverflowError, lib.foobar, -(2 ** 15) - 1, [0, 0])
+    py.test.raises(OverflowError, ffi.new, "mystruct_t *", [0, -1])
+    assert lib.mu == -20
+    assert lib.nu == 20
+
+def test_some_float_type():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef double... foo_t;
+        typedef float... bar_t;
+        foo_t sum(foo_t[]);
+        bar_t neg(bar_t);
+        """)
+    lib = verify(ffi, 'test_some_float_type', """
+        typedef float foo_t;
+        static foo_t sum(foo_t x[]) { return x[0] + x[1]; }
+        typedef double bar_t;
+        static double neg(double x) { return -x; }
+    """)
+    assert lib.sum([40.0, 2.25]) == 42.25
+    assert lib.sum([12.3, 45.6]) != 12.3 + 45.6     # precision loss
+    assert lib.neg(12.3) == -12.3                   # no precision loss
+    assert ffi.sizeof("foo_t") == ffi.sizeof("float")
+    assert ffi.sizeof("bar_t") == ffi.sizeof("double")
+
+def test_some_float_invalid_1():
+    ffi = FFI()
+    py.test.raises((FFIError,      # with pycparser <= 2.17
+                    CDefError),    # with pycparser >= 2.18
+                   ffi.cdef, "typedef long double... foo_t;")
+
+def test_some_float_invalid_2():
+    ffi = FFI()
+    ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);")
+    lib = verify(ffi, 'test_some_float_invalid_2', """
+        typedef unsigned long foo_t;
+        foo_t neg(foo_t x) { return -x; }
+    """)
+    e = py.test.raises(ffi.error, getattr, lib, 'neg')
+    assert str(e.value) == ("primitive floating-point type with an unexpected "
+                            "size (or not a float type at all)")
+
+def test_some_float_invalid_3():
+    ffi = FFI()
+    ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);")
+    lib = verify(ffi, 'test_some_float_invalid_3', """
+        typedef long double foo_t;
+        foo_t neg(foo_t x) { return -x; }
+    """)
+    if ffi.sizeof("long double") == ffi.sizeof("double"):
+        assert lib.neg(12.3) == -12.3
+    else:
+        e = py.test.raises(ffi.error, getattr, lib, 'neg')
+        assert str(e.value) == ("primitive floating-point type is "
+                                "'long double', not supported for now with "
+                                "the syntax 'typedef double... xxx;'")
+
+def test_issue200():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef void (function_t)(void*);
+        void function(void *);
+    """)
+    lib = verify(ffi, 'test_issue200', """
+        static void function(void *p) { (void)p; }
+    """)
+    ffi.typeof('function_t*')
+    lib.function(ffi.NULL)
+    # assert did not crash
+
+def test_alignment_of_longlong():
+    ffi = FFI()
+    x1 = ffi.alignof('unsigned long long')
+    assert x1 in [4, 8]
+    ffi.cdef("struct foo_s { unsigned long long x; };")
+    lib = verify(ffi, 'test_alignment_of_longlong',
+                 "struct foo_s { unsigned long long x; };")
+    assert ffi.alignof('unsigned long long') == x1
+    assert ffi.alignof('struct foo_s') == x1
+
+def test_import_from_lib():
+    ffi = FFI()
+    ffi.cdef("int mybar(int); int myvar;\n#define MYFOO ...")
+    lib = verify(ffi, 'test_import_from_lib',
+                 "#define MYFOO 42\n"
+                 "static int mybar(int x) { return x + 1; }\n"
+                 "static int myvar = -5;")
+    assert sys.modules['_CFFI_test_import_from_lib'].lib is lib
+    assert sys.modules['_CFFI_test_import_from_lib.lib'] is lib
+    from _CFFI_test_import_from_lib.lib import MYFOO
+    assert MYFOO == 42
+    assert hasattr(lib, '__dict__')
+    assert lib.__all__ == ['MYFOO', 'mybar']   # but not 'myvar'
+    assert lib.__name__ == '_CFFI_test_import_from_lib.lib'
+    assert lib.__class__ is type(sys)   # !! hack for help()
+
+def test_macro_var_callback():
+    ffi = FFI()
+    ffi.cdef("int my_value; int *(*get_my_value)(void);")
+    lib = verify(ffi, 'test_macro_var_callback',
+                 "int *(*get_my_value)(void);\n"
+                 "#define my_value (*get_my_value())")
+    #
+    values = ffi.new("int[50]")
+    def it():
+        for i in range(50):
+            yield i
+    it = it()
+    #
+    @ffi.callback("int *(*)(void)")
+    def get_my_value():
+        for nextvalue in it:
+            return values + nextvalue
+    lib.get_my_value = get_my_value
+    #
+    values[0] = 41
+    assert lib.my_value == 41            # [0]
+    p = ffi.addressof(lib, 'my_value')   # [1]
+    assert p == values + 1
+    assert p[-1] == 41
+    assert p[+1] == 0
+    lib.my_value = 42                    # [2]
+    assert values[2] == 42
+    assert p[-1] == 41
+    assert p[+1] == 42
+    #
+    # if get_my_value raises or returns nonsense, the exception is printed
+    # to stderr like with any callback, but then the C expression 'my_value'
+    # expand to '*NULL'.  We assume here that '&my_value' will return NULL
+    # without segfaulting, and check for NULL when accessing the variable.
+    @ffi.callback("int *(*)(void)")
+    def get_my_value():
+        raise LookupError
+    lib.get_my_value = get_my_value
+    py.test.raises(ffi.error, getattr, lib, 'my_value')
+    py.test.raises(ffi.error, setattr, lib, 'my_value', 50)
+    py.test.raises(ffi.error, ffi.addressof, lib, 'my_value')
+    @ffi.callback("int *(*)(void)")
+    def get_my_value():
+        return "hello"
+    lib.get_my_value = get_my_value
+    py.test.raises(ffi.error, getattr, lib, 'my_value')
+    e = py.test.raises(ffi.error, setattr, lib, 'my_value', 50)
+    assert str(e.value) == "global variable 'my_value' is at address NULL"
+
+def test_const_fields():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { const int a; void *const b; };""")
+    lib = verify(ffi, 'test_const_fields', """
+        struct foo_s { const int a; void *const b; };""")
+    foo_s = ffi.typeof("struct foo_s")
+    assert foo_s.fields[0][0] == 'a'
+    assert foo_s.fields[0][1].type is ffi.typeof("int")
+    assert foo_s.fields[1][0] == 'b'
+    assert foo_s.fields[1][1].type is ffi.typeof("void *")
+
+def test_restrict_fields():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { void * restrict b; };""")
+    lib = verify(ffi, 'test_restrict_fields', """
+        struct foo_s { void * __restrict b; };""")
+    foo_s = ffi.typeof("struct foo_s")
+    assert foo_s.fields[0][0] == 'b'
+    assert foo_s.fields[0][1].type is ffi.typeof("void *")
+
+def test_volatile_fields():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { void * volatile b; };""")
+    lib = verify(ffi, 'test_volatile_fields', """
+        struct foo_s { void * volatile b; };""")
+    foo_s = ffi.typeof("struct foo_s")
+    assert foo_s.fields[0][0] == 'b'
+    assert foo_s.fields[0][1].type is ffi.typeof("void *")
+
+def test_const_array_fields():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { const int a[4]; };""")
+    lib = verify(ffi, 'test_const_array_fields', """
+        struct foo_s { const int a[4]; };""")
+    foo_s = ffi.typeof("struct foo_s")
+    assert foo_s.fields[0][0] == 'a'
+    assert foo_s.fields[0][1].type is ffi.typeof("int[4]")
+
+def test_const_array_fields_varlength():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { const int a[]; ...; };""")
+    lib = verify(ffi, 'test_const_array_fields_varlength', """
+        struct foo_s { const int a[4]; };""")
+    foo_s = ffi.typeof("struct foo_s")
+    assert foo_s.fields[0][0] == 'a'
+    assert foo_s.fields[0][1].type is ffi.typeof("int[]")
+
+def test_const_array_fields_unknownlength():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { const int a[...]; ...; };""")
+    lib = verify(ffi, 'test_const_array_fields_unknownlength', """
+        struct foo_s { const int a[4]; };""")
+    foo_s = ffi.typeof("struct foo_s")
+    assert foo_s.fields[0][0] == 'a'
+    assert foo_s.fields[0][1].type is ffi.typeof("int[4]")
+
+def test_const_function_args():
+    ffi = FFI()
+    ffi.cdef("""int foobar(const int a, const int *b, const int c[]);""")
+    lib = verify(ffi, 'test_const_function_args', """
+        int foobar(const int a, const int *b, const int c[]) {
+            return a + *b + *c;
+        }
+    """)
+    assert lib.foobar(100, ffi.new("int *", 40), ffi.new("int *", 2)) == 142
+
+def test_const_function_type_args():
+    ffi = FFI()
+    ffi.cdef("""int (*foobar)(const int a, const int *b, const int c[]);""")
+    lib = verify(ffi, 'test_const_function_type_args', """
+        int (*foobar)(const int a, const int *b, const int c[]);
+    """)
+    t = ffi.typeof(lib.foobar)
+    assert t.args[0] is ffi.typeof("int")
+    assert t.args[1] is ffi.typeof("int *")
+    assert t.args[2] is ffi.typeof("int *")
+
+def test_const_constant():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { int x,y; }; const struct foo_s myfoo;""")
+    lib = verify(ffi, 'test_const_constant', """
+        struct foo_s { int x,y; }; const struct foo_s myfoo = { 40, 2 };
+    """)
+    assert lib.myfoo.x == 40
+    assert lib.myfoo.y == 2
+
+def test_const_via_typedef():
+    ffi = FFI()
+    ffi.cdef("""typedef const int const_t; const_t aaa;""")
+    lib = verify(ffi, 'test_const_via_typedef', """
+        typedef const int const_t;
+        #define aaa 42
+    """)
+    assert lib.aaa == 42
+    py.test.raises(AttributeError, "lib.aaa = 43")
+
+def test_win32_calling_convention_0():
+    ffi = FFI()
+    ffi.cdef("""
+        int call1(int(__cdecl   *cb)(int));
+        int (*const call2)(int(__stdcall *cb)(int));
+    """)
+    lib = verify(ffi, 'test_win32_calling_convention_0', r"""
+        #ifndef _MSC_VER
+        #  define __stdcall  /* nothing */
+        #endif
+        int call1(int(*cb)(int)) {
+            int i, result = 0;
+            //printf("call1: cb = %p\n", cb);
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+        int call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            //printf("call2: cb = %p\n", cb);
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+    """)
+    @ffi.callback("int(int)")
+    def cb1(x):
+        return x * 2
+    @ffi.callback("int __stdcall(int)")
+    def cb2(x):
+        return x * 3
+    res = lib.call1(cb1)
+    assert res == 500*999*2
+    assert res == ffi.addressof(lib, 'call1')(cb1)
+    res = lib.call2(cb2)
+    assert res == -500*999*3
+    assert res == ffi.addressof(lib, 'call2')(cb2)
+    if sys.platform == 'win32' and not sys.maxsize > 2**32:
+        assert '__stdcall' in str(ffi.typeof(cb2))
+        assert '__stdcall' not in str(ffi.typeof(cb1))
+        py.test.raises(TypeError, lib.call1, cb2)
+        py.test.raises(TypeError, lib.call2, cb1)
+    else:
+        assert '__stdcall' not in str(ffi.typeof(cb2))
+        assert ffi.typeof(cb2) is ffi.typeof(cb1)
+
+def test_win32_calling_convention_1():
+    ffi = FFI()
+    ffi.cdef("""
+        int __cdecl   call1(int(__cdecl   *cb)(int));
+        int __stdcall call2(int(__stdcall *cb)(int));
+        int (__cdecl   *const cb1)(int);
+        int (__stdcall *const cb2)(int);
+    """)
+    lib = verify(ffi, 'test_win32_calling_convention_1', r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        int __cdecl   cb1(int x) { return x * 2; }
+        int __stdcall cb2(int x) { return x * 3; }
+
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            //printf("here1\n");
+            //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+        int __stdcall call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            //printf("here1\n");
+            //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2);
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+    """)
+    #print '<<< cb1 =', ffi.addressof(lib, 'cb1')
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    #print '<<< cb2 =', ffi.addressof(lib, 'cb2')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    #print '<<< done'
+
+def test_win32_calling_convention_2():
+    # any mistake in the declaration of plain function (including the
+    # precise argument types and, here, the calling convention) are
+    # automatically corrected.  But this does not apply to the 'cb'
+    # function pointer argument.
+    ffi = FFI()
+    ffi.cdef("""
+        int __stdcall call1(int(__cdecl   *cb)(int));
+        int __cdecl   call2(int(__stdcall *cb)(int));
+        int (__cdecl   *const cb1)(int);
+        int (__stdcall *const cb2)(int);
+    """)
+    lib = verify(ffi, 'test_win32_calling_convention_2', """
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            return result;
+        }
+        int __stdcall call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            return result;
+        }
+        int __cdecl   cb1(int x) { return x * 2; }
+        int __stdcall cb2(int x) { return x * 3; }
+    """)
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    if sys.platform == 'win32' and not sys.maxsize > 2**32:
+        py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2'))
+        py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2'))
+        py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1'))
+        py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1'))
+    assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+
+def test_win32_calling_convention_3():
+    ffi = FFI()
+    ffi.cdef("""
+        struct point { int x, y; };
+
+        int (*const cb1)(struct point);
+        int (__stdcall *const cb2)(struct point);
+
+        struct point __stdcall call1(int(*cb)(struct point));
+        struct point call2(int(__stdcall *cb)(struct point));
+    """)
+    lib = verify(ffi, 'test_win32_calling_convention_3', r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        struct point { int x, y; };
+        int           cb1(struct point pt) { return pt.x + 10 * pt.y; }
+        int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; }
+        struct point __stdcall call1(int(__cdecl *cb)(struct point)) {
+            int i;
+            struct point result = { 0, 0 };
+            //printf("here1\n");
+            //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            for (i = 0; i < 1000; i++) {
+                struct point p = { i, -i };
+                int r = cb(p);
+                result.x += r;
+                result.y -= r;
+            }
+            return result;
+        }
+        struct point __cdecl call2(int(__stdcall *cb)(struct point)) {
+            int i;
+            struct point result = { 0, 0 };
+            for (i = 0; i < 1000; i++) {
+                struct point p = { -i, i };
+                int r = cb(p);
+                result.x += r;
+                result.y -= r;
+            }
+            return result;
+        }
+    """)
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    if sys.platform == 'win32' and not sys.maxsize > 2**32:
+        py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2'))
+        py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2'))
+        py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1'))
+        py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1'))
+    pt = lib.call1(ffi.addressof(lib, 'cb1'))
+    assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+    pt = ptr_call1(ffi.addressof(lib, 'cb1'))
+    assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+    pt = lib.call2(ffi.addressof(lib, 'cb2'))
+    assert (pt.x, pt.y) == (99*500*999, -99*500*999)
+    pt = ptr_call2(ffi.addressof(lib, 'cb2'))
+    assert (pt.x, pt.y) == (99*500*999, -99*500*999)
+
+def test_extern_python_1():
+    import warnings
+    ffi = FFI()
+    with warnings.catch_warnings(record=True) as log:
+        ffi.cdef("""
+        extern "Python" {
+            int bar(int, int);
+            void baz(int, int);
+            int bok(void);
+            void boz(void);
+        }
+        """)
+    assert len(log) == 0, "got a warning: %r" % (log,)
+    lib = verify(ffi, 'test_extern_python_1', """
+        static void baz(int, int);   /* forward */
+    """)
+    assert ffi.typeof(lib.bar) == ffi.typeof("int(*)(int, int)")
+    with FdWriteCapture() as f:
+        res = lib.bar(4, 5)
+    assert res == 0
+    assert f.getvalue() == (
+        b"extern \"Python\": function _CFFI_test_extern_python_1.bar() called, "
+        b"but no code was attached "
+        b"to it yet with @ffi.def_extern().  Returning 0.\n")
+
+    @ffi.def_extern("bar")
+    def my_bar(x, y):
+        seen.append(("Bar", x, y))
+        return x * y
+    assert my_bar != lib.bar
+    seen = []
+    res = lib.bar(6, 7)
+    assert seen == [("Bar", 6, 7)]
+    assert res == 42
+
+    def baz(x, y):
+        seen.append(("Baz", x, y))
+    baz1 = ffi.def_extern()(baz)
+    assert baz1 is baz
+    seen = []
+    baz(long(40), long(4))
+    res = lib.baz(long(50), long(8))
+    assert res is None
+    assert seen == [("Baz", 40, 4), ("Baz", 50, 8)]
+    assert type(seen[0][1]) is type(seen[0][2]) is long
+    assert type(seen[1][1]) is type(seen[1][2]) is int
+
+    @ffi.def_extern(name="bok")
+    def bokk():
+        seen.append("Bok")
+        return 42
+    seen = []
+    assert lib.bok() == 42
+    assert seen == ["Bok"]
+
+    @ffi.def_extern()
+    def boz():
+        seen.append("Boz")
+    seen = []
+    assert lib.boz() is None
+    assert seen == ["Boz"]
+
+def test_extern_python_bogus_name():
+    ffi = FFI()
+    ffi.cdef("int abc;")
+    lib = verify(ffi, 'test_extern_python_bogus_name', "int abc;")
+    def fn():
+        pass
+    py.test.raises(ffi.error, ffi.def_extern("unknown_name"), fn)
+    py.test.raises(ffi.error, ffi.def_extern("abc"), fn)
+    assert lib.abc == 0
+    e = py.test.raises(ffi.error, ffi.def_extern("abc"), fn)
+    assert str(e.value) == ("ffi.def_extern('abc'): no 'extern \"Python\"' "
+                            "function with this name")
+    e = py.test.raises(ffi.error, ffi.def_extern(), fn)
+    assert str(e.value) == ("ffi.def_extern('fn'): no 'extern \"Python\"' "
+                            "function with this name")
+    #
+    py.test.raises(TypeError, ffi.def_extern(42), fn)
+    py.test.raises((TypeError, AttributeError), ffi.def_extern(), "foo")
+    class X:
+        pass
+    x = X()
+    x.__name__ = x
+    py.test.raises(TypeError, ffi.def_extern(), x)
+
+def test_extern_python_bogus_result_type():
+    ffi = FFI()
+    ffi.cdef("""extern "Python" void bar(int);""")
+    lib = verify(ffi, 'test_extern_python_bogus_result_type', "")
+    #
+    @ffi.def_extern()
+    def bar(n):
+        return n * 10
+    with StdErrCapture() as f:
+        res = lib.bar(321)
+    assert res is None
+    assert f.getvalue() == (
+        "From cffi callback %r:\n" % (bar,) +
+        "Trying to convert the result back to C:\n"
+        "TypeError: callback with the return type 'void' must return None\n")
+
+def test_extern_python_redefine():
+    ffi = FFI()
+    ffi.cdef("""extern "Python" int bar(int);""")
+    lib = verify(ffi, 'test_extern_python_redefine', "")
+    #
+    @ffi.def_extern()
+    def bar(n):
+        return n * 10
+    assert lib.bar(42) == 420
+    #
+    @ffi.def_extern()
+    def bar(n):
+        return -n
+    assert lib.bar(42) == -42
+
+def test_extern_python_struct():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { int a, b, c; };
+        extern "Python" int bar(int, struct foo_s, int);
+        extern "Python" { struct foo_s baz(int, int);
+                          struct foo_s bok(void); }
+    """)
+    lib = verify(ffi, 'test_extern_python_struct',
+                 "struct foo_s { int a, b, c; };")
+    #
+    @ffi.def_extern()
+    def bar(x, s, z):
+        return x + s.a + s.b + s.c + z
+    res = lib.bar(1000, [1001, 1002, 1004], 1008)
+    assert res == 5015
+    #
+    @ffi.def_extern()
+    def baz(x, y):
+        return [x + y, x - y, x * y]
+    res = lib.baz(1000, 42)
+    assert res.a == 1042
+    assert res.b == 958
+    assert res.c == 42000
+    #
+    @ffi.def_extern()
+    def bok():
+        return [10, 20, 30]
+    res = lib.bok()
+    assert [res.a, res.b, res.c] == [10, 20, 30]
+
+def test_extern_python_long_double():
+    ffi = FFI()
+    ffi.cdef("""
+        extern "Python" int bar(int, long double, int);
+        extern "Python" long double baz(int, int);
+        extern "Python" long double bok(void);
+    """)
+    lib = verify(ffi, 'test_extern_python_long_double', "")
+    #
+    @ffi.def_extern()
+    def bar(x, l, z):
+        seen.append((x, l, z))
+        return 6
+    seen = []
+    lib.bar(10, 3.5, 20)
+    expected = ffi.cast("long double", 3.5)
+    assert repr(seen) == repr([(10, expected, 20)])
+    #
+    @ffi.def_extern()
+    def baz(x, z):
+        assert x == 10 and z == 20
+        return expected
+    res = lib.baz(10, 20)
+    assert repr(res) == repr(expected)
+    #
+    @ffi.def_extern()
+    def bok():
+        return expected
+    res = lib.bok()
+    assert repr(res) == repr(expected)
+
+def test_extern_python_signature():
+    ffi = FFI()
+    lib = verify(ffi, 'test_extern_python_signature', "")
+    py.test.raises(TypeError, ffi.def_extern(425), None)
+    py.test.raises(TypeError, ffi.def_extern, 'a', 'b', 'c', 'd')
+
+def test_extern_python_errors():
+    ffi = FFI()
+    ffi.cdef("""
+        extern "Python" int bar(int);
+    """)
+    lib = verify(ffi, 'test_extern_python_errors', "")
+
+    seen = []
+    def oops(*args):
+        seen.append(args)
+
+    @ffi.def_extern(onerror=oops)
+    def bar(x):
+        return x + ""
+    assert lib.bar(10) == 0
+
+    @ffi.def_extern(name="bar", onerror=oops, error=-66)
+    def bar2(x):
+        return x + ""
+    assert lib.bar(10) == -66
+
+    assert len(seen) == 2
+    exc, val, tb = seen[0]
+    assert exc is TypeError
+    assert isinstance(val, TypeError)
+    assert tb.tb_frame.f_code.co_name == "bar"
+    exc, val, tb = seen[1]
+    assert exc is TypeError
+    assert isinstance(val, TypeError)
+    assert tb.tb_frame.f_code.co_name == "bar2"
+    #
+    # a case where 'onerror' is not callable
+    py.test.raises(TypeError, ffi.def_extern(name='bar', onerror=42),
+                   lambda x: x)
+
+def test_extern_python_stdcall():
+    ffi = FFI()
+    ffi.cdef("""
+        extern "Python" int __stdcall foo(int);
+        extern "Python" int WINAPI bar(int);
+        int (__stdcall * mycb1)(int);
+        int indirect_call(int);
+    """)
+    lib = verify(ffi, 'test_extern_python_stdcall', """
+        #ifndef _MSC_VER
+        #  define __stdcall
+        #endif
+        static int (__stdcall * mycb1)(int);
+        static int indirect_call(int x) {
+            return mycb1(x);
+        }
+    """)
+    #
+    @ffi.def_extern()
+    def foo(x):
+        return x + 42
+    @ffi.def_extern()
+    def bar(x):
+        return x + 43
+    assert lib.foo(100) == 142
+    assert lib.bar(100) == 143
+    lib.mycb1 = lib.foo
+    assert lib.mycb1(200) == 242
+    assert lib.indirect_call(300) == 342
+
+def test_extern_python_plus_c():
+    ffi = FFI()
+    ffi.cdef("""
+        extern "Python+C" int foo(int);
+        extern "C +\tPython" int bar(int);
+        int call_me(int);
+    """)
+    lib = verify(ffi, 'test_extern_python_plus_c', """
+        int foo(int);
+        #ifdef __GNUC__
+        __attribute__((visibility("hidden")))
+        #endif
+        int bar(int);
+
+        static int call_me(int x) {
+            return foo(x) - bar(x);
+        }
+    """)
+    #
+    @ffi.def_extern()
+    def foo(x):
+        return x * 42
+    @ffi.def_extern()
+    def bar(x):
+        return x * 63
+    assert lib.foo(100) == 4200
+    assert lib.bar(100) == 6300
+    assert lib.call_me(100) == -2100
+
+def test_introspect_function():
+    ffi = FFI()
+    ffi.cdef("float f1(double);")
+    lib = verify(ffi, 'test_introspect_function', """
+        float f1(double x) { return x; }
+    """)
+    assert dir(lib) == ['f1']
+    FUNC = ffi.typeof(lib.f1)
+    assert FUNC.kind == 'function'
+    assert FUNC.args[0].cname == 'double'
+    assert FUNC.result.cname == 'float'
+    assert ffi.typeof(ffi.addressof(lib, 'f1')) is FUNC
+
+def test_introspect_global_var():
+    ffi = FFI()
+    ffi.cdef("float g1;")
+    lib = verify(ffi, 'test_introspect_global_var', """
+        float g1;
+    """)
+    assert dir(lib) == ['g1']
+    FLOATPTR = ffi.typeof(ffi.addressof(lib, 'g1'))
+    assert FLOATPTR.kind == 'pointer'
+    assert FLOATPTR.item.cname == 'float'
+
+def test_introspect_global_var_array():
+    ffi = FFI()
+    ffi.cdef("float g1[100];")
+    lib = verify(ffi, 'test_introspect_global_var_array', """
+        float g1[100];
+    """)
+    assert dir(lib) == ['g1']
+    FLOATARRAYPTR = ffi.typeof(ffi.addressof(lib, 'g1'))
+    assert FLOATARRAYPTR.kind == 'pointer'
+    assert FLOATARRAYPTR.item.kind == 'array'
+    assert FLOATARRAYPTR.item.length == 100
+    assert ffi.typeof(lib.g1) is FLOATARRAYPTR.item
+
+def test_introspect_integer_const():
+    ffi = FFI()
+    ffi.cdef("#define FOO 42")
+    lib = verify(ffi, 'test_introspect_integer_const', """
+        #define FOO 42
+    """)
+    assert dir(lib) == ['FOO']
+    assert lib.FOO == ffi.integer_const('FOO') == 42
+
+def test_introspect_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef int foo_t;")
+    lib = verify(ffi, 'test_introspect_typedef', """
+        typedef int foo_t;
+    """)
+    assert ffi.list_types() == (['foo_t'], [], [])
+    assert ffi.typeof('foo_t').kind == 'primitive'
+    assert ffi.typeof('foo_t').cname == 'int'
+
+def test_introspect_typedef_multiple():
+    ffi = FFI()
+    ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;")
+    lib = verify(ffi, 'test_introspect_typedef_multiple', """
+        typedef signed char a_t, c_t, g_t, b_t;
+    """)
+    assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'g_t'], [], [])
+
+def test_introspect_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a; };")
+    lib = verify(ffi, 'test_introspect_struct', """
+        struct foo_s { int a; };
+    """)
+    assert ffi.list_types() == ([], ['foo_s'], [])
+    assert ffi.typeof('struct foo_s').kind == 'struct'
+    assert ffi.typeof('struct foo_s').cname == 'struct foo_s'
+
+def test_introspect_union():
+    ffi = FFI()
+    ffi.cdef("union foo_s { int a; };")
+    lib = verify(ffi, 'test_introspect_union', """
+        union foo_s { int a; };
+    """)
+    assert ffi.list_types() == ([], [], ['foo_s'])
+    assert ffi.typeof('union foo_s').kind == 'union'
+    assert ffi.typeof('union foo_s').cname == 'union foo_s'
+
+def test_introspect_struct_and_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int a; } foo_t;")
+    lib = verify(ffi, 'test_introspect_struct_and_typedef', """
+        typedef struct { int a; } foo_t;
+    """)
+    assert ffi.list_types() == (['foo_t'], [], [])
+    assert ffi.typeof('foo_t').kind == 'struct'
+    assert ffi.typeof('foo_t').cname == 'foo_t'
+
+def test_introspect_included_type():
+    SOURCE = """
+        typedef signed char schar_t;
+        struct sint_t { int x; };
+    """
+    ffi1 = FFI()
+    ffi1.cdef(SOURCE)
+    ffi2 = FFI()
+    ffi2.include(ffi1)
+    verify(ffi1, "test_introspect_included_type_parent", SOURCE)
+    verify(ffi2, "test_introspect_included_type", SOURCE)
+    assert ffi1.list_types() == ffi2.list_types() == (
+            ['schar_t'], ['sint_t'], [])
+
+def test_introspect_order():
+    ffi = FFI()
+    ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;")
+    ffi.cdef("union CFFIg   { int a; }; typedef struct CFFIcc  { int a; } CFFIbbb;")
+    ffi.cdef("union CFFIaa  { int a; }; typedef struct CFFIa   { int a; } CFFIbb;")
+    verify(ffi, "test_introspect_order", """
+        union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;
+        union CFFIg   { int a; }; typedef struct CFFIcc  { int a; } CFFIbbb;
+        union CFFIaa  { int a; }; typedef struct CFFIa   { int a; } CFFIbb;
+    """)
+    assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'],
+                                ['CFFIa', 'CFFIcc', 'CFFIccc'],
+                                ['CFFIaa', 'CFFIaaa', 'CFFIg'])
+
+def test_bool_in_cpp():
+    # this works when compiled as C, but in cffi < 1.7 it fails as C++
+    ffi = FFI()
+    ffi.cdef("bool f(void);")
+    lib = verify(ffi, "test_bool_in_cpp", "char f(void) { return 2; }")
+    assert lib.f() is True
+
+def test_bool_in_cpp_2():
+    ffi = FFI()
+    ffi.cdef('int add(int a, int b);')
+    lib = verify(ffi, "test_bool_bug_cpp", '''
+        typedef bool _Bool;  /* there is a Windows header with this line */
+        int add(int a, int b)
+        {
+            return a + b;
+        }''', source_extension='.cpp')
+    c = lib.add(2, 3)
+    assert c == 5
+
+def test_struct_field_opaque():
+    ffi = FFI()
+    ffi.cdef("struct a { struct b b; };")
+    e = py.test.raises(TypeError, verify,
+                       ffi, "test_struct_field_opaque", "?")
+    assert str(e.value) == ("struct a: field 'a.b' is of an opaque"
+                            " type (not declared in cdef())")
+    ffi = FFI()
+    ffi.cdef("struct a { struct b b[2]; };")
+    e = py.test.raises(TypeError, verify,
+                       ffi, "test_struct_field_opaque", "?")
+    assert str(e.value) == ("struct a: field 'a.b' is of an opaque"
+                            " type (not declared in cdef())")
+    ffi = FFI()
+    ffi.cdef("struct a { struct b b[]; };")
+    e = py.test.raises(TypeError, verify,
+                       ffi, "test_struct_field_opaque", "?")
+    assert str(e.value) == ("struct a: field 'a.b' is of an opaque"
+                            " type (not declared in cdef())")
+
+def test_function_arg_opaque():
+    py.test.skip("can currently declare a function with an opaque struct "
+                 "as argument, but AFAICT it's impossible to call it later")
+
+def test_function_returns_opaque():
+    ffi = FFI()
+    ffi.cdef("struct a foo(int);")
+    e = py.test.raises(TypeError, verify,
+                       ffi, "test_function_returns_opaque", "?")
+    assert str(e.value) == ("function foo: 'struct a' is used as result type,"
+                            " but is opaque")
+
+def test_function_returns_union():
+    ffi = FFI()
+    ffi.cdef("union u1 { int a, b; }; union u1 f1(int);")
+    lib = verify(ffi, "test_function_returns_union", """
+        union u1 { int a, b; };
+        static union u1 f1(int x) { union u1 u; u.b = x; return u; }
+    """)
+    assert lib.f1(51).a == 51
+
+def test_function_returns_partial_struct():
+    ffi = FFI()
+    ffi.cdef("struct aaa { int a; ...; }; struct aaa f1(int);")
+    lib = verify(ffi, "test_function_returns_partial_struct", """
+        struct aaa { int b, a, c; };
+        static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; }
+    """)
+    assert lib.f1(52).a == 52
+
+def test_function_returns_float_complex():
+    if sys.platform == 'win32':
+        py.test.skip("MSVC may not support _Complex")
+    ffi = FFI()
+    ffi.cdef("float _Complex f1(float a, float b);");
+    lib = verify(ffi, "test_function_returns_float_complex", """
+        #include <complex.h>
+        static float _Complex f1(float a, float b) { return a + I*2.0*b; }
+    """, no_cpp=True)    # <complex.h> fails on some systems with C++
+    result = lib.f1(1.25, 5.1)
+    assert type(result) == complex
+    assert result.real == 1.25   # exact
+    assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact
+
+def test_function_returns_double_complex():
+    if sys.platform == 'win32':
+        py.test.skip("MSVC may not support _Complex")
+    ffi = FFI()
+    ffi.cdef("double _Complex f1(double a, double b);");
+    lib = verify(ffi, "test_function_returns_double_complex", """
+        #include <complex.h>
+        static double _Complex f1(double a, double b) { return a + I*2.0*b; }
+    """, no_cpp=True)    # <complex.h> fails on some systems with C++
+    result = lib.f1(1.25, 5.1)
+    assert type(result) == complex
+    assert result.real == 1.25   # exact
+    assert result.imag == 2*5.1  # exact
+
+def test_function_argument_float_complex():
+    if sys.platform == 'win32':
+        py.test.skip("MSVC may not support _Complex")
+    ffi = FFI()
+    ffi.cdef("float f1(float _Complex x);");
+    lib = verify(ffi, "test_function_argument_float_complex", """
+        #include <complex.h>
+        static float f1(float _Complex x) { return cabsf(x); }
+    """, no_cpp=True)    # <complex.h> fails on some systems with C++
+    x = complex(12.34, 56.78)
+    result = lib.f1(x)
+    assert abs(result - abs(x)) < 1e-5
+
+def test_function_argument_double_complex():
+    if sys.platform == 'win32':
+        py.test.skip("MSVC may not support _Complex")
+    ffi = FFI()
+    ffi.cdef("double f1(double _Complex);");
+    lib = verify(ffi, "test_function_argument_double_complex", """
+        #include <complex.h>
+        static double f1(double _Complex x) { return cabs(x); }
+    """, no_cpp=True)    # <complex.h> fails on some systems with C++
+    x = complex(12.34, 56.78)
+    result = lib.f1(x)
+    assert abs(result - abs(x)) < 1e-11
+
+def test_typedef_array_dotdotdot():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef int foo_t[...], bar_t[...];
+        int gv[...];
+        typedef int mat_t[...][...];
+        typedef int vmat_t[][...];
+        """)
+    lib = verify(ffi, "test_typedef_array_dotdotdot", """
+        typedef int foo_t[50], bar_t[50];
+        int gv[23];
+        typedef int mat_t[6][7];
+        typedef int vmat_t[][8];
+    """)
+    assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int")
+    assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int")
+    assert len(ffi.new("foo_t")) == 50
+    assert len(ffi.new("bar_t")) == 50
+    assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int")
+    assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int")
+    assert len(ffi.new("mat_t")) == 6
+    assert len(ffi.new("mat_t")[3]) == 7
+    py.test.raises(ffi.error, ffi.sizeof, "vmat_t")
+    p = ffi.new("vmat_t", 4)
+    assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int")
+
+def test_call_with_custom_field_pos():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { int x; ...; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_custom_field_pos", """
+        struct foo { int y, x; };
+        struct foo f(void) {
+            struct foo s = { 40, 200 };
+            return s;
+        }
+        struct foo g(int a, ...) { return f(); }
+    """)
+    assert lib.f().x == 200
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        'ctype \'struct foo\' not supported as return value.  It is a '
+        'struct declared with "...;", but the C calling convention may '
+        'depend on the missing fields; or, it contains anonymous '
+        'struct/unions.  Such structs are only supported '
+        'as return value if the function is \'API mode\' and non-variadic '
+        '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() '
+        'and not taking a final \'...\' argument)')
+
+def test_call_with_nested_anonymous_struct():
+    if sys.platform == 'win32':
+        py.test.skip("needs a GCC extension")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { int a; union { int b, c; }; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_nested_anonymous_struct", """
+        struct foo { int a; union { int b, c; }; };
+        struct foo f(void) {
+            struct foo s = { 40 };
+            s.b = 200;
+            return s;
+        }
+        struct foo g(int a, ...) { return f(); }
+    """)
+    assert lib.f().b == 200
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        'ctype \'struct foo\' not supported as return value.  It is a '
+        'struct declared with "...;", but the C calling convention may '
+        'depend on the missing fields; or, it contains anonymous '
+        'struct/unions.  Such structs are only supported '
+        'as return value if the function is \'API mode\' and non-variadic '
+        '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() '
+        'and not taking a final \'...\' argument)')
+
+def test_call_with_bitfield():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { int x:5; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_bitfield", """
+        struct foo { int x:5; };
+        struct foo f(void) {
+            struct foo s = { 11 };
+            return s;
+        }
+        struct foo g(int a, ...) { return f(); }
+    """)
+    assert lib.f().x == 11
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        "ctype 'struct foo' not supported as return value.  It is a struct "
+        "with bit fields, which libffi does not support.  Such structs are "
+        "only supported as return value if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder."
+        "set_source() and not taking a final '...' argument)")
+
+def test_call_with_zero_length_field():
+    if sys.platform == 'win32':
+        py.test.skip("zero-length field not supported by MSVC")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { int a; int x[0]; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_zero_length_field", """
+        struct foo { int a; int x[0]; };
+        struct foo f(void) {
+            struct foo s = { 42 };
+            return s;
+        }
+        struct foo g(int a, ...) { return f(); }
+    """)
+    assert lib.f().a == 42
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        "ctype 'struct foo' not supported as return value.  It is a "
+        "struct with a zero-length array, which libffi does not support."
+        "  Such structs are only supported as return value if the function is "
+        "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+        "+ffibuilder.set_source() and not taking a final '...' argument)")
+
+def test_call_with_union():
+    ffi = FFI()
+    ffi.cdef("""
+        union foo { int a; char b; };
+        union foo f(void);
+        union foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_union", """
+        union foo { int a; char b; };
+        union foo f(void) {
+            union foo s = { 42 };
+            return s;
+        }
+        union foo g(int a, ...) { return f(); }
+    """)
+    assert lib.f().a == 42
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        "ctype 'union foo' not supported as return value by libffi.  "
+        "Unions are only supported as return value if the function is "
+        "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+        "+ffibuilder.set_source() and not taking a final '...' argument)")
+
+def test_call_with_packed_struct():
+    if sys.platform == 'win32':
+        py.test.skip("needs a GCC extension")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { char y; int x; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """, packed=True)
+    lib = verify(ffi, "test_call_with_packed_struct", """
+        struct foo { char y; int x; } __attribute__((packed));
+        struct foo f(void) {
+            struct foo s = { 40, 200 };
+            return s;
+        }
+        struct foo g(int a, ...) {
+            struct foo s = { 41, 201 };
+            return s;
+        }
+    """)
+    assert ord(lib.f().y) == 40
+    assert lib.f().x == 200
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        "ctype 'struct foo' not supported as return value.  It is a "
+        "'packed' structure, with a different layout than expected by libffi."
+        "  Such structs are only supported as return value if the function is "
+        "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+        "+ffibuilder.set_source() and not taking a final '...' argument)")
+
+def test_pack_not_supported():
+    ffi = FFI()
+    ffi.cdef("""struct foo { char y; int x; };""", pack=2)
+    py.test.raises(NotImplementedError, verify,
+                   ffi, "test_pack_not_supported", "")
+
+def test_gcc_visibility_hidden():
+    if sys.platform == 'win32':
+        py.test.skip("test for gcc/clang")
+    ffi = FFI()
+    ffi.cdef("""
+    int f(int);
+    """)
+    lib = verify(ffi, "test_gcc_visibility_hidden", """
+    int f(int a) { return a + 40; }
+    """, extra_compile_args=['-fvisibility=hidden'])
+    assert lib.f(2) == 42
+
+def test_override_default_definition():
+    ffi = FFI()
+    ffi.cdef("typedef long int16_t, char16_t;")
+    lib = verify(ffi, "test_override_default_definition", "")
+    assert ffi.typeof("int16_t") is ffi.typeof("char16_t") is ffi.typeof("long")
+
+def test_char16_char32_type(no_cpp=False):
+    if no_cpp is False and sys.platform == "win32":
+        py.test.skip("aaaaaaa why do modern MSVC compilers still define "
+                     "a very old __cplusplus value")
+    ffi = FFI()
+    ffi.cdef("""
+        char16_t foo_2bytes(char16_t);
+        char32_t foo_4bytes(char32_t);
+    """)
+    lib = verify(ffi, "test_char16_char32_type" + no_cpp * "_nocpp", """
+    #if !defined(__cplusplus) || (!defined(_LIBCPP_VERSION) && __cplusplus < 201103L)
+    typedef uint_least16_t char16_t;
+    typedef uint_least32_t char32_t;
+    #endif
+
+    char16_t foo_2bytes(char16_t a) { return (char16_t)(a + 42); }
+    char32_t foo_4bytes(char32_t a) { return (char32_t)(a + 42); }
+    """, no_cpp=no_cpp)
+    assert lib.foo_2bytes(u+'\u1234') == u+'\u125e'
+    assert lib.foo_4bytes(u+'\u1234') == u+'\u125e'
+    assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f'
+    py.test.raises(TypeError, lib.foo_2bytes, u+'\U00012345')
+    py.test.raises(TypeError, lib.foo_2bytes, 1234)
+    py.test.raises(TypeError, lib.foo_4bytes, 1234)
+
+def test_char16_char32_plain_c():
+    test_char16_char32_type(no_cpp=True)
+
+def test_loader_spec():
+    ffi = FFI()
+    lib = verify(ffi, "test_loader_spec", "")
+    if sys.version_info < (3,):
+        assert not hasattr(lib, '__loader__')
+        assert not hasattr(lib, '__spec__')
+    else:
+        assert lib.__loader__ is None
+        assert lib.__spec__ is None
+
+def test_realize_struct_error():
+    ffi = FFI()
+    ffi.cdef("""typedef ... foo_t; struct foo_s { void (*x)(foo_t); };""")
+    lib = verify(ffi, "test_realize_struct_error", """
+        typedef int foo_t; struct foo_s { void (*x)(foo_t); };
+    """)
+    py.test.raises(TypeError, ffi.new, "struct foo_s *")
diff --git a/testing/cffi1/test_unicode_literals.py b/testing/cffi1/test_unicode_literals.py
new file mode 100644
index 0000000..e9825db
--- /dev/null
+++ b/testing/cffi1/test_unicode_literals.py
@@ -0,0 +1,43 @@
+#
+# ----------------------------------------------
+# WARNING, ALL LITERALS IN THIS FILE ARE UNICODE
+# ----------------------------------------------
+#
+from __future__ import unicode_literals
+#
+#
+#
+from _cffi_backend import FFI
+
+
+def test_cast():
+    ffi = FFI()
+    assert int(ffi.cast("int", 3.14)) == 3        # unicode literal
+
+def test_new():
+    ffi = FFI()
+    assert ffi.new("int[]", [3, 4, 5])[2] == 5    # unicode literal
+
+def test_typeof():
+    ffi = FFI()
+    tp = ffi.typeof("int[51]")                    # unicode literal
+    assert tp.length == 51
+
+def test_sizeof():
+    ffi = FFI()
+    assert ffi.sizeof("int[51]") == 51 * 4        # unicode literal
+
+def test_alignof():
+    ffi = FFI()
+    assert ffi.alignof("int[51]") == 4            # unicode literal
+
+def test_getctype():
+    ffi = FFI()
+    assert ffi.getctype("int**") == "int * *"     # unicode literal
+    assert type(ffi.getctype("int**")) is str
+
+def test_callback():
+    ffi = FFI()
+    cb = ffi.callback("int(int)",                 # unicode literal
+                      lambda x: x + 42)
+    assert cb(5) == 47
diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py
new file mode 100644
index 0000000..75f113d
--- /dev/null
+++ b/testing/cffi1/test_verify1.py
@@ -0,0 +1,2363 @@
+import os, sys, math, py
+from cffi import FFI, FFIError, VerificationError, VerificationMissing, model
+from cffi import CDefError
+from cffi import recompiler
+from testing.support import *
+from testing.support import _verify
+import _cffi_backend
+
+lib_m = ['m']
+if sys.platform == 'win32':
+    #there is a small chance this fails on Mingw via environ $CC
+    import distutils.ccompiler
+    if distutils.ccompiler.get_default_compiler() == 'msvc':
+        lib_m = ['msvcrt']
+    extra_compile_args = []      # no obvious -Werror equivalent on MSVC
+else:
+    if (sys.platform == 'darwin' and
+          [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]):
+        # assume a standard clang or gcc
+        extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion']
+        # special things for clang
+        extra_compile_args.append('-Qunused-arguments')
+    else:
+        # assume a standard gcc
+        extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion']
+
+class FFI(FFI):
+    error = _cffi_backend.FFI.error
+    _extra_compile_args = extra_compile_args
+    _verify_counter = 0
+
+    def verify(self, preamble='', *args, **kwds):
+        # HACK to reuse the tests from ../cffi0/test_verify.py
+        FFI._verify_counter += 1
+        module_name = 'verify%d' % FFI._verify_counter
+        try:
+            del self._assigned_source
+        except AttributeError:
+            pass
+        self.set_source(module_name, preamble)
+        return _verify(self, module_name, preamble, *args,
+                       extra_compile_args=self._extra_compile_args, **kwds)
+
+class FFI_warnings_not_error(FFI):
+    _extra_compile_args = []
+
+
+def test_missing_function(ffi=None):
+    # uses the FFI hacked above with '-Werror'
+    if ffi is None:
+        ffi = FFI()
+    ffi.cdef("void some_completely_unknown_function();")
+    try:
+        lib = ffi.verify()
+    except (VerificationError, OSError, ImportError):
+        pass     # expected case: we get a VerificationError
+    else:
+        # but depending on compiler and loader details, maybe
+        # 'lib' could actually be imported but will fail if we
+        # actually try to call the unknown function...  Hard
+        # to test anything more.
+        pass
+
+def test_missing_function_import_error():
+    # uses the original FFI that just gives a warning during compilation
+    test_missing_function(ffi=FFI_warnings_not_error())
+
+def test_simple_case():
+    ffi = FFI()
+    ffi.cdef("double sin(double x);")
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    assert lib.sin(1.23) == math.sin(1.23)
+
+def _Wconversion(cdef, source, **kargs):
+    if sys.platform in ('win32', 'darwin'):
+        py.test.skip("needs GCC")
+    ffi = FFI()
+    ffi.cdef(cdef)
+    py.test.raises(VerificationError, ffi.verify, source, **kargs)
+    extra_compile_args_orig = extra_compile_args[:]
+    extra_compile_args.remove('-Wconversion')
+    try:
+        lib = ffi.verify(source, **kargs)
+    finally:
+        extra_compile_args[:] = extra_compile_args_orig
+    return lib
+
+def test_Wconversion_unsigned():
+    _Wconversion("unsigned foo(void);",
+                 "int foo(void) { return -1;}")
+
+def test_Wconversion_integer():
+    _Wconversion("short foo(void);",
+                 "long long foo(void) { return 1<<sizeof(short);}")
+
+def test_Wconversion_floating():
+    lib = _Wconversion("float sin(double);",
+                       "#include <math.h>", libraries=lib_m)
+    res = lib.sin(1.23)
+    assert res != math.sin(1.23)     # not exact, because of double->float
+    assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_Wconversion_float2int():
+    _Wconversion("int sinf(float);",
+                 "#include <math.h>", libraries=lib_m)
+
+def test_Wconversion_double2int():
+    _Wconversion("int sin(double);",
+                 "#include <math.h>", libraries=lib_m)
+
+def test_rounding_1():
+    ffi = FFI()
+    ffi.cdef("double sinf(float x);")
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    res = lib.sinf(1.23)
+    assert res != math.sin(1.23)     # not exact, because of double->float
+    assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_rounding_2():
+    ffi = FFI()
+    ffi.cdef("double sin(float x);")
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    res = lib.sin(1.23)
+    assert res != math.sin(1.23)     # not exact, because of double->float
+    assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_strlen_exact():
+    ffi = FFI()
+    ffi.cdef("size_t strlen(const char *s);")
+    lib = ffi.verify("#include <string.h>")
+    assert lib.strlen(b"hi there!") == 9
+
+def test_strlen_approximate():
+    lib = _Wconversion("int strlen(char *s);",
+                       "#include <string.h>")
+    assert lib.strlen(b"hi there!") == 9
+
+def test_return_approximate():
+    for typename in ['short', 'int', 'long', 'long long']:
+        ffi = FFI()
+        ffi.cdef("%s foo(signed char x);" % typename)
+        lib = ffi.verify("signed char foo(signed char x) { return x;}")
+        assert lib.foo(-128) == -128
+        assert lib.foo(+127) == +127
+
+def test_strlen_array_of_char():
+    ffi = FFI()
+    ffi.cdef("size_t strlen(char[]);")
+    lib = ffi.verify("#include <string.h>")
+    assert lib.strlen(b"hello") == 5
+
+def test_longdouble():
+    ffi = FFI()
+    ffi.cdef("long double sinl(long double x);")
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    for input in [1.23,
+                  ffi.cast("double", 1.23),
+                  ffi.cast("long double", 1.23)]:
+        x = lib.sinl(input)
+        assert repr(x).startswith("<cdata 'long double'")
+        assert (float(x) - math.sin(1.23)) < 1E-10
+
+def test_longdouble_precision():
+    # Test that we don't loose any precision of 'long double' when
+    # passing through Python and CFFI.
+    ffi = FFI()
+    ffi.cdef("long double step1(long double x);")
+    SAME_SIZE = ffi.sizeof("long double") == ffi.sizeof("double")
+    lib = ffi.verify("""
+        long double step1(long double x)
+        {
+            return 4*x-x*x;
+        }
+    """)
+    def do(cast_to_double):
+        x = 0.9789
+        for i in range(10000):
+            x = lib.step1(x)
+            if cast_to_double:
+                x = float(x)
+        return float(x)
+
+    more_precise = do(False)
+    less_precise = do(True)
+    if SAME_SIZE:
+        assert more_precise == less_precise
+    else:
+        assert abs(more_precise - less_precise) > 0.1
+        # Check the particular results on Intel
+        import platform
+        if (platform.machine().startswith('i386') or
+            platform.machine().startswith('i486') or
+            platform.machine().startswith('i586') or
+            platform.machine().startswith('i686') or
+            platform.machine().startswith('x86')):
+            assert abs(more_precise - 0.656769) < 0.001
+            assert abs(less_precise - 3.99091) < 0.001
+        else:
+            py.test.skip("don't know the very exact precision of 'long double'")
+
+
+all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES
+if sys.platform == 'win32':
+    all_primitive_types = all_primitive_types.copy()
+    del all_primitive_types['ssize_t']
+all_integer_types = sorted(tp for tp in all_primitive_types
+                           if all_primitive_types[tp] == 'i')
+all_float_types = sorted(tp for tp in all_primitive_types
+                            if all_primitive_types[tp] == 'f')
+
+def all_signed_integer_types(ffi):
+    return [x for x in all_integer_types if int(ffi.cast(x, -1)) < 0]
+
+def all_unsigned_integer_types(ffi):
+    return [x for x in all_integer_types if int(ffi.cast(x, -1)) > 0]
+
+
+def test_primitive_category():
+    for typename in all_primitive_types:
+        tp = model.PrimitiveType(typename)
+        C = tp.is_char_type()
+        F = tp.is_float_type()
+        X = tp.is_complex_type()
+        I = tp.is_integer_type()
+        assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t'))
+        assert F == (typename in ('float', 'double', 'long double'))
+        assert X == (typename in ('float _Complex', 'double _Complex'))
+        assert I + F + C + X == 1      # one and only one of them is true
+
+def test_all_integer_and_float_types():
+    typenames = []
+    for typename in all_primitive_types:
+        if (all_primitive_types[typename] == 'c' or
+            all_primitive_types[typename] == 'j' or    # complex
+            typename == '_Bool' or typename == 'long double'):
+            pass
+        else:
+            typenames.append(typename)
+    #
+    ffi = FFI()
+    ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp)
+                       for tp in typenames]))
+    lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return (%s)(x+1); }" %
+                                (tp, tp.replace(' ', '_'), tp, tp)
+                                for tp in typenames]))
+    for typename in typenames:
+        foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_'))
+        assert foo(42) == 43
+        if sys.version < '3':
+            assert foo(long(44)) == 45
+        assert foo(ffi.cast(typename, 46)) == 47
+        py.test.raises(TypeError, foo, ffi.NULL)
+        #
+        # check for overflow cases
+        if all_primitive_types[typename] == 'f':
+            continue
+        for value in [-2**80, -2**40, -2**20, -2**10, -2**5, -1,
+                      2**5, 2**10, 2**20, 2**40, 2**80]:
+            overflows = int(ffi.cast(typename, value)) != value
+            if overflows:
+                py.test.raises(OverflowError, foo, value)
+            else:
+                assert foo(value) == value + 1
+
+def test_var_signed_integer_types():
+    ffi = FFI()
+    lst = all_signed_integer_types(ffi)
+    csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_'))
+                         for tp in lst])
+    ffi.cdef(csource)
+    lib = ffi.verify(csource)
+    for tp in lst:
+        varname = 'somevar_%s' % tp.replace(' ', '_')
+        sz = ffi.sizeof(tp)
+        max = (1 << (8*sz-1)) - 1
+        min = -(1 << (8*sz-1))
+        setattr(lib, varname, max)
+        assert getattr(lib, varname) == max
+        setattr(lib, varname, min)
+        assert getattr(lib, varname) == min
+        py.test.raises(OverflowError, setattr, lib, varname, max+1)
+        py.test.raises(OverflowError, setattr, lib, varname, min-1)
+
+def test_var_unsigned_integer_types():
+    ffi = FFI()
+    lst = all_unsigned_integer_types(ffi)
+    csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_'))
+                         for tp in lst])
+    ffi.cdef(csource)
+    lib = ffi.verify(csource)
+    for tp in lst:
+        varname = 'somevar_%s' % tp.replace(' ', '_')
+        sz = ffi.sizeof(tp)
+        if tp != '_Bool':
+            max = (1 << (8*sz)) - 1
+        else:
+            max = 1
+        setattr(lib, varname, max)
+        assert getattr(lib, varname) == max
+        setattr(lib, varname, 0)
+        assert getattr(lib, varname) == 0
+        py.test.raises(OverflowError, setattr, lib, varname, max+1)
+        py.test.raises(OverflowError, setattr, lib, varname, -1)
+
+def test_fn_signed_integer_types():
+    ffi = FFI()
+    lst = all_signed_integer_types(ffi)
+    cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp)
+                         for tp in lst])
+    ffi.cdef(cdefsrc)
+    verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" %
+                           (tp, tp.replace(' ', '_'), tp) for tp in lst])
+    lib = ffi.verify(verifysrc)
+    for tp in lst:
+        fnname = 'somefn_%s' % tp.replace(' ', '_')
+        sz = ffi.sizeof(tp)
+        max = (1 << (8*sz-1)) - 1
+        min = -(1 << (8*sz-1))
+        fn = getattr(lib, fnname)
+        assert fn(max) == max
+        assert fn(min) == min
+        py.test.raises(OverflowError, fn, max + 1)
+        py.test.raises(OverflowError, fn, min - 1)
+
+def test_fn_unsigned_integer_types():
+    ffi = FFI()
+    lst = all_unsigned_integer_types(ffi)
+    cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp)
+                         for tp in lst])
+    ffi.cdef(cdefsrc)
+    verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" %
+                           (tp, tp.replace(' ', '_'), tp) for tp in lst])
+    lib = ffi.verify(verifysrc)
+    for tp in lst:
+        fnname = 'somefn_%s' % tp.replace(' ', '_')
+        sz = ffi.sizeof(tp)
+        if tp != '_Bool':
+            max = (1 << (8*sz)) - 1
+        else:
+            max = 1
+        fn = getattr(lib, fnname)
+        assert fn(max) == max
+        assert fn(0) == 0
+        py.test.raises(OverflowError, fn, max + 1)
+        py.test.raises(OverflowError, fn, -1)
+
+def test_char_type():
+    ffi = FFI()
+    ffi.cdef("char foo(char);")
+    lib = ffi.verify("char foo(char x) { return ++x; }")
+    assert lib.foo(b"A") == b"B"
+    py.test.raises(TypeError, lib.foo, b"bar")
+    py.test.raises(TypeError, lib.foo, "bar")
+
+def test_wchar_type():
+    ffi = FFI()
+    if ffi.sizeof('wchar_t') == 2:
+        uniexample1 = u+'\u1234'
+        uniexample2 = u+'\u1235'
+    else:
+        uniexample1 = u+'\U00012345'
+        uniexample2 = u+'\U00012346'
+    #
+    ffi.cdef("wchar_t foo(wchar_t);")
+    lib = ffi.verify("wchar_t foo(wchar_t x) { return x+1; }")
+    assert lib.foo(uniexample1) == uniexample2
+
+def test_no_argument():
+    ffi = FFI()
+    ffi.cdef("int foo(void);")
+    lib = ffi.verify("int foo(void) { return 42; }")
+    assert lib.foo() == 42
+
+def test_two_arguments():
+    ffi = FFI()
+    ffi.cdef("int foo(int, int);")
+    lib = ffi.verify("int foo(int a, int b) { return a - b; }")
+    assert lib.foo(40, -2) == 42
+
+def test_macro():
+    ffi = FFI()
+    ffi.cdef("int foo(int, int);")
+    lib = ffi.verify("#define foo(a, b) ((a) * (b))")
+    assert lib.foo(-6, -7) == 42
+
+def test_ptr():
+    ffi = FFI()
+    ffi.cdef("int *foo(int *);")
+    lib = ffi.verify("int *foo(int *a) { return a; }")
+    assert lib.foo(ffi.NULL) == ffi.NULL
+    p = ffi.new("int *", 42)
+    q = ffi.new("int *", 42)
+    assert lib.foo(p) == p
+    assert lib.foo(q) != p
+
+def test_bogus_ptr():
+    ffi = FFI()
+    ffi.cdef("int *foo(int *);")
+    lib = ffi.verify("int *foo(int *a) { return a; }")
+    py.test.raises(TypeError, lib.foo, ffi.new("short *", 42))
+
+
+def test_verify_typedefs():
+    py.test.skip("ignored so far")
+    types = ['signed char', 'unsigned char', 'int', 'long']
+    for cdefed in types:
+        for real in types:
+            ffi = FFI()
+            ffi.cdef("typedef %s foo_t;" % cdefed)
+            if cdefed == real:
+                ffi.verify("typedef %s foo_t;" % real)
+            else:
+                py.test.raises(VerificationError, ffi.verify,
+                               "typedef %s foo_t;" % real)
+
+def test_nondecl_struct():
+    ffi = FFI()
+    ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);")
+    lib = ffi.verify("typedef struct foo_s foo_t;\n"
+                     "int bar(foo_t *f) { (void)f; return 42; }\n")
+    assert lib.bar(ffi.NULL) == 42
+
+def test_ffi_full_struct():
+    def check(verified_code):
+        ffi = FFI()
+        ffi.cdef("struct foo_s { char x; int y; long *z; };")
+        ffi.verify(verified_code)
+        ffi.new("struct foo_s *", {})
+
+    check("struct foo_s { char x; int y; long *z; };")
+    #
+    if sys.platform != 'win32':  # XXX fixme: only gives warnings
+        py.test.raises(VerificationError, check,
+            "struct foo_s { char x; int y; int *z; };")
+    #
+    py.test.raises(VerificationError, check,
+        "struct foo_s { int y; long *z; };")     # cdef'ed field x is missing
+    #
+    e = py.test.raises(FFI.error, check,
+                       "struct foo_s { int y; char x; long *z; };")
+    assert str(e.value).startswith(
+        "struct foo_s: wrong offset for field 'x'"
+        " (cdef says 0, but C compiler says 4)")
+    #
+    e = py.test.raises(FFI.error, check,
+        "struct foo_s { char x; int y; long *z; char extra; };")
+    assert str(e.value).startswith(
+        "struct foo_s: wrong total size"
+        " (cdef says %d, but C compiler says %d)" % (
+            8 + FFI().sizeof('long *'),
+            8 + FFI().sizeof('long *') * 2))
+    #
+    # a corner case that we cannot really detect, but where it has no
+    # bad consequences: the size is the same, but there is an extra field
+    # that replaces what is just padding in our declaration above
+    check("struct foo_s { char x, extra; int y; long *z; };")
+    #
+    e = py.test.raises(FFI.error, check,
+        "struct foo_s { char x; short pad; short y; long *z; };")
+    assert str(e.value).startswith(
+        "struct foo_s: wrong size for field 'y'"
+        " (cdef says 4, but C compiler says 2)")
+
+def test_ffi_nonfull_struct():
+    ffi = FFI()
+    ffi.cdef("""
+    struct foo_s {
+       int x;
+       ...;
+    };
+    """)
+    py.test.raises(VerificationMissing, ffi.sizeof, 'struct foo_s')
+    py.test.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x')
+    py.test.raises(VerificationMissing, ffi.new, 'struct foo_s *')
+    ffi.verify("""
+    struct foo_s {
+       int a, b, x, c, d, e;
+    };
+    """)
+    assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int')
+    assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int')
+
+def test_ffi_nonfull_alignment():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { char x; ...; };")
+    ffi.verify("struct foo_s { int a, b; char x; };")
+    assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int')
+    assert ffi.alignof('struct foo_s') == ffi.sizeof('int')
+
+def _check_field_match(typename, real, expect_mismatch):
+    ffi = FFI()
+    testing_by_size = (expect_mismatch == 'by_size')
+    if testing_by_size:
+        expect_mismatch = ffi.sizeof(typename) != ffi.sizeof(real)
+    ffi.cdef("struct foo_s { %s x; ...; };" % typename)
+    try:
+        ffi.verify("struct foo_s { %s x; };" % real)
+        ffi.new("struct foo_s *", [])  # because some mismatches show up lazily
+    except (VerificationError, ffi.error):
+        if not expect_mismatch:
+            if testing_by_size and typename != real:
+                print("ignoring mismatch between %s* and %s* even though "
+                      "they have the same size" % (typename, real))
+                return
+            raise AssertionError("unexpected mismatch: %s should be accepted "
+                                 "as equal to %s" % (typename, real))
+    else:
+        if expect_mismatch:
+            raise AssertionError("mismatch not detected: "
+                                 "%s != %s" % (typename, real))
+
+def test_struct_bad_sized_integer():
+    for typename in ['int8_t', 'int16_t', 'int32_t', 'int64_t']:
+        for real in ['int8_t', 'int16_t', 'int32_t', 'int64_t']:
+            _check_field_match(typename, real, "by_size")
+
+def test_struct_bad_sized_float():
+    for typename in all_float_types:
+        for real in all_float_types:
+            _check_field_match(typename, real, "by_size")
+
+def test_struct_signedness_ignored():
+    _check_field_match("int", "unsigned int", expect_mismatch=False)
+    _check_field_match("unsigned short", "signed short", expect_mismatch=False)
+
+def test_struct_float_vs_int():
+    if sys.platform == 'win32':
+        py.test.skip("XXX fixme: only gives warnings")
+    ffi = FFI()
+    for typename in all_signed_integer_types(ffi):
+        for real in all_float_types:
+            _check_field_match(typename, real, expect_mismatch=True)
+    for typename in all_float_types:
+        for real in all_signed_integer_types(ffi):
+            _check_field_match(typename, real, expect_mismatch=True)
+
+def test_struct_array_field():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[17]; ...; };")
+    ffi.verify("struct foo_s { int x; int a[17]; int y; };")
+    assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int')
+
+def test_struct_array_no_length():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[]; int y; ...; };\n"
+             "int bar(struct foo_s *);\n")
+    lib = ffi.verify("struct foo_s { int x; int a[17]; int y; };\n"
+                     "int bar(struct foo_s *f) { return f->a[14]; }\n")
+    assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.typeof(s.a) is ffi.typeof('int[]')   # implicit max length
+    assert len(s.a) == 18  # max length, computed from the size and start offset
+    s.a[14] = 4242
+    assert lib.bar(s) == 4242
+    # with no declared length, out-of-bound accesses are not detected
+    s.a[17] = -521
+    assert s.y == s.a[17] == -521
+    #
+    s = ffi.new("struct foo_s *", {'a': list(range(17))})
+    assert s.a[16] == 16
+    # overflows at construction time not detected either
+    s = ffi.new("struct foo_s *", {'a': list(range(18))})
+    assert s.y == s.a[17] == 17
+
+def test_struct_array_guess_length():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a[...]; };")
+    ffi.verify("struct foo_s { int x; int a[17]; int y; };")
+    assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int')
+    py.test.raises(IndexError, 's.a[17]')
+
+def test_struct_array_c99_1():
+    if sys.platform == 'win32':
+        py.test.skip("requires C99")
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; int a[]; };")
+    ffi.verify("struct foo_s { int x; int a[]; };")
+    assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *", [424242, 4])
+    assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int')
+    assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int')
+    # ^^^ explanation: if you write in C: "char x[5];", then
+    # "sizeof(x)" will evaluate to 5.  The behavior above is
+    # a generalization of that to "struct foo_s[len(a)=5] x;"
+    # if you could do that in C.
+    assert s.a[3] == 0
+    s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]])
+    assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int')
+    assert s.a[3] == -10
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *", [424242])
+    assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int')
+
+def test_struct_array_c99_2():
+    if sys.platform == 'win32':
+        py.test.skip("requires C99")
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; int a[]; ...; };")
+    ffi.verify("struct foo_s { int x, y; int a[]; };")
+    assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *", [424242, 4])
+    assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int')
+    assert s.a[3] == 0
+    s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]])
+    assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int')
+    assert s.a[3] == -10
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int')
+    s = ffi.new("struct foo_s *", [424242])
+    assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int')
+
+def test_struct_ptr_to_array_field():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int (*a)[17]; ...; }; struct bar_s { ...; };")
+    ffi.verify("struct foo_s { int x; int (*a)[17]; int y; };\n"
+               "struct bar_s { int x; int *a; int y; };")
+    assert ffi.sizeof('struct foo_s') == ffi.sizeof("struct bar_s")
+    s = ffi.new("struct foo_s *")
+    assert ffi.sizeof(s.a) == ffi.sizeof('int(*)[17]') == ffi.sizeof("int *")
+
+def test_struct_with_bitfield_exact():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int a:2, b:3; };")
+    ffi.verify("struct foo_s { int a:2, b:3; };")
+    s = ffi.new("struct foo_s *")
+    s.b = 3
+    py.test.raises(OverflowError, "s.b = 4")
+    assert s.b == 3
+
+def test_struct_with_bitfield_enum():
+    ffi = FFI()
+    code = """
+        typedef enum { AA, BB, CC } foo_e;
+        typedef struct { foo_e f:2; } foo_s;
+    """
+    ffi.cdef(code)
+    ffi.verify(code)
+    s = ffi.new("foo_s *")
+    s.f = 1
+    assert s.f == 1
+    if int(ffi.cast("foo_e", -1)) < 0:
+        two = -2
+    else:
+        two = 2
+    s.f = two
+    assert s.f == two
+
+def test_unsupported_struct_with_bitfield_ellipsis():
+    ffi = FFI()
+    py.test.raises(NotImplementedError, ffi.cdef,
+                   "struct foo_s { int a:2, b:3; ...; };")
+
+def test_global_constants():
+    ffi = FFI()
+    # use 'static const int', as generally documented, although in this
+    # case the 'static' is completely ignored.
+    ffi.cdef("static const int AA, BB, CC, DD;")
+    lib = ffi.verify("#define AA 42\n"
+                     "#define BB (-43)   // blah\n"
+                     "#define CC (22*2)  /* foobar */\n"
+                     "#define DD ((unsigned int)142)  /* foo\nbar */\n")
+    assert lib.AA == 42
+    assert lib.BB == -43
+    assert lib.CC == 44
+    assert lib.DD == 142
+
+def test_global_const_int_size():
+    # integer constants: ignore the declared type, always just use the value
+    for value in [-2**63, -2**31, -2**15,
+                  2**15-1, 2**15, 2**31-1, 2**31, 2**32-1, 2**32,
+                  2**63-1, 2**63, 2**64-1]:
+        ffi = FFI()
+        if value == int(ffi.cast("long long", value)):
+            if value < 0:
+                vstr = '(-%dLL-1)' % (~value,)
+            else:
+                vstr = '%dLL' % value
+        elif value == int(ffi.cast("unsigned long long", value)):
+            vstr = '%dULL' % value
+        else:
+            raise AssertionError(value)
+        ffi.cdef("static const unsigned short AA;")
+        lib = ffi.verify("#define AA %s\n" % vstr)
+        assert lib.AA == value
+        assert type(lib.AA) is type(int(lib.AA))
+
+def test_global_constants_non_int():
+    ffi = FFI()
+    ffi.cdef("static char *const PP;")
+    lib = ffi.verify('static char *const PP = "testing!";\n')
+    assert ffi.typeof(lib.PP) == ffi.typeof("char *")
+    assert ffi.string(lib.PP) == b"testing!"
+
+def test_nonfull_enum():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };")
+    py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2')
+    ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+    assert ffi.string(ffi.cast('enum ee', 11)) == "EE2"
+    assert ffi.string(ffi.cast('enum ee', -10)) == "EE3"
+    #
+    assert ffi.typeof("enum ee").relements == {'EE1': 10, 'EE2': 11, 'EE3': -10}
+    assert ffi.typeof("enum ee").elements == {10: 'EE1', 11: 'EE2', -10: 'EE3'}
+
+def test_full_enum():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1, EE2, EE3 };")
+    lib = ffi.verify("enum ee { EE1, EE2, EE3 };")
+    assert [lib.EE1, lib.EE2, lib.EE3] == [0, 1, 2]
+
+def test_enum_usage():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;")
+    lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;")
+    assert lib.EE2 == 1
+    s = ffi.new("sp", [lib.EE2])
+    assert s.x == 1
+    s.x = 17
+    assert s.x == 17
+
+def test_anonymous_enum():
+    ffi = FFI()
+    ffi.cdef("enum { EE1 }; enum { EE2, EE3 };")
+    lib = ffi.verify("enum { EE1 }; enum { EE2, EE3 };")
+    assert lib.EE1 == 0
+    assert lib.EE2 == 0
+    assert lib.EE3 == 1
+
+def test_nonfull_anonymous_enum():
+    ffi = FFI()
+    ffi.cdef("enum { EE1, ... }; enum { EE3, ... };")
+    lib = ffi.verify("enum { EE2, EE1 }; enum { EE3 };")
+    assert lib.EE1 == 1
+    assert lib.EE3 == 0
+
+def test_nonfull_enum_syntax2():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };")
+    py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1')
+    ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+    assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2'
+    assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3'
+    #
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1, EE2=\t... };")
+    py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1')
+    ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+    assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2'
+    #
+    ffi = FFI()
+    ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };")
+    ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ")
+    assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4'
+    assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5'
+
+def test_get_set_errno():
+    ffi = FFI()
+    ffi.cdef("int foo(int);")
+    lib = ffi.verify("""
+        static int foo(int x)
+        {
+            errno += 1;
+            return x * 7;
+        }
+    """)
+    ffi.errno = 15
+    assert lib.foo(6) == 42
+    assert ffi.errno == 16
+
+def test_define_int():
+    ffi = FFI()
+    ffi.cdef("#define FOO ...\n"
+             "\t#\tdefine\tBAR\t...\t\n"
+             "#define BAZ ...\n")
+    lib = ffi.verify("#define FOO 42\n"
+                     "#define BAR (-44)\n"
+                     "#define BAZ 0xffffffffffffffffULL\n")
+    assert lib.FOO == 42
+    assert lib.BAR == -44
+    assert lib.BAZ == 0xffffffffffffffff
+
+def test_access_variable():
+    ffi = FFI()
+    ffi.cdef("int foo(void);\n"
+             "int somenumber;")
+    lib = ffi.verify("""
+        static int somenumber = 2;
+        static int foo(void) {
+            return somenumber * 7;
+        }
+    """)
+    assert lib.somenumber == 2
+    assert lib.foo() == 14
+    lib.somenumber = -6
+    assert lib.foo() == -42
+    assert lib.somenumber == -6
+    lib.somenumber = 2   # reset for the next run, if any
+
+def test_access_address_of_variable():
+    # access the address of 'somenumber': need a trick
+    ffi = FFI()
+    ffi.cdef("int somenumber; static int *const somenumberptr;")
+    lib = ffi.verify("""
+        static int somenumber = 2;
+        #define somenumberptr (&somenumber)
+    """)
+    assert lib.somenumber == 2
+    lib.somenumberptr[0] = 42
+    assert lib.somenumber == 42
+    lib.somenumber = 2    # reset for the next run, if any
+
+def test_access_array_variable(length=5):
+    ffi = FFI()
+    ffi.cdef("int foo(int);\n"
+             "int somenumber[%s];" % (length,))
+    lib = ffi.verify("""
+        static int somenumber[] = {2, 2, 3, 4, 5};
+        static int foo(int i) {
+            return somenumber[i] * 7;
+        }
+    """)
+    if length == '':
+        # a global variable of an unknown array length is implicitly
+        # transformed into a global pointer variable, because we can only
+        # work with array instances whose length we know.  using a pointer
+        # instead of an array gives the correct effects.
+        assert repr(lib.somenumber).startswith("<cdata 'int *' 0x")
+        py.test.raises(TypeError, len, lib.somenumber)
+    else:
+        assert repr(lib.somenumber).startswith("<cdata 'int[%s]' 0x" % length)
+        assert len(lib.somenumber) == 5
+    assert lib.somenumber[3] == 4
+    assert lib.foo(3) == 28
+    lib.somenumber[3] = -6
+    assert lib.foo(3) == -42
+    assert lib.somenumber[3] == -6
+    assert lib.somenumber[4] == 5
+    lib.somenumber[3] = 4    # reset for the next run, if any
+
+def test_access_array_variable_length_hidden():
+    test_access_array_variable(length='')
+
+def test_access_struct_variable():
+    ffi = FFI()
+    ffi.cdef("struct foo { int x; ...; };\n"
+             "int foo(int);\n"
+             "struct foo stuff;")
+    lib = ffi.verify("""
+        struct foo { int x, y, z; };
+        static struct foo stuff = {2, 5, 8};
+        static int foo(int i) {
+            switch (i) {
+            case 0: return stuff.x * 7;
+            case 1: return stuff.y * 7;
+            case 2: return stuff.z * 7;
+            }
+            return -1;
+        }
+    """)
+    assert lib.stuff.x == 2
+    assert lib.foo(0) == 14
+    assert lib.foo(1) == 35
+    assert lib.foo(2) == 56
+    lib.stuff.x = -6
+    assert lib.foo(0) == -42
+    assert lib.foo(1) == 35
+    lib.stuff.x = 2      # reset for the next run, if any
+
+def test_access_callback():
+    ffi = FFI()
+    ffi.cdef("int (*cb)(int);\n"
+             "int foo(int);\n"
+             "void reset_cb(void);")
+    lib = ffi.verify("""
+        static int g(int x) { return x * 7; }
+        static int (*cb)(int);
+        static int foo(int i) { return cb(i) - 1; }
+        static void reset_cb(void) { cb = g; }
+    """)
+    lib.reset_cb()
+    assert lib.foo(6) == 41
+    my_callback = ffi.callback("int(*)(int)", lambda n: n * 222)
+    lib.cb = my_callback
+    assert lib.foo(4) == 887
+
+def test_access_callback_function_typedef():
+    ffi = FFI()
+    ffi.cdef("typedef int mycallback_t(int);\n"
+             "mycallback_t *cb;\n"
+             "int foo(int);\n"
+             "void reset_cb(void);")
+    lib = ffi.verify("""
+        static int g(int x) { return x * 7; }
+        static int (*cb)(int);
+        static int foo(int i) { return cb(i) - 1; }
+        static void reset_cb(void) { cb = g; }
+    """)
+    lib.reset_cb()
+    assert lib.foo(6) == 41
+    my_callback = ffi.callback("int(*)(int)", lambda n: n * 222)
+    lib.cb = my_callback
+    assert lib.foo(4) == 887
+
+def test_call_with_struct_ptr():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; ...; } foo_t; int foo(foo_t *);")
+    lib = ffi.verify("""
+        typedef struct { int y, x; } foo_t;
+        static int foo(foo_t *f) { return f->x * 7; }
+    """)
+    f = ffi.new("foo_t *")
+    f.x = 6
+    assert lib.foo(f) == 42
+
+def test_unknown_type():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef ... token_t;
+        int foo(token_t *);
+        #define TOKEN_SIZE ...
+    """)
+    lib = ffi.verify("""
+        typedef float token_t;
+        static int foo(token_t *tk) {
+            if (!tk)
+                return -42;
+            *tk += 1.601f;
+            return (int)*tk;
+        }
+        #define TOKEN_SIZE sizeof(token_t)
+    """)
+    # we cannot let ffi.new("token_t *") work, because we don't know ahead of
+    # time if it's ok to ask 'sizeof(token_t)' in the C code or not.
+    # See test_unknown_type_2.  Workaround.
+    tkmem = ffi.new("char[]", lib.TOKEN_SIZE)    # zero-initialized
+    tk = ffi.cast("token_t *", tkmem)
+    results = [lib.foo(tk) for i in range(6)]
+    assert results == [1, 3, 4, 6, 8, 9]
+    assert lib.foo(ffi.NULL) == -42
+
+def test_unknown_type_2():
+    ffi = FFI()
+    ffi.cdef("typedef ... token_t;")
+    lib = ffi.verify("typedef struct token_s token_t;")
+    # assert did not crash, even though 'sizeof(token_t)' is not valid in C.
+
+def test_unknown_type_3():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef ... *token_p;
+        token_p foo(token_p);
+    """)
+    lib = ffi.verify("""
+        typedef struct _token_s *token_p;
+        token_p foo(token_p arg) {
+            if (arg)
+                return (token_p)0x12347;
+            else
+                return (token_p)0x12345;
+        }
+    """)
+    p = lib.foo(ffi.NULL)
+    assert int(ffi.cast("intptr_t", p)) == 0x12345
+    q = lib.foo(p)
+    assert int(ffi.cast("intptr_t", q)) == 0x12347
+
+def test_varargs():
+    ffi = FFI()
+    ffi.cdef("int foo(int x, ...);")
+    lib = ffi.verify("""
+        int foo(int x, ...) {
+            va_list vargs;
+            va_start(vargs, x);
+            x -= va_arg(vargs, int);
+            x -= va_arg(vargs, int);
+            va_end(vargs);
+            return x;
+        }
+    """)
+    assert lib.foo(50, ffi.cast("int", 5), ffi.cast("int", 3)) == 42
+
+def test_varargs_exact():
+    if sys.platform == 'win32':
+        py.test.skip("XXX fixme: only gives warnings")
+    ffi = FFI()
+    ffi.cdef("int foo(int x, ...);")
+    py.test.raises(VerificationError, ffi.verify, """
+        int foo(long long x, ...) {
+            return x;
+        }
+    """)
+
+def test_varargs_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { char a; int b; }; int foo(int x, ...);")
+    lib = ffi.verify("""
+        struct foo_s {
+            char a; int b;
+        };
+        int foo(int x, ...) {
+            va_list vargs;
+            struct foo_s s;
+            va_start(vargs, x);
+            s = va_arg(vargs, struct foo_s);
+            va_end(vargs);
+            return s.a - s.b;
+        }
+    """)
+    s = ffi.new("struct foo_s *", [b'B', 1])
+    assert lib.foo(50, s[0]) == ord('A')
+
+def test_autofilled_struct_as_argument():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { long a; double b; ...; };\n"
+             "int foo(struct foo_s);")
+    lib = ffi.verify("""
+        struct foo_s {
+            double b;
+            long a;
+        };
+        int foo(struct foo_s s) {
+            return (int)s.a - (int)s.b;
+        }
+    """)
+    s = ffi.new("struct foo_s *", [100, 1])
+    assert lib.foo(s[0]) == 99
+    assert lib.foo([100, 1]) == 99
+
+def test_autofilled_struct_as_argument_dynamic():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { long a; ...; };\n"
+             "int (*foo)(struct foo_s);")
+    lib = ffi.verify("""
+        struct foo_s {
+            double b;
+            long a;
+        };
+        int foo1(struct foo_s s) {
+            return (int)s.a - (int)s.b;
+        }
+        int (*foo)(struct foo_s s) = &foo1;
+    """)
+    e = py.test.raises(NotImplementedError, lib.foo, "?")
+    msg = ("ctype 'struct foo_s' not supported as argument.  It is a struct "
+           'declared with "...;", but the C calling convention may depend on '
+           "the missing fields; or, it contains anonymous struct/unions.  "
+           "Such structs are only supported as argument "
+           "if the function is 'API mode' and non-variadic (i.e. declared "
+           "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking "
+           "a final '...' argument)")
+    assert str(e.value) == msg
+
+def test_func_returns_struct():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { int aa, bb; };
+        struct foo_s foo(int a, int b);
+    """)
+    lib = ffi.verify("""
+        struct foo_s { int aa, bb; };
+        struct foo_s foo(int a, int b) {
+            struct foo_s r;
+            r.aa = a*a;
+            r.bb = b*b;
+            return r;
+        }
+    """)
+    s = lib.foo(6, 7)
+    assert repr(s) == "<cdata 'struct foo_s' owning 8 bytes>"
+    assert s.aa == 36
+    assert s.bb == 49
+
+def test_func_as_funcptr():
+    ffi = FFI()
+    ffi.cdef("int *(*const fooptr)(void);")
+    lib = ffi.verify("""
+        int *foo(void) {
+            return (int*)"foobar";
+        }
+        int *(*fooptr)(void) = foo;
+    """)
+    foochar = ffi.cast("char *(*)(void)", lib.fooptr)
+    s = foochar()
+    assert ffi.string(s) == b"foobar"
+
+def test_funcptr_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        void qsort(void *base, size_t nel, size_t width,
+            int (*compar)(const void *, const void *));
+    """)
+    ffi.verify("#include <stdlib.h>")
+
+def test_func_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        void qsort(void *base, size_t nel, size_t width,
+            int compar(const void *, const void *));
+    """)
+    ffi.verify("#include <stdlib.h>")
+
+def test_array_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        size_t strlen(char string[]);
+    """)
+    ffi.verify("#include <string.h>")
+
+def test_enum_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        enum foo_e { AA, BB, ... };
+        int foo_func(enum foo_e);
+    """)
+    lib = ffi.verify("""
+        enum foo_e { AA, CC, BB };
+        int foo_func(enum foo_e e) { return (int)e; }
+    """)
+    assert lib.foo_func(lib.BB) == 2
+    py.test.raises(TypeError, lib.foo_func, "BB")
+
+def test_enum_as_function_result():
+    ffi = FFI()
+    ffi.cdef("""
+        enum foo_e { AA, BB, ... };
+        enum foo_e foo_func(int x);
+    """)
+    lib = ffi.verify("""
+        enum foo_e { AA, CC, BB };
+        enum foo_e foo_func(int x) { return (enum foo_e)x; }
+    """)
+    assert lib.foo_func(lib.BB) == lib.BB == 2
+
+def test_enum_values():
+    ffi = FFI()
+    ffi.cdef("enum enum1_e { AA, BB };")
+    lib = ffi.verify("enum enum1_e { AA, BB };")
+    assert lib.AA == 0
+    assert lib.BB == 1
+    assert ffi.string(ffi.cast("enum enum1_e", 1)) == 'BB'
+
+def test_typedef_complete_enum():
+    ffi = FFI()
+    ffi.cdef("typedef enum { AA, BB } enum1_t;")
+    lib = ffi.verify("typedef enum { AA, BB } enum1_t;")
+    assert ffi.string(ffi.cast("enum1_t", 1)) == 'BB'
+    assert lib.AA == 0
+    assert lib.BB == 1
+
+def test_typedef_broken_complete_enum():
+    # xxx this is broken in old cffis, but works with recompiler.py
+    ffi = FFI()
+    ffi.cdef("typedef enum { AA, BB } enum1_t;")
+    lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;")
+    assert lib.AA == 0
+    assert lib.BB == 2
+
+def test_typedef_incomplete_enum():
+    ffi = FFI()
+    ffi.cdef("typedef enum { AA, BB, ... } enum1_t;")
+    lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;")
+    assert ffi.string(ffi.cast("enum1_t", 1)) == '1'
+    assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB'
+    assert lib.AA == 0
+    assert lib.BB == 2
+
+def test_typedef_enum_as_argument():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef enum { AA, BB, ... } foo_t;
+        int foo_func(foo_t);
+    """)
+    lib = ffi.verify("""
+        typedef enum { AA, CC, BB } foo_t;
+        int foo_func(foo_t e) { return (int)e; }
+    """)
+    assert lib.foo_func(lib.BB) == lib.BB == 2
+    py.test.raises(TypeError, lib.foo_func, "BB")
+
+def test_typedef_enum_as_function_result():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef enum { AA, BB, ... } foo_t;
+        foo_t foo_func(int x);
+    """)
+    lib = ffi.verify("""
+        typedef enum { AA, CC, BB } foo_t;
+        foo_t foo_func(int x) { return (foo_t)x; }
+    """)
+    assert lib.foo_func(lib.BB) == lib.BB == 2
+
+def test_function_typedef():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef double func_t(double);
+        func_t sin;
+    """)
+    lib = ffi.verify('#include <math.h>', libraries=lib_m)
+    assert lib.sin(1.23) == math.sin(1.23)
+
+def test_opaque_integer_as_function_result():
+    #import platform
+    #if platform.machine().startswith('sparc'):
+    #    py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)')
+    #elif platform.machine() == 'mips64' and sys.maxsize > 2**32:
+    #    py.test.skip('Segfaults on mips64el')
+    # XXX bad abuse of "struct { ...; }".  It only works a bit by chance
+    # anyway.  XXX think about something better :-(
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct { ...; } myhandle_t;
+        myhandle_t foo(void);
+    """)
+    lib = ffi.verify("""
+        typedef short myhandle_t;
+        myhandle_t foo(void) { return 42; }
+    """)
+    h = lib.foo()
+    assert ffi.sizeof(h) == ffi.sizeof("short")
+
+def test_return_partial_struct():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct { int x; ...; } foo_t;
+        foo_t foo(void);
+    """)
+    lib = ffi.verify("""
+        typedef struct { int y, x; } foo_t;
+        foo_t foo(void) { foo_t r = { 45, 81 }; return r; }
+    """)
+    h = lib.foo()
+    assert ffi.sizeof(h) == 2 * ffi.sizeof("int")
+    assert h.x == 81
+
+def test_take_and_return_partial_structs():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct { int x; ...; } foo_t;
+        foo_t foo(foo_t, foo_t);
+    """)
+    lib = ffi.verify("""
+        typedef struct { int y, x; } foo_t;
+        foo_t foo(foo_t a, foo_t b) {
+            foo_t r = { 100, a.x * 5 + b.x * 7 };
+            return r;
+        }
+    """)
+    args = ffi.new("foo_t[3]")
+    args[0].x = 1000
+    args[2].x = -498
+    h = lib.foo(args[0], args[2])
+    assert ffi.sizeof(h) == 2 * ffi.sizeof("int")
+    assert h.x == 1000 * 5 - 498 * 7
+
+def test_cannot_name_struct_type():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; } **sp; void foo(sp);")
+    e = py.test.raises(VerificationError, ffi.verify,
+                       "typedef struct { int x; } **sp; void foo(sp x) { }")
+    assert 'in argument of foo: unknown type name' in str(e.value)
+
+def test_dont_check_unnamable_fields():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { struct { int x; } someone; };")
+    ffi.verify("struct foo_s { struct { int x; } someone; };")
+    # assert did not crash
+
+def test_nested_anonymous_struct_exact():
+    if sys.platform == 'win32':
+        py.test.skip("nested anonymous struct/union")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { struct { int a; char b; }; union { char c, d; }; };
+    """)
+    assert ffi.offsetof("struct foo_s", "c") == 2 * ffi.sizeof("int")
+    assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int")
+    ffi.verify("""
+        struct foo_s { struct { int a; char b; }; union { char c, d; }; };
+    """)
+    p = ffi.new("struct foo_s *")
+    assert ffi.sizeof(p[0]) == 3 * ffi.sizeof("int")    # with alignment
+    p.a = 1234567
+    p.b = b'X'
+    p.c = b'Y'
+    assert p.a == 1234567
+    assert p.b == b'X'
+    assert p.c == b'Y'
+    assert p.d == b'Y'
+
+def test_nested_anonymous_struct_exact_error():
+    if sys.platform == 'win32':
+        py.test.skip("nested anonymous struct/union")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { struct { int a; char b; }; union { char c, d; }; };
+    """)
+    py.test.raises(VerificationError, ffi.verify, """
+        struct foo_s { struct { int a; short b; }; union { char c, d; }; };
+    """)
+    # works fine now
+    #py.test.raises(VerificationError, ffi.verify, """
+    #    struct foo_s { struct { int a; char e, b; }; union { char c, d; }; };
+    #""")
+
+def test_nested_anonymous_struct_inexact_1():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { struct { char b; ...; }; union { char c, d; }; };
+    """)
+    ffi.verify("""
+        struct foo_s { int a, padding; char c, d, b; };
+    """)
+    assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int")
+
+def test_nested_anonymous_struct_inexact_2():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { union { char c, d; }; struct { int a; char b; }; ...; };
+    """)
+    ffi.verify("""
+        struct foo_s { int a, padding; char c, d, b; };
+    """)
+    assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int")
+
+def test_ffi_union():
+    ffi = FFI()
+    ffi.cdef("union foo_u { char x; long *z; };")
+    ffi.verify("union foo_u { char x; int y; long *z; };")
+
+def test_ffi_union_partial():
+    ffi = FFI()
+    ffi.cdef("union foo_u { char x; ...; };")
+    ffi.verify("union foo_u { char x; int y; };")
+    assert ffi.sizeof("union foo_u") == 4
+
+def test_ffi_union_with_partial_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int x; ...; }; union foo_u { struct foo_s s; };")
+    ffi.verify("struct foo_s { int a; int x; }; "
+               "union foo_u { char b[32]; struct foo_s s; };")
+    assert ffi.sizeof("struct foo_s") == 8
+    assert ffi.sizeof("union foo_u") == 32
+
+def test_ffi_union_partial_2():
+    ffi = FFI()
+    ffi.cdef("typedef union { char x; ...; } u1;")
+    ffi.verify("typedef union { char x; int y; } u1;")
+    assert ffi.sizeof("u1") == 4
+
+def test_ffi_union_with_partial_struct_2():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; ...; } s1;"
+             "typedef union { s1 s; } u1;")
+    ffi.verify("typedef struct { int a; int x; } s1; "
+               "typedef union { char b[32]; s1 s; } u1;")
+    assert ffi.sizeof("s1") == 8
+    assert ffi.sizeof("u1") == 32
+    assert ffi.offsetof("u1", "s") == 0
+
+def test_ffi_struct_packed():
+    if sys.platform == 'win32':
+        py.test.skip("needs a GCC extension")
+    ffi = FFI()
+    ffi.cdef("struct foo_s { int b; ...; };")
+    ffi.verify("""
+        struct foo_s {
+            char a;
+            int b;
+        } __attribute__((packed));
+    """)
+
+def test_tmpdir():
+    import tempfile, os
+    from testing.udir import udir
+    tmpdir = tempfile.mkdtemp(dir=str(udir))
+    ffi = FFI()
+    ffi.cdef("int foo(int);")
+    lib = ffi.verify("int foo(int a) { return a + 42; }", tmpdir=tmpdir)
+    assert os.listdir(tmpdir)
+    assert lib.foo(100) == 142
+
+def test_relative_to():
+    py.test.skip("not available")
+    import tempfile, os
+    from testing.udir import udir
+    tmpdir = tempfile.mkdtemp(dir=str(udir))
+    ffi = FFI()
+    ffi.cdef("int foo(int);")
+    f = open(os.path.join(tmpdir, 'foo.h'), 'w')
+    f.write("int foo(int a) { return a + 42; }\n")
+    f.close()
+    lib = ffi.verify('#include "foo.h"',
+                     include_dirs=['.'],
+                     relative_to=os.path.join(tmpdir, 'x'))
+    assert lib.foo(100) == 142
+
+def test_bug1():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef struct tdlhandle_s { ...; } *tdl_handle_t;
+        typedef struct my_error_code_ {
+            tdl_handle_t *rh;
+        } my_error_code_t;
+    """)
+    ffi.verify("""
+        typedef struct tdlhandle_s { int foo; } *tdl_handle_t;
+        typedef struct my_error_code_ {
+            tdl_handle_t *rh;
+        } my_error_code_t;
+    """)
+
+def test_bool():
+    if sys.platform == 'win32':
+        py.test.skip("_Bool not in MSVC")
+    ffi = FFI()
+    ffi.cdef("struct foo_s { _Bool x; };"
+             "_Bool foo(_Bool); _Bool (*foop)(_Bool);")
+    lib = ffi.verify("""
+        struct foo_s { _Bool x; };
+        int foo(int arg) {
+            return !arg;
+        }
+        _Bool _foofunc(_Bool x) {
+            return !x;
+        }
+        _Bool (*foop)(_Bool) = _foofunc;
+    """)
+    p = ffi.new("struct foo_s *")
+    p.x = 1
+    assert p.x is True
+    py.test.raises(OverflowError, "p.x = -1")
+    py.test.raises(TypeError, "p.x = 0.0")
+    assert lib.foop(1) is False
+    assert lib.foop(True) is False
+    assert lib.foop(0) is True
+    py.test.raises(OverflowError, lib.foop, 42)
+    py.test.raises(TypeError, lib.foop, 0.0)
+    assert lib.foo(1) is False
+    assert lib.foo(True) is False
+    assert lib.foo(0) is True
+    py.test.raises(OverflowError, lib.foo, 42)
+    py.test.raises(TypeError, lib.foo, 0.0)
+    assert int(ffi.cast("_Bool", long(1))) == 1
+    assert int(ffi.cast("_Bool", long(0))) == 0
+    assert int(ffi.cast("_Bool", long(-1))) == 1
+    assert int(ffi.cast("_Bool", 10**200)) == 1
+    assert int(ffi.cast("_Bool", 10**40000)) == 1
+    #
+    class Foo(object):
+        def __int__(self):
+            self.seen = 1
+            return result
+    f = Foo()
+    f.seen = 0
+    result = 42
+    assert int(ffi.cast("_Bool", f)) == 1
+    assert f.seen
+    f.seen = 0
+    result = 0
+    assert int(ffi.cast("_Bool", f)) == 0
+    assert f.seen
+    #
+    py.test.raises(TypeError, ffi.cast, "_Bool", [])
+
+def test_bool_on_long_double():
+    if sys.platform == 'win32':
+        py.test.skip("_Bool not in MSVC")
+    f = 1E-250
+    if f == 0.0 or f*f != 0.0:
+        py.test.skip("unexpected precision")
+    ffi = FFI()
+    ffi.cdef("long double square(long double f); _Bool opposite(_Bool);")
+    lib = ffi.verify("long double square(long double f) { return f*f; }\n"
+                     "_Bool opposite(_Bool x) { return !x; }")
+    f0 = lib.square(0.0)
+    f2 = lib.square(f)
+    f3 = lib.square(f * 2.0)
+    if repr(f2) == repr(f3):
+        py.test.skip("long double doesn't have enough precision")
+    assert float(f0) == float(f2) == float(f3) == 0.0  # too tiny for 'double'
+    assert int(ffi.cast("_Bool", f2)) == 1
+    assert int(ffi.cast("_Bool", f3)) == 1
+    assert int(ffi.cast("_Bool", f0)) == 0
+    py.test.raises(TypeError, lib.opposite, f2)
+
+def test_cannot_pass_float():
+    for basetype in ['char', 'short', 'int', 'long', 'long long']:
+        for sign in ['signed', 'unsigned']:
+            type = '%s %s' % (sign, basetype)
+            ffi = FFI()
+            ffi.cdef("struct foo_s { %s x; };\n"
+                     "int foo(%s);" % (type, type))
+            lib = ffi.verify("""
+                struct foo_s { %s x; };
+                int foo(%s arg) {
+                    return !arg;
+                }
+            """ % (type, type))
+            p = ffi.new("struct foo_s *")
+            py.test.raises(TypeError, "p.x = 0.0")
+            assert lib.foo(42) == 0
+            assert lib.foo(0) == 1
+            py.test.raises(TypeError, lib.foo, 0.0)
+
+def test_addressof():
+    ffi = FFI()
+    ffi.cdef("""
+        struct point_s { int x, y; };
+        struct foo_s { int z; struct point_s point; };
+        struct point_s sum_coord(struct point_s *);
+    """)
+    lib = ffi.verify("""
+        struct point_s { int x, y; };
+        struct foo_s { int z; struct point_s point; };
+        struct point_s sum_coord(struct point_s *point) {
+            struct point_s r;
+            r.x = point->x + point->y;
+            r.y = point->x - point->y;
+            return r;
+        }
+    """)
+    p = ffi.new("struct foo_s *")
+    p.point.x = 16
+    p.point.y = 9
+    py.test.raises(TypeError, lib.sum_coord, p.point)
+    res = lib.sum_coord(ffi.addressof(p.point))
+    assert res.x == 25
+    assert res.y == 7
+    res2 = lib.sum_coord(ffi.addressof(res))
+    assert res2.x == 32
+    assert res2.y == 18
+    py.test.raises(TypeError, lib.sum_coord, res2)
+
+def test_callback_in_thread():
+    py.test.xfail("adapt or remove")
+    if sys.platform == 'win32':
+        py.test.skip("pthread only")
+    import os, subprocess, imp
+    arg = os.path.join(os.path.dirname(__file__), 'callback_in_thread.py')
+    g = subprocess.Popen([sys.executable, arg,
+                          os.path.dirname(imp.find_module('cffi')[1])])
+    result = g.wait()
+    assert result == 0
+
+def test_keepalive_lib():
+    py.test.xfail("adapt or remove")
+    ffi = FFI()
+    ffi.cdef("int foobar(void);")
+    lib = ffi.verify("int foobar(void) { return 42; }")
+    func = lib.foobar
+    ffi_r = weakref.ref(ffi)
+    lib_r = weakref.ref(lib)
+    del ffi
+    import gc; gc.collect()       # lib stays alive
+    assert lib_r() is not None
+    assert ffi_r() is not None
+    assert func() == 42
+
+def test_keepalive_ffi():
+    py.test.xfail("adapt or remove")
+    ffi = FFI()
+    ffi.cdef("int foobar(void);")
+    lib = ffi.verify("int foobar(void) { return 42; }")
+    func = lib.foobar
+    ffi_r = weakref.ref(ffi)
+    lib_r = weakref.ref(lib)
+    del lib
+    import gc; gc.collect()       # ffi stays alive
+    assert ffi_r() is not None
+    assert lib_r() is not None
+    assert func() == 42
+
+def test_FILE_stored_in_stdout():
+    if not sys.platform.startswith('linux'):
+        py.test.skip("likely, we cannot assign to stdout")
+    ffi = FFI()
+    ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);")
+    lib = ffi.verify("""
+        #include <stdio.h>
+        FILE *setstdout(FILE *f) {
+            FILE *result = stdout;
+            stdout = f;
+            return result;
+        }
+    """)
+    import os
+    fdr, fdw = os.pipe()
+    fw1 = os.fdopen(fdw, 'wb', 256)
+    old_stdout = lib.setstdout(fw1)
+    try:
+        #
+        fw1.write(b"X")
+        r = lib.printf(b"hello, %d!\n", ffi.cast("int", 42))
+        fw1.close()
+        assert r == len("hello, 42!\n")
+        #
+    finally:
+        lib.setstdout(old_stdout)
+    #
+    result = os.read(fdr, 256)
+    os.close(fdr)
+    # the 'X' might remain in the user-level buffer of 'fw1' and
+    # end up showing up after the 'hello, 42!\n'
+    assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX"
+
+def test_FILE_stored_explicitly():
+    ffi = FFI()
+    ffi.cdef("int myprintf11(const char *, int); FILE *myfile;")
+    lib = ffi.verify("""
+        #include <stdio.h>
+        FILE *myfile;
+        int myprintf11(const char *out, int value) {
+            return fprintf(myfile, out, value);
+        }
+    """)
+    import os
+    fdr, fdw = os.pipe()
+    fw1 = os.fdopen(fdw, 'wb', 256)
+    lib.myfile = ffi.cast("FILE *", fw1)
+    #
+    fw1.write(b"X")
+    r = lib.myprintf11(b"hello, %d!\n", ffi.cast("int", 42))
+    fw1.close()
+    assert r == len("hello, 42!\n")
+    #
+    result = os.read(fdr, 256)
+    os.close(fdr)
+    # the 'X' might remain in the user-level buffer of 'fw1' and
+    # end up showing up after the 'hello, 42!\n'
+    assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX"
+
+def test_global_array_with_missing_length():
+    ffi = FFI()
+    ffi.cdef("int fooarray[];")
+    lib = ffi.verify("int fooarray[50];")
+    assert repr(lib.fooarray).startswith("<cdata 'int *'")
+
+def test_global_array_with_dotdotdot_length():
+    ffi = FFI()
+    ffi.cdef("int fooarray[...];")
+    lib = ffi.verify("int fooarray[50];")
+    assert repr(lib.fooarray).startswith("<cdata 'int[50]'")
+
+def test_bad_global_array_with_dotdotdot_length():
+    py.test.xfail("was detected only because 23 bytes cannot be divided by 4; "
+                  "redo more generally")
+    ffi = FFI()
+    ffi.cdef("int fooarray[...];")
+    py.test.raises(VerificationError, ffi.verify, "char fooarray[23];")
+
+def test_struct_containing_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { ...; }; struct bar_s { struct foo_s f; ...; };")
+    ffi.verify("struct foo_s { int x; }; struct bar_s { struct foo_s f; };")
+    #
+    ffi = FFI()
+    ffi.cdef("struct foo_s { struct bar_s f; ...; }; struct bar_s { ...; };")
+    ffi.verify("struct bar_s { int x; }; struct foo_s { struct bar_s f; };")
+
+def test_struct_returned_by_func():
+    ffi = FFI()
+    ffi.cdef("typedef ... foo_t; foo_t myfunc(void);")
+    e = py.test.raises(TypeError, ffi.verify,
+                       "typedef struct { int x; } foo_t; "
+                       "foo_t myfunc(void) { foo_t x = { 42 }; return x; }")
+    assert str(e.value) == (
+        "function myfunc: 'foo_t' is used as result type, but is opaque")
+
+def test_include():
+    ffi1 = FFI()
+    ffi1.cdef("typedef struct { int x; ...; } foo_t;")
+    ffi1.verify("typedef struct { int y, x; } foo_t;")
+    ffi2 = FFI()
+    ffi2.include(ffi1)
+    ffi2.cdef("int myfunc(foo_t *);")
+    lib = ffi2.verify("typedef struct { int y, x; } foo_t;"
+                      "int myfunc(foo_t *p) { return 42 * p->x; }")
+    res = lib.myfunc(ffi2.new("foo_t *", {'x': 10}))
+    assert res == 420
+    res = lib.myfunc(ffi1.new("foo_t *", {'x': -10}))
+    assert res == -420
+
+def test_include_enum():
+    ffi1 = FFI()
+    ffi1.cdef("enum foo_e { AA, ... };")
+    lib1 = ffi1.verify("enum foo_e { CC, BB, AA };")
+    ffi2 = FFI()
+    ffi2.include(ffi1)
+    ffi2.cdef("int myfunc(enum foo_e);")
+    lib2 = ffi2.verify("enum foo_e { CC, BB, AA };"
+                       "int myfunc(enum foo_e x) { return (int)x; }")
+    res = lib2.myfunc(lib2.AA)
+    assert res == 2
+
+def test_named_pointer_as_argument():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; } *mystruct_p;\n"
+             "mystruct_p ff5a(mystruct_p);")
+    lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n"
+                     "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }")
+    p = ffi.new("mystruct_p", [-2])
+    q = lib.ff5a(p)
+    assert q == p
+    assert p.x == 38
+
+def test_enum_size():
+    cases = [('123',           4, 4294967295),
+             ('4294967295U',   4, 4294967295),
+             ('-123',          4, -1),
+             ('-2147483647-1', 4, -1),
+             ]
+    if FFI().sizeof("long") == 8:
+        cases += [('4294967296L',        8, 2**64-1),
+                  ('%dUL' % (2**64-1),   8, 2**64-1),
+                  ('-2147483649L',       8, -1),
+                  ('%dL-1L' % (1-2**63), 8, -1)]
+    for hidden_value, expected_size, expected_minus1 in cases:
+        if sys.platform == 'win32' and 'U' in hidden_value:
+            continue   # skipped on Windows
+        ffi = FFI()
+        ffi.cdef("enum foo_e { AA, BB, ... };")
+        lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value)
+        assert lib.AA == 0
+        assert lib.BB == eval(hidden_value.replace('U', '').replace('L', ''))
+        assert ffi.sizeof("enum foo_e") == expected_size
+        if sys.platform != 'win32':
+            assert int(ffi.cast("enum foo_e", -1)) == expected_minus1
+    # test with the large value hidden:
+    # disabled so far, doesn't work
+##    for hidden_value, expected_size, expected_minus1 in cases:
+##        ffi = FFI()
+##        ffi.cdef("enum foo_e { AA, BB, ... };")
+##        lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value)
+##        assert lib.AA == 0
+##        assert ffi.sizeof("enum foo_e") == expected_size
+##        assert int(ffi.cast("enum foo_e", -1)) == expected_minus1
+
+def test_enum_bug118():
+    maxulong = 256 ** FFI().sizeof("unsigned long") - 1
+    for c2, c2c in [(-1, ''),
+                    (-1, ''),
+                    (0xffffffff, 'U'),
+                    (maxulong, 'UL'),
+                    (-int(maxulong / 3), 'L')]:
+        if c2c and sys.platform == 'win32':
+            continue     # enums may always be signed with MSVC
+        ffi = FFI()
+        ffi.cdef("enum foo_e { AA };")
+        lib = ffi.verify("enum foo_e { AA=%s%s };" % (c2, c2c))
+        assert lib.AA == c2
+
+def test_string_to_voidp_arg():
+    ffi = FFI()
+    ffi.cdef("int myfunc(void *);")
+    lib = ffi.verify("int myfunc(void *p) { return ((signed char *)p)[0]; }")
+    res = lib.myfunc(b"hi!")
+    assert res == ord(b"h")
+    p = ffi.new("char[]", b"gah")
+    res = lib.myfunc(p)
+    assert res == ord(b"g")
+    res = lib.myfunc(ffi.cast("void *", p))
+    assert res == ord(b"g")
+    res = lib.myfunc(ffi.cast("int *", p))
+    assert res == ord(b"g")
+
+def test_callback_indirection():
+    ffi = FFI()
+    ffi.cdef("""
+        int (*python_callback)(int how_many, int *values);
+        int (*const c_callback)(int,...);   /* pass this ptr to C routines */
+        int some_c_function(int(*cb)(int,...));
+    """)
+    lib = ffi.verify("""
+        #include <stdarg.h>
+        #ifdef _WIN32
+        #include <malloc.h>
+        #define alloca _alloca
+        #else
+        # ifdef __FreeBSD__
+        #  include <stdlib.h>
+        # else
+        #  include <alloca.h>
+        # endif
+        #endif
+        static int (*python_callback)(int how_many, int *values);
+        static int c_callback(int how_many, ...) {
+            va_list ap;
+            /* collect the "..." arguments into the values[] array */
+            int i, *values = alloca((size_t)how_many * sizeof(int));
+            va_start(ap, how_many);
+            for (i=0; i<how_many; i++)
+                values[i] = va_arg(ap, int);
+            va_end(ap);
+            return python_callback(how_many, values);
+        }
+        int some_c_function(int(*cb)(int,...)) {
+            int result = cb(2, 10, 20);
+            result += cb(3, 30, 40, 50);
+            return result;
+        }
+    """)
+    seen = []
+    @ffi.callback("int(int, int*)")
+    def python_callback(how_many, values):
+        seen.append([values[i] for i in range(how_many)])
+        return 42
+    lib.python_callback = python_callback
+
+    res = lib.some_c_function(lib.c_callback)
+    assert res == 84
+    assert seen == [[10, 20], [30, 40, 50]]
+
+def test_floatstar_argument():
+    ffi = FFI()
+    ffi.cdef("float sum3floats(float *);")
+    lib = ffi.verify("""
+        float sum3floats(float *f) {
+            return f[0] + f[1] + f[2];
+        }
+    """)
+    assert lib.sum3floats((1.5, 2.5, 3.5)) == 7.5
+    p = ffi.new("float[]", (1.5, 2.5, 3.5))
+    assert lib.sum3floats(p) == 7.5
+
+def test_charstar_argument():
+    ffi = FFI()
+    ffi.cdef("char sum3chars(char *);")
+    lib = ffi.verify("""
+        char sum3chars(char *f) {
+            return (char)(f[0] + f[1] + f[2]);
+        }
+    """)
+    assert lib.sum3chars((b'\x10', b'\x20', b'\x30')) == b'\x60'
+    p = ffi.new("char[]", b'\x10\x20\x30')
+    assert lib.sum3chars(p) == b'\x60'
+
+def test_passing_string_or_NULL():
+    ffi = FFI()
+    ffi.cdef("int seeme1(char *); int seeme2(int *);")
+    lib = ffi.verify("""
+        int seeme1(char *x) {
+            return (x == NULL);
+        }
+        int seeme2(int *x) {
+            return (x == NULL);
+        }
+    """)
+    assert lib.seeme1(b"foo") == 0
+    assert lib.seeme1(ffi.NULL) == 1
+    assert lib.seeme2([42, 43]) == 0
+    assert lib.seeme2(ffi.NULL) == 1
+    py.test.raises(TypeError, lib.seeme1, None)
+    py.test.raises(TypeError, lib.seeme2, None)
+    py.test.raises(TypeError, lib.seeme1, 0.0)
+    py.test.raises(TypeError, lib.seeme2, 0.0)
+    py.test.raises(TypeError, lib.seeme1, 0)
+    py.test.raises(TypeError, lib.seeme2, 0)
+    zeroL  = 99999999999999999999
+    zeroL -= 99999999999999999999
+    py.test.raises(TypeError, lib.seeme2, zeroL)
+
+def test_typeof_function():
+    ffi = FFI()
+    ffi.cdef("int foo(int, char);")
+    lib = ffi.verify("int foo(int x, char y) { (void)x; (void)y; return 42; }")
+    ctype = ffi.typeof(lib.foo)
+    assert len(ctype.args) == 2
+    assert ctype.result == ffi.typeof("int")
+
+def test_call_with_voidstar_arg():
+    ffi = FFI()
+    ffi.cdef("int f(void *);")
+    lib = ffi.verify("int f(void *x) { return ((char*)x)[0]; }")
+    assert lib.f(b"foobar") == ord(b"f")
+
+def test_dir():
+    ffi = FFI()
+    ffi.cdef("""void somefunc(void);
+                extern int somevar, somearray[2];
+                static char *const sv2;
+                enum my_e { AA, BB, ... };
+                #define FOO ...""")
+    lib = ffi.verify("""void somefunc(void) { }
+                        int somevar, somearray[2];
+                        #define sv2 "text"
+                        enum my_e { AA, BB };
+                        #define FOO 42""")
+    assert dir(lib) == ['AA', 'BB', 'FOO', 'somearray',
+                        'somefunc', 'somevar', 'sv2']
+
+def test_typeof_func_with_struct_argument():
+    ffi = FFI()
+    ffi.cdef("""struct s { int a; }; int foo(struct s);""")
+    lib = ffi.verify("""struct s { int a; };
+                        int foo(struct s x) { return x.a; }""")
+    s = ffi.new("struct s *", [-1234])
+    m = lib.foo(s[0])
+    assert m == -1234
+    assert repr(ffi.typeof(lib.foo)) == "<ctype 'int(*)(struct s)'>"
+
+def test_bug_const_char_ptr_array_1():
+    ffi = FFI()
+    ffi.cdef("""const char *a[...];""")
+    lib = ffi.verify("""const char *a[5];""")
+    assert repr(ffi.typeof(lib.a)) == "<ctype 'char *[5]'>"
+
+def test_bug_const_char_ptr_array_2():
+    ffi = FFI()
+    ffi.cdef("""const int a[];""")
+    lib = ffi.verify("""const int a[5];""")
+    assert repr(ffi.typeof(lib.a)) == "<ctype 'int *'>"
+
+def _test_various_calls(force_libffi):
+    cdef_source = """
+    int xvalue;
+    long long ivalue, rvalue;
+    float fvalue;
+    double dvalue;
+    long double Dvalue;
+    signed char tf_bb(signed char x, signed char c);
+    unsigned char tf_bB(signed char x, unsigned char c);
+    short tf_bh(signed char x, short c);
+    unsigned short tf_bH(signed char x, unsigned short c);
+    int tf_bi(signed char x, int c);
+    unsigned int tf_bI(signed char x, unsigned int c);
+    long tf_bl(signed char x, long c);
+    unsigned long tf_bL(signed char x, unsigned long c);
+    long long tf_bq(signed char x, long long c);
+    unsigned long long tf_bQ(signed char x, unsigned long long c);
+    float tf_bf(signed char x, float c);
+    double tf_bd(signed char x, double c);
+    long double tf_bD(signed char x, long double c);
+    """
+    if force_libffi:
+        cdef_source = (cdef_source
+            .replace('tf_', '(*const tf_')
+            .replace('(signed char x', ')(signed char x'))
+    ffi = FFI()
+    ffi.cdef(cdef_source)
+    lib = ffi.verify("""
+    int xvalue;
+    long long ivalue, rvalue;
+    float fvalue;
+    double dvalue;
+    long double Dvalue;
+
+    typedef signed char b_t;
+    typedef unsigned char B_t;
+    typedef short h_t;
+    typedef unsigned short H_t;
+    typedef int i_t;
+    typedef unsigned int I_t;
+    typedef long l_t;
+    typedef unsigned long L_t;
+    typedef long long q_t;
+    typedef unsigned long long Q_t;
+    typedef float f_t;
+    typedef double d_t;
+    typedef long double D_t;
+    #define S(letter)  xvalue = (int)x; letter##value = (letter##_t)c;
+    #define R(letter)  return (letter##_t)rvalue;
+
+    signed char tf_bb(signed char x, signed char c) { S(i) R(b) }
+    unsigned char tf_bB(signed char x, unsigned char c) { S(i) R(B) }
+    short tf_bh(signed char x, short c) { S(i) R(h) }
+    unsigned short tf_bH(signed char x, unsigned short c) { S(i) R(H) }
+    int tf_bi(signed char x, int c) { S(i) R(i) }
+    unsigned int tf_bI(signed char x, unsigned int c) { S(i) R(I) }
+    long tf_bl(signed char x, long c) { S(i) R(l) }
+    unsigned long tf_bL(signed char x, unsigned long c) { S(i) R(L) }
+    long long tf_bq(signed char x, long long c) { S(i) R(q) }
+    unsigned long long tf_bQ(signed char x, unsigned long long c) { S(i) R(Q) }
+    float tf_bf(signed char x, float c) { S(f) R(f) }
+    double tf_bd(signed char x, double c) { S(d) R(d) }
+    long double tf_bD(signed char x, long double c) { S(D) R(D) }
+    """)
+    lib.rvalue = 0x7182838485868788
+    for kind, cname in [('b', 'signed char'),
+                        ('B', 'unsigned char'),
+                        ('h', 'short'),
+                        ('H', 'unsigned short'),
+                        ('i', 'int'),
+                        ('I', 'unsigned int'),
+                        ('l', 'long'),
+                        ('L', 'unsigned long'),
+                        ('q', 'long long'),
+                        ('Q', 'unsigned long long'),
+                        ('f', 'float'),
+                        ('d', 'double'),
+                        ('D', 'long double')]:
+        sign = +1 if 'unsigned' in cname else -1
+        lib.xvalue = 0
+        lib.ivalue = 0
+        lib.fvalue = 0
+        lib.dvalue = 0
+        lib.Dvalue = 0
+        fun = getattr(lib, 'tf_b' + kind)
+        res = fun(-42, sign * 99)
+        if kind == 'D':
+            res = float(res)
+        assert res == int(ffi.cast(cname, 0x7182838485868788))
+        assert lib.xvalue == -42
+        if kind in 'fdD':
+            assert float(getattr(lib, kind + 'value')) == -99.0
+        else:
+            assert lib.ivalue == sign * 99
+
+def test_various_calls_direct():
+    _test_various_calls(force_libffi=False)
+
+def test_various_calls_libffi():
+    _test_various_calls(force_libffi=True)
+
+def test_ptr_to_opaque():
+    ffi = FFI()
+    ffi.cdef("typedef ... foo_t; int f1(foo_t*); foo_t *f2(int);")
+    lib = ffi.verify("""
+        #include <stdlib.h>
+        typedef struct { int x; } foo_t;
+        int f1(foo_t* p) {
+            int x = p->x;
+            free(p);
+            return x;
+        }
+        foo_t *f2(int x) {
+            foo_t *p = malloc(sizeof(foo_t));
+            p->x = x;
+            return p;
+        }
+    """)
+    p = lib.f2(42)
+    x = lib.f1(p)
+    assert x == 42
+
+def _run_in_multiple_threads(test1):
+    test1()
+    import sys
+    try:
+        import thread
+    except ImportError:
+        import _thread as thread
+    errors = []
+    def wrapper(lock):
+        try:
+            test1()
+        except:
+            errors.append(sys.exc_info())
+        lock.release()
+    locks = []
+    for i in range(10):
+        _lock = thread.allocate_lock()
+        _lock.acquire()
+        thread.start_new_thread(wrapper, (_lock,))
+        locks.append(_lock)
+    for _lock in locks:
+        _lock.acquire()
+        if errors:
+            raise errors[0][1]
+
+def test_errno_working_even_with_pypys_jit():
+    ffi = FFI()
+    ffi.cdef("int f(int);")
+    lib = ffi.verify("""
+        #include <errno.h>
+        int f(int x) { return (errno = errno + x); }
+    """)
+    @_run_in_multiple_threads
+    def test1():
+        ffi.errno = 0
+        for i in range(10000):
+            e = lib.f(1)
+            assert e == i + 1
+            assert ffi.errno == e
+        for i in range(10000):
+            ffi.errno = i
+            e = lib.f(42)
+            assert e == i + 42
+
+def test_getlasterror_working_even_with_pypys_jit():
+    if sys.platform != 'win32':
+        py.test.skip("win32-only test")
+    ffi = FFI()
+    ffi.cdef("void SetLastError(DWORD);")
+    lib = ffi.dlopen("Kernel32.dll")
+    @_run_in_multiple_threads
+    def test1():
+        for i in range(10000):
+            n = (1 << 29) + i
+            lib.SetLastError(n)
+            assert ffi.getwinerror()[0] == n
+
+def test_verify_dlopen_flags():
+    if not hasattr(sys, 'setdlopenflags'):
+        py.test.skip("requires sys.setdlopenflags()")
+    # Careful with RTLD_GLOBAL.  If by chance the FFI is not deleted
+    # promptly, like on PyPy, then other tests may see the same
+    # exported symbols as well.  So we must not export a simple name
+    # like 'foo'!
+    old = sys.getdlopenflags()
+    try:
+        ffi1 = FFI()
+        ffi1.cdef("int foo_verify_dlopen_flags_1;")
+        sys.setdlopenflags(ffi1.RTLD_GLOBAL | ffi1.RTLD_NOW)
+        lib1 = ffi1.verify("int foo_verify_dlopen_flags_1;")
+    finally:
+        sys.setdlopenflags(old)
+
+    ffi2 = FFI()
+    ffi2.cdef("int *getptr(void);")
+    lib2 = ffi2.verify("""
+        extern int foo_verify_dlopen_flags_1;
+        static int *getptr(void) { return &foo_verify_dlopen_flags_1; }
+    """)
+    p = lib2.getptr()
+    assert ffi1.addressof(lib1, 'foo_verify_dlopen_flags_1') == p
+
+def test_consider_not_implemented_function_type():
+    ffi = FFI()
+    ffi.cdef("typedef union { int a; float b; } Data;"
+             "typedef struct { int a:2; } MyStr;"
+             "typedef void (*foofunc_t)(Data);"
+             "typedef Data (*bazfunc_t)(void);"
+             "typedef MyStr (*barfunc_t)(void);")
+    fooptr = ffi.cast("foofunc_t", 123)
+    bazptr = ffi.cast("bazfunc_t", 123)
+    barptr = ffi.cast("barfunc_t", 123)
+    # assert did not crash so far
+    e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *"))
+    assert str(e.value) == (
+        "ctype 'Data' not supported as argument by libffi.  Unions are only "
+        "supported as argument if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+"
+        "ffibuilder.set_source() and not taking a final '...' argument)")
+    e = py.test.raises(NotImplementedError, bazptr)
+    assert str(e.value) == (
+        "ctype 'Data' not supported as return value by libffi.  Unions are "
+        "only supported as return value if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+"
+        "ffibuilder.set_source() and not taking a final '...' argument)")
+    e = py.test.raises(NotImplementedError, barptr)
+    assert str(e.value) == (
+        "ctype 'MyStr' not supported as return value.  It is a struct with "
+        "bit fields, which libffi does not support.  Such structs are only "
+        "supported as return value if the function is 'API mode' and non-"
+        "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder."
+        "set_source() and not taking a final '...' argument)")
+
+def test_verify_extra_arguments():
+    ffi = FFI()
+    ffi.cdef("#define ABA ...")
+    lib = ffi.verify("", define_macros=[('ABA', '42')])
+    assert lib.ABA == 42
+
+def test_implicit_unicode_on_windows():
+    from cffi import FFIError
+    if sys.platform != 'win32':
+        py.test.skip("win32-only test")
+    ffi = FFI()
+    e = py.test.raises(FFIError, ffi.cdef, "int foo(LPTSTR);")
+    assert str(e.value) == ("The Windows type 'LPTSTR' is only available after"
+                            " you call ffi.set_unicode()")
+    for with_unicode in [True, False]:
+        ffi = FFI()
+        ffi.set_unicode(with_unicode)
+        ffi.cdef("""
+            DWORD GetModuleFileName(HMODULE hModule, LPTSTR lpFilename,
+                                    DWORD nSize);
+        """)
+        lib = ffi.verify("""
+            #include <windows.h>
+        """, libraries=['Kernel32'])
+        outbuf = ffi.new("TCHAR[]", 200)
+        n = lib.GetModuleFileName(ffi.NULL, outbuf, 500)
+        assert 0 < n < 500
+        for i in range(n):
+            #print repr(outbuf[i])
+            assert ord(outbuf[i]) != 0
+        assert ord(outbuf[n]) == 0
+        assert ord(outbuf[0]) < 128     # should be a letter, or '\'
+
+def test_define_known_value():
+    ffi = FFI()
+    ffi.cdef("#define FOO 0x123")
+    lib = ffi.verify("#define FOO 0x123")
+    assert lib.FOO == 0x123
+
+def test_define_wrong_value():
+    ffi = FFI()
+    ffi.cdef("#define FOO 123")
+    lib = ffi.verify("#define FOO 124")     # used to complain
+    e = py.test.raises(ffi.error, "lib.FOO")
+    assert str(e.value) == ("the C compiler says 'FOO' is equal to 124 (0x7c),"
+                            " but the cdef disagrees")
+
+def test_some_integer_type_for_issue73():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef int... AnIntegerWith32Bits;
+        typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void);
+        AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger);
+    """)
+    lib = ffi.verify("""
+        #ifdef __LP64__
+        typedef int AnIntegerWith32Bits;
+        #else
+        typedef long AnIntegerWith32Bits;
+        #endif
+        typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void);
+        AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger f) {
+            return f();
+        }
+    """)
+    @ffi.callback("AFunctionReturningInteger")
+    def add():
+        return 3 + 4
+    x = lib.InvokeFunction(add)
+    assert x == 7
+
+def test_unsupported_some_primitive_types():
+    ffi = FFI()
+    py.test.raises((FFIError,      # with pycparser <= 2.17
+                    CDefError),    # with pycparser >= 2.18
+                   ffi.cdef, """typedef void... foo_t;""")
+    #
+    ffi.cdef("typedef int... foo_t;")
+    py.test.raises(VerificationError, ffi.verify, "typedef float foo_t;")
+
+def test_windows_dllimport_data():
+    if sys.platform != 'win32':
+        py.test.skip("Windows only")
+    from testing.udir import udir
+    tmpfile = udir.join('dllimport_data.c')
+    tmpfile.write('int my_value = 42;\n')
+    ffi = FFI()
+    ffi.cdef("int my_value;")
+    lib = ffi.verify("extern __declspec(dllimport) int my_value;",
+                     sources = [str(tmpfile)])
+    assert lib.my_value == 42
+
+def test_macro_var():
+    ffi = FFI()
+    ffi.cdef("int myarray[50], my_value;")
+    lib = ffi.verify("""
+        int myarray[50];
+        int *get_my_value(void) {
+            static int index = 0;
+            return &myarray[index++];
+        }
+        #define my_value (*get_my_value())
+    """)
+    assert lib.my_value == 0             # [0]
+    lib.my_value = 42                    # [1]
+    assert lib.myarray[1] == 42
+    assert lib.my_value == 0             # [2]
+    lib.myarray[3] = 63
+    assert lib.my_value == 63            # [3]
+    p = ffi.addressof(lib, 'my_value')   # [4]
+    assert p[-1] == 63
+    assert p[0] == 0
+    assert p == lib.myarray + 4
+    p[1] = 82
+    assert lib.my_value == 82            # [5]
+
+def test_const_pointer_to_pointer():
+    ffi = FFI()
+    ffi.cdef("struct s { char *const *a; };")
+    ffi.verify("struct s { char *const *a; };")
+
+def test_share_FILE():
+    ffi1 = FFI()
+    ffi1.cdef("void do_stuff(FILE *);")
+    lib1 = ffi1.verify("void do_stuff(FILE *f) { (void)f; }")
+    ffi2 = FFI()
+    ffi2.cdef("FILE *barize(void);")
+    lib2 = ffi2.verify("FILE *barize(void) { return NULL; }")
+    lib1.do_stuff(lib2.barize())
+
+def test_win_common_types():
+    if sys.platform != 'win32':
+        py.test.skip("Windows only")
+    ffi = FFI()
+    ffi.set_unicode(True)
+    ffi.verify("")
+    assert ffi.typeof("PBYTE") is ffi.typeof("unsigned char *")
+    if sys.maxsize > 2**32:
+        expected = "unsigned long long"
+    else:
+        expected = "unsigned int"
+    assert ffi.typeof("UINT_PTR") is ffi.typeof(expected)
+    assert ffi.typeof("PTSTR") is ffi.typeof("wchar_t *")
+
+def _only_test_on_linux_intel():
+    if not sys.platform.startswith('linux'):
+        py.test.skip('only running the memory-intensive test on Linux')
+    import platform
+    machine = platform.machine()
+    if 'x86' not in machine and 'x64' not in machine:
+        py.test.skip('only running the memory-intensive test on x86/x64')
+
+def test_ffi_gc_size_arg():
+    _only_test_on_linux_intel()
+    ffi = FFI()
+    ffi.cdef("void *malloc(size_t); void free(void *);")
+    lib = ffi.verify(r"""
+        #include <stdlib.h>
+    """)
+    for i in range(2000):
+        p = lib.malloc(20*1024*1024)    # 20 MB
+        p1 = ffi.cast("char *", p)
+        for j in range(0, 20*1024*1024, 4096):
+            p1[j] = b'!'
+        p = ffi.gc(p, lib.free, 20*1024*1024)
+        del p
+        # with PyPy's GC, the above would rapidly consume 40 GB of RAM
+        # without the third argument to ffi.gc()
+
+def test_ffi_gc_size_arg_2():
+    # a variant of the above: this "attack" works on cpython's cyclic gc too
+    # and I found no obvious way to prevent that.  So for now, this test
+    # is skipped on CPython, where it eats all the memory.
+    if '__pypy__' not in sys.builtin_module_names:
+        py.test.skip("find a way to tweak the cyclic GC of CPython")
+    _only_test_on_linux_intel()
+    ffi = FFI()
+    ffi.cdef("void *malloc(size_t); void free(void *);")
+    lib = ffi.verify(r"""
+        #include <stdlib.h>
+    """)
+    class X(object):
+        pass
+    for i in range(2000):
+        p = lib.malloc(50*1024*1024)    # 50 MB
+        p1 = ffi.cast("char *", p)
+        for j in range(0, 50*1024*1024, 4096):
+            p1[j] = b'!'
+        p = ffi.gc(p, lib.free, 50*1024*1024)
+        x = X()
+        x.p = p
+        x.cyclic = x
+        del p, x
+
+def test_ffi_new_with_cycles():
+    # still another variant, with ffi.new()
+    if '__pypy__' not in sys.builtin_module_names:
+        py.test.skip("find a way to tweak the cyclic GC of CPython")
+    ffi = FFI()
+    ffi.cdef("")
+    lib = ffi.verify("")
+    class X(object):
+        pass
+    for i in range(2000):
+        p = ffi.new("char[]", 50*1024*1024)    # 50 MB
+        for j in range(0, 50*1024*1024, 4096):
+            p[j] = b'!'
+        x = X()
+        x.p = p
+        x.cyclic = x
+        del p, x
diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py
new file mode 100644
index 0000000..efc1d86
--- /dev/null
+++ b/testing/cffi1/test_zdist.py
@@ -0,0 +1,426 @@
+import sys, os, py
+import subprocess
+import cffi
+from testing.udir import udir
+from shutil import rmtree
+from tempfile import mkdtemp
+
+
+def chdir_to_tmp(f):
+    f.chdir_to_tmp = True
+    return f
+
+def from_outside(f):
+    f.chdir_to_tmp = False
+    return f
+
+
+class TestDist(object):
+
+    def setup_method(self, meth):
+        self.executable = os.path.abspath(sys.executable)
+        self.rootdir = os.path.abspath(os.path.dirname(os.path.dirname(
+            cffi.__file__)))
+        self.udir = udir.join(meth.__name__)
+        os.mkdir(str(self.udir))
+        if meth.chdir_to_tmp:
+            self.saved_cwd = os.getcwd()
+            os.chdir(str(self.udir))
+
+    def teardown_method(self, meth):
+        if hasattr(self, 'saved_cwd'):
+            os.chdir(self.saved_cwd)
+
+    def run(self, args, cwd=None):
+        env = os.environ.copy()
+        # a horrible hack to prevent distutils from finding ~/.pydistutils.cfg
+        # (there is the --no-user-cfg option, but not in Python 2.6...)
+        # NOTE: pointing $HOME to a nonexistent directory can break certain things
+        # that look there for configuration (like ccache).
+        tmp_home = mkdtemp()
+        assert tmp_home != None, "cannot create temporary homedir"
+        env['HOME'] = tmp_home
+        if cwd is None:
+            newpath = self.rootdir
+            if 'PYTHONPATH' in env:
+                newpath += os.pathsep + env['PYTHONPATH']
+            env['PYTHONPATH'] = newpath
+        try:
+            subprocess.check_call([self.executable] + args, cwd=cwd, env=env)
+        finally:
+            rmtree(tmp_home)
+
+    def _prepare_setuptools(self):
+        if hasattr(TestDist, '_setuptools_ready'):
+            return
+        try:
+            import setuptools
+        except ImportError:
+            py.test.skip("setuptools not found")
+        if os.path.exists(os.path.join(self.rootdir, 'setup.py')):
+            self.run(['setup.py', 'egg_info'], cwd=self.rootdir)
+        TestDist._setuptools_ready = True
+
+    def check_produced_files(self, content, curdir=None):
+        if curdir is None:
+            curdir = str(self.udir)
+        found_so = None
+        for name in os.listdir(curdir):
+            if (name.endswith('.so') or name.endswith('.pyd') or
+                name.endswith('.dylib') or name.endswith('.dll')):
+                found_so = os.path.join(curdir, name)
+                # foo.so => foo
+                parts = name.split('.')
+                del parts[-1]
+                if len(parts) > 1 and parts[-1] != 'bar':
+                    # foo.cpython-34m.so => foo, but foo.bar.so => foo.bar
+                    del parts[-1]
+                name = '.'.join(parts)
+                # foo_d => foo (Python 2 debug builds)
+                if name.endswith('_d') and hasattr(sys, 'gettotalrefcount'):
+                    name = name[:-2]
+                name += '.SO'
+            if name.startswith('pycparser') and name.endswith('.egg'):
+                continue    # no clue why this shows up sometimes and not others
+            if name == '.eggs':
+                continue    # seems new in 3.5, ignore it
+            assert name in content, "found unexpected file %r" % (
+                os.path.join(curdir, name),)
+            value = content.pop(name)
+            if value is None:
+                assert name.endswith('.SO') or (
+                    os.path.isfile(os.path.join(curdir, name)))
+            else:
+                subdir = os.path.join(curdir, name)
+                assert os.path.isdir(subdir)
+                if value == '?':
+                    continue
+                found_so = self.check_produced_files(value, subdir) or found_so
+        assert content == {}, "files or dirs not produced in %r: %r" % (
+            curdir, content.keys())
+        return found_so
+
+    @chdir_to_tmp
+    def test_empty(self):
+        self.check_produced_files({})
+
+    @chdir_to_tmp
+    def test_abi_emit_python_code_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", None)
+        ffi.emit_python_code('xyz.py')
+        self.check_produced_files({'xyz.py': None})
+
+    @chdir_to_tmp
+    def test_abi_emit_python_code_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", None)
+        py.test.raises(IOError, ffi.emit_python_code, 'unexisting/xyz.py')
+
+    @from_outside
+    def test_abi_emit_python_code_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", None)
+        ffi.emit_python_code(str(self.udir.join('xyt.py')))
+        self.check_produced_files({'xyt.py': None})
+
+    @chdir_to_tmp
+    def test_abi_compile_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", None)
+        x = ffi.compile()
+        self.check_produced_files({'mod_name_in_package': {'mymod.py': None}})
+        assert x == os.path.join('.', 'mod_name_in_package', 'mymod.py')
+
+    @chdir_to_tmp
+    def test_abi_compile_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", None)
+        x = ffi.compile('build2')
+        self.check_produced_files({'build2': {
+            'mod_name_in_package': {'mymod.py': None}}})
+        assert x == os.path.join('build2', 'mod_name_in_package', 'mymod.py')
+
+    @from_outside
+    def test_abi_compile_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", None)
+        tmpdir = str(self.udir.join('build3'))
+        x = ffi.compile(tmpdir)
+        self.check_produced_files({'build3': {
+            'mod_name_in_package': {'mymod.py': None}}})
+        assert x == os.path.join(tmpdir, 'mod_name_in_package', 'mymod.py')
+
+    @chdir_to_tmp
+    def test_api_emit_c_code_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", "/*code would be here*/")
+        ffi.emit_c_code('xyz.c')
+        self.check_produced_files({'xyz.c': None})
+
+    @chdir_to_tmp
+    def test_api_emit_c_code_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", "/*code would be here*/")
+        py.test.raises(IOError, ffi.emit_c_code, 'unexisting/xyz.c')
+
+    @from_outside
+    def test_api_emit_c_code_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", "/*code would be here*/")
+        ffi.emit_c_code(str(self.udir.join('xyu.c')))
+        self.check_produced_files({'xyu.c': None})
+
+    @chdir_to_tmp
+    def test_api_compile_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile()
+        if sys.platform != 'win32':
+            sofile = self.check_produced_files({
+                'mod_name_in_package': {'mymod.SO': None,
+                                        'mymod.c': None,
+                                        'mymod.o': None}})
+            assert os.path.isabs(x) and os.path.samefile(x, sofile)
+        else:
+            self.check_produced_files({
+                'mod_name_in_package': {'mymod.SO': None,
+                                        'mymod.c': None},
+                'Release': '?'})
+
+    @chdir_to_tmp
+    def test_api_compile_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile('output')
+        if sys.platform != 'win32':
+            sofile = self.check_produced_files({
+                'output': {'mod_name_in_package': {'mymod.SO': None,
+                                                   'mymod.c': None,
+                                                   'mymod.o': None}}})
+            assert os.path.isabs(x) and os.path.samefile(x, sofile)
+        else:
+            self.check_produced_files({
+                'output': {'mod_name_in_package': {'mymod.SO': None,
+                                                   'mymod.c': None},
+                           'Release': '?'}})
+
+    @from_outside
+    def test_api_compile_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile(str(self.udir.join('foo')))
+        if sys.platform != 'win32':
+            sofile = self.check_produced_files({
+                'foo': {'mod_name_in_package': {'mymod.SO': None,
+                                                'mymod.c': None,
+                                                'mymod.o': None}}})
+            assert os.path.isabs(x) and os.path.samefile(x, sofile)
+        else:
+            self.check_produced_files({
+                'foo': {'mod_name_in_package': {'mymod.SO': None,
+                                                'mymod.c': None},
+                        'Release': '?'}})
+
+    @chdir_to_tmp
+    def test_api_compile_explicit_target_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile(target="foo.bar.*")
+        if sys.platform != 'win32':
+            sofile = self.check_produced_files({
+                'mod_name_in_package': {'foo.bar.SO': None,
+                                        'mymod.c': None,
+                                        'mymod.o': None}})
+            assert os.path.isabs(x) and os.path.samefile(x, sofile)
+        else:
+            self.check_produced_files({
+                'mod_name_in_package': {'foo.bar.SO': None,
+                                        'mymod.c': None},
+                'Release': '?'})
+
+    @chdir_to_tmp
+    def test_api_compile_explicit_target_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile(target="foo.bar.baz")
+        if sys.platform != 'win32':
+            self.check_produced_files({
+                'mod_name_in_package': {'foo.bar.baz': None,
+                                        'mymod.c': None,
+                                        'mymod.o': None}})
+            sofile = os.path.join(str(self.udir),
+                                  'mod_name_in_package', 'foo.bar.baz')
+            assert os.path.isabs(x) and os.path.samefile(x, sofile)
+        else:
+            self.check_produced_files({
+                'mod_name_in_package': {'foo.bar.baz': None,
+                                        'mymod.c': None},
+                'Release': '?'})
+
+    @chdir_to_tmp
+    def test_api_distutils_extension_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        ext = ffi.distutils_extension()
+        self.check_produced_files({'build': {
+            'mod_name_in_package': {'mymod.c': None}}})
+        if hasattr(os.path, 'samefile'):
+            assert os.path.samefile(ext.sources[0],
+                                    'build/mod_name_in_package/mymod.c')
+
+    @from_outside
+    def test_api_distutils_extension_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        ext = ffi.distutils_extension(str(self.udir.join('foo')))
+        self.check_produced_files({'foo': {
+            'mod_name_in_package': {'mymod.c': None}}})
+        if hasattr(os.path, 'samefile'):
+            assert os.path.samefile(ext.sources[0],
+                str(self.udir.join('foo/mod_name_in_package/mymod.c')))
+
+
+    def _make_distutils_api(self):
+        os.mkdir("src")
+        os.mkdir(os.path.join("src", "pack1"))
+        with open(os.path.join("src", "pack1", "__init__.py"), "w") as f:
+            pass
+        with open("setup.py", "w") as f:
+            f.write("""if 1:
+                # https://bugs.python.org/issue23246
+                import sys
+                if sys.platform == 'win32':
+                    try:
+                        import setuptools
+                    except ImportError:
+                        pass
+
+                import cffi
+                ffi = cffi.FFI()
+                ffi.set_source("pack1.mymod", "/*code would be here*/")
+
+                from distutils.core import setup
+                setup(name='example1',
+                      version='0.1',
+                      packages=['pack1'],
+                      package_dir={'': 'src'},
+                      ext_modules=[ffi.distutils_extension()])
+            """)
+
+    @chdir_to_tmp
+    def test_distutils_api_1(self):
+        self._make_distutils_api()
+        self.run(["setup.py", "build"])
+        self.check_produced_files({'setup.py': None,
+                                   'build': '?',
+                                   'src': {'pack1': {'__init__.py': None}}})
+
+    @chdir_to_tmp
+    def test_distutils_api_2(self):
+        self._make_distutils_api()
+        self.run(["setup.py", "build_ext", "-i"])
+        self.check_produced_files({'setup.py': None,
+                                   'build': '?',
+                                   'src': {'pack1': {'__init__.py': None,
+                                                     'mymod.SO': None}}})
+
+    def _make_setuptools_abi(self):
+        self._prepare_setuptools()
+        os.mkdir("src0")
+        os.mkdir(os.path.join("src0", "pack2"))
+        with open(os.path.join("src0", "pack2", "__init__.py"), "w") as f:
+            pass
+        with open(os.path.join("src0", "pack2", "_build.py"), "w") as f:
+            f.write("""if 1:
+                import cffi
+                ffi = cffi.FFI()
+                ffi.set_source("pack2.mymod", None)
+            """)
+        with open("setup.py", "w") as f:
+            f.write("""if 1:
+                from setuptools import setup
+                setup(name='example1',
+                      version='0.1',
+                      packages=['pack2'],
+                      package_dir={'': 'src0'},
+                      cffi_modules=["src0/pack2/_build.py:ffi"])
+            """)
+
+    @chdir_to_tmp
+    def test_setuptools_abi_1(self):
+        self._make_setuptools_abi()
+        self.run(["setup.py", "build"])
+        self.check_produced_files({'setup.py': None,
+                                   'build': '?',
+                                   'src0': {'pack2': {'__init__.py': None,
+                                                      '_build.py': None}}})
+
+    @chdir_to_tmp
+    def test_setuptools_abi_2(self):
+        self._make_setuptools_abi()
+        self.run(["setup.py", "build_ext", "-i"])
+        self.check_produced_files({'setup.py': None,
+                                   'src0': {'pack2': {'__init__.py': None,
+                                                      '_build.py': None,
+                                                      'mymod.py': None}}})
+
+    def _make_setuptools_api(self):
+        self._prepare_setuptools()
+        os.mkdir("src1")
+        os.mkdir(os.path.join("src1", "pack3"))
+        with open(os.path.join("src1", "pack3", "__init__.py"), "w") as f:
+            pass
+        with open(os.path.join("src1", "pack3", "_build.py"), "w") as f:
+            f.write("""if 1:
+                import cffi
+                ffi = cffi.FFI()
+                ffi.set_source("pack3.mymod", "/*code would be here*/")
+                ffi._hi_there = 42
+            """)
+        with open("setup.py", "w") as f:
+            f.write("from __future__ import print_function\n"
+                """if 1:
+                from setuptools import setup
+                from distutils.command.build_ext import build_ext
+                import os
+
+                class TestBuildExt(build_ext):
+                    def pre_run(self, ext, ffi):
+                        print('_make_setuptools_api: in pre_run:', end=" ")
+                        assert ffi._hi_there == 42
+                        assert ext.name == "pack3.mymod"
+                        fn = os.path.join(os.path.dirname(self.build_lib),
+                                          '..', 'see_me')
+                        print('creating %r' % (fn,))
+                        open(fn, 'w').close()
+
+                setup(name='example1',
+                      version='0.1',
+                      packages=['pack3'],
+                      package_dir={'': 'src1'},
+                      cffi_modules=["src1/pack3/_build.py:ffi"],
+                      cmdclass={'build_ext': TestBuildExt},
+                      )
+            """)
+
+    @chdir_to_tmp
+    def test_setuptools_api_1(self):
+        self._make_setuptools_api()
+        self.run(["setup.py", "build"])
+        self.check_produced_files({'setup.py': None,
+                                   'build': '?',
+                                   'see_me': None,
+                                   'src1': {'pack3': {'__init__.py': None,
+                                                      '_build.py': None}}})
+
+    @chdir_to_tmp
+    def test_setuptools_api_2(self):
+        self._make_setuptools_api()
+        self.run(["setup.py", "build_ext", "-i"])
+        self.check_produced_files({'setup.py': None,
+                                   'build': '?',
+                                   'see_me': None,
+                                   'src1': {'pack3': {'__init__.py': None,
+                                                      '_build.py': None,
+                                                      'mymod.SO': None}}})
diff --git a/testing/embedding/__init__.py b/testing/embedding/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/embedding/__init__.py
diff --git a/testing/embedding/add1-test.c b/testing/embedding/add1-test.c
new file mode 100644
index 0000000..b9ede18
--- /dev/null
+++ b/testing/embedding/add1-test.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#include <windows.h>
+#endif
+
+extern int add1(int, int);
+
+
+int main(void)
+{
+    int x, y;
+    x = add1(40, 2);
+    y = add1(100, -5);
+    printf("got: %d %d\n", x, y);
+#ifdef _MSC_VER
+    if (x == 0 && y == 0)
+        Sleep(2000);
+#endif
+    return 0;
+}
diff --git a/testing/embedding/add1.py b/testing/embedding/add1.py
new file mode 100644
index 0000000..e5b3de1
--- /dev/null
+++ b/testing/embedding/add1.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    import sys, time
+    sys.stdout.write("preparing")
+    for i in range(3):
+        sys.stdout.flush()
+        time.sleep(0.2)
+        sys.stdout.write(".")
+    sys.stdout.write("\n")
+
+    from _add1_cffi import ffi
+
+    int(ord("A"))    # check that built-ins are there
+
+    @ffi.def_extern()
+    def add1(x, y):
+        sys.stdout.write("adding %d and %d\n" % (x, y))
+        sys.stdout.flush()
+        return x + y
+""")
+
+ffi.set_source("_add1_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add2-test.c b/testing/embedding/add2-test.c
new file mode 100644
index 0000000..9620843
--- /dev/null
+++ b/testing/embedding/add2-test.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+extern int add1(int, int);
+extern int add2(int, int, int);
+
+
+int main(void)
+{
+    int x, y;
+    x = add1(40, 2);
+    y = add2(100, -5, -20);
+    printf("got: %d %d\n", x, y);
+    return 0;
+}
diff --git a/testing/embedding/add2.py b/testing/embedding/add2.py
new file mode 100644
index 0000000..311a464
--- /dev/null
+++ b/testing/embedding/add2.py
@@ -0,0 +1,29 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add2(int, int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    import sys
+    sys.stdout.write("prepADD2\n")
+
+    assert '_add2_cffi' in sys.modules
+    m = sys.modules['_add2_cffi']
+    import _add2_cffi
+    ffi = _add2_cffi.ffi
+
+    @ffi.def_extern()
+    def add2(x, y, z):
+        sys.stdout.write("adding %d and %d and %d\n" % (x, y, z))
+        sys.stdout.flush()
+        return x + y + z
+""")
+
+ffi.set_source("_add2_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add3.py b/testing/embedding/add3.py
new file mode 100644
index 0000000..1361912
--- /dev/null
+++ b/testing/embedding/add3.py
@@ -0,0 +1,24 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add3(int, int, int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    from _add3_cffi import ffi
+    import sys
+
+    @ffi.def_extern()
+    def add3(x, y, z, t):
+        sys.stdout.write("adding %d, %d, %d, %d\n" % (x, y, z, t))
+        sys.stdout.flush()
+        return x + y + z + t
+""")
+
+ffi.set_source("_add3_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add_recursive-test.c b/testing/embedding/add_recursive-test.c
new file mode 100644
index 0000000..cd29b79
--- /dev/null
+++ b/testing/embedding/add_recursive-test.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#  define DLLIMPORT  __declspec(dllimport)
+#else
+#  define DLLIMPORT  extern
+#endif
+
+DLLIMPORT int add_rec(int, int);
+DLLIMPORT int (*my_callback)(int);
+
+static int some_callback(int x)
+{
+    printf("some_callback(%d)\n", x);
+    fflush(stdout);
+    return add_rec(x, 9);
+}
+
+int main(void)
+{
+    int x, y;
+    my_callback = some_callback;
+    x = add_rec(40, 2);
+    y = add_rec(100, -5);
+    printf("got: %d %d\n", x, y);
+    return 0;
+}
diff --git a/testing/embedding/add_recursive.py b/testing/embedding/add_recursive.py
new file mode 100644
index 0000000..9fa463d
--- /dev/null
+++ b/testing/embedding/add_recursive.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int (*my_callback)(int);
+    int add_rec(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    from _add_recursive_cffi import ffi, lib
+    import sys
+    print("preparing REC")
+    sys.stdout.flush()
+
+    @ffi.def_extern()
+    def add_rec(x, y):
+        print("adding %d and %d" % (x, y))
+        sys.stdout.flush()
+        return x + y
+
+    x = lib.my_callback(400)
+    print('<<< %d >>>' % (x,))
+""")
+
+ffi.set_source("_add_recursive_cffi", """
+/* use CFFI_DLLEXPORT: on windows, it expands to __declspec(dllexport),
+   which is needed to export a variable from a dll */
+CFFI_DLLEXPORT int (*my_callback)(int);
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/empty.py b/testing/embedding/empty.py
new file mode 100644
index 0000000..aa8d830
--- /dev/null
+++ b/testing/embedding/empty.py
@@ -0,0 +1,10 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("")
+
+ffi.set_source("_empty_cffi", "")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/initerror.py b/testing/embedding/initerror.py
new file mode 100644
index 0000000..775cf56
--- /dev/null
+++ b/testing/embedding/initerror.py
@@ -0,0 +1,18 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    raise KeyError
+""")
+
+ffi.set_source("_initerror_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
+
diff --git a/testing/embedding/perf-test.c b/testing/embedding/perf-test.c
new file mode 100644
index 0000000..2195bf5
--- /dev/null
+++ b/testing/embedding/perf-test.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <assert.h>
+#include <sys/time.h>
+#ifdef PTEST_USE_THREAD
+# include <pthread.h>
+static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
+static int remaining;
+#endif
+
+
+extern int add1(int, int);
+
+
+static double time_delta(struct timeval *stop, struct timeval *start)
+{
+    return (stop->tv_sec - start->tv_sec) +
+        1e-6 * (stop->tv_usec - start->tv_usec);
+}
+
+static double measure(void)
+{
+    long long i, iterations;
+    int result;
+    struct timeval start, stop;
+    double elapsed;
+
+    add1(0, 0);   /* prepare off-line */
+
+    i = 0;
+    iterations = 1000;
+    result = gettimeofday(&start, NULL);
+    assert(result == 0);
+
+    while (1) {
+        for (; i < iterations; i++) {
+            add1(((int)i) & 0xaaaaaa, ((int)i) & 0x555555);
+        }
+        result = gettimeofday(&stop, NULL);
+        assert(result == 0);
+
+        elapsed = time_delta(&stop, &start);
+        assert(elapsed >= 0.0);
+        if (elapsed > 2.5)
+            break;
+        iterations = iterations * 3 / 2;
+    }
+
+    return elapsed / (double)iterations;
+}
+
+static void *start_routine(void *arg)
+{
+    double t = measure();
+    printf("time per call: %.3g\n", t);
+
+#ifdef PTEST_USE_THREAD
+    pthread_mutex_lock(&mutex1);
+    remaining -= 1;
+    if (!remaining)
+        pthread_cond_signal(&cond1);
+    pthread_mutex_unlock(&mutex1);
+#endif
+
+    return arg;
+}
+
+
+int main(void)
+{
+#ifndef PTEST_USE_THREAD
+    start_routine(0);
+#else
+    pthread_t th;
+    int i, status;
+
+    add1(0, 0);   /* this is the main thread */
+
+    remaining = PTEST_USE_THREAD;
+    for (i = 0; i < PTEST_USE_THREAD; i++) {
+        status = pthread_create(&th, NULL, start_routine, NULL);
+        assert(status == 0);
+    }
+    pthread_mutex_lock(&mutex1);
+    while (remaining)
+        pthread_cond_wait(&cond1, &mutex1);
+    pthread_mutex_unlock(&mutex1);
+#endif
+    return 0;
+}
diff --git a/testing/embedding/perf.py b/testing/embedding/perf.py
new file mode 100644
index 0000000..a8d20f4
--- /dev/null
+++ b/testing/embedding/perf.py
@@ -0,0 +1,21 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    from _perf_cffi import ffi
+
+    @ffi.def_extern()
+    def add1(x, y):
+        return x + y
+""")
+
+ffi.set_source("_perf_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py
new file mode 100644
index 0000000..8463c3f
--- /dev/null
+++ b/testing/embedding/test_basic.py
@@ -0,0 +1,207 @@
+import py
+import sys, os, re
+import shutil, subprocess, time
+from testing.udir import udir
+import cffi
+
+
+local_dir = os.path.dirname(os.path.abspath(__file__))
+_link_error = '?'
+
+def check_lib_python_found(tmpdir):
+    global _link_error
+    if _link_error == '?':
+        ffi = cffi.FFI()
+        kwds = {}
+        ffi._apply_embedding_fix(kwds)
+        ffi.set_source("_test_lib_python_found", "", **kwds)
+        try:
+            ffi.compile(tmpdir=tmpdir, verbose=True)
+        except cffi.VerificationError as e:
+            _link_error = e
+        else:
+            _link_error = None
+    if _link_error:
+        py.test.skip(str(_link_error))
+
+
+def prefix_pythonpath():
+    cffi_base = os.path.dirname(os.path.dirname(local_dir))
+    pythonpath = org_env.get('PYTHONPATH', '').split(os.pathsep)
+    if cffi_base not in pythonpath:
+        pythonpath.insert(0, cffi_base)
+    return os.pathsep.join(pythonpath)
+
+def copy_away_env():
+    global org_env
+    try:
+        org_env
+    except NameError:
+        org_env = os.environ.copy()
+
+
+class EmbeddingTests:
+    _compiled_modules = {}
+
+    def setup_method(self, meth):
+        check_lib_python_found(str(udir.ensure('embedding', dir=1)))
+        self._path = udir.join('embedding', meth.__name__)
+        if sys.platform == "win32" or sys.platform == "darwin":
+            self._compiled_modules.clear()   # workaround
+
+    def get_path(self):
+        return str(self._path.ensure(dir=1))
+
+    def _run_base(self, args, **kwds):
+        print('RUNNING:', args, kwds)
+        return subprocess.Popen(args, **kwds)
+
+    def _run(self, args):
+        popen = self._run_base(args, cwd=self.get_path(),
+                                 stdout=subprocess.PIPE,
+                                 universal_newlines=True)
+        output = popen.stdout.read()
+        err = popen.wait()
+        if err:
+            raise OSError("popen failed with exit code %r: %r" % (
+                err, args))
+        print(output.rstrip())
+        return output
+
+    def prepare_module(self, name):
+        self.patch_environment()
+        if name not in self._compiled_modules:
+            path = self.get_path()
+            filename = '%s.py' % name
+            # NOTE: if you have an .egg globally installed with an older
+            # version of cffi, this will not work, because sys.path ends
+            # up with the .egg before the PYTHONPATH entries.  I didn't
+            # find a solution to that: we could hack sys.path inside the
+            # script run here, but we can't hack it in the same way in
+            # execute().
+            pathname = os.path.join(path, filename)
+            with open(pathname, 'w') as g:
+                g.write('''
+# https://bugs.python.org/issue23246
+import sys
+if sys.platform == 'win32':
+    try:
+        import setuptools
+    except ImportError:
+        pass
+''')
+                with open(os.path.join(local_dir, filename), 'r') as f:
+                    g.write(f.read())
+
+            output = self._run([sys.executable, pathname])
+            match = re.compile(r"\bFILENAME: (.+)").search(output)
+            assert match
+            dynamic_lib_name = match.group(1)
+            if sys.platform == 'win32':
+                assert dynamic_lib_name.endswith('_cffi.dll')
+            elif sys.platform == 'darwin':
+                assert dynamic_lib_name.endswith('_cffi.dylib')
+            else:
+                assert dynamic_lib_name.endswith('_cffi.so')
+            self._compiled_modules[name] = dynamic_lib_name
+        return self._compiled_modules[name]
+
+    def compile(self, name, modules, opt=False, threads=False, defines={}):
+        path = self.get_path()
+        filename = '%s.c' % name
+        shutil.copy(os.path.join(local_dir, filename), path)
+        shutil.copy(os.path.join(local_dir, 'thread-test.h'), path)
+        import distutils.ccompiler
+        curdir = os.getcwd()
+        try:
+            os.chdir(self.get_path())
+            c = distutils.ccompiler.new_compiler()
+            print('compiling %s with %r' % (name, modules))
+            extra_preargs = []
+            debug = True
+            if sys.platform == 'win32':
+                libfiles = []
+                for m in modules:
+                    m = os.path.basename(m)
+                    assert m.endswith('.dll')
+                    libfiles.append('Release\\%s.lib' % m[:-4])
+                modules = libfiles
+                extra_preargs.append('/MANIFEST')
+                debug = False    # you need to install extra stuff
+                                 # for this to work
+            elif threads:
+                extra_preargs.append('-pthread')
+            objects = c.compile([filename], macros=sorted(defines.items()),
+                                debug=debug)
+            c.link_executable(objects + modules, name, extra_preargs=extra_preargs)
+        finally:
+            os.chdir(curdir)
+
+    def patch_environment(self):
+        copy_away_env()
+        path = self.get_path()
+        # for libpypy-c.dll or Python27.dll
+        path = os.path.split(sys.executable)[0] + os.path.pathsep + path
+        env_extra = {'PYTHONPATH': prefix_pythonpath()}
+        if sys.platform == 'win32':
+            envname = 'PATH'
+        else:
+            envname = 'LD_LIBRARY_PATH'
+        libpath = org_env.get(envname)
+        if libpath:
+            libpath = path + os.path.pathsep + libpath
+        else:
+            libpath = path
+        env_extra[envname] = libpath
+        for key, value in sorted(env_extra.items()):
+            if os.environ.get(key) != value:
+                print('* setting env var %r to %r' % (key, value))
+                os.environ[key] = value
+
+    def execute(self, name):
+        path = self.get_path()
+        print('running %r in %r' % (name, path))
+        executable_name = name
+        if sys.platform == 'win32':
+            executable_name = os.path.join(path, executable_name + '.exe')
+        else:
+            executable_name = os.path.join('.', executable_name)
+        popen = self._run_base([executable_name], cwd=path,
+                               stdout=subprocess.PIPE,
+                               universal_newlines=True)
+        result = popen.stdout.read()
+        err = popen.wait()
+        if err:
+            raise OSError("%r failed with exit code %r" % (name, err))
+        return result
+
+
+class TestBasic(EmbeddingTests):
+    def test_empty(self):
+        empty_cffi = self.prepare_module('empty')
+
+    def test_basic(self):
+        add1_cffi = self.prepare_module('add1')
+        self.compile('add1-test', [add1_cffi])
+        output = self.execute('add1-test')
+        assert output == ("preparing...\n"
+                          "adding 40 and 2\n"
+                          "adding 100 and -5\n"
+                          "got: 42 95\n")
+
+    def test_two_modules(self):
+        add1_cffi = self.prepare_module('add1')
+        add2_cffi = self.prepare_module('add2')
+        self.compile('add2-test', [add1_cffi, add2_cffi])
+        output = self.execute('add2-test')
+        assert output == ("preparing...\n"
+                          "adding 40 and 2\n"
+                          "prepADD2\n"
+                          "adding 100 and -5 and -20\n"
+                          "got: 42 75\n")
+
+    def test_init_time_error(self):
+        initerror_cffi = self.prepare_module('initerror')
+        self.compile('add1-test', [initerror_cffi])
+        output = self.execute('add1-test')
+        assert output == "got: 0 0\n"    # plus lots of info to stderr
diff --git a/testing/embedding/test_performance.py b/testing/embedding/test_performance.py
new file mode 100644
index 0000000..f9f2605
--- /dev/null
+++ b/testing/embedding/test_performance.py
@@ -0,0 +1,52 @@
+import sys
+from testing.embedding.test_basic import EmbeddingTests
+
+if sys.platform == 'win32':
+    import py
+    py.test.skip("written with POSIX functions")
+
+
+class TestPerformance(EmbeddingTests):
+    def test_perf_single_threaded(self):
+        perf_cffi = self.prepare_module('perf')
+        self.compile('perf-test', [perf_cffi], opt=True)
+        output = self.execute('perf-test')
+        print('='*79)
+        print(output.rstrip())
+        print('='*79)
+
+    def test_perf_in_1_thread(self):
+        perf_cffi = self.prepare_module('perf')
+        self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+                     defines={'PTEST_USE_THREAD': '1'})
+        output = self.execute('perf-test')
+        print('='*79)
+        print(output.rstrip())
+        print('='*79)
+
+    def test_perf_in_2_threads(self):
+        perf_cffi = self.prepare_module('perf')
+        self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+                     defines={'PTEST_USE_THREAD': '2'})
+        output = self.execute('perf-test')
+        print('='*79)
+        print(output.rstrip())
+        print('='*79)
+
+    def test_perf_in_4_threads(self):
+        perf_cffi = self.prepare_module('perf')
+        self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+                     defines={'PTEST_USE_THREAD': '4'})
+        output = self.execute('perf-test')
+        print('='*79)
+        print(output.rstrip())
+        print('='*79)
+
+    def test_perf_in_8_threads(self):
+        perf_cffi = self.prepare_module('perf')
+        self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+                     defines={'PTEST_USE_THREAD': '8'})
+        output = self.execute('perf-test')
+        print('='*79)
+        print(output.rstrip())
+        print('='*79)
diff --git a/testing/embedding/test_recursive.py b/testing/embedding/test_recursive.py
new file mode 100644
index 0000000..b85e7ed
--- /dev/null
+++ b/testing/embedding/test_recursive.py
@@ -0,0 +1,15 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestRecursive(EmbeddingTests):
+    def test_recursive(self):
+        add_recursive_cffi = self.prepare_module('add_recursive')
+        self.compile('add_recursive-test', [add_recursive_cffi])
+        output = self.execute('add_recursive-test')
+        assert output == ("preparing REC\n"
+                          "some_callback(400)\n"
+                          "adding 400 and 9\n"
+                          "<<< 409 >>>\n"
+                          "adding 40 and 2\n"
+                          "adding 100 and -5\n"
+                          "got: 42 95\n")
diff --git a/testing/embedding/test_thread.py b/testing/embedding/test_thread.py
new file mode 100644
index 0000000..1895076
--- /dev/null
+++ b/testing/embedding/test_thread.py
@@ -0,0 +1,61 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestThread(EmbeddingTests):
+    def test_first_calls_in_parallel(self):
+        add1_cffi = self.prepare_module('add1')
+        self.compile('thread1-test', [add1_cffi], threads=True)
+        for i in range(20):
+            output = self.execute('thread1-test')
+            assert output == ("starting\n"
+                              "preparing...\n" +
+                              "adding 40 and 2\n" * 10 +
+                              "done\n")
+
+    def _take_out(self, text, content):
+        assert content in text
+        i = text.index(content)
+        return text[:i] + text[i+len(content):]
+
+    def test_init_different_modules_in_different_threads(self):
+        add1_cffi = self.prepare_module('add1')
+        add2_cffi = self.prepare_module('add2')
+        self.compile('thread2-test', [add1_cffi, add2_cffi], threads=True)
+        output = self.execute('thread2-test')
+        output = self._take_out(output, "preparing")
+        output = self._take_out(output, ".")
+        output = self._take_out(output, ".")
+        # at least the 3rd dot should be after everything from ADD2
+        assert output == ("starting\n"
+                          "prepADD2\n"
+                          "adding 1000 and 200 and 30\n"
+                          ".\n"
+                          "adding 40 and 2\n"
+                          "done\n")
+
+    def test_alt_issue(self):
+        add1_cffi = self.prepare_module('add1')
+        add2_cffi = self.prepare_module('add2')
+        self.compile('thread2-test', [add1_cffi, add2_cffi],
+                     threads=True, defines={'T2TEST_AGAIN_ADD1': '1'})
+        output = self.execute('thread2-test')
+        output = self._take_out(output, "adding 40 and 2\n")
+        assert output == ("starting\n"
+                          "preparing...\n"
+                          "adding -1 and -1\n"
+                          "prepADD2\n"
+                          "adding 1000 and 200 and 30\n"
+                          "done\n")
+
+    def test_load_in_parallel_more(self):
+        add2_cffi = self.prepare_module('add2')
+        add3_cffi = self.prepare_module('add3')
+        self.compile('thread3-test', [add2_cffi, add3_cffi], threads=True)
+        for i in range(150):
+            output = self.execute('thread3-test')
+            for j in range(10):
+                output = self._take_out(output, "adding 40 and 2 and 100\n")
+                output = self._take_out(output, "adding 1000, 200, 30, 4\n")
+            assert output == ("starting\n"
+                              "prepADD2\n"
+                              "done\n")
diff --git a/testing/embedding/test_tlocal.py b/testing/embedding/test_tlocal.py
new file mode 100644
index 0000000..6e7c5af
--- /dev/null
+++ b/testing/embedding/test_tlocal.py
@@ -0,0 +1,10 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestThreadLocal(EmbeddingTests):
+    def test_thread_local(self):
+        tlocal_cffi = self.prepare_module('tlocal')
+        self.compile('tlocal-test', [tlocal_cffi], threads=True)
+        for i in range(10):
+            output = self.execute('tlocal-test')
+            assert output == "done\n"
diff --git a/testing/embedding/thread-test.h b/testing/embedding/thread-test.h
new file mode 100644
index 0000000..f66cf70
--- /dev/null
+++ b/testing/embedding/thread-test.h
@@ -0,0 +1,96 @@
+/************************************************************/
+#ifndef _MSC_VER
+/************************************************************/
+
+
+#include <pthread.h>
+
+/* don't include <semaphore.h>, it is not available on OS/X */
+
+typedef struct {
+    pthread_mutex_t mutex1;
+    pthread_cond_t cond1;
+    unsigned int value;
+} sem_t;
+
+static int sem_init(sem_t *sem, int pshared, unsigned int value)
+{
+    assert(pshared == 0);
+    sem->value = value;
+    return (pthread_mutex_init(&sem->mutex1, NULL) ||
+            pthread_cond_init(&sem->cond1, NULL));
+}
+
+static int sem_post(sem_t *sem)
+{
+    pthread_mutex_lock(&sem->mutex1);
+    sem->value += 1;
+    pthread_cond_signal(&sem->cond1);
+    pthread_mutex_unlock(&sem->mutex1);
+    return 0;
+}
+
+static int sem_wait(sem_t *sem)
+{
+    pthread_mutex_lock(&sem->mutex1);
+    while (sem->value == 0)
+        pthread_cond_wait(&sem->cond1, &sem->mutex1);
+    sem->value -= 1;
+    pthread_mutex_unlock(&sem->mutex1);
+    return 0;
+}
+
+
+/************************************************************/
+#else
+/************************************************************/
+
+
+/* Very quick and dirty, just what I need for these tests.
+   Don't use directly in any real code! 
+*/
+
+#include <Windows.h>
+#include <assert.h>
+
+typedef HANDLE sem_t;
+typedef HANDLE pthread_t;
+
+static int sem_init(sem_t *sem, int pshared, unsigned int value)
+{
+    assert(pshared == 0);
+    assert(value == 0);
+    *sem = CreateSemaphore(NULL, 0, 999, NULL);
+    return *sem ? 0 : -1;
+}
+
+static int sem_post(sem_t *sem)
+{
+    return ReleaseSemaphore(*sem, 1, NULL) ? 0 : -1;
+}
+
+static int sem_wait(sem_t *sem)
+{
+    WaitForSingleObject(*sem, INFINITE);
+    return 0;
+}
+
+static DWORD WINAPI myThreadProc(LPVOID lpParameter)
+{
+    void *(* start_routine)(void *) = (void *(*)(void *))lpParameter;
+    start_routine(NULL);
+    return 0;
+}
+
+static int pthread_create(pthread_t *thread, void *attr,
+                          void *start_routine(void *), void *arg)
+{
+    assert(arg == NULL);
+    *thread = CreateThread(NULL, 0, myThreadProc, start_routine, 0, NULL);
+    return *thread ? 0 : -1;
+}
+
+
+/************************************************************/
+#endif
+/************************************************************/
diff --git a/testing/embedding/thread1-test.c b/testing/embedding/thread1-test.c
new file mode 100644
index 0000000..70bb861
--- /dev/null
+++ b/testing/embedding/thread1-test.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+#define NTHREADS 10
+
+
+extern int add1(int, int);
+
+static sem_t done;
+
+
+static void *start_routine(void *arg)
+{
+    int x, status;
+    x = add1(40, 2);
+    assert(x == 42);
+
+    status = sem_post(&done);
+    assert(status == 0);
+
+    return arg;
+}
+
+int main(void)
+{
+    pthread_t th;
+    int i, status = sem_init(&done, 0, 0);
+    assert(status == 0);
+
+    printf("starting\n");
+    fflush(stdout);
+    for (i = 0; i < NTHREADS; i++) {
+        status = pthread_create(&th, NULL, start_routine, NULL);
+        assert(status == 0);
+    }
+    for (i = 0; i < NTHREADS; i++) {
+        status = sem_wait(&done);
+        assert(status == 0);
+    }
+    printf("done\n");
+    return 0;
+}
diff --git a/testing/embedding/thread2-test.c b/testing/embedding/thread2-test.c
new file mode 100644
index 0000000..62f5ec8
--- /dev/null
+++ b/testing/embedding/thread2-test.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+extern int add1(int, int);
+extern int add2(int, int, int);
+
+static sem_t done;
+
+
+static void *start_routine_1(void *arg)
+{
+    int x, status;
+    x = add1(40, 2);
+    assert(x == 42);
+
+    status = sem_post(&done);
+    assert(status == 0);
+
+    return arg;
+}
+
+static void *start_routine_2(void *arg)
+{
+    int x, status;
+#ifdef T2TEST_AGAIN_ADD1
+    add1(-1, -1);
+#endif
+    x = add2(1000, 200, 30);
+    assert(x == 1230);
+
+    status = sem_post(&done);
+    assert(status == 0);
+
+    return arg;
+}
+
+int main(void)
+{
+    pthread_t th;
+    int i, status = sem_init(&done, 0, 0);
+    assert(status == 0);
+
+    printf("starting\n");
+    fflush(stdout);
+    status = pthread_create(&th, NULL, start_routine_1, NULL);
+    assert(status == 0);
+    status = pthread_create(&th, NULL, start_routine_2, NULL);
+    assert(status == 0);
+
+    for (i = 0; i < 2; i++) {
+        status = sem_wait(&done);
+        assert(status == 0);
+    }
+    printf("done\n");
+    return 0;
+}
diff --git a/testing/embedding/thread3-test.c b/testing/embedding/thread3-test.c
new file mode 100644
index 0000000..69ada27
--- /dev/null
+++ b/testing/embedding/thread3-test.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+extern int add2(int, int, int);
+extern int add3(int, int, int, int);
+
+static sem_t done;
+
+
+static void *start_routine_2(void *arg)
+{
+    int x, status;
+    x = add2(40, 2, 100);
+    assert(x == 142);
+
+    status = sem_post(&done);
+    assert(status == 0);
+
+    return arg;
+}
+
+static void *start_routine_3(void *arg)
+{
+    int x, status;
+    x = add3(1000, 200, 30, 4);
+    assert(x == 1234);
+
+    status = sem_post(&done);
+    assert(status == 0);
+
+    return arg;
+}
+
+int main(void)
+{
+    pthread_t th;
+    int i, status = sem_init(&done, 0, 0);
+    assert(status == 0);
+
+    printf("starting\n");
+    fflush(stdout);
+    for (i = 0; i < 10; i++) {
+        status = pthread_create(&th, NULL, start_routine_2, NULL);
+        assert(status == 0);
+        status = pthread_create(&th, NULL, start_routine_3, NULL);
+        assert(status == 0);
+    }
+    for (i = 0; i < 20; i++) {
+        status = sem_wait(&done);
+        assert(status == 0);
+    }
+    printf("done\n");
+    fflush(stdout);   /* this is occasionally needed on Windows */
+    return 0;
+}
diff --git a/testing/embedding/tlocal-test.c b/testing/embedding/tlocal-test.c
new file mode 100644
index 0000000..b78a03d
--- /dev/null
+++ b/testing/embedding/tlocal-test.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+#define NTHREADS 10
+
+
+extern int add1(int, int);
+
+static sem_t done;
+
+
+static void *start_routine(void *arg)
+{
+    int i, x, expected, status;
+
+    expected = add1(40, 2);
+    assert((expected % 1000) == 42);
+
+    for (i=0; i<10; i++) {
+        x = add1(50, i);
+        assert(x == expected + 8 + i);
+    }
+
+    status = sem_post(&done);
+    assert(status == 0);
+
+    return arg;
+}
+
+int main(void)
+{
+    pthread_t th;
+    int i, status = sem_init(&done, 0, 0);
+    assert(status == 0);
+
+    for (i = 0; i < NTHREADS; i++) {
+        status = pthread_create(&th, NULL, start_routine, NULL);
+        assert(status == 0);
+    }
+    for (i = 0; i < NTHREADS; i++) {
+        status = sem_wait(&done);
+        assert(status == 0);
+    }
+    printf("done\n");
+    return 0;
+}
diff --git a/testing/embedding/tlocal.py b/testing/embedding/tlocal.py
new file mode 100644
index 0000000..7800dff
--- /dev/null
+++ b/testing/embedding/tlocal.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+    int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+    from _tlocal_cffi import ffi
+    import itertools
+    try:
+        import thread
+        g_seen = itertools.count().next
+    except ImportError:
+        import _thread as thread      # py3
+        g_seen = itertools.count().__next__
+    tloc = thread._local()
+
+    @ffi.def_extern()
+    def add1(x, y):
+        try:
+            num = tloc.num
+        except AttributeError:
+            num = tloc.num = g_seen() * 1000
+        return x + y + num
+""")
+
+ffi.set_source("_tlocal_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/support.py b/testing/support.py
new file mode 100644
index 0000000..65f010c
--- /dev/null
+++ b/testing/support.py
@@ -0,0 +1,88 @@
+import sys
+
+if sys.version_info < (3,):
+    __all__ = ['u']
+
+    class U(object):
+        def __add__(self, other):
+            return eval('u'+repr(other).replace(r'\\u', r'\u')
+                                       .replace(r'\\U', r'\U'))
+    u = U()
+    long = long     # for further "from testing.support import long"
+    assert u+'a\x00b' == eval(r"u'a\x00b'")
+    assert u+'a\u1234b' == eval(r"u'a\u1234b'")
+    assert u+'a\U00012345b' == eval(r"u'a\U00012345b'")
+
+else:
+    __all__ = ['u', 'unicode', 'long']
+    u = ""
+    unicode = str
+    long = int
+
+
+class StdErrCapture(object):
+    """Capture writes to sys.stderr (not to the underlying file descriptor)."""
+    def __enter__(self):
+        try:
+            from StringIO import StringIO
+        except ImportError:
+            from io import StringIO
+        self.old_stderr = sys.stderr
+        sys.stderr = f = StringIO()
+        return f
+    def __exit__(self, *args):
+        sys.stderr = self.old_stderr
+
+
+class FdWriteCapture(object):
+    """xxx limited to capture at most 512 bytes of output, according
+    to the Posix manual."""
+
+    def __init__(self, capture_fd=2):    # stderr by default
+        if sys.platform == 'win32':
+            import py
+            py.test.skip("seems not to work, too bad")
+        self.capture_fd = capture_fd
+
+    def __enter__(self):
+        import os
+        self.read_fd, self.write_fd = os.pipe()
+        self.copy_fd = os.dup(self.capture_fd)
+        os.dup2(self.write_fd, self.capture_fd)
+        return self
+
+    def __exit__(self, *args):
+        import os
+        os.dup2(self.copy_fd, self.capture_fd)
+        os.close(self.copy_fd)
+        os.close(self.write_fd)
+        self._value = os.read(self.read_fd, 512)
+        os.close(self.read_fd)
+
+    def getvalue(self):
+        return self._value
+
+def _verify(ffi, module_name, preamble, *args, **kwds):
+    import imp
+    from cffi.recompiler import recompile
+    from .udir import udir
+    assert module_name not in sys.modules, "module name conflict: %r" % (
+        module_name,)
+    kwds.setdefault('tmpdir', str(udir))
+    outputfilename = recompile(ffi, module_name, preamble, *args, **kwds)
+    module = imp.load_dynamic(module_name, outputfilename)
+    #
+    # hack hack hack: copy all *bound methods* from module.ffi back to the
+    # ffi instance.  Then calls like ffi.new() will invoke module.ffi.new().
+    for name in dir(module.ffi):
+        if not name.startswith('_'):
+            attr = getattr(module.ffi, name)
+            if attr is not getattr(ffi, name, object()):
+                setattr(ffi, name, attr)
+    def typeof_disabled(*args, **kwds):
+        raise NotImplementedError
+    ffi._typeof = typeof_disabled
+    for name in dir(ffi):
+        if not name.startswith('_') and not hasattr(module.ffi, name):
+            setattr(ffi, name, NotImplemented)
+    return module.lib
diff --git a/testing/udir.py b/testing/udir.py
new file mode 100644
index 0000000..4dd0a11
--- /dev/null
+++ b/testing/udir.py
@@ -0,0 +1,13 @@
+import py
+import sys
+
+udir = py.path.local.make_numbered_dir(prefix = 'ffi-')
+
+
+# Windows-only workaround for some configurations: see
+# https://bugs.python.org/issue23246 (Python 2.7.9)
+if sys.platform == 'win32':
+    try:
+        import setuptools
+    except ImportError:
+        pass