Author: Devin Jeanpierre <[email protected]>
Branch: cpyext-old-buffers
Changeset: r84531:546354142cce
Date: 2016-05-20 08:30 -0700
http://bitbucket.org/pypy/pypy/changeset/546354142cce/
Log: Expose memory leaked by cpyext's buffer -> PyBufferObject to
RPython.
This will let me avoid copying it again inside the implementation of
bf_getreadbuffer etc.
diff --git a/pypy/module/cpyext/bufferobject.py
b/pypy/module/cpyext/bufferobject.py
--- a/pypy/module/cpyext/bufferobject.py
+++ b/pypy/module/cpyext/bufferobject.py
@@ -1,4 +1,4 @@
-from rpython.rlib.buffer import StringBuffer, SubBuffer
+from rpython.rlib.buffer import Buffer, StringBuffer, SubBuffer
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.interpreter.error import oefmt
from pypy.module.cpyext.api import (
@@ -9,6 +9,54 @@
from pypy.objspace.std.bufferobject import W_Buffer
+class LeakedBuffer(Buffer):
+ __slots__ = ['buf','ptr']
+ _immutable_ = True
+
+ def __init__(self, buffer):
+ if not buffer.readonly:
+ raise ValueError("Can only leak a copy of a readonly buffer.")
+ self.buf = buffer
+ self.readonly = True
+ self.ptr = rffi.cast(rffi.VOIDP, rffi.str2charp(self.buf.as_str()))
+
+ def getlength(self):
+ return self.buf.getlength()
+
+ def as_str(self):
+ return self.buf.as_str()
+
+ def as_str_and_offset_maybe(self):
+ return self.buf.as_str_and_offset_maybe()
+
+ def getitem(self, index):
+ return self.buf.getitem(index)
+
+ def getslice(self, start, stop, step, size):
+ return self.buf.getslice(start, stop, step, size)
+
+ def setitem(self, index, char):
+ return self.buf.setitem(index)
+
+ def setslice(self, start, string):
+ return self.buf.setslice(start, string)
+
+ def get_raw_address(self):
+ return self.ptr
+
+
+def leak_stringbuffer(buf):
+ if isinstance(buf, StringBuffer):
+ return LeakedBuffer(buf)
+ elif isinstance(buf, SubBuffer):
+ leaked = leak_stringbuffer(buf.buffer)
+ if leaked is None:
+ return leaked
+ return SubBuffer(leaked, buf.offset, buf.size)
+ else:
+ return None
+
+
PyBufferObjectStruct = lltype.ForwardReference()
PyBufferObject = lltype.Ptr(PyBufferObjectStruct)
PyBufferObjectFields = PyObjectFields + (
@@ -43,17 +91,19 @@
assert isinstance(w_obj, W_Buffer)
buf = w_obj.buf
+ w_obj.buf = buf = leak_stringbuffer(buf) or buf
+ # Now, if it was backed by a StringBuffer, it is now a LeakedBuffer.
+ # We deliberately copy the string so that we can have a pointer to it,
+ # and we make it accessible in the buffer through get_raw_address(), so
that
+ # we can reuse it elsewhere in the C API.
+
if isinstance(buf, SubBuffer):
py_buf.c_b_offset = buf.offset
buf = buf.buffer
- # If buf already allocated a fixed buffer, use it, and keep a
- # reference to buf.
- # Otherwise, b_base stays NULL, and we own the b_ptr.
-
- if isinstance(buf, StringBuffer):
+ if isinstance(buf, LeakedBuffer):
py_buf.c_b_base = lltype.nullptr(PyObject.TO)
- py_buf.c_b_ptr = rffi.cast(rffi.VOIDP, rffi.str2charp(buf.value))
+ py_buf.c_b_ptr = buf.get_raw_address()
py_buf.c_b_size = buf.getlength()
elif isinstance(buf, ArrayBuffer):
w_base = buf.array
diff --git a/pypy/module/cpyext/test/test_bufferobject.py
b/pypy/module/cpyext/test/test_bufferobject.py
--- a/pypy/module/cpyext/test/test_bufferobject.py
+++ b/pypy/module/cpyext/test/test_bufferobject.py
@@ -1,6 +1,8 @@
+from rpython.rlib.buffer import StringBuffer, SubBuffer
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+from pypy.module.cpyext.bufferobject import leak_stringbuffer
from pypy.module.cpyext.api import PyObject
from pypy.module.cpyext.pyobject import Py_DecRef
@@ -63,4 +65,34 @@
a = array.array('c', 'text')
b = buffer(a)
assert module.roundtrip(b) == 'text'
-
+
+
+def test_leaked_buffer():
+ s = 'hello world'
+ buf = leak_stringbuffer(StringBuffer(s))
+ assert buf.getitem(4) == 'o'
+ assert buf.getitem(4) == buf[4]
+ assert buf.getlength() == 11
+ assert buf.getlength() == len(buf)
+ assert buf.getslice(1, 6, 1, 5) == 'ello '
+ assert buf.getslice(1, 6, 1, 5) == buf[1:6]
+ assert buf.getslice(1, 6, 2, 3) == 'el '
+ assert buf.as_str() == 'hello world'
+ assert s == rffi.charp2str(buf.get_raw_address())
+ rffi.free_charp(buf.get_raw_address())
+
+
+def test_leaked_subbuffer():
+ s = 'hello world'
+ buf = leak_stringbuffer(SubBuffer(StringBuffer(s), 1, 10))
+ assert buf.getitem(4) == ' '
+ assert buf.getitem(4) == buf[4]
+ assert buf.getlength() == 10
+ assert buf.getlength() == len(buf)
+ assert buf.getslice(1, 6, 1, 5) == 'llo w'
+ assert buf.getslice(1, 6, 1, 5) == buf[1:6]
+ assert buf.getslice(1, 6, 2, 3) == 'low'
+ assert buf.as_str() == 'ello world'
+ assert s[1:] == rffi.charp2str(buf.get_raw_address())
+ rffi.free_charp(buf.buffer.get_raw_address())
+
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit