Author: Armin Rigo <[email protected]>
Branch: cffi-1.0
Changeset: r1779:3f61ed84336c
Date: 2015-04-22 16:40 +0200
http://bitbucket.org/cffi/cffi/changeset/3f61ed84336c/

Log:    ffi.offsetof()

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -4952,27 +4952,26 @@
     return res;
 }
 
-static PyObject *b_typeoffsetof(PyObject *self, PyObject *args)
-{
-    PyObject *res, *fieldname;
-    CTypeDescrObject *ct;
+static CTypeDescrObject *direct_typeoffsetof(CTypeDescrObject *ct,
+                                             PyObject *fieldname,
+                                             int following, Py_ssize_t *offset)
+{
+    /* Does not return a new reference! */
+    CTypeDescrObject *res;
     CFieldObject *cf;
-    Py_ssize_t offset;
-    int following = 0;
-
-    if (!PyArg_ParseTuple(args, "O!O|i:typeoffsetof",
-                          &CTypeDescr_Type, &ct, &fieldname, &following))
-        return NULL;
 
     if (PyTextAny_Check(fieldname)) {
         if (!following && (ct->ct_flags & CT_POINTER))
             ct = ct->ct_itemdescr;
-        if (!(ct->ct_flags & (CT_STRUCT|CT_UNION)) ||
-                force_lazy_struct(ct) <= 0) {
+        if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) {
+            PyErr_SetString(PyExc_TypeError,
+                            "with a field name argument, expected a "
+                            "struct or union ctype");
+            return NULL;
+        }
+        if (force_lazy_struct(ct) <= 0) {
             if (!PyErr_Occurred())
-                PyErr_SetString(PyExc_TypeError,
-                                "with a field name argument, expected an "
-                                "initialized struct or union ctype");
+                PyErr_SetString(PyExc_TypeError, "struct/union is opaque");
             return NULL;
         }
         cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname);
@@ -4984,8 +4983,8 @@
             PyErr_SetString(PyExc_TypeError, "not supported for bitfields");
             return NULL;
         }
-        res = (PyObject *)cf->cf_type;
-        offset = cf->cf_offset;
+        res = cf->cf_type;
+        *offset = cf->cf_offset;
     }
     else {
         ssize_t index = PyInt_AsSsize_t(fieldname);
@@ -5002,14 +5001,32 @@
                                              "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) {
+        res = 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 res;
+}
+
+static PyObject *b_typeoffsetof(PyObject *self, PyObject *args)
+{
+    PyObject *res, *fieldname;
+    CTypeDescrObject *ct;
+    Py_ssize_t offset;
+    int following = 0;
+
+    if (!PyArg_ParseTuple(args, "O!O|i:typeoffsetof",
+                          &CTypeDescr_Type, &ct, &fieldname, &following))
+        return NULL;
+
+    res = (PyObject *)direct_typeoffsetof(ct, fieldname, following, &offset);
+    if (res == NULL)
+        return NULL;
+
     return Py_BuildValue("(On)", res, offset);
 }
 
diff --git a/new/ffi_obj.c b/new/ffi_obj.c
--- a/new/ffi_obj.c
+++ b/new/ffi_obj.c
@@ -290,58 +290,46 @@
 "string or unicode string.\n"
 "\n"
 "If 'cdata' is an enum, returns the value of the enumerator as a\n"
-"string, or 'NUMBER' if the value is out of range.\n");
+"string, or 'NUMBER' if the value is out of range.");
 
 #define ffi_string  b_string     /* ffi_string() => b_string()
                                     from _cffi_backend.c */
 
