Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r1453:c5e17441bc96 Date: 2014-01-14 09:06 +0100 http://bitbucket.org/cffi/cffi/changeset/c5e17441bc96/
Log: Issue 131: support ffi.cdef("...", packed=True) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -3586,6 +3586,7 @@ #define SF_MSVC_BITFIELDS 1 #define SF_GCC_ARM_BITFIELDS 2 #define SF_GCC_BIG_ENDIAN 4 +#define SF_PACKED 8 static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) { @@ -3671,8 +3672,8 @@ boffset = 0; /* reset each field at offset 0 */ /* update the total alignment requirement, but skip it if the - field is an anonymous bitfield */ - falign = get_alignment(ftype); + field is an anonymous bitfield or if SF_PACKED */ + falign = (sflags & SF_PACKED) ? 1 : get_alignment(ftype); if (falign < 0) goto error; diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -3148,6 +3148,34 @@ p = newp(BArray, None) assert sizeof(p[2:9]) == 7 * sizeof(BInt) +def test_packed(): + BLong = new_primitive_type("long") + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BLong, -1), + ('a2', BChar, -1), + ('a3', BShort, -1)], + None, -1, -1, 8) # SF_PACKED==8 + d = BStruct.fields + assert len(d) == 3 + assert d[0][0] == 'a1' + assert d[0][1].type is BLong + assert d[0][1].offset == 0 + assert d[0][1].bitshift == -1 + assert d[0][1].bitsize == -1 + assert d[1][0] == 'a2' + assert d[1][1].type is BChar + assert d[1][1].offset == sizeof(BLong) + assert d[1][1].bitshift == -1 + assert d[1][1].bitsize == -1 + assert d[2][0] == 'a3' + assert d[2][1].type is BShort + assert d[2][1].offset == sizeof(BLong) + sizeof(BChar) + assert d[2][1].bitshift == -1 + assert d[2][1].bitsize == -1 + assert sizeof(BStruct) == sizeof(BLong) + sizeof(BChar) + sizeof(BShort) + assert alignof(BStruct) == 1 def test_version(): # this test is here mostly for PyPy diff --git a/cffi/api.py b/cffi/api.py --- a/cffi/api.py +++ b/cffi/api.py @@ -88,18 +88,20 @@ self.NULL = self.cast(self.BVoidP, 0) self.CData, self.CType = backend._get_types() - def cdef(self, csource, override=False): + def cdef(self, csource, override=False, packed=False): """Parse the given C source. This registers all declared functions, types, and global variables. The functions and global variables can then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. The types can be used in 'ffi.new()' and other functions. + If 'packed' is specified as True, all structs declared inside this + cdef are packed, i.e. laid out without any field alignment at all. """ if not isinstance(csource, str): # unicode, on Python 2 if not isinstance(csource, basestring): raise TypeError("cdef() argument must be a string") csource = csource.encode('ascii') with self._lock: - self._parser.parse(csource, override=override) + self._parser.parse(csource, override=override, packed=packed) self._cdefsources.append(csource) if override: for cache in self._function_caches: diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py --- a/cffi/backend_ctypes.py +++ b/cffi/backend_ctypes.py @@ -720,7 +720,7 @@ return self._new_struct_or_union('union', name, ctypes.Union) def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, - totalsize=-1, totalalignment=-1): + totalsize=-1, totalalignment=-1, sflags=0): if totalsize >= 0 or totalalignment >= 0: raise NotImplementedError("the ctypes backend of CFFI does not support " "structures completed by verify(); please " @@ -739,6 +739,8 @@ else: cfields.append((fname, BField._ctype, bitsize)) bfield_types[fname] = Ellipsis + if sflags & 8: + struct_or_union._pack_ = 1 struct_or_union._fields_ = cfields CTypesStructOrUnion._bfield_types = bfield_types # diff --git a/cffi/cparser.py b/cffi/cparser.py --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -98,6 +98,7 @@ self._anonymous_counter = 0 self._structnode2type = weakref.WeakKeyDictionary() self._override = False + self._packed = False def _parse(self, csource): csource, macros = _preprocess(csource) @@ -147,13 +148,16 @@ msg = 'parse error\n%s' % (msg,) raise api.CDefError(msg) - def parse(self, csource, override=False): + def parse(self, csource, override=False, packed=False): prev_override = self._override + prev_packed = self._packed try: self._override = override + self._packed = packed self._internal_parse(csource) finally: self._override = prev_override + self._packed = prev_packed def _internal_parse(self, csource): ast, macros = self._parse(csource) @@ -476,6 +480,7 @@ if isinstance(tp, model.StructType) and tp.partial: raise NotImplementedError("%s: using both bitfields and '...;'" % (tp,)) + tp.packed = self._packed return tp def _make_partial(self, tp, nested): diff --git a/cffi/model.py b/cffi/model.py --- a/cffi/model.py +++ b/cffi/model.py @@ -255,6 +255,7 @@ fixedlayout = None completed = False partial = False + packed = False def __init__(self, name, fldnames, fldtypes, fldbitsize): self.name = name @@ -311,7 +312,11 @@ fldtypes = [tp.get_cached_btype(ffi, finishlist) for tp in self.fldtypes] lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) - ffi._backend.complete_struct_or_union(BType, lst, self) + sflags = 0 + if self.packed: + sflags = 8 # SF_PACKED + ffi._backend.complete_struct_or_union(BType, lst, self, + -1, -1, sflags) # else: fldtypes = [] diff --git a/doc/source/index.rst b/doc/source/index.rst --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -851,6 +851,14 @@ ``ffi`` normally caches the string ``"int[]"`` to not need to re-parse it all the time. +.. versionadded:: 0.9 + The ``ffi.cdef()`` call takes an optional argument ``packed``: if + True, then all structs declared within this cdef are "packed". This + has a meaning similar to ``__attribute__((packed))`` in GCC. It + specifies that all structure fields should have an alignment of one + byte. (Note that the packed attribute has no effect on bit fields so + far, which mean that they may be packed differently than on GCC.) + Python 3 support ---------------- diff --git a/testing/backend_tests.py b/testing/backend_tests.py --- a/testing/backend_tests.py +++ b/testing/backend_tests.py @@ -1549,3 +1549,21 @@ ffi2.include(ffi1) p = ffi2.new("foo_p", [142]) assert p.x == 142 + + def test_struct_packed(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef("struct nonpacked { char a; int b; };") + ffi.cdef("struct is_packed { char a; int b; };", packed=True) + assert ffi.sizeof("struct nonpacked") == 8 + assert ffi.sizeof("struct is_packed") == 5 + assert ffi.alignof("struct nonpacked") == 4 + assert ffi.alignof("struct is_packed") == 1 + s = ffi.new("struct is_packed[2]") + s[0].b = 42623381 + s[0].a = 'X' + s[1].b = -4892220 + s[1].a = 'Y' + assert s[0].b == 42623381 + assert s[0].a == 'X' + assert s[1].b == -4892220 + assert s[1].a == 'Y' _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit