Skip to content

Add HashTo/GetCurrentHashAs{UInt32/64} methods #78075

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 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,23 +10,31 @@ public sealed partial class Crc32 : System.IO.Hashing.NonCryptographicHashAlgori
{
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; }
}
public sealed partial class Crc64 : System.IO.Hashing.NonCryptographicHashAlgorithm
{
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; }
}
Expand Down Expand Up @@ -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<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; }
}
Expand All @@ -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<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; }
}
Expand All @@ -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<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; }
}
Expand Down
34 changes: 22 additions & 12 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -77,6 +78,11 @@ protected override void GetHashAndResetCore(Span<byte> destination)
_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>
Expand All @@ -103,7 +109,8 @@ public static byte[] Hash(byte[] source)
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;
}

Expand All @@ -127,7 +134,9 @@ public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, ou
return false;
}

bytesWritten = StaticHash(source, destination);
uint hash = HashToUInt32(source);
BinaryPrimitives.WriteUInt32LittleEndian(destination, hash);
bytesWritten = Size;
return true;
}

Expand All @@ -146,17 +155,18 @@ public static int Hash(ReadOnlySpan<byte> source, Span<byte> destination)
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++)
Expand Down
32 changes: 21 additions & 11 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc64.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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>.
Expand Down Expand Up @@ -75,6 +76,11 @@ protected override void GetHashAndResetCore(Span<byte> destination)
_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>
Expand All @@ -101,7 +107,8 @@ public static byte[] Hash(byte[] source)
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;
}

Expand All @@ -125,7 +132,9 @@ public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, ou
return false;
}

bytesWritten = StaticHash(source, destination);
ulong hash = HashToUInt64(source);
BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
bytesWritten = Size;
return true;
}

Expand All @@ -144,17 +153,18 @@ public static int Hash(ReadOnlySpan<byte> source, Span<byte> destination)
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++)
Expand Down
34 changes: 24 additions & 10 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ public static byte[] Hash(byte[] source, long seed)
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;
}

Expand Down Expand Up @@ -159,7 +160,7 @@ public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, ou
{
if (destination.Length >= sizeof(long))
{
long hash = HashToInt64(source, seed);
ulong hash = HashToUInt64(source, seed);

if (BitConverter.IsLittleEndian)
{
Expand All @@ -175,28 +176,32 @@ public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, ou
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);
}
}

Expand Down Expand Up @@ -310,6 +315,15 @@ public override void Append(ReadOnlySpan<byte> source)
/// <summary>Writes the computed 64-bit hash value to <paramref name="destination"/> without modifying accumulated state.</summary>
/// <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;

Expand Down Expand Up @@ -352,11 +366,11 @@ protected override void GetCurrentHashCore(Span<byte> destination)
{
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)
{
Expand Down
38 changes: 29 additions & 9 deletions src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -109,6 +113,15 @@ public override void Append(ReadOnlySpan<byte> source)
/// without modifying accumulated state.
/// </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;
Expand All @@ -118,8 +131,7 @@ protected override void GetCurrentHashCore(Span<byte> destination)
remaining = new ReadOnlySpan<byte>(_holdback, 0, remainingLength);
}

uint acc = _state.Complete(_length, remaining);
BinaryPrimitives.WriteUInt32BigEndian(destination, acc);
return _state.Complete(_length, remaining);
}

/// <summary>
Expand Down Expand Up @@ -168,7 +180,8 @@ public static byte[] Hash(byte[] source, int seed)
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;
}

Expand All @@ -193,7 +206,9 @@ public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, ou
return false;
}

bytesWritten = StaticHash(source, destination, seed);
uint hash = HashToUInt32(source, seed);
BinaryPrimitives.WriteUInt32BigEndian(destination, hash);
bytesWritten = HashSize;
return true;
}

Expand All @@ -213,10 +228,17 @@ public static int Hash(ReadOnlySpan<byte> source, Span<byte> 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<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);
Expand All @@ -227,9 +249,7 @@ private static int StaticHash(ReadOnlySpan<byte> source, Span<byte> destination,
source = source.Slice(StripeSize);
}

uint val = state.Complete(totalLength, source);
BinaryPrimitives.WriteUInt32BigEndian(destination, val);
return HashSize;
return state.Complete(totalLength, source);
}
}
}
Loading