about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-02-12 14:01:13 +0000
committerbors <bors@rust-lang.org>2022-02-12 14:01:13 +0000
commit9c3a3e3d5bd65fd534336101a06f7b11396c3208 (patch)
tree35eee2e993e3a03d52ca18e23600052c30aa8032 /library/std/src
parentf8f175199e8db959cf25f1582157ca89ecf6210a (diff)
parent1d21ce723cfbadb711eab1cf21df28a1636b7230 (diff)
downloadrust-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.rs26
-rw-r--r--library/std/src/path/tests.rs35
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
+        );
     }
 }