diff options
| author | Armin Rigo <arigo@tunes.org> | 2012-08-12 15:53:06 +0200 |
|---|---|---|
| committer | Armin Rigo <arigo@tunes.org> | 2012-08-12 15:53:06 +0200 |
| commit | 07613592b703a7cf887091820df0e3c7ea25eebe (patch) | |
| tree | e6ad090f78f0be1959a00f0cae8ba7a7b7befa7a /c | |
| parent | 2a3e5600e8a47d9f604bff219869d85bf46b0d33 (diff) | |
| parent | 6751d3980678045f9e35892fd8ddb454ce79f4ec (diff) | |
| download | cffi-07613592b703a7cf887091820df0e3c7ea25eebe.tar.gz | |
hg merge default, and try to resolve all conflicts.
Not tested so far.
Diffstat (limited to 'c')
| -rw-r--r-- | c/_cffi_backend.c | 779 | ||||
| -rw-r--r-- | c/misc_win32.h | 3 | ||||
| -rw-r--r-- | c/test_c.py | 298 |
3 files changed, 745 insertions, 335 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 2e6cf8f..27cb91b 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -23,6 +23,7 @@ #endif #if PY_MAJOR_VERSION >= 3 +# define STR_OR_BYTES "bytes" # define PyText_Type PyUnicode_Type # define PyText_Check PyUnicode_Check # define PyText_FromFormat PyUnicode_FromFormat @@ -32,6 +33,7 @@ # define PyText_FromStringAndSize PyUnicode_FromStringAndSize # define PyText_InternInPlace PyUnicode_InternInPlace #else +# define STR_OR_BYTES "str" # define PyText_Type PyString_Type # define PyText_Check PyString_Check # define PyText_FromFormat PyString_FromFormat @@ -63,7 +65,7 @@ #define CT_PRIMITIVE_SIGNED 1 /* signed integer */ #define CT_PRIMITIVE_UNSIGNED 2 /* unsigned integer */ #define CT_PRIMITIVE_CHAR 4 /* char, wchar_t */ -#define CT_PRIMITIVE_FLOAT 8 /* float, double */ +#define CT_PRIMITIVE_FLOAT 8 /* float, double, long double */ #define CT_POINTER 16 /* pointer, excluding ptr-to-func */ #define CT_ARRAY 32 /* array */ #define CT_STRUCT 64 /* struct */ @@ -78,6 +80,7 @@ #define CT_IS_ENUM 8192 #define CT_IS_PTR_TO_OWNED 16384 #define CT_CUSTOM_FIELD_POS 32768 +#define CT_IS_LONGDOUBLE 65536 #define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \ CT_PRIMITIVE_UNSIGNED | \ CT_PRIMITIVE_CHAR | \ @@ -99,7 +102,8 @@ typedef struct _ctypedescr { 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 */ + 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 */ @@ -110,15 +114,19 @@ 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, cf_bitsize; + short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */ + short cf_bitsize; struct cfieldobject_s *cf_next; } CFieldObject; +#define BS_REGULAR (-1) /* a regular field, not with bitshift */ +#define BS_EMPTY_ARRAY (-2) /* a field which is an array 'type[0]' */ static PyTypeObject CTypeDescr_Type; static PyTypeObject CField_Type; @@ -138,6 +146,7 @@ typedef union { unsigned long long m_longlong; float m_float; double m_double; + long double m_longdouble; } union_alignment; typedef struct { @@ -147,22 +156,17 @@ typedef struct { typedef struct { CDataObject head; - PyObject *weakreflist; -} CDataObject_own_base; - -typedef struct { - CDataObject_own_base head; union_alignment alignment; } CDataObject_own_nolength; typedef struct { - CDataObject_own_base head; + CDataObject head; Py_ssize_t length; union_alignment alignment; } CDataObject_own_length; typedef struct { - CDataObject_own_base head; + CDataObject head; PyObject *structobj; } CDataObject_own_structptr; @@ -553,6 +557,12 @@ read_raw_float_data(char *target, int size) } } +static long double +read_raw_longdouble_data(char *target) +{ + return *((long double*)target); +} + static void write_raw_float_data(char *target, double source, int size) { @@ -564,6 +574,12 @@ write_raw_float_data(char *target, double source, int size) Py_FatalError("write_raw_float_data: bad float size"); } +static void +write_raw_longdouble_data(char *target, long double source) +{ + *((long double*)target) = source; +} + static PyObject * new_simple_cdata(char *data, CTypeDescrObject *ct) { @@ -573,15 +589,17 @@ new_simple_cdata(char *data, CTypeDescrObject *ct) Py_INCREF(ct); cd->c_data = data; cd->c_type = ct; + cd->c_weakreflist = NULL; return (PyObject *)cd; } static PyObject *convert_enum_string_to_int(CTypeDescrObject *ct, PyObject *ob) { PyObject *d_value; + char *p = PyText_AsUTF8(ob); - if (PyText_AsUTF8(ob)[0] == '#') { - char *number = PyText_AsUTF8(ob) + 1; /* strip initial '#' */ + if (p[0] == '#') { + char *number = p + 1; /* strip initial '#' */ PyObject *ob2 = PyText_FromString(number); if (ob2 == NULL) return NULL; @@ -602,6 +620,8 @@ static PyObject *convert_enum_string_to_int(CTypeDescrObject *ct, PyObject *ob) return d_value; } +static CDataObject *_new_casted_primitive(CTypeDescrObject *ct); /*forward*/ + static PyObject * convert_to_object(char *data, CTypeDescrObject *ct) { @@ -616,7 +636,16 @@ convert_to_object(char *data, CTypeDescrObject *ct) ct->ct_name); return NULL; } - else if (ct->ct_flags & (CT_ARRAY|CT_STRUCT|CT_UNION)) { + 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); } } @@ -650,8 +679,17 @@ convert_to_object(char *data, CTypeDescrObject *ct) return PyLong_FromUnsignedLongLong(value); } else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { - double value = read_raw_float_data(data, ct->ct_size); - return PyFloat_FromDouble(value); + 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) { if (ct->ct_size == sizeof(char)) @@ -793,78 +831,91 @@ convert_field_from_object(char *data, CFieldObject *cf, PyObject *value) } static int -convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init) +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; - char buf[sizeof(PY_LONG_LONG)]; + CTypeDescrObject *ctitem = ct->ct_itemdescr; - if (ct->ct_flags & CT_ARRAY) { - CTypeDescrObject *ctitem = ct->ct_itemdescr; - - if (PyList_Check(init) || PyTuple_Check(init)) { - PyObject **items; - Py_ssize_t i, n; - n = PySequence_Fast_GET_SIZE(init); + if (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) { + 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 = PyString_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); + "initializer "STR_OR_BYTES" is too long for '%s' " + "(got %zd characters)", 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; - } + if (n != ct->ct_length) + n++; + srcdata = PyString_AS_STRING(init); + memcpy(data, srcdata, n); return 0; } - else if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) { - if (ctitem->ct_size == sizeof(char)) { - char *srcdata; - Py_ssize_t n; - if (!PyBytes_Check(init)) { - expected = "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 string 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); - memcpy(data, srcdata, n); - return 0; - } #ifdef HAVE_WCHAR_H - else { - Py_ssize_t n; - if (!PyUnicode_Check(init)) { - expected = "unicode or list or tuple"; - goto cannot_convert; - } - n = _my_PyUnicode_SizeAsWideChar(init); - if (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++; - _my_PyUnicode_AsWideChar(init, (wchar_t *)data, n); - return 0; - } -#endif - } else { - expected = "list or tuple"; - goto cannot_convert; + Py_ssize_t n; + if (!PyUnicode_Check(init)) { + expected = "unicode or list or tuple"; + goto cannot_convert; + } + n = _my_PyUnicode_SizeAsWideChar(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++; + _my_PyUnicode_AsWideChar(init, (wchar_t *)data, n); + return 0; } +#endif + } + else { + expected = "list or tuple"; + goto cannot_convert; + } + + cannot_convert: + return _convert_error(init, ct->ct_name, expected); +} + +static int +convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init) +{ + const char *expected; + char buf[sizeof(PY_LONG_LONG)]; + + if (ct->ct_flags & CT_ARRAY) { + return convert_array_from_object(data, ct, init); } if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { char *ptrdata; @@ -937,10 +988,22 @@ convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init) return 0; } if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { - double value = PyFloat_AsDouble(init); + double value; + if ((ct->ct_flags & CT_IS_LONGDOUBLE) && + CData_Check(init) && + (((CDataObject *)init)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { + long double lvalue; + lvalue = read_raw_longdouble_data(((CDataObject *)init)->c_data); + write_raw_longdouble_data(data, lvalue); + return 0; + } + value = PyFloat_AsDouble(init); if (value == -1.0 && PyErr_Occurred()) return -1; - write_raw_float_data(data, value, ct->ct_size); + 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) { @@ -1128,26 +1191,26 @@ get_alignment(CTypeDescrObject *ct) static void cdata_dealloc(CDataObject *cd) { + if (cd->c_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) cd); + Py_DECREF(cd->c_type); PyObject_Del(cd); } -static void cdataowning_dealloc(CDataObject_own_base *cdb) +static void cdataowning_dealloc(CDataObject *cd) { - if (cdb->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) cdb); - - if (cdb->head.c_type->ct_flags & CT_IS_PTR_TO_OWNED) { - Py_DECREF(((CDataObject_own_structptr *)cdb)->structobj); + if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { + Py_DECREF(((CDataObject_own_structptr *)cd)->structobj); } - else if (cdb->head.c_type->ct_flags & CT_FUNCTIONPTR) { + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ - ffi_closure *closure = (ffi_closure *)cdb->head.c_data; + ffi_closure *closure = (ffi_closure *)cd->c_data; PyObject *args = (PyObject *)(closure->user_data); Py_XDECREF(args); cffi_closure_free(closure); } - cdata_dealloc(&cdb->head); + cdata_dealloc(cd); } static int cdata_traverse(CDataObject *cd, visitproc visit, void *arg) @@ -1156,31 +1219,37 @@ static int cdata_traverse(CDataObject *cd, visitproc visit, void *arg) return 0; } +static PyObject *cdata_float(CDataObject *cd); /*forward*/ + static PyObject *cdata_repr(CDataObject *cd) { - char *p, *extra; - PyObject *result, *s = NULL; + char *extra; + PyObject *result, *s; if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) { - PyObject *o = convert_to_object(cd->c_data, cd->c_type); - if (o == NULL) - return NULL; - s = PyObject_Repr(o); - Py_DECREF(o); - if (s == NULL) - return NULL; - p = PyText_AsUTF8(s); + if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) { + PyObject *o = convert_to_object(cd->c_data, cd->c_type); + if (o == NULL) + return NULL; + s = PyObject_Repr(o); + Py_DECREF(o); + } + else { + long double lvalue = read_raw_longdouble_data(cd->c_data); + char buffer[128]; /* big enough */ + sprintf(buffer, "%LE", lvalue); + s = PyText_FromString(buffer); + } } else { if (cd->c_data != NULL) { s = PyText_FromFormat("%p", cd->c_data); - if (s == NULL) - return NULL; - p = PyText_AsUTF8(s); } else - p = "NULL"; + 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...>". */ @@ -1188,95 +1257,12 @@ static PyObject *cdata_repr(CDataObject *cd) extra = " &"; else extra = ""; - result = PyText_FromFormat("<cdata '%s%s' %s>", - cd->c_type->ct_name, extra, p); - Py_XDECREF(s); + result = PyText_FromFormat("<cdata '%s%s' %S>", + cd->c_type->ct_name, extra, s); + Py_DECREF(s); return result; } -static PyObject *cdata_get_value(CDataObject *cd) -{ - if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR && - cd->c_type->ct_size == sizeof(char)) { - return PyBytes_FromStringAndSize(cd->c_data, 1); - } - else if (cd->c_type->ct_itemdescr != NULL && - cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR && - cd->c_type->ct_itemdescr->ct_size == sizeof(char)) { - Py_ssize_t length; - - if (cd->c_type->ct_flags & CT_ARRAY) { - const char *start = cd->c_data; - const char *end; - length = get_array_length(cd); - end = (const char *)memchr(start, 0, length); - if (end != NULL) - length = end - start; - } - else { - if (cd->c_data == NULL) { - PyObject *s = cdata_repr(cd); - if (s != NULL) { - PyErr_Format(PyExc_RuntimeError, - "cannot use str() on %S", s); - Py_DECREF(s); - } - return NULL; - } - length = strlen(cd->c_data); - } - - return PyBytes_FromStringAndSize(cd->c_data, length); - } -#ifdef HAVE_WCHAR_H - else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR && - cd->c_type->ct_size == sizeof(wchar_t)) { - return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1); - } - else if (cd->c_type->ct_itemdescr != NULL && - cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR && - cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t)) { - Py_ssize_t length; - const wchar_t *start = (wchar_t *)cd->c_data; - - if (cd->c_type->ct_flags & CT_ARRAY) { - const Py_ssize_t lenmax = get_array_length(cd); - length = 0; - while (length < lenmax && start[length]) - length++; - } - else { - if (cd->c_data == NULL) { - PyObject *s = cdata_repr(cd); - if (s != NULL) { - PyErr_Format(PyExc_RuntimeError, - "cannot use unicode() on %S", s); - Py_DECREF(s); - } - return NULL; - } - length = 0; - while (start[length]) - length++; - } - - return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, length); - } -#endif - else { - Py_INCREF(cd); - return (PyObject *)cd; - } -} - -static PyObject *cdata_str(CDataObject *cd) -{ - if (cd->c_type->ct_flags & CT_IS_ENUM) - return convert_to_object(cd->c_data, cd->c_type); - else - return Py_TYPE(cd)->tp_repr((PyObject *)cd); -} - static PyObject *cdataowning_repr(CDataObject *cd) { Py_ssize_t size; @@ -1336,7 +1322,7 @@ static PyObject *cdata_int(CDataObject *cd) #endif } else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { - PyObject *o = convert_to_object(cd->c_data, cd->c_type); + PyObject *o = cdata_float(cd); #if PY_MAJOR_VERSION < 3 PyObject *r = o ? PyNumber_Int(o) : NULL; #else @@ -1366,7 +1352,14 @@ static PyObject *cdata_long(CDataObject *cd) static PyObject *cdata_float(CDataObject *cd) { if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { - return convert_to_object(cd->c_data, cd->c_type); + double value; + 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); @@ -1607,10 +1600,13 @@ cdata_getattro(CDataObject *cd, PyObject *attr) if (cf != NULL) { /* read the field 'cf' */ char *data = cd->c_data + cf->cf_offset; - if (cf->cf_bitshift >= 0) - return convert_to_object_bitfield(data, cf); - else + if (cf->cf_bitshift == BS_REGULAR) return convert_to_object(data, cf->cf_type); + else if (cf->cf_bitshift == BS_EMPTY_ARRAY) + return new_simple_cdata(data, + (CTypeDescrObject *)cf->cf_type->ct_stuff); + else + return convert_to_object_bitfield(data, cf); } } return PyObject_GenericGetAttr((PyObject *)cd, attr); @@ -1665,14 +1661,72 @@ static CTypeDescrObject *_get_ct_int(void) return ct_int; } +static PyObject * +_prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init) +{ + /* 'ctptr' is here a pointer type 'ITEM *'. Accept as argument an + initializer for an array 'ITEM[]'. This includes the case of + passing a Python string to a 'char *' argument. */ + Py_ssize_t length, datasize; + CTypeDescrObject *ctitem = ctptr->ct_itemdescr; + PyObject *result; + char *data; + + /* XXX some code duplication, how to avoid it? */ + if (PyString_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 ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) && + (ctitem->ct_size == sizeof(char))) { + Py_INCREF(init); + return init; + } + else + return Py_None; + } + 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 */ + length = _my_PyUnicode_SizeAsWideChar(init) + 1; + } + else { + /* refuse to receive just an integer (and interpret it + as the array size) */ + return Py_None; + } + + if (ctitem->ct_size <= 0) + return Py_None; + datasize = length * ctitem->ct_size; + if ((datasize / ctitem->ct_size) != length) { + PyErr_SetString(PyExc_OverflowError, + "array size would overflow a Py_ssize_t"); + return NULL; + } + + result = PyString_FromStringAndSize(NULL, datasize); + if (result == NULL) + return NULL; + + data = PyString_AS_STRING(result); + memset(data, 0, datasize); + if (convert_array_from_object(data, ctptr, init) < 0) { + Py_DECREF(result); + return NULL; + } + return result; +} + static PyObject* cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) { char *buffer; void** buffer_array; cif_description_t *cif_descr; - Py_ssize_t i, nargs, nargs_declared; - PyObject *signature, *res, *fvarargs; + Py_ssize_t i, nargs, nargs_declared, free_me_until = 0; + PyObject *signature, *res = NULL, *fvarargs; CTypeDescrObject *fresult; char *resultdata; char *errormsg; @@ -1702,7 +1756,10 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) /* regular case: this function does not take '...' arguments */ if (nargs != nargs_declared) { errormsg = "'%s' expects %zd arguments, got %zd"; - goto bad_number_of_arguments; + bad_number_of_arguments: + PyErr_Format(PyExc_TypeError, errormsg, + cd->c_type->ct_name, nargs_declared, nargs); + goto error; } } else { @@ -1778,37 +1835,44 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) else argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i); - if ((argtype->ct_flags & CT_POINTER) && - (argtype->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR)) { - if (argtype->ct_itemdescr->ct_size == sizeof(char)) { - if (PyText_Check(obj)) { - /* special case: Python string -> cdata 'char *' */ - *(char **)data = PyText_AsUTF8(obj); + if (argtype->ct_flags & CT_POINTER) { + PyObject *string; + if (!CData_Check(obj)) { + string = _prepare_pointer_call_argument(argtype, obj); + if (string != Py_None) { + if (string == NULL) + goto error; + ((char **)data)[0] = PyBytes_AS_STRING(string); + ((char **)data)[1] = (char *)string; + assert(i < nargs_declared); /* otherwise, obj is a CData */ + free_me_until = i + 1; continue; } } -#ifdef HAVE_WCHAR_H - else { - if (PyUnicode_Check(obj)) { - /* Python Unicode string -> cdata 'wchar_t *': - not supported yet */ - PyErr_SetString(PyExc_NotImplementedError, - "automatic unicode-to-'wchar_t *' conversion"); - goto error; - } - } -#endif + ((char **)data)[1] = NULL; } - if (convert_from_object(data, argtype, obj) < 0) + if (convert_from_object(data, argtype, obj) < 0) { + if (CData_Check(obj) && (argtype->ct_flags & CT_IS_PTR_TO_OWNED) && + argtype->ct_itemdescr == ((CDataObject *)obj)->c_type) { + /* special case to make the life of verifier.py easier: + if the formal argument type is 'struct foo *' but + we pass a 'struct foo', then get a pointer to it */ + PyErr_Clear(); + ((char **)data)[0] = ((CDataObject *)obj)->c_data; + continue; + } goto error; + } } resultdata = buffer + cif_descr->exchange_offset_arg[0]; + 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)) { @@ -1831,23 +1895,26 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) else { res = convert_to_object(resultdata, fresult); } - PyObject_Free(buffer); - done: + /* fall-through */ + + error: + for (i=0; i<free_me_until; i++) { + CTypeDescrObject *argtype; + argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 2 + i); + if (argtype->ct_flags & CT_POINTER) { + char *data = buffer + cif_descr->exchange_offset_arg[1 + i]; + PyObject *string_or_null = (PyObject *)(((char **)data)[1]); + Py_XDECREF(string_or_null); + } + } + 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; - - bad_number_of_arguments: - PyErr_Format(PyExc_TypeError, errormsg, - cd->c_type->ct_name, nargs_declared, nargs); - error: - if (buffer) - PyObject_Free(buffer); - res = NULL; - goto done; } static PyObject *cdata_iter(CDataObject *); @@ -1894,15 +1961,6 @@ static PyMappingMethods CDataOwn_as_mapping = { (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/ }; -static PyMethodDef CData_methods[] = { - {NULL, NULL} /* sentinel */ -}; - -static PyGetSetDef CData_getset[] = { - {"value", (getter)cdata_get_value, NULL, NULL}, - {0} -}; - static PyTypeObject CData_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CData", @@ -1919,7 +1977,7 @@ static PyTypeObject CData_Type = { &CData_as_mapping, /* tp_as_mapping */ (hashfunc)cdata_hash, /* tp_hash */ (ternaryfunc)cdata_call, /* tp_call */ - (reprfunc)cdata_str, /* tp_str */ + 0, /* tp_str */ (getattrofunc)cdata_getattro, /* tp_getattro */ (setattrofunc)cdata_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ @@ -1928,18 +1986,15 @@ static PyTypeObject CData_Type = { (traverseproc)cdata_traverse, /* tp_traverse */ 0, /* tp_clear */ cdata_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ + offsetof(CDataObject, c_weakreflist), /* tp_weaklistoffset */ (getiterfunc)cdata_iter, /* tp_iter */ 0, /* tp_iternext */ - CData_methods, /* tp_methods */ - 0, /* tp_members */ - CData_getset, /* tp_getset */ }; static PyTypeObject CDataOwning_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_cffi_backend.CDataOwn", - sizeof(CDataObject_own_base), + sizeof(CDataObject), 0, (destructor)cdataowning_dealloc, /* tp_dealloc */ 0, /* tp_print */ @@ -1961,7 +2016,7 @@ static PyTypeObject CDataOwning_Type = { 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ - offsetof(CDataObject_own_base, weakreflist),/* tp_weaklistoffset */ + 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ @@ -2053,24 +2108,24 @@ cdata_iter(CDataObject *cd) /************************************************************/ -static CDataObject_own_base *allocate_owning_object(Py_ssize_t size, - CTypeDescrObject *ct) +static CDataObject *allocate_owning_object(Py_ssize_t size, + CTypeDescrObject *ct) { - CDataObject_own_base *cdb; - cdb = (CDataObject_own_base *)PyObject_Malloc(size); - if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL) + CDataObject *cd; + cd = (CDataObject *)PyObject_Malloc(size); + if (PyObject_Init((PyObject *)cd, &CDataOwning_Type) == NULL) return NULL; Py_INCREF(ct); - cdb->head.c_type = ct; - cdb->weakreflist = NULL; - return cdb; + cd->c_type = ct; + cd->c_weakreflist = NULL; + return cd; } static PyObject * convert_struct_to_owning_object(char *data, CTypeDescrObject *ct) { - CDataObject_own_base *cdb; + CDataObject *cd; Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment); Py_ssize_t datasize = ct->ct_size; @@ -2079,20 +2134,19 @@ convert_struct_to_owning_object(char *data, CTypeDescrObject *ct) "return type is not a struct or is opaque"); return NULL; } - cdb = allocate_owning_object(dataoffset + datasize, ct); - if (cdb == NULL) + cd = allocate_owning_object(dataoffset + datasize, ct); + if (cd == NULL) return NULL; - cdb->head.c_data = ((char *)cdb) + dataoffset; + cd->c_data = ((char *)cd) + dataoffset; - memcpy(cdb->head.c_data, data, datasize); - return (PyObject *)cdb; + memcpy(cd->c_data, data, datasize); + return (PyObject *)cd; } static PyObject *b_newp(PyObject *self, PyObject *args) { CTypeDescrObject *ct, *ctitem; CDataObject *cd; - CDataObject_own_base *cdb; PyObject *init = Py_None; Py_ssize_t dataoffset, datasize, explicitlength; if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init)) @@ -2160,33 +2214,31 @@ static PyObject *b_newp(PyObject *self, PyObject *args) 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_own_base *cdp; + CDataObject *cds; - cdb = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr); - if (cdb == NULL) + cds = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr); + if (cds == NULL) return NULL; - cdp = allocate_owning_object(sizeof(CDataObject_own_structptr), ct); - if (cdp == NULL) { - Py_DECREF(cdb); + cd = allocate_owning_object(sizeof(CDataObject_own_structptr), ct); + if (cd == NULL) { + Py_DECREF(cds); return NULL; } - /* store the only reference to cdb into cdp */ - ((CDataObject_own_structptr *)cdp)->structobj = (PyObject *)cdb; + /* store the only reference to cds into cd */ + ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds; assert(explicitlength < 0); - cdb->head.c_data = cdp->head.c_data = ((char *)cdb) + dataoffset; - cd = &cdp->head; + cds->c_data = cd->c_data = ((char *)cds) + dataoffset; } else { - cdb = allocate_owning_object(dataoffset + datasize, ct); - if (cdb == NULL) + cd = allocate_owning_object(dataoffset + datasize, ct); + if (cd == NULL) return NULL; - cdb->head.c_data = ((char *)cdb) + dataoffset; + cd->c_data = ((char *)cd) + dataoffset; if (explicitlength >= 0) - ((CDataObject_own_length*)cdb)->length = explicitlength; - cd = &cdb->head; + ((CDataObject_own_length*)cd)->length = explicitlength; } memset(cd->c_data, 0, datasize); @@ -2209,6 +2261,7 @@ static CDataObject *_new_casted_primitive(CTypeDescrObject *ct) Py_INCREF(ct); cd->c_type = ct; cd->c_data = ((char*)cd) + dataoffset; + cd->c_weakreflist = NULL; return cd; } @@ -2337,6 +2390,16 @@ static PyObject *b_cast(PyObject *self, PyObject *args) } value = (unsigned char)PyBytes_AS_STRING(io)[0]; } + else if ((ct->ct_flags & CT_IS_LONGDOUBLE) && + CData_Check(io) && + (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { + long double lvalue; + lvalue = read_raw_longdouble_data(((CDataObject *)io)->c_data); + cd = _new_casted_primitive(ct); + if (cd != NULL) + write_raw_longdouble_data(cd->c_data, lvalue); + return (PyObject *)cd; + } else { value = PyFloat_AsDouble(io); } @@ -2345,8 +2408,12 @@ static PyObject *b_cast(PyObject *self, PyObject *args) return NULL; cd = _new_casted_primitive(ct); - if (cd != NULL) - write_raw_float_data(cd->c_data, value, ct->ct_size); + 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 { @@ -2500,19 +2567,26 @@ static PyTypeObject dl_type = { static PyObject *b_load_library(PyObject *self, PyObject *args) { - char *filename; + char *filename_or_null, *printable_filename; void *handle; DynLibObject *dlobj; int is_global = 0; - if (!PyArg_ParseTuple(args, "et|i:load_library", - Py_FileSystemDefaultEncoding, &filename, + if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) { + filename_or_null = NULL; + is_global = 1; + } + else if (!PyArg_ParseTuple(args, "et|i:load_library", + Py_FileSystemDefaultEncoding, &filename_or_null, &is_global)) return NULL; - handle = dlopen(filename, RTLD_LAZY | (is_global?RTLD_GLOBAL:RTLD_LOCAL)); + printable_filename = filename_or_null ? filename_or_null : "<None>"; + handle = dlopen(filename_or_null, + RTLD_LAZY | (is_global?RTLD_GLOBAL:RTLD_LOCAL)); if (handle == NULL) { - PyErr_Format(PyExc_OSError, "cannot load library: %s", filename); + PyErr_Format(PyExc_OSError, "cannot load library: %s", + printable_filename); return NULL; } @@ -2522,7 +2596,7 @@ static PyObject *b_load_library(PyObject *self, PyObject *args) return NULL; } dlobj->dl_handle = handle; - dlobj->dl_name = strdup(filename); + dlobj->dl_name = strdup(printable_filename); return (PyObject *)dlobj; } @@ -2588,7 +2662,8 @@ static PyObject *b_new_primitive_type(PyObject *self, PyObject *args) 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(d, double, CT_PRIMITIVE_FLOAT ) \ + EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE ) #ifdef HAVE_WCHAR_H # define ENUM_PRIMITIVE_TYPES_WCHAR \ EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR ) @@ -2654,6 +2729,8 @@ static PyObject *b_new_primitive_type(PyObject *self, PyObject *args) ffitype = &ffi_type_float; else if (strcmp(ptypes->name, "double") == 0) ffitype = &ffi_type_double; + else if (strcmp(ptypes->name, "long double") == 0) + ffitype = &ffi_type_longdouble; else goto bad_ffi_type; } @@ -2713,6 +2790,7 @@ static PyObject *b_new_pointer_type(PyObject *self, PyObject *args) 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; @@ -2908,7 +2986,10 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) if (fbitsize < 0 || (fbitsize == 8 * ftype->ct_size && !(ftype->ct_flags & CT_PRIMITIVE_CHAR))) { fbitsize = -1; - bitshift = -1; + if (ftype->ct_flags & CT_ARRAY && ftype->ct_length == 0) + bitshift = BS_EMPTY_ARRAY; + else + bitshift = BS_REGULAR; prev_bit_position = 0; } else { @@ -3225,6 +3306,15 @@ static int fb_build(struct funcbuilder_s *fb, PyObject *fargs, exchange_offset = ALIGN_ARG(exchange_offset); cif_descr->exchange_offset_arg[1 + i] = exchange_offset; exchange_offset += atype->size; + /* if 'farg' is a pointer type 'ITEM *', then we might receive + as argument to the function call what is an initializer + for an array 'ITEM[]'. This includes the case of passing a + Python string to a 'char *' argument. In this case, we + convert the initializer to a cdata 'ITEM[]' that gets + temporarily stored here: */ + if (farg->ct_flags & CT_POINTER) { + exchange_offset += sizeof(PyObject *); + } } } @@ -3505,6 +3595,9 @@ static void invoke_callback(ffi_cif *cif, void *result, void **args, { save_errno(); { +#ifdef WITH_THREAD + PyGILState_STATE state = PyGILState_Ensure(); +#endif PyObject *cb_args = (PyObject *)userdata; CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0); PyObject *signature = ct->ct_stuff; @@ -3539,6 +3632,9 @@ static void invoke_callback(ffi_cif *cif, void *result, void **args, Py_XDECREF(py_args); Py_XDECREF(py_res); Py_DECREF(cb_args); +#ifdef WITH_THREAD + PyGILState_Release(state); +#endif restore_errno(); return; @@ -3558,7 +3654,7 @@ static void invoke_callback(ffi_cif *cif, void *result, void **args, static PyObject *b_callback(PyObject *self, PyObject *args) { CTypeDescrObject *ct, *ctresult; - CDataObject_own_base *cdb; + CDataObject *cd; PyObject *ob, *error_ob = Py_None; PyObject *py_rawerr, *infotuple = NULL; cif_description_t *cif_descr; @@ -3602,13 +3698,13 @@ static PyObject *b_callback(PyObject *self, PyObject *args) closure = cffi_closure_alloc(); - cdb = PyObject_New(CDataObject_own_base, &CDataOwning_Type); - if (cdb == NULL) + cd = PyObject_New(CDataObject, &CDataOwning_Type); + if (cd == NULL) goto error; Py_INCREF(ct); - cdb->head.c_type = ct; - cdb->head.c_data = (char *)closure; - cdb->weakreflist = NULL; + cd->c_type = ct; + cd->c_data = (char *)closure; + cd->c_weakreflist = NULL; cif_descr = (cif_description_t *)ct->ct_extra; if (cif_descr == NULL) { @@ -3623,14 +3719,14 @@ static PyObject *b_callback(PyObject *self, PyObject *args) goto error; } assert(closure->user_data == infotuple); - return (PyObject *)cdb; + return (PyObject *)cd; error: closure->user_data = NULL; - if (cdb == NULL) + if (cd == NULL) cffi_closure_free(closure); else - Py_DECREF(cdb); + Py_DECREF(cd); Py_XDECREF(infotuple); return NULL; } @@ -3812,6 +3908,84 @@ static PyObject *b_getcname(PyObject *self, PyObject *args) return PyText_FromStringAndSize(s, namelen + replacelen); } +static PyObject *b_string(PyObject *self, PyObject *args) +{ + CDataObject *cd; + Py_ssize_t maxlen = -1; + if (!PyArg_ParseTuple(args, "O!|n:string", + &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)) { + 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", + PyString_AS_STRING(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) + length = strlen(start); + else { + const char *end; + end = (const char *)memchr(start, 0, length); + if (end != NULL) + length = end - start; + } + return PyString_FromStringAndSize(start, length); + } +#ifdef HAVE_WCHAR_H + else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) { + const wchar_t *start = (wchar_t *)cd->c_data; + assert(cd->c_type->ct_itemdescr->ct_size == sizeof(wchar_t)); + if (length < 0) { + length = 0; + while (start[length]) + length++; + } + else { + maxlen = length; + length = 0; + while (length < maxlen && start[length]) + length++; + } + return _my_PyUnicode_FromWideChar(start, length); + } +#endif + } + else if (cd->c_type->ct_flags & CT_IS_ENUM) { + return convert_to_object(cd->c_data, cd->c_type); + } + else if (cd->c_type->ct_flags & (CT_PRIMITIVE_CHAR | + CT_PRIMITIVE_SIGNED | + CT_PRIMITIVE_UNSIGNED)) { + if (cd->c_type->ct_size == sizeof(char)) { + return PyString_FromStringAndSize(cd->c_data, 1); + } +#ifdef HAVE_WCHAR_H + else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) { + assert(cd->c_type->ct_size == sizeof(wchar_t)); + return _my_PyUnicode_FromWideChar((wchar_t *)cd->c_data, 1); + } +#endif + } + PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument", + cd->c_type->ct_name); + return NULL; +} + static PyObject *b_buffer(PyObject *self, PyObject *args) { CDataObject *cd; @@ -3996,6 +4170,24 @@ static struct _testfunc17_s _testfunc17(int n) return result; } +static int _testfunc18(struct _testfunc17_s *ptr) +{ + return ptr->a1 + (int)ptr->a2; +} + +static long double _testfunc19(long double x) +{ + int i; + for (i=0; i<28; i++) + x += x; + return x; +} + +static short _testfunc20(struct _testfunc7_s *ptr) +{ + return ptr->a1 + ptr->a2; +} + static PyObject *b__testfunc(PyObject *self, PyObject *args) { /* for testing only */ @@ -4022,6 +4214,9 @@ static PyObject *b__testfunc(PyObject *self, PyObject *args) 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; default: PyErr_SetNone(PyExc_ValueError); return NULL; @@ -4050,6 +4245,7 @@ static PyMethodDef FFIBackendMethods[] = { {"typeof", b_typeof, METH_O}, {"offsetof", b_offsetof, METH_VARARGS}, {"getcname", b_getcname, METH_VARARGS}, + {"string", b_string, METH_VARARGS}, {"buffer", b_buffer, METH_VARARGS}, {"get_errno", b_get_errno, METH_NOARGS}, {"set_errno", b_set_errno, METH_VARARGS}, @@ -4209,12 +4405,13 @@ static struct PyModuleDef FFIBackendModuleDef = { }; #define INITERROR return NULL -PyObject * +PyMODINIT_FUNC PyInit__cffi_backend(void) #else #define INITERROR return -void init_cffi_backend(void) +PyMODINIT_FUNC +init_cffi_backend(void) #endif { PyObject *m, *v; @@ -4253,7 +4450,7 @@ void init_cffi_backend(void) if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0) INITERROR; - v = PyText_FromString("0.2.1"); + v = PyText_FromString("0.3"); if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0) INITERROR; diff --git a/c/misc_win32.h b/c/misc_win32.h index bcba9d2..718514d 100644 --- a/c/misc_win32.h +++ b/c/misc_win32.h @@ -21,7 +21,8 @@ static struct cffi_errno_s *_geterrno_object(void) LPVOID p = TlsGetValue(cffi_tls_index); if (p == NULL) { - p = PyMem_Malloc(sizeof(struct cffi_errno_s)); + /* XXX this malloc() leaks */ + p = malloc(sizeof(struct cffi_errno_s)); if (p == NULL) return NULL; memset(p, 0, sizeof(struct cffi_errno_s)); diff --git a/c/test_c.py b/c/test_c.py index a5e9ad7..2d2df6a 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -26,7 +26,10 @@ def size_of_ptr(): def find_and_load_library(name, is_global=0): import ctypes.util - path = ctypes.util.find_library(name) + if name is None: + path = None + else: + path = ctypes.util.find_library(name) return load_library(path, is_global) def test_load_library(): @@ -92,6 +95,7 @@ def test_integer_types(): 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 @@ -122,6 +126,39 @@ def test_float_types(): assert float(cast(p, True)) == 1.0 py.test.raises(TypeError, cast, p, None) +def test_complex_types(): + py.test.skip("later") + INF = 1E200 * 1E200 + for name in ["float", "double"]: + p = new_primitive_type("_Complex " + name) + assert bool(cast(p, 0)) + assert bool(cast(p, INF)) + assert bool(cast(p, -INF)) + assert bool(cast(p, 0j)) + assert bool(cast(p, INF*1j)) + assert bool(cast(p, -INF*1j)) + 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 float(cast(p, INF*1j)) == INF*1j + assert float(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, 3+1E200j)) == 3+INF*1j # 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' + assert complex(cast(p, '\x09')) == 9.0 + assert complex(cast(p, True)) == 1.0 + py.test.raises(TypeError, cast, p, None) + # + py.test.raises(cast, new_primitive_type(name), 1+2j) + py.test.raises(cast, new_primitive_type("int"), 1+2j) + def test_character_type(): p = new_primitive_type("char") assert bool(cast(p, '\x00')) @@ -130,7 +167,7 @@ def test_character_type(): assert long(cast(p, 'A')) == 65L assert type(int(cast(p, 'A'))) is int assert type(long(cast(p, 'A'))) is long - assert str(cast(p, 'A')) == 'A' + assert str(cast(p, 'A')) == repr(cast(p, 'A')) assert repr(cast(p, 'A')) == "<cdata 'char' 'A'>" assert repr(cast(p, 255)) == r"<cdata 'char' '\xff'>" assert repr(cast(p, 0)) == r"<cdata 'char' '\x00'>" @@ -235,7 +272,9 @@ def test_reading_pointer_to_char(): assert p[0] == 'A' py.test.raises(TypeError, newp, BPtr, 65) py.test.raises(TypeError, newp, BPtr, "foo") - assert str(cast(BChar, 'A')) == 'A' + c = cast(BChar, 'A') + assert str(c) == repr(c) + assert int(c) == ord('A') py.test.raises(TypeError, cast, BChar, 'foo') def test_reading_pointer_to_pointer(): @@ -261,6 +300,16 @@ def test_reading_pointer_to_pointer(): 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(KeyError, x.load_function, + BVoidP, 'xxx_this_function_does_not_exist') + def test_hash_differences(): BChar = new_primitive_type("char") BInt = new_primitive_type("int") @@ -295,6 +344,9 @@ def test_invalid_indexing(): py.test.raises(TypeError, "p[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) @@ -320,7 +372,7 @@ def test_cast_from_cdataint(): y = cast(BInt, x) assert int(y) == 42 y = cast(new_primitive_type("char"), x) - assert str(y) == chr(42) + assert int(y) == 42 y = cast(new_primitive_type("float"), x) assert float(y) == 42.0 # @@ -461,7 +513,7 @@ def test_cast_primitive_from_cdata(): # p = new_primitive_type("char") n = cast(p, cast(p, "A")) - assert str(n) == "A" + assert int(n) == ord("A") def test_new_primitive_from_cdata(): p = new_primitive_type("int") @@ -763,12 +815,22 @@ def test_call_function_6(): BFunc6bis = new_function_type((BIntArray,), BIntPtr, False) f = cast(BFunc6bis, _testfunc(6)) # - py.test.raises(TypeError, f, [142]) + 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") @@ -786,6 +848,22 @@ def test_call_function_7(): res = f(x[0]) assert res == -4042 + ord('A') +def test_call_function_20(): + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BStruct = new_struct_type("foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BShort, -1)]) + BFunc18 = new_function_type((BStructPtr,), BShort, False) + f = cast(BFunc18, _testfunc(20)) + x = newp(BStructPtr, {'a1': 'A', 'a2': -4042}) + # test the exception that allows us to pass a 'struct foo' where the + # function really expects a 'struct foo *'. + res = f(x[0]) + assert res == -4042 + ord('A') + assert res == f(x) + def test_call_function_9(): BInt = new_primitive_type("int") BFunc9 = new_function_type((BInt,), BInt, True) # vararg @@ -949,14 +1027,14 @@ def test_cast_to_enum(): BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20)) e = cast(BEnum, 0) assert repr(e) == "<cdata 'enum foo' 'def'>" - assert str(e) == 'def' - assert str(cast(BEnum, -20)) == 'ab' - assert str(cast(BEnum, 'c')) == 'c' + assert string(e) == 'def' + assert string(cast(BEnum, -20)) == 'ab' + assert string(cast(BEnum, 'c')) == 'c' assert int(cast(BEnum, 'c')) == 1 assert int(cast(BEnum, 'def')) == 0 assert int(cast(BEnum, -242 + 2**128)) == -242 - assert str(cast(BEnum, -242 + 2**128)) == '#-242' - assert str(cast(BEnum, '#-20')) == 'ab' + assert string(cast(BEnum, -242 + 2**128)) == '#-242' + assert string(cast(BEnum, '#-20')) == 'ab' assert repr(cast(BEnum, '#-20')) == "<cdata 'enum foo' 'ab'>" assert repr(cast(BEnum, '#-21')) == "<cdata 'enum foo' '#-21'>" @@ -1082,8 +1160,8 @@ def test_weakref(): BPtr = new_pointer_type(BInt) weakref.ref(BInt) weakref.ref(newp(BPtr, 42)) - py.test.raises(TypeError, weakref.ref, cast(BPtr, 42)) - py.test.raises(TypeError, weakref.ref, cast(BInt, 42)) + weakref.ref(cast(BPtr, 42)) + weakref.ref(cast(BInt, 42)) def test_no_inheritance(): BInt = new_primitive_type("int") @@ -1106,11 +1184,12 @@ def test_assign_string(): BArray1 = new_array_type(new_pointer_type(BChar), 5) BArray2 = new_array_type(new_pointer_type(BArray1), 5) a = newp(BArray2, ["abc", "de", "ghij"]) - assert str(a[2]) == "ghij" + assert string(a[1]) == "de" + assert string(a[2]) == "ghij" a[2] = "." - assert str(a[2]) == "." + assert string(a[2]) == "." a[2] = "12345" - assert str(a[2]) == "12345" + assert string(a[2]) == "12345" e = py.test.raises(IndexError, 'a[2] = "123456"') assert 'char[5]' in str(e.value) assert 'got 6 characters' in str(e.value) @@ -1203,16 +1282,56 @@ def test_newp_copying(): p2 = newp(new_pointer_type(BFunc), p1) assert p2[0] == p1 -def test_str(): +def test_string(): BChar = new_primitive_type("char") + assert string(cast(BChar, 42)) == '*' + assert string(cast(BChar, 0)) == '\x00' BCharP = new_pointer_type(BChar) BArray = new_array_type(BCharP, 10) a = newp(BArray, "hello") assert len(a) == 10 - assert str(a) == "hello" + assert string(a) == "hello" p = a + 2 - assert str(p) == "llo" - py.test.raises(RuntimeError, str, cast(BCharP, 0)) + assert string(p) == "llo" + assert string(newp(new_array_type(BCharP, 4), "abcd")) == "abcd" + py.test.raises(RuntimeError, string, cast(BCharP, 0)) + assert string(a, 4) == "hell" + assert string(a, 5) == "hello" + assert string(a, 6) == "hello" + +def test_string_byte(): + BByte = new_primitive_type("signed char") + assert string(cast(BByte, 42)) == '*' + assert string(cast(BByte, 0)) == '\x00' + BArray = new_array_type(new_pointer_type(BByte), None) + a = newp(BArray, [65, 66, 67]) + assert type(string(a)) is str and string(a) == 'ABC' + # + BByte = new_primitive_type("unsigned char") + assert string(cast(BByte, 42)) == '*' + assert string(cast(BByte, 0)) == '\x00' + BArray = new_array_type(new_pointer_type(BByte), None) + a = newp(BArray, [65, 66, 67]) + assert type(string(a)) is str and string(a) == 'ABC' + if 'PY_DOT_PY' not in globals(): + assert string(a, 8).startswith('ABC') # may contain additional garbage + +def test_string_wchar(): + BWChar = new_primitive_type("wchar_t") + 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(): + assert string(a, 8).startswith(u'ABC') # may contain additional garbage + +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") @@ -1229,12 +1348,12 @@ def test_set_struct_fields(): BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)]) p = newp(BStructPtr, None) - assert str(p.a1) == '' + assert string(p.a1) == '' p.a1 = 'foo' - assert str(p.a1) == 'foo' + assert string(p.a1) == 'foo' assert list(p.a1) == ['f', 'o', 'o'] + ['\x00'] * 7 p.a1 = ['x', 'y'] - assert str(p.a1) == 'xyo' + assert string(p.a1) == 'xyo' def test_invalid_function_result_types(): BFunc = new_function_type((), new_void_type()) @@ -1332,6 +1451,14 @@ def test_struct_return_in_func(): 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()) @@ -1356,7 +1483,7 @@ def test_wchar(): if wchar4: x = cast(BWChar, 0x12345) assert str(x) == "<cdata 'wchar_t' u'\U00012345'>" - assert unicode(x) == u'\U00012345' + assert int(x) == 0x12345 else: assert not pyuni4 # @@ -1387,20 +1514,20 @@ def test_wchar(): BWCharArray = new_array_type(BWCharP, None) a = newp(BWCharArray, u'hello \u1234 world') assert len(a) == 14 # including the final null - assert unicode(a) == u'hello \u1234 world' + assert string(a) == u'hello \u1234 world' a[13] = u'!' - assert unicode(a) == u'hello \u1234 world!' + assert string(a) == u'hello \u1234 world!' assert str(a) == repr(a) assert a[6] == u'\u1234' a[6] = u'-' - assert unicode(a) == 'hello - world!' + assert string(a) == u'hello - world!' assert str(a) == repr(a) # if wchar4: u = u'\U00012345\U00012346\U00012347' a = newp(BWCharArray, u) assert len(a) == 4 - assert unicode(a) == u + assert string(a) == u assert len(list(a)) == 4 expected = [u'\U00012345', u'\U00012346', u'\U00012347', unichr(0)] assert list(a) == expected @@ -1411,17 +1538,17 @@ def test_wchar(): w = cast(BWChar, 'a') assert repr(w) == "<cdata 'wchar_t' u'a'>" assert str(w) == repr(w) - assert unicode(w) == u'a' + assert string(w) == u'a' assert int(w) == ord('a') w = cast(BWChar, 0x1234) assert repr(w) == "<cdata 'wchar_t' u'\u1234'>" assert str(w) == repr(w) - assert unicode(w) == u'\u1234' + assert string(w) == u'\u1234' assert int(w) == 0x1234 w = cast(BWChar, u'\u8234') assert repr(w) == "<cdata 'wchar_t' u'\u8234'>" assert str(w) == repr(w) - assert unicode(w) == u'\u8234' + assert string(w) == u'\u8234' assert int(w) == 0x8234 w = cast(BInt, u'\u1234') assert repr(w) == "<cdata 'int' 4660>" @@ -1429,7 +1556,7 @@ def test_wchar(): w = cast(BWChar, u'\U00012345') assert repr(w) == "<cdata 'wchar_t' u'\U00012345'>" assert str(w) == repr(w) - assert unicode(w) == u'\U00012345' + assert string(w) == u'\U00012345' assert int(w) == 0x12345 w = cast(BInt, u'\U00012345') assert repr(w) == "<cdata 'int' 74565>" @@ -1439,34 +1566,33 @@ def test_wchar(): # a = newp(BWCharArray, u'hello - world') p = cast(BWCharP, a) - assert unicode(p) == u'hello - world' + assert string(p) == u'hello - world' p[6] = u'\u2345' - assert unicode(p) == u'hello \u2345 world' + 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 unicode(s.a2) == u'hello \u2345 world' + assert string(s.a2) == u'hello \u2345 world' # q = cast(BWCharP, 0) assert str(q) == repr(q) - py.test.raises(RuntimeError, unicode, q) + py.test.raises(RuntimeError, string, q) # def cb(p): assert repr(p).startswith("<cdata 'wchar_t *' 0x") - return len(unicode(p)) + return len(string(p)) BFunc = new_function_type((BWCharP,), BInt, False) f = callback(BFunc, cb, -42) - #assert f(u'a\u1234b') == 3 -- not implemented - py.test.raises(NotImplementedError, f, u'a\u1234b') + assert f(u'a\u1234b') == 3 # if wchar4 and not pyuni4: # try out-of-range wchar_t values x = cast(BWChar, 1114112) - py.test.raises(ValueError, unicode, x) + py.test.raises(ValueError, string, x) x = cast(BWChar, -1) - py.test.raises(ValueError, unicode, x) + py.test.raises(ValueError, string, x) def test_keepalive_struct(): # exception to the no-keepalive rule: p=newp(BStructPtr) returns a @@ -1578,12 +1704,12 @@ def test_buffer(): assert c[2] == '-' assert str(buf) == "hi-there\x00" buf[:2] = 'HI' - assert str(c) == 'HI-there' + assert string(c) == 'HI-there' assert buf[:4:2] == 'H-' if '__pypy__' not in sys.builtin_module_names: # XXX pypy doesn't support the following assignment so far buf[:4:2] = 'XY' - assert str(c) == 'XIYthere' + assert string(c) == 'XIYthere' def test_getcname(): BUChar = new_primitive_type("unsigned char") @@ -1689,3 +1815,89 @@ def test_variable_length_struct(): 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() + 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,), BLongDouble) + f = cast(BFunc19, _testfunc(19)) + start = 8 + for i in range(107): + start = f(start) + if sizeof(BLongDouble) > sizeof(new_primitive_type("double")): + if not py_py: + assert repr(start).startswith("<cdata 'long double' 6.15") + assert repr(start).endswith("E+902>") + # + c = newp(BLongDoubleArray, [start]) + x = c[0] + if not py_py: + assert repr(x).endswith("E+902>") + assert float(x) == float("inf") + +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("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) |
