Add HashTo/GetCurrentHashAs{UInt32/64} methods (#78075)
authorStephen Toub <stoub@microsoft.com>
Wed, 16 Nov 2022 11:12:53 +0000 (06:12 -0500)
committerGitHub <noreply@github.com>
Wed, 16 Nov 2022 11:12:53 +0000 (06:12 -0500)
* Add HashTo/GetCurrentHashAs{UInt32/64} methods

* Address PR feedback

17 files changed:
src/libraries/System.IO.Hashing/ref/System.IO.Hashing.cs
src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs
src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc64.cs
src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs
src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash32.cs
src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.cs
src/libraries/System.IO.Hashing/tests/Crc32Tests.cs
src/libraries/System.IO.Hashing/tests/Crc64Tests.cs
src/libraries/System.IO.Hashing/tests/NonCryptoHashTestDriver.cs
src/libraries/System.IO.Hashing/tests/System.IO.Hashing.Tests.csproj
src/libraries/System.IO.Hashing/tests/XxHash32Tests.007.cs
src/libraries/System.IO.Hashing/tests/XxHash32Tests.cs
src/libraries/System.IO.Hashing/tests/XxHash32Tests.f00d.cs
src/libraries/System.IO.Hashing/tests/XxHash3Tests.cs
src/libraries/System.IO.Hashing/tests/XxHash64Tests.007.cs
src/libraries/System.IO.Hashing/tests/XxHash64Tests.cs
src/libraries/System.IO.Hashing/tests/XxHash64Tests.f00d.cs

index 430f12e..73f3a64 100644 (file)
@@ -10,11 +10,15 @@ namespace System.IO.Hashing
     {
         public Crc32() : base (default(int)) { }
         public override void Append(System.ReadOnlySpan<byte> source) { }
+        [System.CLSCompliantAttribute(false)]
+        public uint GetCurrentHashAsUInt32() { throw null; }
         protected override void GetCurrentHashCore(System.Span<byte> destination) { }
         protected override void GetHashAndResetCore(System.Span<byte> destination) { }
         public static byte[] Hash(byte[] source) { throw null; }
         public static byte[] Hash(System.ReadOnlySpan<byte> source) { throw null; }
         public static int Hash(System.ReadOnlySpan<byte> source, System.Span<byte> destination) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static uint HashToUInt32(System.ReadOnlySpan<byte> source) { throw null; }
         public override void Reset() { }
         public static bool TryHash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }
     }
@@ -22,11 +26,15 @@ namespace System.IO.Hashing
     {
         public Crc64() : base (default(int)) { }
         public override void Append(System.ReadOnlySpan<byte> source) { }
+        [System.CLSCompliantAttribute(false)]
+        public ulong GetCurrentHashAsUInt64() { throw null; }
         protected override void GetCurrentHashCore(System.Span<byte> destination) { }
         protected override void GetHashAndResetCore(System.Span<byte> destination) { }
         public static byte[] Hash(byte[] source) { throw null; }
         public static byte[] Hash(System.ReadOnlySpan<byte> source) { throw null; }
         public static int Hash(System.ReadOnlySpan<byte> source, System.Span<byte> destination) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static ulong HashToUInt64(System.ReadOnlySpan<byte> source) { throw null; }
         public override void Reset() { }
         public static bool TryHash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }
     }
@@ -56,11 +64,15 @@ namespace System.IO.Hashing
         public XxHash3() : base (default(int)) { }
         public XxHash3(long seed) : base (default(int)) { }
         public override void Append(System.ReadOnlySpan<byte> source) { }
+        [System.CLSCompliantAttribute(false)]
+        public ulong GetCurrentHashAsUInt64() { 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; }
         public static byte[] Hash(System.ReadOnlySpan<byte> source, long seed = (long)0) { throw null; }
         public static int Hash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, long seed = (long)0) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static ulong HashToUInt64(System.ReadOnlySpan<byte> source, long seed = (long)0) { throw null; }
         public override void Reset() { }
         public static bool TryHash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, long seed = (long)0) { throw null; }
     }
@@ -69,11 +81,15 @@ namespace System.IO.Hashing
         public XxHash32() : base (default(int)) { }
         public XxHash32(int seed) : base (default(int)) { }
         public override void Append(System.ReadOnlySpan<byte> source) { }
+        [System.CLSCompliantAttribute(false)]
+        public uint GetCurrentHashAsUInt32() { throw null; }
         protected override void GetCurrentHashCore(System.Span<byte> 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<byte> source, int seed = 0) { throw null; }
         public static int Hash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, int seed = 0) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static uint HashToUInt32(System.ReadOnlySpan<byte> source, int seed = 0) { throw null; }
         public override void Reset() { }
         public static bool TryHash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, int seed = 0) { throw null; }
     }
@@ -82,11 +98,15 @@ namespace System.IO.Hashing
         public XxHash64() : base (default(int)) { }
         public XxHash64(long seed) : base (default(int)) { }
         public override void Append(System.ReadOnlySpan<byte> source) { }
+        [System.CLSCompliantAttribute(false)]
+        public ulong GetCurrentHashAsUInt64() { 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; }
         public static byte[] Hash(System.ReadOnlySpan<byte> source, long seed = (long)0) { throw null; }
         public static int Hash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, long seed = (long)0) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static ulong HashToUInt64(System.ReadOnlySpan<byte> source, long seed = (long)0) { throw null; }
         public override void Reset() { }
         public static bool TryHash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten, long seed = (long)0) { throw null; }
     }
index 05793ad..2304969 100644 (file)
@@ -11,8 +11,9 @@ namespace System.IO.Hashing
     /// </summary>
     /// <remarks>
     ///   <para>
-    ///     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 <c>{ 0x1C, 0xDF, 0x44, 0x21 }</c>,
     ///     the Little Endian representation of <c>0x2144DF1C</c>.
     ///   </para>
@@ -77,6 +78,11 @@ namespace System.IO.Hashing
             _crc = InitialState;
         }
 
+        /// <summary>Gets the current computed hash value without modifying accumulated state.</summary>
+        /// <returns>The hash value for the data already provided.</returns>
+        [CLSCompliant(false)]
+        public uint GetCurrentHashAsUInt32() => ~_crc;
+
         /// <summary>
         ///   Computes the CRC-32 hash of the provided data.
         /// </summary>
@@ -103,7 +109,8 @@ namespace System.IO.Hashing
         public static byte[] Hash(ReadOnlySpan<byte> source)
         {
             byte[] ret = new byte[Size];
-            StaticHash(source, ret);
+            uint hash = HashToUInt32(source);
+            BinaryPrimitives.WriteUInt32LittleEndian(ret, hash);
             return ret;
         }
 
@@ -127,7 +134,9 @@ namespace System.IO.Hashing
                 return false;
             }
 
-            bytesWritten = StaticHash(source, destination);
+            uint hash = HashToUInt32(source);
+            BinaryPrimitives.WriteUInt32LittleEndian(destination, hash);
+            bytesWritten = Size;
             return true;
         }
 
@@ -146,17 +155,18 @@ namespace System.IO.Hashing
                 ThrowDestinationTooShort();
             }
 
-            return StaticHash(source, destination);
-        }
-
-        private static int StaticHash(ReadOnlySpan<byte> source, Span<byte> destination)
-        {
-            uint crc = InitialState;
-            crc = Update(crc, source);
-            BinaryPrimitives.WriteUInt32LittleEndian(destination, ~crc);
+            uint hash = HashToUInt32(source);
+            BinaryPrimitives.WriteUInt32LittleEndian(destination, hash);
             return Size;
         }
 
+        /// <summary>Computes the CRC-32 hash of the provided data.</summary>
+        /// <param name="source">The data to hash.</param>
+        /// <returns>The computed CRC-32 hash.</returns>
+        [CLSCompliant(false)]
+        public static uint HashToUInt32(ReadOnlySpan<byte> source) =>
+            ~Update(InitialState, source);
+
         private static uint Update(uint crc, ReadOnlySpan<byte> source)
         {
             for (int i = 0; i < source.Length; i++)
index bc9917d..c4a164f 100644 (file)
@@ -10,7 +10,8 @@ namespace System.IO.Hashing
     /// </summary>
     /// <remarks>
     ///   <para>
-    ///     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
     ///     <c>{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }</c>.
@@ -75,6 +76,11 @@ namespace System.IO.Hashing
             _crc = InitialState;
         }
 
+        /// <summary>Gets the current computed hash value without modifying accumulated state.</summary>
+        /// <returns>The hash value for the data already provided.</returns>
+        [CLSCompliant(false)]
+        public ulong GetCurrentHashAsUInt64() => _crc;
+
         /// <summary>
         ///   Computes the CRC-64 hash of the provided data.
         /// </summary>
