Skip to content

Commit

Permalink
Add NonCryptographicHashAlgorithm Clone methods (#113087)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephentoub authored Mar 3, 2025
1 parent 12e0ad6 commit 3d27554
Show file tree
Hide file tree
Showing 18 changed files with 164 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/libraries/System.IO.Hashing/ref/System.IO.Hashing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public sealed partial class Crc32 : System.IO.Hashing.NonCryptographicHashAlgori
{
public Crc32() : base (default(int)) { }
public override void Append(System.ReadOnlySpan<byte> source) { }
public System.IO.Hashing.Crc32 Clone() { throw null; }
[System.CLSCompliantAttribute(false)]
public uint GetCurrentHashAsUInt32() { throw null; }
protected override void GetCurrentHashCore(System.Span<byte> destination) { }
Expand All @@ -25,6 +26,7 @@ public override void Reset() { }
public sealed partial class Crc64 : System.IO.Hashing.NonCryptographicHashAlgorithm
{
public Crc64() : base (default(int)) { }
public System.IO.Hashing.Crc64 Clone() { throw null; }
public override void Append(System.ReadOnlySpan<byte> source) { }
[System.CLSCompliantAttribute(false)]
public ulong GetCurrentHashAsUInt64() { throw null; }
Expand Down Expand Up @@ -64,6 +66,7 @@ public sealed partial class XxHash128 : System.IO.Hashing.NonCryptographicHashAl
public XxHash128() : base (default(int)) { }
public XxHash128(long seed) : base (default(int)) { }
public override void Append(System.ReadOnlySpan<byte> source) { }
public System.IO.Hashing.XxHash128 Clone() { throw null; }
protected override void GetCurrentHashCore(System.Span<byte> destination) { }
public static byte[] Hash(byte[] source) { throw null; }
public static byte[] Hash(byte[] source, long seed) { throw null; }
Expand All @@ -77,6 +80,7 @@ public sealed partial class XxHash3 : System.IO.Hashing.NonCryptographicHashAlgo
public XxHash3() : base (default(int)) { }
public XxHash3(long seed) : base (default(int)) { }
public override void Append(System.ReadOnlySpan<byte> source) { }
public System.IO.Hashing.XxHash3 Clone() { throw null; }
[System.CLSCompliantAttribute(false)]
public ulong GetCurrentHashAsUInt64() { throw null; }
protected override void GetCurrentHashCore(System.Span<byte> destination) { }
Expand All @@ -94,6 +98,7 @@ public sealed partial class XxHash32 : System.IO.Hashing.NonCryptographicHashAlg
public XxHash32() : base (default(int)) { }
public XxHash32(int seed) : base (default(int)) { }
public override void Append(System.ReadOnlySpan<byte> source) { }
public System.IO.Hashing.XxHash32 Clone() { throw null; }
[System.CLSCompliantAttribute(false)]
public uint GetCurrentHashAsUInt32() { throw null; }
protected override void GetCurrentHashCore(System.Span<byte> destination) { }
Expand All @@ -111,6 +116,7 @@ public sealed partial class XxHash64 : System.IO.Hashing.NonCryptographicHashAlg
public XxHash64() : base (default(int)) { }
public XxHash64(long seed) : base (default(int)) { }
public override void Append(System.ReadOnlySpan<byte> source) { }
public System.IO.Hashing.XxHash64 Clone() { throw null; }
[System.CLSCompliantAttribute(false)]
public ulong GetCurrentHashAsUInt64() { throw null; }
protected override void GetCurrentHashCore(System.Span<byte> destination) { }
Expand Down
10 changes: 10 additions & 0 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ public Crc32()
{
}

/// <summary>Initializes a new instance of the <see cref="Crc32"/> class using the state from another instance.</summary>
private Crc32(uint crc) : base(Size)
{
_crc = crc;
}

/// <summary>Returns a clone of the current instance, with a copy of the current instance's internal state.</summary>
/// <returns>A new instance that will produce the same sequence of values as the current instance.</returns>
public Crc32 Clone() => new(_crc);

/// <summary>
/// Appends the contents of <paramref name="source"/> to the data already
/// processed for the current hash computation.
Expand Down
10 changes: 10 additions & 0 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc64.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ public Crc64()
{
}

/// <summary>Initializes a new instance of the <see cref="Crc64"/> class using the state from another instance.</summary>
private Crc64(ulong crc) : base(Size)
{
_crc = crc;
}

/// <summary>Returns a clone of the current instance, with a copy of the current instance's internal state.</summary>
/// <returns>A new instance that will produce the same sequence of values as the current instance.</returns>
public Crc64 Clone() => new(_crc);

/// <summary>
/// Appends the contents of <paramref name="source"/> to the data already
/// processed for the current hash computation.
Expand Down
10 changes: 10 additions & 0 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ public XxHash128(long seed) : base(HashLengthInBytes)
Initialize(ref _state, (ulong)seed);
}

/// <summary>Initializes a new instance of the <see cref="XxHash128"/> class using the state from another instance.</summary>
private XxHash128(State state) : base(HashLengthInBytes)
{
_state = state;
}

/// <summary>Returns a clone of the current instance, with a copy of the current instance's internal state.</summary>
/// <returns>A new instance that will produce the same sequence of values as the current instance.</returns>
public XxHash128 Clone() => new(_state);

/// <summary>Computes the XXH128 hash of the provided <paramref name="source"/> data.</summary>
/// <param name="source">The data to hash.</param>
/// <returns>The XXH128 128-bit hash code of the provided data.</returns>
Expand Down
10 changes: 10 additions & 0 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ public XxHash3(long seed) : base(HashLengthInBytes)
Initialize(ref _state, (ulong)seed);
}

/// <summary>Initializes a new instance of the <see cref="XxHash3"/> class using the state from another instance.</summary>
private XxHash3(State state) : base(HashLengthInBytes)
{
_state = state;
}

/// <summary>Returns a clone of the current instance, with a copy of the current instance's internal state.</summary>
/// <returns>A new instance that will produce the same sequence of values as the current instance.</returns>
public XxHash3 Clone() => new(_state);

/// <summary>Computes the XXH3 hash of the provided <paramref name="source"/> data.</summary>
/// <param name="source">The data to hash.</param>
/// <returns>The XXH3 64-bit hash code of the provided data.</returns>
Expand Down
17 changes: 17 additions & 0 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ public XxHash32(int seed)
Reset();
}

