Skip to content

Commit

Permalink
Plug String.GetNonRandomizedHashCode to support cosmos memory model f…
Browse files Browse the repository at this point in the history
…or strings allocated on the heap with no extra space
  • Loading branch information
quajak committed Jul 21, 2022
1 parent 67b9c8b commit 75f5245
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using Cosmos.TestRunner;

Expand Down Expand Up @@ -566,6 +567,26 @@ public static void Execute()

//TODO: Add GUID test once newGUID returns something other than a zero initialized guid.

//We need to ensure that string hashing is the same for constant strings and strings on the heap
{
var dictionary = new Dictionary<string, bool>
{
{"a", true },
{"ab", true },
{"abab", true},
{"ababa", true},
{"aba", true }
};
string t = "aba";
string a = "ab";
string b = "a";
string alt = a + b;

Assert.IsTrue(dictionary.ContainsKey("a"), "Dictionary<string, bool> ContainsKey works for length 1");
Assert.IsTrue(dictionary.ContainsKey(t), "Dictionary<string, bool> ContainsKey works for length 3");
Assert.IsTrue(dictionary.ContainsKey(alt), "Dictionary<string, bool> ContainsKey works for length 3 allocated on the heap");
Assert.IsTrue(dictionary.ContainsKey("abab"), "Dictionary<string, bool> ContainsKey works for length 4");
}
}
}
}
30 changes: 30 additions & 0 deletions source/Cosmos.Core_Plugs/System/StringImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -974,5 +974,35 @@ public static int Compare(string strA, int indexA, string strB, int indexB, int
throw new ArgumentException("Not Supported StringComparison");
}
}

public static unsafe int GetNonRandomizedHashCode(string aString)
{
// the code is the same as the one used in .net except for the explicit == 2 and == 1 cases
// we need this since a new object can start directly behind the string in memory, so the standard
// implementation would read the allocated size of the next object and use it for the hash
fixed (char* ptr = &aString.AsSpan()[0])
{
uint num = 352654597u;
uint num2 = num;
uint* ptr2 = (uint*)ptr;
int num3 = aString.Length;
while (num3 > 2)
{
num3 -= 4;
num = (global::System.Numerics.BitOperations.RotateLeft(num, 5) + num) ^ *ptr2;
num2 = (global::System.Numerics.BitOperations.RotateLeft(num2, 5) + num2) ^ ptr2[1];
ptr2 += 2;
}
if (num3 == 2)
{
num2 = (global::System.Numerics.BitOperations.RotateLeft(num2, 5) + num2) ^ *ptr2;
}
if (num3 == 1)
{
num2 = (global::System.Numerics.BitOperations.RotateLeft(num2, 5) + num2) ^ *(char*)ptr2;
}
return (int)(num + num2 * 1566083941);
}
}
}
}

0 comments on commit 75f5245

Please sign in to comment.