Author: Armin Rigo <[email protected]>
Branch: null_byte_after_str
Changeset: r85950:793c88ca9d2e
Date: 2016-07-31 14:08 +0200
http://bitbucket.org/pypy/pypy/changeset/793c88ca9d2e/
Log: Allow ffi.from_buffer(string). Returns a 'char *' that is valid as
long as the string object is alive.
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
@@ -353,7 +353,7 @@
'array.array' or numpy arrays."""
#
w_ctchara = newtype._new_chara_type(self.space)
- return func.from_buffer(self.space, w_ctchara, w_python_buffer)
+ return func._from_buffer(self.space, w_ctchara, w_python_buffer)
@unwrap_spec(w_arg=W_CData)
diff --git a/pypy/module/_cffi_backend/func.py
b/pypy/module/_cffi_backend/func.py
--- a/pypy/module/_cffi_backend/func.py
+++ b/pypy/module/_cffi_backend/func.py
@@ -1,7 +1,7 @@
from rpython.rtyper.annlowlevel import llstr
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
-from rpython.rlib.objectmodel import keepalive_until_here
+from rpython.rlib.objectmodel import keepalive_until_here, we_are_translated
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.gateway import unwrap_spec, WrappedDefault
@@ -132,17 +132,63 @@
raise oefmt(space.w_TypeError,
"needs 'char[]', got '%s'", w_ctype.name)
#
+ return _from_buffer(space, w_ctype, w_x)
+
+def _from_buffer(space, w_ctype, w_x):
buf = _fetch_as_read_buffer(space, w_x)
- try:
- _cdata = buf.get_raw_address()
- except ValueError:
- raise oefmt(space.w_TypeError,
- "from_buffer() got a '%T' object, which supports the "
- "buffer interface but cannot be rendered as a plain "
- "raw address on PyPy", w_x)
+ if space.isinstance_w(w_x, space.w_str):
+ _cdata = get_raw_address_of_string(space, w_x)
+ else:
+ try:
+ _cdata = buf.get_raw_address()
+ except ValueError:
+ raise oefmt(space.w_TypeError,
+ "from_buffer() got a '%T' object, which supports the "
+ "buffer interface but cannot be rendered as a plain "
+ "raw address on PyPy", w_x)
#
return cdataobj.W_CDataFromBuffer(space, _cdata, w_ctype, buf, w_x)
+# ____________________________________________________________
+
+class RawBytes(object):
+ def __init__(self, string):
+ self.ptr = rffi.str2charp(string, track_allocation=False)
+ def __del__(self):
+ rffi.free_charp(self.ptr, track_allocation=False)
+
+class RawBytesCache(object):
+ def __init__(self, space):
+ from pypy.interpreter.baseobjspace import W_Root
+ from rpython.rlib import rweakref
+ self.wdict = rweakref.RWeakKeyDictionary(W_Root, RawBytes)
+
+def get_raw_address_of_string(space, w_x):
+ """Special case for ffi.from_buffer(string). Returns a 'char *' that
+ is valid as long as the string object is alive. Two calls to
+ ffi.from_buffer(same_string) are guaranteed to return the same pointer.
+ """
+ from rpython.rtyper.annlowlevel import llstr
+ from rpython.rtyper.lltypesystem.rstr import STR
+ from rpython.rtyper.lltypesystem import llmemory
+ from rpython.rlib import rgc
+
+ cache = space.fromcache(RawBytesCache)
+ rawbytes = cache.wdict.get(w_x)
+ if rawbytes is None:
+ data = space.str_w(w_x)
+ if we_are_translated() and not rgc.can_move(data):
+ lldata = llstr(data)
+ data_start = (llmemory.cast_ptr_to_adr(lldata) +
+ rffi.offsetof(STR, 'chars') +
+ llmemory.itemoffsetof(STR.chars, 0))
+ return rffi.cast(rffi.CCHARP, data_start)
+ rawbytes = RawBytes(data)
+ cache.wdict.set(w_x, rawbytes)
+ return rawbytes.ptr
+
+# ____________________________________________________________
+
def unsafe_escaping_ptr_for_ptr_or_array(w_cdata):
if not w_cdata.ctype.is_nonfunc_pointer_or_array:
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py
b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -3330,7 +3330,10 @@
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
BCharA = new_array_type(BCharP, None)
- py.test.raises(TypeError, from_buffer, BCharA, b"foo")
+ p1 = from_buffer(BCharA, b"foo")
+ assert p1 == from_buffer(BCharA, b"foo")
+ import gc; gc.collect()
+ assert p1 == from_buffer(BCharA, b"foo")
py.test.raises(TypeError, from_buffer, BCharA, u+"foo")
try:
from __builtin__ import buffer
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit