Author: Armin Rigo <[email protected]>
Branch: cffi-new-allocator
Changeset: r78446:ceb59c88b235
Date: 2015-07-06 10:14 +0200
http://bitbucket.org/pypy/pypy/changeset/ceb59c88b235/

Log:    Non-standard allocators

diff --git a/pypy/module/_cffi_backend/allocator.py 
b/pypy/module/_cffi_backend/allocator.py
--- a/pypy/module/_cffi_backend/allocator.py
+++ b/pypy/module/_cffi_backend/allocator.py
@@ -9,19 +9,50 @@
 class W_Allocator(W_Root):
     _immutable_ = True
 
-    def __init__(self, ffi, should_clear_after_alloc):
+    def __init__(self, ffi, w_alloc, w_free, should_clear_after_alloc):
         self.ffi = ffi
+        if w_alloc is not None and ffi.space.is_none(w_alloc): w_alloc = None
+        if w_free  is not None and ffi.space.is_none(w_free):  w_free = None
+        self.w_alloc = w_alloc
+        self.w_free = w_free
         self.should_clear_after_alloc = should_clear_after_alloc
 
     def allocate(self, space, datasize, ctype, length=-1):
-        from pypy.module._cffi_backend.cdataobj import W_CDataNewStd
-        if self.should_clear_after_alloc:
-            ptr = lltype.malloc(rffi.CCHARP.TO, datasize,
-                                flavor='raw', zero=True)
+        from pypy.module._cffi_backend import cdataobj, ctypeptr, ffi_obj
+        if self.w_alloc is None:
+            if self.should_clear_after_alloc:
+                ptr = lltype.malloc(rffi.CCHARP.TO, datasize,
+                                    flavor='raw', zero=True)
+            else:
+                ptr = lltype.malloc(rffi.CCHARP.TO, datasize,
+                                    flavor='raw', zero=False)
+            return cdataobj.W_CDataNewStd(space, ptr, ctype, length)
         else:
-            ptr = lltype.malloc(rffi.CCHARP.TO, datasize,
-                                flavor='raw', zero=False)
-        return W_CDataNewStd(space, ptr, ctype, length)
+            w_raw_cdata = space.call_function(self.w_alloc,
+                                              space.wrap(datasize))
+            if not isinstance(w_raw_cdata, cdataobj.W_CData):
+                raise oefmt(ffi_obj.get_ffi_error(space),
+                            "expected cdata object from the call to %R, "
+                            "got '%T'", self.w_alloc, w_raw_cdata)
+            if not isinstance(w_raw_cdata.ctype, ctypeptr.W_CTypePtrOrArray):
+                raise oefmt(ffi_obj.get_ffi_error(space),
+                            "expected cdata pointer from the call to %R, "
+                            "got '%s'", self.w_alloc, w_raw_cdata.ctype.name)
+            #
+            ptr = w_raw_cdata.unsafe_escaping_ptr()
+            if self.should_clear_after_alloc:
+                rffi.c_memset(rffi.cast(rffi.VOIDP, ptr), 0,
+                              rffi.cast(rffi.SIZE_T, datasize))
+            #
+            if self.w_free is None:
+                # use this class which does not have a __del__, but still
+                # keeps alive w_raw_cdata
+                res = cdataobj.W_CDataNewNonStdNoFree(space, ptr, ctype, 
length)
+            else:
+                res = cdataobj.W_CDataNewNonStdFree(space, ptr, ctype, length)
+                res.w_free = self.w_free
+            res.w_raw_cdata = w_raw_cdata
+            return res
 
     @unwrap_spec(w_init=WrappedDefault(None))
     def descr_call(self, space, w_arg, w_init):
@@ -44,5 +75,5 @@
 W_Allocator.typedef.acceptable_as_base_class = False
 
 
-default_allocator = W_Allocator(ffi=None, should_clear_after_alloc=True)
-nonzero_allocator = W_Allocator(ffi=None, should_clear_after_alloc=False)
+default_allocator = W_Allocator(None, None, None, 
should_clear_after_alloc=True)
+nonzero_allocator = W_Allocator(None, None, 
None,should_clear_after_alloc=False)
diff --git a/pypy/module/_cffi_backend/cdataobj.py 
b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -417,11 +417,16 @@
         lltype.free(self._ptr, flavor='raw')
 
 
