From effb2f9db64cde069b17ab671d6ed22e18aff7ec Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Fri, 8 Nov 2024 12:46:31 -0500 Subject: [PATCH] Clean up XXH64 --- SabreTools.Hashing/XxHash/XXH64State.cs | 158 +++++++++--------------- SabreTools.Hashing/XxHash/XxHash64.cs | 50 ++++++++ 2 files changed, 106 insertions(+), 102 deletions(-) create mode 100644 SabreTools.Hashing/XxHash/XxHash64.cs diff --git a/SabreTools.Hashing/XxHash/XXH64State.cs b/SabreTools.Hashing/XxHash/XXH64State.cs index cffbe42..8090c68 100644 --- a/SabreTools.Hashing/XxHash/XXH64State.cs +++ b/SabreTools.Hashing/XxHash/XXH64State.cs @@ -13,32 +13,22 @@ namespace SabreTools.Hashing.XxHash /// /// Total length hashed. This is always 64-bit. /// - public ulong TotalLength { get; set; } + private ulong _totalLen; /// /// Accumulator lanes /// - public ulong[] V { get; } = new ulong[4]; + private readonly ulong[] _acc = new ulong[4]; /// - /// Internal buffer for partial reads. Treated as unsigned char[16]. + /// Internal buffer for partial reads. Treated as unsigned char[32]. /// - public byte[] Memory { get; } = new byte[16]; + private byte[] _mem64 = new byte[32]; /// - /// Amount of data in + /// Amount of data in /// - public int Memsize { get; set; } - - /// - /// Reserved field, needed for padding anyways - /// - public uint Reserved32 { get; set; } - - /// - /// Reserved field. Do not read nor write to it. - /// - public ulong Reserved64 { get; set; } + private int _memsize; /// /// Resets to begin a new hash @@ -46,21 +36,18 @@ namespace SabreTools.Hashing.XxHash /// The 64-bit seed to alter the hash result predictably. public void Reset(ulong seed) { - TotalLength = default; + _totalLen = 0; - V[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2; - V[1] = seed + XXH_PRIME64_2; - V[2] = seed + 0; - V[3] = seed - XXH_PRIME64_1; - - for (int i = 0; i < Memory.Length; i++) + unchecked { - Memory[i] = default; + _acc[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + _acc[1] = seed + XXH_PRIME64_2; + _acc[2] = seed + 0; + _acc[3] = seed - XXH_PRIME64_1; } - Memsize = default; - Reserved32 = default; - Reserved64 = default; + Array.Clear(_mem64, 0, _mem64.Length); + _memsize = 0; } /// @@ -73,45 +60,47 @@ namespace SabreTools.Hashing.XxHash { int bEnd = offset + length; - TotalLength += (ulong)length; + _totalLen += (ulong)length; - if (Memsize + length < 32) + // Fill in tmp buffer + if (_memsize + length < 32) { - // Fill in tmp buffer - Array.Copy(data, offset, Memory, Memsize, length); - Memsize += length; + Array.Copy(data, offset, _mem64, _memsize, length); + _memsize += length; return; } - if (Memsize > 0) + // Some data left from previous update + if (_memsize > 0) { - // tmp buffer is full - Array.Copy(data, offset, Memory, Memsize, 32 - Memsize); - V[0] = Round(V[0], ReadLE64(Memory, 0)); - V[1] = Round(V[1], ReadLE64(Memory, 1)); - V[2] = Round(V[2], ReadLE64(Memory, 2)); - V[3] = Round(V[3], ReadLE64(Memory, 3)); - offset += 32 - Memsize; - Memsize = 0; + Array.Copy(data, offset, _mem64, _memsize, 32 - _memsize); + + int p64 = 0; + _acc[0] = Round(_acc[0], ReadLE64(_mem64, p64)); p64 += 8; + _acc[1] = Round(_acc[1], ReadLE64(_mem64, p64)); p64 += 8; + _acc[2] = Round(_acc[2], ReadLE64(_mem64, p64)); p64 += 8; + _acc[3] = Round(_acc[3], ReadLE64(_mem64, p64)); + + offset += 32 - _memsize; + _memsize = 0; } - if (offset + 32 <= bEnd) + if (offset <= bEnd - 32) { int limit = bEnd - 32; - do { - V[0] = Round(V[0], ReadLE64(data, offset)); offset += 8; - V[1] = Round(V[1], ReadLE64(data, offset)); offset += 8; - V[2] = Round(V[2], ReadLE64(data, offset)); offset += 8; - V[3] = Round(V[3], ReadLE64(data, offset)); offset += 8; + _acc[0] = Round(_acc[0], ReadLE64(data, offset)); offset += 8; + _acc[1] = Round(_acc[1], ReadLE64(data, offset)); offset += 8; + _acc[2] = Round(_acc[2], ReadLE64(data, offset)); offset += 8; + _acc[3] = Round(_acc[3], ReadLE64(data, offset)); offset += 8; } while (offset <= limit); } if (offset < bEnd) { - Array.Copy(data, offset, Memory, 0, bEnd - offset); - Memsize = bEnd - offset; + Array.Copy(data, offset, _mem64, 0, bEnd - offset); + _memsize = bEnd - offset; } } @@ -123,22 +112,25 @@ namespace SabreTools.Hashing.XxHash { ulong h64; - if (TotalLength >= 32) + if (_totalLen >= 32) { - h64 = RotateLeft64(V[0], 1) + RotateLeft64(V[1], 7) + RotateLeft64(V[2], 12) + RotateLeft64(V[3], 18); - h64 = MergeRound(h64, V[0]); - h64 = MergeRound(h64, V[1]); - h64 = MergeRound(h64, V[2]); - h64 = MergeRound(h64, V[3]); + h64 = RotateLeft64(_acc[0], 1) + + RotateLeft64(_acc[1], 7) + + RotateLeft64(_acc[2], 12) + + RotateLeft64(_acc[3], 18); + h64 = MergeRound(h64, _acc[0]); + h64 = MergeRound(h64, _acc[1]); + h64 = MergeRound(h64, _acc[2]); + h64 = MergeRound(h64, _acc[3]); } else { - h64 = V[2] /*seed*/ + XXH_PRIME64_5; + h64 = _acc[2] /*seed*/ + XXH_PRIME64_5; } - h64 += TotalLength; + h64 += _totalLen; - return Finalize(h64, Memory, 0, (int)TotalLength, Alignment.XXH_aligned); + return Finalize(h64, _mem64, 0, (int)_totalLen); } /// @@ -195,72 +187,34 @@ namespace SabreTools.Hashing.XxHash /// The remaining length, modulo 32. /// Whether @p ptr is aligned. /// The finalized hash - private static ulong Finalize(ulong hash, byte[] data, int offset, int length, Alignment align) + private static ulong Finalize(ulong hash, byte[] data, int offset, int length) { length &= 31; while (length >= 8) { - ulong k1 = Round(0, ReadLE64Align(data, offset, align)); + ulong k1 = Round(0, ReadLE64(data, offset)); offset += 8; hash ^= k1; hash = RotateLeft64(hash, 27) * XXH_PRIME64_1 + XXH_PRIME64_4; length -= 8; } + if (length >= 4) { - hash ^= (ulong)(ReadLE32Align(data, offset, align)) * XXH_PRIME64_1; + hash ^= ReadLE32(data, offset) * XXH_PRIME64_1; offset += 4; hash = RotateLeft64(hash, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; length -= 4; } + while (length > 0) { hash ^= data[offset++] * XXH_PRIME64_5; hash = RotateLeft64(hash, 11) * XXH_PRIME64_1; --length; } + return Avalanche(hash); } - - /// - /// The implementation for XXH64 - /// - /// The calculated hash. - private static ulong EndianAlign(byte[] data, int offset, int length, ulong seed, Alignment align) - { - ulong h64; - if (length >= 32) - { - int bEnd = offset + length; - int limit = bEnd - 31; - ulong v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; - ulong v2 = seed + XXH_PRIME64_2; - ulong v3 = seed + 0; - ulong v4 = seed - XXH_PRIME64_1; - - do - { - v1 = Round(v1, ReadLE64Align(data, offset, align)); offset += 8; - v2 = Round(v2, ReadLE64Align(data, offset, align)); offset += 8; - v3 = Round(v3, ReadLE64Align(data, offset, align)); offset += 8; - v4 = Round(v4, ReadLE64Align(data, offset, align)); offset += 8; - } while (offset < limit); - - h64 = RotateLeft64(v1, 1) + RotateLeft64(v2, 7) + RotateLeft64(v3, 12) + RotateLeft64(v4, 18); - h64 = MergeRound(h64, v1); - h64 = MergeRound(h64, v2); - h64 = MergeRound(h64, v3); - h64 = MergeRound(h64, v4); - - } - else - { - h64 = seed + XXH_PRIME64_5; - } - - h64 += (ulong)length; - - return Finalize(h64, data, offset, length, align); - } } } \ No newline at end of file diff --git a/SabreTools.Hashing/XxHash/XxHash64.cs b/SabreTools.Hashing/XxHash/XxHash64.cs new file mode 100644 index 0000000..2bd7087 --- /dev/null +++ b/SabreTools.Hashing/XxHash/XxHash64.cs @@ -0,0 +1,50 @@ +using System; + +namespace SabreTools.Hashing.XxHash +{ + internal class XxHash64 + { + /// + /// The 64-bit seed to alter the hash result predictably. + /// + private readonly uint _seed; + + /// + /// Internal xxHash64 state + /// + private readonly XXH64State _state; + + public XxHash64(uint seed = 0) + { + _seed = seed; + _state = new XXH64State(); + _state.Reset(seed); + } + + /// + /// Reset the internal hashing state + /// + public void Reset() + { + _state.Reset(_seed); + } + + /// + /// Hash a block of data and append it to the existing hash + /// + /// Byte array representing the data + /// Offset in the byte array to include + /// Length of the data to hash + public void TransformBlock(byte[] data, int offset, int length) + => _state.Update(data, offset, length); + + /// + /// Finalize the hash and return as a byte array + /// + public byte[] Finalize() + { + ulong hash = _state.Digest(); + return BitConverter.GetBytes(hash); + } + } +} \ No newline at end of file