Skip to content

Commit 9c3a3e3

Browse files
committed
Auto merge of #93697 - the8472:fix-windows-path-hash, r=Mark-Simulacrum
Fix hashing for windows paths containing a CurDir component * the logic only checked for / but not for \ * verbatim paths shouldn't skip items at all since they don't get normalized * the extra branches get optimized out on unix since is_sep_byte is a trivial comparison and is_verbatim is always-false * tests lacked windows coverage for these cases That lead to equal paths not having equal hashes and to unnecessary collisions.
2 parents f8f1751 + 1d21ce7 commit 9c3a3e3

File tree

3 files changed

+53
-9
lines changed

3 files changed

+53
-9
lines changed

library/std/src/path.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -2922,32 +2922,40 @@ impl cmp::PartialEq for Path {
29222922
impl Hash for Path {
29232923
fn hash<H: Hasher>(&self, h: &mut H) {
29242924
let bytes = self.as_u8_slice();
2925-
let prefix_len = match parse_prefix(&self.inner) {
2925+
let (prefix_len, verbatim) = match parse_prefix(&self.inner) {
29262926
Some(prefix) => {
29272927
prefix.hash(h);
2928-
prefix.len()
2928+
(prefix.len(), prefix.is_verbatim())
29292929
}
2930-
None => 0,
2930+
None => (0, false),
29312931
};
29322932
let bytes = &bytes[prefix_len..];
29332933

29342934
let mut component_start = 0;
29352935
let mut bytes_hashed = 0;
29362936

29372937
for i in 0..bytes.len() {
2938-
if is_sep_byte(bytes[i]) {
2938+
let is_sep = if verbatim { is_verbatim_sep(bytes[i]) } else { is_sep_byte(bytes[i]) };
2939+
if is_sep {
29392940
if i > component_start {
29402941
let to_hash = &bytes[component_start..i];
29412942
h.write(to_hash);
29422943
bytes_hashed += to_hash.len();
29432944
}
29442945

29452946
// skip over separator and optionally a following CurDir item
2946-
// since components() would normalize these away
2947-
component_start = i + match bytes[i..] {
2948-
[_, b'.', b'/', ..] | [_, b'.'] => 2,
2949-
_ => 1,
2950-
};
2947+
// since components() would normalize these away.
2948+
component_start = i + 1;
2949+
2950+
let tail = &bytes[component_start..];
2951+
2952+
if !verbatim {
2953+
component_start += match tail {
2954+
[b'.'] => 1,
2955+
[b'.', sep @ _, ..] if is_sep_byte(*sep) => 1,
2956+
_ => 0,
2957+
};
2958+
}
29512959
}
29522960
}
29532961

library/std/src/path/tests.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,20 @@ pub fn test_compare() {
14981498
relative_from: Some("")
14991499
);
15001500

1501+
tc!("foo/.", "foo",
1502+
eq: true,
1503+
starts_with: true,
1504+
ends_with: true,
1505+
relative_from: Some("")
1506+
);
1507+
1508+
tc!("foo/./bar", "foo/bar",
1509+
eq: true,
1510+
starts_with: true,
1511+
ends_with: true,
1512+
relative_from: Some("")
1513+
);
1514+
15011515
tc!("foo/bar", "foo",
15021516
eq: false,
15031517
starts_with: true,
@@ -1541,6 +1555,27 @@ pub fn test_compare() {
15411555
ends_with: true,
15421556
relative_from: Some("")
15431557
);
1558+
1559+
tc!(r"C:\foo\.\bar.txt", r"C:\foo\bar.txt",
1560+
eq: true,
1561+
starts_with: true,
1562+
ends_with: true,
1563+
relative_from: Some("")
1564+
);
1565+
1566+
tc!(r"C:\foo\.", r"C:\foo",
1567+
eq: true,
1568+
starts_with: true,
1569+
ends_with: true,
1570+
relative_from: Some("")
1571+
);
1572+
1573+
tc!(r"\\?\C:\foo\.\bar.txt", r"\\?\C:\foo\bar.txt",
1574+
eq: false,
1575+
starts_with: false,
1576+
ends_with: false,
1577+
relative_from: None
1578+
);
15441579
}
15451580
}
15461581

src/test/ui/issues/issue-23036.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// run-pass
2+
// ignore-wasm32-bare FIXME(#93923) llvm miscompilation
23

34
use std::collections::HashMap;
45
use std::path::Path;

0 commit comments

Comments
 (0)