[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