[pypy-commit] pypy ffistruct: make it possible to allocate a struct, and set/get fields on it. The only supported type is 'long' so far

antocuni noreply at buildbot.pypy.org
Wed Sep 7 14:55:52 CEST 2011


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: ffistruct
Changeset: r47133:63d9d0f04f6a
Date: 2011-09-07 11:48 +0200
http://bitbucket.org/pypy/pypy/changeset/63d9d0f04f6a/

Log:	make it possible to allocate a struct, and set/get fields on it.
	The only supported type is 'long' so far

diff --git a/pypy/module/_ffi/interp_struct.py b/pypy/module/_ffi/interp_struct.py
--- a/pypy/module/_ffi/interp_struct.py
+++ b/pypy/module/_ffi/interp_struct.py
@@ -1,10 +1,10 @@
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.rlib import clibffi
 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.interpreter.typedef import TypeDef, interp_attrproperty
 from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.objspace.std.typetype import type_typedef
-from pypy.module._ffi.interp_ffitype import W_FFIType
+from pypy.module._ffi.interp_ffitype import W_FFIType, app_types
 
 class W_Field(Wrappable):
 
@@ -30,12 +30,15 @@
 
 # ==============================================================================
 
-
 class W__StructDescr(Wrappable):
 
-    def __init__(self, name, ffistruct):
+    def __init__(self, name, fields_w, ffistruct):
         self.ffistruct = ffistruct
-        self.ffitype = W_FFIType('struct %s' % name, ffistruct.ffistruct, 'fixme')
+        self.w_ffitype = W_FFIType('struct %s' % name, ffistruct.ffistruct, 'fixme')
+        self.fields_w = fields_w
+        self.name2w_field = {}
+        for w_field in fields_w:
+            self.name2w_field[w_field.name] = w_field
 
     @staticmethod
     @unwrap_spec(name=str)
@@ -51,7 +54,15 @@
             field_types.append(w_field.w_ffitype.ffitype)
         #
         ffistruct = clibffi.make_struct_ffitype_e(size, alignment, field_types)
-        return W__StructDescr(name, ffistruct)
+        return W__StructDescr(name, fields_w, ffistruct)
+
+    def allocate(self, space):
+        return W__StructInstance(self)
+
+    #@jit.elidable...
+    def get_type_and_offset_for_field(self, name):
+        w_field = self.name2w_field[name]
+        return w_field.w_ffitype, w_field.offset
 
     def __del__(self):
         if self.ffistruct:
@@ -61,6 +72,60 @@
 W__StructDescr.typedef = TypeDef(
     '_StructDescr',
     __new__ = interp2app(W__StructDescr.descr_new),
-    ffitype = interp_attrproperty('ffitype', W__StructDescr),
+    ffitype = interp_attrproperty('w_ffitype', W__StructDescr),
+    allocate = interp2app(W__StructDescr.allocate),
     )
 
+
+# ==============================================================================
+
+class W__StructInstance(Wrappable):
+
+    _immutable_fields_ = ['structdescr', 'rawmem']
+
+    def __init__(self, structdescr):
+        self.structdescr = structdescr
+        size = structdescr.w_ffitype.sizeof()
+        self.rawmem = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw',
+                                    zero=True, add_memory_pressure=True)
+
+    def __del__(self):
+        if self.rawmem:
+            lltype.free(self.rawmem, flavor='raw')
+            self.rawmem = lltype.nullptr(rffi.VOIDP.TO)
+
+    def getaddr(self, space):
+        addr = rffi.cast(rffi.ULONG, self.rawmem)
+        return space.wrap(addr)
+
+    @unwrap_spec(name=str)
+    def getfield(self, space, name):
+        w_ffitype, offset = self.structdescr.get_type_and_offset_for_field(name)
+        assert w_ffitype is app_types.slong # XXX: handle all cases
+        FIELD_TYPE  = rffi.LONG
+        #
+        addr = rffi.ptradd(self.rawmem, offset)
+        PTR_FIELD = lltype.Ptr(rffi.CArray(FIELD_TYPE))
+        value = rffi.cast(PTR_FIELD, addr)[0]
+        #
+        return space.wrap(value)
+
+    @unwrap_spec(name=str)
+    def setfield(self, space, name, w_value):
+        w_ffitype, offset = self.structdescr.get_type_and_offset_for_field(name)
+        assert w_ffitype is app_types.slong # XXX: handle all cases
+        FIELD_TYPE  = rffi.LONG
+        value = space.int_w(w_value)
+        #
+        addr = rffi.ptradd(self.rawmem, offset)
+        PTR_FIELD = lltype.Ptr(rffi.CArray(FIELD_TYPE))
+        rffi.cast(PTR_FIELD, addr)[0] = value
+
+
+
+W__StructInstance.typedef = TypeDef(
+    '_StructInstance',
+    getaddr  = interp2app(W__StructInstance.getaddr),
+    getfield = interp2app(W__StructInstance.getfield),
+    setfield = interp2app(W__StructInstance.setfield),
+    )
diff --git a/pypy/module/_ffi/test/test_struct.py b/pypy/module/_ffi/test/test_struct.py
--- a/pypy/module/_ffi/test/test_struct.py
+++ b/pypy/module/_ffi/test/test_struct.py
@@ -2,6 +2,20 @@
 
 class AppTestStruct(BaseAppTestFFI):
 
+    def setup_class(cls):
+        BaseAppTestFFI.setup_class.im_func(cls)
+        #
+        def read_raw_mem(self, addr, typename, length):
+            import ctypes
+            addr = ctypes.cast(addr, ctypes.c_void_p)
+            c_type = getattr(ctypes, typename)
+            array_type = ctypes.POINTER(c_type * length)
+            ptr_array = ctypes.cast(addr, array_type)
+            array = ptr_array[0]
+            lst = [array[i] for i in range(length)]
+            return lst
+        cls.w_read_raw_mem = cls.space.wrap(read_raw_mem)
+        
     def test__StructDescr(self):
         from _ffi import _StructDescr, Field, types
         longsize = types.slong.sizeof()
@@ -28,3 +42,19 @@
         assert Point.y.offset == longsize
         assert Point._struct_.ffitype.sizeof() == longsize*2
         assert Point._struct_.ffitype.name == 'struct Point'
+
+    def test_getfield_setfield(self):
+        from _ffi import _StructDescr, Field, types
+        longsize = types.slong.sizeof()
+        fields = [
+            Field('x', types.slong),
+            Field('y', types.slong),
+            ]
+        descr = _StructDescr('foo', fields)
+        struct = descr.allocate()
+        struct.setfield('x', 42)
+        struct.setfield('y', 43)
+        assert struct.getfield('x') == 42
+        assert struct.getfield('y') == 43
+        mem = self.read_raw_mem(struct.getaddr(), 'c_long', 2)
+        assert mem == [42, 43]


More information about the pypy-commit mailing list