Skip to content

Commit f8d6869

Browse files
authored
Merge branch 'main' into patch-1
2 parents 6840977 + 2ce2e12 commit f8d6869

File tree

13 files changed

+360
-109
lines changed

13 files changed

+360
-109
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ jobs:
9595
{
9696
echo "leak:dyld4::RuntimeState"
9797
echo "leak:fetchInitializingClassList"
98+
echo "leak:std::sys::pal::unix::stack_overflow::imp::init"
9899
} > suppressions.txt
99100
export LSAN_OPTIONS="suppressions=$(pwd)/suppressions.txt"
100101
fi

LICENSE-MIT

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2013-2022 The rust-url developers
1+
Copyright (c) 2013-2025 The rust-url developers
22

33
Permission is hereby granted, free of charge, to any
44
person obtaining a copy of this software and associated

data-url/src/lib.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ impl<'a> DataUrl<'a> {
124124
/// The URL’s fragment identifier (after `#`)
125125
pub struct FragmentIdentifier<'a>(&'a str);
126126

127-
impl<'a> FragmentIdentifier<'a> {
127+
impl FragmentIdentifier<'_> {
128128
/// Like in a parsed URL
129129
pub fn to_percent_encoded(&self) -> String {
130130
let mut string = String::new();
@@ -165,10 +165,10 @@ fn pretend_parse_data_url(input: &str) -> Option<&str> {
165165
let mut iter = bytes
166166
.by_ref()
167167
.filter(|&byte| !matches!(byte, b'\t' | b'\n' | b'\r'));
168-
require!(iter.next()?.to_ascii_lowercase() == b'd');
169-
require!(iter.next()?.to_ascii_lowercase() == b'a');
170-
require!(iter.next()?.to_ascii_lowercase() == b't');
171-
require!(iter.next()?.to_ascii_lowercase() == b'a');
168+
require!(iter.next()?.eq_ignore_ascii_case(&b'd'));
169+
require!(iter.next()?.eq_ignore_ascii_case(&b'a'));
170+
require!(iter.next()?.eq_ignore_ascii_case(&b't'));
171+
require!(iter.next()?.eq_ignore_ascii_case(&b'a'));
172172
require!(iter.next()? == b':');
173173
}
174174
let bytes_consumed = left_trimmed.len() - bytes.len();
@@ -256,10 +256,10 @@ fn remove_base64_suffix(s: &str) -> Option<&str> {
256256

257257
require!(iter.next()? == b'4');
258258
require!(iter.next()? == b'6');
259-
require!(iter.next()?.to_ascii_lowercase() == b'e');
260-
require!(iter.next()?.to_ascii_lowercase() == b's');
261-
require!(iter.next()?.to_ascii_lowercase() == b'a');
262-
require!(iter.next()?.to_ascii_lowercase() == b'b');
259+
require!(iter.next()?.eq_ignore_ascii_case(&b'e'));
260+
require!(iter.next()?.eq_ignore_ascii_case(&b's'));
261+
require!(iter.next()?.eq_ignore_ascii_case(&b'a'));
262+
require!(iter.next()?.eq_ignore_ascii_case(&b'b'));
263263
require!(iter.skip_while(|&byte| byte == b' ').next()? == b';');
264264
}
265265
Some(&s[..bytes.len()])

form_urlencoded/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ pub struct ParseIntoOwned<'a> {
104104
inner: Parse<'a>,
105105
}
106106

107-
impl<'a> Iterator for ParseIntoOwned<'a> {
107+
impl Iterator for ParseIntoOwned<'_> {
108108
type Item = (String, String);
109109

110110
fn next(&mut self) -> Option<Self::Item> {
@@ -195,7 +195,7 @@ impl Target for String {
195195
type Finished = Self;
196196
}
197197

198-
impl<'a> Target for &'a mut String {
198+
impl Target for &mut String {
199199
fn as_mut_string(&mut self) -> &mut String {
200200
self
201201
}

idna/src/punycode.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ where
277277
phantom: PhantomData<C>,
278278
}
279279

280-
impl<'a, T: PunycodeCodeUnit + Copy, C: PunycodeCaller> Iterator for Decode<'a, T, C> {
280+
impl<T: PunycodeCodeUnit + Copy, C: PunycodeCaller> Iterator for Decode<'_, T, C> {
281281
type Item = char;
282282

283283
fn next(&mut self) -> Option<Self::Item> {
@@ -309,7 +309,7 @@ impl<'a, T: PunycodeCodeUnit + Copy, C: PunycodeCaller> Iterator for Decode<'a,
309309
}
310310
}
311311

312-
impl<'a, T: PunycodeCodeUnit + Copy, C: PunycodeCaller> ExactSizeIterator for Decode<'a, T, C> {
312+
impl<T: PunycodeCodeUnit + Copy, C: PunycodeCaller> ExactSizeIterator for Decode<'_, T, C> {
313313
fn len(&self) -> usize {
314314
self.len - self.position
315315
}

percent_encoding/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ impl<'a> Iterator for PercentEncode<'a> {
181181
}
182182
}
183183

184-
impl<'a> fmt::Display for PercentEncode<'a> {
184+
impl fmt::Display for PercentEncode<'_> {
185185
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
186186
for c in (*self).clone() {
187187
formatter.write_str(c)?
@@ -257,7 +257,7 @@ fn after_percent_sign(iter: &mut slice::Iter<'_, u8>) -> Option<u8> {
257257
Some(h as u8 * 0x10 + l as u8)
258258
}
259259

260-
impl<'a> Iterator for PercentDecode<'a> {
260+
impl Iterator for PercentDecode<'_> {
261261
type Item = u8;
262262

263263
fn next(&mut self) -> Option<u8> {

url/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ wasm-bindgen-test = "0.3"
2828
form_urlencoded = { version = "1.2.1", path = "../form_urlencoded", default-features = false, features = ["alloc"] }
2929
idna = { version = "1.0.3", path = "../idna", default-features = false, features = ["alloc", "compiled_data"] }
3030
percent-encoding = { version = "2.3.1", path = "../percent_encoding", default-features = false, features = ["alloc"] }
31-
serde = { version = "1.0", optional = true, features = ["derive"] }
31+
serde = { version = "1.0", optional = true, features = ["derive"], default-features = false }
3232

3333
[features]
3434
default = ["std"]

url/benches/parse_url.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,27 @@ fn long(bench: &mut Bencher) {
1919
bench.iter(|| black_box(url).parse::<Url>().unwrap());
2020
}
2121

22+
fn fragment(bench: &mut Bencher) {
23+
let url = "https://example.com/parkbench?tre=es&st=uff#fragment";
24+
25+
bench.bytes = url.len() as u64;
26+
bench.iter(|| black_box(url).parse::<Url>().unwrap());
27+
}
28+
2229
fn plain(bench: &mut Bencher) {
2330
let url = "https://example.com/";
2431

2532
bench.bytes = url.len() as u64;
2633
bench.iter(|| black_box(url).parse::<Url>().unwrap());
2734
}
2835

36+
fn port(bench: &mut Bencher) {
37+
let url = "https://example.com:8080";
38+
39+
bench.bytes = url.len() as u64;
40+
bench.iter(|| black_box(url).parse::<Url>().unwrap());
41+
}
42+
2943
fn hyphen(bench: &mut Bencher) {
3044
let url = "https://hyphenated-example.com/";
3145

@@ -82,11 +96,26 @@ fn punycode_rtl(bench: &mut Bencher) {
8296
bench.iter(|| black_box(url).parse::<Url>().unwrap());
8397
}
8498

99+
fn url_to_file_path(bench: &mut Bencher) {
100+
let url = if cfg!(windows) {
101+
"file:///C:/dir/next_dir/sub_sub_dir/testing/testing.json"
102+
} else {
103+
"file:///data/dir/next_dir/sub_sub_dir/testing/testing.json"
104+
};
105+
let url = url.parse::<Url>().unwrap();
106+
107+
bench.iter(|| {
108+
black_box(url.to_file_path().unwrap());
109+
});
110+
}
111+
85112
benchmark_group!(
86113
benches,
87114
short,
88115
long,
116+
fragment,
89117
plain,
118+
port,
90119
hyphen,
91120
leading_digit,
92121
unicode_mixed,
@@ -95,5 +124,6 @@ benchmark_group!(
95124
punycode_ltr,
96125
unicode_rtl,
97126
punycode_rtl,
127+
url_to_file_path
98128
);
99129
benchmark_main!(benches);

url/src/host.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub enum Host<S = String> {
6464
Ipv6(Ipv6Addr),
6565
}
6666

67-
impl<'a> Host<&'a str> {
67+
impl Host<&str> {
6868
/// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`.
6969
pub fn to_owned(&self) -> Host<String> {
7070
match *self {

url/src/lib.rs

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,6 @@ impl Url {
14971497
/// # }
14981498
/// # run().unwrap();
14991499
/// ```
1500-
15011500
#[inline]
15021501
pub fn query_pairs(&self) -> form_urlencoded::Parse<'_> {
15031502
form_urlencoded::parse(self.query().unwrap_or("").as_bytes())
@@ -1560,7 +1559,7 @@ impl Url {
15601559
/// # fn run() -> Result<(), ParseError> {
15611560
/// let mut url = Url::parse("https://example.com/data.csv")?;
15621561
/// assert_eq!(url.as_str(), "https://example.com/data.csv");
1563-
1562+
///
15641563
/// url.set_fragment(Some("cell=4,1-6,2"));
15651564
/// assert_eq!(url.as_str(), "https://example.com/data.csv#cell=4,1-6,2");
15661565
/// assert_eq!(url.fragment(), Some("cell=4,1-6,2"));
@@ -2679,8 +2678,7 @@ impl Url {
26792678
fragment_start,
26802679
};
26812680
if cfg!(debug_assertions) {
2682-
url.check_invariants()
2683-
.map_err(|reason| Error::custom(reason))?
2681+
url.check_invariants().map_err(Error::custom)?
26842682
}
26852683
Ok(url)
26862684
}
@@ -2727,7 +2725,26 @@ impl Url {
27272725
_ => return Err(()),
27282726
};
27292727

2730-
return file_url_segments_to_pathbuf(host, segments);
2728+
let str_len = self.as_str().len();
2729+
let estimated_capacity = if cfg!(target_os = "redox") {
2730+
let scheme_len = self.scheme().len();
2731+
let file_scheme_len = "file".len();
2732+
// remove only // because it still has file:
2733+
if scheme_len < file_scheme_len {
2734+
let scheme_diff = file_scheme_len - scheme_len;
2735+
(str_len + scheme_diff).saturating_sub(2)
2736+
} else {
2737+
let scheme_diff = scheme_len - file_scheme_len;
2738+
str_len.saturating_sub(scheme_diff + 2)
2739+
}
2740+
} else if cfg!(windows) {
2741+
// remove scheme: - has posssible \\ for hostname
2742+
str_len.saturating_sub(self.scheme().len() + 1)
2743+
} else {
2744+
// remove scheme://
2745+
str_len.saturating_sub(self.scheme().len() + 3)
2746+
};
2747+
return file_url_segments_to_pathbuf(estimated_capacity, host, segments);
27312748
}
27322749
Err(())
27332750
}
@@ -2897,7 +2914,7 @@ impl<'de> serde::Deserialize<'de> for Url {
28972914

28982915
struct UrlVisitor;
28992916

2900-
impl<'de> Visitor<'de> for UrlVisitor {
2917+
impl Visitor<'_> for UrlVisitor {
29012918
type Value = Url;
29022919

29032920
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
@@ -3037,6 +3054,7 @@ fn path_to_file_url_segments_windows(
30373054
any(unix, target_os = "redox", target_os = "wasi", target_os = "hermit")
30383055
))]
30393056
fn file_url_segments_to_pathbuf(
3057+
estimated_capacity: usize,
30403058
host: Option<&str>,
30413059
segments: str::Split<'_, char>,
30423060
) -> Result<PathBuf, ()> {
@@ -3048,17 +3066,16 @@ fn file_url_segments_to_pathbuf(
30483066
use std::os::hermit::ffi::OsStrExt;
30493067
#[cfg(any(unix, target_os = "redox"))]
30503068
use std::os::unix::prelude::OsStrExt;
3051-
use std::path::PathBuf;
30523069

30533070
if host.is_some() {
30543071
return Err(());
30553072
}
30563073

3057-
let mut bytes = if cfg!(target_os = "redox") {
3058-
b"file:".to_vec()
3059-
} else {
3060-
Vec::new()
3061-
};
3074+
let mut bytes = Vec::new();
3075+
bytes.try_reserve(estimated_capacity).map_err(|_| ())?;
3076+
if cfg!(target_os = "redox") {
3077+
bytes.extend(b"file:");
3078+
}
30623079

30633080
for segment in segments {
30643081
bytes.push(b'/');
@@ -3090,22 +3107,27 @@ fn file_url_segments_to_pathbuf(
30903107

30913108
#[cfg(all(feature = "std", windows))]
30923109
fn file_url_segments_to_pathbuf(
3110+
estimated_capacity: usize,
30933111
host: Option<&str>,
30943112
segments: str::Split<char>,
30953113
) -> Result<PathBuf, ()> {
3096-
file_url_segments_to_pathbuf_windows(host, segments)
3114+
file_url_segments_to_pathbuf_windows(estimated_capacity, host, segments)
30973115
}
30983116

30993117
// Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
31003118
#[cfg(feature = "std")]
31013119
#[cfg_attr(not(windows), allow(dead_code))]
31023120
fn file_url_segments_to_pathbuf_windows(
3121+
estimated_capacity: usize,
31033122
host: Option<&str>,
31043123
mut segments: str::Split<'_, char>,
31053124
) -> Result<PathBuf, ()> {
3106-
use percent_encoding::percent_decode;
3107-
let mut string = if let Some(host) = host {
3108-
r"\\".to_owned() + host
3125+
use percent_encoding::percent_decode_str;
3126+
let mut string = String::new();
3127+
string.try_reserve(estimated_capacity).map_err(|_| ())?;
3128+
if let Some(host) = host {
3129+
string.push_str(r"\\");
3130+
string.push_str(host);
31093131
} else {
31103132
let first = segments.next().ok_or(())?;
31113133

@@ -3115,7 +3137,7 @@ fn file_url_segments_to_pathbuf_windows(
31153137
return Err(());
31163138
}
31173139

3118-
first.to_owned()
3140+
string.push_str(first);
31193141
}
31203142

31213143
4 => {
@@ -3127,7 +3149,8 @@ fn file_url_segments_to_pathbuf_windows(
31273149
return Err(());
31283150
}
31293151

3130-
first[0..1].to_owned() + ":"
3152+
string.push_str(&first[0..1]);
3153+
string.push(':');
31313154
}
31323155

31333156
_ => return Err(()),
@@ -3138,11 +3161,20 @@ fn file_url_segments_to_pathbuf_windows(
31383161
string.push('\\');
31393162

31403163
// Currently non-unicode windows paths cannot be represented
3141-
match String::from_utf8(percent_decode(segment.as_bytes()).collect()) {
3164+
match percent_decode_str(segment).decode_utf8() {
31423165
Ok(s) => string.push_str(&s),
31433166
Err(..) => return Err(()),
31443167
}
31453168
}
3169+
// ensure our estimated capacity was good
3170+
if cfg!(test) {
3171+
debug_assert!(
3172+
string.len() <= estimated_capacity,
3173+
"len: {}, capacity: {}",
3174+
string.len(),
3175+
estimated_capacity
3176+
);
3177+
}
31463178
let path = PathBuf::from(string);
31473179
debug_assert!(
31483180
path.is_absolute(),
@@ -3182,7 +3214,7 @@ impl<'a> form_urlencoded::Target for UrlQuery<'a> {
31823214
type Finished = &'a mut Url;
31833215
}
31843216

3185-
impl<'a> Drop for UrlQuery<'a> {
3217+
impl Drop for UrlQuery<'_> {
31863218
fn drop(&mut self) {
31873219
if let Some(url) = self.url.take() {
31883220
url.restore_already_parsed_fragment(self.fragment.take())

0 commit comments

Comments
 (0)