using System.Security.Cryptography; namespace DTLib; // честно взятый с гитхаба алгоритм хеширования // выдаёт хеш в виде массива четырёх байтов sealed class XXHash32 : HashAlgorithm { private const uint PRIME32_1 = 2654435761U; private const uint PRIME32_2 = 2246822519U; private const uint PRIME32_3 = 3266489917U; private const uint PRIME32_4 = 668265263U; private const uint PRIME32_5 = 374761393U; private static readonly Func FuncGetLittleEndianUInt32; private static readonly Func FuncGetFinalHashUInt32; private uint _Seed32; private uint _ACC32_1; private uint _ACC32_2; private uint _ACC32_3; private uint _ACC32_4; private uint _Hash32; private int _RemainingLength; private long _TotalLength = 0; private int _CurrentIndex; private byte[] _CurrentArray; static XXHash32() { if (BitConverter.IsLittleEndian) { FuncGetLittleEndianUInt32 = new Func((x, i) => { unsafe { fixed (byte* array = x) { return *(uint*)(array + i); } } }); FuncGetFinalHashUInt32 = new Func(i => (i & 0x000000FFU) << 24 | (i & 0x0000FF00U) << 8 | (i & 0x00FF0000U) >> 8 | (i & 0xFF000000U) >> 24); } else { FuncGetLittleEndianUInt32 = new Func((x, i) => { unsafe { fixed (byte* array = x) { return (uint)(array[i++] | (array[i++] << 8) | (array[i++] << 16) | (array[i] << 24)); } } }); FuncGetFinalHashUInt32 = new Func(i => i); } } /// Creates an instance of class by default seed(0). /// public new static XXHash32 Create() => new(); /// Initializes a new instance of the class by default seed(0). public XXHash32() => Initialize(0); /// Initializes a new instance of the class, and sets the to the specified value. /// Represent the seed to be used for xxHash32 computing. public XXHash32(uint seed) => Initialize(seed); /// Gets the value of the computed hash code. /// Hash computation has not yet completed. public uint HashUInt32 => State == 0 ? _Hash32 : throw new InvalidOperationException("Hash computation has not yet completed."); /// Gets or sets the value of seed used by xxHash32 algorithm. /// Hash computation has not yet completed. public uint Seed { get => _Seed32; set { if (value != _Seed32) { if (State != 0) throw new InvalidOperationException("Hash computation has not yet completed."); _Seed32 = value; Initialize(); } } } /// Initializes this instance for new hash computing. public override void Initialize() { _ACC32_1 = _Seed32 + PRIME32_1 + PRIME32_2; _ACC32_2 = _Seed32 + PRIME32_2; _ACC32_3 = _Seed32 + 0; _ACC32_4 = _Seed32 - PRIME32_1; } /// Routes data written to the object into the hash algorithm for computing the hash. /// The input to compute the hash code for. /// The offset into the byte array from which to begin using data. /// The number of bytes in the byte array to use as data. protected override void HashCore(byte[] array, int ibStart, int cbSize) { if (State != 1) State = 1; int size = cbSize - ibStart; _RemainingLength = size & 15; if (cbSize >= 16) { int limit = size - _RemainingLength; do { _ACC32_1 = Round32(_ACC32_1, FuncGetLittleEndianUInt32(array, ibStart)); ibStart += 4; _ACC32_2 = Round32(_ACC32_2, FuncGetLittleEndianUInt32(array, ibStart)); ibStart += 4; _ACC32_3 = Round32(_ACC32_3, FuncGetLittleEndianUInt32(array, ibStart)); ibStart += 4; _ACC32_4 = Round32(_ACC32_4, FuncGetLittleEndianUInt32(array, ibStart)); ibStart += 4; } while (ibStart < limit); } _TotalLength += cbSize; if (_RemainingLength != 0) { _CurrentArray = array; _CurrentIndex = ibStart; } } /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. /// The computed hash code. protected override byte[] HashFinal() { _Hash32 = _TotalLength >= 16 ? RotateLeft32(_ACC32_1, 1) + RotateLeft32(_ACC32_2, 7) + RotateLeft32(_ACC32_3, 12) + RotateLeft32(_ACC32_4, 18) : _Seed32 + PRIME32_5; _Hash32 += (uint)_TotalLength; while (_RemainingLength >= 4) { _Hash32 = RotateLeft32(_Hash32 + FuncGetLittleEndianUInt32(_CurrentArray, _CurrentIndex) * PRIME32_3, 17) * PRIME32_4; _CurrentIndex += 4; _RemainingLength -= 4; } unsafe { fixed (byte* arrayPtr = _CurrentArray) { while (_RemainingLength-- >= 1) { _Hash32 = RotateLeft32(_Hash32 + arrayPtr[_CurrentIndex++] * PRIME32_5, 11) * PRIME32_1; } } } _Hash32 = (_Hash32 ^ (_Hash32 >> 15)) * PRIME32_2; _Hash32 = (_Hash32 ^ (_Hash32 >> 13)) * PRIME32_3; _Hash32 ^= _Hash32 >> 16; _TotalLength = State = 0; return BitConverter.GetBytes(FuncGetFinalHashUInt32(_Hash32)); } private static uint Round32(uint input, uint value) => RotateLeft32(input + (value * PRIME32_2), 13) * PRIME32_1; private static uint RotateLeft32(uint value, int count) => (value << count) | (value >> (32 - count)); private void Initialize(uint seed) { HashSizeValue = 32; _Seed32 = seed; Initialize(); } }