Created
July 24, 2015 07:04
-
-
Save jcdickinson/4bda826eb2e3f58e38c4 to your computer and use it in GitHub Desktop.
Mumur3 C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Security.Cryptography; | |
/// <summary> | |
/// Represents the Murmur 3 Hash Algorithm. | |
/// </summary> | |
/// <remarks> | |
/// Murmur 3 is not cryptographically secure, instead it is used | |
/// for content identification. | |
/// </remarks> | |
public sealed class Murmur3 : HashAlgorithm | |
{ | |
private const int InputBlockSizeBytes = 16; | |
private const ulong C1 = 0x87c37b91114253d5ul; | |
private const ulong C2 = 0x4cf5ad432745937ful; | |
// Only provide the X64 implementation. | |
/// <summary> | |
/// When overridden in a derived class, gets a value indicating whether multiple blocks can be transformed. | |
/// </summary> | |
/// <returns>true if multiple blocks can be transformed; otherwise, false.</returns> | |
public override bool CanTransformMultipleBlocks | |
{ | |
get { return true; } | |
} | |
/// <summary> | |
/// Gets the size, in bits, of the computed hash code. | |
/// </summary> | |
/// <returns>The size, in bits, of the computed hash code.</returns> | |
public override int HashSize | |
{ | |
get { return 128; } | |
} | |
/// <summary> | |
/// Gets the input block size. | |
/// </summary> | |
/// <returns>The input block size.</returns> | |
public override int InputBlockSize | |
{ | |
get { return InputBlockSizeBytes; } | |
} | |
/// <summary> | |
/// Gets the output block size. | |
/// </summary> | |
/// <returns>The output block size.</returns> | |
public override int OutputBlockSize | |
{ | |
get { return InputBlockSizeBytes; } | |
} | |
/// <summary> | |
/// Gets a value indicating whether the current transform can be reused. | |
/// </summary> | |
/// <returns>Always true.</returns> | |
public override bool CanReuseTransform | |
{ | |
get { return true; } | |
} | |
/// <summary> | |
/// Gets the seed used in the algorithm. | |
/// </summary> | |
/// <value> | |
/// The seed used in the algorithm. | |
/// </value> | |
public long Seed | |
{ | |
get { return unchecked((long)_seed); } | |
} | |
private readonly ulong _seed; | |
private ulong _h1; | |
private ulong _h2; | |
private ulong _length; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="Murmur3"/> class. | |
/// </summary> | |
public Murmur3() | |
: this(0) | |
{ } | |
/// <summary> | |
/// Initializes a new instance of the <see cref="Murmur3" /> class. | |
/// </summar> | |
/// <param name="seed">The seed for the hash.</param> | |
public Murmur3(long seed) | |
{ | |
_seed = unchecked((ulong)seed); | |
Initialize(); | |
} | |
/// <summary> | |
/// Creates an instance of the default implementation of <see cref="Murmur3"/>. | |
/// </summary> | |
/// <returns>A new instance of <see cref="Murmur3"/>.</returns> | |
public new static Murmur3 Create() | |
{ | |
// TODO: X86/X64. | |
return new Murmur3(); | |
} | |
/// <summary> | |
/// Creates an instance of the specified implementation of <see cref="Murmur3"/>. | |
/// </summary> | |
/// <param name="hashName">The name of the specific implementation of <see cref="Murmur3"/> | |
/// to be used.</param> | |
/// <returns>A new instance of <see cref="Murmur3"/> using the specified implementation.</returns> | |
public new static Murmur3 Create(string hashName) | |
{ | |
// TODO: X86/X64 + 32/64/128. | |
return new Murmur3(); | |
} | |
/// <summary> | |
/// Routes data written to the object into the hash algorithm for computing the hash. | |
/// </summary> | |
/// <param name="array">The input to compute the hash code for.</param> | |
/// <param name="ibStart">The offset into the byte array from which to begin using data.</param> | |
/// <param name="cbSize">The number of bytes in the byte array to use as data.</param> | |
//[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
//[TargetedPatchingOptOut("Inlining across NGen images crucial for runtime performance")] | |
protected override void HashCore(byte[] array, int ibStart, int cbSize) | |
{ | |
_length += (uint)cbSize; | |
unsafe | |
{ | |
fixed (byte* pbyte = &array[ibStart]) | |
{ | |
byte* ppbyte = pbyte; | |
var k1 = 0ul; | |
var k2 = 0ul; | |
while (cbSize >= InputBlockSizeBytes) | |
{ | |
k1 = *((ulong*)ppbyte); | |
ppbyte += 8; | |
k2 = *((ulong*)ppbyte); | |
ppbyte += 8; | |
cbSize -= 16; | |
_h1 = _h1 ^ (Rol(k1 * C1, 31) * C2); | |
_h1 = (Rol(_h1, 27) + _h2) * 5 + 0x52dce729; | |
_h2 = _h2 ^ (Rol(k2 * C2, 33) * C1); | |
_h2 = (Rol(_h2, 31) + _h1) * 5 + 0x38495ab5; | |
} | |
// ------------ TAIL ALGORITHM ------------ | |
k1 = 0; | |
k2 = 0; | |
switch (cbSize & 15) | |
{ | |
case 15: k2 ^= (ulong)(*(ppbyte + 14)) << 48; goto case 14; | |
case 14: k2 ^= (ulong)(*(ppbyte + 13)) << 40; goto case 13; | |
case 13: k2 ^= (ulong)(*(ppbyte + 12)) << 32; goto case 12; | |
case 12: k2 ^= (ulong)(*(ppbyte + 11)) << 24; goto case 11; | |
case 11: k2 ^= (ulong)(*(ppbyte + 10)) << 16; goto case 10; | |
case 10: k2 ^= (ulong)(*(ppbyte + 9)) << 8; goto case 9; | |
case 9: | |
k2 ^= (ulong)(*(ppbyte + 8)); | |
_h2 ^= (Rol(k2 * C2, 33) * C1); | |
goto case 8; | |
case 8: k1 ^= (ulong)(*(ppbyte + 7)) << 56; goto case 7; | |
case 7: k1 ^= (ulong)(*(ppbyte + 6)) << 48; goto case 6; | |
case 6: k1 ^= (ulong)(*(ppbyte + 5)) << 40; goto case 5; | |
case 5: k1 ^= (ulong)(*(ppbyte + 4)) << 32; goto case 4; | |
case 4: k1 ^= (ulong)(*(ppbyte + 3)) << 24; goto case 3; | |
case 3: k1 ^= (ulong)(*(ppbyte + 2)) << 16; goto case 2; | |
case 2: k1 ^= (ulong)(*(ppbyte + 1)) << 8; goto case 1; | |
case 1: | |
k1 ^= (ulong)(*(ppbyte)); | |
_h1 ^= (Rol(k1 * C1, 31) * C2); | |
break; | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// Finalizes the hash computation after the last data is processed by the cryptographic stream object. | |
/// </summary> | |
/// <returns> | |
/// The computed hash code. | |
/// </returns> | |
//[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
//[TargetedPatchingOptOut("Inlining across NGen images crucial for runtime performance")] | |
protected override byte[] HashFinal() | |
{ | |
_h1 ^= _length; | |
_h2 ^= _length; | |
_h1 += _h2; | |
_h2 += _h1; | |
_h1 = FinalMix(_h1); | |
_h2 = FinalMix(_h2); | |
_h1 += _h2; | |
_h2 += _h1; | |
var result = new byte[16]; | |
UInt64(result, 0, _h1); | |
UInt64(result, 8, _h2); | |
return result; | |
} | |
//[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
//[TargetedPatchingOptOut("Inlining across NGen images crucial for runtime performance")] | |
private static ulong FinalMix(ulong value) | |
{ | |
value = (value ^ (value >> 33)) * 0xff51afd7ed558ccdul; | |
value = (value ^ (value >> 33)) * 0xc4ceb9fe1a85ec53ul; | |
value ^= value >> 33; | |
return value; | |
} | |
/// <summary> | |
/// Initializes an implementation of the <see cref="T:System.Security.Cryptography.HashAlgorithm" /> class. | |
/// </summary> | |
public override void Initialize() | |
{ | |
_h1 = _h2 = _seed; | |
_length = 0ul; | |
} | |
#region Math Utils | |
//[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
//[TargetedPatchingOptOut("Inlining across NGen images crucial for runtime performance")] | |
static ulong Rol(ulong original, byte bits) | |
{ | |
return (original << bits) | (original >> (64 - bits)); | |
} | |
//[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
//[TargetedPatchingOptOut("Inlining across NGen images crucial for runtime performance")] | |
unsafe static void UInt64(byte[] bb, int pos, ulong value) | |
{ | |
fixed (byte* pbyte = &bb[pos]) | |
{ | |
*((ulong*)pbyte) = value; | |
} | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment