Jason R. Coombs avatar Jason R. Coombs committed 1389996

Extracted a LineBuffer class for buffering lines

Comments (0)

Files changed (1)

     pass
 
 
-# Huh!?  Crrrrazy EFNet doesn't follow the RFC: their ircd seems to
-# use \n as message separator!  :P
-_linesep_regexp = re.compile(b"\r?\n")
+class LineBuffer(object):
+    r"""
+    Buffer bytes read in from a connection and serve complete lines back.
+
+    >>> b = LineBuffer()
+    >>> len(b)
+    0
+
+    >>> b.feed(b'foo\nbar')
+    >>> len(b)
+    7
+    >>> list(b.lines())
+    [u'foo']
+    >>> len(b)
+    3
+
+    >>> b.feed(b'bar\r\nbaz\n')
+    >>> list(b.lines())
+    [u'barbar', u'baz']
+    >>> len(b)
+    0
+    """
+    line_sep_exp = re.compile(b'\r?\n')
+    encoding = 'utf-8'
+
+    def __init__(self):
+        self.buffer = b''
+
+    def feed(self, bytes):
+        self.buffer += bytes
+
+    def lines(self):
+        lines = self.line_sep_exp.split(self.buffer)
+        # save the last, unfinished, possibly empty line
+        self.buffer = lines.pop()
+        return (line.decode(self.encoding) for line in lines)
+
+    def __iter__(self):
+        return self.lines()
+
+    def __len__(self):
+        return len(self.buffer)
+
 
 class ServerConnection(Connection):
     """This class represents an IRC server connection.
         if self.connected:
             self.disconnect("Changing servers")
 
-        self.previous_buffer = b""
+        self.buffer = LineBuffer()
         self.handlers = {}
         self.real_server_name = ""
         self.real_nickname = nickname
             self.disconnect("Connection reset by peer")
             return
 
-        lines = _linesep_regexp.split(self.previous_buffer + new_data)
+        self.buffer.feed(new_data)
 
-        # Save the last, unfinished line.
-        self.previous_buffer = lines.pop()
-
-        for line in lines:
-            line = line.decode('utf-8')
-
+        for line in self.buffer:
             log.debug("FROM SERVER: %s", line)
 
             if not line:
         self.peeraddress = socket.gethostbyname(address)
         self.peerport = port
         self.socket = None
-        self.previous_buffer = ""
+        self.buffer = LineBuffer()
         self.handlers = {}
         self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         self.passive = 0
         peer, the peer address and port are available as
         self.peeraddress and self.peerport.
         """
-        self.previous_buffer = ""
+        self.buffer = LineBuffer()
         self.handlers = {}
         self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         self.passive = 1
             return
 
         if self.dcctype == "chat":
-            # The specification says lines are terminated with LF, but
-            # it seems safer to handle CR LF terminations too.
-            chunks = _linesep_regexp.split(self.previous_buffer + new_data)
+            self.buffer.feed(new_data)
 
-            # Save the last, unfinished line.
-            self.previous_buffer = chunks[-1]
-            if len(self.previous_buffer) > 2 ** 14:
+            chunks = list(self.buffer)
+
+            if len(self.buffer) > 2 ** 14:
                 # Bad peer! Naughty peer!
                 self.disconnect()
                 return
-            chunks = chunks[:-1]
         else:
             chunks = [new_data]
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.