Skip to content
forked from rust-lang/rust
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ee06e7d

Browse files
authoredJul 7, 2024
Rollup merge of rust-lang#127297 - the8472:path-new-hash, r=Nilstrieb
Improve std::Path's Hash quality by avoiding prefix collisions This adds a bit rotation to the already existing state so that the same sequence of characters chunked at different offsets into separate path components results in different hashes. The tests are from rust-lang#127255 Closes rust-lang#127254
2 parents 2206c6b + ed3d487 commit ee06e7d

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed
 

‎std/src/path.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -3192,15 +3192,19 @@ impl Hash for Path {
31923192
let bytes = &bytes[prefix_len..];
31933193

31943194
let mut component_start = 0;
3195-
let mut bytes_hashed = 0;
3195+
// track some extra state to avoid prefix collisions.
3196+
// ["foo", "bar"] and ["foobar"], will have the same payload bytes
3197+
// but result in different chunk_bits
3198+
let mut chunk_bits: usize = 0;
31963199

31973200
for i in 0..bytes.len() {
31983201
let is_sep = if verbatim { is_verbatim_sep(bytes[i]) } else { is_sep_byte(bytes[i]) };
31993202
if is_sep {
32003203
if i > component_start {
32013204
let to_hash = &bytes[component_start..i];
3205+
chunk_bits = chunk_bits.wrapping_add(to_hash.len());
3206+
chunk_bits = chunk_bits.rotate_right(2);
32023207
h.write(to_hash);
3203-
bytes_hashed += to_hash.len();
32043208
}
32053209

32063210
// skip over separator and optionally a following CurDir item
@@ -3221,11 +3225,12 @@ impl Hash for Path {
32213225

32223226
if component_start < bytes.len() {
32233227
let to_hash = &bytes[component_start..];
3228+
chunk_bits = chunk_bits.wrapping_add(to_hash.len());
3229+
chunk_bits = chunk_bits.rotate_right(2);
32243230
h.write(to_hash);
3225-
bytes_hashed += to_hash.len();
32263231
}
32273232

3228-
h.write_usize(bytes_hashed);
3233+
h.write_usize(chunk_bits);
32293234
}
32303235
}
32313236

‎std/src/path/tests.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,20 @@ pub fn test_compare() {
16191619
relative_from: Some("")
16201620
);
16211621

1622+
tc!("foo//", "foo",
1623+
eq: true,
1624+
starts_with: true,
1625+
ends_with: true,
1626+
relative_from: Some("")
1627+
);
1628+
1629+
tc!("foo///", "foo",
1630+
eq: true,
1631+
starts_with: true,
1632+
ends_with: true,
1633+
relative_from: Some("")
1634+
);
1635+
16221636
tc!("foo/.", "foo",
16231637
eq: true,
16241638
starts_with: true,
@@ -1633,13 +1647,34 @@ pub fn test_compare() {
16331647
relative_from: Some("")
16341648
);
16351649

1650+
tc!("foo/.//bar", "foo/bar",
1651+
eq: true,
1652+
starts_with: true,
1653+
ends_with: true,
1654+
relative_from: Some("")
1655+
);
1656+
1657+
tc!("foo//./bar", "foo/bar",
1658+
eq: true,
1659+
starts_with: true,
1660+
ends_with: true,
1661+
relative_from: Some("")
1662+
);
1663+
16361664
tc!("foo/bar", "foo",
16371665
eq: false,
16381666
starts_with: true,
16391667
ends_with: false,
16401668
relative_from: Some("bar")
16411669
);
16421670

1671+
tc!("foo/bar", "foobar",
1672+
eq: false,
1673+
starts_with: false,
1674+
ends_with: false,
1675+
relative_from: None
1676+
);
1677+
16431678
tc!("foo/bar/baz", "foo/bar",
16441679
eq: false,
16451680
starts_with: true,

0 commit comments

Comments
 (0)
Failed to load comments.