Author: Michal Vyskocil <[email protected]>
Branch: 
Changeset: r3196:48ca9a578dac
Date: 2019-01-08 10:29 +0100
http://bitbucket.org/cffi/cffi/changeset/48ca9a578dac/

Log:    merge with latest tip

diff too long, truncating to 2000 out of 8938 lines

diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -12,3 +12,8 @@
 0000000000000000000000000000000000000000 release-0.2
 ca6e81df7f1ea58d891129ad016a8888c08f238b release-0.1
 0000000000000000000000000000000000000000 release-0.1
+ada126bd7d1e96cc76303c1fca64a556912549d8 v1.11.1
+5f9690f5832b0292056df45f72314d69c191f75a v1.11.2
+1aafccb9255dbb36f8e785b65624e39628cee63a v1.11.3
+e08abd4703fef26f036e82255f4070277a9e03bd v1.11.4
+48416163071ed48300c3ae4358cc7fd841912413 v1.11.5
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -2,7 +2,7 @@
 #include <Python.h>
 #include "structmember.h"
 
-#define CFFI_VERSION  "1.10.0"
+#define CFFI_VERSION  "1.12.0"
 
 #ifdef MS_WIN32
 #include <windows.h>
@@ -60,7 +60,38 @@
 # endif
 #endif
 
-#include "malloc_closure.h"
+
+/* 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"
@@ -70,7 +101,11 @@
 # define PyText_FromFormat PyUnicode_FromFormat
 # define PyText_AsUTF8 _PyUnicode_AsString   /* PyUnicode_AsUTF8 in Py3.3 */
 # define PyText_AS_UTF8 _PyUnicode_AsString
-# define PyText_GetSize PyUnicode_GetSize
+# 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
@@ -116,36 +151,39 @@
 /************************************************************/
 
 /* base type flag: exactly one of the following: */
-#define CT_PRIMITIVE_SIGNED   1    /* signed integer */
-#define CT_PRIMITIVE_UNSIGNED 2    /* unsigned integer */
-#define CT_PRIMITIVE_CHAR     4    /* char, wchar_t */
-#define CT_PRIMITIVE_FLOAT    8    /* float, double, long double */
-#define CT_POINTER           16    /* pointer, excluding ptr-to-func */
-#define CT_ARRAY             32    /* array */
-#define CT_STRUCT            64    /* struct */
-#define CT_UNION            128    /* union */
-#define CT_FUNCTIONPTR      256    /* pointer to function */
-#define CT_VOID             512    /* void */
+#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       1024
-#define CT_PRIMITIVE_FITS_LONG   2048
-#define CT_IS_OPAQUE             4096
-#define CT_IS_ENUM               8192
-#define CT_IS_PTR_TO_OWNED      16384   /* only owned if CDataOwning_Type */
-#define CT_CUSTOM_FIELD_POS     32768
-#define CT_IS_LONGDOUBLE        65536
-#define CT_IS_BOOL             131072
-#define CT_IS_FILE             262144
-#define CT_IS_VOID_PTR         524288
-#define CT_WITH_VAR_ARRAY     1048576
-#define CT_IS_UNSIZED_CHAR_A  2097152
-#define CT_LAZY_FIELD_LIST    4194304
-#define CT_WITH_PACKED_CHANGE 8388608
+#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_FLOAT |         \
+                           CT_PRIMITIVE_COMPLEX)
 
 typedef struct _ctypedescr {
     PyObject_VAR_HEAD
@@ -256,6 +294,11 @@
 } 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
@@ -283,10 +326,14 @@
 # include "file_emulator.h"
 #endif
 
-#ifdef HAVE_WCHAR_H
+#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;
@@ -845,11 +892,21 @@
     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;                      \
-            memcpy(target, &r, sizeof(type));           \
+            _cffi_memcpy(target, &r, sizeof(type));           \
             return;                                     \
         }                                               \
     } while(0)
@@ -883,6 +940,26 @@
     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)
 {
@@ -898,6 +975,25 @@
     _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)
 {
@@ -1008,12 +1104,18 @@
     }
     else if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
         /*READ(data, ct->ct_size)*/
-        if (ct->ct_size == sizeof(char))
+        switch (ct->ct_size) {
+        case sizeof(char):
             return PyBytes_FromStringAndSize(data, 1);
-#ifdef HAVE_WCHAR_H
-        else
-            return _my_PyUnicode_FromWideChar((wchar_t *)data, 1);
-#endif
+        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,
@@ -1088,39 +1190,65 @@
     return -1;
 }
 
-#ifdef HAVE_WCHAR_H
-static wchar_t _convert_to_wchar_t(PyObject *init)
-{
+static cffi_char16_t _convert_to_char16_t(PyObject *init)
+{
+    char err_got[80];
+    err_got[0] = 0;
+
     if (PyUnicode_Check(init)) {
-        wchar_t ordinal;
-        if (_my_PyUnicode_AsSingleWideChar(init, &ordinal) == 0)
+        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 == sizeof(wchar_t))) {
+           (((CDataObject *)init)->c_type->ct_size == 2)) {
         char *data = ((CDataObject *)init)->c_data;
-        /*READ(data, sizeof(wchar_t))*/
-        return *(wchar_t *)data;
+        /*READ(data, 2)*/
+        return *(cffi_char16_t *)data;
     }
     PyErr_Format(PyExc_TypeError,
-                 "initializer for ctype 'wchar_t' must be a unicode string "
-                 "of length 1, not %.200s", Py_TYPE(init)->tp_name);
-    return (wchar_t)-1;
-}
-#endif
-
-static int _convert_error(PyObject *init, const char *ct_name,
+                 "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)) {
-        const char *ct_name_2 = ((CDataObject *)init)->c_type->ct_name;
-        if (strcmp(ct_name, ct_name_2) != 0)
+        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_name, expected, ct_name_2);
-        else {
+                         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
@@ -1129,14 +1257,21 @@
                          "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_name, ct_name_2);
+                         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_name, expected, Py_TYPE(init)->tp_name);
+                     ct->ct_name, expected, Py_TYPE(init)->tp_name);
     return -1;
 }
 
@@ -1146,7 +1281,7 @@
 convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init);
 
 static Py_ssize_t
-get_new_array_length(PyObject **pvalue)
+get_new_array_length(CTypeDescrObject *ctitem, PyObject **pvalue)
 {
     PyObject *value = *pvalue;
 
@@ -1159,13 +1294,24 @@
     }
     else if (PyUnicode_Check(value)) {
         /* from a unicode, we add the null terminator */
-        return _my_PyUnicode_SizeAsWideChar(value) + 1;
+        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_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;
         }
@@ -1190,7 +1336,8 @@
 {
     /* 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(&value);
+        Py_ssize_t varsizelength = get_new_array_length(
+                                      cf->cf_type->ct_itemdescr, &value);
         if (varsizelength < 0)
             return -1;
         if (optvarsize != NULL) {
@@ -1238,6 +1385,15 @@
     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)
 {
@@ -1291,14 +1447,18 @@
             memcpy(data, srcdata, n);
             return 0;
         }
-#ifdef HAVE_WCHAR_H
         else {
             Py_ssize_t n;
             if (!PyUnicode_Check(init)) {
                 expected = "unicode or list or tuple";
                 goto cannot_convert;
             }
-            n = _my_PyUnicode_SizeAsWideChar(init);
+
+            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' "
@@ -1307,10 +1467,11 @@
             }
             if (n != ct->ct_length)
                 n++;
-            _my_PyUnicode_AsWideChar(init, (wchar_t *)data, n);
-            return 0;
-        }
-#endif
+            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";
@@ -1318,13 +1479,24 @@
     }
 
  cannot_convert:
-    return _convert_error(init, ct->ct_name, expected);
+    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) {
@@ -1371,7 +1543,7 @@
     }
     expected = optvarsize == NULL ? "list or tuple or dict or struct-cdata"
                                   : "list or tuple or dict";
-    return _convert_error(init, ct->ct_name, expected);
+    return _convert_error(init, ct, expected);
 }
 
 #ifdef __GNUC__
@@ -1421,7 +1593,8 @@
                 /* 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. */
+                   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 "
@@ -1431,7 +1604,12 @@
                     "will be forbidden in the future (check that the types "
                     "are as you expect; use an explicit ffi.cast() if they "
                     "are correct)");
-                if (PyErr_WarnEx(PyExc_UserWarning, msg, 1))
+                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 {
@@ -1492,22 +1670,29 @@
         return 0;
     }
     if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
-        if (ct->ct_size == sizeof(char)) {
+        switch (ct->ct_size) {
+        case sizeof(char): {
             int res = _convert_to_char(init);
             if (res < 0)
                 return -1;
             data[0] = res;
             return 0;
         }
-#ifdef HAVE_WCHAR_H
-        else {
-            wchar_t res = _convert_to_wchar_t(init);
-            if (res == (wchar_t)-1 && PyErr_Occurred())
+        case 2: {
+            cffi_char16_t res = _convert_to_char16_t(init);
+            if (res == (cffi_char16_t)-1 && PyErr_Occurred())
                 return -1;
-            *(wchar_t *)data = res;
+            *(cffi_char16_t *)data = res;
             return 0;
         }
-#endif
+        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)) {
 
@@ -1519,6 +1704,13 @@
         }
         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;
@@ -1527,7 +1719,7 @@
     return _convert_overflow(init, ct->ct_name);
 
  cannot_convert:
-    return _convert_error(init, ct->ct_name, expected);
+    return _convert_error(init, ct, expected);
 }
 
 static int
@@ -1587,15 +1779,6 @@
     return 0;
 }
 
-static Py_ssize_t
-get_array_length(CDataObject *cd)
-{
-    if (cd->c_type->ct_length < 0)
-        return ((CDataObject_own_length *)cd)->length;
-    else
-        return cd->c_type->ct_length;
-}
-
 static int
 get_alignment(CTypeDescrObject *ct)
 {
@@ -1678,12 +1861,16 @@
         Py_DECREF(x);
     }
     else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {   /* a callback */
-        ffi_closure *closure = (ffi_closure *)cd->c_data;
+        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);
-    }
-    else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) {  /* from_buffer */
+#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);
@@ -1698,11 +1885,11 @@
         Py_VISIT(x);
     }
     else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {   /* a callback */
-        ffi_closure *closure = (ffi_closure *)cd->c_data;
+        ffi_closure *closure = ((CDataObject_closure *)cd)->closure;
         PyObject *args = (PyObject *)(closure->user_data);
         Py_VISIT(args);
     }
-    else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) {  /* from_buffer */
+    else if (cd->c_type->ct_flags & CT_ARRAY) {         /* from_buffer */
         Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
         Py_VISIT(view->obj);
     }
@@ -1719,12 +1906,12 @@
         Py_DECREF(x);
     }
     else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {   /* a callback */
-        ffi_closure *closure = (ffi_closure *)cd->c_data;
+        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_IS_UNSIZED_CHAR_A) {  /* from_buffer */
+    else if (cd->c_type->ct_flags & CT_ARRAY) {         /* from_buffer */
         Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
         PyBuffer_Release(view);
     }
@@ -1732,7 +1919,8 @@
 }
 
 /* forward */
-static void _my_PyErr_WriteUnraisable(char *objdescr, PyObject *obj,
+static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb,
+                                      char *objdescr, PyObject *obj,
                                       char *extra_error_line);
 
 
@@ -1752,8 +1940,15 @@
             Py_DECREF(result);
         }
         else {
-            _my_PyErr_WriteUnraisable("From callback for ffi.gc ",
+            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);
 
@@ -1763,7 +1958,6 @@
     Py_XDECREF(origobj);
 }
 
-#ifdef Py_TPFLAGS_HAVE_FINALIZE     /* CPython >= 3.4 */
 static void cdatagcp_finalize(CDataObject_gcp *cd)
 {
     PyObject *destructor = cd->destructor;
@@ -1772,7 +1966,6 @@
     cd->origobj = NULL;
     gcp_finalize(destructor, origobj);
 }
-#endif
 
 static void cdatagcp_dealloc(CDataObject_gcp *cd)
 {
@@ -1925,13 +2118,14 @@
         return _cdata_repr2(cd, "handle to", x);
     }
     else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {   /* a callback */
-        PyObject *args = (PyObject *)((ffi_closure *)cd->c_data)->user_data;
+        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_IS_UNSIZED_CHAR_A) {  /* from_buffer */
+    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(
@@ -1956,6 +2150,11 @@
                 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;
 }
@@ -1976,12 +2175,19 @@
     }
     else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) {
         /*READ(cd->c_data, cd->c_type->ct_size)*/
-        if (cd->c_type->ct_size == sizeof(char))
+        switch (cd->c_type->ct_size) {
+        case sizeof(char):
             return PyInt_FromLong((unsigned char)cd->c_data[0]);
-#ifdef HAVE_WCHAR_H
-        else
-            return PyInt_FromLong((long)*(wchar_t *)cd->c_data);
-#endif
+        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);
@@ -2163,7 +2369,7 @@
     else if (cd->c_type->ct_flags & CT_ARRAY) {
         if (i < 0) {
             PyErr_SetString(PyExc_IndexError,
-                            "negative index not supported");
+                            "negative index");
             return NULL;
         }
         if (i >= get_array_length(cd)) {
@@ -2216,7 +2422,7 @@
     if (ct->ct_flags & CT_ARRAY) {
         if (start < 0) {
             PyErr_SetString(PyExc_IndexError,
-                            "negative index not supported");
+                            "negative index");
             return NULL;
         }
         if (stop > get_array_length(cd)) {
@@ -2507,7 +2713,7 @@
 static void
 _cdata_attr_errmsg(char *errmsg, CDataObject *cd, PyObject *attr)
 {
-    char *text;
+    const char *text;
     if (!PyErr_ExceptionMatches(PyExc_AttributeError))
         return;
     PyErr_Clear();
@@ -2673,7 +2879,11 @@
     }
     else if (PyUnicode_Check(init)) {
         /* from a unicode, we add the null terminator */
-        length = _my_PyUnicode_SizeAsWideChar(init) + 1;
+        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);
@@ -2904,6 +3114,93 @@
     }
 }
 
+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 = {
@@ -2953,8 +3250,11 @@
 };
 
 static PyMethodDef cdata_methods[] = {
-    {"__dir__",   cdata_dir,      METH_NOARGS},
-    {NULL,        NULL}           /* sentinel */
+    {"__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 = {
@@ -3010,24 +3310,24 @@
     0,                                          /* tp_setattr */
     0,                                          /* tp_compare */
     (reprfunc)cdataowning_repr,                 /* tp_repr */
-    0,                                          /* tp_as_number */
+    0,  /* inherited */                         /* tp_as_number */
     0,                                          /* tp_as_sequence */
     &CDataOwn_as_mapping,                       /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
+    0,  /* inherited */                         /* tp_hash */
+    0,  /* inherited */                         /* tp_call */
     0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
+    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,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
+    0,  /* inherited */                         /* tp_richcompare */
+    0,  /* inherited */                         /* tp_weaklistoffset */
+    0,  /* inherited */                         /* tp_iter */
     0,                                          /* tp_iternext */
-    0,                                          /* tp_methods */
+    0,  /* inherited */                         /* tp_methods */
     0,                                          /* tp_members */
     0,                                          /* tp_getset */
     &CData_Type,                                /* tp_base */
@@ -3052,25 +3352,25 @@
     0,                                          /* tp_setattr */
     0,                                          /* tp_compare */
     (reprfunc)cdataowninggc_repr,               /* tp_repr */
-    0,                                          /* tp_as_number */
+    0,  /* inherited */                         /* tp_as_number */
     0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
+    0,  /* inherited */                         /* tp_as_mapping */
+    0,  /* inherited */                         /* tp_hash */
+    0,  /* inherited */                         /* tp_call */
     0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
+    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,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
+    0,  /* inherited */                         /* tp_richcompare */
+    0,  /* inherited */                         /* tp_weaklistoffset */
+    0,  /* inherited */                         /* tp_iter */
     0,                                          /* tp_iternext */
-    0,                                          /* tp_methods */
+    0,  /* inherited */                         /* tp_methods */
     0,                                          /* tp_members */
     0,                                          /* tp_getset */
     &CDataOwning_Type,                          /* tp_base */
@@ -3094,15 +3394,15 @@
     0,                                          /* tp_getattr */
     0,                                          /* tp_setattr */
     0,                                          /* tp_compare */
-    0,                                          /* tp_repr */
-    0,                                          /* tp_as_number */
+    0,  /* inherited */                         /* tp_repr */
+    0,  /* inherited */                         /* tp_as_number */
     0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
+    0,  /* inherited */                         /* tp_as_mapping */
+    0,  /* inherited */                         /* tp_hash */
+    0,  /* inherited */                         /* tp_call */
     0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
+    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
@@ -3112,11 +3412,11 @@
     0,                                          /* tp_doc */
     (traverseproc)cdatagcp_traverse,            /* tp_traverse */
     0,                                          /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
+    0,  /* inherited */                         /* tp_richcompare */
+    0,  /* inherited */                         /* tp_weaklistoffset */
+    0,  /* inherited */                         /* tp_iter */
     0,                                          /* tp_iternext */
-    0,                                          /* tp_methods */
+    0,  /* inherited */                         /* tp_methods */
     0,                                          /* tp_members */
     0,                                          /* tp_getset */
     &CData_Type,                                /* tp_base */
@@ -3128,7 +3428,7 @@
     0,                                          /* tp_init */
     0,                                          /* tp_alloc */
     0,                                          /* tp_new */
-    0,                                          /* tp_free */
+    0,  /* inherited */                         /* tp_free */
     0,                                          /* tp_is_gc */
     0,                                          /* tp_bases */
     0,                                          /* tp_mro */
@@ -3228,6 +3528,8 @@
                                            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);
@@ -3258,6 +3560,7 @@
     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)
@@ -3380,7 +3683,7 @@
         dataoffset = offsetof(CDataObject_own_nolength, alignment);
         datasize = ct->ct_size;
         if (datasize < 0) {
-            explicitlength = get_new_array_length(&init);
+            explicitlength = get_new_array_length(ct->ct_itemdescr, &init);
             if (explicitlength < 0)
                 return NULL;
             ctitem = ct->ct_itemdescr;
@@ -3550,18 +3853,24 @@
         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
-    else if (PyUnicode_Check(ob)) {
-        wchar_t ordinal;
-        if (_my_PyUnicode_AsSingleWideChar(ob, &ordinal) < 0) {
-            PyErr_Format(PyExc_TypeError,
-                      "cannot cast unicode string of length %zd to ctype '%s'",
-                         PyUnicode_GET_SIZE(ob), ct->ct_name);
-            return NULL;
-        }
-        value = (long)ordinal;
-    }
+        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)
@@ -3587,6 +3896,35 @@
     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;
@@ -3628,6 +3966,7 @@
         /* cast to a float */
         double value;
         PyObject *io;
+        int res;
 
         if (CData_Check(ob)) {
             CDataObject *cdsrc = (CDataObject *)ob;
@@ -3643,37 +3982,23 @@
             Py_INCREF(io);
         }
 
-        if (PyBytes_Check(io)) {
-            if (PyBytes_GET_SIZE(io) != 1) {
-                Py_DECREF(io);
-                goto cannot_cast;
-            }
-            value = (unsigned char)PyBytes_AS_STRING(io)[0];
-        }
-#if HAVE_WCHAR_H
-        else if (PyUnicode_Check(io)) {
-            wchar_t ordinal;
-            if (_my_PyUnicode_AsSingleWideChar(io, &ordinal) < 0) {
-                Py_DECREF(io);
-                goto cannot_cast;
-            }
-            value = (long)ordinal;
-        }
-#endif
-        else if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+        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;
-        }
-        else {
+                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);
@@ -3689,6 +4014,45 @@
         }
         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);
