summaryrefslogtreecommitdiff
path: root/c
diff options
context:
space:
mode:
authorKevin Cheng <kevcheng@google.com>2019-04-18 11:31:16 -0700
committerKevin Cheng <kevcheng@google.com>2019-05-02 13:59:40 -0700
commit757c264bc10ebc71074ee3f5fb66d670667a09bc (patch)
tree26c7f7b74c752db99d9b0ac1f94fc592aca1e53a /c
parent99013222844839c42437f16eace25f4e6a8a8b20 (diff)
downloadcffi-757c264bc10ebc71074ee3f5fb66d670667a09bc.tar.gz
Add in cffi 1.12.2 (e0c7666)
Since this is a mercurial repo, d/led zip of src: https://bitbucket.org/cffi/cffi/get/v1.12.2.zip Also add in misc METADATA/NOTICE/Android.bp/etc files. Bug: 122778810 Test: None Change-Id: I36c58ed07a2cdd4d9d11831908175a5c988f33c1
Diffstat (limited to 'c')
-rw-r--r--c/.libs_cffi_backend/libffi-45372312.so.6.0.4bin0 -> 149064 bytes
-rw-r--r--c/Android.bp53
-rw-r--r--c/_cffi_backend.c7686
-rw-r--r--c/_cffi_backend.sobin0 -> 787992 bytes
-rw-r--r--c/_dummy_file_cffi_backend.py0
-rw-r--r--c/_dummy_file_libffi.py0
-rw-r--r--c/call_python.c278
-rw-r--r--c/cdlopen.c361
-rw-r--r--c/cffi1_module.c231
-rw-r--r--c/cglob.c113
-rw-r--r--c/commontypes.c216
-rw-r--r--c/ffi_obj.c1221
-rw-r--r--c/file_emulator.h93
-rw-r--r--c/lib_obj.c712
-rw-r--r--c/libffi_msvc/LICENSE20
-rw-r--r--c/libffi_msvc/README502
-rw-r--r--c/libffi_msvc/README.ctypes7
-rw-r--r--c/libffi_msvc/ffi.c486
-rw-r--r--c/libffi_msvc/ffi.h322
-rw-r--r--c/libffi_msvc/ffi_common.h77
-rw-r--r--c/libffi_msvc/fficonfig.h96
-rw-r--r--c/libffi_msvc/ffitarget.h85
-rw-r--r--c/libffi_msvc/prep_cif.c181
-rw-r--r--c/libffi_msvc/types.c104
-rw-r--r--c/libffi_msvc/win32.c162
-rw-r--r--c/libffi_msvc/win64.asm156
-rw-r--r--c/libffi_msvc/win64.objbin0 -> 1176 bytes
-rw-r--r--c/malloc_closure.h176
-rw-r--r--c/minibuffer.h408
-rw-r--r--c/misc_thread_common.h371
-rw-r--r--c/misc_thread_posix.h49
-rw-r--r--c/misc_win32.h241
-rw-r--r--c/parse_c_type.c847
-rw-r--r--c/realize_c_type.c797
-rw-r--r--c/test_c.py4256
-rw-r--r--c/wchar_helper.h246
-rw-r--r--c/wchar_helper_3.h149
37 files changed, 20702 insertions, 0 deletions
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;
+}