Skip to content

Commit 7e1af98

Browse files
committed
⚡️ Faster ResponseReader: simpler too large guards
We should only check these twice per loop: once before and once after each `read_line`. Checking after reading the line handles two cases at once: * when we run out of buffer space without completing the response * when we don't have enough buffer space for the incoming literal (benchmarks will be posted with the PR)
1 parent 0e89dbb commit 7e1af98

1 file changed

Lines changed: 11 additions & 14 deletions

File tree

lib/net/imap/response_reader.rb

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ def read_response_buffer
1919
@buff = String.new
2020
catch :eof do
2121
while true
22+
guard_response_too_large!
2223
read_line
24+
# check before allocating memory for literal
25+
guard_response_too_large!
2326
break unless literal_size
2427
read_literal
2528
end
@@ -47,25 +50,18 @@ def get_literal_size(buff)
4750
end
4851

4952
def read_line
50-
line = (@sock.gets(CRLF, read_limit) or throw :eof)
51-
buff << line
52-
max_response_remaining! unless line_done?
53+
line = (@sock.gets(CRLF, max_response_remaining) or throw :eof)
5354
@literal_size = get_literal_size(line)
55+
buff << line
5456
end
5557

5658
def read_literal
57-
# check before allocating memory for literal
58-
max_response_remaining!
5959
literal = String.new(capacity: literal_size)
60-
buff << (@sock.read(read_limit(literal_size), literal) or throw :eof)
60+
buff << (@sock.read(literal_size, literal) or throw :eof)
6161
ensure
6262
@literal_size = nil
6363
end
6464

65-
def read_limit(limit = nil)
66-
[limit, max_response_remaining!].compact.min
67-
end
68-
6965
def max_response_remaining = max_response_size &.- bytes_read
7066
def response_too_large? = max_response_size &.< min_response_size
7167
def min_response_size = bytes_read + min_response_remaining
@@ -74,10 +70,11 @@ def min_response_remaining
7470
empty? ? 3 : done? ? 0 : (literal_size || 0) + 2
7571
end
7672

77-
def max_response_remaining!
78-
return max_response_remaining unless response_too_large?
79-
raise ResponseTooLargeError.new(
80-
max_response_size:, bytes_read:, literal_size:,
73+
def guard_response_too_large! = (raise self if response_too_large?)
74+
75+
def exception(msg = nil)
76+
ResponseTooLargeError.new(
77+
msg, max_response_size:, bytes_read:, literal_size:,
8178
)
8279
end
8380

0 commit comments

Comments
 (0)