@@ -3726,7 +4090,8 @@
 
 static void dl_dealloc(DynLibObject *dlobj)
 {
-    dlclose(dlobj->dl_handle);
+    if (dlobj->dl_handle != NULL)
+        dlclose(dlobj->dl_handle);
     free(dlobj->dl_name);
     PyObject_Del(dlobj);
 }
@@ -3736,6 +4101,17 @@
     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;
@@ -3746,6 +4122,9 @@
                           &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'",
@@ -3778,6 +4157,9 @@
                           &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) {
@@ -3803,6 +4185,9 @@
                           &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) {
@@ -3818,10 +4203,22 @@
     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 */
 };
 
@@ -3857,44 +4254,103 @@
     dl_methods,                         /* tp_methods */
 };
 
-static PyObject *b_load_library(PyObject *self, PyObject *args)
-{
-    char *filename_or_null, *printable_filename;
+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;
-    DynLibObject *dlobj;
+    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;
-    }
-    else if (!PyArg_ParseTuple(args, "et|i:load_library",
-                          Py_FileSystemDefaultEncoding, &filename_or_null,
-                          &flags))
-        return 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;
 
-    printable_filename = filename_or_null ? filename_or_null : "<None>";
     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",
-                     printable_filename, error);
+        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);
-        return NULL;
+        goto error;
     }
     dlobj->dl_handle = handle;
     dlobj->dl_name = strdup(printable_filename);
