murmurhash-net icon indicating copy to clipboard operation
murmurhash-net copied to clipboard

Error on TryComputeHash

Open Zetanova opened this issue 2 years ago • 1 comments

The generic HashAlgorithm.TryComputeHash(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten) implementation of this function gives an error on the FinalBlock call.

It would be nice to overload this method

Zetanova avatar Mar 23 '23 19:03 Zetanova

I experienced the same problem today when I was reimplementing MurmurHash myself and was running tests that compared my implementation to this library's implementations as a reference.

  • The root cause is (what I consider) a poor API design in .NET's HashAlgorithm class: when you subclass HashAlgorithm you need to set the hash length; this can be done in two ways:
    • By either setting the protected Int32 HashSizeValue field in your constructor - or anywhere in your subclass (because the field is fully mutable and not readonly).
    • Or by overriding the public virtual Int32 HashSize { get; } property.
    • However, if you only override HashSize and never set HashSizeValue then you'll get this error.
  • When HashAlgorithm added support for Span<Byte> in the new virtual methods TryComputeHash and TryHashFinal, the default implementations only check the protected Int32 HashSizeValue field and not the property.
  • ...but this MurmurHashXX library only overrides the properties, it never sets the HashSizeValue field.
  • So the default implementations throw the InvalidOperationException "The algorithm's implementation is incorrect." because it thinks the hash length is 0 bytes, instead of 4 or 16.
  • Subclassing MurmurHash32 and MurmurHash128 isn't really a feasible option, but the good news is that you can use reflection to correctly set the field right-after you get an instance of the objects.

Like so:

private static readonly FieldInfo _hashAlgorithm_HashSizeValue_Field = typeof(HashAlgorithm).GetField( "HashSizeValue", BindingFlags.NonPublic | BindingFlags.Instance ) ?? throw new InvalidOperationException( "Couldn't find HashAlgorithm.HashSizeValue field." );

private static void FixHashSizeValueField( this HashAlgorithm hashAlgo )
{
	_hashAlgorithm_HashSizeValue_Field.SetValue( obj: hashAlgo, value: hashAlgo.HashSize );
}

daiplusplus avatar Feb 17 '24 13:02 daiplusplus