-class W_CDataMemNonStd(W_CDataNewOwning):
-    """Subclass using a non-standard allocator"""
-    _attrs_ = []
+class W_CDataNewNonStdNoFree(W_CDataNewOwning):
+    """Subclass using a non-standard allocator, no free()"""
+    _attrs_ = ['w_raw_cdata']
 
-    # XXXXXXXXX
+class W_CDataNewNonStdFree(W_CDataNewNonStdNoFree):
+    """Subclass using a non-standard allocator, with a free()"""
+    _attrs_ = ['w_free']
+
+    def __del__(self):
+        self.space.call_function(self.w_free, self.w_raw_cdata)
 
 
 class W_CDataPtrToStructOrUnion(W_CData):
diff --git a/pypy/module/_cffi_backend/ffi_obj.py 
b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -422,13 +422,30 @@
         return w_ctype.newp(w_init, default_allocator)
 
 
-    @unwrap_spec(should_clear_after_alloc=int)
-    def descr_new_allocator(self, should_clear_after_alloc=1):
+    @unwrap_spec(w_alloc=WrappedDefault(None),
+                 w_free=WrappedDefault(None),
+                 should_clear_after_alloc=int)
+    def descr_new_allocator(self, w_alloc, w_free,
+                            should_clear_after_alloc=1):
         """\
-Return a new allocator. Xxx
+Return a new allocator.
+
+'alloc' is called with the size as argument.  If it returns NULL, a
+MemoryError is raised.  'free' is called with the result of 'alloc'
+as argument.  Both can be either Python function or directly C
+functions.  If 'free' is explicitly set to None, then no free function
+is called.
+
+If both 'alloc' and 'free' are None, the default is used.
+If 'should_clear_after_alloc' is set to False, then the memory
+returned by 'alloc' is assumed to be already cleared; otherwise
+CFFI will clear it.
         """
         #
-        alloc = W_Allocator(self, bool(should_clear_after_alloc))
+        if self.space.is_none(w_alloc) and not self.space.is_none(w_free):
+            raise oefmt(self.w_TypeError, "cannot give free without alloc")
+        alloc = W_Allocator(self, w_alloc, w_free,
+                            bool(should_clear_after_alloc))
         return self.space.wrap(alloc)
 
 
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py 
b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -296,3 +296,46 @@
         else:
             raise AssertionError("cannot seem to get an int[10] not "
                                  "completely cleared")
+
+    def test_ffi_new_allocator_2(self):
+        import _cffi_backend as _cffi1_backend
+        ffi = _cffi1_backend.FFI()
+        seen = []
+        def myalloc(size):
+            seen.append(size)
+            return ffi.new("char[]", "X" * size)
+        def myfree(raw):
+            seen.append('free')
+        alloc1 = ffi.new_allocator(myalloc, myfree)
+        alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree,
+                                   should_clear_after_alloc=False)
+        p1 = alloc1("int[10]")
+        p2 = alloc2("int[]", 10)
+        assert seen == [40, 40]
+        assert ffi.typeof(p1) == ffi.typeof("int[10]")
+        assert ffi.sizeof(p1) == 40
+        assert ffi.typeof(p2) == ffi.typeof("int[]")
+        assert ffi.sizeof(p2) == 40
+        assert p1[5] == 0
+        assert p2[6] != 0
+        del p1, p2
+        retries = 0
+        while len(seen) != 4:
+            retries += 1
+            assert retries <= 5
+            import gc; gc.collect()
+        assert seen == [40, 40, 'free', 'free']
+
+    def test_ffi_new_allocator_3(self):
+        import _cffi_backend as _cffi1_backend
+        ffi = _cffi1_backend.FFI()
+        seen = []
+        def myalloc(size):
+            seen.append(size)
+            return ffi.new("char[]", "X" * size)
+        alloc1 = ffi.new_allocator(myalloc)    # no 'free'
+        p1 = alloc1("int[10]")
+        assert seen == [40]
+        assert ffi.typeof(p1) == ffi.typeof("int[10]")
+        assert ffi.sizeof(p1) == 40
+        assert p1[5] == 0
diff --git a/rpython/rtyper/lltypesystem/rffi.py 
b/rpython/rtyper/lltypesystem/rffi.py
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -1249,3 +1249,8 @@
             lltype.Void,
             releasegil=False
         )
+c_memset = llexternal("memset",
+            [VOIDP, lltype.Signed, SIZE_T],
+            lltype.Void,
+            releasegil=False
+        )
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to