diff -r 024827a9db64 Lib/ctypes/__init__.py --- a/Lib/ctypes/__init__.py Fri Jun 24 20:52:27 2011 +0200 +++ b/Lib/ctypes/__init__.py Sun Jul 10 15:16:35 2011 -0700 @@ -529,6 +529,11 @@ elif sizeof(kind) == 8: c_uint64 = kind del(kind) +# Bitfield allocation strategies +BITFIELD_ALLOCATION_NATIVE = 0 +BITFIELD_ALLOCATION_GCC = 1 +BITFIELD_ALLOCATION_MSVC = 2 + # XXX for whatever reasons, creating the first instance of a callback # function is needed for the unittests on Win64 to succeed. This MAY # be a compiler bug, since the problem occurs only when _ctypes is diff -r 024827a9db64 Lib/ctypes/test/test_bitfield_allocation.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/ctypes/test/test_bitfield_allocation.py Sun Jul 10 15:16:35 2011 -0700 @@ -0,0 +1,65 @@ +import unittest +import os +from ctypes import * + +# test explicit bitfield allocation strategies + +# tests will use a bitfield structure which has 2 bytes when compiled with GCC +# and 3 bytes when compiled with MSVC +class BitfieldAllocationTestCase(unittest.TestCase): + # size of structure using native allocation should be equivalent to + # size of structure with unspecified allocation strategy + def test_native_allocation(self): + class BitFieldStructure(Structure): + _pack_ = 1 + _fields_ = [('A', c_byte, 3), + ('B', c_ushort, 12)] + + class NativeBitFieldStructure(Structure): + _pack_ = 1 + _fields_ = [('A', c_byte, 3), + ('B', c_ushort, 12)] + _bitfield_allocation_ = BITFIELD_ALLOCATION_NATIVE + + self.assertEqual(sizeof(NativeBitFieldStructure), sizeof(BitFieldStructure)) + + # OS-specific - on Windows size should be 3, on other OS-es it should be 2 + if os.name == 'nt': + self.assertEqual(sizeof(NativeBitFieldStructure), 3) + else: + self.assertEqual(sizeof(NativeBitFieldStructure), 2) + + # MSVC allocation should allocate 3 bytes for the structure + def test_msvc_allocation(self): + class MSVCBitFieldStructure(Structure): + _pack_ = 1 + _fields_ = [('A', c_byte, 3), + ('B', c_ushort, 12)] + _bitfield_allocation_ = BITFIELD_ALLOCATION_MSVC + + self.assertEqual(sizeof(MSVCBitFieldStructure), 3) + + # GCC allocation should allocate 2 bytes for the structure + def test_gcc_allocation(self): + class GCCBitFieldStructure(Structure): + _pack_ = 1 + _fields_ = [('A', c_byte, 3), + ('B', c_ushort, 12)] + + _bitfield_allocation_ = BITFIELD_ALLOCATION_GCC + + self.assertEqual(sizeof(GCCBitFieldStructure), 2) + + # ValueError should be raised when using an invalid allocation strategy + def test_invalid_allocation(self): + def invalid_define(): + class InvalidAllocation(Structure): + _pack_ = 1 + _fields_ = [('A', c_byte, 3), + ('B', c_ushort, 12)] + _bitfield_allocation_ = 7 # 7 is not a valid allocation strategy + self.assertRaises(ValueError, invalid_define) + +if __name__ == '__main__': + unittest.main() + diff -r 024827a9db64 Modules/_ctypes/cfield.c --- a/Modules/_ctypes/cfield.c Fri Jun 24 20:52:27 2011 +0200 +++ b/Modules/_ctypes/cfield.c Sun Jul 10 15:16:35 2011 -0700 @@ -32,6 +32,42 @@ return (PyObject *)obj; } +/* Helper function to determine whether current bitfield can be continued based on allocation strategy */ +int +CanContinueField(int bitfield_allocation, StgDictObject *dict, Py_ssize_t *pfield_size) +{ + switch (bitfield_allocation) { + /* GCC can continue bitfield if requested size in bits is <= open field size */ + case BITFIELD_ALLOCATION_GCC: + return dict->size * 8 <= *pfield_size; + /* MSVC can continue bitfield if requested size in bits is == open field size */ + case BITFIELD_ALLOCATION_MSVC: + return dict->size * 8 == *pfield_size; + /* Should never reach this - inavlid argument */ + default: + assert(0); + return 0; + } +} + +/* Helper function to determine whether current bitfield can be expanded based on allocation strategy */ +int +CanExpandField(int bitfield_allocation, StgDictObject *dict, Py_ssize_t *pfield_size) +{ + switch (bitfield_allocation) { + /* GCC can expand bitfield if requested size in bits is >= open field size */ + case BITFIELD_ALLOCATION_GCC: + return dict->size * 8 >= *pfield_size; + /* MSVC never expands bitfield */ + case BITFIELD_ALLOCATION_MSVC: + return 0; + /* Should never reach this - invalid argument */ + default: + assert(0); + return 0; + } +} + /* * Expects the size, index and offset for the current field in *psize and * *poffset, stores the total size so far in *psize, the offset for the next @@ -48,7 +84,7 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, Py_ssize_t *pfield_size, int bitsize, int *pbitofs, Py_ssize_t *psize, Py_ssize_t *poffset, Py_ssize_t *palign, - int pack, int big_endian) + int pack, int big_endian, int bitfield_allocation) { CFieldObject *self; PyObject *proto; @@ -62,6 +98,15 @@ #define CONT_BITFIELD 2 #define EXPAND_BITFIELD 3 + /* Native is MSVC on Windows, GCC otherwise */ + if (bitfield_allocation == BITFIELD_ALLOCATION_NATIVE) { +#ifdef MS_WIN32 + bitfield_allocation = BITFIELD_ALLOCATION_MSVC; +#else + bitfield_allocation = BITFIELD_ALLOCATION_GCC; +#endif + } + self = (CFieldObject *)PyObject_CallObject((PyObject *)&PyCField_Type, NULL); if (self == NULL) @@ -75,24 +120,16 @@ } if (bitsize /* this is a bitfield request */ && *pfield_size /* we have a bitfield open */ -#ifdef MS_WIN32 - /* MSVC, GCC with -mms-bitfields */ - && dict->size * 8 == *pfield_size -#else - /* GCC */ - && dict->size * 8 <= *pfield_size -#endif + && CanContinueField(bitfield_allocation, dict, pfield_size) && (*pbitofs + bitsize) <= *pfield_size) { /* continue bit field */ fieldtype = CONT_BITFIELD; -#ifndef MS_WIN32 } else if (bitsize /* this is a bitfield request */ && *pfield_size /* we have a bitfield open */ - && dict->size * 8 >= *pfield_size + && CanExpandField(bitfield_allocation, dict, pfield_size) && (*pbitofs + bitsize) <= dict->size * 8) { /* expand bit field */ fieldtype = EXPAND_BITFIELD; -#endif } else if (bitsize) { /* start new bitfield */ fieldtype = NEW_BITFIELD; diff -r 024827a9db64 Modules/_ctypes/ctypes.h --- a/Modules/_ctypes/ctypes.h Fri Jun 24 20:52:27 2011 +0200 +++ b/Modules/_ctypes/ctypes.h Sun Jul 10 15:16:35 2011 -0700 @@ -129,7 +129,7 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, Py_ssize_t *pfield_size, int bitsize, int *pbitofs, Py_ssize_t *psize, Py_ssize_t *poffset, Py_ssize_t *palign, - int pack, int is_big_endian); + int pack, int is_big_endian, int bitfield_allocation); extern PyObject *PyCData_AtAddress(PyObject *type, void *buf); extern PyObject *PyCData_FromBytes(PyObject *type, char *data, Py_ssize_t length); @@ -366,6 +366,11 @@ extern PyObject *ComError; #endif +/* Bitfield allocation strategies (allocation strategy is compiler specific) */ +#define BITFIELD_ALLOCATION_NATIVE 0 /* MSVC on Windows, GCC otherwise */ +#define BITFIELD_ALLOCATION_GCC 1 /* GCC */ +#define BITFIELD_ALLOCATION_MSVC 2 /* MSVC or GCC with -mms-bitfields flag */ + /* Local Variables: compile-command: "python setup.py -q build install --home ~" diff -r 024827a9db64 Modules/_ctypes/stgdict.c --- a/Modules/_ctypes/stgdict.c Fri Jun 24 20:52:27 2011 +0200 +++ b/Modules/_ctypes/stgdict.c Sun Jul 10 15:16:35 2011 -0700 @@ -310,6 +310,8 @@ int bitofs; PyObject *isPacked; int pack = 0; + PyObject *hasBitfieldAllocation; + int bitfield_allocation = BITFIELD_ALLOCATION_NATIVE; Py_ssize_t ffi_ofs; int big_endian; @@ -346,6 +348,22 @@ } else PyErr_Clear(); + hasBitfieldAllocation = PyObject_GetAttrString(type, "_bitfield_allocation_"); + if (hasBitfieldAllocation) { + bitfield_allocation = PyLong_AsLong(hasBitfieldAllocation); + if ((bitfield_allocation != BITFIELD_ALLOCATION_NATIVE) && + (bitfield_allocation != BITFIELD_ALLOCATION_GCC) && + (bitfield_allocation != BITFIELD_ALLOCATION_MSVC)) { + Py_XDECREF(hasBitfieldAllocation); + PyErr_Format(PyExc_ValueError, + "Invalid _bitfield_allocation_ value '%zd'", + bitfield_allocation); + return -1; + } + Py_DECREF(hasBitfieldAllocation); + } else + PyErr_Clear(); + len = PySequence_Length(fields); if (len == -1) { PyErr_SetString(PyExc_TypeError, @@ -500,7 +518,7 @@ prop = PyCField_FromDesc(desc, i, &field_size, bitsize, &bitofs, &size, &offset, &align, - pack, big_endian); + pack, big_endian, bitfield_allocation); } else /* union */ { size = 0; offset = 0; @@ -508,7 +526,7 @@ prop = PyCField_FromDesc(desc, i, &field_size, bitsize, &bitofs, &size, &offset, &align, - pack, big_endian); + pack, big_endian, bitfield_allocation); union_size = max(size, union_size); } total_align = max(align, total_align);