Skip to content

Instantly share code, notes, and snippets.

@macklinb
Last active August 27, 2024 14:12
Show Gist options
  • Save macklinb/f8bff700060377b2abaffa9acf504bcd to your computer and use it in GitHub Desktop.
Save macklinb/f8bff700060377b2abaffa9acf504bcd to your computer and use it in GitHub Desktop.
.NET's String.GetHashCode in C# and JavaScript

This is an equivalent implementation of .NET's String.GetHashCode in "safe" C# and ES5 JavaScript. If you write your own version in another language, feel free to comment below!

Notes

  • The implementation (and therefore the generated hashcode) varies depending on the version of .NET that the app was compiled with. Here I provide .NET 3.5 and .NET 4.0, although it's likely that other versions work differently.
  • In .NET 4.0, GetHashCode will often overflow the 32-bit signed integer limit. Other languages often use double-precision floating point values to represent all numbers, and will need to implement 32-bit signed integer overflow to mimic this.
  • Be mindful of accuracy when multiplying large numbers. For example in C# 782507206 + 1369868898 * 1566083941 is 2145329683215674224, but in JavaScript it's represented as 2145329683215674400.

Tests

Input .NET 3.5 .NET 4.0
"foo" 101574 1502598398
"foobar" -1268878963 836807663
"the quick brown fox jumped over the lazy dog" -772038699 -1284118495
// .NET 3.5 implementation
public int GetHashCodeDotNet35(string str)
{
int hashCode = 0;
foreach (char ch in str)
{
hashCode = (hashCode << 5) - hashCode + (int)ch;
}
return hashCode;
}
// .NET 4.0 implementation
public static int GetHashCodeDotNet40(string str)
{
int hash1 = 5381;
int hash2 = hash1;
for (int i = 0; i < str.Length; i += 2)
{
char currentChar = str[i];
hash1 = (hash1 << 5) + hash1 ^ currentChar;
if (i + 1 < str.Length)
{
char nextChar = str[i + 1];
hash2 = (hash2 << 5) + hash2 ^ nextChar;
}
}
return hash1 + hash2 * 1566083941;
}
function getHashCodeDotNet35(str)
{
var hash = 0;
for (var i = 0; i < str.length; i++)
{
var charCode = str.charCodeAt(i);
hash = (hash << 5) - hash + charCode;
}
// Force conversion to 32-bit signed integer
hash = hash & hash;
return hash;
}
function getHashCodeDotNet40(str)
{
var hash1 = 5381;
var hash2 = hash1;
for (var i = 0; i < str.length; i += 2)
{
var currentChar = str.charCodeAt(i);
hash1 = (hash1 << 5) + hash1 ^ currentChar;
if (i + 1 < str.length)
{
var nextChar = str.charCodeAt(i + 1);
hash2 = (hash2 << 5) + hash2 ^ nextChar;
}
}
// Use BigInt because JavaScript loses accuracy with very large regular floating point numbers
var result = BigInt(hash1) + BigInt(hash2) * BigInt(1566083941);
// Convert the BigInt to a signed 32-bit integer (performs overflow)
return Number(BigInt.asIntN(32, result));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment