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