Repository navigation
Description
BlockTransformerBase.TransformBytes(byte[] data, int offset, int count) hashes the whole array on every call instead
of the count bytes starting at offset. Code that feeds a buffer to a block transformer in slices with this overload
gets a wrong hash, with no error.
public void TransformBytes(byte[] data, int offset, int count) =>
TransformBytes(data, 0, data.Length, CancellationToken.None);Reproduction
HashifyNET 7.1.1, .NET 8, with MurmurHash3 32-bit as the example. The overload is defined in BlockTransformerBase,
so the problem is not specific to MurmurHash3.
using HashifyNet;
using HashifyNet.Algorithms.MurmurHash;
var murmur = HashFactory<IMurmurHash3>.Create(new MurmurHash3Config { HashSizeInBits = 32 });
byte[] data = "Hello, World!"u8.ToArray();
Console.WriteLine(murmur.ComputeHash(data).AsHexString()); // 2352D5C7
var threeArg = murmur.CreateBlockTransformer();
threeArg.TransformBytes(data, 0, 5);
threeArg.TransformBytes(data, 5, data.Length - 5);
Console.WriteLine(threeArg.FinalizeHashValue().AsHexString()); // 644D8781, expected 2352D5C7
var fourArg = murmur.CreateBlockTransformer();
fourArg.TransformBytes(data, 0, 5, CancellationToken.None);
fourArg.TransformBytes(data, 5, data.Length - 5, CancellationToken.None);
Console.WriteLine(fourArg.FinalizeHashValue().AsHexString()); // 2352D5C7 (correct)
var slice = murmur.CreateBlockTransformer();
slice.TransformBytes(data, 0, 5);
Console.WriteLine(slice.FinalizeHashValue().AsHexString()); // 2352D5C7, expected 12DA77C8
Console.WriteLine(murmur.ComputeHash(data[..5]).AsHexString()); // 12DA77C8644D8781 is the MurmurHash3 of data appended twice, and the single 5-byte slice returns the hash of the whole array
(checked against the SMHasher reference implementation). The 4-argument overload, the ReadOnlySpan<byte> overload
and ComputeHash(Stream) return the correct hashes.
Suggested fix
public void TransformBytes(byte[] data, int offset, int count) =>
TransformBytes(data, offset, count, CancellationToken.None);Related
The overload just above it (L102-L112) drops its cancellation token:
public void TransformBytes(byte[] data, CancellationToken cancellationToken)
{
...
TransformBytes(data, 0, data.Length, CancellationToken.None); // should pass cancellationToken
}so cancelling has no effect when the token is passed through TransformBytes(byte[], CancellationToken).
A test that feeds the same input in slices through each TransformBytes overload and compares the result with
ComputeHash would catch both.
Activity
github-actions commentedon Nov 14, 2026
Thank you for highlighting this issue. We genuinely appreciate your effort!
LordOfXen commentedon Nov 14, 2026
Thank you for bringing this to my attention. I've patched the issue.
We may also want to add a dedicated test to our base block transformer unit tests to prevent similar bugs in the future.
If you have the time, feel free to submit a PR adding a regression test for this issue.
Best.