Armin Rigo avatar Armin Rigo committed 4e06afe

MSVC-style bitfields, first attempt

Comments (0)

Files changed (2)

c/_cffi_backend.c

     return cf;   /* borrowed reference */
 }
 
+#define SF_MSVC_BITFIELDS 1
+
 static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct;
     Py_ssize_t totalsize = -1;
     int totalalignment = -1;
     CFieldObject **previous;
-
-    if (!PyArg_ParseTuple(args, "O!O!|Oni:complete_struct_or_union",
+    int prev_bitfield_size, prev_bitfield_free;
+#ifdef MS_WIN32
+    int sflags = SF_MSVC_BITFIELDS;
+#else
+    int sflags = 0;
+#endif
+
+    if (!PyArg_ParseTuple(args, "O!O!|Onii:complete_struct_or_union",
                           &CTypeDescr_Type, &ct,
                           &PyList_Type, &fields,
-                          &ignored, &totalsize, &totalalignment))
+                          &ignored, &totalsize, &totalalignment, &sflags))
         return NULL;
 
     if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) ==
     alignment = 1;
     boffset = 0;         /* this number is in *bits*, not bytes! */
     boffsetmax = 0;      /* the maximum value of boffset, in bits too */
+    prev_bitfield_size = 0;
+    prev_bitfield_free = 0;
     nb_fields = PyList_GET_SIZE(fields);
     interned_fields = PyDict_New();
     if (interned_fields == NULL)
                 previous = &(*previous)->cf_next;
             }
             boffset += ftype->ct_size * 8;
+            prev_bitfield_size = 0;
         }
         else {
             /* this is the case of a bitfield */
                     assert(boffset < field_offset_bytes * 8);
                 }
                 boffset = field_offset_bytes * 8;   /* the only effect */
+                prev_bitfield_size = 0;
             }
             else {
-                /* Can the field start at the offset given by 'boffset'?  It
-                   can if it would entirely fit into an aligned ftype field. */
-                bits_already_occupied = boffset - (field_offset_bytes * 8);
-
-                if (bits_already_occupied + fbitsize > 8 * ftype->ct_size) {
-                    /* it would not fit, we need to start at the next
-                       allowed position */
-                    field_offset_bytes += falign;
-                    assert(boffset < field_offset_bytes * 8);
-                    boffset = field_offset_bytes * 8;
-                    bitshift = 0;
+                if (!(sflags & SF_MSVC_BITFIELDS)) {
+                    /* GCC's algorithm */
+
+                    /* Can the field start at the offset given by 'boffset'?  It
+                       can if it would entirely fit into an aligned ftype field. */
+                    bits_already_occupied = boffset - (field_offset_bytes * 8);
+
+                    if (bits_already_occupied + fbitsize > 8 * ftype->ct_size) {
+                        /* it would not fit, we need to start at the next
+                           allowed position */
+                        field_offset_bytes += falign;
+                        assert(boffset < field_offset_bytes * 8);
+                        boffset = field_offset_bytes * 8;
+                        bitshift = 0;
+                    }
+                    else {
+                        bitshift = bits_already_occupied;
+                        assert(bitshift >= 0);
+                    }
+                    boffset += fbitsize;
                 }
-                else
-                    bitshift = bits_already_occupied;
+                else {
+                    /* MSVC's algorithm */
+
+                    /* A bitfield is considered as taking the full width
+                       of their declared type.  It can share some bits
+                       with the previous field only if it was also a
+                       bitfield and used a type of the same size. */
+                    if (prev_bitfield_size == ftype->ct_size &&
+                        prev_bitfield_free >= fbitsize) {
+                        /* yes: reuse */
+                        bitshift = 8 * prev_bitfield_size - prev_bitfield_free;
+                    }
+                    else {
+                        /* no: start a new full field */
+                        boffset = (boffset + falign*8-1) & ~(falign*8-1); /*align*/
+                        boffset += ftype->ct_size * 8;
+                        bitshift = 0;
+                        prev_bitfield_size = ftype->ct_size;
+                        prev_bitfield_free = 8 * prev_bitfield_size;
+                    }
+                    prev_bitfield_free -= fbitsize;
+                    field_offset_bytes = boffset / 8 - ftype->ct_size;
+                }
 
                 *previous = _add_field(interned_fields, fname, ftype,
                                        field_offset_bytes, bitshift, fbitsize);
                 if (*previous == NULL)
                     goto error;
                 previous = &(*previous)->cf_next;
-                boffset += fbitsize;
             }
         }
 
     assert wr() is None
     py.test.raises(RuntimeError, from_handle, cast(BCharP, 0))
 
-def test_bitfield_as_gcc():
+def _test_bitfield_details(flag):
     BChar = new_primitive_type("char")
     BShort = new_primitive_type("short")
     BInt = new_primitive_type("int")
+    BUInt = new_primitive_type("unsigned int")
     BStruct = new_struct_type("foo1")
     complete_struct_or_union(BStruct, [('a', BChar, -1),
-                                       ('b', BInt, 9),
-                                       ('c', BChar, -1)])
-    assert typeoffsetof(BStruct, 'c') == (BChar, 3)
-    assert sizeof(BStruct) == 4
+                                       ('b1', BInt, 9),
+                                       ('b2', BUInt, 7),
+                                       ('c', BChar, -1)], -1, -1, -1, flag)
+    if flag == 0:   # gcc
+        assert typeoffsetof(BStruct, 'c') == (BChar, 3)
+        assert sizeof(BStruct) == 4
+    else:           # msvc
+        assert typeoffsetof(BStruct, 'c') == (BChar, 8)
+        assert sizeof(BStruct) == 12
     assert alignof(BStruct) == 4
     #
     BStruct = new_struct_type("foo2")
     complete_struct_or_union(BStruct, [('a', BChar, -1),
                                        ('',  BShort, 9),
-                                       ('c', BChar, -1)])
+                                       ('c', BChar, -1)], -1, -1, -1, flag)
     assert typeoffsetof(BStruct, 'c') == (BChar, 4)
     assert sizeof(BStruct) == 5
     assert alignof(BStruct) == 1
     complete_struct_or_union(BStruct, [('a', BChar, -1),
                                        ('',  BInt, 0),
                                        ('',  BInt, 0),
-                                       ('c', BChar, -1)])
+                                       ('c', BChar, -1)], -1, -1, -1, flag)
     assert typeoffsetof(BStruct, 'c') == (BChar, 4)
     assert sizeof(BStruct) == 5
     assert alignof(BStruct) == 1
 
 
+def test_bitfield_as_gcc():
+    _test_bitfield_details(flag=0)
+
+def test_bitfield_as_msvc():
+    _test_bitfield_details(flag=1)
+
+
 def test_version():
     # this test is here mostly for PyPy
     assert __version__ == "0.7"
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.