+ 
+ error:
+    Py_XDECREF(temp);
     return (PyObject *)dlobj;
 }
 
@@ -3946,7 +4402,10 @@
 
     assert(x->ct_unique_key == NULL);
     x->ct_unique_key = key; /* the key will be freed in ctypedescr_dealloc() */
-    Py_DECREF(x);          /* the 'value' in unique_cache doesn't count as 1 */
+    /* 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:
@@ -3954,6 +4413,11 @@
     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                                    \
@@ -3971,7 +4435,11 @@
        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)                  \
@@ -4008,7 +4476,8 @@
 
 #ifdef HAVE_WCHAR_H
 # define ENUM_PRIMITIVE_TYPES_WCHAR                             \
-       EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR )
+       EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR |                  \
+                           (((wchar_t)-1) > 0 ? 0 : CT_IS_SIGNED_WCHAR))
 #else
 # define ENUM_PRIMITIVE_TYPES_WCHAR   /* nothing */
 #endif
@@ -4081,6 +4550,13 @@
         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;
@@ -4215,9 +4691,6 @@
         sprintf(extra_text, "[]");
         length = -1;
         arraysize = -1;
-        if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) &&
-                ctitem->ct_size == sizeof(char))
-            flags |= CT_IS_UNSIZED_CHAR_A;
     }
     else {
         sprintf(extra_text, "[%llu]", (unsigned PY_LONG_LONG)length);
@@ -4344,6 +4817,12 @@
 #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 */
@@ -4403,14 +4882,22 @@
     CFieldObject **previous;
     int prev_bitfield_size, prev_bitfield_free;
     int sflags = 0, fflags;
-
-    if (!PyArg_ParseTuple(args, "O!O!|Onii:complete_struct_or_union",
+    int pack = 0;
+
+    if (!PyArg_ParseTuple(args, "O!O!|Oniii:complete_struct_or_union",
                           &CTypeDescr_Type, &ct,
                           &PyList_Type, &fields,
-                          &ignored, &totalsize, &totalalignment, &sflags))
+                          &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)) {
@@ -4471,9 +4958,9 @@
         /* update the total alignment requirement, but skip it if the
            field is an anonymous bitfield or if SF_PACKED */
         falignorg = get_alignment(ftype);
-        falign = (sflags & SF_PACKED) ? 1 : falignorg;
-        if (falign < 0)
+        if (falignorg < 0)
             goto error;
+        falign = (pack < falignorg) ? pack : falignorg;
 
         do_align = 1;
         if (!(sflags & SF_GCC_ARM_BITFIELDS) && fbitsize >= 0) {
@@ -4520,7 +5007,6 @@
             if (PyText_GetSize(fname) == 0 &&
                     ftype->ct_flags & (CT_STRUCT|CT_UNION)) {
                 /* a nested anonymous struct or union */
-                /* note: it seems we only get here with ffi.verify() */
                 CFieldObject *cfsrc = (CFieldObject *)ftype->ct_extra;
                 for (; cfsrc != NULL; cfsrc = cfsrc->cf_next) {
                     /* broken complexity in the call to get_field_name(),
@@ -4773,7 +5259,7 @@
 {
     const char *place = is_result_type ? "return value" : "argument";
 
-    if (ct->ct_flags & CT_PRIMITIVE_ANY) {
+    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)) {
@@ -4899,9 +5385,16 @@
         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",
-                     ct->ct_name, ct->ct_size, place);
+                     "ctype '%s' (size %zd) not supported as %s%s",
+                     ct->ct_name, ct->ct_size, place, extra);
         return NULL;
     }
 }
@@ -5269,9 +5762,14 @@
             return 0;
         }
         else if (ctype->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED |
-                                    CT_PRIMITIVE_UNSIGNED)) {
+                                    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 */
+               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);
@@ -5282,12 +5780,12 @@
     return convert_from_object(result, ctype, pyobj);
 }
 
-static void _my_PyErr_WriteUnraisable(char *objdescr, PyObject *obj,
+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, *t, *v, *tb;
-    PyErr_Fetch(&t, &v, &tb);
+    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);
@@ -5377,8 +5875,12 @@
     }
     onerror_cb = PyTuple_GET_ITEM(cb_args, 3);
     if (onerror_cb == Py_None) {
-        _my_PyErr_WriteUnraisable("From cffi callback ", py_ob,
+        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;
@@ -5402,14 +5904,17 @@
         }
         else {
             /* double exception! print a double-traceback... */
+            PyObject *ecap;
             PyErr_Fetch(&exc2, &val2, &tb2);
-            PyErr_Restore(exc1, val1, tb1);
-            _my_PyErr_WriteUnraisable("From cffi callback ", py_ob,
+            ecap = _cffi_start_error_capture();
+            _my_PyErr_WriteUnraisable(exc1, val1, tb1,
+                                      "From cffi callback ", py_ob,
                                       extra_error_line);
-            PyErr_Restore(exc2, val2, tb2);
             extra_error_line = ("\nDuring the call to 'onerror', "
                                 "another exception occurred:\n\n");
-            _my_PyErr_WriteUnraisable(NULL, NULL, extra_error_line);
+            _my_PyErr_WriteUnraisable(exc2, val2, tb2,
+                                      NULL, NULL, extra_error_line);
+            _cffi_stop_error_capture(ecap);
         }
     }
     goto done;
@@ -5488,11 +5993,12 @@
 static PyObject *b_callback(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct;
-    CDataObject *cd;
+    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))
@@ -5502,15 +6008,24 @@
     if (infotuple == NULL)
         return NULL;
 
+#ifdef CFFI_TRUST_LIBFFI
+    closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec);
+#else
     closure = cffi_closure_alloc();
-
-    cd = PyObject_GC_New(CDataObject, &CDataOwningGC_Type);
+    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->c_type = ct;
-    cd->c_data = (char *)closure;
-    cd->c_weakreflist = NULL;
+    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;
@@ -5520,8 +6035,13 @@
                      "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;
@@ -5544,8 +6064,13 @@
 
  error:
     closure->user_data = NULL;
-    if (cd == 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);
@@ -5596,7 +6121,7 @@
         if (!PyText_Check(tmpkey)) {
 #if PY_MAJOR_VERSION < 3
             if (PyUnicode_Check(tmpkey)) {
-                char *text = PyText_AsUTF8(tmpkey);
+                const char *text = PyText_AsUTF8(tmpkey);
                 if (text == NULL)
                     goto error;
                 Py_DECREF(tmpkey);
@@ -5888,27 +6413,46 @@
             }
             return PyBytes_FromStringAndSize(start, length);
         }
-#ifdef HAVE_WCHAR_H
         else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) {
-            const wchar_t *start = (wchar_t *)cd->c_data;
-            assert(cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t));
-            if (length < 0) {
-                /*READ(start, sizeof(wchar_t))*/
-                length = 0;
-                while (start[length])
-                    length++;
-                /*READ(start, sizeof(wchar_t) * length)*/
+            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);
             }
-            else {
-                /*READ(start, sizeof(wchar_t) * length)*/
-                maxlen = length;
-                length = 0;
-                while (length < maxlen && start[length])
-                    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);
             }
-            return _my_PyUnicode_FromWideChar(start, length);
-        }
-#endif
+            }
+        }
     }
     else if (cd->c_type->ct_flags & CT_IS_ENUM) {
         return convert_cdata_to_enum_string(cd, 0);
@@ -5922,12 +6466,14 @@
         /*READ(cd->c_data, cd->c_type->ct_size)*/
         if (cd->c_type->ct_size == sizeof(char))
             return PyBytes_FromStringAndSize(cd->c_data, 1);
-#ifdef HAVE_WCHAR_H
         else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) {
-            assert(cd->c_type->ct_size == sizeof(wchar_t));
-            return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1);
-        }
-#endif
+            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);
@@ -5972,12 +6518,14 @@
     /* byte- and unicode strings */
     ctitem = cd->c_type->ct_itemdescr;
     if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) {
-        if (ctitem->ct_size == sizeof(char))
+        switch (ctitem->ct_size) {
+        case sizeof(char):
             return PyBytes_FromStringAndSize(cd->c_data, length);
-#ifdef HAVE_WCHAR_H
-        else if (ctitem->ct_size == sizeof(wchar_t))
-            return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, length);
-#endif
+        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
@@ -5993,6 +6541,7 @@
     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;
@@ -6261,10 +6810,18 @@
     return 0;
 }
 