/// <summary>Initializes a new instance of the <see cref="XxHash32"/> class using the state from another instance.</summary>
private XxHash32(uint seed, State state, byte[]? holdback, int length) :
base(HashSize)
{
_seed = seed;
_state = state;
if (((int)length & 0x0F) > 0)
{
_holdback = (byte[]?)holdback?.Clone();
}
_length = length;
}

/// <summary>Returns a clone of the current instance, with a copy of the current instance's internal state.</summary>
/// <returns>A new instance that will produce the same sequence of values as the current instance.</returns>
public XxHash32 Clone() => new(_seed, _state, _holdback, _length);

/// <summary>
/// Resets the hash computation to the initial state.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ public XxHash64(long seed)
Reset();
}

/// <summary>Initializes a new instance of the <see cref="XxHash64"/> class using the state from another instance.</summary>
private XxHash64(ulong seed, State state, byte[]? holdback, long length) :
base(HashSize)
{
_seed = seed;
_state = state;
if (((int)length & 0x1F) > 0)
{
_holdback = (byte[]?)holdback?.Clone();
}
_length = length;
}

/// <summary>Returns a clone of the current instance, with a copy of the current instance's internal state.</summary>
/// <returns>A new instance that will produce the same sequence of values as the current instance.</returns>
public XxHash64 Clone() => new(_seed, _state, _holdback, _length);

/// <summary>
/// Resets the hash computation to the initial state.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.IO.Hashing/tests/Crc32Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public static IEnumerable<object[]> TestCases
};

protected override NonCryptographicHashAlgorithm CreateInstance() => new Crc32();
protected override NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance) => ((Crc32)instance).Clone();

protected override byte[] StaticOneShot(byte[] source) => Crc32.Hash(source);

Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.IO.Hashing/tests/Crc64Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public static IEnumerable<object[]> TestCases
};

protected override NonCryptographicHashAlgorithm CreateInstance() => new Crc64();
protected override NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance) => ((Crc64)instance).Clone();

protected override byte[] StaticOneShot(byte[] source) => Crc64.Hash(source);

Expand Down
26 changes: 26 additions & 0 deletions src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ protected NonCryptoHashTestDriver(byte[] emptyHash)
}

protected abstract NonCryptographicHashAlgorithm CreateInstance();
protected abstract NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance);

protected abstract byte[] StaticOneShot(byte[] source);
protected abstract byte[] StaticOneShot(ReadOnlySpan<byte> source);
Expand Down Expand Up @@ -283,6 +284,31 @@ public void StaticVerifyTryOneShotSpanTooShortNoWrites()
}
}

