diff options
author | Tom Krauss <thomas.p.krauss@gmail.com> | 2017-03-12 19:43:18 -0500 |
---|---|---|
committer | Tom Krauss <thomas.p.krauss@gmail.com> | 2017-03-12 19:43:18 -0500 |
commit | 2438e1714b363623a496853a8be0aade2442e6ee (patch) | |
tree | 256067a86ecbaac84792a404b7be8ed9b12c2924 | |
parent | e85f9b6b4c56770ef71617a81b947176c6cc8378 (diff) | |
parent | 7022759e383360ea40f9fda08451c12e83e42b24 (diff) | |
download | cffi-2438e1714b363623a496853a8be0aade2442e6ee.tar.gz |
Merge default in.
-rw-r--r-- | c/_cffi_backend.c | 196 | ||||
-rw-r--r-- | c/parse_c_type.c | 3 | ||||
-rw-r--r-- | c/realize_c_type.c | 2 | ||||
-rw-r--r-- | c/test_c.py | 41 | ||||
-rw-r--r-- | cffi/_cffi_include.h | 3 | ||||
-rw-r--r-- | cffi/cffi_opcode.py | 7 | ||||
-rw-r--r-- | cffi/model.py | 2 | ||||
-rw-r--r-- | cffi/parse_c_type.h | 4 | ||||
-rw-r--r-- | testing/cffi1/test_recompiler.py | 24 |
9 files changed, 252 insertions, 30 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 0781a1a..fca8f11 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -62,6 +62,8 @@ #include "malloc_closure.h" +#include <complex.h> + #if PY_MAJOR_VERSION >= 3 # define STR_OR_BYTES "bytes" # define PyText_Type PyUnicode_Type @@ -127,6 +129,8 @@ #define CT_FUNCTIONPTR 256 /* pointer to function */ #define CT_VOID 512 /* void */ +#define CT_PRIMITIVE_COMPLEX 16777216 /* float _Complex, double _Complex */ + /* other flags that may also be set in addition to the base flag: */ #define CT_IS_VOIDCHAR_PTR 1024 #define CT_PRIMITIVE_FITS_LONG 2048 @@ -145,7 +149,8 @@ #define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \ CT_PRIMITIVE_UNSIGNED | \ CT_PRIMITIVE_CHAR | \ - CT_PRIMITIVE_FLOAT) + CT_PRIMITIVE_FLOAT | \ + CT_PRIMITIVE_COMPLEX) typedef struct _ctypedescr { PyObject_VAR_HEAD @@ -883,6 +888,26 @@ read_raw_longdouble_data(char *target) return 0; } +static Py_complex +read_raw_complex_data(char *target, int size) +{ + Py_complex r = {.real=0, .imag=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.real), target, 2*sizeof(double)); + return r; + } + Py_FatalError("read_raw_complex_data: bad float size"); + return r; +} + static void write_raw_float_data(char *target, double source, int size) { @@ -898,6 +923,25 @@ write_raw_longdouble_data(char *target, long double source) _write_raw_data(long double); } +#define _write_raw_complex_data(type) \ + do { \ + if (size == 2*sizeof(type)) { \ + type r = (type)source.real; \ + memcpy(target, &r, sizeof(type)); \ + type i = (type)source.imag; \ + 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 float size"); +} + static PyObject * new_simple_cdata(char *data, CTypeDescrObject *ct) { @@ -1006,6 +1050,10 @@ convert_to_object(char *data, CTypeDescrObject *ct) return (PyObject *)cd; } } + else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { + Py_complex value = read_raw_complex_data(data, ct->ct_size); + return PyComplex_FromCComplex(value); + } else if (ct->ct_flags & CT_PRIMITIVE_CHAR) { /*READ(data, ct->ct_size)*/ if (ct->ct_size == sizeof(char)) @@ -2024,6 +2072,11 @@ static PyObject *cdata_float(CDataObject *cd) } return PyFloat_FromDouble(value); } + if ((cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) && + (cd->c_type->ct_size<16)) { + double value = read_raw_float_data(cd->c_data, cd->c_type->ct_size); + return PyComplex_FromDoubles(value, 0.0); + } PyErr_Format(PyExc_TypeError, "float() not supported on cdata '%s'", cd->c_type->ct_name); return NULL; @@ -2904,6 +2957,22 @@ static PyObject *cdata_dir(PyObject *cd, PyObject *noarg) } } +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); + PyComplexObject *opc = (PyComplexObject *) op; + printf("%f %f\n",opc->cval.real,opc->cval.imag); + return op; + } + PyErr_Format(PyExc_TypeError, "complex() not supported on cdata '%s'", + cd->c_type->ct_name); + return NULL; +} + static PyObject *cdata_iter(CDataObject *); static PyNumberMethods CData_as_number = { @@ -2953,8 +3022,9 @@ static PyMappingMethods CDataOwn_as_mapping = { }; static PyMethodDef cdata_methods[] = { - {"__dir__", cdata_dir, METH_NOARGS}, - {NULL, NULL} /* sentinel */ + {"__dir__", cdata_dir, METH_NOARGS}, + {"__complex__", cdata_complex, METH_NOARGS}, + {NULL, NULL} /* sentinel */ }; static PyTypeObject CData_Type = { @@ -3587,6 +3657,36 @@ static CDataObject *cast_to_integer_or_char(CTypeDescrObject *ct, PyObject *ob) return cd; } +static void check_bytes_for_float_compatible( + PyObject *io, + double * value, + int * got_value_indicator, + int * cannot_cast_indicator) +{ + *got_value_indicator = 0; + *cannot_cast_indicator = 0; + if (PyBytes_Check(io)) { + if (PyBytes_GET_SIZE(io) != 1) { + Py_DECREF(io); + *cannot_cast_indicator = 1; + } + *value = (unsigned char)PyBytes_AS_STRING(io)[0]; + *got_value_indicator = 1; + } +#if HAVE_WCHAR_H + else if (PyUnicode_Check(io)) { + wchar_t ordinal; + if (_my_PyUnicode_AsSingleWideChar(io, &ordinal) < 0) { + Py_DECREF(io); + *cannot_cast_indicator = 1; + } + *value = (long)ordinal; + *got_value_indicator = 1; + } +#endif + +} + static PyObject *do_cast(CTypeDescrObject *ct, PyObject *ob) { CDataObject *cd; @@ -3643,23 +3743,16 @@ static PyObject *do_cast(CTypeDescrObject *ct, PyObject *ob) Py_INCREF(io); } - if (PyBytes_Check(io)) { - if (PyBytes_GET_SIZE(io) != 1) { - Py_DECREF(io); - goto cannot_cast; - } - value = (unsigned char)PyBytes_AS_STRING(io)[0]; + int got_value_indicator; + int cannot_cast_indicator; + check_bytes_for_float_compatible(io, &value, + &got_value_indicator, &cannot_cast_indicator); + if (cannot_cast_indicator) { + goto cannot_cast; } -#if HAVE_WCHAR_H - else if (PyUnicode_Check(io)) { - wchar_t ordinal; - if (_my_PyUnicode_AsSingleWideChar(io, &ordinal) < 0) { - Py_DECREF(io); - goto cannot_cast; - } - value = (long)ordinal; + if (got_value_indicator) { + // got it from string } -#endif else if ((ct->ct_flags & CT_IS_LONGDOUBLE) && CData_Check(io) && (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { @@ -3689,6 +3782,51 @@ static PyObject *do_cast(CTypeDescrObject *ct, PyObject *ob) } return (PyObject *)cd; } + + + else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { + /* cast to a complex */ + Py_complex value; + PyObject *io; + if (CData_Check(ob)) { + CDataObject *cdsrc = (CDataObject *)ob; + + if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY)) + goto cannot_cast; + io = convert_to_object(cdsrc->c_data, cdsrc->c_type); + if (io == NULL) + return NULL; + } + else { + io = ob; + Py_INCREF(io); + } + + int got_value_indicator; + int cannot_cast_indicator; + check_bytes_for_float_compatible(io, &(value.real), + &got_value_indicator, &cannot_cast_indicator); + if (cannot_cast_indicator) { + goto cannot_cast; + } + if (got_value_indicator) { + // got it from string + value.imag = 0.0; + } else { + value = PyComplex_AsCComplex(io); + } + Py_DECREF(io); + if (value.real == -1.0 && value.imag == 0.0 && 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); @@ -3971,6 +4109,8 @@ static PyObject *new_primitive_type(const char *name) EPTYPE(f, float, CT_PRIMITIVE_FLOAT ) \ EPTYPE(d, double, CT_PRIMITIVE_FLOAT ) \ EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE ) \ + EPTYPE(fc, float _Complex, CT_PRIMITIVE_COMPLEX ) \ + EPTYPE(dc, double _Complex, CT_PRIMITIVE_COMPLEX ) \ ENUM_PRIMITIVE_TYPES_WCHAR \ EPTYPE(b, _Bool, CT_PRIMITIVE_UNSIGNED | CT_IS_BOOL ) \ /* the following types are not primitive in the C sense */ \ @@ -4081,6 +4221,16 @@ static PyObject *new_primitive_type(const char *name) else goto bad_ffi_type; } + else if (ptypes->flags & CT_PRIMITIVE_COMPLEX) { + // as of March 2017, still no libffi support for complex + // but it fails silently. + if (strcmp(ptypes->name, "float _Complex") == 0) + ffitype = &ffi_type_complex_float; + else if (strcmp(ptypes->name, "double _Complex") == 0) + ffitype = &ffi_type_complex_double; + else + goto bad_ffi_type; + } else { switch (ptypes->size) { case 1: ffitype = &ffi_type_uint8; break; @@ -6578,6 +6728,14 @@ static int _testfunc23(char *p) return 1000 * p[0]; return -42; } +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; +} static PyObject *b__testfunc(PyObject *self, PyObject *args) { @@ -6611,6 +6769,8 @@ static PyObject *b__testfunc(PyObject *self, PyObject *args) case 21: f = &_testfunc21; break; case 22: f = &_testfunc22; break; case 23: f = &_testfunc23; break; + case 24: f = &_testfunc24; break; + case 25: f = &_testfunc25; break; default: PyErr_SetNone(PyExc_ValueError); return NULL; diff --git a/c/parse_c_type.c b/c/parse_c_type.c index d0c8a3d..15c6b0c 100644 --- a/c/parse_c_type.c +++ b/c/parse_c_type.c @@ -25,7 +25,7 @@ enum token_e { /* keywords */ TOK__BOOL, TOK_CHAR, - //TOK__COMPLEX, + TOK__COMPLEX, TOK_CONST, TOK_DOUBLE, TOK_ENUM, @@ -797,6 +797,7 @@ static int parse_c_type_from(struct _cffi_parse_info_s *info, size_t *output_index, const char *input) { + printf("parse_c_type_from\n"); int result; token_t token; diff --git a/c/realize_c_type.c b/c/realize_c_type.c index b4ad4f5..52bc811 100644 --- a/c/realize_c_type.c +++ b/c/realize_c_type.c @@ -151,6 +151,8 @@ static PyObject *build_primitive_type(int num) "uint_fast64_t", "intmax_t", "uintmax_t", + "float _Complex", + "double _Complex", }; PyObject *x; diff --git a/c/test_c.py b/c/test_c.py index de95712..8ca5f8f 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -185,10 +185,9 @@ def test_float_types(): 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) + p = new_primitive_type(name + " _Complex") assert bool(cast(p, 0)) assert bool(cast(p, INF)) assert bool(cast(p, -INF)) @@ -200,22 +199,23 @@ def test_complex_types(): 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 + 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, 3+1E200j)) == 3+INF*1j # 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' - assert complex(cast(p, '\x09')) == 9.0 + #assert repr(complex(cast(p, -0j))) == '-0j' # http://bugs.python.org/issue29602 + assert complex(cast(p, b'\x09')) == 9.0 + assert complex(cast(p, u+'\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) + py.test.raises(TypeError, cast, new_primitive_type(name), 1+2j) + py.test.raises(TypeError, cast, new_primitive_type("int"), 1+2j) def test_character_type(): p = new_primitive_type("char") @@ -1116,6 +1116,29 @@ def test_call_function_9(): BSShort = new_primitive_type("short") assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192 +def test_call_function_24(): + py.test.skip("libffi returning nonsense silently") + BFloat = new_primitive_type("float") + BFloatComplex = new_primitive_type("float _Complex") + BFunc3 = new_function_type((BFloat, BFloat), BFloatComplex, False) + 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 + +def test_call_function_25(): + py.test.skip("libffi returning nonsense silently") + BDouble = new_primitive_type("double") + BDoubleComplex = new_primitive_type("double _Complex") + BFunc3 = new_function_type((BDouble, BDouble), BDoubleComplex, False) + 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 + + def test_cannot_call_with_a_autocompleted_struct(): BSChar = new_primitive_type("signed char") BDouble = new_primitive_type("double") diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h index a0e0990..fdfd21a 100644 --- a/cffi/_cffi_include.h +++ b/cffi/_cffi_include.h @@ -110,6 +110,9 @@ extern "C" { PyInt_FromLong((long)x) : \ PyLong_FromLongLong((long long)x))) +#define _cffi_from_c_float__Complex(x) PyComplex_FromDoubles(crealf(x), cimagf(x)) +#define _cffi_from_c_double__Complex(x) PyComplex_FromDoubles(creal(x), cimag(x)) + #define _cffi_to_c_int(o, type) \ ((type)( \ sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ diff --git a/cffi/cffi_opcode.py b/cffi/cffi_opcode.py index 0cf76c9..8540988 100644 --- a/cffi/cffi_opcode.py +++ b/cffi/cffi_opcode.py @@ -105,8 +105,11 @@ PRIM_INT_FAST64 = 44 PRIM_UINT_FAST64 = 45 PRIM_INTMAX = 46 PRIM_UINTMAX = 47 +PRIM_FLOATCOMPLEX = 48 +PRIM_DOUBLECOMPLEX = 49 -_NUM_PRIM = 48 + +_NUM_PRIM = 50 _UNKNOWN_PRIM = -1 _UNKNOWN_FLOAT_PRIM = -2 _UNKNOWN_LONG_DOUBLE = -3 @@ -128,6 +131,8 @@ PRIMITIVE_TO_INDEX = { 'float': PRIM_FLOAT, 'double': PRIM_DOUBLE, 'long double': PRIM_LONGDOUBLE, + 'float _Complex': PRIM_FLOATCOMPLEX, + 'double _Complex': PRIM_DOUBLECOMPLEX, '_Bool': PRIM_BOOL, 'wchar_t': PRIM_WCHAR, 'int8_t': PRIM_INT8, diff --git a/cffi/model.py b/cffi/model.py index 41bab0a..ab67b31 100644 --- a/cffi/model.py +++ b/cffi/model.py @@ -116,6 +116,8 @@ class PrimitiveType(BasePrimitiveType): 'float': 'f', 'double': 'f', 'long double': 'f', + 'float _Complex': 'f', + 'double _Complex': 'f', '_Bool': 'i', # the following types are not primitive in the C sense 'wchar_t': 'c', diff --git a/cffi/parse_c_type.h b/cffi/parse_c_type.h index a01d89e..12f2e4e 100644 --- a/cffi/parse_c_type.h +++ b/cffi/parse_c_type.h @@ -79,8 +79,10 @@ typedef void *_cffi_opcode_t; #define _CFFI_PRIM_UINT_FAST64 45 #define _CFFI_PRIM_INTMAX 46 #define _CFFI_PRIM_UINTMAX 47 +#define _CFFI_PRIM_FLOATCOMPLEX 48 +#define _CFFI_PRIM_DOUBLECOMPLEX 49 -#define _CFFI__NUM_PRIM 48 +#define _CFFI__NUM_PRIM 50 #define _CFFI__UNKNOWN_PRIM (-1) #define _CFFI__UNKNOWN_FLOAT_PRIM (-2) #define _CFFI__UNKNOWN_LONG_DOUBLE (-3) diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index e48820b..8b429fa 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -2000,6 +2000,30 @@ def test_function_returns_partial_struct(): """) assert lib.f1(52).a == 52 +def test_function_returns_float_complex(): + ffi = FFI() + ffi.cdef("float _Complex f1(float a, float b);"); + lib = verify(ffi, "test_function_returns_float_complex", """ + #include <complex.h> + static float _Complex f1 (float a, float b) { return a + I*2.0*b; } + """) + result = lib.f1(1.25, 5.1) + assert type(result) == complex + assert result.real == 1.25 # exact + assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact + +def test_function_returns_double_complex(): + ffi = FFI() + ffi.cdef("double _Complex f1(double a, double b);"); + lib = verify(ffi, "test_function_returns_double_complex", """ + #include <complex.h> + static double _Complex f1 (double a, double b) { return a + I*2.0*b; } + """) + result = lib.f1(1.25, 5.1) + assert type(result) == complex + assert result.real == 1.25 # exact + assert result.imag == 2*5.1 # exact + def test_typedef_array_dotdotdot(): ffi = FFI() ffi.cdef(""" |