[pypy-svn] r25500 - in pypy/dist/pypy/rpython/rctypes: . test

arigo at codespeak.net arigo at codespeak.net
Fri Apr 7 16:08:49 CEST 2006


Author: arigo
Date: Fri Apr  7 16:08:47 2006
New Revision: 25500

Added:
   pypy/dist/pypy/rpython/rctypes/ctypes_platform.py   (contents, props changed)
   pypy/dist/pypy/rpython/rctypes/test/test_ctypes_platform.py
Log:
(arre, arigo)

A ctypes utility that discovers the layout of structures by invoking
the C compiler on example code.



Added: pypy/dist/pypy/rpython/rctypes/ctypes_platform.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/rctypes/ctypes_platform.py	Fri Apr  7 16:08:47 2006
@@ -0,0 +1,190 @@
+#! /usr/bin/env python
+
+import os, py
+import ctypes
+from pypy.translator.tool.cbuild import build_executable
+from pypy.tool.udir import udir
+
+
+def uniquefilepath(LAST=[0]):
+    i = LAST[0]
+    LAST[0] += 1
+    return udir.join('ctypesplatcheck_%d.c' % i)
+
+alignment_types = [
+    ctypes.c_short,
+    ctypes.c_int,
+    ctypes.c_long,
+    ctypes.c_float,
+    ctypes.c_double,
+    ctypes.c_char_p,
+    ctypes.c_void_p,
+    ctypes.c_longlong,
+    ctypes.c_wchar,
+    ctypes.c_wchar_p,
+    ]
+
+integer_class = [ctypes.c_byte,     ctypes.c_ubyte,
+                 ctypes.c_short,    ctypes.c_ushort,
+                 ctypes.c_int,      ctypes.c_uint,
+                 ctypes.c_long,     ctypes.c_ulong,
+                 ctypes.c_longlong, ctypes.c_ulonglong,
+                 ]
+float_class = [ctypes.c_float, ctypes.c_double]
+
+class Field(object):
+    def __init__(self, name, ctype):
+        self.name = name
+        self.ctype = ctype
+    def __repr__(self):
+        return '<field %s: %s>' % (self.name, self.ctype)
+
+def layout_addfield(layout, offset, ctype, prefix):
+    size = ctypes.sizeof(ctype)
+    name = prefix
+    i = 0
+    while name in layout:
+        i += 1
+        name = '%s_%d' % (prefix, i)
+    field = Field(name, ctype)
+    for i in range(offset, offset+size):
+        assert layout[i] is None, "%s overlaps %r" % (fieldname, layout[i])
+        layout[i] = field
+    return field
+
+def size_and_sign(ctype):
+    return (ctypes.sizeof(ctype),
+            ctype in integer_class and ctype(-1).value > 0)
+
+def fixup_ctype(fieldtype, fieldname, expected_size_and_sign):
+    for typeclass in [integer_class, float_class]:
+        if fieldtype in typeclass:
+            for ctype in typeclass:
+                if size_and_sign(ctype) == expected_size_and_sign:
+                    return ctype
+    raise TypeError("conflicting field type %r for %r" % (fieldtype,
+                                                          fieldname))
+
+
+def getstruct(name, c_header_source, interesting_fields):
+    filepath = uniquefilepath()
+    f = filepath.open('w')
+    print >> f, '#include <stdio.h>'
+    print >> f, '#include <stddef.h>   /* for offsetof() */'
+    print >> f
+    print >> f, c_header_source
+    print >> f
+    print >> f, 'typedef %s ctypesplatcheck_t;' % (name,)
+    print >> f, 'typedef struct {'
+    print >> f, '    char c;'
+    print >> f, '    ctypesplatcheck_t s;'
+    print >> f, '} ctypesplatcheck2_t;'
+    print >> f
+    print >> f, 'void dump(char* key, int value) {'
+    print >> f, '    printf("%s: %d\\n", key, value);'
+    print >> f, '}'
+    print >> f
+    print >> f, 'int main(void) {'
+    print >> f, '    ctypesplatcheck_t s;'
+    print >> f, '    dump("align", offsetof(ctypesplatcheck2_t, s));'
+    print >> f, '    dump("size",  sizeof(ctypesplatcheck_t));'
+    for fieldname, fieldtype in interesting_fields:
+        print >> f, '    dump("fldofs %s", offsetof(ctypesplatcheck_t, %s));'%(
+            fieldname, fieldname)
+        print >> f, '    dump("fldsize %s",   sizeof(s.%s));' % (
+            fieldname, fieldname)
+        if fieldtype in integer_class:
+            print >> f, '    s.%s = 0; s.%s = ~s.%s;' % (fieldname,
+                                                         fieldname,
+                                                         fieldname)
+            print >> f, '    dump("fldunsigned %s", s.%s > 0);' % (fieldname,
+                                                                   fieldname)
+    print >> f, '    return 0;'
+    print >> f, '}'
+    f.close()
+
+    executable = build_executable([filepath])
+    output = py.process.cmdexec(executable)
+    info = {}
+    for line in output.splitlines():
+        key, value = line.strip().split(': ')
+        info[key] = int(value)
+
+    alignment = 1
+    layout = [None] * info['size']
+    for fieldname, fieldtype in interesting_fields:
+        offset = info['fldofs '  + fieldname]
+        size   = info['fldsize ' + fieldname]
+        sign   = info.get('fldunsigned ' + fieldname, False)
+        if (size, sign) != size_and_sign(fieldtype):
+            fieldtype = fixup_ctype(fieldtype, fieldname, (size, sign))
+        layout_addfield(layout, offset, fieldtype, fieldname)
+        alignment = max(alignment, ctypes.alignment(fieldtype))
+
+    # try to enforce the same alignment as the one of the original structure
+    if alignment < info['align']:
+        choices = [ctype for ctype in alignment_types
+                         if ctypes.alignment(ctype) == info['align']]
+        assert choices, "unsupported alignment %d" % (info['align'],)
+        choices = [(ctypes.sizeof(ctype), i, ctype)
+                   for i, ctype in enumerate(choices)]
+        csize, _, ctype = min(choices)
+        for i in range(0, info['size'] - csize + 1, info['align']):
+            if layout[i:i+csize] == [None] * csize:
+                layout_addfield(layout, i, ctype, '_alignment')
+                break
+        else:
+            raise AssertionError("unenforceable alignment %d" % (
+                info['align'],))
+
+    n = 0
+    for i, cell in enumerate(layout):
+        if cell is not None:
+            continue
+        layout_addfield(layout, i, ctypes.c_char, '_pad%d' % (n,))
+        n += 1
+
+    # build the ctypes Structure
+    seen = {}
+    fields = []
+    for cell in layout:
+        if cell in seen:
+            continue
+        fields.append((cell.name, cell.ctype))
+        seen[cell] = True
+
+    class S(ctypes.Structure):
+        _fields_ = fields
+    if name.startswith('struct '):
+        name = name[7:]
+    S.__name__ = name
+    return S
+
+
+if __name__ == '__main__':
+    doc = """Example:
+    
+       ctypes_platform.py  -h sys/types.h  -h netinet/in.h
+                           'struct sockaddr_in'
+                           sin_port  c_int
+    """
+    import sys, getopt
+    opts, args = getopt.gnu_getopt(sys.argv[1:], 'h:')
+    if not args:
+        print >> sys.stderr, doc
+        sys.exit(2)
+    assert len(args) % 2 == 1
+    headers = []
+    for opt, value in opts:
+        if opt == '-h':
+            headers.append('#include <%s>' % (value,))
+    name = args[0]
+    fields = []
+    for i in range(1, len(args), 2):
+        ctype = getattr(ctypes, args[i+1])
+        fields.append((args[i], ctype))
+
+    S = getstruct(name, '\n'.join(headers), fields)
+
+    for key, value in S._fields_:
+        print key, value

