[pypy-commit] creflect default: copy the logic for ffi.cast()

arigo noreply at buildbot.pypy.org
Fri Dec 5 21:08:45 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r180:9eb2b89d1adc
Date: 2014-12-05 21:09 +0100
http://bitbucket.org/cffi/creflect/changeset/9eb2b89d1adc/

Log:	copy the logic for ffi.cast()

diff --git a/zeffir/ffi_obj.c b/zeffir/ffi_obj.c
--- a/zeffir/ffi_obj.c
+++ b/zeffir/ffi_obj.c
@@ -255,6 +255,208 @@
     return (PyObject *)cd;
 }
 
+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 *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_ARRAY)) {
+        value = (Py_intptr_t)((CDataObject *)ob)->c_data;
+    }
+    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];
+    }
+    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;
+}
+
+static PyObject *ffi_cast(ZefFFIObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    CDataObject *cd;
+    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;
+
+    if (ct->ct_flags & (CT_POINTER | CT_ARRAY) &&
+        ct->ct_size >= 0) {
+        /* cast to a pointer 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_ARRAY)) {
+                return new_simple_cdata(cdsrc->c_data, 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;
+
+        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);
+        }
+
+        if (PyBytes_Check(io)) {
+            if (PyBytes_GET_SIZE(io) != 1) {
+                Py_DECREF(io);
+                goto cannot_cast;
+            }
+            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;
+            char *data = ((CDataObject *)io)->c_data;
+            /*READ(data, sizeof(long double)*/
+            lvalue = read_raw_longdouble_data(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);
+        }
+        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 {
+        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 *ffi_string(ZefFFIObject *self, PyObject *args)
 {
     CDataObject *cd;
@@ -443,6 +645,7 @@
 
 static PyMethodDef ffi_methods[] = {
     {"addressof",     (PyCFunction)ffi_addressof,METH_VARARGS},
+    {"cast",          (PyCFunction)ffi_cast,     METH_VARARGS},
     {"close_library", ffi_close_library,         METH_VARARGS | METH_STATIC},
     {"load_library",  (PyCFunction)ffi_load_library,
                                                  METH_VARARGS | METH_KEYWORDS},
diff --git a/zeffir/test/test_basic.py b/zeffir/test/test_basic.py
--- a/zeffir/test/test_basic.py
+++ b/zeffir/test/test_basic.py
@@ -48,3 +48,9 @@
 def test_addressof_missing_glob():
     ffi, lib = support.compile_and_open('basic')
     py.test.raises(ffi.error, ffi.addressof, lib, "some_missing_name")
+
+def test_cast():
+    ffi = support.new_ffi()
+    p = ffi.new("char[]", "----abcd")
+    q = ffi.cast("int *", p)
+    assert q[1] in [0x61626364, 0x64636261]


More information about the pypy-commit mailing list