diff options
| author | bors <bors@rust-lang.org> | 2022-02-12 14:01:13 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-02-12 14:01:13 +0000 |
| commit | 9c3a3e3d5bd65fd534336101a06f7b11396c3208 (patch) | |
| tree | 35eee2e993e3a03d52ca18e23600052c30aa8032 /library/std/src | |
| parent | f8f175199e8db959cf25f1582157ca89ecf6210a (diff) | |
| parent | 1d21ce723cfbadb711eab1cf21df28a1636b7230 (diff) | |
| download | rust-9c3a3e3d5bd65fd534336101a06f7b11396c3208.tar.gz rust-9c3a3e3d5bd65fd534336101a06f7b11396c3208.zip | |
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.
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/path.rs | 26 | ||||
| -rw-r--r-- | library/std/src/path/tests.rs | 35 |
2 files changed, 52 insertions, 9 deletions
diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 85cad65da6a..551b66891c9 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2922,12 +2922,12 @@ impl cmp::PartialEq for Path { impl Hash for Path { fn hash<H: Hasher>(&self, h: &mut H) { let bytes = self.as_u8_slice(); - let prefix_len = match parse_prefix(&self.inner) { + let (prefix_len, verbatim) = match parse_prefix(&self.inner) { Some(prefix) => { prefix.hash(h); - prefix.len() + (prefix.len(), prefix.is_verbatim()) } - None => 0, + None => (0, false), }; let bytes = &bytes[prefix_len..]; @@ -2935,7 +2935,8 @@ impl Hash for Path { let mut bytes_hashed = 0; for i in 0..bytes.len() { - if is_sep_byte(bytes[i]) { + let is_sep = if verbatim { is_verbatim_sep(bytes[i]) } else { is_sep_byte(bytes[i]) }; + if is_sep { if i > component_start { let to_hash = &bytes[component_start..i]; h.write(to_hash); @@ -2943,11 +2944,18 @@ impl Hash for Path { } // skip over separator and optionally a following CurDir item - // since components() would normalize these away - component_start = i + match bytes[i..] { - [_, b'.', b'/', ..] | [_, b'.'] => 2, - _ => 1, - }; + // since components() would normalize these away. + component_start = i + 1; + + let tail = &bytes[component_start..]; + + if !verbatim { + component_start += match tail { + [b'.'] => 1, + [b'.', sep @ _, ..] if is_sep_byte(*sep) => 1, + _ => 0, + }; + } } } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 2bf499e1ab8..0ab5956e1bc 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1498,6 +1498,20 @@ pub fn test_compare() { relative_from: Some("") ); + tc!("foo/.", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/./bar", "foo/bar", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + tc!("foo/bar", "foo", eq: false, starts_with: true, @@ -1541,6 +1555,27 @@ pub fn test_compare() { ends_with: true, relative_from: Some("") ); + + tc!(r"C:\foo\.\bar.txt", r"C:\foo\bar.txt", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!(r"C:\foo\.", r"C:\foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!(r"\\?\C:\foo\.\bar.txt", r"\\?\C:\foo\bar.txt", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); } } |
