Permalink
Browse files

Add GetPersistentHashCode for bool, int, long, string.

  • Loading branch information...
bgrainger committed Feb 23, 2012
1 parent 41c3a3c commit 7e0605cc53e91567505027595317e162d75089a3
Showing with 155 additions and 3 deletions.
  1. +104 −1 src/Logos.Utility/HashCodeUtility.cs
  2. +51 −2 tests/Logos.Utility.Tests/HashCodeUtilityTests.cs
@@ -1,4 +1,3 @@
-
namespace Logos.Utility
{
/// <summary>
@@ -156,6 +155,110 @@ public static int CombineHashCodes(params int[] hashCodes)
}
}
+ /// <summary>
+ /// Gets a hash code for the specified <see cref="bool"/>; this hash code is guaranteed not to change in the future.
+ /// </summary>
+ /// <param name="value">The <see cref="bool"/> to hash.</param>
+ /// <returns>A hash code for the specified <see cref="bool"/>.</returns>
+ public static int GetPersistentHashCode(bool value)
+ {
+ // these values are the persistent hash codes for 0 and 1
+ return value ? -1266253386 : 1800329511;
+ }
+
+ /// <summary>
+ /// Gets a hash code for the specified <see cref="int"/>; this hash code is guaranteed not to change in the future.
+ /// </summary>
+ /// <param name="value">The <see cref="int"/> to hash.</param>
+ /// <returns>A hash code for the specified <see cref="int"/>.</returns>
+ /// <remarks>Based on <a href="http://www.concentric.net/~Ttwang/tech/inthash.htm">Robert Jenkins' 32 bit integer hash function</a>.</remarks>
+ public static int GetPersistentHashCode(int value)
+ {
+ unchecked
+ {
+ uint hash = (uint) value;
+ hash = (hash + 0x7ed55d16) + (hash << 12);
+ hash = (hash ^ 0xc761c23c) ^ (hash >> 19);
+ hash = (hash + 0x165667b1) + (hash << 5);
+ hash = (hash + 0xd3a2646c) ^ (hash << 9);
+ hash = (hash + 0xfd7046c5) + (hash << 3);
+ hash = (hash ^ 0xb55a4f09) ^ (hash >> 16);
+ return (int) hash;
+ }
+ }
+
+ /// <summary>
+ /// Gets a hash code for the specified <see cref="long"/>; this hash code is guaranteed not to change in the future.
+ /// </summary>
+ /// <param name="value">The <see cref="long"/> to hash.</param>
+ /// <returns>A hash code for the specified <see cref="long"/>.</returns>
+ /// <remarks>Based on <a href="http://www.concentric.net/~Ttwang/tech/inthash.htm">64 bit to 32 bit Hash Functions</a>.</remarks>
+ public static int GetPersistentHashCode(long value)
+ {
+ unchecked
+ {
+ ulong hash = (ulong) value;
+ hash = (~hash) + (hash << 18);
+ hash = hash ^ (hash >> 31);
+ hash = hash * 21;
+ hash = hash ^ (hash >> 11);
+ hash = hash + (hash << 6);
+ hash = hash ^ (hash >> 22);
+ return (int) hash;
+ }
+ }
+
+ /// <summary>
+ /// Gets a hash code for the specified <see cref="string"/>; this hash code is guaranteed not to change in the future.
+ /// </summary>
+ /// <param name="value">The <see cref="string"/> to hash.</param>
+ /// <returns>A hash code for the specified <see cref="string"/>.</returns>
+ /// <remarks>Based on <a href="http://www.azillionmonkeys.com/qed/hash.html">SuperFastHash</a>.</remarks>
+ public static int GetPersistentHashCode(string value)
+ {
+ unchecked
+ {
+ // check for degenerate input
+ if (string.IsNullOrEmpty(value))
+ return 0;
+
+ int length = value.Length;
+ uint hash = (uint) length;
+
+ int remainder = length & 1;
+ length >>= 1;
+
+ // main loop
+ int index = 0;
+ for (; length > 0; length--)
+ {
+ hash += value[index];
+ uint temp = (uint) (value[index + 1] << 11) ^ hash;
+ hash = (hash << 16) ^ temp;
+ index += 2;
+ hash += hash >> 11;
+ }
+
+ // handle odd string length
+ if (remainder == 1)
+ {
+ hash += value[index];
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ }
+
+ // force "avalanching" of final 127 bits
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return (int) hash;
+ }
+ }
+
// The "rot()" macro from http://burtleburtle.net/bob/c/lookup3.c
private static uint Rotate(uint x, int k)
{
@@ -1,5 +1,4 @@
-
-using NUnit.Framework;
+using NUnit.Framework;
namespace Logos.Utility.Tests
{
@@ -38,5 +37,55 @@ public void HashFour(int value1, int value2, int value3, int value4)
{
Assert.That(HashCodeUtility.CombineHashCodes(value1, value2, value3, value4), Is.EqualTo(HashCodeUtility.CombineHashCodes(new[] { value1, value2, value3, value4 })));
}
+
+ [TestCase(false, 1800329511)]
+ [TestCase(true, -1266253386)]
+ public void GetPersistentBoolHashCode(bool value, int expected)
+ {
+ Assert.That(HashCodeUtility.GetPersistentHashCode(value), Is.EqualTo(expected));
+ }
+
+ [TestCase(0, 1800329511)]
+ [TestCase(1, -1266253386)]
+ [TestCase(2, -496519092)]
+ [TestCase(3, 1332612764)]
+ [TestCase(4, 946348061)]
+ [TestCase(-1, -26951294)]
+ [TestCase(-2, 2115881666)]
+ public void GetPersistentIntHashCode(int value, int expected)
+ {
+ Assert.That(HashCodeUtility.GetPersistentHashCode((sbyte) value), Is.EqualTo(expected));
+ Assert.That(HashCodeUtility.GetPersistentHashCode((short) value), Is.EqualTo(expected));
+ if (value >= 0)
+ Assert.That(HashCodeUtility.GetPersistentHashCode((char) value), Is.EqualTo(expected));
+ Assert.That(HashCodeUtility.GetPersistentHashCode(value), Is.EqualTo(expected));
+ }
+
+ [TestCase(0, 720020139)]
+ [TestCase(1, 357654460)]
+ [TestCase(2, 715307540)]
+ [TestCase(3, 1072960876)]
+ [TestCase(4, 1430614333)]
+ [TestCase(-1, 532412650)]
+ [TestCase(-2, 340268856)]
+ public void GetPersistentLongHashCode(long value, int expected)
+ {
+ Assert.That(HashCodeUtility.GetPersistentHashCode(value), Is.EqualTo(expected));
+ }
+
+ [TestCase("", 0)]
+ [TestCase(null, 0)]
+ [TestCase("a", -889528276)]
+ [TestCase("b", -685344420)]
+ [TestCase("c", -414938692)]
+ [TestCase("abc", 2058321224)]
+ [TestCase("abcabc", 120553164)]
+ [TestCase("abcabca", 451022788)]
+ [TestCase("\" \"", 1671599841)]
+ [TestCase("#@!", 1671599841)]
+ public void GetPersistentHash(string value, int expected)
+ {
+ Assert.That(HashCodeUtility.GetPersistentHashCode(value), Is.EqualTo(expected));
+ }
}
}

0 comments on commit 7e0605c

Please sign in to comment.