-static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x)
+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.
@@ -6281,9 +6838,44 @@
         PyErr_NoMemory();
         return NULL;
     }
-    if (_my_PyObject_GetContiguousBuffer(x, view, 0) < 0)
+    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)
@@ -6293,7 +6885,7 @@
     cd->c_type = ct;
     cd->c_data = view->buf;
     cd->c_weakreflist = NULL;
-    ((CDataObject_owngc_frombuf *)cd)->length = view->len;
+    ((CDataObject_owngc_frombuf *)cd)->length = arraylength;
     ((CDataObject_owngc_frombuf *)cd)->bufferview = view;
     PyObject_GC_Track(cd);
     return (PyObject *)cd;
@@ -6309,15 +6901,13 @@
 {
     CTypeDescrObject *ct;
     PyObject *x;
-
-    if (!PyArg_ParseTuple(args, "O!O", &CTypeDescr_Type, &ct, &x))
+    int require_writable = 0;
+
+    if (!PyArg_ParseTuple(args, "O!O|i", &CTypeDescr_Type, &ct, &x,
+                          &require_writable))
         return NULL;
 
-    if (!(ct->ct_flags & CT_IS_UNSIZED_CHAR_A)) {
-        PyErr_Format(PyExc_TypeError, "needs 'char[]', got '%s'", ct->ct_name);
-        return NULL;
-    }
-    return direct_from_buffer(ct, x);
+    return direct_from_buffer(ct, x, require_writable);
 }
 
 static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only)
@@ -6384,10 +6974,12 @@
     CDataObject *cd;
     CDataObject *origobj;
     PyObject *destructor;
-    static char *keywords[] = {"cdata", "destructor", NULL};
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O:gc", keywords,
-                                     &CData_Type, &origobj, &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) {
@@ -6405,6 +6997,15 @@
     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)
@@ -6579,6 +7180,20 @@
     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 */
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to