summaryrefslogtreecommitdiffstats
path: root/Lib/_pyio.py
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2014-06-08 03:06:48 (GMT)
committerBenjamin Peterson <benjamin@python.org>2014-06-08 03:06:48 (GMT)
commit10e847bbc7427e85f38191298275eaf14be625e5 (patch)
treebe494f64eebfc2fdc0be613ddd0d73b2fe0033d0 /Lib/_pyio.py
parent66f80e92c31cdcede9951d661b3538d862f632b1 (diff)
downloadcpython-10e847bbc7427e85f38191298275eaf14be625e5.zip
cpython-10e847bbc7427e85f38191298275eaf14be625e5.tar.gz
cpython-10e847bbc7427e85f38191298275eaf14be625e5.tar.bz2
add BufferedIOBase.readinto1 (closes #20578)
Patch by Nikolaus Rath.
Diffstat (limited to 'Lib/_pyio.py')
-rw-r--r--Lib/_pyio.py83
1 files changed, 82 insertions, 1 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index b04d23a..a53f281 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -655,8 +655,26 @@ class BufferedIOBase(IOBase):
Raises BlockingIOError if the underlying raw stream has no
data at the moment.
"""
+
+ return self._readinto(b, read1=False)
+
+ def readinto1(self, b):
+ """Read up to len(b) bytes into *b*, using at most one system call
+
+ Returns an int representing the number of bytes read (0 for EOF).
+
+ Raises BlockingIOError if the underlying raw stream has no
+ data at the moment.
+ """
+
+ return self._readinto(b, read1=True)
+
+ def _readinto(self, b, read1):
# XXX This ought to work with anything that supports the buffer API
- data = self.read(len(b))
+ if read1:
+ data = self.read1(len(b))
+ else:
+ data = self.read(len(b))
n = len(data)
try:
b[:n] = data
@@ -1058,6 +1076,62 @@ class BufferedReader(_BufferedIOMixin):
return self._read_unlocked(
min(size, len(self._read_buf) - self._read_pos))
+ # Implementing readinto() and readinto1() is not strictly necessary (we
+ # could rely on the base class that provides an implementation in terms of
+ # read() and read1()). We do ai anyway to keep the _pyio implementation
+ # similar to the io implementation (which implements the methods for
+ # performance reasons).
+ def readinto(self, buf):
+ """Read data into *buf*."""
+ return self._readinto(buf, read1=False)
+ def readinto1(self, buf):
+ """Read data into *buf* with at most one system call."""
+ return self._readinto(buf, read1=True)
+
+ def _readinto(self, buf, read1):
+ """Read data into *buf* with at most one system call."""
+
+ if len(buf) == 0:
+ return 0
+
+ written = 0
+ with self._read_lock:
+ while written < len(buf):
+
+ # First try to read from internal buffer
+ avail = min(len(self._read_buf) - self._read_pos, len(buf))
+ if avail:
+ buf[written:written+avail] = \
+ self._read_buf[self._read_pos:self._read_pos+avail]
+ self._read_pos += avail
+ written += avail
+ if written == len(buf):
+ break
+
+ # If remaining space in callers buffer is larger than
+ # internal buffer, read directly into callers buffer
+ if len(buf) - written > self.buffer_size:
+ # If we don't use a memoryview, slicing buf will create
+ # a new object
+ if not isinstance(buf, memoryview):
+ buf = memoryview(buf)
+ n = self.raw.readinto(buf[written:])
+ if not n:
+ break # eof
+ written += n
+
+ # Otherwise refill internal buffer - unless we're
+ # in read1 mode and already got some data
+ elif not (read1 and written):
+ if not self._peek_unlocked(1):
+ break # eof
+
+ # In readinto1 mode, return as soon as we have some data
+ if read1 and written:
+ break
+
+ return written
+
def tell(self):
return _BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos
@@ -1207,6 +1281,9 @@ class BufferedRWPair(BufferedIOBase):
def read1(self, size):
return self.reader.read1(size)
+ def readinto1(self, b):
+ return self.reader.readinto1(b)
+
def readable(self):
return self.reader.readable()
@@ -1289,6 +1366,10 @@ class BufferedRandom(BufferedWriter, BufferedReader):
self.flush()
return BufferedReader.read1(self, size)
+ def readinto1(self, b):
+ self.flush()
+ return BufferedReader.readinto1(self, b)
+
def write(self, b):
if self._read_buf:
# Undo readahead