-#if 0
-static CFieldObject *_ffi_field(CTypeDescrObject *ct, const char *fieldname)
+PyDoc_STRVAR(ffi_offsetof_doc,
+"Return the offset of the named field inside the given structure or\n"
+"array, which must be given as a C type name.  You can give several\n"
+"field names in case of nested structures.  You can also give numeric\n"
+"values which correspond to array items, in case of an array type.");
+
+static PyObject *ffi_offsetof(FFIObject *self, PyObject *args)
 {
-    CFieldObject *cf;
-    if (force_lazy_struct(ct) == NULL) {
-        PyErr_Format(PyExc_TypeError, "'%s' is incomplete", ct->ct_name);
+    PyObject *arg;
+    CTypeDescrObject *ct;
+    Py_ssize_t i, offset;
+
+    if (PyTuple_Size(args) < 2) {
+        PyErr_SetString(PyExc_TypeError,
+                        "offsetof() expects at least 2 arguments");
         return NULL;
     }
-    cf = (CFieldObject *)PyDict_GetItemString(ct->ct_stuff, fieldname);
-    if (cf == NULL) {
-        PyErr_Format(PyExc_KeyError, "'%s' has got no field '%s'",
-                     ct->ct_name, fieldname);
-        return NULL;
-    }
-    if (cf->cf_bitshift >= 0) {
-        PyErr_SetString(PyExc_TypeError, "not supported for bitfields");
-        return NULL;
-    }
-    return cf;
-}
 
-static PyObject *ffi_offsetof(ZefFFIObject *self, PyObject *args)
-{
-    PyObject *arg;
-    char *fieldname;
-    CTypeDescrObject *ct;
-    CFieldObject *cf;
-
-    if (!PyArg_ParseTuple(args, "Os:offsetof", &arg, &fieldname))
-        return NULL;
-
+    arg = PyTuple_GET_ITEM(args, 0);
     ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE);
     if (ct == NULL)
         return NULL;
 
-    if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) {
-        PyErr_Format(PyExc_TypeError,
-                     "expected a struct or union ctype, got '%s'",
-                     ct->ct_name);
-        return NULL;
+    offset = 0;
+    for (i = 1; i < PyTuple_GET_SIZE(args); i++) {
+        Py_ssize_t ofs1;
+        ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i), i > 1, &ofs1);
+        if (ct == NULL)
+            return NULL;
+        offset += ofs1;
     }
-    cf = _ffi_field(ct, fieldname);
-    if (cf == NULL)
-        return NULL;
-    return PyInt_FromSsize_t(cf->cf_offset);
+    return PyInt_FromSsize_t(offset);
 }
 
+#if 0
 static PyObject *ffi_addressof(ZefFFIObject *self, PyObject *args)
 {
     PyObject *obj;
@@ -612,8 +600,8 @@
     {"gc",            (PyCFunction)ffi_gc,        METH_VARARGS},
     {"getctype",      (PyCFunction)ffi_getctype,  METH_VARARGS},
     {"load_library",  
(PyCFunction)ffi_load_library,METH_VARARGS|METH_KEYWORDS},
-    {"offsetof",      (PyCFunction)ffi_offsetof,  METH_VARARGS},
 #endif
+    {"offsetof",      (PyCFunction)ffi_offsetof, 
METH_VARARGS,ffi_offsetof_doc},
     {"new",           (PyCFunction)ffi_new,       METH_VARARGS, ffi_new_doc},
 #if 0
     {"new_handle",    (PyCFunction)ffi_new_handle,METH_O},
diff --git a/new/test_recompiler.py b/new/test_recompiler.py
--- a/new/test_recompiler.py
+++ b/new/test_recompiler.py
@@ -216,6 +216,9 @@
     py.test.raises(OverflowError, "p.b -= 1")
     q = ffi.new("struct bar_s *", {'f': p})
     assert q.f == p
+    #
+    assert ffi.offsetof("struct foo_s", "a") == 0
+    assert ffi.offsetof("struct foo_s", "b") == 4
 
 def test_verify_exact_field_offset():
     ffi = FFI()
diff --git a/new/test_verify1.py b/new/test_verify1.py
--- a/new/test_verify1.py
+++ b/new/test_verify1.py
@@ -451,8 +451,8 @@
     };
     """)
     py.test.raises(ffi.error, ffi.sizeof, 'struct foo_s')
-    py.test.raises(ffi.error, ffi.offsetof, 'struct foo_s', 'x')
-    py.test.raises(ffi.error, ffi.new, 'struct foo_s *')
+    py.test.raises(TypeError, ffi.offsetof, 'struct foo_s', 'x')
+    py.test.raises(TypeError, ffi.new, 'struct foo_s *')
     ffi.verify("""
     struct foo_s {
        int a, b, x, c, d, e;
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to