From dda33b77386df58d08360ccc701b9620c067450a Mon Sep 17 00:00:00 2001 From: Quentin Santos Date: Mon, 6 Mar 2023 17:59:57 +0100 Subject: [PATCH 1/3] Avoid string allocation to get length of port --- url/src/slicing.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/url/src/slicing.rs b/url/src/slicing.rs index c2e441ef2..a4372bf35 100644 --- a/url/src/slicing.rs +++ b/url/src/slicing.rs @@ -152,7 +152,8 @@ impl Url { Position::AfterPort => { if let Some(port) = self.port { debug_assert!(self.byte_at(self.host_end) == b':'); - self.host_end as usize + ":".len() + port.to_string().len() + let port_length = port.checked_ilog10().unwrap_or(0) as usize + 1; + self.host_end as usize + ":".len() + port_length } else { self.host_end as usize } From 09090fd3a61298c1d7995ab218725dc2c92dc37e Mon Sep 17 00:00:00 2001 From: Quentin Santos Date: Tue, 7 Mar 2023 20:09:27 +0100 Subject: [PATCH 2/3] Fallback for ilog10 for older versions of Rust --- url/src/slicing.rs | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/url/src/slicing.rs b/url/src/slicing.rs index a4372bf35..8be66c431 100644 --- a/url/src/slicing.rs +++ b/url/src/slicing.rs @@ -37,6 +37,42 @@ impl Index> for Url { } } +// Counts how many base-10 digits are required to represent n in the given base +fn count_digits(n: u16) -> usize { + // just use ilog10 in 1.67+ + #[cfg(int_log)] + return n.checked_ilog10().unwrap_or(0) as usize + 1; + // fall-back before 1.67 + // we avoid a branch to handle the special case of n == 0 by starting at m = 10 instead of m = 1 + let mut m = 10; + let mut log10m = 1; + while m <= n { + log10m += 1; + if let Some(m_times_10) = m.checked_mul(10) { + m = m_times_10; + } else { + // m * 10 would overflow, so it must be bigger than n + // we break our invariant log10(m) == log10m + // it's okay because we won't use m anymore + break; + } + } + // we now have 10**(log10m - 1) <= n < 10**log10m + log10m +} + +#[test] +fn test_count_digits() { + assert_eq!(count_digits(0), 1); + assert_eq!(count_digits(1), 1); + assert_eq!(count_digits(9), 1); + assert_eq!(count_digits(10), 2); + assert_eq!(count_digits(99), 2); + assert_eq!(count_digits(100), 3); + assert_eq!(count_digits(9999), 4); + assert_eq!(count_digits(65535), 5); +} + /// Indicates a position within a URL based on its components. /// /// A range of positions can be used for slicing `Url`: @@ -152,8 +188,7 @@ impl Url { Position::AfterPort => { if let Some(port) = self.port { debug_assert!(self.byte_at(self.host_end) == b':'); - let port_length = port.checked_ilog10().unwrap_or(0) as usize + 1; - self.host_end as usize + ":".len() + port_length + self.host_end as usize + ":".len() + count_digits(port) } else { self.host_end as usize } From 28c2d6c0c46cefdda11d78eb207c082999351fe2 Mon Sep 17 00:00:00 2001 From: Quentin Santos Date: Tue, 7 Mar 2023 21:15:05 +0100 Subject: [PATCH 3/3] Just use a match to count digits on u16 --- url/src/slicing.rs | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/url/src/slicing.rs b/url/src/slicing.rs index 8be66c431..c061fee84 100644 --- a/url/src/slicing.rs +++ b/url/src/slicing.rs @@ -39,26 +39,13 @@ impl Index> for Url { // Counts how many base-10 digits are required to represent n in the given base fn count_digits(n: u16) -> usize { - // just use ilog10 in 1.67+ - #[cfg(int_log)] - return n.checked_ilog10().unwrap_or(0) as usize + 1; - // fall-back before 1.67 - // we avoid a branch to handle the special case of n == 0 by starting at m = 10 instead of m = 1 - let mut m = 10; - let mut log10m = 1; - while m <= n { - log10m += 1; - if let Some(m_times_10) = m.checked_mul(10) { - m = m_times_10; - } else { - // m * 10 would overflow, so it must be bigger than n - // we break our invariant log10(m) == log10m - // it's okay because we won't use m anymore - break; - } + match n { + 0..=9 => 1, + 10..=99 => 2, + 100..=999 => 3, + 1000..=9999 => 4, + 10000..=65535 => 5, } - // we now have 10**(log10m - 1) <= n < 10**log10m - log10m } #[test]