@@ -120,6 +120,7 @@ def initialize(io, read_timeout: 60, write_timeout: 60, continue_timeout: nil, d
120
120
@continue_timeout = continue_timeout
121
121
@debug_output = debug_output
122
122
@rbuf = '' . b
123
+ @rbuf_offset = 0
123
124
end
124
125
125
126
attr_reader :io
@@ -154,14 +155,15 @@ def read(len, dest = ''.b, ignore_eof = false)
154
155
LOG "reading #{ len } bytes..."
155
156
read_bytes = 0
156
157
begin
157
- while read_bytes + @rbuf . size < len
158
- s = rbuf_consume ( @rbuf . size )
159
- read_bytes += s . size
160
- dest << s
158
+ while read_bytes + rbuf_size < len
159
+ if s = rbuf_consume_all_shareable!
160
+ read_bytes += s . bytesize
161
+ dest << s
162
+ end
161
163
rbuf_fill
162
164
end
163
165
s = rbuf_consume ( len - read_bytes )
164
- read_bytes += s . size
166
+ read_bytes += s . bytesize
165
167
dest << s
166
168
rescue EOFError
167
169
raise unless ignore_eof
@@ -175,9 +177,10 @@ def read_all(dest = ''.b)
175
177
read_bytes = 0
176
178
begin
177
179
while true
178
- s = rbuf_consume ( @rbuf . size )
179
- read_bytes += s . size
180
- dest << s
180
+ if s = rbuf_consume_all_shareable!
181
+ read_bytes += s . bytesize
182
+ dest << s
183
+ end
181
184
rbuf_fill
182
185
end
183
186
rescue EOFError
@@ -188,14 +191,16 @@ def read_all(dest = ''.b)
188
191
end
189
192
190
193
def readuntil ( terminator , ignore_eof = false )
194
+ offset = @rbuf_offset
191
195
begin
192
- until idx = @rbuf . index ( terminator )
196
+ until idx = @rbuf . index ( terminator , offset )
197
+ offset = @rbuf . bytesize
193
198
rbuf_fill
194
199
end
195
- return rbuf_consume ( idx + terminator . size )
200
+ return rbuf_consume ( idx + terminator . bytesize - @rbuf_offset )
196
201
rescue EOFError
197
202
raise unless ignore_eof
198
- return rbuf_consume ( @rbuf . size )
203
+ return rbuf_consume
199
204
end
200
205
end
201
206
@@ -208,12 +213,16 @@ def readline
208
213
BUFSIZE = 1024 * 16
209
214
210
215
def rbuf_fill
211
- tmp = @rbuf . empty? ? @rbuf : nil
216
+ tmp = @rbuf_empty ? @rbuf : nil
212
217
case rv = @io . read_nonblock ( BUFSIZE , tmp , exception : false )
213
218
when String
214
- return if rv . equal? ( tmp )
215
- @rbuf << rv
216
- rv . clear
219
+ @rbuf_empty = false
220
+ if rv . equal? ( tmp )
221
+ @rbuf_offset = 0
222
+ else
223
+ @rbuf << rv
224
+ rv . clear
225
+ end
217
226
return
218
227
when :wait_readable
219
228
( io = @io . to_io ) . wait_readable ( @read_timeout ) or raise Net ::ReadTimeout . new ( io )
@@ -228,13 +237,50 @@ def rbuf_fill
228
237
end while true
229
238
end
230
239
231
- def rbuf_consume ( len )
232
- if len == @rbuf . size
240
+ def rbuf_flush
241
+ if @rbuf_empty
242
+ @rbuf . clear
243
+ @rbuf_offset = 0
244
+ end
245
+ nil
246
+ end
247
+
248
+ def rbuf_size
249
+ @rbuf . bytesize - @rbuf_offset
250
+ end
251
+
252
+ # Warning: this method may share the buffer to avoid
253
+ # copying. The caller must no longer use the returned
254
+ # string once rbuf_fill has been called again
255
+ def rbuf_consume_all_shareable!
256
+ @rbuf_empty = true
257
+ buf = if @rbuf_offset == 0
258
+ @rbuf
259
+ else
260
+ @rbuf . byteslice ( @rbuf_offset ..-1 )
261
+ end
262
+ @rbuf_offset = @rbuf . bytesize
263
+ buf
264
+ end
265
+
266
+ def rbuf_consume ( len = nil )
267
+ if @rbuf_offset == 0 && ( len . nil? || len == @rbuf . bytesize )
233
268
s = @rbuf
234
269
@rbuf = '' . b
270
+ @rbuf_offset = 0
271
+ @rbuf_empty = true
272
+ elsif len . nil?
273
+ s = @rbuf . byteslice ( @rbuf_offset ..-1 )
274
+ @rbuf = '' . b
275
+ @rbuf_offset = 0
276
+ @rbuf_empty = true
235
277
else
236
- s = @rbuf . slice! ( 0 , len )
278
+ s = @rbuf . byteslice ( @rbuf_offset , len )
279
+ @rbuf_offset += len
280
+ @rbuf_empty = @rbuf_offset == @rbuf . bytesize
281
+ rbuf_flush
237
282
end
283
+
238
284
@debug_output << %Q[-> #{ s . dump } \n ] if @debug_output
239
285
s
240
286
end
0 commit comments