Author: Armin Rigo <[email protected]>
Branch:
Changeset: r1631:6acfa48521dd
Date: 2015-01-11 22:23 +0100
http://bitbucket.org/cffi/cffi/changeset/6acfa48521dd/
Log: Change again ffi.offsetof() and ffi.addressof(), generalizing them.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -4836,27 +4836,19 @@
CTypeDescrObject *ct;
CFieldObject *cf;
Py_ssize_t offset;
-
- if (!PyArg_ParseTuple(args, "O!O:typeoffsetof",
- &CTypeDescr_Type, &ct, &fieldname))
+ int following = 0;
+
+ if (!PyArg_ParseTuple(args, "O!O|i:typeoffsetof",
+ &CTypeDescr_Type, &ct, &fieldname, &following))
return NULL;
- if (fieldname == Py_None) {
- if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) {
- PyErr_SetString(PyExc_TypeError,
- "expected a struct or union ctype");
- return NULL;
- }
- res = (PyObject *)ct;
- offset = 0;
- }
- else {
- if (ct->ct_flags & CT_POINTER)
+ if (PyTextAny_Check(fieldname)) {
+ if (!following && (ct->ct_flags & CT_POINTER))
ct = ct->ct_itemdescr;
if (!(ct->ct_flags & (CT_STRUCT|CT_UNION)) || ct->ct_stuff == NULL) {
PyErr_SetString(PyExc_TypeError,
- "expected an initialized struct or union ctype, "
- "or a pointer to one");
+ "with a field name argument, expected an "
+ "initialized struct or union ctype");
return NULL;
}
cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname);
@@ -4871,6 +4863,29 @@
res = (PyObject *)cf->cf_type;
offset = cf->cf_offset;
}
+ else {
+ ssize_t index = PyInt_AsSsize_t(fieldname);
+ if (index < 0 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError,
+ "field name or array index expected");
+ return NULL;
+ }
+
+ if (!(ct->ct_flags & (CT_ARRAY|CT_POINTER)) ||
+ ct->ct_itemdescr->ct_size < 0) {
+ PyErr_SetString(PyExc_TypeError, "with an integer argument, "
+ "expected an array ctype or a "
+ "pointer to non-opaque");
+ return NULL;
+ }
+ res = (PyObject *)ct->ct_itemdescr;
+ offset = index * ct->ct_itemdescr->ct_size;
+ if ((offset / ct->ct_itemdescr->ct_size) != index) {
+ PyErr_SetString(PyExc_OverflowError,
+ "array offset would overflow a Py_ssize_t");
+ return NULL;
+ }
+ }
return Py_BuildValue("(On)", res, offset);
}
@@ -4878,15 +4893,17 @@
{
CTypeDescrObject *ct;
CDataObject *cd;
- Py_ssize_t offset = 0;
-
- if (!PyArg_ParseTuple(args, "O!O!|n:rawaddressof",
+ Py_ssize_t offset;
+ int accepted_flags;
+
+ if (!PyArg_ParseTuple(args, "O!O!n:rawaddressof",
&CTypeDescr_Type, &ct,
&CData_Type, &cd,
&offset))
return NULL;
- if ((cd->c_type->ct_flags & (CT_STRUCT|CT_UNION|CT_IS_PTR_TO_OWNED)) == 0)
{
+ accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER;
+ if ((cd->c_type->ct_flags & accepted_flags) == 0) {
PyErr_SetString(PyExc_TypeError,
"expected a 'cdata struct-or-union' object");
return NULL;
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -2538,13 +2538,31 @@
('a2', BChar, -1),
('a3', BChar, -1)])
py.test.raises(TypeError, typeoffsetof, BStructPtr, None)
- assert typeoffsetof(BStruct, None) == (BStruct, 0)
+ py.test.raises(TypeError, typeoffsetof, BStruct, None)
assert typeoffsetof(BStructPtr, 'a1') == (BChar, 0)
assert typeoffsetof(BStruct, 'a1') == (BChar, 0)
assert typeoffsetof(BStructPtr, 'a2') == (BChar, 1)
assert typeoffsetof(BStruct, 'a3') == (BChar, 2)
+ assert typeoffsetof(BStructPtr, 'a2', 0) == (BChar, 1)
+ py.test.raises(TypeError, typeoffsetof, BStructPtr, 'a2', 1)
py.test.raises(KeyError, typeoffsetof, BStructPtr, 'a4')
py.test.raises(KeyError, typeoffsetof, BStruct, 'a5')
+ py.test.raises(TypeError, typeoffsetof, BStruct, 42)
+ py.test.raises(TypeError, typeoffsetof, BChar, 'a1')
+
+def test_typeoffsetof_array():
+ BInt = new_primitive_type("int")
+ BIntP = new_pointer_type(BInt)
+ BArray = new_array_type(BIntP, None)
+ py.test.raises(TypeError, typeoffsetof, BArray, None)
+ py.test.raises(TypeError, typeoffsetof, BArray, 'a1')
+ assert typeoffsetof(BArray, 51) == (BInt, 51 * size_of_int())
+ assert typeoffsetof(BIntP, 51) == (BInt, 51 * size_of_int())
+ assert typeoffsetof(BArray, -51) == (BInt, -51 * size_of_int())
+ MAX = sys.maxsize // size_of_int()
+ assert typeoffsetof(BArray, MAX) == (BInt, MAX * size_of_int())
+ assert typeoffsetof(BIntP, MAX) == (BInt, MAX * size_of_int())
+ py.test.raises(OverflowError, typeoffsetof, BArray, MAX + 1)
def test_typeoffsetof_no_bitfield():
BInt = new_primitive_type("int")
@@ -2564,14 +2582,14 @@
assert repr(p) == "<cdata 'struct foo *' owning 3 bytes>"
s = p[0]
assert repr(s) == "<cdata 'struct foo' owning 3 bytes>"
- a = rawaddressof(BStructPtr, s)
+ a = rawaddressof(BStructPtr, s, 0)
assert repr(a).startswith("<cdata 'struct foo *' 0x")
- py.test.raises(TypeError, rawaddressof, BStruct, s)
- b = rawaddressof(BCharP, s)
+ py.test.raises(TypeError, rawaddressof, BStruct, s, 0)
+ b = rawaddressof(BCharP, s, 0)
assert b == cast(BCharP, p)
- c = rawaddressof(BStructPtr, a)
+ c = rawaddressof(BStructPtr, a, 0)
assert c == a
- py.test.raises(TypeError, rawaddressof, BStructPtr, cast(BChar, '?'))
+ py.test.raises(TypeError, rawaddressof, BStructPtr, cast(BChar, '?'), 0)
#
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
@@ -191,14 +191,16 @@
cdecl = self._typeof(cdecl)
return self._backend.alignof(cdecl)
- def offsetof(self, cdecl, fieldname):
+ def offsetof(self, cdecl, *fields_or_indexes):
"""Return the offset of the named field inside the given
- structure, which must be given as a C type name. The field
- may be 'x.y.z' in case of nested structures.
+ structure or array, which must be given as a C type name.
+ You can give several field names in case of nested structures.
+ You can also give numeric values which correspond to array
+ items, in case of an array type.
"""
if isinstance(cdecl, basestring):
cdecl = self._typeof(cdecl)
- return self._typeoffsetof(cdecl, fieldname)[1]
+ return self._typeoffsetof(cdecl, *fields_or_indexes)[1]
def new(self, cdecl, init=None):
"""Allocate an instance according to the specified C type and
@@ -383,24 +385,28 @@
with self._lock:
return model.pointer_cache(self, ctype)
- def addressof(self, cdata, field=None):
+ def addressof(self, cdata, *fields_or_indexes):
"""Return the address of a <cdata 'struct-or-union'>.
- If 'field' is specified, return the address of this field.
- The field may be 'x.y.z' in case of nested structures.
+ If 'fields_or_indexes' are given, returns the address of that
+ field or array item in the structure or array, recursively in
+ case of nested structures.
"""
ctype = self._backend.typeof(cdata)
- ctype, offset = self._typeoffsetof(ctype, field)
+ if fields_or_indexes:
+ ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes)
+ else:
+ if ctype.kind == "pointer":
+ raise TypeError("addressof(pointer)")
+ offset = 0
ctypeptr = self._pointer_to(ctype)
return self._backend.rawaddressof(ctypeptr, cdata, offset)
- def _typeoffsetof(self, ctype, field):
- if field is not None and '.' in field:
- offset = 0
- for field1 in field.split('.'):
- ctype, offset1 = self._backend.typeoffsetof(ctype, field1)
- offset += offset1
- return ctype, offset
- return self._backend.typeoffsetof(ctype, field)
+ def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes):
+ ctype, offset = self._backend.typeoffsetof(ctype, field_or_index)
+ for field1 in fields_or_indexes:
+ ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1)
+ offset += offset1
+ return ctype, offset
def include(self, ffi_to_include):
"""Includes the typedefs, structs, unions and enums defined
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -169,6 +169,7 @@
class CTypesGenericPtr(CTypesData):
__slots__ = ['_address', '_as_ctype_ptr']
_automatic_casts = False
+ kind = "pointer"
@classmethod
def _newp(cls, init):
@@ -370,10 +371,12 @@
(CTypesPrimitive, type(source).__name__))
return source
#
+ kind1 = kind
class CTypesPrimitive(CTypesGenericPrimitive):
__slots__ = ['_value']
_ctype = ctype
_reftypename = '%s &' % name
+ kind = kind1
def __init__(self, value):
self._value = value
@@ -703,12 +706,13 @@
class struct_or_union(base_ctypes_class):
pass
struct_or_union.__name__ = '%s_%s' % (kind, name)
+ kind1 = kind
#
class CTypesStructOrUnion(CTypesBaseStructOrUnion):
__slots__ = ['_blob']
_ctype = struct_or_union
_reftypename = '%s &' % (name,)
- _kind = kind
+ _kind = kind = kind1
#
CTypesStructOrUnion._fix_class()
return CTypesStructOrUnion
@@ -994,27 +998,42 @@
def getcname(self, BType, replace_with):
return BType._get_c_name(replace_with)
- 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:
+ def typeoffsetof(self, BType, fieldname, num=0):
+ if isinstance(fieldname, str):
+ if num == 0 and issubclass(BType, CTypesGenericPtr):
+ BType = BType._BItem
+ if not issubclass(BType, CTypesBaseStructOrUnion):
+ raise TypeError("expected a struct or union ctype")
BField = BType._bfield_types[fieldname]
if BField is Ellipsis:
raise TypeError("not supported for bitfields")
return (BField, BType._offsetof(fieldname))
+ elif isinstance(fieldname, (int, long)):
+ if issubclass(BType, CTypesGenericArray):
+ BType = BType._CTPtr
+ if not issubclass(BType, CTypesGenericPtr):
+ raise TypeError("expected an array or ptr ctype")
+ BItem = BType._BItem
+ offset = BItem._get_size() * fieldname
+ if offset > sys.maxsize:
+ raise OverflowError
+ return (BItem, offset)
+ else:
+ raise TypeError(type(fieldname))
- def rawaddressof(self, BTypePtr, cdata, offset):
+ def rawaddressof(self, BTypePtr, cdata, offset=None):
if isinstance(cdata, CTypesBaseStructOrUnion):
ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata))
elif isinstance(cdata, CTypesGenericPtr):
+ if offset is None or not issubclass(type(cdata)._BItem,
+ CTypesBaseStructOrUnion):
+ raise TypeError("unexpected cdata type")
+ ptr = type(cdata)._to_ctypes(cdata)
+ elif isinstance(cdata, CTypesGenericArray):
ptr = type(cdata)._to_ctypes(cdata)
else:
raise TypeError("expected a <cdata 'struct-or-union'>")
- if offset != 0:
+ if offset:
ptr = ctypes.cast(
ctypes.c_void_p(
ctypes.cast(ptr, ctypes.c_void_p).value + offset),
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1301,11 +1301,15 @@
**ffi.alignof("C type")**: return the alignment of the C type.
Corresponds to the ``__alignof__`` operator in GCC.
-**ffi.offsetof("C struct type", "fieldname")**: return the offset within
-the struct of the given field. Corresponds to ``offsetof()`` in C.
+**ffi.offsetof("C struct or array type", *fields_or_indexes)**: return the
+offset within the struct of the given field. Corresponds to ``offsetof()``
+in C.
.. versionchanged:: 0.9
- ``"fieldname"`` can be ``"x.y"`` in case of nested structures.
+ You can give several field names in case of nested structures. You
+ can also give numeric values which correspond to array items, in case
+ of a pointer or array type. For example, ``ffi.offsetof("int[5]", 2)``
+ is equal to the size of two integers, as is ``ffi.offsetof("int *", 2)``.
**ffi.getctype("C type" or <ctype>, extra="")**: return the string
representation of the given C type. If non-empty, the "extra" string is
@@ -1358,21 +1362,26 @@
.. "versionadded:: 0.7" --- inlined in the previous paragraph
-**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 you need a C int whose
-address can be taken, use ``ffi.new("int[1]")`` in the first place;
-similarly, if it's a C pointer, use ``ffi.new("foo_t *[1]")``.)
-If ``field`` is given,
-returns the address of that field in the structure. The returned
-pointer is only valid as long as the original ``cdata`` object is; be
-sure to keep it alive if it was obtained directly from ``ffi.new()``.
-*New in version 0.4.*
+**ffi.addressof(cdata, *fields_or_indexes)**: equivalent to the C
+expression ``&cdata`` or ``&cdata.field`` or ``&cdata->field`` or
+``&cdata[index]`` (or any combination of fields and indexes). Works
+with the same ctypes where one of the above expressions would work in
+C, with one exception: if no ``fields_or_indexes`` is specified, it
+cannot be used to take the address of a primitive or pointer (it would
+be difficult to implement because only structs and unions and arrays
+are internally stored as an indirect pointer to the data. If you need
+a C int whose address can be taken, use ``ffi.new("int[1]")`` in the
+first place; similarly, for a pointer, use ``ffi.new("foo_t *[1]")``.)
+
+The returned pointer is only valid as long as the original ``cdata``
+object is; be sure to keep it alive if it was obtained directly from
+``ffi.new()``. *New in version 0.4.*
.. versionchanged:: 0.9
- ``"field"`` can be ``"x.y"`` in case of nested structures.
+ You can give several field names in case of nested structures, and
+ you can give numeric values for array items. Note that
+ ``&cdata[index]`` can also be expressed as simply ``cdata + index``,
+ both in C and in CFFI.
.. "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
@@ -964,9 +964,19 @@
ffi.cdef("struct foo { int a, b, c; };"
"struct bar { struct foo d, e; };")
assert ffi.offsetof("struct bar", "e") == 12
- assert ffi.offsetof("struct bar", "e.a") == 12
- assert ffi.offsetof("struct bar", "e.b") == 16
- assert ffi.offsetof("struct bar", "e.c") == 20
+ py.test.raises(KeyError, ffi.offsetof, "struct bar", "e.a")
+ assert ffi.offsetof("struct bar", "e", "a") == 12
+ assert ffi.offsetof("struct bar", "e", "b") == 16
+ assert ffi.offsetof("struct bar", "e", "c") == 20
+
+ def test_offsetof_array(self):
+ ffi = FFI(backend=self.Backend())
+ assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int")
+ assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int")
+ ffi.cdef("struct bar { int a, b; int c[99]; };")
+ assert ffi.offsetof("struct bar", "c") == 2 * ffi.sizeof("int")
+ assert ffi.offsetof("struct bar", "c", 0) == 2 * ffi.sizeof("int")
+ assert ffi.offsetof("struct bar", "c", 51) == 53 * ffi.sizeof("int")
def test_alignof(self):
ffi = FFI(backend=self.Backend())
@@ -1500,8 +1510,10 @@
p = ffi.new("struct foo_s *")
a = ffi.addressof(p[0])
assert repr(a).startswith("<cdata 'struct foo_s *' 0x")
+ assert a == p
py.test.raises(TypeError, ffi.addressof, p)
py.test.raises((AttributeError, TypeError), ffi.addressof, 5)
+ py.test.raises(TypeError, ffi.addressof, ffi.cast("int", 5))
def test_addressof_field(self):
ffi = FFI(backend=self.Backend())
@@ -1519,7 +1531,8 @@
ffi.cdef("struct foo_s { int x, y; };"
"struct bar_s { struct foo_s a, b; };")
p = ffi.new("struct bar_s *")
- a = ffi.addressof(p[0], 'b.y')
+ py.test.raises(KeyError, ffi.addressof, p[0], 'b.y')
+ a = ffi.addressof(p[0], 'b', 'y')
assert int(ffi.cast("uintptr_t", a)) == (
int(ffi.cast("uintptr_t", p)) +
ffi.sizeof("struct foo_s") + ffi.sizeof("int"))
@@ -1531,6 +1544,49 @@
a = ffi.addressof(p[0])
assert a == p
+ def test_addressof_array(self):
+ ffi = FFI()
+ p = ffi.new("int[52]")
+ p0 = ffi.addressof(p)
+ assert p0 == p
+ assert ffi.typeof(p0) is ffi.typeof("int(*)[52]")
+ py.test.raises(TypeError, ffi.addressof, p0)
+ #
+ p1 = ffi.addressof(p, 25)
+ assert ffi.typeof(p1) is ffi.typeof("int *")
+ assert (p1 - p) == 25
+ assert ffi.addressof(p, 0) == p
+
+ def test_addressof_pointer(self):
+ ffi = FFI()
+ array = ffi.new("int[50]")
+ p = ffi.cast("int *", array)
+ py.test.raises(TypeError, ffi.addressof, p)
+ assert ffi.addressof(p, 0) == p
+ assert ffi.addressof(p, 25) == p + 25
+ assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p)
+ #
+ ffi.cdef("struct foo { int a, b; };")
+ array = ffi.new("struct foo[50]")
+ p = ffi.cast("int *", array)
+ py.test.raises(TypeError, ffi.addressof, p)
+ assert ffi.addressof(p, 0) == p
+ assert ffi.addressof(p, 25) == p + 25
+ assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p)
+
+ def test_addressof_array_in_struct(self):
+ ffi = FFI()
+ ffi.cdef("struct foo { int a, b; int c[50]; };")
+ p = ffi.new("struct foo *")
+ p1 = ffi.addressof(p, "c", 25)
+ assert ffi.typeof(p1) is ffi.typeof("int *")
+ assert p1 == ffi.cast("int *", p) + 27
+ assert ffi.addressof(p, "c") == ffi.cast("int *", p) + 2
+ assert ffi.addressof(p, "c", 0) == ffi.cast("int *", p) + 2
+ p2 = ffi.addressof(p, 1)
+ assert ffi.typeof(p2) is ffi.typeof("struct foo *")
+ assert p2 == p + 1
+
def test_multiple_independent_structs(self):
ffi1 = FFI(); ffi1.cdef("struct foo { int x; };")
ffi2 = FFI(); ffi2.cdef("struct foo { int y, z; };")
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit