Skip to content

Commit 4a986de

Browse files
committed
LibURL: Optimize ASCII path/query parsing
Copy contiguous ASCII path/query runs in one slice append, instead of processing each code point individually. Fall back to the normal state machine for delimiters, percent escapes, non-ASCII input, and validation cases. This gives a 2.4x speedup on a benchmark parsing common web URLs.
1 parent 331dd40 commit 4a986de

1 file changed

Lines changed: 85 additions & 1 deletion

File tree

Libraries/LibURL/Rust/src/url/parser.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,71 @@ pub(super) fn is_url_code_point(code_point: char) -> bool {
8181
// U+003B (;), U+003D (=), U+003F (?), U+0040 (@), U+005F (_), U+007E (~), and code
8282
// points in the range U+00A0 to U+10FFFD, inclusive, excluding surrogates and
8383
// noncharacters.
84-
code_point.is_ascii_alphanumeric() || "!$&'()*+,-./:;=?@_~".contains(code_point)
84+
code_point.is_ascii() && is_ascii_url_code_point_byte(code_point as u8)
85+
}
86+
87+
#[inline]
88+
fn is_ascii_url_code_point_byte(byte: u8) -> bool {
89+
byte.is_ascii_alphanumeric()
90+
|| matches!(
91+
byte,
92+
b'!' | b'$'
93+
| b'&'
94+
| b'\''
95+
| b'('
96+
| b')'
97+
| b'*'
98+
| b'+'
99+
| b','
100+
| b'-'
101+
| b'.'
102+
| b'/'
103+
| b':'
104+
| b';'
105+
| b'='
106+
| b'?'
107+
| b'@'
108+
| b'_'
109+
| b'~'
110+
)
111+
}
112+
113+
#[inline]
114+
fn is_ascii_path_state_copyable_byte(byte: u8) -> bool {
115+
byte.is_ascii_alphanumeric()
116+
|| matches!(
117+
byte,
118+
b'!' | b'$'
119+
| b'&'
120+
| b'\''
121+
| b'('
122+
| b')'
123+
| b'*'
124+
| b'+'
125+
| b','
126+
| b'-'
127+
| b'.'
128+
| b':'
129+
| b';'
130+
| b'='
131+
| b'@'
132+
| b'_'
133+
| b'~'
134+
)
135+
}
136+
137+
#[inline]
138+
fn is_ascii_query_state_copyable_byte(byte: u8) -> bool {
139+
is_ascii_url_code_point_byte(byte)
140+
}
141+
142+
#[inline]
143+
fn ascii_copyable_state_prefix_length(input: &str, is_copyable_byte: impl Fn(u8) -> bool) -> usize {
144+
input
145+
.as_bytes()
146+
.iter()
147+
.position(|&byte| !is_copyable_byte(byte))
148+
.unwrap_or(input.len())
85149
}
86150

87151
// https://url.spec.whatwg.org/#single-dot-path-segment
@@ -1061,6 +1125,16 @@ pub(crate) fn basic_parse_into(
10611125
}
10621126
// 2. Otherwise, run these steps:
10631127
else {
1128+
// OPTIMIZATION: The spec processes one code point at a time here. Copy the prefix of ASCII
1129+
// bytes that can be appended without changing parser state or requiring percent-encoding.
1130+
let prefix_length =
1131+
ascii_copyable_state_prefix_length(remaining, is_ascii_path_state_copyable_byte);
1132+
if prefix_length > 0 {
1133+
buffer.push_str(&remaining[..prefix_length]);
1134+
pointer += prefix_length;
1135+
continue;
1136+
}
1137+
10641138
// 1. If c is not a URL code point and not U+0025 (%), invalid-URL-unit validation error.
10651139
if let Some(byte) = code_point {
10661140
if !is_url_code_point(byte) && byte != '%' {
@@ -1167,6 +1241,16 @@ pub(crate) fn basic_parse_into(
11671241
}
11681242
// 3. Otherwise, if c is not the EOF code point:
11691243
else if let Some(byte) = code_point {
1244+
// OPTIMIZATION: The spec appends query code points one at a time before the final encoding pass.
1245+
// Copy the matching ASCII URL-code-point prefix directly and resume normal handling at the first special byte.
1246+
let prefix_length =
1247+
ascii_copyable_state_prefix_length(remaining, is_ascii_query_state_copyable_byte);
1248+
if prefix_length > 0 {
1249+
buffer.push_str(&remaining[..prefix_length]);
1250+
pointer += prefix_length;
1251+
continue;
1252+
}
1253+
11701254
// 1. If c is not a URL code point and not U+0025 (%), invalid-URL-unit validation error.
11711255
if !is_url_code_point(byte) && byte != '%' {
11721256
report_validation_error(State::Query, pointer, code_point, "invalid-URL-unit");

0 commit comments

Comments
 (0)