RelativePath.GetHashCode uses a custom vectorised function for calculating the HashCode of a string that I once made for my Virtual FileSystem. This function is case-sensitive (as it hashes the raw bytes), however the current specification suggests that paths should be compared case insensitive.
More precisely, our GetHashCode is case sensitive, while our Equals is case insensitive, which breaks the contract.
Steps to reproduce
Hash two RelativePath(s) with different casing, and note the hash differs.
What is the expected behaviour?
For any two strings which are equal with Equals, their GetHashCode must match too.
Design
Apply the following steps:
Create helper method which converts to lower on stack, before hashing. GetNonRandomizedHashCode32Lower
Create benchmark for Hashing with string.GetHashCode(OrdinalIgnoreCase) vs GetNonRandomizedHashCode32Lower
Compare the performance of the two, pick the faster of the two.
Apply the new faster hash function to AbsolutePath too.
Note:
Use TextInfo.ChangeCase<ToUpperConversion> for changing the case. It contains a backported vectorised implementation from .NET 8.
We can switch to regular ToLower Span extension once we start targeting .NET 8. If you want, can use #if !NET7_0_OR_GREATER define to include conditional, so we don't forget to replace it.
Other information
When the Path library was initially rewritten by me, the requirements and implementation were a bit different. Back then we converted RelativePath(s) into lowercase. However, circumstances have changed, and we stopped doing this a while back.
Bug Report
Summary
RelativePath.GetHashCode
uses a custom vectorised function for calculating the HashCode of a string that I once made for my Virtual FileSystem. This function is case-sensitive (as it hashes the raw bytes), however the current specification suggests that paths should be compared case insensitive.More precisely, our
GetHashCode
is case sensitive, while ourEquals
is case insensitive, which breaks the contract.Steps to reproduce
Hash two
RelativePath
(s) with different casing, and note the hash differs.What is the expected behaviour?
For any two strings which are equal with
Equals
, theirGetHashCode
must match too.Design
Apply the following steps:
GetNonRandomizedHashCode32Lower
string.GetHashCode(OrdinalIgnoreCase)
vsGetNonRandomizedHashCode32Lower
AbsolutePath
too.Note:
Use
TextInfo.ChangeCase<ToUpperConversion>
for changing the case. It contains a backported vectorised implementation from .NET 8. We can switch to regularToLower
Span extension once we start targeting .NET 8. If you want, can use#if !NET7_0_OR_GREATER
define to include conditional, so we don't forget to replace it.Other information
When the Path library was initially rewritten by me, the requirements and implementation were a bit different. Back then we converted
RelativePath
(s) into lowercase. However, circumstances have changed, and we stopped doing this a while back.