Skip to content

Commit

Permalink
Fixed memory leak in ParserBuffer issue: aio-libs#579
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeroen van der Heijden committed Oct 21, 2015
1 parent 1741564 commit c1282a1
Showing 1 changed file with 42 additions and 33 deletions.
75 changes: 42 additions & 33 deletions aiohttp/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,17 @@ def eof_received(self):
self.reader.feed_eof()


class ParserBuffer(bytearray):
"""ParserBuffer is a bytearray extension.
class ParserBuffer:
"""ParserBuffer is NOT a bytearray extension anymore.
ParserBuffer provides helper methods for parsers.
"""
__slots__ = ('_exception', '_writer')
__slots__ = ('_exception', '_writer', '_data')

def __init__(self, *args):
super().__init__(*args)

self._data = bytearray(*args)
self._exception = None
self._writer = self._feed_data()
self._writer = self._feed_data(self._data)
next(self._writer)

def exception(self):
Expand All @@ -283,45 +282,50 @@ def exception(self):
def set_exception(self, exc):
self._exception = exc

def _feed_data(self):
@staticmethod
def _feed_data(data):
while True:
chunk = yield
if chunk:
self.extend(chunk)
data.extend(chunk)

if self._exception:
self._writer = self._feed_data()
next(self._writer)
raise self._exception
def _check_exception(self):
if self._exception:
self._writer = self._feed_data(self._data)
next(self._writer)
raise self._exception

def feed_data(self, data):
self._writer.send(data)
self._check_exception()

def read(self, size):
"""read() reads specified amount of bytes."""

while True:
if len(self) >= size:
data = self[:size]
del self[:size]
if len(self._data) >= size:
data = self._data[:size]
del self._data[:size]
return data

self._writer.send((yield))
self._check_exception()

def readsome(self, size=None):
"""reads size of less amount of bytes."""

while True:
length = len(self)
length = len(self._data)
if length > 0:
if size is None or length < size:
size = length

data = self[:size]
del self[:size]
data = self._data[:size]
del self._data[:size]
return data

self._writer.send((yield))
self._check_exception()

def readuntil(self, stop, limit=None):
assert isinstance(stop, bytes) and stop, \
Expand All @@ -330,33 +334,35 @@ def readuntil(self, stop, limit=None):
stop_len = len(stop)

while True:
pos = self.find(stop)
pos = self._data.find(stop)
if pos >= 0:
end = pos + stop_len
size = end
if limit is not None and size > limit:
raise errors.LineLimitExceededParserError(
'Line is too long.', limit)

data = self[:size]
del self[:size]
data = self._data[:size]
del self._data[:size]
return data
else:
if limit is not None and len(self) > limit:
if limit is not None and len(self._data) > limit:
raise errors.LineLimitExceededParserError(
'Line is too long.', limit)

self._writer.send((yield))
self._check_exception()

def wait(self, size):
"""wait() waits for specified amount of bytes
then returns data without changing internal buffer."""

while True:
if len(self) >= size:
return self[:size]
if len(self._data) >= size:
return self._data[:size]

self._writer.send((yield))
self._check_exception()

def waituntil(self, stop, limit=None):
"""waituntil() reads until `stop` bytes sequence."""
Expand All @@ -366,28 +372,30 @@ def waituntil(self, stop, limit=None):
stop_len = len(stop)

while True:
pos = self.find(stop)
pos = self._data.find(stop)
if pos >= 0:
size = pos + stop_len
if limit is not None and size > limit:
raise errors.LineLimitExceededParserError(
'Line is too long. %s' % bytes(self), limit)
'Line is too long. %s' % bytes(self._data), limit)

return self[:size]
return self._data[:size]
else:
if limit is not None and len(self) > limit:
if limit is not None and len(self._data) > limit:
raise errors.LineLimitExceededParserError(
'Line is too long. %s' % bytes(self), limit)
'Line is too long. %s' % bytes(self._data), limit)

self._writer.send((yield))
self._check_exception()

def skip(self, size):
"""skip() skips specified amount of bytes."""

while len(self) < size:
while len(self._data) < size:
self._writer.send((yield))
self._check_exception()

del self[:size]
del self._data[:size]

def skipuntil(self, stop):
"""skipuntil() reads until `stop` bytes sequence."""
Expand All @@ -397,13 +405,14 @@ def skipuntil(self, stop):
stop_len = len(stop)

while True:
stop_line = self.find(stop)
stop_line = self._data.find(stop)
if stop_line >= 0:
size = stop_line + stop_len
del self[:size]
del self._data[:size]
return

self._writer.send((yield))
self._check_exception()


class LinesParser:
Expand Down

0 comments on commit c1282a1

Please sign in to comment.