1111process.
1212"""
1313import select
14- from .exceptions import ConnectionResetError
14+ from .exceptions import ConnectionResetError , LineTooLongError
1515
1616class BufferedSocket (object ):
1717 """
@@ -76,6 +76,25 @@ def can_read(self):
7676
7777 return False
7878
79+ def new_buffer (self ):
80+ """
81+ This method moves all the data in the backing buffer to the start of
82+ a new, fresh buffer. This gives the ability to read much more data.
83+ """
84+ def read_all_from_buffer ():
85+ end = self ._index + self ._bytes_in_buffer
86+ return self ._buffer_view [self ._index :end ]
87+
88+ new_buffer = bytearray (self ._buffer_size )
89+ new_buffer_view = memoryview (new_buffer )
90+ new_buffer_view [0 :self ._bytes_in_buffer ] = read_all_from_buffer ()
91+
92+ self ._index = 0
93+ self ._backing_buffer = new_buffer
94+ self ._buffer_view = new_buffer_view
95+
96+ return
97+
7998 def recv (self , amt ):
8099 """
81100 Read some data from the socket.
@@ -85,26 +104,16 @@ def recv(self, amt):
85104 bytes. The data *must* be copied out by the caller before the next
86105 call to this function.
87106 """
88- def read_all_from_buffer ():
89- end = self ._index + self ._bytes_in_buffer
90- return self ._buffer_view [self ._index :end ]
91-
92107 # In this implementation you can never read more than the number of
93108 # bytes in the buffer.
94109 if amt > self ._buffer_size :
95110 amt = self ._buffer_size
96111
97112 # If the amount of data we've been asked to read is less than the
98113 # remaining space in the buffer, we need to clear out the buffer and
99- # start over. Copy the data into the new array.
114+ # start over.
100115 if amt > self ._remaining_capacity :
101- new_buffer = bytearray (self ._buffer_size )
102- new_buffer_view = memoryview (new_buffer )
103- new_buffer_view [0 :self ._bytes_in_buffer ] = read_all_from_buffer ()
104-
105- self ._index = 0
106- self ._backing_buffer = new_buffer
107- self ._buffer_view = new_buffer_view
116+ self .new_buffer ()
108117
109118 # If there's still some room in the buffer, opportunistically attempt
110119 # to read into it.
@@ -136,5 +145,69 @@ def read_all_from_buffer():
136145
137146 return data
138147
148+ def readline (self ):
149+ """
150+ Read up to a newline from the network and returns it. The implicit
151+ maximum line length is the buffer size of the buffered socket.
152+
153+ Note that, unlike recv, this method absolutely *does* block until it
154+ can read the line.
155+
156+ :returns: A ``memoryview`` object containing the appropriate number of
157+ bytes. The data *must* be copied out by the caller before the next
158+ call to this function.
159+ """
160+ # First, check if there's anything in the buffer. This is one of those
161+ # rare circumstances where this will work correctly on all platforms.
162+ index = self ._backing_buffer .find (
163+ b'\n ' ,
164+ self ._index ,
165+ self ._index + self ._bytes_in_buffer
166+ )
167+
168+ if index != - 1 :
169+ length = index + 1 - self ._index
170+ data = self ._buffer_view [self ._index :self ._index + length ]
171+ self ._index += length
172+ self ._bytes_in_buffer -= length
173+ return data
174+
175+ # In this case, we didn't find a newline in the buffer. To fix that,
176+ # read some data into the buffer. To do our best to satisfy the read,
177+ # we should shunt the data down in the buffer so that it's right at
178+ # the start. We don't bother if we're already at the start of the
179+ # buffer.
180+ if self ._index != 0 :
181+ self .new_buffer ()
182+
183+ while self ._bytes_in_buffer < self ._buffer_size :
184+ count = self ._sck .recv_into (self ._buffer_view [self ._buffer_end :])
185+ if not count :
186+ raise ConnectionResetError ()
187+
188+ # We have some more data. Again, look for a newline in that gap.
189+ first_new_byte = self ._buffer_end
190+ self ._bytes_in_buffer += count
191+ index = self ._backing_buffer .find (
192+ b'\n ' ,
193+ first_new_byte ,
194+ first_new_byte + count ,
195+ )
196+
197+ if index != - 1 :
198+ # The length of the buffer is the index into the
199+ # buffer at which we found the newline plus 1, minus the start
200+ # index of the buffer, which really should be zero.
201+ assert not self ._index
202+ length = index + 1
203+ data = self ._buffer_view [:length ]
204+ self ._index += length
205+ self ._bytes_in_buffer -= length
206+ return data
207+
208+ # If we got here, it means we filled the buffer without ever getting
209+ # a newline. Time to throw an exception.
210+ raise LineTooLongError ()
211+
139212 def __getattr__ (self , name ):
140213 return getattr (self ._sck , name )
0 commit comments