Added: pypy/dist/pypy/rpython/rctypes/test/test_ctypes_platform.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/rctypes/test/test_ctypes_platform.py	Fri Apr  7 16:08:47 2006
@@ -0,0 +1,85 @@
+import py, sys, struct
+from pypy.rpython.rctypes import ctypes_platform
+import ctypes
+
+
+if sys.platform != 'linux2':
+    py.test.skip("the test must be adapted to your platform")
+
+
+def test_dirent():
+    dirent = ctypes_platform.getstruct("struct dirent",
+                                       """
+           struct dirent  /* for this example only, not the exact dirent */
+           {
+               long d_ino;
+               int d_off;
+               unsigned short d_reclen;
+               char d_name[32];
+           };
+                                       """,
+                                       [("d_reclen", ctypes.c_ushort)])
+    assert issubclass(dirent, ctypes.Structure)
+    ssize = (ctypes.sizeof(ctypes.c_long) +
+             ctypes.sizeof(ctypes.c_int) +
+             ctypes.sizeof(ctypes.c_ushort) +
+             32)
+    extra_padding = (-ssize) % ctypes.alignment(ctypes.c_long)
+
+    assert dirent._fields_ == [('_alignment', ctypes.c_long),
+                               ('_pad0', ctypes.c_char),
+                               ('_pad1', ctypes.c_char),
+                               ('_pad2', ctypes.c_char),
+                               ('_pad3', ctypes.c_char),
+                               ('d_reclen', ctypes.c_ushort),
+                               ] + [
+                               ('_pad%d' % n, ctypes.c_char)
+                                    for n in range(4, 4+32+extra_padding)]
+    assert ctypes.sizeof(dirent) == ssize + extra_padding
+    assert ctypes.alignment(dirent) == ctypes.alignment(ctypes.c_long)
+
+def test_fit_type():
+    S = ctypes_platform.getstruct("struct S",
+                                  """
+           struct S {
+               signed char c;
+               unsigned char uc;
+               short s;
+               unsigned short us;
+               int i;
+               unsigned int ui;
+               long l;
+               unsigned long ul;
+               long long ll;
+               unsigned long long ull;
+               float f;
+               double d;
+           };
+                                  """,
+                                  [("c",   ctypes.c_int),
+                                   ("uc",  ctypes.c_int),
+                                   ("s",   ctypes.c_uint),
+                                   ("us",  ctypes.c_int),
+                                   ("i",   ctypes.c_int),
+                                   ("ui",  ctypes.c_int),
+                                   ("l",   ctypes.c_int),
+                                   ("ul",  ctypes.c_int),
+                                   ("ll",  ctypes.c_int),
+                                   ("ull", ctypes.c_int),
+                                   ("f",   ctypes.c_double),
+                                   ("d",   ctypes.c_float)])
+    assert issubclass(S, ctypes.Structure)
+    fields = dict(S._fields_)
+    assert fields["c"] == ctypes.c_byte
+    assert fields["uc"] == ctypes.c_ubyte
+    assert fields["s"] == ctypes.c_short
+    assert fields["us"] == ctypes.c_ushort
+    assert fields["i"] == ctypes.c_int
+    assert fields["ui"] == ctypes.c_uint
+    assert fields["l"] == ctypes.c_long
+    assert fields["ul"] == ctypes.c_ulong
+    assert fields["ll"] == ctypes.c_longlong
+    assert fields["ull"] == ctypes.c_ulonglong
+    assert fields["f"] == ctypes.c_float
+    assert fields["d"] == ctypes.c_double
+



More information about the Pypy-commit mailing list