Skip to content

Commit d3facc2

Browse files
committed
⚡️ Simpler, faster ENVELOPE and address parser
* Envelope can *NOT* be `NIL`. * Using aliased rules for envelope and address parts * Using QUOTED_rev2 regexp for IMAP4rev2 and UTF8=ACCEPT address strings * remove extraneous `envelope_data` method Benchmarks: ``` Calculating ------------------------------------- v0.4.5-6-gbc32ddb2-dirty 0.4.4 bodystructure_mixed_boundary 3.379k 3.109k i/s 10.152k times in 3.004058s 3.265234s rfc3501_8_example_3_FETCH_ENVELOPE 5.355k 4.646k i/s 15.924k times in 2.973921s 3.427366s Comparison: bodystructure_mixed_boundary v0.4.5-6-gbc32ddb2-dirty: 3379.4 i/s 0.4.4: 3109.1 i/s - 1.09x slower rfc3501_8_example_3_FETCH_ENVELOPE v0.4.5-6-gbc32ddb2-dirty: 5354.5 i/s 0.4.4: 4646.1 i/s - 1.15x slower ```
1 parent d9b7164 commit d3facc2

File tree

1 file changed

+76
-81
lines changed

1 file changed

+76
-81
lines changed

lib/net/imap/response_parser.rb

Lines changed: 76 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -941,41 +941,57 @@ def msg_att__label
941941
# this represents the partial size for BODY or BINARY
942942
alias gt__number__lt atom
943943

944+
# RFC3501 & RFC9051:
945+
# envelope = "(" env-date SP env-subject SP env-from SP
946+
# env-sender SP env-reply-to SP env-to SP env-cc SP
947+
# env-bcc SP env-in-reply-to SP env-message-id ")"
944948
def envelope
945949
@lex_state = EXPR_DATA
946-
token = lookahead
947-
if token.symbol == T_NIL
948-
shift_token
949-
result = nil
950-
else
951-
match(T_LPAR)
952-
date = nstring
953-
match(T_SPACE)
954-
subject = nstring
955-
match(T_SPACE)
956-
from = address_list
957-
match(T_SPACE)
958-
sender = address_list
959-
match(T_SPACE)
960-
reply_to = address_list
961-
match(T_SPACE)
962-
to = address_list
963-
match(T_SPACE)
964-
cc = address_list
965-
match(T_SPACE)
966-
bcc = address_list
967-
match(T_SPACE)
968-
in_reply_to = nstring
969-
match(T_SPACE)
970-
message_id = nstring
971-
match(T_RPAR)
972-
result = Envelope.new(date, subject, from, sender, reply_to,
973-
to, cc, bcc, in_reply_to, message_id)
974-
end
950+
lpar; date = env_date
951+
SP!; subject = env_subject
952+
SP!; from = env_from
953+
SP!; sender = env_sender
954+
SP!; reply_to = env_reply_to
955+
SP!; to = env_to
956+
SP!; cc = env_cc
957+
SP!; bcc = env_bcc
958+
SP!; in_reply_to = env_in_reply_to
959+
SP!; message_id = env_message_id
960+
rpar
961+
Envelope.new(date, subject, from, sender, reply_to,
962+
to, cc, bcc, in_reply_to, message_id)
963+
ensure
975964
@lex_state = EXPR_BEG
976-
return result
977965
end
978966

967+
# env-date = nstring
968+
# env-subject = nstring
969+
# env-in-reply-to = nstring
970+
# env-message-id = nstring
971+
alias env_date nstring
972+
alias env_subject nstring
973+
alias env_in_reply_to nstring
974+
alias env_message_id nstring
975+
976+
# env-from = "(" 1*address ")" / nil
977+
# env-sender = "(" 1*address ")" / nil
978+
# env-reply-to = "(" 1*address ")" / nil
979+
# env-to = "(" 1*address ")" / nil
980+
# env-cc = "(" 1*address ")" / nil
981+
# env-bcc = "(" 1*address ")" / nil
982+
def nlist__address
983+
return if NIL?
984+
lpar; list = [address]; list << address until rpar?
985+
list
986+
end
987+
988+
alias env_from nlist__address
989+
alias env_sender nlist__address
990+
alias env_reply_to nlist__address
991+
alias env_to nlist__address
992+
alias env_cc nlist__address
993+
alias env_bcc nlist__address
994+
979995
# date-time = DQUOTE date-day-fixed "-" date-month "-" date-year
980996
# SP time SP zone DQUOTE
981997
alias date_time quoted
@@ -1877,61 +1893,40 @@ def resp_code_copy__data
18771893
UIDPlusData.new(validity, src_uids, dst_uids)
18781894
end
18791895

1880-
def address_list
1881-
token = lookahead
1882-
if token.symbol == T_NIL
1883-
shift_token
1884-
return nil
1885-
else
1886-
result = []
1887-
match(T_LPAR)
1888-
while true
1889-
token = lookahead
1890-
case token.symbol
1891-
when T_RPAR
1892-
shift_token
1893-
break
1894-
when T_SPACE
1895-
shift_token
1896-
end
1897-
result.push(address)
1898-
end
1899-
return result
1900-
end
1901-
end
1902-
1903-
ADDRESS_REGEXP = /\G\
1904-
(?# 1: NAME )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
1905-
(?# 2: ROUTE )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
1906-
(?# 3: MAILBOX )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
1907-
(?# 4: HOST )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)")\
1908-
\)/ni
1909-
1896+
ADDRESS_REGEXP = /\G
1897+
\( (?: NIL | #{Patterns::QUOTED_rev2} ) # 1: NAME
1898+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 2: ROUTE
1899+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 3: MAILBOX
1900+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 4: HOST
1901+
\)
1902+
/nix
1903+
1904+
# address = "(" addr-name SP addr-adl SP addr-mailbox SP
1905+
# addr-host ")"
1906+
# addr-adl = nstring
1907+
# addr-host = nstring
1908+
# addr-mailbox = nstring
1909+
# addr-name = nstring
19101910
def address
1911-
match(T_LPAR)
1912-
if @str.index(ADDRESS_REGEXP, @pos)
1913-
# address does not include literal.
1914-
@pos = $~.end(0)
1915-
name = $1
1916-
route = $2
1917-
mailbox = $3
1918-
host = $4
1919-
for s in [name, route, mailbox, host]
1920-
Patterns.unescape_quoted! s
1921-
end
1922-
else
1923-
name = nstring
1924-
match(T_SPACE)
1925-
route = nstring
1926-
match(T_SPACE)
1927-
mailbox = nstring
1928-
match(T_SPACE)
1929-
host = nstring
1930-
match(T_RPAR)
1911+
if (match = accept_re(ADDRESS_REGEXP))
1912+
# note that "NIL" isn't captured by the regexp
1913+
name, route, mailbox, host = match.captures
1914+
.map { Patterns.unescape_quoted _1 }
1915+
else # address may include literals
1916+
lpar; name = addr_name
1917+
SP!; route = addr_adl
1918+
SP!; mailbox = addr_mailbox
1919+
SP!; host = addr_host
1920+
rpar
19311921
end
1932-
return Address.new(name, route, mailbox, host)
1922+
Address.new(name, route, mailbox, host)
19331923
end
19341924

1925+
alias addr_adl nstring
1926+
alias addr_host nstring
1927+
alias addr_mailbox nstring
1928+
alias addr_name nstring
1929+
19351930
# flag-list = "(" [flag *(SP flag)] ")"
19361931
def flag_list
19371932
match_re(Patterns::FLAG_LIST, "flag-list")[1]

0 commit comments

Comments
 (0)