Skip to content

IBlockTransformer.TransformBytes(byte[], int, int) ignores offset and count #4

Closed

Description

@JeremyEspresso

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.

https://github.com/Deskasoft/HashifyNET/blob/b571c87/HashifyNet/Core/BlockTransformerBase.cs#L120-L121

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());      // 12DA77C8

644D8781 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

  1. github-actions commented on Nov 14, 2026

    @github-actions

    Thank you for highlighting this issue. We genuinely appreciate your effort!

  2. added theissue type on Nov 14, 2026
  3. LordOfXen commented on Nov 14, 2026

    @LordOfXen
    Contributor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions