Skip to content

Commit 56557c4

Browse files
authored
Rollup merge of #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 #127255 Closes #127254
2 parents d84d221 + f216834 commit 56557c4

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

library/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

library/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)