[pypy-commit] cffi cpy-extension: Completing structs.

arigo noreply at buildbot.pypy.org
Tue Jun 12 23:24:28 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: cpy-extension
Changeset: r303:b69a8e2d13a1
Date: 2012-06-12 23:24 +0200
http://bitbucket.org/cffi/cffi/changeset/b69a8e2d13a1/

Log:	Completing structs.

diff --git a/c/_ffi_backend.c b/c/_ffi_backend.c
--- a/c/_ffi_backend.c
+++ b/c/_ffi_backend.c
@@ -2271,12 +2271,14 @@
     PyObject *fields, *interned_fields, *ignored;
     int is_union, alignment;
     Py_ssize_t offset, i, nb_fields, maxsize, prev_bit_position;
+    Py_ssize_t totalsize = -1;
+    int totalalignment = -1;
     CFieldObject **previous, *prev_field;
 
-    if (!PyArg_ParseTuple(args, "O!O!|O:complete_struct_or_union",
+    if (!PyArg_ParseTuple(args, "O!O!|Oni:complete_struct_or_union",
                           &CTypeDescr_Type, &ct,
                           &PyList_Type, &fields,
-                          &ignored))
+                          &ignored, &totalsize, &totalalignment))
         return NULL;
 
     if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) ==
@@ -2308,13 +2310,13 @@
     for (i=0; i<nb_fields; i++) {
         PyObject *fname;
         CTypeDescrObject *ftype;
-        int fbitsize, falign, err, bitshift;
+        int fbitsize, falign, err, bitshift, foffset = -1;
         CFieldObject *cf;
 
-        if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!i:list item",
+        if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!i|i:list item",
                               &PyString_Type, &fname,
                               &CTypeDescr_Type, &ftype,
-                              &fbitsize))
+                              &fbitsize, &foffset))
             goto error;
 
         if (ftype->ct_size < 0) {
@@ -2331,8 +2333,12 @@
         if (alignment < falign)
             alignment = falign;
 
-        /* align this field to its own 'falign' by inserting padding */
-        offset = (offset + falign - 1) & ~(falign-1);
+        if (foffset < 0) {
+            /* align this field to its own 'falign' by inserting padding */
+            offset = (offset + falign - 1) & ~(falign-1);
+        }
+        else
+            offset = foffset;
 
         if (fbitsize < 0 || (fbitsize == 8 * ftype->ct_size &&
                              !(ftype->ct_flags & CT_PRIMITIVE_CHAR))) {
@@ -2407,15 +2413,23 @@
 
     if (is_union) {
         assert(offset == 0);
-        ct->ct_size = maxsize;
+        offset = maxsize;
     }
     else {
         if (offset == 0)
             offset = 1;
         offset = (offset + alignment - 1) & ~(alignment-1);
-        ct->ct_size = offset;
     }
-    ct->ct_length = alignment;
+    if (totalsize < 0)
+        totalsize = offset;
+    else if (totalsize < offset) {
+        PyErr_Format(PyExc_TypeError,
+                     "%s cannot be of size %zd: there are fields at least "
+                     "up to %zd", ct->ct_name, totalsize, offset);
+        goto error;
+    }
+    ct->ct_size = totalsize;
+    ct->ct_length = totalalignment < 0 ? alignment : totalalignment;
     ct->ct_stuff = interned_fields;
     ct->ct_flags &= ~CT_IS_OPAQUE;
 
@@ -3328,6 +3342,28 @@
     return result;
 }
 
+static PyObject *_cffi_get_struct_layout(Py_ssize_t nums[])
+{
+    PyObject *result;
+    int count = 0;
+    while (nums[count] >= 0)
+        count++;
+
+    result = PyList_New(count);
+    if (result == NULL)
+        return NULL;
+
+    while (--count >= 0) {
+        PyObject *o = PyInt_FromSsize_t(nums[count]);
+        if (o == NULL) {
+            Py_DECREF(result);
+            return NULL;
+        }
+        PyList_SET_ITEM(result, count, o);
+    }
+    return result;
+}
+
 static void *cffi_exports[] = {
     _cffi_to_c_char_p,
     _cffi_to_c_signed_char,
@@ -3346,6 +3382,7 @@
     _cffi_to_c_char,
     _cffi_from_c_pointer,
     _cffi_to_c_pointer,
+    _cffi_get_struct_layout,
 };
 
 /************************************************************/
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -129,7 +129,8 @@
 
 class StructOrUnion(BaseType):
     _attrs_ = ('name',)
-        
+    fixedlayout = None
+
     def __init__(self, name, fldnames, fldtypes, fldbitsize):
         self.name = name
         self.fldnames = fldnames
@@ -148,8 +149,14 @@
         return args
 
     def finish_backend_type(self, ffi, BType, *fldtypes):
-        lst = zip(self.fldnames, fldtypes, self.fldbitsize)
-        ffi._backend.complete_struct_or_union(BType, lst, self)
+        if self.fixedlayout is None:
+            lst = zip(self.fldnames, fldtypes, self.fldbitsize)
+            ffi._backend.complete_struct_or_union(BType, lst, self)
+        else:
+            fieldofs, totalsize, totalalignment = self.fixedlayout
+            lst = zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)
+            ffi._backend.complete_struct_or_union(BType, lst, self,
+                                                  totalsize, totalalignment)
         return BType
 
 
@@ -158,7 +165,7 @@
     partial = False
 
     def check_not_partial(self):
-        if self.partial:
+        if self.partial and self.fixedlayout is None:
             from . import ffiplatform
             raise ffiplatform.VerificationMissing(self.get_c_name())
 
@@ -166,59 +173,6 @@
         self.check_not_partial()
         return ffi._backend.new_struct_type(self.name)
 
-    def verifier_declare_struct(self, verifier, name):
-        assert name == self.name
-        if self.partial:
-            self.verifier_decl_partial(verifier)
-        else:
-            self.verifier_decl_notpartial(verifier)
-
-    def verifier_decl_notpartial(self, verifier):
-        if self.fldnames is None:    # not partial, but fully opaque:
-            return                   # cannot really test even for existence
-        struct = verifier.ffi._get_cached_btype(self)
-        verifier.write('{')
-        verifier.write('struct __aligncheck__ { char x; struct %s y; };' %
-                       self.name)
-        verifier.write(
-            '__sameconstant__(sizeof(struct %s), %d)' % (
-            self.name, verifier.ffi.sizeof(struct)))
-        verifier.write(
-            '__sameconstant__(offsetof(struct __aligncheck__, y), %d)' % (
-            verifier.ffi.alignof(struct),))
-        for fname, ftype, fbitsize in zip(self.fldnames, self.fldtypes,
-                                          self.fldbitsize):
-            if fbitsize >= 0:
-                assert 0, "XXX: bitfield"
-            verifier.write('__sameconstant__(offsetof(struct %s, %s), %d)' % (
-                self.name, fname, verifier.ffi.offsetof(struct, fname)))
-            # XXX gcc only!
-            verifier.write('__sametype__(%s, typeof(((struct %s *)0)->%s))' % (
-                ftype.get_c_name('** result'), self.name, fname))
-        verifier.write('}')
-
-    def verifier_decl_partial(self, verifier):
-        assert self.fldnames is not None
-        verifier.write('{')
-        verifier.write('struct __aligncheck__ { char x; struct %s y; };' %
-                       self.name)
-        verifier.write('struct %s __test__;' % self.name)
-        verifier.write_printf('BEGIN struct %s' % self.name)
-        verifier.write_printf('SIZE %ld %ld',
-                              '(long)sizeof(struct %s)' % self.name,
-                              '(long)offsetof(struct __aligncheck__, y)')
-        for fname, ftype, fbitsize in zip(self.fldnames, self.fldtypes,
-                                          self.fldbitsize):
-            if fbitsize < 0:
-                verifier.write_printf('FIELD ' + fname + ' %ld %ld',
-                                      '(long)offsetof(struct %s, %s)' %
-                                      (self.name, fname),
-                                      '(long)sizeof(__test__.%s)' % fname)
-            else:
-                assert 0, "XXX: bitfield"
-        verifier.write_printf('END')
-        verifier.write('}')
-
 
 class UnionType(StructOrUnion):
     kind = 'union'
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -68,6 +68,12 @@
         lst = [revmapping[i] for i in range(len(revmapping))]
         module._cffi_setup(lst)
         del module._cffi_setup
+        #
+        for name, tp in self.ffi._parser._declarations.iteritems():
+            kind, realname = name.split(' ', 1)
+            method = getattr(self, 'loading_cpy_%s' % (kind,))
+            method(tp, realname, module)
+        #
         return module
 
     def generate(self, step_name):
@@ -79,6 +85,9 @@
     def generate_nothing(self, tp, name):
         pass
 
+    def loaded_noop(self, tp, name, module):
+        pass
+
     # ----------
 
     def convert_to_c(self, tp, fromvar, tovar, errcode, is_funcarg=False):
@@ -115,13 +124,15 @@
             raise NotImplementedError(tp)
 
     # ----------
+    # typedefs: generates no code so far
 
-    # XXX
     generate_cpy_typedef_decl   = generate_nothing
     generate_cpy_typedef_method = generate_nothing
     generate_cpy_typedef_init   = generate_nothing
+    loading_cpy_typedef         = loaded_noop
 
     # ----------
+    # function declarations
 
     def generate_cpy_function_decl(self, tp, name):
         assert isinstance(tp, model.FunctionType)
@@ -133,8 +144,8 @@
             argname = 'arg0'
         else:
             argname = 'args'
-        prnt('static PyObject *_cffi_f_%s(PyObject *self, PyObject *%s)' %
-             (name, argname))
+        prnt('static PyObject *')
+        prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
         prnt('{')
         assert not tp.ellipsis  # XXX later
         #
@@ -186,10 +197,50 @@
         self.prnt('  {"%s", _cffi_f_%s, %s},' % (name, name, meth))
 
     generate_cpy_function_init = generate_nothing
+    loading_cpy_function       = loaded_noop
+
+    # ----------
+    # struct declarations
+
+    def generate_cpy_struct_decl(self, tp, name):
+        assert name == tp.name
+        prnt = self.prnt
+        prnt('static PyObject *')
+        prnt('_cffi_struct_%s(PyObject *self, PyObject *noarg)' % name)
+        prnt('{')
+        prnt('  struct _cffi_aligncheck { char x; struct %s y; };' % name)
+        prnt('  static Py_ssize_t nums[] = {')
+        prnt('    sizeof(struct %s),' % name)
+        prnt('    offsetof(struct _cffi_aligncheck, y),')
+        for i in range(len(tp.fldnames)):
+            prnt('    offsetof(struct %s, %s),' % (name, tp.fldnames[i]))
+        prnt('    -1')
+        prnt('  };')
+        prnt('  return _cffi_get_struct_layout(nums);')
+        prnt('}')
+
+    def generate_cpy_struct_method(self, tp, name):
+        self.prnt('  {"_cffi_struct_%s", _cffi_struct_%s, METH_NOARGS},' % (
+            name, name))
+
+    generate_cpy_struct_init = generate_nothing
+
+    def loading_cpy_struct(self, tp, name, module):
+        assert name == tp.name
+        function = getattr(module, '_cffi_struct_%s' % name)
+        layout = function()
+        totalsize = layout[0]
+        totalalignment = layout[1]
+        fieldofs = layout[2:]
+        assert len(fieldofs) == len(tp.fldnames)
+        tp.fixedlayout = fieldofs, totalsize, totalalignment
+
+    # ----------
 
 
 cffimod_header = r'''
 #include <Python.h>
+#include <stddef.h>
 
 #define _cffi_from_c_double PyFloat_FromDouble
 #define _cffi_from_c_float PyFloat_FromDouble
@@ -253,6 +304,8 @@
     ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
 #define _cffi_to_c_pointer                                               \
     ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
+#define _cffi_get_struct_layout                                          \
+    ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12])
 
 #if SIZEOF_LONG < SIZEOF_LONG_LONG
 #  define _cffi_to_c_long_long PyLong_AsLongLong
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -113,7 +113,7 @@
 
 
 def test_verify_typedefs():
-    py.test.skip("XXX?")
+    py.test.skip("ignored so far")
     types = ['signed char', 'unsigned char', 'int', 'long']
     for cdefed in types:
         for real in types:
@@ -127,7 +127,7 @@
 
 
 def test_ffi_full_struct():
-    py.test.skip("XXX?")
+    py.test.skip("XXX")
     ffi = FFI()
     ffi.cdef("struct foo_s { char x; int y; long *z; };")
     ffi.verify("struct foo_s { char x; int y; long *z; };")
@@ -147,7 +147,6 @@
 
 
 def test_ffi_nonfull_struct():
-    py.test.skip("XXX")
     ffi = FFI()
     ffi.cdef("""
     struct foo_s {
@@ -163,5 +162,5 @@
        int a, b, x, c, d, e;
     };
     """)
-    assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof(int)
-    assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof(int)
+    assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int')
+    assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int')


More information about the pypy-commit mailing list