Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r943:fcbef63e13e5 Date: 2012-09-18 19:37 +0200 http://bitbucket.org/cffi/cffi/changeset/fcbef63e13e5/
Log: ffi.addressof(struct, field). diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -4020,28 +4020,73 @@ return PyInt_FromSsize_t(cf->cf_offset); } -static PyObject *b_addressof(PyObject *self, PyObject *args) +static PyObject *b_typeoffsetof(PyObject *self, PyObject *args) +{ + PyObject *res, *fieldname; + CTypeDescrObject *ct; + CFieldObject *cf; + Py_ssize_t offset; + + if (!PyArg_ParseTuple(args, "O!O:typeof", + &CTypeDescr_Type, &ct, &fieldname)) + return NULL; + + if (fieldname == Py_None) { + if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) + goto typeerror; + res = (PyObject *)ct; + offset = 0; + } + else { + if (ct->ct_flags & CT_POINTER) + ct = ct->ct_itemdescr; + if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) + goto typeerror; + if (ct->ct_flags & CT_IS_OPAQUE) + cf = NULL; + else + cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname); + if (cf == NULL) { + PyErr_SetObject(PyExc_KeyError, fieldname); + return NULL; + } + if (cf->cf_bitshift >= 0) { + PyErr_SetString(PyExc_TypeError, "not supported for bitfields"); + return NULL; + } + res = (PyObject *)cf->cf_type; + offset = cf->cf_offset; + } + return Py_BuildValue("(On)", res, offset); + + typeerror: + PyErr_SetString(PyExc_TypeError, "expected a struct or union ctype"); + return NULL; +} + +static PyObject *b_rawaddressof(PyObject *self, PyObject *args) { CTypeDescrObject *ct; CDataObject *cd; - - if (!PyArg_ParseTuple(args, "O!O!:addressof", + Py_ssize_t offset = 0; + + if (!PyArg_ParseTuple(args, "O!O!|n:rawaddressof", &CTypeDescr_Type, &ct, - &CData_Type, &cd)) + &CData_Type, &cd, + &offset)) return NULL; - if ((cd->c_type->ct_flags & (CT_STRUCT|CT_UNION)) == 0) { + if ((cd->c_type->ct_flags & (CT_STRUCT|CT_UNION|CT_IS_PTR_TO_OWNED)) == 0) { PyErr_SetString(PyExc_TypeError, "expected a 'cdata struct-or-union' object"); return NULL; } - if ((ct->ct_flags & CT_POINTER) == 0 || - (ct->ct_itemdescr != cd->c_type)) { + if ((ct->ct_flags & CT_POINTER) == 0) { PyErr_SetString(PyExc_TypeError, - "arg 1 must be the type of pointers to arg 2"); + "expected a pointer ctype"); return NULL; } - return new_simple_cdata(cd->c_data, ct); + return new_simple_cdata(cd->c_data + offset, ct); } static PyObject *b_getcname(PyObject *self, PyObject *args) @@ -4428,7 +4473,8 @@ {"sizeof", b_sizeof, METH_O}, {"typeof", b_typeof, METH_O}, {"offsetof", b_offsetof, METH_VARARGS}, - {"addressof", b_addressof, METH_VARARGS}, + {"typeoffsetof", b_typeoffsetof, METH_VARARGS}, + {"rawaddressof", b_rawaddressof, METH_VARARGS}, {"getcname", b_getcname, METH_VARARGS}, {"string", b_string, METH_VARARGS}, {"buffer", b_buffer, METH_VARARGS}, diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -2117,21 +2117,48 @@ assert int(cast(BBool, cast(BDouble, 0.1))) == 1 assert int(cast(BBool, cast(BDouble, 0.0))) == 0 -def test_addressof(): +def test_typeoffsetof(): BChar = new_primitive_type("char") BStruct = new_struct_type("foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BChar, -1), ('a3', BChar, -1)]) + py.test.raises(TypeError, typeoffsetof, BStructPtr, None) + assert typeoffsetof(BStruct, None) == (BStruct, 0) + assert typeoffsetof(BStructPtr, 'a1') == (BChar, 0) + assert typeoffsetof(BStruct, 'a1') == (BChar, 0) + assert typeoffsetof(BStructPtr, 'a2') == (BChar, 1) + assert typeoffsetof(BStruct, 'a3') == (BChar, 2) + py.test.raises(KeyError, typeoffsetof, BStructPtr, 'a4') + py.test.raises(KeyError, typeoffsetof, BStruct, 'a5') + +def test_typeoffsetof_no_bitfield(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("foo") + complete_struct_or_union(BStruct, [('a1', BInt, 4)]) + py.test.raises(TypeError, typeoffsetof, BStruct, 'a1') + +def test_rawaddressof(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BStruct = new_struct_type("foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BChar, -1), + ('a3', BChar, -1)]) p = newp(BStructPtr) assert repr(p) == "<cdata 'struct foo *' owning 3 bytes>" s = p[0] assert repr(s) == "<cdata 'struct foo' owning 3 bytes>" - a = addressof(BStructPtr, s) + a = rawaddressof(BStructPtr, s) assert repr(a).startswith("<cdata 'struct foo *' 0x") - py.test.raises(TypeError, addressof, BStruct, s) - py.test.raises(TypeError, addressof, new_pointer_type(BChar), s) - py.test.raises(TypeError, addressof, BStructPtr, a) - py.test.raises(TypeError, addressof, new_pointer_type(BStructPtr), a) - py.test.raises(TypeError, addressof, BStructPtr, cast(BChar, '?')) + py.test.raises(TypeError, rawaddressof, BStruct, s) + b = rawaddressof(BCharP, s) + assert b == cast(BCharP, p) + c = rawaddressof(BStructPtr, a) + assert c == a + py.test.raises(TypeError, rawaddressof, BStructPtr, cast(BChar, '?')) + # + d = rawaddressof(BCharP, s, 1) + assert d == cast(BCharP, p) + 1 diff --git a/cffi/api.py b/cffi/api.py --- a/cffi/api.py +++ b/cffi/api.py @@ -287,15 +287,18 @@ errno = property(_get_errno, _set_errno, None, "the value of 'errno' from/to the C calls") - def addressof(self, cdata): - """Return the address of a <cdata 'struct-or-union'>.""" + def addressof(self, cdata, field=None): + """Return the address of a <cdata 'struct-or-union'>. + If 'field' is specified, return the address of this field. + """ ctype = self._backend.typeof(cdata) + ctype, offset = self._backend.typeoffsetof(ctype, field) try: ctypeptr = self._pointer_type_cache[ctype] except KeyError: ctypeptr = self._pointer_type_cache[ctype] = ( self._typeof(self.getctype(ctype, '*'))) - return self._backend.addressof(ctypeptr, cdata) + return self._backend.rawaddressof(ctypeptr, cdata, offset) def _make_ffi_library(ffi, libname): diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py --- a/cffi/backend_ctypes.py +++ b/cffi/backend_ctypes.py @@ -718,13 +718,17 @@ btypes = [BField for (fname, BField, bitsize) in fields] bitfields = [bitsize for (fname, BField, bitsize) in fields] # + bfield_types = {} cfields = [] for (fname, BField, bitsize) in fields: if bitsize < 0: cfields.append((fname, BField._ctype)) + bfield_types[fname] = BField else: cfields.append((fname, BField._ctype, bitsize)) + bfield_types[fname] = Ellipsis struct_or_union._fields_ = cfields + CTypesStructOrUnion._bfield_types = bfield_types # @staticmethod def _create_ctype_obj(init): @@ -1044,10 +1048,31 @@ def getcname(self, BType, replace_with): return BType._get_c_name(replace_with) - def addressof(self, BTypePtr, cdata): - if not isinstance(cdata, CTypesBaseStructOrUnion): + def typeoffsetof(self, BType, fieldname): + if fieldname is not None and issubclass(BType, CTypesGenericPtr): + BType = BType._BItem + if not issubclass(BType, CTypesBaseStructOrUnion): + raise TypeError("expected a struct or union ctype") + if fieldname is None: + return (BType, 0) + else: + BField = BType._bfield_types[fieldname] + if BField is Ellipsis: + raise TypeError("not supported for bitfields") + return (BField, BType._offsetof(fieldname)) + + def rawaddressof(self, BTypePtr, cdata, offset): + if isinstance(cdata, CTypesBaseStructOrUnion): + ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) + elif isinstance(cdata, CTypesGenericPtr): + ptr = type(cdata)._to_ctypes(cdata) + else: raise TypeError("expected a <cdata 'struct-or-union'>") - ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) + if offset != 0: + ptr = ctypes.cast( + ctypes.c_void_p( + ctypes.cast(ptr, ctypes.c_void_p).value + offset), + type(ptr)) return BTypePtr._from_ctypes(ptr) diff --git a/doc/source/index.rst b/doc/source/index.rst --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1034,12 +1034,14 @@ .. "versionadded:: 0.3" --- inlined in the previous paragraph -``ffi.addressof(cdata)``: from a cdata whose type is ``struct foo_s``, -return its "address", as a cdata whose type is ``struct foo_s *``. Also -works on unions, but not on any other type. (It would be difficult -because only structs and unions are internally stored as an indirect -pointer to the data.) The returned pointer is only valid as long as -the original object is. *New in version 0.4.* +``ffi.addressof(cdata, field=None)``: from a cdata whose type is +``struct foo_s``, return its "address", as a cdata whose type is +``struct foo_s *``. Also works on unions, but not on any other type. +(It would be difficult because only structs and unions are internally +stored as an indirect pointer to the data.) If ``field`` is given, +returns the address of that field in the structure. The returned +pointer is only valid as long as the original object is. *New in +version 0.4.* .. "versionadded:: 0.4" --- inlined in the previous paragraph diff --git a/testing/backend_tests.py b/testing/backend_tests.py --- a/testing/backend_tests.py +++ b/testing/backend_tests.py @@ -1456,3 +1456,14 @@ assert repr(a).startswith("<cdata 'struct foo_s *' 0x") py.test.raises(TypeError, ffi.addressof, p) py.test.raises((AttributeError, TypeError), ffi.addressof, 5) + + def test_addressof_field(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef("struct foo_s { int x, y; };") + p = ffi.new("struct foo_s *") + a = ffi.addressof(p[0], 'y') + assert repr(a).startswith("<cdata 'int *' 0x") + assert int(ffi.cast("uintptr_t", a)) == ( + int(ffi.cast("uintptr_t", p)) + ffi.sizeof("int")) + assert a == ffi.addressof(p, 'y') + assert a != ffi.addressof(p, 'x') _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit