From 9bddd01c7ce291fb871cee38e6ef77191b0899a4 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 16 Nov 2022 06:12:53 -0500 Subject: [PATCH] Add HashTo/GetCurrentHashAs{UInt32/64} methods (#78075) * Add HashTo/GetCurrentHashAs{UInt32/64} methods * Address PR feedback --- .../ref/System.IO.Hashing.cs | 20 ++++++++++ .../src/System/IO/Hashing/Crc32.cs | 34 +++++++++++------ .../src/System/IO/Hashing/Crc64.cs | 32 ++++++++++------ .../src/System/IO/Hashing/XxHash3.cs | 34 ++++++++++++----- .../src/System/IO/Hashing/XxHash32.cs | 38 ++++++++++++++----- .../src/System/IO/Hashing/XxHash64.cs | 38 ++++++++++++++----- .../System.IO.Hashing/tests/Crc32Tests.cs | 11 ++++++ .../System.IO.Hashing/tests/Crc64Tests.cs | 11 ++++++ .../tests/NonCryptoHashTestDriver.cs | 19 ++++++++++ .../tests/System.IO.Hashing.Tests.csproj | 1 + .../tests/XxHash32Tests.007.cs | 11 ++++++ .../System.IO.Hashing/tests/XxHash32Tests.cs | 11 ++++++ .../tests/XxHash32Tests.f00d.cs | 11 ++++++ .../System.IO.Hashing/tests/XxHash3Tests.cs | 5 +++ .../tests/XxHash64Tests.007.cs | 11 ++++++ .../System.IO.Hashing/tests/XxHash64Tests.cs | 11 ++++++ .../tests/XxHash64Tests.f00d.cs | 11 ++++++ 17 files changed, 258 insertions(+), 51 deletions(-) diff --git a/src/libraries/System.IO.Hashing/ref/System.IO.Hashing.cs b/src/libraries/System.IO.Hashing/ref/System.IO.Hashing.cs index 430f12ef53ae..73f3a644506c 100644 --- a/src/libraries/System.IO.Hashing/ref/System.IO.Hashing.cs +++ b/src/libraries/System.IO.Hashing/ref/System.IO.Hashing.cs @@ -10,11 +10,15 @@ public sealed partial class Crc32 : System.IO.Hashing.NonCryptographicHashAlgori { public Crc32() : base (default(int)) { } public override void Append(System.ReadOnlySpan source) { } + [System.CLSCompliantAttribute(false)] + public uint GetCurrentHashAsUInt32() { throw null; } protected override void GetCurrentHashCore(System.Span destination) { } protected override void GetHashAndResetCore(System.Span destination) { } public static byte[] Hash(byte[] source) { throw null; } public static byte[] Hash(System.ReadOnlySpan source) { throw null; } public static int Hash(System.ReadOnlySpan source, System.Span destination) { throw null; } + [System.CLSCompliantAttribute(false)] + public static uint HashToUInt32(System.ReadOnlySpan source) { throw null; } public override void Reset() { } public static bool TryHash(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { throw null; } } @@ -22,11 +26,15 @@ public sealed partial class Crc64 : System.IO.Hashing.NonCryptographicHashAlgori { public Crc64() : base (default(int)) { } public override void Append(System.ReadOnlySpan source) { } + [System.CLSCompliantAttribute(false)] + public ulong GetCurrentHashAsUInt64() { throw null; } protected override void GetCurrentHashCore(System.Span destination) { } protected override void GetHashAndResetCore(System.Span destination) { } public static byte[] Hash(byte[] source) { throw null; } public static byte[] Hash(System.ReadOnlySpan source) { throw null; } public static int Hash(System.ReadOnlySpan source, System.Span destination) { throw null; } + [System.CLSCompliantAttribute(false)] + public static ulong HashToUInt64(System.ReadOnlySpan source) { throw null; } public override void Reset() { } public static bool TryHash(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { throw null; } } @@ -56,11 +64,15 @@ 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 source) { } + [System.CLSCompliantAttribute(false)] + public ulong GetCurrentHashAsUInt64() { throw null; } protected override void GetCurrentHashCore(System.Span destination) { } public static byte[] Hash(byte[] source) { throw null; } public static byte[] Hash(byte[] source, long seed) { throw null; } public static byte[] Hash(System.ReadOnlySpan source, long seed = (long)0) { throw null; } public static int Hash(System.ReadOnlySpan source, System.Span destination, long seed = (long)0) { throw null; } + [System.CLSCompliantAttribute(false)] + public static ulong HashToUInt64(System.ReadOnlySpan source, long seed = (long)0) { throw null; } public override void Reset() { } public static bool TryHash(System.ReadOnlySpan source, System.Span destination, out int bytesWritten, long seed = (long)0) { throw null; } } @@ -69,11 +81,15 @@ 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 source) { } + [System.CLSCompliantAttribute(false)] + public uint GetCurrentHashAsUInt32() { throw null; } protected override void GetCurrentHashCore(System.Span destination) { } public static byte[] Hash(byte[] source) { throw null; } public static byte[] Hash(byte[] source, int seed) { throw null; } public static byte[] Hash(System.ReadOnlySpan source, int seed = 0) { throw null; } public static int Hash(System.ReadOnlySpan source, System.Span destination, int seed = 0) { throw null; } + [System.CLSCompliantAttribute(false)] + public static uint HashToUInt32(System.ReadOnlySpan source, int seed = 0) { throw null; } public override void Reset() { } public static bool TryHash(System.ReadOnlySpan source, System.Span destination, out int bytesWritten, int seed = 0) { throw null; } } @@ -82,11 +98,15 @@ 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 source) { } + [System.CLSCompliantAttribute(false)] + public ulong GetCurrentHashAsUInt64() { throw null; } protected override void GetCurrentHashCore(System.Span destination) { } public static byte[] Hash(byte[] source) { throw null; } public static byte[] Hash(byte[] source, long seed) { throw null; } public static byte[] Hash(System.ReadOnlySpan source, long seed = (long)0) { throw null; } public static int Hash(System.ReadOnlySpan source, System.Span destination, long seed = (long)0) { throw null; } + [System.CLSCompliantAttribute(false)] + public static ulong HashToUInt64(System.ReadOnlySpan source, long seed = (long)0) { throw null; } public override void Reset() { } public static bool TryHash(System.ReadOnlySpan source, System.Span destination, out int bytesWritten, long seed = (long)0) { throw null; } } diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs index 05793ad4499c..23049699e470 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs @@ -11,8 +11,9 @@ namespace System.IO.Hashing /// /// /// - /// This implementation emits the answer in the Little Endian byte order so that - /// the CRC residue relationship (CRC(message concat CRC(message))) is a fixed value) holds. + /// For methods that return byte arrays or that write into spans of bytes, this implementation + /// emits the answer in the Little Endian byte order so that the CRC residue relationship + /// (CRC(message concat CRC(message))) is a fixed value) holds. /// For CRC-32 this stable output is the byte sequence { 0x1C, 0xDF, 0x44, 0x21 }, /// the Little Endian representation of 0x2144DF1C. /// @@ -77,6 +78,11 @@ protected override void GetHashAndResetCore(Span destination) _crc = InitialState; } + /// Gets the current computed hash value without modifying accumulated state. + /// The hash value for the data already provided. + [CLSCompliant(false)] + public uint GetCurrentHashAsUInt32() => ~_crc; + /// /// Computes the CRC-32 hash of the provided data. /// @@ -103,7 +109,8 @@ public static byte[] Hash(byte[] source) public static byte[] Hash(ReadOnlySpan source) { byte[] ret = new byte[Size]; - StaticHash(source, ret); + uint hash = HashToUInt32(source); + BinaryPrimitives.WriteUInt32LittleEndian(ret, hash); return ret; } @@ -127,7 +134,9 @@ public static bool TryHash(ReadOnlySpan source, Span destination, ou return false; } - bytesWritten = StaticHash(source, destination); + uint hash = HashToUInt32(source); + BinaryPrimitives.WriteUInt32LittleEndian(destination, hash); + bytesWritten = Size; return true; } @@ -146,17 +155,18 @@ public static int Hash(ReadOnlySpan source, Span destination) ThrowDestinationTooShort(); } - return StaticHash(source, destination); - } - - private static int StaticHash(ReadOnlySpan source, Span destination) - { - uint crc = InitialState; - crc = Update(crc, source); - BinaryPrimitives.WriteUInt32LittleEndian(destination, ~crc); + uint hash = HashToUInt32(source); + BinaryPrimitives.WriteUInt32LittleEndian(destination, hash); return Size; } + /// Computes the CRC-32 hash of the provided data. + /// The data to hash. + /// The computed CRC-32 hash. + [CLSCompliant(false)] + public static uint HashToUInt32(ReadOnlySpan source) => + ~Update(InitialState, source); + private static uint Update(uint crc, ReadOnlySpan source) { for (int i = 0; i < source.Length; i++) diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc64.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc64.cs index bc9917d1faa2..c4a164f88a34 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc64.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc64.cs @@ -10,7 +10,8 @@ namespace System.IO.Hashing /// /// /// - /// This implementation emits the answer in the Big Endian byte order so that + /// For methods that return byte arrays or that write into spans of bytes, + /// this implementation emits the answer in the Big Endian byte order so that /// the CRC residue relationship (CRC(message concat CRC(message))) is a fixed value) holds. /// For CRC-64 this stable output is the byte sequence /// { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }. @@ -75,6 +76,11 @@ protected override void GetHashAndResetCore(Span destination) _crc = InitialState; } + /// Gets the current computed hash value without modifying accumulated state. + /// The hash value for the data already provided. + [CLSCompliant(false)] + public ulong GetCurrentHashAsUInt64() => _crc; + /// /// Computes the CRC-64 hash of the provided data. /// @@ -101,7 +107,8 @@ public static byte[] Hash(byte[] source) public static byte[] Hash(ReadOnlySpan source) { byte[] ret = new byte[Size]; - StaticHash(source, ret); + ulong hash = HashToUInt64(source); + BinaryPrimitives.WriteUInt64BigEndian(ret, hash); return ret; } @@ -125,7 +132,9 @@ public static bool TryHash(ReadOnlySpan source, Span destination, ou return false; } - bytesWritten = StaticHash(source, destination); + ulong hash = HashToUInt64(source); + BinaryPrimitives.WriteUInt64BigEndian(destination, hash); + bytesWritten = Size; return true; } @@ -144,17 +153,18 @@ public static int Hash(ReadOnlySpan source, Span destination) ThrowDestinationTooShort(); } - return StaticHash(source, destination); - } - - private static int StaticHash(ReadOnlySpan source, Span destination) - { - ulong crc = InitialState; - crc = Update(crc, source); - BinaryPrimitives.WriteUInt64BigEndian(destination, crc); + ulong hash = HashToUInt64(source); + BinaryPrimitives.WriteUInt64BigEndian(destination, hash); return Size; } + /// Computes the CRC-64 hash of the provided data. + /// The data to hash. + /// The computed CRC-64 hash. + [CLSCompliant(false)] + public static ulong HashToUInt64(ReadOnlySpan source) => + Update(InitialState, source); + private static ulong Update(ulong crc, ReadOnlySpan source) { for (int i = 0; i < source.Length; i++) diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs index 47bf2ad2d28b..51b4b4ce35ad 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs @@ -129,7 +129,8 @@ public static byte[] Hash(byte[] source, long seed) public static byte[] Hash(ReadOnlySpan source, long seed = 0) { byte[] result = new byte[HashLengthInBytes]; - BinaryPrimitives.WriteInt64BigEndian(result, HashToInt64(source, seed)); + ulong hash = HashToUInt64(source, seed); + BinaryPrimitives.WriteUInt64BigEndian(result, hash); return result; } @@ -159,7 +160,7 @@ public static bool TryHash(ReadOnlySpan source, Span destination, ou { if (destination.Length >= sizeof(long)) { - long hash = HashToInt64(source, seed); + ulong hash = HashToUInt64(source, seed); if (BitConverter.IsLittleEndian) { @@ -175,28 +176,32 @@ public static bool TryHash(ReadOnlySpan source, Span destination, ou return false; } - // TODO https://github.com/dotnet/runtime/issues/76279: Make this public. - private static long HashToInt64(ReadOnlySpan source, long seed = 0) + /// Computes the XXH3 hash of the provided data. + /// The data to hash. + /// The seed value for this hash computation. + /// The computed XXH3 hash. + [CLSCompliant(false)] + public static ulong HashToUInt64(ReadOnlySpan source, long seed = 0) { uint length = (uint)source.Length; fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source)) { if (length <= 16) { - return (long)HashLength0To16(sourcePtr, length, (ulong)seed); + return HashLength0To16(sourcePtr, length, (ulong)seed); } if (length <= 128) { - return (long)HashLength17To128(sourcePtr, length, (ulong)seed); + return HashLength17To128(sourcePtr, length, (ulong)seed); } if (length <= MidSizeMaxBytes) { - return (long)HashLength129To240(sourcePtr, length, (ulong)seed); + return HashLength129To240(sourcePtr, length, (ulong)seed); } - return (long)HashLengthOver240(sourcePtr, length, (ulong)seed); + return HashLengthOver240(sourcePtr, length, (ulong)seed); } } @@ -310,6 +315,15 @@ public override void Append(ReadOnlySpan source) /// Writes the computed 64-bit hash value to without modifying accumulated state. /// The buffer that receives the computed hash value. protected override void GetCurrentHashCore(Span destination) + { + ulong hash = GetCurrentHashAsUInt64(); + BinaryPrimitives.WriteUInt64BigEndian(destination, hash); + } + + /// Gets the current computed hash value without modifying accumulated state. + /// The hash value for the data already provided. + [CLSCompliant(false)] + public ulong GetCurrentHashAsUInt64() { ulong current; @@ -352,11 +366,11 @@ protected override void GetCurrentHashCore(Span destination) { fixed (byte* buffer = _state.Buffer) { - current = (ulong)HashToInt64(new ReadOnlySpan(buffer, (int)_state.TotalLength), (long)_state.Seed); + current = HashToUInt64(new ReadOnlySpan(buffer, (int)_state.TotalLength), (long)_state.Seed); } } - BinaryPrimitives.WriteUInt64BigEndian(destination, current); + return current; void DigestLong(ulong* accumulators, byte* secret) { diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash32.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash32.cs index a877480727d2..a746e934f392 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash32.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash32.cs @@ -11,6 +11,10 @@ namespace System.IO.Hashing /// /// Provides an implementation of the XxHash32 algorithm. /// + /// + /// For methods that persist the computed numerical hash value as bytes, + /// the value is written in the Big Endian byte order. + /// public sealed partial class XxHash32 : NonCryptographicHashAlgorithm { private const int HashSize = sizeof(uint); @@ -109,6 +113,15 @@ public override void Append(ReadOnlySpan source) /// without modifying accumulated state. /// protected override void GetCurrentHashCore(Span destination) + { + uint hash = GetCurrentHashAsUInt32(); + BinaryPrimitives.WriteUInt32BigEndian(destination, hash); + } + + /// Gets the current computed hash value without modifying accumulated state. + /// The hash value for the data already provided. + [CLSCompliant(false)] + public uint GetCurrentHashAsUInt32() { int remainingLength = _length & 0x0F; ReadOnlySpan remaining = ReadOnlySpan.Empty; @@ -118,8 +131,7 @@ protected override void GetCurrentHashCore(Span destination) remaining = new ReadOnlySpan(_holdback, 0, remainingLength); } - uint acc = _state.Complete(_length, remaining); - BinaryPrimitives.WriteUInt32BigEndian(destination, acc); + return _state.Complete(_length, remaining); } /// @@ -168,7 +180,8 @@ public static byte[] Hash(byte[] source, int seed) public static byte[] Hash(ReadOnlySpan source, int seed = 0) { byte[] ret = new byte[HashSize]; - StaticHash(source, ret, seed); + uint hash = HashToUInt32(source, seed); + BinaryPrimitives.WriteUInt32BigEndian(ret, hash); return ret; } @@ -193,7 +206,9 @@ public static bool TryHash(ReadOnlySpan source, Span destination, ou return false; } - bytesWritten = StaticHash(source, destination, seed); + uint hash = HashToUInt32(source, seed); + BinaryPrimitives.WriteUInt32BigEndian(destination, hash); + bytesWritten = HashSize; return true; } @@ -213,10 +228,17 @@ public static int Hash(ReadOnlySpan source, Span destination, int se ThrowDestinationTooShort(); } - return StaticHash(source, destination, seed); + uint hash = HashToUInt32(source, seed); + BinaryPrimitives.WriteUInt32BigEndian(destination, hash); + return HashSize; } - private static int StaticHash(ReadOnlySpan source, Span destination, int seed) + /// Computes the XxHash32 hash of the provided data. + /// The data to hash. + /// The seed value for this hash computation. The default is zero. + /// The computed XxHash32 hash. + [CLSCompliant(false)] + public static uint HashToUInt32(ReadOnlySpan source, int seed = 0) { int totalLength = source.Length; State state = new State((uint)seed); @@ -227,9 +249,7 @@ private static int StaticHash(ReadOnlySpan source, Span destination, source = source.Slice(StripeSize); } - uint val = state.Complete(totalLength, source); - BinaryPrimitives.WriteUInt32BigEndian(destination, val); - return HashSize; + return state.Complete(totalLength, source); } } } diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.cs index 0be3bbc9f432..e57c7e8cf92e 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.cs @@ -11,6 +11,10 @@ namespace System.IO.Hashing /// /// Provides an implementation of the XxHash64 algorithm. /// + /// + /// For methods that persist the computed numerical hash value as bytes, + /// the value is written in the Big Endian byte order. + /// public sealed partial class XxHash64 : NonCryptographicHashAlgorithm { private const int HashSize = sizeof(ulong); @@ -109,6 +113,15 @@ public override void Append(ReadOnlySpan source) /// without modifying accumulated state. /// protected override void GetCurrentHashCore(Span destination) + { + ulong hash = GetCurrentHashAsUInt64(); + BinaryPrimitives.WriteUInt64BigEndian(destination, hash); + } + + /// Gets the current computed hash value without modifying accumulated state. + /// The hash value for the data already provided. + [CLSCompliant(false)] + public ulong GetCurrentHashAsUInt64() { int remainingLength = (int)_length & 0x1F; ReadOnlySpan remaining = ReadOnlySpan.Empty; @@ -118,8 +131,7 @@ protected override void GetCurrentHashCore(Span destination) remaining = new ReadOnlySpan(_holdback, 0, remainingLength); } - ulong acc = _state.Complete(_length, remaining); - BinaryPrimitives.WriteUInt64BigEndian(destination, acc); + return _state.Complete(_length, remaining); } /// @@ -168,7 +180,8 @@ public static byte[] Hash(byte[] source, long seed) public static byte[] Hash(ReadOnlySpan source, long seed = 0) { byte[] ret = new byte[HashSize]; - StaticHash(source, ret, seed); + ulong hash = HashToUInt64(source, seed); + BinaryPrimitives.WriteUInt64BigEndian(ret, hash); return ret; } @@ -193,7 +206,9 @@ public static bool TryHash(ReadOnlySpan source, Span destination, ou return false; } - bytesWritten = StaticHash(source, destination, seed); + ulong hash = HashToUInt64(source, seed); + BinaryPrimitives.WriteUInt64BigEndian(destination, hash); + bytesWritten = HashSize; return true; } @@ -213,10 +228,17 @@ public static int Hash(ReadOnlySpan source, Span destination, long s ThrowDestinationTooShort(); } - return StaticHash(source, destination, seed); + ulong hash = HashToUInt64(source, seed); + BinaryPrimitives.WriteUInt64BigEndian(destination, hash); + return HashSize; } - private static int StaticHash(ReadOnlySpan source, Span destination, long seed) + /// Computes the XxHash64 hash of the provided data. + /// The data to hash. + /// The seed value for this hash computation. + /// The computed XxHash64 hash. + [CLSCompliant(false)] + public static ulong HashToUInt64(ReadOnlySpan source, long seed = 0) { int totalLength = source.Length; State state = new State((ulong)seed); @@ -227,9 +249,7 @@ private static int StaticHash(ReadOnlySpan source, Span destination, source = source.Slice(StripeSize); } - ulong val = state.Complete((uint)totalLength, source); - BinaryPrimitives.WriteUInt64BigEndian(destination, val); - return HashSize; + return state.Complete((uint)totalLength, source); } } } diff --git a/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs b/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs index 0761450fb9a5..217d7bfe90c8 100644 --- a/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/Crc32Tests.cs @@ -141,5 +141,16 @@ public void StaticVerifyTryOneShot(TestCase testCase) { StaticVerifyTryOneShotDriver(testCase); } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyHashToUInt32(TestCase testCase) + { + var alg = new Crc32(); + alg.Append(testCase.Input); + AssertEqualHashNumber(testCase.OutputHex, alg.GetCurrentHashAsUInt32(), littleEndian: true); + + AssertEqualHashNumber(testCase.OutputHex, Crc32.HashToUInt32(testCase.Input), littleEndian: true); + } } } diff --git a/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs b/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs index 52971f0c9777..c439ec8d1b2f 100644 --- a/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/Crc64Tests.cs @@ -145,5 +145,16 @@ public void StaticVerifyTryOneShot(TestCase testCase) { StaticVerifyTryOneShotDriver(testCase); } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyHashToUInt64(TestCase testCase) + { + var alg = new Crc64(); + alg.Append(testCase.Input); + AssertEqualHashNumber(testCase.OutputHex, alg.GetCurrentHashAsUInt64()); + + AssertEqualHashNumber(testCase.OutputHex, Crc64.HashToUInt64(testCase.Input)); + } } } diff --git a/src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs b/src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs index cb684c030b84..2342f5c3916b 100644 --- a/src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs +++ b/src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Collections.Generic; using System.Reflection; using Xunit; @@ -294,6 +295,24 @@ private void VerifyEmptyResult(ReadOnlySpan result) } } + protected static void AssertEqualHashNumber(string hex, uint hash, bool littleEndian = false) + { + if (littleEndian == BitConverter.IsLittleEndian) + { + hash = BinaryPrimitives.ReverseEndianness(hash); + } + Assert.Equal(hex, hash.ToString("X8")); + } + + protected static void AssertEqualHashNumber(string hex, ulong hash, bool littleEndian = false) + { + if (littleEndian == BitConverter.IsLittleEndian) + { + hash = BinaryPrimitives.ReverseEndianness(hash); + } + Assert.Equal(hex, hash.ToString("X16")); + } + public abstract class TestCaseBase { private readonly byte[] _output; diff --git a/src/libraries/System.IO.Hashing/tests/System.IO.Hashing.Tests.csproj b/src/libraries/System.IO.Hashing/tests/System.IO.Hashing.Tests.csproj index 11e73cf70a55..264e4d0559b4 100644 --- a/src/libraries/System.IO.Hashing/tests/System.IO.Hashing.Tests.csproj +++ b/src/libraries/System.IO.Hashing/tests/System.IO.Hashing.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs index 28d00ce94d02..db6f73da73e9 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs @@ -199,5 +199,16 @@ public void StaticVerifyTryOneShot(TestCase testCase) { StaticVerifyTryOneShotDriver(testCase); } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyHashToUInt32(TestCase testCase) + { + var alg = new XxHash32(Seed); + alg.Append(testCase.Input); + AssertEqualHashNumber(testCase.OutputHex, alg.GetCurrentHashAsUInt32()); + + AssertEqualHashNumber(testCase.OutputHex, XxHash32.HashToUInt32(testCase.Input, Seed)); + } } } diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs index 803a10ddb542..e6bd60a1ac7d 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs @@ -213,5 +213,16 @@ public void StaticVerifyTryOneShot(TestCase testCase) { StaticVerifyTryOneShotDriver(testCase); } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyHashToUInt32(TestCase testCase) + { + var alg = new XxHash32(); + alg.Append(testCase.Input); + AssertEqualHashNumber(testCase.OutputHex, alg.GetCurrentHashAsUInt32()); + + AssertEqualHashNumber(testCase.OutputHex, XxHash32.HashToUInt32(testCase.Input)); + } } } diff --git a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs index ac17869188ff..455651926e62 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs @@ -199,5 +199,16 @@ public void StaticVerifyTryOneShot(TestCase testCase) { StaticVerifyTryOneShotDriver(testCase); } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyHashToUInt32(TestCase testCase) + { + var alg = new XxHash32(Seed); + alg.Append(testCase.Input); + AssertEqualHashNumber(testCase.OutputHex, alg.GetCurrentHashAsUInt32()); + + AssertEqualHashNumber(testCase.OutputHex, XxHash32.HashToUInt32(testCase.Input, Seed)); + } } } diff --git a/src/libraries/System.IO.Hashing/tests/XxHash3Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash3Tests.cs index 533d5cebd3c9..fe6592bd85a8 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash3Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash3Tests.cs @@ -39,6 +39,9 @@ public void Hash_OneShot_Expected() Assert.Equal(test.Hash, BinaryPrimitives.ReadUInt64BigEndian(XxHash3.Hash(input, test.Seed))); Assert.Equal(test.Hash, BinaryPrimitives.ReadUInt64BigEndian(XxHash3.Hash((ReadOnlySpan)input, test.Seed))); + // Validate `XxHash3.HashToUInt64` + Assert.Equal(test.Hash, XxHash3.HashToUInt64(input, test.Seed)); + Assert.False(XxHash3.TryHash(input, destination.AsSpan(0, destination.Length - 1), out int bytesWritten, test.Seed)); Assert.Equal(0, bytesWritten); @@ -102,6 +105,7 @@ public void Hash_Streaming_Expected() // Validate that the hash we get from doing a one-shot of all the data up to this point // matches the incremental hash for the data appended until now. + Assert.Equal(XxHash3.HashToUInt64(asciiBytes.AsSpan(0, processed), test.Seed), hash.GetCurrentHashAsUInt64()); Assert.True(hash.TryGetCurrentHash(destination, out int bytesWritten)); Assert.Equal(8, XxHash3.Hash(asciiBytes.AsSpan(0, processed), destination2, test.Seed)); AssertExtensions.SequenceEqual(destination, destination2); @@ -109,6 +113,7 @@ public void Hash_Streaming_Expected() } // Validate the final hash code. + Assert.Equal(test.Hash, hash.GetCurrentHashAsUInt64()); Array.Clear(destination, 0, destination.Length); Assert.Equal(8, hash.GetHashAndReset(destination)); Assert.Equal(test.Hash, BinaryPrimitives.ReadUInt64BigEndian(destination)); diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs index 94c53f8f14d7..d91b1fde80f6 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs @@ -209,5 +209,16 @@ public void StaticVerifyTryOneShot(TestCase testCase) { StaticVerifyTryOneShotDriver(testCase); } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyHashToUInt64(TestCase testCase) + { + var alg = new XxHash64(Seed); + alg.Append(testCase.Input); + AssertEqualHashNumber(testCase.OutputHex, alg.GetCurrentHashAsUInt64()); + + AssertEqualHashNumber(testCase.OutputHex, XxHash64.HashToUInt64(testCase.Input, Seed)); + } } } diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs index 7e8cb82fc6fe..42d55a79287a 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs @@ -226,5 +226,16 @@ public void StaticVerifyTryOneShot(TestCase testCase) { StaticVerifyTryOneShotDriver(testCase); } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyHashToUInt64(TestCase testCase) + { + var alg = new XxHash64(); + alg.Append(testCase.Input); + AssertEqualHashNumber(testCase.OutputHex, alg.GetCurrentHashAsUInt64()); + + AssertEqualHashNumber(testCase.OutputHex, XxHash64.HashToUInt64(testCase.Input)); + } } } diff --git a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs index 2b3bfdf3b4b7..2b0595aea12d 100644 --- a/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs +++ b/src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs @@ -209,5 +209,16 @@ public void StaticVerifyTryOneShot(TestCase testCase) { StaticVerifyTryOneShotDriver(testCase); } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyHashToUInt64(TestCase testCase) + { + var alg = new XxHash64(Seed); + alg.Append(testCase.Input); + AssertEqualHashNumber(testCase.OutputHex, alg.GetCurrentHashAsUInt64()); + + AssertEqualHashNumber(testCase.OutputHex, XxHash64.HashToUInt64(testCase.Input, Seed)); + } } }