@@ -101,7 +107,8 @@ namespace System.IO.Hashing
         public static byte[] Hash(ReadOnlySpan<byte> source)
         {
             byte[] ret = new byte[Size];
-            StaticHash(source, ret);
+            ulong hash = HashToUInt64(source);
+            BinaryPrimitives.WriteUInt64BigEndian(ret, hash);
             return ret;
         }
 
@@ -125,7 +132,9 @@ namespace System.IO.Hashing
                 return false;
             }
 
-            bytesWritten = StaticHash(source, destination);
+            ulong hash = HashToUInt64(source);
+            BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
+            bytesWritten = Size;
             return true;
         }
 
@@ -144,17 +153,18 @@ namespace System.IO.Hashing
                 ThrowDestinationTooShort();
             }
 
-            return StaticHash(source, destination);
-        }
-
-        private static int StaticHash(ReadOnlySpan<byte> source, Span<byte> destination)
-        {
-            ulong crc = InitialState;
-            crc = Update(crc, source);
-            BinaryPrimitives.WriteUInt64BigEndian(destination, crc);
+            ulong hash = HashToUInt64(source);
+            BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
             return Size;
         }
 
+        /// <summary>Computes the CRC-64 hash of the provided data.</summary>
+        /// <param name="source">The data to hash.</param>
+        /// <returns>The computed CRC-64 hash.</returns>
+        [CLSCompliant(false)]
+        public static ulong HashToUInt64(ReadOnlySpan<byte> source) =>
+            Update(InitialState, source);
+
         private static ulong Update(ulong crc, ReadOnlySpan<byte> source)
         {
             for (int i = 0; i < source.Length; i++)
index 47bf2ad..51b4b4c 100644 (file)
@@ -129,7 +129,8 @@ namespace System.IO.Hashing
         public static byte[] Hash(ReadOnlySpan<byte> 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 @@ namespace System.IO.Hashing
         {
             if (destination.Length >= sizeof(long))
             {
-                long hash = HashToInt64(source, seed);
+                ulong hash = HashToUInt64(source, seed);
 
                 if (BitConverter.IsLittleEndian)
                 {
@@ -175,28 +176,32 @@ namespace System.IO.Hashing
             return false;
         }
 
-        // TODO https://github.com/dotnet/runtime/issues/76279: Make this public.
-        private static long HashToInt64(ReadOnlySpan<byte> source, long seed = 0)
+        /// <summary>Computes the XXH3 hash of the provided data.</summary>
+        /// <param name="source">The data to hash.</param>
+        /// <param name="seed">The seed value for this hash computation.</param>
+        /// <returns>The computed XXH3 hash.</returns>
+        [CLSCompliant(false)]
+        public static ulong HashToUInt64(ReadOnlySpan<byte> 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);
             }
         }
 
@@ -311,6 +316,15 @@ namespace System.IO.Hashing
         /// <param name="destination">The buffer that receives the computed hash value.</param>
         protected override void GetCurrentHashCore(Span<byte> destination)
         {
+            ulong hash = GetCurrentHashAsUInt64();
+            BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
+        }
+
+        /// <summary>Gets the current computed hash value without modifying accumulated state.</summary>
+        /// <returns>The hash value for the data already provided.</returns>
+        [CLSCompliant(false)]
+        public ulong GetCurrentHashAsUInt64()
+        {
             ulong current;
 
             if (_state.TotalLength > MidSizeMaxBytes)
@@ -352,11 +366,11 @@ namespace System.IO.Hashing
             {
                 fixed (byte* buffer = _state.Buffer)
                 {
-                    current = (ulong)HashToInt64(new ReadOnlySpan<byte>(buffer, (int)_state.TotalLength), (long)_state.Seed);
+                    current = HashToUInt64(new ReadOnlySpan<byte>(buffer, (int)_state.TotalLength), (long)_state.Seed);
                 }
             }
 
-            BinaryPrimitives.WriteUInt64BigEndian(destination, current);
+            return current;
 
             void DigestLong(ulong* accumulators, byte* secret)
             {
index a877480..a746e93 100644 (file)
@@ -11,6 +11,10 @@ namespace System.IO.Hashing
     /// <summary>
     ///   Provides an implementation of the XxHash32 algorithm.
     /// </summary>
+    /// <remarks>
+    /// For methods that persist the computed numerical hash value as bytes,
+    /// the value is written in the Big Endian byte order.
+    /// </remarks>
     public sealed partial class XxHash32 : NonCryptographicHashAlgorithm
     {
         private const int HashSize = sizeof(uint);
@@ -110,6 +114,15 @@ namespace System.IO.Hashing
         /// </summary>
         protected override void GetCurrentHashCore(Span<byte> destination)
         {
+            uint hash = GetCurrentHashAsUInt32();
+            BinaryPrimitives.WriteUInt32BigEndian(destination, hash);
+        }
+
+        /// <summary>Gets the current computed hash value without modifying accumulated state.</summary>
+        /// <returns>The hash value for the data already provided.</returns>
+        [CLSCompliant(false)]
+        public uint GetCurrentHashAsUInt32()
+        {
             int remainingLength = _length & 0x0F;
             ReadOnlySpan<byte> remaining = ReadOnlySpan<byte>.Empty;
 
@@ -118,8 +131,7 @@ namespace System.IO.Hashing
                 remaining = new ReadOnlySpan<byte>(_holdback, 0, remainingLength);
             }
 
-            uint acc = _state.Complete(_length, remaining);
-            BinaryPrimitives.WriteUInt32BigEndian(destination, acc);
+            return _state.Complete(_length, remaining);
         }
 
         /// <summary>
@@ -168,7 +180,8 @@ namespace System.IO.Hashing
         public static byte[] Hash(ReadOnlySpan<byte> 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 @@ namespace System.IO.Hashing
                 return false;
             }
 
-            bytesWritten = StaticHash(source, destination, seed);
+            uint hash = HashToUInt32(source, seed);
+            BinaryPrimitives.WriteUInt32BigEndian(destination, hash);
+            bytesWritten = HashSize;
             return true;
         }
 
@@ -213,10 +228,17 @@ namespace System.IO.Hashing
                 ThrowDestinationTooShort();
             }
 
-            return StaticHash(source, destination, seed);
+            uint hash = HashToUInt32(source, seed);
+            BinaryPrimitives.WriteUInt32BigEndian(destination, hash);
+            return HashSize;
         }
 
-        private static int StaticHash(ReadOnlySpan<byte> source, Span<byte> destination, int seed)
+        /// <summary>Computes the XxHash32 hash of the provided data.</summary>
+        /// <param name="source">The data to hash.</param>
+        /// <param name="seed">The seed value for this hash computation. The default is zero.</param>
+        /// <returns>The computed XxHash32 hash.</returns>
+        [CLSCompliant(false)]
+        public static uint HashToUInt32(ReadOnlySpan<byte> source, int seed = 0)
         {
             int totalLength = source.Length;
             State state = new State((uint)seed);
@@ -227,9 +249,7 @@ namespace System.IO.Hashing
                 source = source.Slice(StripeSize);
             }
 
-            uint val = state.Complete(totalLength, source);
-            BinaryPrimitives.WriteUInt32BigEndian(destination, val);
-            return HashSize;
+            return state.Complete(totalLength, source);
         }
     }
 }
