std: Base Hash for Path on its iterator

Almost all operations on Path are based on the components iterator in one form
or another to handle equivalent paths. The `Hash` implementations, however,
mistakenly just went straight to the underlying `OsStr`, causing these
equivalent paths to not get merged together.

This commit updates the `Hash` implementation to also be based on the iterator
which should ensure that if two paths are equal they hash to the same thing.

cc #29008, but doesn't close it
This commit is contained in:
Alex Crichton 2015-10-29 10:56:54 -07:00
parent 01fd4d6227
commit d2dd700891

View File

@ -103,6 +103,7 @@ use borrow::{Borrow, IntoCow, ToOwned, Cow};
use cmp;
use fmt;
use fs;
use hash::{Hash, Hasher};
use io;
use iter;
use mem;
@ -446,7 +447,7 @@ enum State {
///
/// Does not occur on Unix.
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Copy, Clone, Eq, Hash, Debug)]
#[derive(Copy, Clone, Eq, Debug)]
pub struct PrefixComponent<'a> {
/// The prefix as an unparsed `OsStr` slice.
raw: &'a OsStr,
@ -490,6 +491,13 @@ impl<'a> cmp::Ord for PrefixComponent<'a> {
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Hash for PrefixComponent<'a> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.parsed.hash(h);
}
}
/// A single component of a path.
///
/// See the module documentation for an in-depth explanation of components and
@ -932,7 +940,7 @@ impl<'a> cmp::Ord for Components<'a> {
/// path.push("system32");
/// path.set_extension("dll");
/// ```
#[derive(Clone, Hash)]
#[derive(Clone)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct PathBuf {
inner: OsString
@ -1171,6 +1179,13 @@ impl cmp::PartialEq for PathBuf {
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl Hash for PathBuf {
fn hash<H: Hasher>(&self, h: &mut H) {
self.as_path().hash(h)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl cmp::Eq for PathBuf {}
@ -1224,7 +1239,6 @@ impl Into<OsString> for PathBuf {
/// let parent_dir = path.parent();
/// ```
///
#[derive(Hash)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Path {
inner: OsStr
@ -1809,6 +1823,15 @@ impl cmp::PartialEq for Path {
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl Hash for Path {
fn hash<H: Hasher>(&self, h: &mut H) {
for component in self.components() {
component.hash(h);
}
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl cmp::Eq for Path {}
@ -3035,6 +3058,14 @@ mod tests {
#[test]
pub fn test_compare() {
use hash::{Hash, Hasher, SipHasher};
fn hash<T: Hash>(t: T) -> u64 {
let mut s = SipHasher::new_with_keys(0, 0);
t.hash(&mut s);
s.finish()
}
macro_rules! tc(
($path1:expr, $path2:expr, eq: $eq:expr,
starts_with: $starts_with:expr, ends_with: $ends_with:expr,
@ -3045,6 +3076,9 @@ mod tests {
let eq = path1 == path2;
assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}",
$path1, $path2, $eq, eq);
assert!($eq == (hash(path1) == hash(path2)),
"{:?} == {:?}, expected {:?}, got {} and {}",
$path1, $path2, $eq, hash(path1), hash(path2));
let starts_with = path1.starts_with(path2);
assert!(starts_with == $starts_with,