summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Krauss <thomas.p.krauss@gmail.com>2017-03-12 19:43:18 -0500
committerTom Krauss <thomas.p.krauss@gmail.com>2017-03-12 19:43:18 -0500
commit2438e1714b363623a496853a8be0aade2442e6ee (patch)
tree256067a86ecbaac84792a404b7be8ed9b12c2924
parente85f9b6b4c56770ef71617a81b947176c6cc8378 (diff)
parent7022759e383360ea40f9fda08451c12e83e42b24 (diff)
downloadcffi-2438e1714b363623a496853a8be0aade2442e6ee.tar.gz
Merge default in.
-rw-r--r--c/_cffi_backend.c196
-rw-r--r--c/parse_c_type.c3
-rw-r--r--c/realize_c_type.c2
-rw-r--r--c/test_c.py41
-rw-r--r--cffi/_cffi_include.h3
-rw-r--r--cffi/cffi_opcode.py7
-rw-r--r--cffi/model.py2
-rw-r--r--cffi/parse_c_type.h4
-rw-r--r--testing/cffi1/test_recompiler.py24
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("""