diff --git a/url/benches/parse_url.rs b/url/benches/parse_url.rs index ace1f47a..61e88162 100644 --- a/url/benches/parse_url.rs +++ b/url/benches/parse_url.rs @@ -33,6 +33,13 @@ fn plain(bench: &mut Bencher) { bench.iter(|| black_box(url).parse::().unwrap()); } +fn port(bench: &mut Bencher) { + let url = "https://example.com:8080"; + + bench.bytes = url.len() as u64; + bench.iter(|| black_box(url).parse::().unwrap()); +} + fn hyphen(bench: &mut Bencher) { let url = "https://hyphenated-example.com/"; @@ -95,6 +102,7 @@ benchmark_group!( long, fragment, plain, + port, hyphen, leading_digit, unicode_mixed, diff --git a/url/src/parser.rs b/url/src/parser.rs index b89b8f32..1ab0dc1d 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -970,13 +970,17 @@ impl<'a> Parser<'a> { let (port, remaining) = if let Some(remaining) = remaining.split_prefix(':') { let scheme = || default_port(&self.serialization[..scheme_end as usize]); - Parser::parse_port(remaining, scheme, self.context)? + let (port, remaining) = Parser::parse_port(remaining, scheme, self.context)?; + if let Some(port) = port { + self.serialization.push(':'); + let mut buffer = [0u8; 5]; + let port_str = fast_u16_to_str(&mut buffer, port); + self.serialization.push_str(port_str); + } + (port, remaining) } else { (None, remaining) }; - if let Some(port) = port { - write!(&mut self.serialization, ":{}", port).unwrap() - } Ok((host_end, host.into(), port, remaining)) } @@ -1744,3 +1748,25 @@ fn starts_with_windows_drive_letter_segment(input: &Input<'_>) -> bool { _ => false, } } + +#[inline] +fn fast_u16_to_str( + // max 5 digits for u16 (65535) + buffer: &mut [u8; 5], + mut value: u16, +) -> &str { + let mut index = buffer.len(); + + loop { + index -= 1; + buffer[index] = b'0' + (value % 10) as u8; + value /= 10; + if value == 0 { + break; + } + } + + // SAFETY: we know the values in the buffer from the + // current index on will be a number + unsafe { core::str::from_utf8_unchecked(&buffer[index..]) } +}