diff -r 98b0ae585f5e Lib/ctypes/test/test_bitfields.py --- a/Lib/ctypes/test/test_bitfields.py Thu Jun 21 16:22:15 2012 +0200 +++ b/Lib/ctypes/test/test_bitfields.py Thu Jun 21 18:19:43 2012 +0100 @@ -231,6 +231,22 @@ else: self.assertEqual(sizeof(X), sizeof(c_int) * 2) + def test_sane_offsets_with_mixed_types(self): + class X(Structure): + _fields_ = [("a", c_short, 2), + ("b", c_byte, 7), + ("c", c_int, 28), + ("d", c_short, 4)] + + # Sanity checks: offsets should be such that the field + # fits in the containing type. + for name, c_type, requested_width in X._fields_: + field = getattr(X, name) + width, offset = divmod(field.size, 65536) + self.assertEqual(width, requested_width) + self.assertLessEqual(0, offset) + self.assertLessEqual(offset + width, sizeof(c_type) * 8) + def test_anon_bitfields(self): # anonymous bit-fields gave a strange error message class X(Structure): diff -r 98b0ae585f5e Modules/_ctypes/cfield.c --- a/Modules/_ctypes/cfield.c Thu Jun 21 16:22:15 2012 +0200 +++ b/Modules/_ctypes/cfield.c Thu Jun 21 18:19:43 2012 +0100 @@ -55,6 +55,7 @@ GETFUNC getfunc = NULL; StgDictObject *dict; int fieldtype; + int bit_offset; #define NO_BITFIELD 0 #define NEW_BITFIELD 1 #define CONT_BITFIELD 2 @@ -187,12 +188,22 @@ break; case CONT_BITFIELD: + /* Adjust current bit offset if necessary so that the next field + doesn't straddle a multiple of 8*dict->size. */ + if (*pbitofs && ( + (*pbitofs + bitsize - 1) % (8*dict->size) != + bitsize + (*pbitofs - 1) % (8*dict->size))) + *pbitofs += (8*dict->size) - 1 - (*pbitofs - 1) % (8*dict->size); + + /* bit offset within outer field */ + bit_offset = *pbitofs % (8*dict->size); if (big_endian) - self->size = (bitsize << 16) + *pfield_size - *pbitofs - bitsize; + self->size = (bitsize << 16) + *pfield_size - bit_offset - bitsize; else - self->size = (bitsize << 16) + *pbitofs; + self->size = (bitsize << 16) + bit_offset; - self->offset = *poffset - size; /* poffset is already updated for the NEXT field */ + /* poffset is already updated for the NEXT field */ + self->offset = *poffset - *pfield_size/8 + (*pbitofs - bit_offset)/8; *pbitofs += bitsize; break; }