index 0be3bbc..e57c7e8 100644 (file)
@@ -11,6 +11,10 @@ namespace System.IO.Hashing
     /// <summary>
     ///   Provides an implementation of the XxHash64 algorithm.
     /// </summary>
+    /// <remarks>
+    /// For methods that persist the computed numerical hash value as bytes,
+    /// the value is written in the Big Endian byte order.
+    /// </remarks>
     public sealed partial class XxHash64 : NonCryptographicHashAlgorithm
     {
         private const int HashSize = sizeof(ulong);
@@ -110,6 +114,15 @@ namespace System.IO.Hashing
         /// </summary>
         protected override void GetCurrentHashCore(Span<byte> destination)
         {
+            ulong hash = GetCurrentHashAsUInt64();
+            BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
+        }
+
+        /// <summary>Gets the current computed hash value without modifying accumulated state.</summary>
+        /// <returns>The hash value for the data already provided.</returns>
+        [CLSCompliant(false)]
+        public ulong GetCurrentHashAsUInt64()
+        {
             int remainingLength = (int)_length & 0x1F;
             ReadOnlySpan<byte> remaining = ReadOnlySpan<byte>.Empty;
 
@@ -118,8 +131,7 @@ namespace System.IO.Hashing
                 remaining = new ReadOnlySpan<byte>(_holdback, 0, remainingLength);
             }
 
-            ulong acc = _state.Complete(_length, remaining);
-            BinaryPrimitives.WriteUInt64BigEndian(destination, acc);
+            return _state.Complete(_length, remaining);
         }
 
         /// <summary>