[Fact]
public void Clone_ProducesSameSequence()
{
NonCryptographicHashAlgorithm hash = CreateInstance();

for (int outer = 0; outer < 4; outer++)
{
NonCryptographicHashAlgorithm clone = Clone(hash);
Assert.Equal(hash.HashLengthInBytes, clone.HashLengthInBytes);
Assert.Equal(hash.GetCurrentHash(), clone.GetCurrentHash());

Random r = new Random(42);
byte[] bytes = new byte[r.Next(1, 10)];

for (int inner = 0; inner < 4; inner++)
{
r.NextBytes(bytes);
hash.Append(bytes);
clone.Append(bytes);

Assert.Equal(hash.GetCurrentHash(), clone.GetCurrentHash());
}
}
}

private void VerifyEmptyResult(ReadOnlySpan<byte> result)
{
if (!result.SequenceEqual(_emptyHash))
Expand Down
25 changes: 25 additions & 0 deletions src/libraries/System.IO.Hashing/tests/XxHash128Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,31 @@ public void Hash_Streaming_Expected()
}
}

[Fact]
public void Clone_ProducesSameSequence()
{
XxHash128 hash = new(42);

for (int outer = 0; outer < 4; outer++)
{
XxHash128 clone = hash.Clone();
Assert.Equal(hash.HashLengthInBytes, clone.HashLengthInBytes);
Assert.Equal(hash.GetCurrentHash(), clone.GetCurrentHash());

Random r = new Random(42);
byte[] bytes = new byte[r.Next(1, 10)];

for (int inner = 0; inner < 4; inner++)
{
r.NextBytes(bytes);
hash.Append(bytes);
clone.Append(bytes);

Assert.Equal(hash.GetCurrentHash(), clone.GetCurrentHash());
}
}
}

private static Hash128 ReadHashBigEndian(ReadOnlySpan<byte> span)
{
var high = BinaryPrimitives.ReadUInt64BigEndian(span);
Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public static IEnumerable<object[]> LargeTestCases
};

protected override NonCryptographicHashAlgorithm CreateInstance() => new XxHash32(Seed);
protected override NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance) => ((XxHash32)instance).Clone();

protected override byte[] StaticOneShot(byte[] source) => XxHash32.Hash(source, Seed);

Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public static IEnumerable<object[]> LargeTestCases
};

protected override NonCryptographicHashAlgorithm CreateInstance() => new XxHash32();
protected override NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance) => ((XxHash32)instance).Clone();

protected override byte[] StaticOneShot(byte[] source) => XxHash32.Hash(source);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public static IEnumerable<object[]> LargeTestCases
};

protected override NonCryptographicHashAlgorithm CreateInstance() => new XxHash32(Seed);
protected override NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance) => ((XxHash32)instance).Clone();

protected override byte[] StaticOneShot(byte[] source) => XxHash32.Hash(source, Seed);

Expand Down
25 changes: 25 additions & 0 deletions src/libraries/System.IO.Hashing/tests/XxHash3Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,31 @@ public void Hash_Streaming_Expected()
}
}

[Fact]
public void Clone_ProducesSameSequence()
{
XxHash3 hash = new(42);

for (int outer = 0; outer < 4; outer++)
{
XxHash3 clone = hash.Clone();
Assert.Equal(hash.HashLengthInBytes, clone.HashLengthInBytes);
Assert.Equal(hash.GetCurrentHash(), clone.GetCurrentHash());

Random r = new Random(42);
byte[] bytes = new byte[r.Next(1, 10)];

for (int inner = 0; inner < 4; inner++)
{
r.NextBytes(bytes);
hash.Append(bytes);
clone.Append(bytes);

Assert.Equal(hash.GetCurrentHash(), clone.GetCurrentHash());
}
}
}

private static IEnumerable<(ulong Hash, long Seed, string Ascii)> TestCases()
{
yield return (Hash: 0x2d06800538d394c2UL, Seed: 0x0000000000000000L, Ascii: "");
Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public static IEnumerable<object[]> TestCases
};

protected override NonCryptographicHashAlgorithm CreateInstance() => new XxHash64(Seed);
protected override NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance) => ((XxHash64)instance).Clone();

protected override byte[] StaticOneShot(byte[] source) => XxHash64.Hash(source, Seed);

Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ public static IEnumerable<object[]> LargeTestCases
};

protected override NonCryptographicHashAlgorithm CreateInstance() => new XxHash64();
protected override NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance) => ((XxHash64)instance).Clone();

protected override byte[] StaticOneShot(byte[] source) => XxHash64.Hash(source);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public static IEnumerable<object[]> LargeTestCases
};

protected override NonCryptographicHashAlgorithm CreateInstance() => new XxHash64(Seed);
protected override NonCryptographicHashAlgorithm Clone(NonCryptographicHashAlgorithm instance) => ((XxHash64)instance).Clone();

protected override byte[] StaticOneShot(byte[] source) => XxHash64.Hash(source, Seed);

Expand Down

0 comments on commit 3d27554

Please sign in to comment.