[pypy-commit] cffi default: ffi.addressof(struct, field).
arigo
noreply at buildbot.pypy.org
Tue Sep 18 19:37:22 CEST 2012
Author: Armin Rigo <arigo at 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')
More information about the pypy-commit
mailing list