Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit