@@ -218,6 +218,28 @@ impl Url {
218218 where
219219 Input : AsRef < str > ,
220220 {
221+ let input_str = input. as_ref ( ) ;
222+
223+ // Optimised path: when a base is supplied but the input is itself an
224+ // absolute URL, the parsed base is never consulted for resolution.
225+ // Instead of fully parsing the base string into a `Url` (which
226+ // allocates a String buffer), validate it with the zero-allocation
227+ // validator. The spec still requires failure when the base is invalid,
228+ // so we check that too — but without heap allocation.
229+ if let Some ( b) = base
230+ && let Some ( url) = parser:: try_parse_absolute_fast ( input_str)
231+ {
232+ // Validate base cheaply: try the zero-alloc fast checker first,
233+ // fall back to the full zero-alloc validator for edge cases.
234+ let base_ok =
235+ parser:: try_validate_absolute_fast ( b) . is_some ( ) || validator:: can_parse_no_base ( b) ;
236+ return if base_ok {
237+ Ok ( url)
238+ } else {
239+ Err ( ParseUrlError { input } )
240+ } ;
241+ }
242+
221243 let base_url = if let Some ( b) = base {
222244 match parser:: parse_url ( b, None ) {
223245 Some ( u) if u. is_valid => Some ( u) ,
@@ -227,12 +249,33 @@ impl Url {
227249 None
228250 } ;
229251
230- match parser:: parse_url ( input . as_ref ( ) , base_url. as_ref ( ) ) {
252+ match parser:: parse_url ( input_str , base_url. as_ref ( ) ) {
231253 Some ( u) if u. is_valid => Ok ( u) ,
232254 _ => Err ( ParseUrlError { input } ) ,
233255 }
234256 }
235257
258+ /// Parse `input` relative to an already-parsed `base` URL.
259+ ///
260+ /// This is more efficient than [`Url::parse`] with a base string because the
261+ /// base URL is **not** re-parsed — use this in hot loops where the same base
262+ /// is reused across many inputs (e.g. the WPT URL benchmark pattern).
263+ ///
264+ /// Returns `None` when either `base` is invalid or `input` cannot be resolved.
265+ #[ must_use]
266+ pub fn parse_with_base < Input > ( input : Input , base : & Url ) -> Option < Self >
267+ where
268+ Input : AsRef < str > ,
269+ {
270+ if !base. is_valid {
271+ return None ;
272+ }
273+ match parser:: parse_url ( input. as_ref ( ) , Some ( base) ) {
274+ Some ( u) if u. is_valid => Some ( u) ,
275+ _ => None ,
276+ }
277+ }
278+
236279 /// Returns `true` when `input` can be parsed as a valid URL.
237280 ///
238281 /// When `base` is `None` this uses a zero-allocation fast-path validator
@@ -1682,6 +1725,35 @@ impl Url {
16821725 self . buffer . push_str ( input) ;
16831726 return ;
16841727 }
1728+ // Fast append: path already set, no dot-segments, no encoding, AND
1729+ // no search/fragment follows the path in the buffer. Only then can
1730+ // we safely push directly to the buffer end without displacing query
1731+ // or fragment bytes that sit after the current path.
1732+ //
1733+ // Extra guard: when the current path is exactly "/" appending "/"
1734+ // + input would produce "//input". `update_base_pathname("//...")` has
1735+ // a side-effect of inserting "/." for authority-less URLs; bypassing it
1736+ // would produce a wrong href (e.g. "non-spec://path" instead of
1737+ // "non-spec:/.//path"). Avoid fast_append for this edge case.
1738+ let fast_append = trivial
1739+ && !self . is_at_path ( )
1740+ && !input. starts_with ( ".." )
1741+ && !input. starts_with ( '.' )
1742+ && self . components . search_start == OMITTED
1743+ && self . components . hash_start == OMITTED
1744+ && self . pathname ( ) != "/" ;
1745+ if fast_append {
1746+ let added = ( 1 + input. len ( ) ) as u32 ;
1747+ self . buffer . push ( '/' ) ;
1748+ self . buffer . push_str ( input) ;
1749+ if self . components . search_start != OMITTED {
1750+ self . components . search_start += added;
1751+ }
1752+ if self . components . hash_start != OMITTED {
1753+ self . components . hash_start += added;
1754+ }
1755+ return ;
1756+ }
16851757 let mut new_path = if self . is_at_path ( ) {
16861758 String :: new ( )
16871759 } else {
0 commit comments