[pypy-commit] pypy py3.5: Bug-for-bug compatibility (and performance optimisation) in BufferedReader.readinto1()

rlamy pypy.commits at gmail.com
Mon Nov 20 13:35:20 EST 2017


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3.5
Changeset: r93102:feaba8e9bb0a
Date: 2017-11-20 18:34 +0000
http://bitbucket.org/pypy/pypy/changeset/feaba8e9bb0a/

Log:	Bug-for-bug compatibility (and performance optimisation) in
	BufferedReader.readinto1()

diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -111,14 +111,15 @@
         self._unsupportedoperation(space, "detach")
 
     def readinto_w(self, space, w_buffer):
-        return self._readinto(space, w_buffer, "read")
+        return self._readinto(space, w_buffer, read_once=False)
 
     def readinto1_w(self, space, w_buffer):
-        return self._readinto(space, w_buffer, "read1")
+        return self._readinto(space, w_buffer, read_once=True)
 
-    def _readinto(self, space, w_buffer, methodname):
+    def _readinto(self, space, w_buffer, read_once):
         rwbuffer = space.writebuf_w(w_buffer)
         length = rwbuffer.getlength()
+        methodname = "read1" if read_once else "read"
         w_data = space.call_method(self, methodname, space.newint(length))
 
         if not space.isinstance_w(w_data, space.w_bytes):
@@ -882,6 +883,52 @@
         self._reader_reset_buf()
         self.state = STATE_OK
 
+    def _readinto(self, space, w_buffer, read_once):
+        rwbuffer = space.writebuf_w(w_buffer)
+        length = rwbuffer.getlength()
+        with self.lock:
+            have = self._readahead()
+            if have >= length:
+                rwbuffer.setslice(0, self.buffer[self.pos:self.pos + length])
+                return space.newint(length)
+            written = 0
+            if have > 0:
+                rwbuffer.setslice(0, self.buffer[self.pos:self.read_end])
+                written = have
+
+            while written < length:
+                if self.writable:
+                    self._flush_and_rewind_unlocked(space)
+                self._reader_reset_buf()
+                self.pos = 0
+                if written + len(self.buffer) < length:
+                    try:
+                        got = self._raw_read(space, rwbuffer, written, length - written)
+                        written += got
+                    except BlockingIOError:
+                        got = 0
+                    if got == 0:
+                        break
+                elif read_once and written:
+                    break
+                else:
+                    try:
+                        have = self._fill_buffer(space)
+                    except BlockingIOError:
+                        have = 0
+                    if have == 0:
+                        break
+                    endpos = min(have, length - written)
+                    assert endpos >= 0
+                    rwbuffer.setslice(written, self.buffer[0:endpos])
+                    written += endpos
+                    self.pos = endpos
+                if read_once:
+                    break
+            return space.newint(written)
+
+
+
 W_BufferedReader.typedef = TypeDef(
     '_io.BufferedReader', W_BufferedIOBase.typedef,
     __new__ = generic_new_descr(W_BufferedReader),
diff --git a/pypy/module/_io/test/test_bufferedio.py b/pypy/module/_io/test/test_bufferedio.py
--- a/pypy/module/_io/test/test_bufferedio.py
+++ b/pypy/module/_io/test/test_bufferedio.py
@@ -189,6 +189,31 @@
         b = bytearray(2)
         raises(ValueError, bufio.readinto, b)
 
+    def test_readinto1(self):
+        import _io
+
+        class MockIO(_io._IOBase):
+            def readable(self):
+                return True
+
+            def readinto(self, buf):
+                buf[:3] = b"abc"
+                return 3
+        bufio = _io.BufferedReader(MockIO(), buffer_size=5)
+        buf = bytearray(10)
+        bufio.read(2)
+        n = bufio.readinto1(buf)
+        assert n == 4
+        assert buf[:n] == b'cabc'
+
+        # Yes, CPython's observable behavior depends on buffer_size!
+        bufio = _io.BufferedReader(MockIO(), buffer_size=20)
+        buf = bytearray(10)
+        bufio.read(2)
+        n = bufio.readinto1(buf)
+        assert n == 1
+        assert buf[:n] == b'c'
+
     def test_seek(self):
         import _io
         raw = _io.FileIO(self.tmpfile)


More information about the pypy-commit mailing list