http2: sane readline
authorBrendan Cully <brendan@kublai.com>
Fri, 01 Feb 2013 15:00:23 -0800
changeset 19038 36733ab7fa05
parent 19037 1fde25ad9396
child 19039 41669a18a7d6
http2: sane readline It turns out that it pays off to read more than a byte at a time with a select in between :)
mercurial/httpclient/__init__.py
mercurial/httpclient/_readers.py
--- a/mercurial/httpclient/__init__.py	Fri Feb 01 14:41:35 2013 -0800
+++ b/mercurial/httpclient/__init__.py	Fri Feb 01 15:00:23 2013 -0800
@@ -125,24 +125,16 @@
         This may block until either a line ending is found or the
         response is complete.
         """
-        # TODO: move this into the reader interface where it can be
-        # smarter (and probably avoid copies)
-        bytes = []
-        while not bytes:
-            try:
-                bytes = [self._reader.read(1)]
-            except _readers.ReadNotReady:
-                self._select()
-        while bytes[-1] != '\n' and not self.complete():
+        blocks = []
+        while True:
+            self._reader.readto('\n', blocks)
+
+            if blocks and blocks[-1][-1] == '\n' or self.complete():
+                break
+
             self._select()
-            bytes.append(self._reader.read(1))
-        if bytes[-1] != '\n':
-            next = self._reader.read(1)
-            while next and next != '\n':
-                bytes.append(next)
-                next = self._reader.read(1)
-            bytes.append(next)
-        return ''.join(bytes)
+
+        return ''.join(blocks)
 
     def read(self, length=None):
         # if length is None, unbounded read
--- a/mercurial/httpclient/_readers.py	Fri Feb 01 14:41:35 2013 -0800
+++ b/mercurial/httpclient/_readers.py	Fri Feb 01 15:00:23 2013 -0800
@@ -96,6 +96,29 @@
 
         return result
 
+    def readto(self, delimstr, blocks = None):
+        """return available data chunks up to the first one in which delimstr
+        occurs. No data will be returned after delimstr -- the chunk in which
+        it occurs will be split and the remainder pushed back onto the available
+        data queue. If blocks is supplied chunks will be added to blocks, otherwise
+        a new list will be allocated.
+        """
+        if blocks is None:
+            blocks = []
+
+        while self._done_chunks:
+            b = self.popchunk()
+            i = b.find(delimstr) + len(delimstr)
+            if i:
+                if i < len(b):
+                    self.pushchunk(b[i:])
+                blocks.append(b[:i])
+                break
+            else:
+                blocks.append(b)
+
+        return blocks
+
     def _load(self, data): # pragma: no cover
         """Subclasses must implement this.