Skip to content

Commit

Permalink
Fix .NET 3.5 InternableString.GetHashCode to match the full implement…
Browse files Browse the repository at this point in the history
…ation (#8340)

Fixes part of 8329

Context
The 3.5 version of InternableString uses a simpler hash code calculation, the results of which were not matching the 4.72/Core version. This manifested as a failing unit test RetainsLastStringWithGivenHashCode in VS.

Changes Made
Made the routine return the same numbers as the other implementation. It fixed the UT and also made the hash code "better" as previously we were shifting instead of rotating bits.

Testing
Existing unit tests (previously failing).

Notes
It would be nice to figure out how to run 3.5 tests in CI.
  • Loading branch information
ladipro authored Feb 6, 2023
1 parent 4db1a71 commit fc3ab4c
Showing 1 changed file with 26 additions and 10 deletions.
36 changes: 26 additions & 10 deletions src/StringTools/InternableString.Simple.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,29 +200,45 @@ public override unsafe string ToString()
/// <returns>A stable hashcode of the string represented by this instance.</returns>
public override int GetHashCode()
{
int hashCode = 5381;
uint hash = (5381 << 16) + 5381;
bool isOddIndex = false;

if (_firstString != null)
{
foreach (char ch in _firstString)
{
unchecked
{
hashCode = hashCode * 33 ^ ch;
}
hash = HashOneCharacter(hash, ch, isOddIndex);
isOddIndex = !isOddIndex;
}
}
else if (_builder != null)
{
for (int i = 0; i < _builder.Length; i++)
{
unchecked
{
hashCode = hashCode * 33 ^ _builder[i];
}
hash = HashOneCharacter(hash, _builder[i], isOddIndex);
isOddIndex = !isOddIndex;
}
}
return hashCode;
return (int)hash;
}

/// <summary>
/// A helper to hash one character.
/// </summary>
/// <param name="hash">The running hash code.</param>
/// <param name="ch">The character to hash.</param>
/// <param name="isOddIndex">True if the index of the character in the string is odd.</param>
/// <returns></returns>
private static uint HashOneCharacter(uint hash, char ch, bool isOddIndex)
{
if (isOddIndex)
{
// The hash code was rotated for the previous character, just xor.
return hash ^ ((uint)ch << 16);
}

uint rotatedHash = (hash << 5) | (hash >> (32 - 5));
return (rotatedHash + hash) ^ ch;
}
}
}

0 comments on commit fc3ab4c

Please sign in to comment.