@@ -168,7 +180,8 @@ namespace System.IO.Hashing
         public static byte[] Hash(ReadOnlySpan<byte> 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 @@ namespace System.IO.Hashing
                 return false;
             }
 
-            bytesWritten = StaticHash(source, destination, seed);
+            ulong hash = HashToUInt64(source, seed);
+            BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
+            bytesWritten = HashSize;
             return true;
         }
 
@@ -213,10 +228,17 @@ namespace System.IO.Hashing
                 ThrowDestinationTooShort();
             }
 
-            return StaticHash(source, destination, seed);
+            ulong hash = HashToUInt64(source, seed);
+            BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
+            return HashSize;
         }
 
-        private static int StaticHash(ReadOnlySpan<byte> source, Span<byte> destination, long seed)
+        /// <summary>Computes the XxHash64 hash of the provided data.</summary>
+        /// <param name="source">The data to hash.</param>
+        /// <param name="seed">The seed value for this hash computation.</param>
+        /// <returns>The computed XxHash64 hash.</returns>
+        [CLSCompliant(false)]
+        public static ulong HashToUInt64(ReadOnlySpan<byte> source, long seed = 0)
         {
             int totalLength = source.Length;
             State state = new State((ulong)seed);
@@ -227,9 +249,7 @@ namespace System.IO.Hashing
                 source = source.Slice(StripeSize);
             }
 
-            ulong val = state.Complete((uint)totalLength, source);
-            BinaryPrimitives.WriteUInt64BigEndian(destination, val);
-            return HashSize;
+            return state.Complete((uint)totalLength, source);
         }
     }
 }
index 0761450..217d7bf 100644 (file)
@@ -141,5 +141,16 @@ namespace System.IO.Hashing.Tests
         {
             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);
+        }
     }
 }
index 52971f0..c439ec8 100644 (file)
@@ -145,5 +145,16 @@ namespace System.IO.Hashing.Tests
         {
             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));
+        }
     }
 }
index cb684c0..2342f5c 100644 (file)
@@ -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 @@ namespace System.IO.Hashing.Tests
             }
         }
 
+        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;
index 11e73cf..264e4d0 100644 (file)
@@ -10,6 +10,7 @@
     <Compile Include="NonCryptoHashTestDriver.cs" />
     <Compile Include="XxHash3Tests.cs" />
     <Compile Include="XxHash32Tests.cs" />
+    <Compile Include="XxHash32Tests.007.cs" />
     <Compile Include="XxHash32Tests.f00d.cs" />
     <Compile Include="XxHash64Tests.cs" />
     <Compile Include="XxHash64Tests.007.cs" />
index 28d00ce..db6f73d 100644 (file)
@@ -199,5 +199,16 @@ namespace System.IO.Hashing.Tests
         {
             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));
+        }
     }
 }
index 803a10d..e6bd60a 100644 (file)
@@ -213,5 +213,16 @@ namespace System.IO.Hashing.Tests
         {
             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));
+        }
     }
 }
index ac17869..4556519 100644 (file)
@@ -199,5 +199,16 @@ namespace System.IO.Hashing.Tests
         {
             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));
+        }
     }
 }
index 533d5ce..fe6592b 100644 (file)
@@ -39,6 +39,9 @@ namespace System.IO.Hashing.Tests
                 Assert.Equal(test.Hash, BinaryPrimitives.ReadUInt64BigEndian(XxHash3.Hash(input, test.Seed)));
                 Assert.Equal(test.Hash, BinaryPrimitives.ReadUInt64BigEndian(XxHash3.Hash((ReadOnlySpan<byte>)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 @@ namespace System.IO.Hashing.Tests
 
                             // 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 @@ namespace System.IO.Hashing.Tests
                         }
 
                         // 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));
index 94c53f8..d91b1fd 100644 (file)
@@ -209,5 +209,16 @@ namespace System.IO.Hashing.Tests
         {
             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));
+        }
     }
 }
index 7e8cb82..42d55a7 100644 (file)
@@ -226,5 +226,16 @@ namespace System.IO.Hashing.Tests
         {
             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));
+        }
     }
 }
index 2b3bfdf..2b0595a 100644 (file)
@@ -209,5 +209,16 @@ namespace System.IO.Hashing.Tests
         {
             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));
+        }
     }
 }